Skip to content

Instantly share code, notes, and snippets.

@nosmokingpistol
Last active November 8, 2023 11:13
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save nosmokingpistol/302c4c3ef30f183cf70e to your computer and use it in GitHub Desktop.
Save nosmokingpistol/302c4c3ef30f183cf70e to your computer and use it in GitHub Desktop.
Setup Swagger with an embedded Jetty Server

Swagger Setup for Embedded Jetty Server

In setting up a Jetty server with Jersey servlets, you may choose to use an embedded Jetty server setup. (See here for how to setup an embedded Jetty server). In this gist, we'll go through how to setup Swagger for this setup. I am using Swagger 1.5, Maven 3.3.3, Jersey 1.8, and Jetty 7.3. Make sure you add all dependencies to your pom.xml.

In the Swagger Core setup, the current official recommendations involve an Application class, or a web.xml, neither of which are used in an embedded Jetty server setup. To add Swagger to your embedded Jetty Server, you must do 3 things:

  1. Add the package scanning packages to your servlets which will serve your REST API.
  2. Add the Swagger package scanning servlet.
  3. Package the Swagger-UI static HTML5 content with your server

Let's start with a basic server class, adapted from the Eclipse tutorial above.

public class MyServer {
  public static void main(String[] args) throws Exception
  {
      Server server = new Server(8080);
      ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
      context.setContextPath("/");
      server.setHandler(context);
      
      // Setup API resources
      ServletHolder apiServlet = context.addServlet(ServletContainer.class, "/api/*");
      apiServlet.setInitOrder(1);
      apiServlet.setInitParameter("com.sun.jersey.config.property.packages", "com.api.resources");
      
      server.start();
      server.join();
  }
}

Our package setup is very minimal:

  • src
    • main
      • java
        • MyServer.java
      • resources

In this example, the servlet serving the API is initialized as a ServletContainer and has its values instantiated in-code. However, if you choose to make your own servlet class, then you will need to set parameters in that class instead, and you can then add a servlet like so:

ServletHolder apiServlet = context.addServlet(new MyServlet(), "/path/*");

##Add the package scanning packages to your servlets which will serve your REST API## First, to add the Swagger package scanning classes. Usually one would add them to the web.xml like this. In your embedded Jetty servlet setup, you are setting the packages you are serving via the "com.sun.jersey.config.property.packages" property. So, add the io.swagger.jaxrs.json;io.swagger.jaxrs.listing packages to your servlet setup, like so:

public class MyServer {
  public static void main(String[] args) throws Exception
  {
      Server server = new Server(8080);
      ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
      context.setContextPath("/");
      server.setHandler(context);
      
      ServletHolder apiServlet = context.addServlet(ServletContainer.class, "/api/*");
      apiServlet.setInitOrder(1);
      apiServlet.setInitParameter("com.sun.jersey.config.property.packages", "com.api.resources;io.swagger.jaxrs.json;io.swagger.jaxrs.listing");
      
      server.start();
      server.join();
  }
}

##Add the Swagger package scanning servlet## Now, to configure and initialize Swagger. Based on these instructions, you'll now manually create and add the Swagger servlet. The path provided for the Swagger servlet is irrelevant, as it is not actually accessed through this path. However, make sure it does not collide with any of your other resource paths!

public class MyServer {
  public static void main(String[] args) throws Exception
  {
      Server server = new Server(8080);
      ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
      context.setContextPath("/");
      server.setHandler(context);
      
      // Setup API resources
      ServletHolder apiServlet = context.addServlet(ServletContainer.class, "/api/*");
      apiServlet.setInitOrder(1);
      apiServlet.setInitParameter("com.sun.jersey.config.property.packages", "com.api.resources;io.swagger.jaxrs.json;io.swagger.jaxrs.listing");
      
      // Setup Swagger servlet
      ServletHolder swaggerServlet = context.addServlet(DefaultJaxrsConfig.class, "/swagger-core");
      swaggerServlet.setInitOrder(2);
      swaggerServlet.setInitParameter("api.version", "1.0.0");
    
      server.start();
      server.join();
  }
}

Now, when you navigate to http://<host>:8080/api/swagger.json, Swagger will generate a JSON response detailing your resource paths, their parameters, and their responses.

##Package the Swagger-UI static HTML5 content with your server## However, most people prefer a more human readable method -- so we will now package the Swagger-UI HTML5 project with your server.

To your pom.xml, you need to add the ability to download and and then copy the Swagger-UI static resources to your project's resources folder. That way when you package and run your server eventually via java -cp <libs> MyServer.class, it will be able to display the Swagger UI at http:://<host>:8080/.

Similar to here, we will add the following lines to our pom.xml, in the <build></build> portion:

    <!-- Download the Swagger-UI project -->
    <build>
        <plugins>
        ...
            <plugin>
                <groupId>com.googlecode.maven-download-plugin</groupId>
                <artifactId>download-maven-plugin</artifactId>
                <version>1.2.1</version>
                <executions>
                    <execution>
                        <id>swagger-ui</id>
                        <phase>validate</phase>
                        <goals>
                            <goal>wget</goal>
                        </goals>
                        <configuration>
                            <url>https://github.com/swagger-api/swagger-ui/archive/v2.1.1.tar.gz</url>
                            <unpack>true</unpack>
                            <outputDirectory>${project.build.directory}</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <!-- Load the Swagger-UI components into the src/main/webapp/ directory to enable
            viewing/testing of the API routes. Accessible at http://<host>:<port>/swagger. -->
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.7</version>
                <executions>
                    <execution>
                        <id>copy-resources</id>
                        <phase>initialize</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${basedir}/src/main/resources/webapp</outputDirectory>
                            <resources>
                                <resource>
                                    <directory>${project.build.directory}/swagger-ui-2.1.1/dist</directory>
                                </resource>
                            </resources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

When you run mvn clean package on your project, this will first download the Swagger-UI into target folder. Then, the dist folder of the Swagger-UI project is copied into the /resources/webapp folder of your main project. When compiled, the webapp folder will then appear in target/classes, and be accessible to your compiled and running server JAR.

Now, you have to point your ServletContextHandler to these static resources. Begin by using your MyServer class to find the resource, then set the welcome file to be the name of the desired HTML file (in this case, 'index.html'). Update your ServletContextHandler's resource base path to point to the static resources. Finally, add a DefaultServlet to serve these static resources. This DefaultServlet must be the last servlet added to your ServletContextHandler.

public class MyServer {
  public static void main(String[] args) throws Exception
  {
      Server server = new Server(8080);
      ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
      context.setContextPath("/");
      server.setHandler(context);
      
      // Setup API resources
      ServletHolder apiServlet = context.addServlet(ServletContainer.class, "/api/*");
      apiServlet.setInitOrder(1);
      apiServlet.setInitParameter("com.sun.jersey.config.property.packages", "com.api.resources;io.swagger.jaxrs.json;io.swagger.jaxrs.listing");
      
      // Setup Swagger servlet
      ServletHolder swaggerServlet = context.addServlet(DefaultJaxrsConfig.class, "/swagger-core");
      swaggerServlet.setInitOrder(2);
      swaggerServlet.setInitParameter("api.version", "1.0.0");
      
      // Setup Swagger-UI static resources
      String resourceBasePath = ServicesRunner.class.getResource("/webapp").toExternalForm();
      context.setWelcomeFiles(new String[] {"index.html"});
      context.setResourceBase(resourceBasePath);
      context.addServlet(new ServletHolder(new DefaultServlet()), "/*");
      
      server.start();
      server.join();
  }
}

Now, when you start up your server via usr/bin/java -cp <libs> MyServer, you should be able to navigate to http://<host>:8080 and see the Swagger-UI welcome page, similar to this demo page. Enter your api Swagger paths (e.g. http://<host>:8080/api/swagger.json) in order to see your API Resources Swagger-fied.

@mvkrish
Copy link

mvkrish commented Aug 29, 2016

i followed the setup but my API endpoint returns but not the swagger, can you please help me?
http://stackoverflow.com/questions/39197880/jax-rs-embedded-jetty-and-swagger-not-getting-json

@Robinyo
Copy link

Robinyo commented Dec 12, 2016

After some trial and error I created this post which walks you through the steps I followed when adding support for Swagger and Swagger UI to a RESTful API built using RESTEasy (with an embedded Jetty instance packaged in a fat JAR with no web.xml).

See: http://robferguson.org/2016/12/11/resteasy-embedded-jetty-fat-jars-swagger-and-swagger-ui/

And: https://github.com/Robinyo/resteasy/tree/master/examples/fatjar-swagger

@rhycce
Copy link

rhycce commented Dec 22, 2016

Hi, I have a similar problem to the commentor above. I get the project up and running successfully and see the demo UI page. But I cannot see a swagger UI documentation for my servlet. Regular HTTP posts go through to my endpoint but none of the Swagger paths I tried worked
http://<host>:8080/api/swagger.json resulted in 404 error
http://<host>:8080/swagger-core resulted in a 405 (Get not supported) error

@Mosouley
Copy link

Mosouley commented Nov 1, 2017

netbeans can not resolve ServicesRunner.class why?

@Daanielvb
Copy link

Daanielvb commented Dec 4, 2017

Hi,
I followed the whole tutorial (except the maven part), I'm using a Gradle project and I could not compile the pom you wrote since I didn't have all the dependencies there. However, I downloaded the dist from swagger-ui and replaced them in the right places. What happens now is that I can't run my api, swagger.io, and index.html at the same time.

Could you check whats wrong with my solution?
I can only access localhost:8080/api/ and localhost:8080/api/swagger.json. When I try to access the docs it won't load anything and if I copy your final code it will only provide the index.html (swagger homepage) but won't run the API

`private static void startServer(int port) throws Exception {
ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.setApplicationName(RunnerConstants.TITLE);
resourceConfig.packages(APIAbstract.class.getPackage().getName() + ";io.swagger.jaxrs.json;io.swagger.jaxrs" +
".listing");

    ServletContainer servletContainer = new ServletContainer(resourceConfig);
    ServletHolder servletHolder = new ServletHolder(servletContainer);

    Server server = new Server(port);
    ServletContextHandler servletContextHandler = new ServletContextHandler(server, "/api/");

    // Setup API resources
    servletHolder.setInitOrder(1);
    servletContextHandler.addServlet(servletHolder, "/*");

    // Setup Swagger servlet
    ServletHolder swaggerServlet = servletContextHandler.addServlet(DefaultJaxrsConfig.class, "/");
    swaggerServlet.setInitOrder(2);
    swaggerServlet.setInitParameter("api.version", "1.0.0");


    // Setup Swagger-UI static resources
    
    String resourceBasePath = Runner.class.getResource("/docs").toExternalForm();


    servletContextHandler.setWelcomeFiles(new String[]{"index.html"});
    servletContextHandler.setResourceBase(resourceBasePath);

    servletContextHandler.addServlet(new ServletHolder(new DefaultServlet()), "/docs");

    try {
        server.start();
        server.join();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        server.destroy();
    }
}`

@hghotra
Copy link

hghotra commented Oct 21, 2018

@Mosouley You can use something like Main.class.getResource("/webapp").toExternalForm();

@selvarajendran
Copy link

selvarajendran commented Apr 22, 2021

Is it possible to change the swagger.json path from http://:8080/api/swagger.json to http://:8080/new/swagger.json. If yes, could you please let me know how to do that?

My resource is present in /api/*

@anirtek
Copy link

anirtek commented May 3, 2021

This seems to be quite old now. Can this solution work for Jetty 11 which works only with Jakarta namespace now? I tried configuring this but I could not find DefaultJaxrsConfig.class instance in Swagger-core-jakarta:2.1.9. Does someone know?

@anirtek
Copy link

anirtek commented May 24, 2021

@Daanielvb were you able to resolve the your static-ui HTML issue? I am facing exact same problem right now.

@selvarajendran
Copy link

@anirtek Could you please add more details which helps to understand and might be able to provide some solution.

Based on my understanding above, you were able to access swagger.json file and the API's, also the static html, however the html doesn't list down the API's in your swagger.json file? If yes, could you please give the absolute path of swagger.json in the static-ui html text box and try if it loads.

@koalabi
Copy link

koalabi commented Jan 24, 2022

I was really interested after so many non-working examples of JAX-RS with Jetty embedded. Alas, this does not work for me either ... No clue ...

$ java -jar target/fatjar-swagger-1.0-SNAPSHOT.jar
08:30:26.884 [main] INFO org.eclipse.jetty.util.log - Logging initialized @792ms
08:30:26.940 [main] INFO org.eclipse.jetty.server.Server - jetty-9.3.z-SNAPSHOT
08:30:27.774 [main] INFO org.reflections.Reflections - Reflections took 694 ms to scan 1 urls, producing 9571 keys and 17155 values
08:30:27.902 [main] WARN / - unavailable
java.lang.RuntimeException: RESTEASY003325: Failed to construct public org.robferguson.resteasy.examples.fatjar.FatJarApplication()
at org.jboss.resteasy.core.ConstructorInjectorImpl.construct(ConstructorInjectorImpl.java:162)
at org.jboss.resteasy.spi.ResteasyProviderFactory.createProviderInstance(ResteasyProviderFactory.java:2246)
at org.jboss.resteasy.spi.ResteasyDeployment.createApplication(ResteasyDeployment.java:304)
at org.jboss.resteasy.spi.ResteasyDeployment.start(ResteasyDeployment.java:245)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.init(ServletContainerDispatcher.java:113)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.init(HttpServletDispatcher.java:36)
...

Any idea about the cause?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment