Preloader image

Apache Tomee pode ser executad como uma biblioteca dentro da sua propria JVM, sem a necessidade de separar processos ou instalar um servidor standalone. Nesta abordagem, incluímos as bibliotecas certas em nosso projeto e então inicializamos o TomEE usando a API Server.Builder.

Incluindo a dependência tomee-plus

Para tornar as coisas o mais fáceis possível, há apenas uma dependência que fornecerá um caminho de classe 100% idêntico à sua distribuição favorita do Apache TomEE. A seguinte dependência fornecerá a você um ambiente idêntico a uma distribuição binária Apache TomEE Plus.

<dependency>
  <groupId>org.apache.tomee.bom</groupId>
  <artifactId>tomee-plus</artifactId>
  <version>${version.tomee}</version>
</dependency>
O org.apache.tomee.bom:tomee-plus é, na verdade, gerado pela análise do apache-tomee-plus-xyz.zip, portanto, é garantido ser 100% idêntico, facilitando a transição de um arquivo zip para um maven simples dependência.

Escrevendo um código regular

Aqui a gente ve um simples API JAX-RS para enviar/receber objetos Movie como JSON.

@Path("/movies")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@RequestScoped
public class MovieService {

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

    @PostConstruct
    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));
    }
    @GET
    public List<Movie> getAllMovies() {
        return new ArrayList<>(store.values());
    }

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

}

Bootstrap TomEE com o Server Builder

Neste ponto, temos um projeto Maven com as dependências corretas e algum código de aplicação em nosso projeto. A partir daqui, usamos a API Server.Builder para construir uma instância do Server dentro de nossa JVM.

Aqui, vemos uma classe Main simples que inicializa uma instância do Server na porta 8080 e bloqueia:

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()
                .httpPort(8080)
                .add("webapps/ROOT/WEB-INF/classes", Archive.archive()
                        .add(Api.class)
                        .add(Movie.class)
                        .add(MovieService.class))
                .build();

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

O exemplo abaixo inicializa uma instância do Server em portas aleatórias dentro de um caso de teste e sai quando o caso de teste é concluído:

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

public class MovieServiceTest {

    private static URI serverURI;

    @BeforeClass
    public static void setup() {
        // Adicione as classes de que você precisa a um Archive
        // ou adicione-os a um jar por qualquer meio
        final Archive classes = Archive.archive()
                .add(Api.class)
                .add(Movie.class)
                .add(MovieService.class);

        // Coloque as classes onde você gostaria
        // eles em uma instalação do Tomcat
        final Server server = Server.builder()
                // Isso cria efetivamente um webapp chamado ROOT
                .add("webapps/ROOT/WEB-INF/classes", classes)
                .build();

        serverURI = server.getURI();
    }

    @Test
    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());
    }
}

No código acima, reunimos as classes Api,Movie e MovieService em um arquivo virtual, então adicionamos esse arquivo em uma instalação virtual do Tomcat no local webapps/ROOT/WEB-INF/classes. Quando chamamos build(), a instância do servidor Tomcat é iniciada em nossa JVM e começará a servir solicitações HTTP no host/porta identificado por server.getURI()

Resumindo, inicializamos um servidor Tomcat em nossa JVM que ocupa um espaço de disco muito pequeno; três classes e um punhado de arquivos de configuração padrão.

Executando

Se executássemos a classe principal ou caso de teste acima, veríamos uma saída como a seguinte:

Sep 03, 2020 8:41:29 AM org.apache.openejb.server.cxf.rs.CxfRsHttpListener deployApplication
INFO:      org.apache.cxf.jaxrs.validation.ValidationExceptionMapper@2d313c8c
Sep 03, 2020 8:41:29 AM org.apache.openejb.server.cxf.rs.CxfRsHttpListener logEndpoints
INFO: REST Application: http://localhost:8080/api        -> org.superbiz.movie.Api@6b2dd3df
Sep 03, 2020 8:41:29 AM org.apache.openejb.server.cxf.rs.CxfRsHttpListener logEndpoints
INFO:      Service URI: http://localhost:8080/api/movies -> Pojo org.superbiz.movie.MovieService
Sep 03, 2020 8:41:29 AM org.apache.openejb.server.cxf.rs.CxfRsHttpListener logEndpoints
INFO:               GET http://localhost:8080/api/movies ->      List<Movie> getAllMovies()
Sep 03, 2020 8:41:29 AM org.apache.openejb.server.cxf.rs.CxfRsHttpListener 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