blob: c1bdbd09c5931d4d4305e19dd9f574afe11b959e [file] [log] [blame]
:index-group: Serverless
:jbake-type: page
:jbake-status: status=published
= Serverless TomEE WebProfile
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-webprofile` 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 WebProfile binary distribution.
[source,xml]
----
<dependency>
<groupId>org.apache.tomee.bom</groupId>
<artifactId>tomee-webprofile</artifactId>
<version>${version.tomee}</version>
</dependency>
----
NOTE: The `org.apache.tomee.bom:tomee-webprofile` is actually generated by analyzing the apache-tomee-webprofile-x.y.z.zip, 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.
[source,java]
----
@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 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:
[source,java]
----
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();
}
}
----
The example below bootstraps a `Server` instance on random ports inside a test case and exits when the test case is complete:
[source,java]
----
import org.apache.tomee.bootstrap.Archive;
import org.apache.tomee.bootstrap.Server;
//...
public class MovieServiceTest {
private static URI serverURI;
@BeforeClass
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()
.add(Api.class)
.add(Movie.class)
.add(MovieService.class);
// 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)
.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());
}
}
----
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.
== Running
Were we to run the above Main class or Test Case we'd see output like the following:
[source,bash]
----
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
----