Wednesday, August 23, 2017

Dynamically adding an interceptor to a build-in CDI bean

In Java EE's CDI, beans can be augmented via 2 artefacts; Decorators and Interceptors.

Decorators are typically owned by the application code and can decorate a bean that's shipped by the container (build-in beans) or a library.

Interceptors are typically shipped by a library and can be applied (bound) to a bean that's owned by the application.

So how do you bind a library shipped interceptor to a library shipped/build-in bean? In CDI 1.2 and before this wasn't really possible, but in CDI 2.0 we can take advantage of the new InterceptionFactory to do just this. It's not entirely trivial yet, but it's doable. In this article we'll demonstrate how to apply the @RememberMe interceptor binding from the new Java EE 8 Security spec to a build-in bean of type HttpAuthenticationMechanism, which is from the Security spec as well.

First we configure our authentication mechanism by means of the following annotation:

@BasicAuthenticationMechanismDefinition(
    realmName="foo"
)

 

This will cause the container to enable a build-in bean with interface type HttpAuthenticationMechanism, but having an unknown (vendor specific) implementation.

Next we'll definite an alternative for this bean via a CDI producer:

@Alternative
@Priority(500)
@ApplicationScoped
public class ApplicationInit {
    
    @Produces
    public HttpAuthenticationMechanism produce(InterceptionFactory<HttpAuthenticationMechanismWrapper> interceptionFactory, BeanManager beanManager) {
        return ...
    }
Note that perhaps somewhat counter intuitively the @Alternative annotation is put on the bean hosting the producer method, not on the producer method itself.

A small challenge here is to obtain the bean with type HttpAuthenticationMechanism that would have been chosen by the CDI runtime had our producer not been there. For a decorator this is easy as CDI makes that exact bean injectable via the @Decorated qualifier. Here we'll have to do this manually. One way is to get all the beans of type HttpAuthenticationMechanism from the bean manager (this will include both alternatives and non-alternatives), filter ourselves from that set and then let the bean manager resolve the set to the one that would be chosen for injection. We then create a reference for that chosen bean.

The following shows this in code:

HttpAuthenticationMechanism mechanism =
    createRef(
        beanManager.resolve(
            beanManager.getBeans(HttpAuthenticationMechanism.class)
                       .stream()
                       .filter(e -> !e.getBeanClass().equals(ApplicationInit.class))
                       .collect(toSet())), 
            beanManager);

 

With createRef being defined as:

HttpAuthenticationMechanism createRef(Bean<?> bean, BeanManager beanManager) {
    return (HttpAuthenticationMechanism) 
        beanManager.getReference(
            bean, 
            HttpAuthenticationMechanism.class, 
            beanManager.createCreationalContext(bean));
}

 

We now have an instance to the bean to which we like to apply the interceptor binding. Unfortunately, there's a somewhat peculiar and very nasty note in the CDI spec regarding the method that creates a proxy with the required interceptor attached:

If the provided instance is an internal container construct (such as client proxy), non-portable behavior results.

Since the HttpAuthenticationMechanism is a client proxy (it's application scoped by spec definition) we have no choice but to introduce some extra ceremony here and that's by providing a wrapper ourselves. The interceptor will be applied to the wrapper then, and the wrapper will delegate to the actual HttpAuthenticationMechanism instance:

 

HttpAuthenticationMechanismWrapper wrapper = 
    new HttpAuthenticationMechanismWrapper(mechanism);

 

Having our HttpAuthenticationMechanism instance ready, we can now dynamically configure an annotation instance. Such instance can be created via CDI's provided AnnotationLiteral helper type:

interceptionFactory.configure().add(
    new RememberMeAnnotationLiteral(
        86400, "",       // cookieMaxAgeSeconds
        false, "",       // cookieSecureOnly
        true, "",        // cookieHttpOnly
        "JREMEMBERMEID", // cookieName
        true, ""         // isRememberMe
    )
);

 

Finally, we create the above mentioned new proxy with the configured interceptor binding applied to it using the interception factory's createInterceptedInstance method and return this from our producer method:

return interceptionFactory.createInterceptedInstance(
    new HttpAuthenticationMechanismWrapper(wrapper));

 

A full example can be found in the Java EE 8 samples project.

Note that there's a small caveat here; if the Interceptor needs access to the interceptor bindings (which is almost always the case when the binding has attributes), you can't just inspect the target type as one would usually do in CDI 1.2 and earlier code. The interceptor binding annotation is not physically present on the type. At the moment it's not entirely clear how to obtain these in a portable way. The interceptors in the Java EE Security RI (Soteria) uses an RI specific way for now.

The example was tested on Payara Server 5, of which a snapshot can be downloaded from the snapshot repository. An initial alpha will be released very soon, but in the mean time the latest version can be downloaded here:
payara-5.0.0.173-SNAPSHOT.zip.

Arjan Tijms

Thursday, August 17, 2017

Extensionless URLs with JSF 2.3

An extensionless URL is a URL without a final suffix like .xhtml, .html, .jsp, etc. Such a suffix is seen as technical "clutter" that's hard to remember for humans. Servers often need it though to route a request to the right controller.

JSF, a Java EE MVC framework, has supported extensionless URLs for some time via PrettyFaces (now merged to the general Rewrite framework) and OmniFaces. Both of these solutions used various workarounds to trick JSF into working with extensionless URLs.

Though JSF 2.3 does, unfortunately, still not support extensionless URLs fully out of the box via e.g. a single parameter, it can provide support for it by basically combining the new support for exact mapping and the API for obtaining a list of all view resources. Additionally combining this with the Servlet 3.1 feature for dynamically adding Servlet mappings and some JDK8 streaming and lambdas, makes it possible to enable extensionless support with just 2 statements (albeit somewhat long statements):

@WebListener
public class MappingInit implements ServletContextListener {
    
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        FacesContext context = FacesContext.getCurrentInstance();
        
        sce.getServletContext()
           .getServletRegistrations()
           .values()
           .stream()
           .filter(e -> e.getClassName().equals(FacesServlet.class.getName()))
           .findAny()
           .ifPresent(
               reg -> context.getApplication()
                             .getViewHandler()
                             .getViews(context, "/", RETURN_AS_MINIMAL_IMPLICIT_OUTCOME)
                             .forEach(e -> reg.addMapping(e)));
    }
}

What the above code does is finding the existing FacesServlet, then getting all views for the entire application in a form that happens to be exactly suitable for extensionless URLs, and then adding each of them as mapping to the FacesServlet we previously found.

After adding the above shown WebListener to an application, its view can be requested via URLs like example.com/login, example.com/users/all etc.

The example was tested on Payara Server 5, of which a snapshot can be downloaded from the snapshot repository. An initial alpha will be released very soon, but in the mean time the latest version can be downloaded here:
payara-5.0.0.173-SNAPSHOT.zip.

Arjan Tijms

Tuesday, August 15, 2017

Dynamic beans in CDI 2.0

A while ago we wrote about CDIs ability to dynamically add Bean<T> instances to the CDI runtime.

A Bean<T> is a kind of factory for beans, that makes types available for injection, lookup via the bean manager, or by referencing them in expression language. CDI producers (via the @Produces annotation) fulfil a somewhat similar role, but they essentially only make the "create instance" method dynamic; the rest (like scope, types, etc) is more or less static. A programmatically added Bean<T> essentially makes all those aspects dynamic.

As the previous article showed, dynamically adding such Bean<T> is a bit more work and it's quite verbose, as well as a little complex as the developer has to find out what to return as a default for various methods that are not directly of interest.

CDI 2.0 has addressed some of the above issues by providing a very convenient builder that not only makes creating a Bean<T> instance far less verbose, but also takes away most of the guesswork. The following shows an example:

public class CdiExtension implements Extension {

    public void afterBean(final @Observes AfterBeanDiscovery afterBeanDiscovery) {
        afterBeanDiscovery
            .addBean()
            .scope(ApplicationScoped.class)
            .types(MyBean.class)
            .id("Created by " + CdiExtension.class)
            .createWith(e -> new MyBeanImpl("Hi!"));
    }
}

The above makes a bean available for injection into MyBean injection points and with the @ApplicationScoped scope, backed by a MyBeanImpl class.

A fully working example is provided in the Java EE 8 Samples project.

The example was tested on Payara Server 5, of which a snapshot can be downloaded from the snapshot repository. An initial alpha will be released very soon, but in the mean time the latest version can be downloaded here:
payara-5.0.0.173-SNAPSHOT.zip.

Arjan Tijms

Tuesday, June 13, 2017

Should the community take over JSF.next or not?

JSF aka JavaServer Faces is a component based MVC framework that's part of Java EE and is one of the oldest Java MVC frameworks that's still supported and actively used (version 1.0 was released in 2004).

Over time, Java EE itself has grown considerably and as such the resources required to maintain and evolve Java EE have grown as well. Now Oracle has indicated at several occasions that it just doesn't have the resources required for this, and for most constituent specs of Java EE it can do at most small updates, but in other cases can't do any updates at all.

In order to lessen this immense burden on Oracle somewhat, the community has largely taken over for JSF 2.3 and Java EE Security API 1.0. The following graph (taken from a presentation by JSF spec lead Ed Burns) gives an indication:

The question is how to continue for JSF.next?

Since the community has largely taken over JSF already, should this perhaps be made more formal by actually letting the community (individual, foundation, or even representative small company) take the lead in developing the next version of JSF? In such scenario, the existing JSF versions (2.3 and before) and their respective TCKs would stay with Oracle, but JSF.next (i.e. JSF 2.4 or 3.0) would be fully specified, implemented and released by the community (OmniFaces in particular, with possibly the help of others).

Is a large and important spec such as JSF better off at a large and responsible, albeit resource constrained, organisation such as Oracle, or do you want OmniFaces to take over the spec lead role? If you want, you can cast a vote in the poll below or leave a comment:

Do you want OmniFaces to take over the JSF spec lead role?

Wednesday, May 24, 2017

Draft list of changes in Servlet 4.0

The proposed final draft (PDF) of the Servlet 4.0 spec has just been made available at GitHub.

The major new feature is HTTP/2 support and specifically the push support that comes with it. Java EE already has support for push via WebSockets (including WebSocket support in JSF 2.3), but there are other interesting changes as well, such as for instance the Mapping Discovery API.

The following contains a terse list of changes, taken from section A.1 of the above linked Servlet 4.0 PFD document. All mentioned references to sections are to that document.

  1. Requirement to support HTTP/2, see Section 1.2, “What is a Servlet Container?” on page 1-1 and “What is a Servlet?” on page 1 1. This includes HTTP/2 server push, see “HTTP/2 Server Push” on page 3 29.
  2. Modify javadoc for ServletContext getAttribute() and getInitParameter(), specify that NullPointerException must be thrown if the argument “name” is null.
  3. Modify javadoc for ServletContext.setAttribute() and setInitParameter() to specify that NullPointerException must be thrown if the “name” argument is null.
  4. DeprecateHttpServletRequestWrapper.isRequestedSessionIdFromUrl().
  5. Add @Deprecated to classes and methods with @deprecated in javadoc: ServletContext, ServletRequestWrapper, SingleThreadModel, UnavailableException, HttpServletRequest, HttpServletResponse, HttpServletResponseWrapper, HttpSession, HttpSessionContext, HttpUtils.
  6. Add default-context-path in the schema of web.xml and the description in Section 30., “default-context-path Element” on page 14-180 and the figure, Section FIGURE 14-1, “web-app Element Structure”.
  7. Modify Section 7.7.1, “Threading Issues” to clarify non-thread safety of objects vended from requests and responses.
  8. Clarify metadata-complete in Section 8.1, “Annotations and pluggability”.
  9. Add default to methods in ServletContextAttributeListener, ServletContextListener, ServletRequestAttributeListener, ServletRequestListener, HttpSessionActivationListener, HttpSessionAttributeListener, HttpSessionBindingListener, HttpSessionListener.
  10. Add javax.servlet.GenericFilter and javax.servlet.http.HttpFilter
  11. Clarify the merging of in web.xml and web-fragment.xml in Section 8.2.3, “Assembling the descriptor from web.xml, web-fragment.xml and annotations”.
  12. Modify javadoc for ServletContext.getEffectiveSessionTrackingModes() without specifying the default value.
  13. Remove DTDs and Schemas from binary artifact for Servlet API.
  14. Add getSessionTimeout and setSessionTimeout in ServletContext. See javadoc, Section 4.4.4 and Section 7.5.
  15. Add addJspFile() in ServletContext. See javadoc, Section 4.4.1.4 and Section 4.4.1.7.
  16. Add request-character-encoding and response-character-encoding in the schema of web.xml. See the corresponding descriptions of the elements in Section 31. and Section 32.
  17. Add getRequestCharacterEncoding, setRequestCharacterEncoding, getResponseCharacterEncoding and setResponseCharacterEncoding in ServletContext. Update the corresponding javadoc of ServletContext, ServletRequest and ServletResponse. See Section 4.4.5, Section 3.12 and Section 5.6.
  18. Describe mapping discovery API. See Section 12.3, “Runtime Discovery of Mappings”.
  19. Update the javadoc of Registration, ServletContext, ServletRegistration for the behaviors of returned sets.
  20. Clarify the behaviors of complete and dispatch in AsyncContext before the container-initiated dispatch that called startAsync has returned to the container. See Section , “AsyncContext” on page 2-13.
  21. Clarify interpretation of fileName parameter for method Part.write(). See the javadoc for details.
  22. Clarify encoding used for reading the request URL. See Section 12.1, “Use of URL Paths” on page 12-125 for details.
  23. Specified support for HTTP trailer. See Section 5.3, “HTTP Trailer” for details. Add getTrailerFields(), isTrailerFieldsReady() in HttpServletRequest and setTrailerFields in HttpServletResponse. See the corresponding javadoc.

Tuesday, March 28, 2017

JSF 2.3 released!

After a long and at times intense spec and development process the JSF 2.3 EG is proud to announce that today we've released JSF 2.3.

JSF (JavaServer Faces), is a component based MVC framework that's part of Java EE. JSF 2.3 in particular is part of Java EE 8.

Major new features in JSF 2.3 are a tighter integration with CDI, support for WebSockets, a really cool component search expression framework (donated by PrimeFaces), basic support for extensionless URLs, and class level bean validation.

The age old native managed beans of JSF 2.3 have finally been deprecated (although they are still available for now) in favour of CDI. It's expected that these will be fully removed (pruned) in a future release.

The JSF 2.3 EG would like to thank everyone who contributed to JSF in whatever way, by creating bug reports, testing builds, providing comments and insights on the mailinglist and contributing code. Without those community contributions JSF 2.3 would not have been possible! Thanks to all our fantastic community members!

JSF 2.3 (Mojarra 2.3) can be downloaded per direct from the project's download page.

Maven coordinates for the implementation (includes API) are:

<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.faces</artifactId>
    <version>2.3.0</version>
</dependency>

The full implementation can be used for Servlet containers such as Tomcat and Jetty.

Maven coordinates for just the API are:

<dependency>
    <groupId>javax.faces</groupId>
    <artifactId>javax.faces-api</artifactId>
    <version>2.3</version>
</dependency>

The API jar can only be used as a compile time dependency.

Application servers Payara and GlassFish can be trivially updated by replacing the JSF 2.2 glassfish/modules/javax.faces.jar with the 2.3 version. It's usually a good idea to clear the OSGI cache after that (e.g. rm -rf [payara/gf gome]/ glassfish/domains/domain1/osgi-cache/felix/)

Arjan Tijms

Sunday, December 4, 2016

The state of portable authentication in Java EE, end 2016 update

In the beginning and middle of this year we looked at how well modern Java EE servers supported portable authentication (JASPIC) in Java EE. As the end of 2016 approaches we take a third look to see how things are progressing.

Since our last time new versions of all servers have been released. Payara went from 163-beta to 164, WildFly went from 10.0 to 10.1, Liberty beta went from 2016-5 to 2016-11, WebLogic went from 12.2.1 to 12.2.1.2 and TomEE went from 7.0 to 7.0.2. We also added a new server, namely Tomcat. Tomcat was indirectly already tested via TomEE, but given the importance of standalone Tomcat we decided to put this one in explicitly. Do note that Tomcat is not a full or web profile Java EE server, so the integration tests for technologies it doesn't support (like JSF, CDI, etc) are simply omitted.

Tests were added for request.authenticate, an injected CDI request, the servlet path after a forward, isMandatory in a SAM, and finally for a SAM request to be seen by a Filter. For the first time there was also a test removed, namely including a JSF based resource from a SAM. This fails on many servers but the failure appeared to be a general JSF failure unrelated to JASPIC and/or its integration with the Java EE environment.

The results of running the latest series of JASPIC tests are shown below:

Running the Java EE 7 samples JASPIC tests
Module Test Payara 164 WildFly 10.1 Liberty 2016-11 Weblogic 12.2.1.2 TomEE 7.0.2 Tomcat 8.5.6
async-authentication testBasicAsync
Passed
Passed
Passed
Passed
Passed
-
basic-authentication testProtectedPageNotLoggedin
Passed
Passed
Passed
Passed
Passed
Passed
basic-authentication testProtectedPageLoggedin
Passed
Passed
Passed
Passed
Passed
Passed
basic-authentication testPublicPageLoggedin
Passed
Passed
Passed
Passed
Passed
Passed
basic-authentication testPublicPageNotLoggedin
Passed
Passed
Passed
Passed
Passed
Passed
basic-authentication testPublicAccessIsStateless
Passed
Passed
Passed
Passed
Passed
Passed
basic-authentication testProtectedAccessIsStateless
Passed
Passed
Passed
Passed
Passed
Passed
basic-authentication testProtectedAccessIsStateless2
Passed
Passed
Passed
Passed
Passed
Passed
basic-authentication testProtectedThenPublicAccessIsStateless
Passed
Passed
Passed
Passed
Passed
Passed
custom-principal testProtectedPageLoggedin
Passed
Passed
Passed
Failure
Passed
Passed
custom-principal testPublicPageLoggedin
Passed
Passed
Passed
Failure
Passed
Passed
custom-principal testPublicAccessIsStateless
Passed
Passed
Passed
Passed
Passed
Passed
custom-principal testProtectedAccessIsStateless
Passed
Passed
Passed
Passed
Passed
Passed
custom-principal testProtectedAccessIsStateless2
Passed
Passed
Passed
Passed
Passed
Passed
custom-principal testProtectedThenPublicAccessIsStateless
Passed
Passed
Passed
Passed
Passed
Passed
dispatching testBasicForwardViaProtectedResource
Passed
Passed
Passed
Passed
Passed
Passed
dispatching testBasicForwardViaPublicResource
Passed
Passed
Passed
Passed
Passed
Passed
dispatching testBasicIncludeViaPublicResource
Passed
Passed
Passed
Passed
Passed
Passed
dispatching-jsf-cdi testCDIForwardWithRequestProtected
Passed
Passed
Passed
Passed
Passed
-
dispatching-jsf-cdi testCDIForwardWithRequestInjectPublic
Passed
Passed
Passed
Passed
Failure
-
dispatching-jsf-cdi testCDIForwardViaProtectedResource
Passed
Passed
Passed
Passed
Passed
-
dispatching-jsf-cdi testCDIForwardViaPublicResource
Passed
Passed
Passed
Passed
Passed
-
dispatching-jsf-cdi testCDIForwardWithRequestPublic
Passed
Passed
Passed
Passed
Passed
-
dispatching-jsf-cdi testCDIForwardWithRequestInjectProtected
Passed
Passed
Passed
Passed
Failure
-
dispatching-jsf-cdi testCDIIncludeViaPublicResource
Passed
Passed
Passed
Passed
Passed
-
dispatching-jsf-cdi testJSFwithCDIForwardViaPublicResource
Passed
Passed
Passed
Passed
Passed
-
dispatching-jsf-cdi testJSFwithCDIForwardViaProtectedResource
Passed
Passed
Passed
Passed
Passed
-
dispatching-jsf-cdi testJSFForwardViaPublicResource
Passed
Passed
Passed
Passed
Passed
-
dispatching-jsf-cdi testJSFForwardViaProtectedResource
Passed
Passed
Passed
Passed
Passed
-
ejb-propagation publicServletCallingProtectedEJB
Passed
Passed
Passed
Failure
Passed
-
ejb-propagation protectedServletCallingProtectedEJB
Passed
Passed
Passed
Failure
Passed
-
ejb-propagation publicServletCallingPublicEJBThenLogout
Passed
Passed
Passed
Passed
Passed
-
ejb-propagation protectedServletCallingPublicEJB
Passed
Passed
Passed
Passed
Passed
-
ejb-register-session testRemembersSession
Passed
Passed
Passed
Failure
Passed
-
ejb-register-session testRemembersSession
Passed
Passed
Passed
Failure
Passed
-
invoke-ejb-cdi protectedInvokeCDIFromSecureResponse
Passed
Passed
Failure
Failure
Passed
-
invoke-ejb-cdi protectedInvokeCDIFromCleanSubject
Passed
Passed
Passed
Passed
Passed
-
invoke-ejb-cdi protectedInvokeCDIFromValidateRequest
Passed
Passed
Failure
Failure
Passed
-
invoke-ejb-cdi publicInvokeCDIUseInjectedRequestFromValidateRequest
Passed
Passed
Failure
Failure
Passed
-
invoke-ejb-cdi publicInvokeCDIFromSecureResponse
Passed
Passed
Failure
Failure
Passed
-
invoke-ejb-cdi publicInvokeCDIFromValidateRequest
Passed
Passed
Failure
Failure
Passed
-
invoke-ejb-cdi publicInvokeCDIUseInjectedRequestFromCleanSubject
Passed
Passed
Passed
Passed
Passed
-
invoke-ejb-cdi publicInvokeCDIUseInjectedRequestFromSecureResponse
Passed
Passed
Failure
Failure
Passed
-
invoke-ejb-cdi publicInvokeCDIFromCleanSubject
Passed
Passed
Passed
Passed
Passed
-
invoke-ejb-cdi protectedInvokeEJBFromSecureResponse
Passed
Passed
Passed
Passed
Passed
-
invoke-ejb-cdi protectedInvokeEJBFromCleanSubject
Passed
Passed
Passed
Passed
Passed
-
invoke-ejb-cdi protectedInvokeEJBFromValidateRequest
Passed
Passed
Passed
Passed
Passed
-
invoke-ejb-cdi publicInvokeEJBFromSecureResponse
Passed
Passed
Passed
Passed
Passed
-
invoke-ejb-cdi publicInvokeEJBFromValidateRequest
Passed
Passed
Passed
Passed
Passed
-
invoke-ejb-cdi publicInvokeEJBFromCleanSubject
Passed
Passed
Passed
Passed
Passed
-
jacc-propagation callingJACCWhenAuthenticated
Passed
Passed
Failure
Failure
Failure
-
jacc-propagation callingJACCWhenAuthenticated
Passed
Passed
Failure
Failure
Failure
-
jacc-propagation callingJACCWhenNotAuthenticated
Passed
Passed
Failure
Failure
Failure
-
lifecycle testBasicSAMMethodsCalled
Passed
Passed
Passed
Passed
Passed
Passed
lifecycle testLogout
Passed
Passed
Passed
Passed
Passed
Passed
lifecycle testProtectedIsMandatory
Passed
Passed
Passed
Passed
Passed
Passed
lifecycle testPublicIsNonMandatory
Passed
Passed
Passed
Passed
Passed
Passed
programmatic-authentication testAuthenticateFailFirstTwice
Passed
Passed
Passed
Passed
Passed
Passed
programmatic-authentication testAuthenticate
Passed
Passed
Passed
Passed
Passed
Passed
programmatic-authentication testAuthenticateFailFirstOnce
Passed
Passed
Passed
Passed
Passed
Passed
register-session testJoinSessionIsOptional
Passed
Passed
Passed
Failure
Passed
Passed
register-session testRemembersSession
Passed
Passed
Passed
Failure
Passed
Passed
register-session testJoinSessionIsOptional
Passed
Passed
Passed
Failure
Passed
Passed
register-session testRemembersSession
Passed
Passed
Passed
Failure
Passed
Passed
status-codes test404inResponse
Passed
Passed
Passed
Passed
Passed
Passed
status-codes test404inResponse
Passed
Passed
Passed
Passed
Passed
Passed
wrapping testResponseWrapping
Passed
Passed
Passed
Passed
Passed
Passed
wrapping testDeclaredFilterResponseWrapping
Passed
Passed
Failure
Passed
Passed
Passed
wrapping testProgrammaticFilterResponseWrapping
Passed
Passed
Failure
Passed
Passed
Passed
wrapping testDeclaredFilterRequestWrapping
Passed
Passed
Failure
Passed
Passed
Passed
wrapping testProgrammaticFilterRequestWrapping
Passed
Passed
Failure
Passed
Passed
Passed
wrapping testRequestWrapping
Passed
Passed
Passed
Passed
Passed
Passed

As can be see we now have 3 perfectly scoring servers; Payara, WildFly and Tomcat now pass every test being thrown at them. All 3 do this for the first time, and the slightly older versions of each of them contain a variety of (small) bugs that prevents a perfect score.

Liberty still fails the CDI tests as before, but there's a hack available that can let Liberty pass these. Since we test as much as possible out of the box here this hack was not applied. JACC propagation also doesn't work, but this is because there's no JACC provider available by default.

Perhaps the most surprising result of this round is that WebLogic is not longer completely broken when it comes to JASPIC. While WebLogic 12.1.x had decent support for JASPIC, a major regression was introduced in 12.2.1.0 were the basic cases didn't work properly anymore. When the basic cases fail it's almost pointless to continue testing since in practice when an authentication itself doesn't work anymore it doesn't matter so much whether say request wrapping is supported or not. WebLogic does have major problems with the "register session" feature (which likely caused the regression with basic authentication in the first place), doesn't support setting a custom principal (a major feature of JASPIC), and has problems with propagating to EJB. WebLogic fails the same CDI and JACC tests as Liberty does. Contrary to Liberty, WebLogic -does- have a default JACC provider but it's not activated out of the box.

TomEE performs very well, but has a small hick up when an injected request is used in a forwarded resource. It also fails the JACC propagation, but this is because TomEE doesn't implement JACC at all for the Web module (only for EJB).

An important additional aspect not shown in the test table above is whether JASPIC works out of the box (like e.g. a Servlet Filter does), or whether it needs (server specific) activation of some sort.

In this regard, Liberty, WebLogic, TomEE and Tomcat need zero activation. There's a small caveat with Liberty and that's if you use the server specific group to role mapping in combination with a JASPIC SAM, all role checking fails. This is a somewhat peculiar incompatibility. All other servers don't care whether the groups that are set come from a portable JASPIC SAM or from something proprietary when doing the server specific/proprietary group to role mapping.

Payara and WildFly both need an activation of some kind. Payara needs a glassfish-web.xml where default group to role mapping is activated (or alternatively, where groups are actually mapped to roles), while WildFly needs a jboss-web.xml where the "jaspitest" security domain is set. WildFly should eventually lift this mandated activation when the new Elytron security system is introduced, which could possibly happen in WildFly 11. Payara could eventually default to default group to role mapping. WebLogic made this move before, but this is somewhat of a major change that should be done carefully if indeed planned.

Conclusion

Three servers, namely WildFly 10.1, Payara 164 and Tomcat 8.5.6 now pass all tests, but two of them (WildFly and Payara) need some kind of activation so are not perfect-perfect, but still very, very close to it. TomEE performs very well too, with only a minor regression and all the major core authentication functionality working perfectly.

Liberty and WebLogic have a bit more work left to be done, where as core features are concerned Liberty fails the request/response wrapping partially, while WebLogic fails the custom principal, register session and EJB propagation.

In a next article we'll be looking at what things look like after we applied the CDI/Weld hack for Liberty and Weblogic, and if JACC propagation works on them when we install/activate a JACC provider.

Arjan Tijms