Saturday, 21 January 2012

Unit tests with embeded tomcat artifacts

In the 7.x releases of Apache Tomcat, some maven artifacts are now published which include a nice and fluent embeded api to run a Tomcat instance.

So it's a nice opportunity to use it writing units to test servlets, rest api etc..
But until 7.0.25 it was only possible to do it with using a barcoding port which can cause some issues on ci servers where you are not sure ports are not used by something else running.

I have personally sended a RFC to ITEF to have port allocation for only my personal use on my birthday year or zip code port but strangely this RFC was never approved :-).

Now you can use the java ServerSocket port 0 feature to use any free port available on the machine.
It has been fixed with the issue 52028.

So now you can write a unit test as it (here a test with a REST service provided by Apache CXF).



@Before
public void startTomcat()
throws Exception
{
tomcat = new Tomcat();
tomcat.setBaseDir( System.getProperty( "java.io.tmpdir" ) );
tomcat.setPort( 0 );

Context context = tomcat.addContext( "", System.getProperty( "java.io.tmpdir" ) );

A context param in your web.xml:

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:META-INF/spring-context.xml</param-value>
</context-param>

In the code

ApplicationParameter applicationParameter = new ApplicationParameter();
applicationParameter.setName( "contextConfigLocation" );
applicationParameter.setValue( "classpath*:META-INF/spring-context.xml" );
context.addApplicationParameter( applicationParameter );

A listener class in your web.xml:

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

In the code

context.addApplicationListener( ContextLoaderListener.class.getName() );

CXF servlet declaration in your web.xml:

<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/restServices/*</url-pattern>
</servlet-mapping>

In the code:

tomcat.addServlet( context, "cxf", new CXFServlet() );
context.addServletMapping( "/restServices/*", "cxf" );

tomcat.start();

port = tomcat.getConnector().getLocalPort();

System.out.println("Tomcat started on port:"+port);
}



So now you can test/consume you REST services on localhost with the port.

Don't miss to shutdown the tomcat instance on tearDown or @After

@After
public void stopTomcat()
throws Exception
{
tomcat.stop();
}


If you use Maven you need the following dependencies:

<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<scope>test</scope>
<version>7.0.25</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-juli</artifactId>
<scope>test</scope>
<version>7.0.25</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-logging-juli</artifactId>
<scope>test</scope>
<version>7.0.25</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<scope>test</scope>
<version>7.0.25</version>
</dependency>


As samples talks more than long docs ("Code talks, bullshit walks" :-) ).
The tomcat maven archetype has been improved with a sample. (see previous post)

Have Fun!

Friday, 13 January 2012

Tomcat Maven plugin archetype. Sample talks more than long documentation :-)

As code sample talks more than long and borying documentation (or maybe because I don't like to write too long documentation :-) ), I have writen an archetype for the Apache Tomcat Maven Plugin.

Some features describe in this post are now implemented.

As it's not yet released but soon !, just use :

mvn archetype:generate \
-DarchetypeGroupId=org.apache.tomcat.maven \
-DarchetypeArtifactId=tomcat-maven-archetype \
-DarchetypeVersion=2.0-SNAPSHOT \
-DarchetypeRepository=https://repository.apache.org/content/repositories/snapshots/
....
[INFO] Using property: groupId = org.apache.tomcat.maven
Define value for property 'artifactId': : tomcat-sample (project will be created in ./tomcat-sample )
...
cd tomcat-sample


You can run your webapp with: mvn tomcat6:run or mvn tomcat7:run (depends on tomcat version you want)
And hit your browser to http://localhost:9090 and you will use a very complicated hello world webapp sample :-)

Now you can try: mvn clean install .
You will see a selenium test running (by default firefox), use -Pchrome for using chrome should work too with -Piexplore (not tested :-) ).

Note you have now an executable war.
Try it !

cd basic-webapp-exec/target/
java -jar basic-webapp-exec-1.0-SNAPSHOT-war-exec.jar -httpPort 9191


And hit your browser to http://localhost:9191.
So you have a tomcat7 running our fabulous application and without installing nothing !

More details on the project



This archetype build a simple project with some maven modules. IMHO it's nice layout to use.

basic-api (service interface)
basic-api-impl (service default impl)
basic-webapp (our webapp module)
basic-webapp-exec (module to generated executable war)
basic-webapp-it (module to run selenium tests with generated war)


The application is exposing a REST service called HelloService (in basic-api module)


@Path( "HelloService" )
public interface HelloService
{
@Path( "sayHello/{who}" )
@GET
@Produces( { MediaType.TEXT_PLAIN } )
String sayHello( @PathParam( "who" ) String who );
}


The implementation is in the module basic-api-impl.
Note we use Apache Cxf to provide REST services (for more details have a look at the various spring files).

The webapp is a simple page based on jquery and twitter bootstrap.

So have fun !