CXF/Jetty equivalent of the following Jersey/Jetty

2019-07-21 06:54发布

问题:

I've got some test code with Jersey (2.7) and Jetty (9.2.5.v2014112) that I'm trying to port to CXF (3.0.x) and Jetty (same version). There's one part that I can't figure out, the equivalent of the rc.register(resource) line below:

    ServletContextHandler sch = new ServletContextHandler();
    sch.setContextPath("/xxx");

    resource = new TheResource();
    ResourceConfig rc = new ResourceConfig();
    rc.register(resource);

    ServletContainer sc = new ServletContainer(rc);        
    ServletHolder holder = new ServletHolder(sc);
    sch.addServlet(holder, "/*");

    Server server = new Server(port);
    server.setHandler(sch);
    server.start();
    server.join();

For the CXF/Jetty case I know I need to do something like:

CXFNonSpringJaxrsServlet context = new CXFNonSpringJaxrsServlet();
// EQUIVALENT OF:
//     ResourceConfig rc = new ResourceConfig();
//     rc.register(myresource)
//     SOMETHING.setResourceConfig(rc)
ServletHolder holder = new ServletHolder(context);
ServletContextHandler handler = new ServletContextHandler("/restroot");
handler.addServlet(holder, "/*");

// Using ContextHandlerCollection cos my jetty needs to serve more
// than just this REST service
ContextHandlerCollection collection = new ContextHandlerCollection();
collection.addHandler(handler);

Server server = new Server(8080);
server.setHandler(collection);
server.start();
server.join();

Does anybody know how/if this is possible?

Just to clarify what I'm trying to do here: I'm trying to publish a JAX-RS service on an embedded Jetty instance, and I need full control over the lifecycle of the JAX-RS bean.

Maarten

回答1:

Look at the source for CXFNonSpringJaxrsServlet, it is configured via init params from web.xml. However you're free to override that functionality of course



回答2:

I've managed to get something working by:

  • extending CXFNonSpringJaxrsServlet
  • Overriding getResourceProviders()
  • Providing my own implementation of ResourceProvider

It all looks a bit complex, and I feel that there should be a standard API for this in CXF, but anyways, here's my solution:

public class TheApp {
    private static final Logger LOG = LoggerFactory.getLogger(TheApp.class);

    TheResource resource;

    public static class BeanResourceProvider implements ResourceProvider {
        private final Object theBean;

        public BeanResourceProvider(Object theBean) {
            this.theBean = theBean;
        }

        @Override
        public Object getInstance(Message m) {
            return theBean;
        }

        @Override
        public void releaseInstance(Message m, Object o) {            
        }

        @Override
        public Class<?> getResourceClass() {
            return theBean.getClass();
        }

        @Override
        public boolean isSingleton() {
            return true;
        }

    };

    public TheApp(int port) throws Exception {
        resource = new TheResource();

        CXFNonSpringJaxrsServlet context = new CXFNonSpringJaxrsServlet() {
            public void configureSingleton(Object o) {
                LOG.info("configureSingleton() called");
            }

            public Map<Class<?>, ResourceProvider>
                    getResourceProviders(ServletConfig servletConfig,
                            Map<Class<?>, Map<String, List<String>>> resourceClasses) throws ServletException {
                LOG.info("getResourceProviders called");

                Map<Class<?>, ResourceProvider> map = new HashMap<Class<?>, ResourceProvider>();
                for (Map.Entry<Class<?>, Map<String, List<String>>> entry : resourceClasses.entrySet()) {
                    Class<?> c = entry.getKey();
                    LOG.info("getting provider for {}", c.getName());
                    map.put(c, new BeanResourceProvider(resource));
                }
                return map;
            }
        };

        ServletHolder holder = new ServletHolder(context);
        holder.setInitParameter("jaxrs.serviceClasses", "com.test.cxfjetty.TheResource");

        ServletContextHandler handler = new ServletContextHandler();
        handler.addServlet(holder, "/*");
        handler.setContextPath("/theroot");

        Server server = new Server(port);
        server.setHandler(handler);
        server.start();
        server.join();
    }

    public static void main(String[] args) throws Exception {
        TheApp app = new TheApp(8122);        
    }
}

Fun exercise this was...



回答3:

From CXF version 3.0.4 onwards, there are additional constructors on the CXFNonSpringJaxrsServlet that make this a lot easier:

public SNAPSHOTApp(int port) throws Exception {
    resource = new TheResource();
    Set<Object> resourceSingletons = new HashSet<>();
    resourceSingletons.add(resource);

    CXFNonSpringJaxrsServlet context = new CXFNonSpringJaxrsServlet(resourceSingletons);

    ServletHolder servlet = new ServletHolder(context);
    ServletContextHandler handler = new ServletContextHandler();
    handler.addServlet(servlet, "/*");
    handler.setContextPath("/snapshot");

    Server server = new Server(port);
    server.setHandler(handler);
    server.start();
    server.join();
}

Big thanks to @Sergey Beryozkin for adding this based on my question on the CXF user mailing list.