Jetty: Pass object from main method to servlet

前端 未结 3 1896
臣服心动
臣服心动 2020-12-18 13:38

I have two classes Server (with the main method, starting the server) and StartPageServlet with a Servlet.

The most important part of the c

相关标签:
3条回答
  • 2020-12-18 14:11

    Embedded Jetty is so wonderful here.

    You have a few common options:

    1. Direct instantiation of the servlet, use constructors or setters, then hand it off to Jetty via the ServletHolder (can be any value or object type)
    2. Add it to the ServletContext in your main, and then access it via the ServletContext in your application (can be any value or object type).

    Examples:

    package jetty;
    
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.eclipse.jetty.server.Server;
    import org.eclipse.jetty.servlet.ServletContextHandler;
    import org.eclipse.jetty.servlet.ServletHolder;
    
    public class ObjectPassingExample
    {
        public static void main(String args[]) throws Exception
        {
            Server server = new Server(8080);
    
            ServletContextHandler context = new ServletContextHandler();
            context.setContextPath("/");
    
            // Option 1: Direct servlet instantiation and ServletHolder
            HelloServlet hello = new HelloServlet("everyone");
            ServletHolder helloHolder = new ServletHolder(hello);
            context.addServlet(helloHolder, "/hello/*");
    
            // Option 2: Using ServletContext attribute
            context.setAttribute("my.greeting", "you");
            context.addServlet(GreetingServlet.class, "/greetings/*");
    
            server.setHandler(context);
            server.start();
            server.join();
        }
    
        public static class HelloServlet extends HttpServlet
        {
            private final String hello;
    
            public HelloServlet(String greeting)
            {
                this.hello = greeting;
            }
    
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
            {
                resp.setContentType("text/plain");
                resp.getWriter().println("Hello " + this.hello);
            }
        }
    
        public static class GreetingServlet extends HttpServlet
        {
            private String greeting;
    
            @Override
            public void init() throws ServletException
            {
                this.greeting = (String) getServletContext().getAttribute("my.greeting");
            }
    
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
            {
                resp.setContentType("text/plain");
                resp.getWriter().println("Greetings to " + this.greeting);
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-18 14:13

    I had a similar situation but needed to share a singleton with a servlet deployed via war with hot (re)deploy in a Jetty container. The accepted answer wasn't quite what I needed in my case since the servlet has a lifecycle and context managed by a deployer.

    I ended up with a brute-force approach, adding the object to the server context, which persists for the life of the container, and then fetching the object from within the servlet(s). This required loading the class of the object in a parent (system) classloader so that the war webapp doesn't load its own version of the class into its own classloader, which would cause a cast exception as explained here.

    Embedded Jetty server code:

        Server server = new Server(8090);
    
        // Add all classes related to the object(s) you want to share here.
        WebAppContext.addSystemClasses(server, "my.package.MyFineClass", ...);
    
        // Handler config
        ContextHandlerCollection contexts = new ContextHandlerCollection();
        HandlerCollection handlers = new HandlerCollection();
        handlers.setHandlers(new Handler[] { contexts });
        server.setHandler(handlers);
    
        // Deployer config (hot deploy)
        DeploymentManager deployer = new DeploymentManager();
        DebugListener debug = new DebugListener(System.err,true,true,true);
        server.addBean(debug);
        deployer.addLifeCycleBinding(new DebugListenerBinding(debug));
        deployer.setContexts(contexts);
        deployer.setContextAttribute(
                "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
                ".*/[^/]*servlet-api-[^/]*\\.jar$|.*/javax.servlet.jsp.jstl-.*\\.jar$|.*/[^/]*taglibs.*\\.jar$");
    
        WebAppProvider webapp_provider = new WebAppProvider();
        webapp_provider.setMonitoredDirName("/.../webapps");
        webapp_provider.setScanInterval(1);
        webapp_provider.setExtractWars(true);
        webapp_provider.setConfigurationManager(new PropertiesConfigurationManager());
    
        deployer.addAppProvider(webapp_provider);
        server.addBean(deployer);
    
        // Other config...
    
        // Tuck any objects/data you want into the root server object.
        server.setAttribute("my.package.MyFineClass", myFineSingleton);
    
        server.start();
        server.join();
    

    Example servlet:

    public class MyFineServlet extends HttpServlet
    {
        MyFineClass myFineSingleton;
    
        @Override
        public void init() throws ServletException
        {
            // Sneak access to the root server object (non-portable).
            // Not possible to cast this to `Server` because of classloader restrictions in Jetty.
            Object server = request.getAttribute("org.eclipse.jetty.server.Server");
    
            // Because we cannot cast to `Server`, use reflection to access the object we tucked away there.
            try {
                myFineSingleton = (MyFineClass) server.getClass().getMethod("getAttribute", String.class).invoke(server, "my.package.MyFineClass");
            } catch (Exception ex) {
                throw new ServletException("Unable to reflect MyFineClass instance via Jetty Server", ex);
            }
        }
    
        @Override
        protected void doGet( HttpServletRequest request,
                HttpServletResponse response ) throws ServletException, IOException
        {
            response.setContentType("text/html");
            response.setStatus(HttpServletResponse.SC_OK);
            response.getWriter().println("<h1>Hello from MyFineServlet</h1>");
            response.getWriter().println("Here's: " + myFineSingleton.toString());
        }
    }
    

    My build file for the servlet (sbt) placed the my.package.MyFineClass dependency into the "provided" scope so it wouldn't get packaged into the war as it will already be loaded into the Jetty server.

    0 讨论(0)
  • 2020-12-18 14:23

    Singleton

    You want to pass the same single instance to each servlet?

    Use the Singleton pattern to create a single instance that is available globally.

    The simplest fool-proof way to do that in Java is through an Enum. See Oracle Tutorial. Also see this article and the book Effective Java: Programming Language Guide, Second Edition (ISBN 978-0-321-35668-0, 2008) by Dr. Joshua Bloch.

    So no need to pass an object. Each servlet can access the same single instance through the enum.

    Per web app

    If you want to do some work when your web app is first launching but before any servlet in that web app has handled any request, write a class that implements the ServletContextListener interface.

    Mark your class with the @WebListener annotation to have your web container automatically instantiate and invoke.

    0 讨论(0)
提交回复
热议问题