To main content

My top 10 favourite OpenLiberty hidden features

Published by Benjamin Marwell on

Here is a list of my top 10 OpenLiberty hidden features. They are not the "best" features by any means of »hidden knowledge«, but they are the most handy features I use regularly not everyone knows about. YMMV. 😉

10: Activating OpenAPI for a free OpenAPI description

Whether or not you add OpenAPI (aka Swagger) annotations to your JAX-RS endpoints, you can "just switch on" the OpenAPI feature. Enabling this feature will reveal two new endpoints: /openapi/ for a Swagger YAML document of your JAX-RS endpoints, and /openapi/ui/ for a really nice Web-UI. Over are the days of complicated maven plugins!

This is extremely useful for quick testing or publishing your API. No further configuration needed. The UI even includes a "try it out button", so you do not even need to set up Postman or Insomnia for quick tests!

<server>
  <features>
    <feature>mpOpenAPI-1.1</feature>
  </features>
</server>

9: Disabling deferred servlet loading

This is a no-brainer for every web application I write. By default, Liberty does not eagerly initialise servlets, which usually is good for CPU usage when booting up multiple OpenLiberty servers at once.

With this option set, the servlets are initialized early, which will reduce the time for the first hit-to-response for the first user.

<server>
  <webContainer deferServletLoad="false" />
</server>

8: Activating shared class cache

If you want to start the same application multiple times, a shared class cache will allow any Java OpenJ9 VM to re-use already compiled and optimized classes. This is also the default configuration if you run the official Docker Images and did a RUN configure.sh in your Dockerfile. Just make sure you use a OpenJ9 VM and point the VMs to the same directory using this line in your jvm.options file.

-XX:+OriginalJDK8HeapSizeCompatibilityMode
-Xshareclasses:name=myapp,cacheDir=/tmp/myapp/.classCache/

You can also use an environment variable, e.g. in your server.env file.

IBM_JAVA_OPTIONS="-XX:+OriginalJDK8HeapSizeCompatibilityMode -Xshareclasses:name=myapp,cacheDir=/tmp/myapp/.classCache/"

7: Using different truststore and keystore configurations

Security is easily one of the most important issues when configuring an application container. You can change the truststore (e.g. for your custom PKI) or you can change the default (outgoing) protocols.

<server>
  <ssl id="defaultSSLConfig" trustDefaultCerts="true" sslProtocol="TLSv1.2" />
</server>

Oddly enough, the default is sslProtocol="SSL_TLSv2", which is an alias for everything starting from SSLv3 (deprecated, insecure). Please note that such security-related configurations must be updated from time to time. For example, TLSv1.3 will soon be a new standard you might want to add to the list.

6: Using server.env for testing different JVM implementations

Different JVMs and Java versions will behave differently -- even for your app! If you want to try out some more JVMs, here are some intersting ones:

Now, open your server.env file and add the corresponding environment variable.

JAVA_HOME=$HOME/.local/apps/adopt-openj9@1.11.0-6

5: Using includes to switch the database

Are you testing against more than just one database? You can control this by environment variables! Just include a environment variable DATABASE_TYPE=postgresql in your server.env file and then include the database configuration like so:

<server>
  <include optional="false" location="/config/ds-${env.DATABASE_TYPE}.xml" />
</server>

This will include the file /config/ds-postgresql.xml and fail the server startup if it does not exist. The content for such a file could be the following:

<server>
  <!--    PostgreSQL Driver   -->
  <library id="postgresqllib"
  >
    <fileset dir="${shared.resource.dir}/postgresql/"
             includes="postgre*.jar"
    />
  </library>

  <jdbcDriver id="postgresql"
              libraryRef="postgresqllib"
  />

  <dataSource id="mydb"
              jndiName="jdbc/mydb"
              jdbcDriverRef="postgresql"
              type="javax.sql.Datasource"
  >
    <connectionManager maxPoolSize="2"
                       minPoolSize="10"
    />
    <properties
        URL="jdbc:postgresql://${env.DB_HOST}:${env.DB_PORT}/${env.DB_NAME}"
        user="${env.DB_USER}"
        password="${env.DB_PASSWORD}"
    />
  </dataSource>
</server>

If you need more databases like this, just create similar configuration files with the same scheme. For example you coud create a file /config/ds-postgresql.xml and replace everything PostgreSQL-related with H2.

4: Reading FFDCs for error analysis

Developing a new application? I bet you will have an uncought RuntimeException at some point. Those might be hard to debug and hard to find in the first place. Starting any application container with a debugger attached (e.g. from IntelliJ) can be a pain in the neck, depending on your hardware.

Liberty to the rescue! For every first occurence of any uncought RuntimeException at a specific code position, Liberty will create an FFDC file (First Failure Data Capture). Look in $SERVER_DIR/logs/ffdc/. The files contain a complete (i.e. no omnissions ) stack trace, a thread dump of the caller and some additional dumps. The stack trace alone is worth so much!

3: Disabling CDI scanning

If your application uses CDI, but every bean archive does have a beans.xml file, you can bost Liberty’s startup time considerably by disabling archive scanning.

Background: CDI 1.2 introduced bean archives without even defining a beans.xml at all. For this to work, the application container needs to scan *every* *single* *jar* *archive* included in your .war or .ear file.

The remedy to this is to add the following configuration to your server.xml file. This will work for CDI 1.2 onwards.

<server>
  <!-- this also works for CDI 2.0 and above -->
  <cdi12 enableImplicitBeanArchives="false" />
</server>

2: Enabling ISO DateTime format

Now, have you ever sat in front of a developer machine with German Locale? OpenLiberty will use your machine’s locale by default for displaying timestamps in its main logfile messages.log.

The remedy is to set isoDateFormat option to true.

<server>
  <logging isoDateFormat="true" />
</server>
# isoDateFormat=false and locale set to en_GB
[18/08/2020, 04:52:53:136 CEST] 00000001 com.ibm.ws.kernel.launch.internal.FrameworkManager           A CWWKE0001I: The server jenkins has been launched.

# isoDateFormat=false and locale set to de_DE
# even the TZ is translated, yuck! And still the colon for ms
[18.08.20, 04:52:53:136 MESZ] 00000001 com.ibm.ws.kernel.launch.internal.FrameworkManager           I CWWKE0002I: Der Kernel wurde nach  0,742 Sekunden gestartet.

# isoDateFormat=true and any locale
[2020-08-04:52:53.136+0200] 00000001 com.ibm.ws.kernel.launch.internal.FrameworkManager           A CWWKE0001I: The server jenkins has been launched.

You can also set the option com.ibm.ws.logging.isoDateFormat=true in your bootstrap.properties (recommended).

1: Setting WLP_USER_DIR environment variable

This is the number one useful thing to do, whether you are a developer or responsible for production!

Setting the evinronment variable WLP_USER_DIR to any value (must be a valid path on your local file system), the server directory and shared resource directory will be read from that location.

Why this is useful? Well, as a developer just imagine you want to test your application from your IDE with both OpenLiberty 20.0.0.8 and IBM Liberty Profile 19.0.0.12. If you did not set the environment variable, you had to create two servers, one in each installation directory. If you update your OpenLiberty installation, you must not forget to move all the servers and shared resources. With the option set, you can just execute the corresponding ./bin/server binary and they will read the configuration from the same location. IntelliJ will pick it up after you logged in again.

As a production engineer, you will want to upgrade your (Open)Liberty installation from time to time -- maybe even for security reasons. If you do not want to move the server directories after each upgrade, this option is for you. It will make your life much easier.

Conclusion

Were some of the tips and tricks new to you? What is your favourite feature? Do you agree or disagree on my list? Leave me a comment here or on twitter!