1

Using the following dependencies (Gradle):

  • org.glassfish.jersey.containers:jersey-container-servlet:2.22.2

  • org.eclipse.jetty:jetty-servlet:9.3.2.v20150730

I have an embedded Jetty server, with a Jersey servlet container... something like this ...

package mypkg.rest.jersey;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.servlet.ServletContainer;
import se.transmode.tnm.alarm.api.AlarmRetrieval;
import mypkg.rest.RestServer;
import mypkg.rest.jersey.serviceImpl.ModelAdapter;

public class JerseyBasedRestServer implements RestServer {

public static final int INITIALIZE_ON_USE = 0;
private Server server;
private final ServletContextHandler context;
private final ServletHolder servlet;
private final ModelAdapter modelAdapter;

public JerseyBasedRestServer(BusinessObjects businessObjects) {
    this.modelAdapter = new ModelAdapter(businessObjects);  //I want this instance to somehow be available for my ServletContainer to use.

    context = new ServletContextHandler(ServletContextHandler.SESSIONS);
    servlet = context.addServlet(ServletContainer.class, "/*");
    servlet.setInitOrder(INITIALIZE_ON_USE);
    servlet.setInitParameter(ServerProperties.PROVIDER_PACKAGES, "mypackage.jersey.generated.api.service");
    servlet.setInitParameter(ServerProperties.MEDIA_TYPE_MAPPINGS, "json : application/json");
    context.setContextPath("/");
}

private void startServlet() {
    try {
        servlet.start();
        servlet.initialize();
    } catch (Exception e) {
        log.error("Failed to initialize servlet. {}", e.getMessage());
    }
}

@Override
public void init(int port) {
    server = new Server(port);
    server.setHandler(context);
    try {
        server.start();
        server.join();
        startServlet();
    } catch (Exception e) {
        log.error("Failed to start jetty server for rest interface");
    } finally {
        server.destroy();
    }
}

The Jersey Container will run server code and model generated using the Swagger code-gen tool

https://github.com/swagger-api/swagger-codegen#getting-started

which delivers the generated model, JacksonJsonProvider, and a RestApi class:

package mypackage.jersey.generated.api.service

Path("/")
public class RestApi  {
   private final RestApiService delegate = new RestApiServiceImpl(); //Integration point of the generated code

   @GET
   @Path("/list/")
   @Consumes({ "application/json" })
   @Produces({ "application/json" })
   public Response retrieveAlarmList(@Context SecurityContext securityContext) throws NotFoundException {
    return delegate.retrieveAlarmList(securityContext);
   }
}

To integrate the generated code we are left to implement RestApiServiceImpl ourselves.

The ModelAdapter's job is to convert our business objects to the generated rest model.

So the question is how do I make the instance of the adapter of our business objects, in this case ModelAdapter, which lies outside the context of the Jersey servlet context, available to the RestApi class, or rather the RestApiServiceImpl?

I kind of understood from reading the past 24 hours that I need to use some sort of Context Dependency Injection either through Jetty, Jersey, or some other library (Weld seems to appear a lot), and have tried various combinations of @Inject, @Context, etc etc, but have come to the conclusion that I have no clue what I am actually doing... I'm not even sure I understand enough about the situation to phrase my question correctly.

More info can be made available on request.

Any help is appreciated.

EDIT: added a link here to https://github.com/englishbobster/JersetAndJetty

using @peeskillets suggestions, but still not working.

Englishbob
  • 197
  • 1
  • 11

1 Answers1

2

First thing you need to make DI work, is an AbstractBinder. This is where you will make your objects available to be injected.

class Binder extends AbstractBinder {
    @Override
    protected void configure() {
       bind(modelAdapter).to(ModelAdapter.class);
    }
}

Then you need to register the binder with Jersey. The easiest way is to register in Jersey's ResourceConfig. In your case, you are not using one. You are configuring everything in the "web.xml". For that, you should take a look at this post.

If you want to change your configuration to use a ResourceConfig, which personally I'd rather use, you can do this

package com.some.pkg;

public class JerseyConfig extends ResourceConfig {
    public JerseyConfig() {
        packages("mypackage.jersey.generated.api.service");
        property(ServerProperties.MEDIA_TYPE_MAPPINGS, "json : application/json");
        register(new Binder());
    }
}

Then to configure it with Jetty, you can do

servlet.setInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, 
                         "com.some.pkg.JerseyConfig");

Now you can get rid of those other two init-params, as you are configuring it inside the ResourceConfig.

Another way, without any init-params, is to do

ResourceConfig config = new JerseyConfig();
ServletHolder jerseyServlet = new ServletHolder(ServletContainer(config));
context.addServlet(jerseyServlet, "/*");

See full example of last code snippet, here.

Now you can just inject the ModelAdapter pretty much anywhere within Jersey

In a field

@Inject
private ModelAdapter adapter;

Or in a contructor

@Inject
public RestApi(ModelAdapter adapter) {
    this.adapter = adapter;
}

Or method parameter

@GET
public Response get(@Context ModelAdapter adapter) {}
Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • OK thanks for the reply. I have tried something similar to this during my experimentations. I have just tried your recipe above, creating a Binder and a resource configuration, then using injection on the field, but am still getting a null pointer exception when using my ModelAdapter. Whats the best way to go about seeing the context in a debugger (if at all possible)? – Englishbob May 19 '16 at 09:03
  • btw I never used a web.xml for configuration, (fear of XML) and tried to do everything programmatically. – Englishbob May 19 '16 at 09:07
  • 1
    It should inject into the `RestApi` just fine. If you trying to inject it into the service, then you cannot instantiate the service yourself. You need to 1) Bind the service with the binder also, and 2) _Inject_ the service into the RestApi, don't instantiate it – Paul Samsotha May 20 '16 at 08:55
  • OK got his to work. it seems that the injection of my ModelAdapter has to happen in the RestAPI and nowhere else. This has a lot to do with my swagger generated code and the hooks I have in to my generated RestApi class. Also a lot to do with my understanding of DI. Thanks for the help. – Englishbob May 20 '16 at 12:13