Jetty: Pass object from main method with WebAppPro

2019-08-23 09:59发布

I need to pass an object from the embedded jetty code in a main method to be used in a servlet.

This is an issue because of the separate classloader used within the WebAppContext - otherwise I would just use a static variable.

My main code sets things up like this:

Server server = new Server(); 
// setup connectors here...
ContextHandlerCollection contexts = new ContextHandlerCollection();
RequestLogHandler requestLogHandler = new RequestLogHandler();
HandlerCollection handlers = new HandlerCollection();
handlers.setHandlers(new Handler[] { contexts, new DefaultHandler(), requestLogHandler });
server.setHandler(instrumentedHandler(handlers, metrics));

addRequestLogging(requestLogHandler);

DeploymentManager deploymentManager = new DeploymentManager();
deploymentManager.setContexts(contexts);

WebAppProvider webAppProvider = new WebAppProvider();
webAppProvider.setMonitoredDirName(jettyHome + "/webapps");
webAppProvider.setParentLoaderPriority(false);
webAppProvider.setExtractWars(true);
webAppProvider.setScanInterval(1);
webAppProvider.setDefaultsDescriptor(jettyHome + "/webdefault.xml");
webAppProvider.setConfigurationManager(new PropertiesConfigurationManager());

deploymentManager.addAppProvider(webAppProvider);
server.addBean(deploymentManager);
// Attempt to set the metrics on the server - but I can't access them in the Servlet
server.setAttribute(MetricRegistry.class.getName(), metrics);
server.start();
server.join();

I tried a few things from this question, but they did not work. Specifically, there is no org.eclipse.jetty.server.Server attribute set on the servlet context.

(Specifically, I am trying to setup dropwizard metrics on the jetty objects, but I need the same MetricRegistry object for the rest of my application so I can keep all my metrics and reporters together)

1条回答
Evening l夕情丶
2楼-- · 2019-08-23 10:13

When using the DeploymentManager, you have no access to the WebAppContext, or ServletContextHandler, or ContextHandler during your main start code.

You'll instead have to use the facilities in the DeploymentManager to provide a custom AppLifeCycle.Binding that does what you need it to do during the deployment phases.

Bonus is that this works during hot (re)deploy as well.

Here's a working example of this setup in embedded-jetty from the embedded-jetty-cookbook

DeployWebApps.java

package org.eclipse.jetty.cookbook;

import java.io.File;
import java.io.FileNotFoundException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppLifeCycle;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.deploy.graph.Node;
import org.eclipse.jetty.deploy.providers.WebAppProvider;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;

public class DeployWebApps
{
    public static void main(String[] args) throws Exception
    {
        Server server = new Server(8080);

        ContextHandlerCollection contexts = new ContextHandlerCollection();
        HandlerCollection handlers = new HandlerCollection();

        handlers.setHandlers(new Handler[]{contexts, new DefaultHandler()});

        server.setHandler(handlers);

        Path confFile = Paths.get(System.getProperty("user.dir"), "example.conf");

        ContextAttributeCustomizer contextAttributeCustomizer = new ContextAttributeCustomizer();
        contextAttributeCustomizer.setAttribute("common.conf", confFile);

        DeploymentManager deploymentManager = new DeploymentManager();
        deploymentManager.setContexts(contexts);
        deploymentManager.addLifeCycleBinding(contextAttributeCustomizer);

        String jettyBaseProp = System.getProperty("jetty.base");
        if (jettyBaseProp == null)
        {
            throw new FileNotFoundException("Missing System Property 'jetty.base'");
        }
        Path jettyBase = new File(jettyBaseProp).toPath().toAbsolutePath();

        WebAppProvider webAppProvider = new WebAppProvider();
        webAppProvider.setMonitoredDirName(jettyBase.resolve("webapps").toString());

        deploymentManager.addAppProvider(webAppProvider);
        server.addBean(deploymentManager);

        // Lets dump the server after start.
        // We can look for the deployed contexts, along with an example of the
        // result of ContextAttributesCustomizer in the dump section for "Handler attributes"
        server.setDumpAfterStart(true);
        server.start();
        server.join();
    }

    public static class ContextAttributeCustomizer implements AppLifeCycle.Binding
    {
        public final Map<String, Object> attributes = new HashMap<>();

        public void setAttribute(String name, Object value)
        {
            this.attributes.put(name, value);
        }

        @Override
        public String[] getBindingTargets()
        {
            return new String[]{ AppLifeCycle.DEPLOYING };
        }

        @Override
        public void processBinding(Node node, App app) throws Exception
        {
            ContextHandler handler = app.getContextHandler();
            if (handler == null)
            {
                throw new NullPointerException("No Handler created for App: " + app);
            }
            attributes.forEach((name, value) -> handler.setAttribute(name, value));
        }
    }
}
查看更多
登录 后发表回答