Hello!
After almost 2 years of refactoring and development, I think Pax Web 8 is ready for release.
Manual updates are still on my list and some exhaustive documentation (and maybe a blog post) is being prepared...
TL;DR:
- start with clean Karaf 4.3.3
karaf@root()> repo-remove org.ops4j.pax.web-7.3.19
karaf@root()> repo-add mvn:org.ops4j.pax.web/pax-web-features/8.0.0-SNAPSHOT/xml/features
karaf@root()> feature:install pax-web-http-tomcat
karaf@root()> feature:install pax-web-war
karaf@root()> bundle:install -s 'webbundle:mvn:io.hawt/hawtio-war/2.13.6/war?Web-ContextPath=/hawtio'
However I think it'd be good to describe the rationale behind the rewrite and some key points of the new release.
Background information:
Whiteboard specification says, that every web element may reference a target ServletContextHelper using "osgi.http.whiteboard.context.select" service registration property and its value is an LDAP filter, which may also be:
which means "register the servlet into ALL available ServletContextHelpers".
Pax Web 7 was taking the osgi.http.whiteboard.context.select property value and was doing split("=") on it to get the name/id of the target context... Effectively 1:1 relation was assumed, while Whiteboard specification assumes 1:N relationship.
So I started to rewrite the internal Pax Web model... And somehow much much more was refactored.
Pax Web 8 goals:
- I've carefully read chapters 102 (Http Service specification), 128 (Web Applications specification) and 140 (Whiteboard specification) of OSGi CMPN R7 and tried to implement everything in best possible way
- I always appreciated how well and cleverly Pax Web was written by others from this list:
11:41 $ git shortlog -snc origin/pax-web-7.2.x
894
adreg...@gmail.com 747 anierbeck
284 Achim Nierbeck
182 ANierbeck
123 Guillaume Nodet
114 Jean-Baptiste Onofré
103 Marc Schlegel
77 jbonofre
53 lostiniceland
44 Stephan Siano
37 Harald Wellmann
21 Marc Klinger
20 Freeman Fang
...
- I wanted to keep the "spirit" of Pax Web - emphasizing target runtime (Jetty, Tomcat, Undertow) over "specification first" approach
- I wanted to ensure that everything in Pax Web works as similar as possible in all 3 supported runtimes
- I wanted to keep the possibility to use native container configuration (jetty.xml, tomcat-server.xml, undertow.xml) in addition to what we can pass through org.ops4j.pax.web PID
- I wanted to make Pax Web 8 more reliable (no more flaky tests, no more random Thread.sleep() in tests, ...)
Pax Web 8 highlights:
- Latest versions of Jetty 9.4.x, Tomcat 9.0.x (without TIPI!) and Undertow 2.2.x are used
- Web "elements" mentioned in Http Service and Whiteboard specifications are handled: servlets, filters, listeners, error pages
- additionally, Pax Web 8 supports everything that can be configured in web.xml besides: <env-entry>, <post-construct>, <pre-destroy>, <resource-env-ref>, <resource-ref>, <administered-object>, <connection-factory>, <data-source>, <default-context-path>, <description>, <ejb-local-ref>, <ejb-ref>, <icon>, <jms-connection-factory>, <jms-destination>, <mail-session>, <message-destination>, <message-destination-ref>, <module-name>, <persistence-context-ref>, <persistence-unit-ref>, <service-ref>
- "above" what we can find in web.xml, Pax Web 8 supports:
- ServletContainerInitializers (SCI) - proved by working JSF/Primefaces/Vaadin examples
- web-fragment.xmls
- annotated web elements (@WebServlet, @WebFilter, @WebListener)
- META-INF/resources locations
- websockets via HttpService and Whiteboard
- web fragment scanning using tomcat-util-scanner
- without any mention in any CMPN specification, JSPs, welcome-pages, security configurations are supported
- no more xbean used to scan the "class space"
- no more dependency to ASM
- single configuration thread that operates on global model and synchronizes the model changes with the state of target runtime (Jetty, Tomcat, Undertow)
- consistent structure of pax-web-jetty, pax-web-tomcat and pax-web-undertow:
- there's single org.ops4j.pax.web.service.<runtime>.internal.<Runtime>ServerController class (per runtime) implementing org.ops4j.pax.web.service.spi.ServerController#sendBatch() method
- org.ops4j.pax.web.service.spi.task.Batch is a sequence of "operations" that change the model or affect the runtime
- there's single org.ops4j.pax.web.service.<runtime>.internal.<Runtime>ServerWrapper class (per runtime) that:
- keeps an instance of the "server" (org.eclipse.jetty.server.Server, org.apache.catalina.core.StandardServer or io.undertow.server.HttpHandler + collection of org.xnio.channels.AcceptingChannels) - Pax Web 8 DOESN'T use easy-to-use io.undertow.Undertow class!
- implements org.ops4j.pax.web.service.spi.task.BatchVisitor interface that's used to react to state-changing operations (like registration of a servlet) - mind that a "batch" may be "transactional"
- each runtime bundle contains some overriden runtime classes (like org.ops4j.pax.web.service.jetty.internal.PaxWebFilterHolder that extends org.eclipse.jetty.servlet.FilterHolder)
- Knowing that we have 3 "ways into" the Pax Web (HttpService, Whiteboard, WABs), Pax Web 8 introduces the concept of "the view of the WebContainer" - each "way" uses specific "view" to interact with Pax Web runtime
- The model is greatly simplified comparing to Pax Web 7:
- there exist "model" classes (like org.ops4j.pax.web.service.spi.model.elements.ServletModel) which are held internall and passed around between whiteboard, war, runtime and target container bundles
- from Whiteboard perspective, the "incoming" services, like "javax.servlet.Servlet" or "org.osgi.service.http.context.ServletContextHelper" are "tracked into" the model classes, so whether the servlet is registered by HttpService, Whiteboard or through a WAB, its processing is consistent and there's no problem mixing WABs, Whiteboard and HttpService approaches
- the "model" classes are divided into "web elements" and "web contexts" and each "web element" may reference one or many "web contexts" and the relation is dynamic
- ...
Pax Web 8 example:
The most important use-case is:
- user/bundle registers (Whiteboard approach) a servlet without specifying a "context" and the servlet has mapping "/my-servlet"
- this servlet should be available at URL http://localhost:8181/my-servlet
- another user/bundle registers a org.osgi.service.http.context.ServletContextHelper service with "osgi.http.whiteboard.context.path=/my-context" and "osgi.http.whiteboard.context.name=default" properties
- immediately (asap) the first servlet should be available at http://localhost:8181/my-context/my-servlet and no longer it should be available at the first URL
Yes, it works. Also shadowing may happen, because when two servlets are registered with the same name and target context, only the one with higher ranking/lower
service.id should be available. There are many integration tests that show what happens when conflicting services are registered across different contexts.
Summary:
For example in Karaf 4.3.3 (after uncommenting "karaf" user in `etc/users.properties`) we can do:
karaf@root()> repo-remove org.ops4j.pax.web-7.3.19
Removing features repository: mvn:org.ops4j.pax.web/pax-web-features/7.3.19/xml/features
karaf@root()> repo-add mvn:org.ops4j.pax.web/pax-web-features/8.0.0-SNAPSHOT/xml/features
Adding feature url mvn:org.ops4j.pax.web/pax-web-features/8.0.0-SNAPSHOT/xml/features
karaf@root()> feature:install pax-web-http-tomcat
karaf@root()> feature:install pax-web-war
karaf@root()> bundle:install 'webbundle:mvn:io.hawt/hawtio-war/2.13.6/war?Web-ContextPath=/hawtio'
Bundle ID: 68
karaf@root()> start 68
kind regards and thanks for the patience ;)
Grzegorz Grzybek