Creating a custom ServerFactory implementation

704 views
Skip to first unread message

Matt Wharton

unread,
Sep 17, 2016, 10:11:14 PM9/17/16
to dropwizard-user
Hi all,

I'm interested in creating a ServerFactory implementation that's something of a hybrid between SimpleServerFactory and DefaultServerFactory. Specifically, I'd like to be able to run an SSL and a non-SSL port (which DefaultServerFactory allows for) but also have the application and admin connectors run on the same port (which SimpleServerFactory allows for).

I have an attempted implementation below, but I'm not sure how to register the thing properly so that it can be properly processed by the config parser when the server 'type' is set to 'sharedport'. Is there anything special I'd need to do? Apologies if this has been covered before.

Thanks!

-Matt

import javax.validation.Valid;
import javax.validation.constraints.NotNull;

import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.thread.ThreadPool;
import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.google.common.collect.ImmutableMap;

import io.dropwizard.jetty.ConnectorFactory;
import io.dropwizard.jetty.ContextRoutingHandler;
import io.dropwizard.jetty.HttpConnectorFactory;
import io.dropwizard.server.AbstractServerFactory;
import io.dropwizard.setup.Environment;

/**
 * This is an implementation of AbstractServerFactory which allows for distinct HTTP and 
 * HTTPS Connectors while also running application and admin on the same port within the given 
 * Connector. It's basically a hybrid of SimpleServerFactory and DefaultServerFactory.
 */
@JsonTypeName("sharedport")
public class SharedPortServerFactory extends AbstractServerFactory
{
  private static final Logger LOG = LoggerFactory.getLogger(SharedPortServerFactory.class);
  
  @Valid
  @NotNull
  private ConnectorFactory connector = HttpConnectorFactory.application();
  
  @Valid
  @NotNull
  private ConnectorFactory sslConnector = HttpConnectorFactory.application();

  @NotEmpty
  private String applicationContextPath = "/";

  @NotEmpty
  private String adminContextPath = "/";

  @JsonProperty
  public ConnectorFactory getConnector() 
  {
    return connector;
  }

  @JsonProperty
  public void setConnector(ConnectorFactory factory) 
  {
    this.connector = factory;
  }
  
  @JsonProperty
  public ConnectorFactory getSslConnector()
  {
    return sslConnector;
  }
  
  @JsonProperty
  public void setSslConnector(ConnectorFactory sslFactory)
  {
    this.sslConnector = sslFactory;
  }

  @JsonProperty
  public String getApplicationContextPath() 
  {
    return applicationContextPath;
  }

  @JsonProperty
  public void setApplicationContextPath(String contextPath) 
  {
    this.applicationContextPath = contextPath;
  }

  @JsonProperty
  public String getAdminContextPath() 
  {
    return adminContextPath;
  }

  @JsonProperty
  public void setAdminContextPath(String contextPath) 
  {
    this.adminContextPath = contextPath;
  }

  @Override
  public Server build(Environment environment)
  {
    printBanner(environment.getName());
    ThreadPool threadPool = createThreadPool(environment.metrics());
    Server server = buildServer(environment.lifecycle(), threadPool);

    LOG.info("Registering jersey handler with root path prefix: {}", applicationContextPath);
    environment.getApplicationContext().setContextPath(applicationContextPath);
    Handler applicationHandler = createAppServlet(server,
                                                  environment.jersey(),
                                                  environment.getObjectMapper(),
                                                  environment.getValidator(),
                                                  environment.getApplicationContext(),
                                                  environment.getJerseyServletContainer(),
                                                  environment.metrics());

    LOG.info("Registering admin handler with root path prefix: {}", adminContextPath);
    environment.getAdminContext().setContextPath(adminContextPath);
    Handler adminHandler = createAdminServlet(server,
                                                    environment.getAdminContext(),
                                                    environment.metrics(),
                                                    environment.healthChecks());

    Connector conn = connector.build(server,
                                     environment.metrics(),
                                     environment.getName(),
                                     null);
    Connector sslConn = sslConnector.build(server, 
                                           environment.metrics(),
                                           environment.getName(),
                                           null);

    server.addConnector(conn);
    server.addConnector(sslConn);
    
    ContextRoutingHandler routingHandler = new ContextRoutingHandler(ImmutableMap.of(applicationContextPath, applicationHandler,
                                                                                     adminContextPath, adminHandler));
    server.setHandler(addStatsHandler(addRequestLog(server, routingHandler, environment.getName())));

    return server;
  }

}



Evan Meagher

unread,
Sep 18, 2016, 12:29:54 PM9/18/16
to dropwiz...@googlegroups.com
Check out the "Polymorphic configuration" section of Dropwizard's user manual. Dropwizard loads Discoverable subclasses by looking up services registered as resources under META-INF/services/. This is a convoluted JVM jargon way of saying that you need to add a src/main/resources/META-INF/services/io.dropwizard.server.ServerFactory file containing a single line "com.your.packagename.SharedPortServerFactory" to your application.

For comparison, here's the services file within dropwizard-core which registers {Default,Simple}ServerFactory.

--
You received this message because you are subscribed to the Google Groups "dropwizard-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dropwizard-user+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Evan Meagher

Matt Wharton

unread,
Sep 18, 2016, 1:07:31 PM9/18/16
to dropwizard-user
Brilliant, that did the trick! Thanks so much!

-Matt
To unsubscribe from this group and stop receiving emails from it, send an email to dropwizard-us...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
Evan Meagher

Abu

unread,
Dec 13, 2017, 2:06:18 PM12/13/17
to dropwizard-user
Matt, I'm also working on similar sort of requirement (application and admin to share ports and to secure API with mutual authentication). Appreciate if you could share your final implementation of "SharedPortServerFactory", along with learnings and experience. Thank you.

-Abu
Reply all
Reply to author
Forward
0 new messages