Monday, July 4, 2011

Embedded Tomcat in Maven enable SSI support

As in one of my projects I needed to run a Tomcat server for later deployment of the webapp, I decided to use an embedded variant of the tomcat server in maven.

This works really fine as you can just use a maven plugin:

<build>
<finalName>project</finalName>
<outputDirectory>src/main/webapp/WEB-INF/classes</outputDirectory>
<!-- To use the plugin goals in your POM or parent POM -->
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>tomcat-maven-plugin</artifactId>
<configuration>
<mode>both</mode>
<systemProperties>
<org.apache.catalina.level>FINEST</org.apache.catalina.level>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>


Important here is to add the extra configuration with mode set to both, i.e. allowing to deploy a given context.xml.

This context.xml is needed to tell tomcat that this webapp needs to be executed in provileged mode. (thus privileged="true" )

Otherwise we would get ane xception like this:
Servlet of class org.apache.catalina.ssi.SSIServlet is privileged and cannot be loaded by this web application

So lets create a context.xml under the META-INF (if not existant create it) in the webapp directory:

context.xml
<?xml version="1.0" encoding="UTF-8"?>
<Context antiResourceLocking="false" privileged="true" useHttpOnly="true" />


Lets now configure our web.xml in order to detect .shtml files and process the SSI inside of them.

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

<display-name>app</display-name>

<!-- SSI -->

<servlet>
<servlet-name>ssi</servlet-name>
<servlet-class>org.apache.catalina.ssi.SSIServlet</servlet-class>
<init-param>
<param-name>buffered</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>1</param-value>
</init-param>
<init-param>
<param-name>expires</param-name>
<param-value>1</param-value>
</init-param>
<init-param>
<param-name>isVirtualWebappRelative</param-name>
<param-value>1</param-value>
</init-param>
<load-on-startup>4</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>ssi</servlet-name>
<url-pattern>*.shtml</url-pattern>
</servlet-mapping>

<mime-mapping>
<extension>shtml</extension>
<mime-type>text/html</mime-type>
</mime-mapping>

</web-app>


As you can see we used the SSI Servlet instead of the SSI Filter, which also could be used. Just look into the documentation of tomcat server.

Important: We need to set the mime type differently than the official documentation says. Otherwise FireFox (tested in 4/5) won't detect the document and instead trys to download it.

Thats all of it!

JPA joinTransaction Problem

When using JPA as your ORM in Java Web applications, you likely use transactions. As transaction-type you specify "RESOURCE_LOCAL" in your persistence.xml.

Suppose we have these two methods:
 public Object create(Object o){
em.getTransaction().begin();
em.persist(o);
em.getTransaction().commit();
return o;
}
and

public void delete(Object entity){
em.getTransaction().begin();
em.remove(entity);
em.getTransaction().commit();
}




The problem which occured when calling multiple times a new transaction was:

"joinTransaction has been called on a resource-local EntityManager"

That means, somehow a joined Transaction should be used, although we didn't configure JTA.
The source of this problem is, that we would call multiple times new transactions, which won't be executed after each other, but in parallel.

The solution was to set all methods in which a transaction is used to synchronized.
This leads to exact processing of the transactions in correct order.

 public synchronized Object create(Object o){
em.getTransaction().begin();
em.persist(o);
em.getTransaction().commit();
return o;
}
and

public synchronized void delete(Object entity){
em.getTransaction().begin();
em.remove(entity);
em.getTransaction().commit();
}