Thursday, September 30, 2021

Configuring JVM for Tomcat Service Manager

It's been slightly more than a year since my last post, but my team had recently ventured into preparing our own deployment script. At the behest of an eager member, we'd built up a PowerShell script using PowerShell App Deployment Toolkit, also known as "PSAppDeployToolkit", or even shorter as PSADT. This was a script meant to automate installation of a number of programs on the target Windows machines, silently and automatically. 

With the background of what we were doing established, this was about one specific setup we wanted to configure. One of the platforms we were using, was Tomcat 9. Fine, it was TomEE 9, using Tomcat 9, but you get the idea. Tomcat was to be installed as a service by the having our script chained to trigger the standard "service.install.as.admin.bat" script. 

Then we realised that the JVM would default to "auto", which means using whichever JRE it can find. We prefer to stick to our version for consistency. The easiest was for us to edit the provided batch script. After finding this page of documentation, I started poking around the script more. We need the --Jvm="/path/to/the/jvm.dll" option added in. 

The standard configuration out of the box is this:

"%EXECUTABLE%" //IS//%SERVICE_NAME% ^
--DisplayName=%SERVICE_NAME% ^
--StartClass org.apache.catalina.startup.Bootstrap ^
--StopClass org.apache.catalina.startup.Bootstrap ^
--StartParams start ^
--StopParams stop ^
--Startup auto ^
--JvmMs=512 ^
--JvmMx=1024 ^
--JvmSs=2048 ^
--StartMode jvm ^
--StopMode jvm ^
--LogLevel Info ^
--LogPrefix TomEE

So I just need to add it in, right? I tested.

"%EXECUTABLE%" //IS//%SERVICE_NAME% ^
--DisplayName=%SERVICE_NAME% ^
--StartClass org.apache.catalina.startup.Bootstrap ^
--StopClass org.apache.catalina.startup.Bootstrap ^
--StartParams start ^
--StopParams stop ^
--Startup auto ^
--Jvm=%PR_JVM% ^
--JvmMs=512 ^
--JvmMx=1024 ^
--JvmSs=2048 ^
--StartMode jvm ^
--StopMode jvm ^
--LogLevel Info ^
--LogPrefix TomEE

It seemed fine on the surface. And after more elaborate tests, I realised that doing so would break the Tomcat Service Manager. The memory values (JvmMs/JvmMx/JvmSs) would not take. Neither did the StartMode and StopMode parameters. I couldn't be certain if it was because of how the PSADT script worked. I'd only extracted the portion of our whole script for Tomcat to test on after all. After some more fumbling, I thought I could try adding it to the end, and gave it a shot. The command wouldn't recognise it past the LogPrefix parameter at that point. It was then that I decided to move it up 2 lines.

"%EXECUTABLE%" //IS//%SERVICE_NAME% ^
--DisplayName=%SERVICE_NAME% ^
--StartClass org.apache.catalina.startup.Bootstrap ^
--StopClass org.apache.catalina.startup.Bootstrap ^
--StartParams start ^
--StopParams stop ^
--Startup auto ^
--JvmMs=512 ^
--JvmMx=1024 ^
--JvmSs=2048 ^
--StartMode jvm ^
--StopMode jvm ^
--Jvm=%PR_JVM% ^
--LogLevel Info ^
--LogPrefix TomEE

Ha! It finally worked. The JVM was no longer "auto", the memory values took effect, and the start/stop modes were in as well. There was very little details surrounding this specific configuration, much less documentation relating to this particular quirk of positioning the option correctly.

Thursday, September 3, 2020

My minimum standards for coding in Java

People have a tendency to overlook certain issues, and may require gentle reminders every so often, to nudge them in the right direction. I'd imagine a simplified list would help with rehearsing the drill. This happens everywhere, including at work with my team, with regards to Java coding for webapp development. Here's what I've come up with succinctly abbreviated as "SCROLL":
  • Switches
  • Comments
  • Reusability
  • OWASP
  • LogLevels

Switches

Implement soft toggles that allow the running application to toggle features without server restarts. This is particularly useful for rolling out enhancements prior to the actual go-live date.

One way is to store such values in the database via system variables or code tables. The "enhanced" code should then check the switch each time it is called. Of course, not all situations can adopt this method, but I'd think that doing this as much as possible will be of great help.

Comments

Elaborate explanations in the codes will help others understand your thought processes in future. I find it equally helpful for when I revisit very old codes that was written by yours truly. The explanations for changes in workflows will be useful for troubleshooting several years down the road. 

A recommended format would be //name, date, description for single-liners. The next person could potentially approach the person who built it, and be able to discern a timeline of which set of codes came after which.

An added bonus would be if the comments were following conventional Java /** **/ format such that it can show up properly in generated javadocs.

Reusability

Optimised codes can be refactored into methods that can be write-once-run-anywhere (at the code level). This includes system variables and constants. Also part of this category are Util classes that serve a common, generic purpose.

OWASP

Security should never be an afterthought, where the OWASP still is the recommended set of guidelines that web developers should work with. Proper validations should be ensured (even for basic "!= null" checks) which will aid in the long run in case of code scans and security tests.

LogLevels

Logging is important, but so is the correct use of loglevels. Only use INFO level for production environments, while aiming to only output a single line containing all the useful information without generating extraneous logs. A sub-point would be to avoid logging sensitive data, and sanitising the output if it's necessary.


The above are meant for highlighting specific areas to focus on, in the name of brevity. Do you have any others you'd consider adding/replacing to the list?

Wednesday, August 19, 2020

StackOverflowError during Maven assembly

While trying to rebuild a project after reviewing changes from a team member, the compilation threw a Stack Overflow error (not the website) in the process of assembling the final JAR file.

Exception in thread "main" java.lang.StackOverflowError
    at sun.nio.cs.SingleByte.withResult(SingleByte.java:44)
    at sun.nio.cs.SingleByte.access$000(SingleByte.java:38)
    at sun.nio.cs.SingleByte$Encoder.encodeArrayLoop(SingleByte.java:187)
    at sun.nio.cs.SingleByte$Encoder.encodeLoop(SingleByte.java:219)
    at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:579)
    at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:271)
    at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
    at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
    at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
    at java.io.PrintStream.write(PrintStream.java:526)
    at java.io.PrintStream.print(PrintStream.java:669)
    at java.io.PrintStream.println(PrintStream.java:806)
    at org.slf4j.impl.SimpleLogger.write(SimpleLogger.java:381)
    at org.slf4j.impl.SimpleLogger.log(SimpleLogger.java:376)
    at org.slf4j.impl.SimpleLogger.info(SimpleLogger.java:538)
    at org.apache.maven.cli.logging.Slf4jLogger.info(Slf4jLogger.java:59)
    at org.codehaus.plexus.archiver.AbstractArchiver$1.hasNext(AbstractArchiver.java:464)
    at org.codehaus.plexus.archiver.AbstractArchiver$1.hasNext(AbstractArchiver.java:467)
    at org.codehaus.plexus.archiver.AbstractArchiver$1.hasNext(AbstractArchiver.java:467)

Some suggestions included looking at the JRE memory heap. Another hinted at the thread stack size instead. Turns out that the latter was more correct. Naturally, setting the MAVEN_OPTS value in the System PATH variable did not help. Restarting Eclipse didn't help either. 

To which, my next line of thought went towards wondering, what if the value was set into the JRE when executing the Maven build. 


Here's what I did:

  1. Navigate to Eclipse
  2. Run Configurations > [Select build profile]
  3. JRE tab > VM arguments
  4. Input "-Xss2m"
  5. Apply and Run
The thread stack size would have been increased to 2MB at this point for the build, the complain goes away, and the build completes successfully (for me at least).