Friday, April 26, 2013

Standalone Java Webapp made easy with Maven

Introduction

As you may already know, creating a war archive and deploying it on a preinstalled Servlet Container is not the only way running a Java webapp, since you can also embed the Web-Server in the application itself. Sometimes such artifacts are delivered as standalone executable programs that can be easily started and stopped in the commandline and even run as a service in the background. Maybe you should also consider producing your app this way and benefit from its huge advantages:
  • No preinstalled Webserver/Servlet Container required that would take additional maintenance.
  • Easy Cross-Platform. Same artifact can of course be run on any System.
  • Complete Standalone Software
Apache Sonar is a great Example of such Software. It uses an embedded Jetty Server and creates an embedded H2 Database for persistence that can be replaced in configuration. As you can see here they prepared startup scripts for any Platform that execute the actual StartServer class.

Embedded Tomcat

In my tutorial ill use an embedded Tomcat as a Servlet Container. So we want to add the Maven dependencies first.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<!-- embedded Tomcat -->
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-core</artifactId>
    <version>7.0.37</version>
</dependency>
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-logging-log4j</artifactId>
    <version>7.0.37</version>
</dependency>
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
    <version>7.0.37</version>
</dependency>

We can use them now to create a StartServer class

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public final class StartServer {

    private StartServer() {
    }

    public static void main(String[] args) throws Exception {
        
        // We will set the basedir Systemproperty accordingly when running the Program
        // Actually the mavenplugin appassembler will do this for us
        String basedir = System.getProperty("basedir");
        String webappLocation = new File(basedir + "/webapp").getAbsolutePath();
        int port = 8080;

        Tomcat tomcat = new Tomcat();
        tomcat.setPort(port);
        tomcat.addWebapp("/", webappLocation);
        tomcat.start();
        tomcat.getServer().await();
    }
}

Exploded War

Now we need to make an exploded war in our "target" directory. I choose to create a folder inside "target" that holds the application as a whole including bin files, configuration and the webapp itself. This makes it easy to transport the complete app to another machine.
  • src/...
  • target/myapp/bin
  • target/myapp/conf
  • target/myapp/webapp
Ill include the maven-war-plugin to create the exploded war.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.3</version>
    <executions>
        <execution>
            <id>war-exploded</id>
            <phase>package</phase>
            <goals>
                <goal>exploded</goal>
            </goals>
            <configuration>
                <webappDirectory>${project.build.directory}/myapp/webapp</webappDirectory>
                <archiveClasses>true</archiveClasses>
            </configuration>
        </execution>
    </executions>
</plugin>

If you run mvn clean package right now you should get your webapp compiled into target/myapp/webapp

Startup Scripts

Finally we need startup Scripts to actually run our application. We need a shell script for unix Systems and a bat file for Windows. The Appassembler maven plugin comes in handy here. It is a very thoughtout plugin that creates exactly what we need and even sets the basedir Systemproperty. I'll configure it to use our exploded webapp and StartServer class.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>appassembler-maven-plugin</artifactId>
    <version>1.3</version>
    <configuration>
        <assembleDirectory>${project.build.directory}/myapp</assembleDirectory>
        <repositoryLayout>flat</repositoryLayout>
        <repositoryName>webapp/WEB-INF/lib</repositoryName>
        <generateRepository>false</generateRepository>
        <copyConfigurationDirectory>true</copyConfigurationDirectory>
        <configurationDirectory>conf</configurationDirectory>
        <programs>
            <program>
                <mainClass>com.myapp.StartServer</mainClass>
                <name>myapp</name>
            </program>
        </programs>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>assemble</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Done. Appassembler now creates executable scripts for all platforms inside the target/myapp/bin directory. You could even put configuration files into src/main/config that would be copied into target/myapp/conf automatically by the plugin.

Result


If we run mvn clean package now we get a completely transportable standalone webapp in our target directory!

Wednesday, April 10, 2013

I18n of JPA Entities

Sadly the support for i18n for Entities in JPA is actually very poor to non-existent. There's just basically no out of the box solution provided by Hibernate & Co, although i18n is a feature that could be considered mandatory for a powerful ORM Framework. If you take a look at PHP for example Doctrine supports i18n very well. So besides some really unpopular i18n extensions on forums/github you are basically on your own.

So how do you do it?

There isn't really a general solution to achieve i18n. There are many different ways and you have to choose which is actually best for you. If you do have a relatively small amount of data and ordering by translations is not necessary you can store "keys" in your tables that are being translated by Spring i18n for example. Spring i18n could store the translations in separate property files. Since you wont have to worry about performance, this is a very easy way to achieve i18n.

But lets say you have lots of data, and you need to order it on the database side. With such requirements you are better off storing translations inside your database tables. While there are guides, and also an opensource framework on the internet that implement i18n with one centralized translation-table that holds all translations i highly recommend not to follow that idea. The problem with this solution is, that you will have to add one more join for every field that has to be translated, whereas multiple joins for a single table with lots of data can easily become a performanceissue.
Its better to follow the approach to create a separate translation-table for every entity. This means that you would have one entity containing simple data, and another one containing only the fields that require translation. Like so:

This schema is pretty normalized and you only have a single join on fetching an entity, while you can still order appropriate.

Your entities would look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@Entity
public class Book {

    @Id
    private int isbn;

    private String author;

    private Date releaseDate;

    @OneToMany
    @JoinColumn(name = "isbn")
    private List<BookTranslation> translations;

    // constructor, getters and setters -...

}

and that

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Entity
public class BookTranslation implements Serializable {

    @Id
    private int isbn;

    @Id
    private String locale;

    private String title;

    private String description;

    // constructor, getters and setters -...

}

You could then fetch a book with its translations with:

select b from Book b join fetch b.translations bt where bt.locale = 'de';

If you cannot accept your ServiceLayer to deliver two entities for every Book only because of translations, i suggest you to implement some DTO like this BookTO which holds all values.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class BookTO {

    private int isbn;

    private String author;

    private Date releaseDate;

    private String title;

    private String description;

}

You'd just need to map the values of Book and BookTranslation to BookTO within your ServiceLayer and also return BookTO instead of Book in your ServiceLayer.

This solution might not always be the most appropriate, but it definitely comes in handy sometimes.