package org.jboss.resteasy.plugins.server.undertow; import io.undertow.Undertow; import io.undertow.server.handlers.PathHandler; import io.undertow.server.handlers.resource.ResourceHandler; import io.undertow.servlet.api.DeploymentInfo; import io.undertow.servlet.api.DeploymentManager; import io.undertow.servlet.api.ServletContainer; import io.undertow.servlet.api.ServletInfo; import org.jboss.resteasy.util.PortProvider; import org.jboss.resteasy.plugins.server.servlet.HttpServlet30Dispatcher; import org.jboss.resteasy.spi.ResteasyDeployment; import javax.servlet.ServletException; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; import static io.undertow.servlet.Servlets.servlet; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; /** * Wrapper around Undertow to make resteasy deployments easier * Each ResteasyDeployment or jaxrs Application is deployed under its own web deployment (WAR) * * You may also deploy after the server has started. * * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @version $Revision: 1 $ */ public class UndertowJaxrsServer { final PathHandler root = new PathHandler(); final ServletContainer container = ServletContainer.Factory.newInstance(); protected Undertow server; /** * Creates a web deployment for your ResteasyDeployent so you can set up things like security constraints * You'd call this method, add your servlet security constraints, then call deploy(DeploymentInfo) * * Note, only one ResteasyDeployment can be applied per DeploymentInfo * ResteasyServlet is mapped to mapping + "/*" * * Example: * * DeploymentInfo di = server.undertowDeployment(resteasyDeployment, "rest"); * di.setDeploymentName("MyDeployment") * di.setContextRoot("root"); * server.deploy(di); * * @param deployment * @param mapping resteasy.servlet.mapping.prefix * @return must be deployed by calling deploy(DeploymentInfo), also does not set context path or deployment name */ public DeploymentInfo undertowDeployment(ResteasyDeployment deployment, String mapping) { if (mapping == null) mapping = "/"; if (!mapping.startsWith("/")) mapping = "/" + mapping; if (!mapping.endsWith("/")) mapping += "/"; mapping = mapping + "*"; String prefix = null; if (!mapping.equals("/*")) prefix = mapping.substring(0, mapping.length() - 2); ServletInfo resteasyServlet = servlet("ResteasyServlet", HttpServlet30Dispatcher.class) .setAsyncSupported(true) .setLoadOnStartup(1) .addMapping(mapping); if (prefix != null) resteasyServlet.addInitParam("resteasy.servlet.mapping.prefix", prefix); return new DeploymentInfo() .addServletContextAttribute(ResteasyDeployment.class.getName(), deployment) .addServlet( resteasyServlet ); } /** * Creates a web deployment for your ResteasyDeployent so you can set up things like security constraints * You'd call this method, add your servlet security constraints, then call deploy(DeploymentInfo) * * Note, only one ResteasyDeployment can be applied per DeploymentInfo. Resteasy servlet is mapped to "/*" * * @param deployment * @return */ public DeploymentInfo undertowDeployment(ResteasyDeployment deployment) { return undertowDeployment(deployment, "/"); } /** * Creates a web deployment for the jaxrs Application. Will ignore any @ApplicationPath annotation. * * @param application * @param mapping resteasy.servlet.mapping.prefix * @return */ public DeploymentInfo undertowDeployment(Class<? extends Application> application, String mapping) { ResteasyDeployment deployment = new ResteasyDeployment(); deployment.setApplicationClass(application.getName()); DeploymentInfo di = undertowDeployment(deployment, mapping); di.setClassLoader(application.getClassLoader()); return di; } /** * Creates a web deployment for the jaxrs Application. Will bind the resteasy.servlet.mapping.prefix * to @ApplicationPath if it exists, otherwise "/". * * @param application * @return */ public DeploymentInfo undertowDeployment(Class<? extends Application> application) { ApplicationPath appPath = application.getAnnotation(ApplicationPath.class); String path = "/"; if (appPath != null) path = appPath.value(); return undertowDeployment(application, path); } /** * Maps a path prefix to a resource handler to allow serving resources other than the JAX-RS endpoints. * For example, this can be used for serving static resources like web pages or API documentation that might * be deployed with the REST application server. * * @param path * @param handler */ public void addResourcePrefixPath(String path, ResourceHandler handler) { root.addPrefixPath(path, handler); } /** * Creates a web deployment under "/" * * @param deployment * @return */ public UndertowJaxrsServer deploy(ResteasyDeployment deployment) { return deploy(deployment, "/"); } /** * Creates a web deployment under contextPath * * @param deployment * @param contextPath * @return */ public UndertowJaxrsServer deploy(ResteasyDeployment deployment, String contextPath) { return deploy(deployment, contextPath, null, null); } public UndertowJaxrsServer deploy(ResteasyDeployment deployment, String contextPath, Map<String, String> contextParams, Map<String, String> initParams) { if (contextPath == null) contextPath = "/"; if (!contextPath.startsWith("/")) contextPath = "/" + contextPath; DeploymentInfo builder = undertowDeployment(deployment); builder.setContextPath(contextPath); builder.setDeploymentName("Resteasy" + contextPath); builder.setClassLoader(deployment.getApplication().getClass().getClassLoader()); if (contextParams != null) { for (Entry<String, String> e : contextParams.entrySet()) { builder.addInitParameter(e.getKey(), e.getValue()); } } if (initParams != null) { ServletInfo servletInfo = builder.getServlets().get("ResteasyServlet"); for (Entry<String, String> e : initParams.entrySet()) { servletInfo.addInitParam(e.getKey(), e.getValue()); } } return deploy(builder); } /** * Creates a web deployment for the jaxrs Application. Will bind the contextPath * to @ApplicationPath if it exists, otherwise "/". * * @param application * @return */ public UndertowJaxrsServer deploy(Class<? extends Application> application) { ApplicationPath appPath = application.getAnnotation(ApplicationPath.class); String path = "/"; if (appPath != null) path = appPath.value(); return deploy(application, path); } /** * Creates a web deployment for the jaxrs Application. Will ignore any @ApplicationPath annotation. * * @param application * @param contextPath * @return */ public UndertowJaxrsServer deploy(Class<? extends Application> application, String contextPath) { if (contextPath == null) contextPath = "/"; if (!contextPath.startsWith("/")) contextPath = "/" + contextPath; ResteasyDeployment deployment = new ResteasyDeployment(); deployment.setApplicationClass(application.getName()); DeploymentInfo di = undertowDeployment(deployment); di.setClassLoader(application.getClassLoader()); di.setContextPath(contextPath); di.setDeploymentName("Resteasy" + contextPath); return deploy(di); } /** * Creates a web deployment for the jaxrs Application. Will bind the contextPath * to @ApplicationPath if it exists, otherwise "/". * * @param application * @return */ public UndertowJaxrsServer deploy(Application application) { ApplicationPath appPath = application.getClass().getAnnotation(ApplicationPath.class); String path = "/"; if (appPath != null) path = appPath.value(); return deploy(application, path); } /** * Creates a web deployment for the jaxrs Application. Will ignore any @ApplicationPath annotation. * * @param application * @param contextPath * @return */ public UndertowJaxrsServer deploy(Application application, String contextPath) { if (contextPath == null) contextPath = "/"; if (!contextPath.startsWith("/")) contextPath = "/" + contextPath; ResteasyDeployment deployment = new ResteasyDeployment(); deployment.setApplication(application); DeploymentInfo di = undertowDeployment(deployment); di.setClassLoader(application.getClass().getClassLoader()); di.setContextPath(contextPath); di.setDeploymentName("Resteasy" + contextPath); return deploy(di); } /** * Adds an arbitrary web deployment to underlying Undertow server. This is for your own deployments * * @param builder * @return */ public UndertowJaxrsServer deploy(DeploymentInfo builder) { DeploymentManager manager = container.addDeployment(builder); manager.deploy(); try { root.addPrefixPath(builder.getContextPath(), manager.start()); } catch (ServletException e) { throw new RuntimeException(e); } return this; } public UndertowJaxrsServer start(Undertow.Builder builder) { server = builder.setHandler(root).build(); server.start(); return this; } public UndertowJaxrsServer start() { server = Undertow.builder() .addHttpListener(PortProvider.getPort(), "localhost") .setHandler(root) .build(); server.start(); return this; } public void stop() { server.stop(); } }