Wednesday, January 12, 2011

Build executable war using grails, maven and jetty

I needed to build a quick and simple web application which I could run standalone. I looked around and did not find much help or grails/maven plugins which will help me build a simple web application with jetty container so I could run it standalone.
Here is simple proof of concept (poc) web application I developed using maven, grails and jetty.

grails create-app poc

Add a pom.xml to the poc project with grails, hsqldb and jetty dependencies.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mycompany</groupId>
    <artifactId>poc</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>mysqlProps - main dashboard project</name>
    <url>http://vrittis.com</url>


    <dependencies>
        <dependency>
            <groupId>org.grails</groupId>
            <artifactId>grails-crud</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.grails</groupId>
            <artifactId>grails-gorm</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.14</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty</artifactId>
            <version>6.1.14</version>
        </dependency>
        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty-util</artifactId>
            <version>6.1.14</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache-core</artifactId>
            <version>1.7.2</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.5.5</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.tmatesoft.svnkit</groupId>
            <artifactId>svnkit</artifactId>
            <version>1.2.3.5521</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.hsqldb</groupId>
            <artifactId>hsqldb</artifactId>
            <version>2.0.0</version>
        </dependency>
        <dependency>
            <groupId>opensymphony</groupId>
            <artifactId>oscache</artifactId>
            <version>2.4</version>
        </dependency>
    </dependencies>
    <build>
        <pluginManagement />
        <plugins>
            <plugin>
                <groupId>org.grails</groupId>
                <artifactId>grails-maven-plugin</artifactId>
                <version>1.1-INTEGRATE</version>
                <extensions>true</extensions>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <properties>
        <grailsHome>${env.GRAILS_HOME}</grailsHome>
    </properties>
</project>


Now you can just run
    mvn grails:war
This will generate a war file you can deploy to your choice of web container.



But, If you want to run it standalone with jetty container:
Add _Events.groovy to your scripts folder to intersect grails life cycle and perform before and after war preparation tasks
to interleave jetty stubs.
scripts/_Events.groovy
unJarTask = {jarName,includePattern ->

ant.mkdir(dir:"$stagingDir/tmp/")
ant.unjar(src:"$stagingDir/WEB-INF/lib/$jarName", dest:"$stagingDir/tmp")
ant.move(todir:"$stagingDir"){
fileset(dir:"$stagingDir/tmp"){
include (name: "$includePattern/**")
exclude (name: "META-INF/**")
}
}
ant.delete(dir:"$stagingDir/tmp")
}
eventCreateWarStart = {warName, stagingDir ->
ant.mkdir(dir:"$stagingDir/com/mycompany/")
ant.move(file:"$stagingDir/WEB-INF/classes/com/mycompany/Start.class",todir:"$stagingDir/com/mycompany/")
unJarTask("jetty-6.1.14.jar","org")
unJarTask("jetty-util-6.1.14.jar","org")
unJarTask("servlet-api-2.5-6.1.14.jar","javax")
}


eventCreateWarEnd = {warName, stagingDir ->
def libPath =""
File f = new File("$stagingDir/WEB-INF/lib")
if(f.exists()){
f.eachFile{ libPath += "WEB-INF/lib/${it.name} " }
}
ant.jar(destfile:warName, update:true) {
manifest { attribute(name: "Main-Class", value: "com.mycompany.Start")}
manifest { attribute(name: "Class-Path", value: "$libPath") }
}
}


Finally, Create a main class specified in the the manifest above com.mycompany.Start in src/java source directory.
The main class is referenced from Executable WARs with Jetty Blog from: Manuel Woelker



package com.mycompany;


import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.webapp.WebAppContext;
import java.security.ProtectionDomain;
import java.net.URL;

public class Start {

  public static void main(String[] args) throws Exception {
    Server server = new Server();
    SocketConnector connector = new SocketConnector();

    // Set some timeout options to make debugging easier.
    connector.setMaxIdleTime(1000 * 60 * 60);
    connector.setSoLingerTime(-1);
    connector.setPort(8080);
    server.setConnectors(new Connector[] { connector });

    WebAppContext context = new WebAppContext();
    context.setServer(server);
    context.setContextPath("/");

    ProtectionDomain protectionDomain = Start.class.getProtectionDomain();
    URL location = protectionDomain.getCodeSource().getLocation();
    context.setWar(location.toExternalForm());

    server.addHandler(context);
    try {
      server.start();
      System.in.read();
      server.stop();
      server.join();
    } catch (Exception e) {
      e.printStackTrace();
      System.exit(100);
    }
  }
}


Now, Run
   mvn grails:war
You should have a war file which you can just run as
   java -jar poc-0.1.war







Comments