- Jetty for web server
- Jersey for REST
- Jackson for JSON
- Swagger for API documentation
plugins { id "com.github.johnrengelman.shadow" version "2.0.1" id "java" } apply plugin: "java" apply plugin: "com.github.johnrengelman.shadow" defaultTasks = ["clean", "build", "shadowJar"] repositories { jcenter() } dependencies { // Jetty dependencies compile "org.eclipse.jetty:jetty-server:9.4.6.v20170531" compile "org.eclipse.jetty:jetty-servlet:9.4.6.v20170531" // Jersey dependencies compile "org.glassfish.jersey.core:jersey-server:2.25.1" compile "org.glassfish.jersey.media:jersey-media-json-jackson:2.25.1" compile "org.glassfish.jersey.containers:jersey-container-servlet:2.25.1" // Swagger dependencies compile "io.swagger:swagger-core:1.5.15" compile "io.swagger:swagger-annotations:1.5.15" compile "io.swagger:swagger-jersey2-jaxrs:1.5.15" // Required by BeanConfig runtime "org.codehaus.groovy:groovy:2.4.12" // Required by Swagger runtime "ch.qos.logback:logback-core:1.2.3" runtime "ch.qos.logback:logback-classic:1.2.3" } jar { manifest { attributes "Main-Class": "org.fredy.example.rest.HelloServer" } }
Second, we will write a typical JAX-RS application with JAX-RS, Jackson, and Swagger annotations.
package org.fredy.example.rest; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.annotations.Api; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiResponse; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @Api(value = "/hello", description = "API for Hello") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Path("/hello") public class HelloResource { @ApiModel("request") public static class HelloRequest { @ApiModelProperty(value = "The name", required = true) private final String name; @JsonCreator public HelloRequest(@JsonProperty("name") String name) { this.name = name; } public String getName() { return name; } } @ApiModel("response") public static class HelloResponse { @ApiModelProperty(value = "The message", required = true) private final String message; @JsonCreator public HelloResponse(@JsonProperty("message") String message) { this.message = message; } public String getMessage() { return message; } } @ApiOperation(value = "Gets the message", response = HelloResponse.class) @ApiResponse(code = 200, message = "Successful operation") @Path("/message") @POST public Response getMessage(HelloRequest request) { return Response.ok(new HelloResponse("Hello, " + request.getName())).build(); } }
Third, we will need to register our REST resource into Jetty and start the server.
package org.fredy.example.rest; import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; import io.swagger.jaxrs.config.BeanConfig; import io.swagger.jaxrs.listing.ApiListingResource; import io.swagger.jaxrs.listing.SwaggerSerializers; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.server.handler.ResourceHandler; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.resource.Resource; import org.glassfish.jersey.server.ServerProperties; import org.glassfish.jersey.servlet.ServletContainer; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import java.util.StringJoiner; public class HelloServer { public static class Bootstrap extends HttpServlet { @Override public void init(ServletConfig config) throws ServletException { super.init(config); BeanConfig beanConfig = new BeanConfig(); beanConfig.setTitle("Hello API"); beanConfig.setVersion("1.0.0"); beanConfig.setSchemes(new String[]{"http"}); beanConfig.setBasePath("/api"); beanConfig.setResourcePackage("org.fredy.example.rest"); beanConfig.setScan(true); } } public static void main(String[] args) throws Exception { ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); servletHandler.setContextPath("/"); ServletHolder jerseyServlet = servletHandler.addServlet(ServletContainer.class, "/api/*"); jerseyServlet.setInitOrder(0); jerseyServlet.setInitParameter( ServerProperties.PROVIDER_CLASSNAMES, new StringJoiner(",") .add(HelloResource.class.getCanonicalName()) .add(JacksonJsonProvider.class.getCanonicalName()) .add(ApiListingResource.class.getCanonicalName()) .add(SwaggerSerializers.class.getCanonicalName()) .toString()); ResourceHandler staticHandler = new ResourceHandler(); staticHandler.setWelcomeFiles(new String[]{"index.html"}); staticHandler.setBaseResource(Resource.newClassPathResource("/webapp")); HandlerList handlers = new HandlerList(); // Add two handlers, one for static content and the other one for dynamic content. handlers.setHandlers(new Handler[]{staticHandler, servletHandler}); // The mapping doesn't really matter here. ServletHolder swaggerServlet = servletHandler.addServlet(Bootstrap.class, "/"); swaggerServlet.setInitOrder(2); Server jettyServer = new Server(8080); jettyServer.setHandler(handlers); try { jettyServer.start(); jettyServer.join(); } finally { jettyServer.destroy(); } } }
Few important things to note.
- Add the Jersey servlet container (org.glassfish.jersey.servlet.ServletContainer). This servlet implementation comes from Jersey library.
- Register some providers, such as our own REST provider (org.fredy.example.rest.HelloResource), Jackson provider (com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider), and Swagger providers (io.swagger.jaxrs.listing.ApiListingResource and io.swagger.jaxrs.listing.SwaggerSerializers). The Swagger providers will create swagger.json specified by the servlet mapping path, which is api/swagger.json in this case.
Lastly, we need to add some Swagger UI so that we can display the API documentation in a nice format. What we need is basically download Swagger UI from https://github.com/swagger-api/swagger-ui and copy the dist directory into src/main/resources. The goal of this is to keep the Swagger UI in the JAR and is available in the classpath. Swagger UI is just a static content and we can tell Jetty to serve static content by using org.eclipse.jetty.server.handler.ResourceHandler. Make sure to modify the URL in Swagger's index.html from http://petstore.swagger.io/v2/swagger.json to http://localhost:8080/api/swagger.json since that's where our swagger.json file lives. To build and run the example, we can type the following command.
./gradlew && java -jar build/libs/java-rest-swagger-example-all.jarFull source code can be found here.