How to Deploy a Vaadin Application to Google Cloud App Engine

blog-post-img

Today I tried to deploy a Vaadin application to Google Cloud App Engine.

It was not as straightforward as expected so I want to share my findings.

The example project is available on GitHub: https://github.com/simasch/vaadin-appengine-demo

The Vaadin Application

First, I’ve created a new Vaadin application: https://start.vaadin.com

Then I did a production build:

mvn package -Pproduction

That will generate an executable JAR file in the target directory.

First Deployment

This JAR file I’ve deployed to App Engine using the CLI:

gcloud app deploy

This seemed to work, but when I tried to access the app, I got an error in the logs

java.lang.UnsupportedClassVersionError: io/seventytwo/demo/gae/Application has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 55.0

Google App Engine uses Java 11 by default, but I want to use Java 17.

Using Java 17

But how can I configure another Java version? After reading the documentation, I found out that I can add an app.yaml in the src/appengine folder with this content:

runtime: java17

Maven Plugin

I also noticed that I could use a Maven plugin to deploy the application:

<plugin>
    <groupId>com.google.cloud.tools</groupId>
    <artifactId>appengine-maven-plugin</artifactId>
    <version>2.4.2</version>
    <configuration>
        <projectId>GCLOUD_CONFIG</projectId>
        <version>GCLOUD_CONFIG</version>
    </configuration>
</plugin>

So I simply can execute:

mvn package appengine:deploy -Pproduction

After the deployment was finished, I tried to access the application again. This time it failed with this message:

java.lang.NullPointerException: Cannot invoke "java.io.RandomAccessFile.length()" because "this.cache" is null
at javax.imageio.stream.FileCacheImageOutputStream.close ( javax/imageio.stream/FileCacheImageOutputStream.java:229 )
at javax.imageio.ImageIO.write ( javax/imageio/ImageIO.java:1598 )
at com.vaadin.flow.server.PwaIcon.setImage ( com/vaadin.flow.server/PwaIcon.java:220 )

Memory Limit

The exception comes from the PWA support of Vaadin, so I removed the @PWA annotation and the application run. But looking at the logs, I’ve found issues with memory limits:

Exceeded soft memory limit of 256 MB with 262 MB after servicing 1 requests total. Consider setting a larger instance class in app.yaml.

By default, an instance of type F1 is allocated, which was not enough to start the Vaadin application.

So finally, I configured an F2 instance in the app.yaml

runtime: java17
instance_class: F2

Session State

As you may know, Vaadin applications have server state and may also have push enabled that will use WebSockets. We must enable session affinity to ensure that our application runs correctly in multiple instances.
This can be done in the app.yaml as well

network:
  session_affinity: true

But what does session affinity mean? Let’s check the documentation:

session_affinityOptional. Set to true to configure App Engine to route multiple sequential requests for a given user to the same App Engine instance such as when storing user data locally during a session. Session affinity enables inspecting the value of a cookie to identify multiple requests by the same user and then directs all such requests to the same instance. If the instance is rebooted, unhealthy, overloaded or becomes unavailable when the number of instances has been scaled down, session affinity will be broken and further requests are then routed to a different instance. Note that enabling session affinity can affect your load balancing setup. This parameter is disabled by default.

Conclusion

Deploying to Google App Engine is straight-forwarded using the Maven plugin, but you must analyze the log files and probably configure the Java version, the instance size, and session affinity.

Simon Martinelli
Follow Me