Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Embedded Jetty: Different ports for internally- and externally-visible endpoints?

I've got a REST app that uses embedded Jetty as the server. Most of the endpoints need to be publicly-visible (and have appropriate authentication built in), but a few are for internal-use only. I'd like to avoid the overhead of authentication on those and instead use the firewall to restrict access:

Externally-visible endpoints are served on port 10000, which the external firewall leaves open. Internally-visible endpoints are served on port 20000, which the external firewall blocks.

However, I can't figure out how to achieve this with embedded Jetty. I've tried instantiating two Server objects, one on port 10000 with the appropriate servlet handlers registered and one on port 20000 with the appropriate servlet handlers registered. However, only the server instance that is started second works; requests to endpoints hosted by the one started first result in 404 responses.

The Jetty documentation talks about how to do this with *.xml configurations , but not for an embedded instance.

Any thoughts or ideas? Or is there a better way to achieve the internal/external endpoint isolation I'm after? The hard requirement is that both internal and external endpoints need to "run" in the same JVM.


Edit

Turns out that the problem was related to using Guice and the Guice-servlets extension (issues 618 and 635). Running two embedded Jetty instances works fine as described in James Kingsbery's answer below.

Guice uses a filter (GuiceFilter) registered with server context to get ahold of requests that need request-scoped dependency injection (DI) and to construct servlets and filters that require DI. Unfortunately, it uses a static object to manage the list of servlets and filters associated with it.

In a typical setup, the guice-servlet.jar containing GuiceFilter is included per-application and thus loaded by a different classloader for each application--- and everything works fine. No so with embedded Jetty, where essentially everything is loaded by the default system classloader.

Solution to Guice Problem

The latest master (commit fbbb52dcc92e) of Guice contains an updated GuiceFilter with support for a dynamic reference to the FilterPipeline object (the static object causing the problems). Unfortunately, the constructor to inject the FilterPipeline instance is package-private. So, to use it you need to create a wrapper class in the com.google.inject.servlet package that exposes that constructor:

package com.google.inject.servlet;

import com.google.inject.Inject;

public class NonStaticGuiceFilter extends GuiceFilter {

    /**
     * Do not use. Must inject a {@link FilterPipeline} via the constructor.
     */
    @SuppressWarnings("unused")
    private NonStaticGuiceFilter() {
        throw new IllegalStateException();
    }

    @Inject
    public NonStaticGuiceFilter(FilterPipeline filterPipeline) {
        super(filterPipeline);
    }

}

To use this class, create an instance using the injector with your ServletModule installed and register it with your Jetty Context:

// Create the context handler
ServletContextHandler handler = new ServletContextHandler(myServer, "/context");

// Create the injector, registering your ServletModule
final Injector injector = Guice.createInjector(new MyServletModule());

// Add the Guice listener for this injector
handler.addEventListener(new GuiceServletContextListener() {
   @Override
   protected Injector getInjector() {
       return injector;
   }
});

// Filter all requests through Guice via NonStaticGuiceFilter.
// Guice will construct the FilterPipeline instance needed by
// NonStaticGuiceFilter.
handler.addFilter(
       new FilterHolder(injector
               .getInstance(NonStaticGuiceFilter.class)), "/*", null);
like image 614
David B. Avatar asked Jan 31 '12 05:01

David B.


1 Answers

My usual crutch in getting started with an embedded Jetty project is the Wicket maven archetype. Here is a class based on that archetype that should do pretty much what you need:

package net.kingsbery;

import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.webapp.WebAppContext;

public class Start {

    public static void main(String[] args) throws Exception {
            Server server = new Server();
            SocketConnector connector = new SocketConnector();

            // Set some timeout options to make debugging easier.                                                                                                                                
            connector.setMaxIdleTime(1000 * 60 * 60);
            connector.setSoLingerTime(-1);
            connector.setPort(10080);
            server.setConnectors(new Connector[] { connector });

            WebAppContext bb = new WebAppContext();
            bb.setServer(server);
            bb.setContextPath("/");
            bb.setWar("src/main/secret-webapp");

            server.addHandler(bb);

            Server server2 = new Server();
            SocketConnector connector2 = new SocketConnector();

            // Set some timeout options to make debugging easier.                                                                                                                                
            connector2.setMaxIdleTime(1000 * 60 * 60);
            connector2.setSoLingerTime(-1);
            connector2.setPort(20000);
            server2.setConnectors(new Connector[] { connector });

            WebAppContext bb2 = new WebAppContext();
            bb2.setServer(server);
            bb2.setContextPath("/");
            bb2.setWar("src/main/webapp");



            server.addHandler(bb);
            server2.addHandler(bb2);

            try {
                    server.start();
                    server2.start();
            } catch (Exception e) {
                    e.printStackTrace();
                    System.exit(100);
            }
    }
}

If you use some other handler, replace that with the webapp handler.

That being said, I'm not sure that this is the right way of doing it.

like image 148
James Kingsbery Avatar answered Nov 19 '22 18:11

James Kingsbery