Apache CXF JBoss 4.2 deployment – tips and tricks

If you are not able to deploy an application using Apache CXF library in JBoss, you might want to check first Application Server Specific Configuration Guide chapter of CXF user’s guide.

Furthermore, check if the JBossWS framework is already deployed (jbossws.sar directory in the JBoss deployment directory) and try to remove it. It contains already a lot of APIs and implementations that can confict with APIs and implementations provided with CXF.

Concrete example – I was using CXF 2.0 series which implements JAX-WS 2.0 standard. JBossWS 2.0 framework, shipped with JBoss 4.2, implements JAX-WS 2.1 specification. In some situations when a dateTime was parsed I was getting NullPointerException with a message about marshalling error. Removing the jbossws.sar helped.

Integrating Apache Beehive authentication with JBoss security

Each Beehive page flow controller implementation inherits from org.apache.beehive.netui.pageflow.FlowController two methods related to authentication:

void login(String username, String password)
void logout(boolean invalidateSessions)

You typically call these methods in actions resulting from a login form or logout link/button. They use a servlet container adapter interface (org.apache.beehive.netui.pageflow.ServletContainerAdapter) which is a glue between the generic authentication mechanism and an underlying application server. Beehive distribution comes with a org.apache.beehive.netui.pageflow.DefaultServletContainerAdapter implementation whose login() and logout() methods throw UnsupportedOperationException. You have to write your own ServletContainerAdapter implementation for the application server you are using.

If you want to connect Beehive security with a JBoss security domain, you need JBoss at least in 4.2.0.GA version. Starting from this release JBoss ships with a WebAuthentication class. The class allows a programmatic web login.

First what you need to do is to configure a JBoss security. You can do it in 3 easy steps:

1. Create user and role files in $JBOSS_HOME/server/default/conf/props.

test-users.properties:

jboss=jboss

test-roles.properties:

test=jboss

2. Define a simple security domain; create a test-login-config-service.xml file in $JBOSS_HOME/server/default/deploy. Doing it in this way you don’t have to modify any JBoss configuration file.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE server>
<server>
  <mbean code="org.jboss.security.auth.login.DynamicLoginConfig"
    name="jboss.security:service=TestDynamicLoginConfig">
    <attribute name="PolicyConfig" serialDataType="jbxb">
      <jaas:policy
        xsi:schemaLocation="urn:jboss:security-config:4.1 resource:security-config_4_1.xsd"
        xmlns:jaas="urn:jboss:security-config:4.1"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <jaas:application-policy name="test-realm">
          <jaas:authentication>
            <jaas:login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule" flag="required">
              <jaas:module-option name="usersProperties">../conf/props/test-users.properties</jaas:module-option>
              <jaas:module-option name="rolesProperties">../conf/props/test-roles.properties</jaas:module-option>
            </jaas:login-module>
          </jaas:authentication>
        </jaas:application-policy>
      </jaas:policy>
    </attribute>
    <depends optional-attribute-name="LoginConfigService">
      jboss.security:service=XMLLoginConfig
    </depends>
    <depends optional-attribute-name="SecurityManagerService">
      jboss.security:service=JaasSecurityManager
    </depends>
 </mbean>
</server>

3. In your web application create a jboss-web.xml file in WEB-INF directory:

<jboss-web>
  <security-domain>java:/jaas/test-realm</security-domain>
</jboss-web>

That’s all, now you should have a BASIC HTTP authentication working. If you want to test it, add following lines to web.xml, restart the application and check if a login window pop up in the browser.

<security-constraint>
    <web-resource-collection>
        <web-resource-name>all</web-resource-name>
        <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>test</role-name>
    </auth-constraint>
</security-constraint>

<security-role>
    <role-name>test</role-name>
</security-role>

<login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>test-realm</realm-name>
</login-config>

Let’s follow then the Beehive manual chapter Writing a Servlet Container Adapter and go through steps required to implement an adapter for JBoss server:

1. Beehive manual:

Write a class that implements org.apache.beehive.netui.pageflow.ServletContainerAdapter. You can extend org.apache.beehive.netui.pageflow.DefaultServletContainerAdapter

Here is the class definition:

package net.mgr.jboss;

import javax.security.auth.login.LoginException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.beehive.netui.pageflow.DefaultServletContainerAdapter;
import org.apache.beehive.netui.pageflow.adapter.AdapterContext;
import org.jboss.web.tomcat.security.login.WebAuthentication;

public class JBossServletContainerAdapter extends DefaultServletContainerAdapter {

    /**
     * This method must always return true, so Beehive will use this adapter.
     */
    public boolean accept(AdapterContext aContext) {
        return true;
    }

    /**
     * @see org.apache.beehive.netui.pageflow.DefaultServletContainerAdapter#login(java.lang.String,
     *      java.lang.String, javax.servlet.http.HttpServletRequest,
     *      javax.servlet.http.HttpServletResponse)
     */
    @Override
    public void login(String username, String password, HttpServletRequest request,
        HttpServletResponse response) throws LoginException {

        WebAuthentication webAuth = new WebAuthentication();
        boolean loginOk = webAuth.login(username, password);
        if (!loginOk ) {
            throw new LoginException("Login failed");
        }

        System.out.println("Login ok, user principal: " + request.getUserPrincipal());
    }

    /**
     * invalidateSessions is used to invalidate a session on all sigle sign-on applications;
     * in this adapter is ignored - there is no possibility to achieve this with WebAuthentication.
     *
     * @see org.apache.beehive.netui.pageflow.DefaultServletContainerAdapter#logout(boolean,
     *      javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
     */
    @Override
    public void logout(boolean invalidateSessions, HttpServletRequest request,
        HttpServletResponse response) {

        WebAuthentication webAuth = new WebAuthentication();
        webAuth .logout();

        System.out.println("Logout ok, user principal: " + request.getUserPrincipal());
    }
}

2. Beehive manual:

Package the class in a JAR file which also contains a text file in directory META-INF/services called org.apache.beehive.netui.pageflow.ServletContainerAdapter. The text file should have a single line that is the full class name of your ServletContainerAdapter

Here is the file contents:

net.mgr.JBossServletContainerAdapter

3. Beehive manual:

Drop the JAR into the WEB-INF/lib directory of your web application. The NetUI runtime will automatically pick up the first “discovered” adapter that answers true when its accept method is called.

accept method of our servlet container adapter returns true, so it will be used by Beehive.

The final step is to create a fake security constraint in web.xml. There is a bug in JBoss that prevents persisting the security principal between two requests without any security constraint defined. Just add to web.xml:

<security-constraint>
    <web-resource-colection>
        <web-resource-name>fake</web-resource-name>
        <url-pattern>/fake/*</url-pattern>
    </web-resource-collection>
</security-constraint>

Links:

jawr configuration location settings in JBoss 4.0 and 4.2

I use jawr in my current project. jawr is a Java library for bundling and compressing JavaScript and CSS files.

The original jawr servlet configuration (taken from the online documentation) was working fine in Weblogic 10 and in JBoss 4.0.5 GA:

<servlet>
    <servlet-name>JavascriptServlet</servlet-name>
    <servlet-class>net.jawr.web.servlet.JawrServlet</servlet-class>
    <init-param>
        <param-name>configLocation</param-name>
        <param-value>/jawr.properties</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

After switching to JBoss 4.2.2 GA, I was getting a NullPointerException when the JawrServlet was starting. Lines 56 and 57 from the PropsFilePropertiesSource class were causing the exception:

URL url = Thread.currentThread().getContextClassLoader().getResource(configLocation);
InputStream is = new FileInputStream(new File(url.getFile()));

The cause to this exception is the classloader of JBoss, which doesn’t resolve the root to the WEB-INF/classes or WEB-INF/lib. The solution is easy – remove the leading slash from location of the jawr configuration file in the jawr servlet configuration:

<servlet>
    <servlet-name>JavascriptServlet</servlet-name>
    <servlet-class>net.jawr.web.servlet.JawrServlet</servlet-class>
    <init-param>
        <param-name>configLocation</param-name>
        <param-value>jawr.properties</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>