Preloader image

Apache TomEE can be run as a library inside your JVM with no need for separate processes or a standalone server install. In this approach we include the right libraries in our project and then bootstrap TomEE using the Server.Builder API.

Include the tomee-plus dependency

To make things as easy as possible there is just one dependency that will give you a classpath that is 100% identical to your favorite Apache TomEE distribution. The following dependency will pull give you an environment identical to an Apache TomEE Plus binary distribution.

The is actually generated by analyzing the, so is guaranteed to be 100% identical making it easy to transition from a zip file to a simple maven dependency.

Write Regular Code

Here we see a simple JAX-RS API for sending/recieving Movie objects as JSON.

public class MovieService {

    private Map<Integer, Movie> store = new ConcurrentHashMap<>();

    public void construct(){
        this.addMovie(new Movie("Wedding Crashers", "David Dobkin", "Comedy", 1, 2005));
        this.addMovie(new Movie("Starsky & Hutch", "Todd Phillips", "Action", 2, 2004));
        this.addMovie(new Movie("Shanghai Knights", "David Dobkin", "Action", 3, 2003));
        this.addMovie(new Movie("I-Spy", "Betty Thomas", "Adventure", 4, 2002));
        this.addMovie(new Movie("The Royal Tenenbaums", "Wes Anderson", "Comedy", 5, 2001));
        this.addMovie(new Movie("Zoolander", "Ben Stiller", "Comedy", 6, 2001));
    public List<Movie> getAllMovies() {
        return new ArrayList<>(store.values());

    public Movie addMovie(final Movie newMovie) {
        store.put(newMovie.getId(), newMovie);
        return newMovie;


Bootstrap TomEE with the Server Builder

A this point we have a Maven project with the right dependencies and some application code in our project. From here we use the Server.Builder API to construct a Server instance inside our JVM.

Here we see a simple Main class that bootstraps a Server instance on port 8080 and blocks:

import org.apache.tomee.bootstrap.Archive;
import org.apache.tomee.bootstrap.Server;

import java.util.concurrent.Semaphore;

public class Main {
    public static void main(String[] args) throws InterruptedException {

        final Server server = Server.builder()
                .add("webapps/ROOT/WEB-INF/classes", Archive.archive()

        System.out.println("Listening for requests at " + server.getURI());
        new Semaphore(0).acquire();

The example below bootstraps a Server instance on random ports inside a test case and exits when the test case is complete:

import org.apache.tomee.bootstrap.Archive;
import org.apache.tomee.bootstrap.Server;

public class MovieServiceTest {

    private static URI serverURI;

    public static void setup() {
        // Add any classes you need to an Archive
        // or add them to a jar via any means
        final Archive classes = Archive.archive()

        // Place the classes where you would want
        // them in a Tomcat install
        final Server server = Server.builder()
                // This effectively creates a webapp called ROOT
                .add("webapps/ROOT/WEB-INF/classes", classes)

        serverURI = server.getURI();

    public void getAllMovies() {
        final WebTarget target = ClientBuilder.newClient().target(serverURI);

        final Movie[] movies = target.path("/api/movies").request().get(Movie[].class);

        assertEquals(6, movies.length);

        final Movie movie = movies[1];
        assertEquals("Todd Phillips", movie.getDirector());
        assertEquals("Starsky & Hutch", movie.getTitle());
        assertEquals("Action", movie.getGenre());
        assertEquals(2004, movie.getYear());
        assertEquals(2, movie.getId());

In the above code we have assembled the classes Api, Movie and MovieService into a virtual archive, then we add that archive into a virtual Tomcat install at the location webapps/ROOT/WEB-INF/classes. When we call build() the Tomcat server instance is started in our JVM and will begin serving HTTP requests at the host/port identified by server.getURI()

In short, we’ve bootstrapped a Tomcat server in our JVM that has a very tiny disk footprint; three classes and a handful of default configuration files.


Were we to run the above Main class or Test Case we’d see output like the following:

Sep 03, 2020 8:41:29 AM deployApplication
INFO:      org.apache.cxf.jaxrs.validation.ValidationExceptionMapper@2d313c8c
Sep 03, 2020 8:41:29 AM logEndpoints
INFO: REST Application: http://localhost:8080/api        ->
Sep 03, 2020 8:41:29 AM logEndpoints
INFO:      Service URI: http://localhost:8080/api/movies -> Pojo
Sep 03, 2020 8:41:29 AM logEndpoints
INFO:               GET http://localhost:8080/api/movies ->      List<Movie> getAllMovies()
Sep 03, 2020 8:41:29 AM logEndpoints
INFO:              POST http://localhost:8080/api/movies ->      Movie addMovie(Movie)
Sep 03, 2020 8:41:29 AM jdk.internal.reflect.DelegatingMethodAccessorImpl invoke
INFO: Deployment of web application directory [/private/var/folders/bd/f9ntqy1m8xj_fs006s6crtjh0000gn/T/temp14966428831095231081dir/apache-tomee/webapps/ROOT] has finished in [1,798] ms
Sep 03, 2020 8:41:29 AM jdk.internal.reflect.DelegatingMethodAccessorImpl invoke
INFO: Starting ProtocolHandler ["http-nio-8080"]
Sep 03, 2020 8:41:29 AM jdk.internal.reflect.DelegatingMethodAccessorImpl invoke
INFO: Server startup in [1877] milliseconds
Sep 03, 2020 8:41:29 AM jdk.internal.reflect.DelegatingMethodAccessorImpl invoke
INFO: Full bootstrap in [3545] milliseconds
Listening for requests at http://localhost:8080