Deploying Vaadin app on Karaf
Karaf is an OSGi container rather than a WAR container. Hence it is not as easy to deploy your WAR into Karaf as opposed to, say, Tomcat. You basically have three options:
- Split your WAR into separate jars, add OSGi descriptors to all of those so that Karaf recognizes them as OSGi bundles, then plug your servlets into Karaf’s pax-web something. That’s a lot of work.
- Repackage your WAR as JAR with JARs, add OSGi descriptor so that Karaf recognizes that as a big bundle, then plug your servlets into Karaf’s Pax-Web. That’s still a lot of work.
- Use WAR Deployer and just copy your WAR to the
deploy/
folder. Ta-daa! This is the easiest way of deploying WAR to Karaf, the one that doesn’t require you to add OSGi manifests to your WAR and/or tackle the XML bundle configurations. Let’s go.
Using Karaf WAR Deployer
First, download Karaf, the Karaf
Runtime is enough. Unpack it, go into the bin/
folder and run it: ./karaf
.
Karaf will start and will present you with a console. Great. First lesson
is inspired by vim: we’ll learn how to quit. Luckily that’s easy, just press Ctrl+D
.
We’ll use the Karaf WAR deployer which is able to take a WAR file, add all necessary OSGi descriptors automatically and deploy the WAR file on an internal Jetty server (or whatever is configured to be used by the Pax-Web OSGi Http Service). In the Karaf console, type in:
karaf@root()> feature:install war
This will install the http
feature (which is a basic support for serving
http in OSGi), http-whiteboard
(which allows for mapping servlets to context
roots I guess) and war
which is the WAR Deployer itself. You can verify
that everything is installed, by listing features:
feature:list | grep war
feature:list | grep http
WAR Deployer is old-fashioned and requires web.xml
to be present in the
WAR file in order to be recognized and deployed properly by Karaf. Just add
the following dummy web.xml
to your WAR project’s src/main/webapp/WEB-INF/
folder:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
</web-app>
That is enough - Jetty will properly autodiscover all of your servlets and
other annotated stuff. Now build your WAR file. You can use any Vaadin-based webapp;
for the purpose of this text we’re going to experiment on the
karibu-helloworld-application
Vaadin 8 webapp. Just don’t forget to add the web.xml
, then run ./gradlew
and find the WAR file in build/libs
.
Note: Vaadin 10 apps will crash with NPE for some reason, please feel free to report a bug to the Vaadin folk.
Copy the WAR file into Karaf’s deploy/
folder. The WAR file should be picked
up automatically and launched. You can follow the log of your app in Karaf
by using the log:tail
Karaf command - consult the log for any exceptions
or other deployment failures. You can also use the web:list
command to
check that your app is up and running, for example:
karaf@root()> web:list
ID │ State │ Web-State │ Level │ Web-ContextPath │ Name
────┼─────────────┼─────────────┼───────┼────────────────────────────────┼──────────────────────────────────────
105 │ Active │ Deployed │ 80 │ /karibu-helloworld-application │ karibu-helloworld-application (0.0.0)
Now you can go and see your app running on http://localhost:8181/karibu-helloworld-application. Enjoy.
Enabling Push
@Push
via WebSockets will not work with Vaadin 7.7.x and Karaf 4.1.6 and
will fall back to longpolling, because the bundled Jetty is newer than Vaadin
expects and Vaadin will fail with java.lang.ClassNotFoundException: org.eclipse.jetty.websocket.server.WebSocketServerFactory
.
You need to force Vaadin to use JSR356 in Atmosphere:
@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true, initParams = {@WebInitParam(name = "org.atmosphere.cpr.asyncSupport", value = "org.atmosphere.container.JSR356AsyncSupport") })
Now we’ll get java.lang.RuntimeException: Cannot load platform configurator
,
because OSGi doesn’t support standard stuff like ServiceLoader out-of-the-box.
On Stackoverflow
there is a solution, but luckily a proper artifact is already provided by
the servicemix people. First remove all of your WAR files from the deploy/
folder. Stop Karaf and run it with ./karaf clean
. Then, in your karaf console:
$ feature:install war
$ bundle:install mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.javax-websocket-api/1.1_1
$ la -u|grep websocket-api
55 │ Active │ 30 │ 1.1 │ mvn:javax.websocket/javax.websocket-api/1.1
87 │ Active │ 30 │ 9.3.24.v20180605 │ mvn:org.eclipse.jetty.websocket/websocket-api/9.3.24.v20180605
105 │ Installed │ 80 │ 1.1.0.1 │ mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.javax-websocket-api/1.1_1
$ bundle:start 105
$ bundle:uninstall 55
And now try to redeploy your WAR file. If you don’t to start from a clean
state, you can get java.lang.ClassCastException: org.eclipse.jetty.websocket.jsr356.server.ServerContainer cannot be cast to javax.websocket.server.ServerContainer
(that’s your app loaded by a classloader which references old javax-websocket-api classloader);
if you fail to remove the old javax.websocket-api
you can still get the
Cannot load platform configurator
error.
Karaf, employing OSGi and class loaders, may suprise you with lots of nasty exceptions. Be careful to start with a clean state, and repeat the steps if necessary.