- Jetty for web server
- Jersey for REST
- Jackson for JSON
- Swagger for API documentation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | 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.
No comments:
Post a Comment