Wednesday, January 13, 2010

Formatting source code in blogs

This is something I found useful when writing my previous blog: http://formatmysourcecode.blogspot.com

Running embedded Jetty in a Java Web Start application

There's not a lot of examples of how to run embedded Jetty in a Java Web Start application, so it took some time with trial and error to get this working. We already had a Maven project with a working web application (that could be deployed to a standard Jetty server (or the one embedded in maven-jetty-plugin). This project is called app-web.

From the working app-web web application, we needed to make a few modifications to have it successfully deploy in the web started Jetty application... First, the finished war file is packaged in a jar file, along with a ResourceAnchor class. ResourceAnchor is a minimalist class, only used to get the class loader:

public class ResourceAnchor {
    public ResourceAnchor() {
    }
}

To create the jar file, we use maven-assembly-plugin:

                        <plugin>
                                <artifactId>maven-assembly-plugin</artifactId>
                                <version>2.2-beta-4</version>
                                <configuration>
                                        <descriptors>
                                                <descriptor>src/assembly/packagedwar.xml</descriptor>
                                        </descriptors>
                                </configuration>
                                <executions>
                                        <execution>
                                                <phase>package</phase>
                                                <goals>
                                                        <goal>single</goal>
                                                </goals>
                                        </execution>
                                </executions>
                        </plugin>


It uses the packagedwar.xml descriptor:

<assembly>
    <id>packagedwar</id>
    <includeBaseDirectory>false</includeBaseDirectory>    
    <formats>
        <format>jar</format>
    </formats>
    <fileSets>
            <fileSet>
            <outputDirectory></outputDirectory>
                        <directory>target</directory>
                        <includes>
                                <include>app-web*.war</include>
                        </includes>
            </fileSet>
        <fileSet>
            <outputDirectory></outputDirectory>
            <directory>target/classes</directory>
            <includes>
                <include>com/mycorp/webassembly/ResourceAnchor.class</include>
            </includes>
        </fileSet>
    </fileSets>

</assembly>


Finally, we had to add some dependencies to app-war, so that the web app contains all that it needs (stuff that was previously found from the web container):

    <dependencies>
        <dependency> 
            <groupId>javax.servlet</groupId>  
            <artifactId>jstl</artifactId>  
            <version>1.2</version>  
        </dependency>  
        <dependency> 
            <groupId>javax.faces</groupId>  
            <artifactId>jsf-api</artifactId>  
            <version>1.2_12</version>  
        </dependency>  
        <dependency> 
            <groupId>javax.faces</groupId>  
            <artifactId>jsf-impl</artifactId>  
            <version>1.2_12</version>  
        </dependency> 
        <dependency> 
            <groupId>javax.xml</groupId>  
            <artifactId>jaxrpc-api</artifactId>  
            <version>1.1</version> 
        </dependency> 
        <dependency>
            <groupId>axis</groupId>
            <artifactId>axis</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency> 
            <groupId>commons-discovery</groupId>  
            <artifactId>commons-discovery</artifactId>  
            <version>0.2</version> 
        </dependency>  
        <dependency> 
            <groupId>wsdl4j</groupId>  
            <artifactId>wsdl4j</artifactId>  
            <version>1.5.2</version> 
        </dependency>  
        <!-- Dependencies to use log4j via commons logging -->  
        <dependency> 
            <groupId>log4j</groupId>  
            <artifactId>log4j</artifactId>  
            <version>1.2.13</version>  
            <scope>installerlib</scope> 
        </dependency>  
        <dependency> 
            <groupId>org.slf4j</groupId>  
            <artifactId>slf4j-jcl</artifactId>  
            <version>1.5.8</version>  
            <type>jar</type> 
        </dependency>  
        <dependency> 
            <groupId>org.slf4j</groupId>  
            <artifactId>slf4j-api</artifactId>  
            <version>1.5.8</version>  
            <type>jar</type> 
        </dependency>  
        <dependency> 
            <groupId>org.springframework</groupId>  
            <artifactId>spring-aop</artifactId>  
            <version>2.5.6</version> 
        </dependency>
        <dependency> 
            <groupId>org.springframework</groupId>  
            <artifactId>spring-beans</artifactId>  
            <version>2.5.6</version> 
        </dependency>
        <dependency> 
            <groupId>org.springframework</groupId>  
            <artifactId>spring-context</artifactId>  
            <version>2.5.6</version> 
        </dependency>
        <dependency> 
            <groupId>org.springframework</groupId>  
            <artifactId>spring-context-support</artifactId>  
            <version>2.5.6</version> 
        </dependency>
        <dependency> 
            <groupId>org.springframework</groupId>  
            <artifactId>spring-core</artifactId>  
            <version>2.5.6</version> 
        </dependency>
        <dependency> 
            <groupId>org.springframework</groupId>  
            <artifactId>spring-tx</artifactId>  
            <version>2.5.6</version> 
        </dependency>
        <dependency> 
            <groupId>org.springframework</groupId>  
            <artifactId>spring-web</artifactId>  
            <version>2.5.6</version> 
        </dependency>
    </dependencies>  

To deploy our web application through Java Web Start, we needed a new project that could hold the web server application. This project is called app-jnlp.

In our application, we need to read/write files and connect to other hosts, so the application must be run with all permissions. This is achieved by putting the following in the JNLP file:

<security>
   <all-permissions>
   </all-permissions>
</security>

This security setting requires all jar files to be signed, and they must all be signed with the same signature. We use Maven, so this is easily achieved with the following configuration for the webstart-maven-plugin in project app-jnlp:

<configuration>
   <sign>
      <keystore>${project.basedir}/src/main/jnlp/resources/myKeystore</keystore>
      <storepass>kspw</storepass>
      <alias>MyAlias</alias>
   </sign>
   <unsignalreadysignedjars>true</unsignalreadysignedjars>
</configuration>

Next, we needed a main class to start the application. This is the class that starts the embedded Jetty:

public class JettyStarter {

    public static void main( String[] args ) throws Exception {

        int port = 8080;

        Server server = new Server();
        SelectChannelConnector connector = new SelectChannelConnector();
        connector.setPort( port );
        server.setConnectors( new Connector[] { connector } );

        System.setSecurityManager( null );

        ClassLoader cl = ResourceAnchor.class.getClassLoader();
        String warFileName = null;
        try {
            File tmpFile = File.createTempFile( "app-web", ".war" );
            warFileName = tmpFile.getAbsolutePath();
            OutputStream warOut = new FileOutputStream( tmpFile );
            // TODO: War file name must be retrieved from somewhere (to get correct version)
            InputStream warIn = cl.getResourceAsStream( "app-web-1.1.7.war" );
            byte[] buf = new byte[1024];
            int len;
            while( (len = warIn.read( buf )) > 0 ) {
                warOut.write( buf, 0, len );
            }
            warIn.close();
            warOut.close();
        } catch( FileNotFoundException ex ) {
            // TODO: Handle exception properly
            System.err.println( ex.getMessage() + " in the specified directory." );
        } catch( IOException e ) {
            // TODO: Handle exception properly
            System.err.println( e.getMessage() );
        }

        WebAppContext webapp = new WebAppContext();
        webapp.setWar( warFileName );
        webapp.setContextPath( URIUtil.SLASH );
        WebAppClassLoader loader = new WebAppClassLoader( JettyStarter.class.getClassLoader(), webapp );
        webapp.setClassLoader( loader );

        ContextHandlerCollection contexts = new ContextHandlerCollection();
        contexts.addHandler( webapp );
        server.setHandler( contexts );

        server.start();

    }

}


A few things to notice here:
  1. We call System.setSecurityManager(null). The reason for this, is that resources (e.g. all Java classes) loaded in the web application will be loaded with another class loader than the JNLP class loader - and will not be granted access to privileged operations by the default security manager.
  2. We use a ResourceAnchor packaged along with the war file inside a jar that is delivered through JNLP. The ResourceAnchor allows us to get the class loader for that jar, which we can then use to retrieve the war file. The war file is then written to a temporary file, and passed on to Jetty.
  3. After instantiating the Jetty Server class and handing it the WebAppContext for our web app, we can start it.
In the POM for app-jnlp we have the following dependencies:

    <dependencies> 
        <dependency> 
            <groupId>com.mycorp</groupId>  
            <artifactId>app-web</artifactId>  
            <version>${pom.version}</version>
            <classifier>packagedwar</classifier> 
        </dependency>  
        <dependency> 
            <groupId>org.mortbay.jetty</groupId>  
            <artifactId>jetty</artifactId>  
            <version>6.1.19</version> 
        </dependency>
        <dependency> 
            <groupId>org.mortbay.jetty</groupId>  
            <artifactId>jetty-util</artifactId>  
            <version>6.1.19</version> 
        </dependency>
        <dependency> 
            <groupId>org.mortbay.jetty</groupId>  
            <artifactId>servlet-api</artifactId>  
            <version>2.5-20081211</version> 
        </dependency>
        <dependency> 
            <groupId>org.mortbay.jetty</groupId>  
            <artifactId>jsp-2.1-jetty</artifactId>  
            <version>6.1.19</version> 
        </dependency>
        <dependency> 
            <groupId>org.mortbay.jetty</groupId>  
            <artifactId>jsp-api-2.1-glassfish</artifactId>  
            <version>9.1.1.B60.25.p0</version> 
        </dependency>
        <dependency> 
            <groupId>org.mortbay.jetty</groupId>  
            <artifactId>jsp-2.1-glassfish</artifactId>  
            <version>9.1.1.B60.25.p0</version> 
        </dependency>
        <dependency> 
            <groupId>org.apache.ant</groupId>  
            <artifactId>ant</artifactId>  
            <version>1.7.1</version> 
        </dependency>
        <dependency> 
            <groupId>org.apache.ant</groupId>  
            <artifactId>ant-launcher</artifactId>  
            <version>1.7.1</version> 
        </dependency>
        <dependency> 
            <groupId>org.eclipse.jdt</groupId>  
            <artifactId>core</artifactId>  
            <version>3.1.1</version> 
        </dependency>
    </dependencies> 


These are all needed, let's go through them:
  1. The most notable dependency, is the first one. This is the zip file containing the ResourceAnchor and the web application. Note the packagedwar classifier, which is used to get the zip file instead of the plain war. This is a custom classifier, whose only requirement is that it must match the classifier defined in the app-web project (Maven uses this to create the file name of the artifact).
  2. The org.mortbay.jetty dependencies should be self-explanatory...
  3. Ant and JDT are needed by Jetty for JSP compilation.

Friday, March 9, 2007

My first post

Test of HTML with colors and links