Java EE + Tomcat = TomEE

Testing TomEE featuring Arquillian

JonathanGallimore
Arquilian300

Following on from yesterday’s tutorial, Jonathan Gallimore details how to test your TomEE application with Arquillian

Following on from yesterday’s Getting Started with TomEE tutorial, Jonathan Gallimore details how to test your newly created application, using the JVM testing platform Arquillian. He’ll also look at the JAX-RS and Webservice features of TomEE Plus.

Basic EJB test

One of the easiest ways to test an app is using the Embeddable EJBContainer API. This is pretty straightforward and is supported by all the Java EE Web Profile compliant servers. Just make sure that openejb-core is on your classpath – add it with a test scope, similar to Listing 1 if you are using Maven.

Listing 1: pom.xml dependency for openejb-core

 

  <dependency>
                <groupId>org.apache.openejb</groupId>
                <artifactId>openejb-core</artifactId>
                <version>4.5.0</version>
                <scope>test</scope>
        </dependency>

 

You can then start up the embedded EJB container in your test using this line of code:

       EJBContainer ejbContainer = EJBContainer.createEJBContainer();

This will start up the EJB container found on the classpath in memory, and will deploy any EJB beans available. EJBs can be looked up from the EJBContainer:

       Object object = ejbContainer.getContext().lookup("java:global/application-name/bean-name!interface-class");

We can use this to test the logic in the session bean using the test in Listing 2

Listing 2 - Example Embedded EJBContainer test

 

public class MoviesTest {

                @Test
                public void testShouldAddAMovie() throws Exception {
                        
                        EJBContainer ejbContainer = EJBContainer.createEJBContainer();
                        MoviesImpl movies = (MoviesImpl) ejbContainer.getContext().lookup("java:global/moviefun/MoviesBean!org.superbiz.moviefun.MoviesBean");
                        
                        Movie movie = new Movie();
                        movie.setDirector("Michael Bay");
                        movie.setGenre("Action");
                        movie.setRating(9);
                        movie.setTitle("Bad Boys");
                        movie.setYear(1995);
                        movies.addMovie(movie);

                        Assert.assertEquals(1, movies.count());
                        List<Movie> moviesFound = movies.findByTitle("Bad Boys");

                        Assert.assertEquals(1, moviesFound.size());
                        Assert.assertEquals("Michael Bay", moviesFound.get(0).getDirector());
                        // more assertions

                        ejbContainer.close();
                }

        }

 

This test will start up OpenEJB in embedded mode, and deploy all the EJBs on the classpath. It will then attempt to add a movie, retrieve the full list of movies to ensure the new movie was added to the database. Finally, once the test is complete, the test will remove all the movies from the database.

The EJBContainer API is a standard part of the Java EE Web Profile specification, so this test should also be server agnostic – changing the classpath should be all you need to do.

As an alternative to looking up the session bean using its global JNDI name, OpenEJB can also inject a reference into the test, making the test simpler and more readable. This can be done by creating a private field: @EJB private MoviesBean movies, and replacing the bean lookup with:

ejbContainer.getContext().bind("inject", this)

This technique also works for injecting CDI beans, persistence contexts and resources.

Testing the UI

Testing the EJB is all very well, but what about web frontend? TomEE takes the Embedded EJBContainer API one step further, and can start a full embedded copy of TomEE inside the unit test, with a little bit of extra setup. To make use of the feature, add the tomee-dependency to the classpath. Listing 3 shows the Maven dependency to add to pom.xml.

Listing 3 - pom.xml adding the tomee-embedded dependency

 

       <dependency>
                <groupId>org.apache.openejb</groupId>
                <artifactId>tomee-embedded</artifactId>
                <version>1.5.0</version>
                <scope>test</scope>
        </dependency>

 

In the test itself, add a method to create a copy of the application in a temporary directory. This should be like the exploded directory that gets created in the webapps/ directory when TomEE/Tomcat deploys a .war file. Then start the Embedded EJB container in the same way, but with these additional properties:

  • EJBContainer.APP_NAME – this should be the application name, and will determine the URL context for the application.
  • EJBContainer.PROVIDER – always “tomee-embedded”. This tells the container to start a full TomEE embedded container.
  • EJBContainer.MODULES – the path to the temporary directory where the application should be deployed from
  • EmbeddedTomEEContainer.TOMEE_EJBCONTAINER_HTTP_PORT – the port to use for web application.

Using “new ServerSocket(0).getLocalPort()” is a great way to select a random free port to start TomEE on – this will allow this test to be run without clashing with TomEE or any other server you might have running at the time.

The application will now run completely embedded in the test, exactly as if it were running in a TomEE server, and can be tested using a framework such as Selenium or HtmlUnit. Listing 4 shows an example using HtmlUnit.

Listing 4 - Example embedded test with HtmlUnit

 

private static File createWebApp() throws IOException {
                File file = new File(System.getProperty("java.io.tmpdir") + "/tomee-" + Math.random());
                if (!file.mkdirs() && !file.exists()) {
                        throw new RuntimeException("can't create " + file.getAbsolutePath());
                }

                FileUtils.copyDirectory(new File("target/classes"), new File(file, "WEB-INF/classes"));
                FileUtils.copyDirectory(new File("src/main/webapp"), file);

                return file;
        }

        @BeforeClass
        public static void start() throws IOException {         
                webApp = createWebApp();
                Properties p = new Properties();
                p.setProperty(EJBContainer.APP_NAME, "moviefun");
                p.setProperty(EJBContainer.PROVIDER, "tomee-embedded"); // need web feature
                p.setProperty(EJBContainer.MODULES, webApp.getAbsolutePath());
                p.setProperty(EmbeddedTomEEContainer.TOMEE_EJBCONTAINER_HTTP_PORT, String.valueOf(httpPort));
                container = EJBContainer.createEJBContainer(p);
        }

 

Arquillian test

Of course, no article that covers Java EE testing would be complete without mentioning Arquillian. Arquillian is a great testing framework, and provides a number of adapters for different Java EE servers. Arquillian and its adapters take care of all the heavy lifting of packaging up your application, starting a server, deploying your application, running your test and tearing everything down afterwards, providing a great way of writing tests that can be run directly from the IDE without needing a pre-configured server specifically setup. The same test can also be run across different application servers just by changing your settings or using a different Maven profile.

The TomEE team develop and maintain their own adapters for TomEE – we are one of the only servers that does this, with most other adapters are maintained by the Arquillian team themselves. The TomEE team are big believers in testing, and believe that the server should play a big role in making testing as easy as possible.

We provide two adapters – embedded or remote, which will either start an instance of TomEE embedded in the test, or start and deploy to a remote TomEE server respectively.

The anatomy of an Arquillian test is quite straightforward. Listing 5 shows an example.

Listing 5 – Sample Arquillian Test

 

@RunWith(Arquillian.class)
        public class MoviesArquillianHtmlUnitTest {
                @Deployment public static WebArchive createDeployment() {

                        Collection<String> dependencies = Arrays.asList(new String[] {
                                        "javax.servlet:jstl",
                                        "taglibs:standard",
                                        "commons-lang:commons-lang"
                        });

                        File[] libs = Maven.resolver()
                                .loadPomFromFile("pom.xml").resolve(dependencies)
                                .withTransitivity().asFile();

                        WebArchive war = ShrinkWrap.create(WebArchive.class, "test.war")
                                .addClasses(Movie.class, MoviesBean.class, MoviesArquillianHtmlUnitTest.class, ActionServlet.class)
                                .addAsResource(new ClassLoaderAsset("META-INF/ejb-jar.xml"), "META-INF/ejb-jar.xml")
                                .addAsResource(new ClassLoaderAsset("META-INF/persistence.xml"), "META-INF/persistence.xml")
                                .addAsLibraries(libs);

                        war.merge(ShrinkWrap.create(GenericArchive.class).as(ExplodedImporter.class)
                                        .importDirectory("src/main/webapp").as(GenericArchive.class),
                                        "/", Filters.includeAll());

                        return war;
                }

                @EJB private MoviesBean movies;

                @ArquillianResource
                private URL deploymentUrl;

                @Test
                public void testShouldMakeSureWebappIsWorking() throws Exception {
                        WebClient webClient = new WebClient();
                        HtmlPage page = webClient.getPage(deploymentUrl + "/setup.jsp");

                        assertTrue(pageAsText.contains("Wedding Crashers"));
                        // more assertions

                        page = webClient.getPage(deploymentUrl + "/moviefun");

                        assertMoviesPresent(page);
                        webClient.closeAllWindows();
                }
        }

 

The first part of the test – the createDeployment method – uses an API called ShrinkWrap to build a .war file. The great thing here is you can choose what you would like to be deployed. That might be your whole application, it might be a single bean, or it might be most of your application, but with some classes swapped out for dummy implementations. You will also notice that this example includes the test class itself in the archive as well. That’s because in this case, the test actually runs on the server, and transmits the result back to the test runner. That provides some great advantages, such as being able to inject EJBs or CDI beans into the test itself as this example shows with the @EJB private MoviesBean movies line.

The second part of the test class is the test itself. Here we have used HTML unit to exercise the moviefun application by executing the setup.jsp page to populate the database, and then scraping the main page to check that some of the movies are present. There are a number of different ways you could test the application. You could use the injected EJB to add or list the movies, or you could use the drone support that Arquillian offers to test your application with a tool such as Selenium. For more information, take a look at the moviefun example in the TomEE source code repository and the Arquillian homepage.

The line: @ArquillianResource private URL deploymentUrl is another great Arquillian feature. This field will be injected with the URL that the archive is deployed to. This avoids the need to hardcode “http://localhost:8080/test” everywhere, and hope that this was the URL the archive got deployed to. The TomEE Arquillian adapters can also be configured to use a random available port. This is done by adding properties httpPort and stopPort to an arquillian.xml file on the classpath, and setting the value for both to -1, as shown in Listing 6.

Listing 6 – arquillian.xml settings

 

<container qualifier="tomee" default="true">
            <configuration>
                <property name="httpPort">-1</property>
                <property name="stopPort">-1</property>
            </configuration>
        </container>

 

Remote EJB client

The current EJB does not define any interface, and so is treated as a “no-interface proxy”. This is can be convenient in terms of development, as any class annotated with @Stateless, @Stateful or @Singleton can be treated as an EJB, it does have the drawback that it can’t be remotely invoked. If we wanted to make the EJB remoteable, that’s easy, we can just provide an interface, annotate it with @Remote, and push the business methods up, as shown in Listing 7.

Listing 7 – Creating a @Remote Interface

 

@Remote
        public interface Movies {
                public abstract List<Movie> findRange(String field, String searchTerm, int firstResult, int maxResults);
                public abstract int count(String field, String searchTerm);
                public abstract int countAll();
                public abstract List<Movie> findRangeAll(int firstResult, int maxResults);
                public abstract List<Movie> getMovies();
                public abstract void deleteMovieId(long id);
                public abstract void deleteMovie(Movie movie);
                public abstract void editMovie(Movie movie);
                public abstract void addMovie(Movie movie);
                public abstract Movie find(Long id);
        }

 

The server also needs to provide a URL for the client to connect to. This is available in the tomee console application if it is deployed, or alternatively can a reference to the org.apache.openejb.server.httpd.ServerServlet servlet can be added to the web.xml of any application.

The bean can then be invoked remotely by including the openejb-client jar in the client project, and looking up the bean using code similar to the code below. Note that HTTP (HTTPS can also be used) is used at the network transport – this allows this remoting to take place very easily without the need to open additional network ports.

Listing 8 shows how to configure the ServerServlet and an example client.

Listing 8 - Example remote EJB client 

 

web.xml:
        <servlet>
                <servlet-name>ServerServlet</servlet-name>
                <servlet-class>org.apache.openejb.server.httpd.ServerServlet</servlet-class>
        </servlet>
        <servlet-mapping>
                <servlet-name>ServerServlet</servlet-name>
                <url-pattern>/ejb</url-pattern>
        </servlet-mapping>


Client code:
        Properties p = new Properties();
        p.setProperty(Context.INITIAL_CONTEXT_FACTORY, RemoteInitialContextFactory.class.getName());
        p.setProperty(Context.PROVIDER_URL, "http://localhost:8080/moviefun/ejb");
        
        InitialContext context = new InitialContext(p);
        MoviesRemote movies = (MoviesRemote) context.lookup("java:global/moviefun/Movies");

 

There is one important point to note here. When the MovieBean class is changed to extend to extend the Movies interface we have added a “business interface” to this class, and it will no longer act as a no-interface proxy. EJBs are only treated as no-interface proxies by default if no business interfaces are specified. That means if any injections in your code where you are using the class name, i.e.:

 

       @EJB
        private MoviesBean moviesBean;

 

will no longer work. To resolve this, you can either use one of the business interfaces to inject the bean:

 

       @EJB
       private Movies moviesBean

 

or you can add the @LocalBean annotation to the bean itself:

 

@Stateless
        @LocalBean
        public class MoviesBean implements Movies {
                ...
        }

 

You may wish to consider always using an interface, or always adding @LocalBean to your EJBs to avoid potentially breaking your code further down the line.

Webservice

The plus version of TomEE also provides support for both EJB and POJO-based webservices using the Apache CXF library. Exposing the MoviesBean class as a webservice is very straightforward, simply annotate the bean and an interface using the @WebService annotation, as shown in Listing 9.

Listing 9 – Annotating an EJB to create a webservice

@WebService
        public interface Movies {
                ...
        }


        @LocalBean
        @Stateless
        @WebService(endpointInterface = "org.superbiz.moviefun.Movies")
        public class MoviesBean implements Movies {
                ...
        }

The webservice does not need to be an EJB, however. You can create any class, implement an interface, and annotate both with @WebService. In either case, your application will be searched for @WebService annotations at deployment time, and all the webservices will be deployed for you automatically. TomEE also helpfully displays the address to the webservice on the console output when TomEE is started up: 

 

INFO: Creating Service {http://moviefun.superbiz.org/}MoviesBeanService from class org.superbiz.moviefun.Movies

Nov 19, 2012 6:22:07 PM org.apache.cxf.endpoint.ServerImpl initDestination

INFO: Setting the server's publish address to be http://nopath:80

Nov 19, 2012 6:22:07 PM org.apache.openejb.server.webservices.WsService afterApplicationCreated

INFO: Webservice(wsdl=http://localhost:8080/moviefun/webservices/MoviesBean, qname={http://moviefun.superbiz.org/}MoviesBeanService) --> Ejb(id=MoviesBean)

The WSDL for the service can be accessed by adding ?wsdl to the end of the URL.

JAX-RS

JAX-RS is a popular API for providing RESTful webservices, and combined with Javascript is a great way of creating a Rich Internet Application. The TomEE-RS and TomEE Plus distributions provide JAX-RS support without requiring any extra setup. Just like webservice support, you can use JAX-RS simply by annotating your Java classes. To apply this technique to the MoviesBean class, we can add the @Path annotation specifying the relative URL that should provide the service, and the @Produces annotation to specify the type of output that service should provide.

Each method you wish to use through JAX-RS also needs annotating with the path, any parameters, and the HTTP verbs that can be used to access that method. The root entity needs to be annotated with @XmlRootElement as well, as shown in Listing 10.

Listing 10 – Annotating moviefun for JAX-RS

Movie.java:

        @Entity
        @XmlRootElement(name = "movie")
        public class Movie implements Serializable {
                ...
        }

MoviesBean.java:

        @Stateless
        @javax.ws.rs.Path("/movies")
        @Produces(MediaType.APPLICATION_JSON)
        public class MoviesBean {

                @javax.ws.rs.Path("/find/{id}")
                @GET
                public Movie find(@PathParam("id") Long id) {
                        return entityManager.find(Movie.class, id);
                }
        }

Will produce the following JSON if you visit this URL: http://localhost:8080/moviefun/movies/find/1:

 

       {

          "movie":{

             "director":"David Dobkin",

             "genre":"Comedy",

             "id":1,

             "rating":7,

             "title":"Wedding Crashers",

             "year":2005

          }

       }

 

In conclusion

This two-part article series has shown you how to get started with TomEE, and how to use a few of the features available in the Java EE Web Profile and the JAX-RS distributions of TomEE. Its certainly possible to build applications that run in Tomcat, and add the various Java EE features that you need, either to your application, or even to your Tomcat installation. But if you’re doing this, I would strongly suggest you check out TomEE – its still the same Tomcat you know and love, but with the extra Java EE features you’ve been after, and fully Web Profile certified.

Author Bio - 

Jon is an Open Source and Java EE enthusiast working for Weatherbys Ltd in the UK. As an Open Source consumer-turned-contributor, Jon has been a committer for the Apache TomEE and OpenEJB projects for the past four years, and has worked on a number of different features including the early Tomcat 7 integration in TomEE, parts of the EJB 3.1 compliance, TomEE Arquillian adapters and the OpenEJB Eclipse Plugin. When not grinding away at work by day, and burning the midnight oil on Apache TomEE at night, Jon enjoys cooking, watching Formula One, and playing the occasional game of golf.

This article originally appeared in JAX Magazine TomEE. For that issue and others, click here.

Author

JonathanGallimore

All Posts by JonathanGallimore

Comments
comments powered by Disqus