2016-04-04 4 views
1

В причале/jersey2 резидентных приложения API конечных точек генерируются программно внутри ApiServiceConfig класса ConfigurationProperties класса считывает и загружает файл свойства в java.util.Properties класса.Инициировать распознаватель зависимости джерси внутри ResourceConfig

Создание экземпляра сервера Jetty осуществляется следующим образом.

 // Create and register resources 
    final ResourceConfig resourceConfig = new ApiServiceConfig() 
      .register(new DependencyInjectionBinder()); 

    ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); 

    contextHandler.setContextPath("/mydomain/api"); 
    Server jettyServer = new Server(8585); 
    jettyServer.setHandler(contextHandler); 

    ServletHolder jerseyServlet = new ServletHolder(new ServletContainer(resourceConfig)); 
    contextHandler.addServlet(jerseyServlet, "/*"); 

    try { 
     jettyServer.start(); 
     jettyServer.join(); 
    } catch (Exception e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } finally { 
     jettyServer.destroy(); 
    } 

public class ApiServiceConfig extends ResourceConfig { 
    public ApiServiceConfig() { 
     for(JsonNode jsonNode: nodeArray) { 
      // JSON endpoint service description example. 
      //{ 
      // "service": "/item/{id}", 
      // "method": "GET", 
      // "process": { 
      //  "@type": "com.mycompany.projectx.endpoint.services.GetController", 
      //  "uri_param": "id", 
      //  "type": "item", 
      //  "fields": "uuid,name,content_area,title,grade,dok,bloom,item_banks,...,item_banks_titles" 
      // } 
      //} 
      // Json property "service" describes a URL pattern for a request (eg. "/item/{id}"). 
      final String path = jsonNode.get("service").asText(); 

      // Api RESTful verb ('GET', 'POST', etc.) 
      final String method = jsonNode.get("method").asText(); 

      // Map a process description of a service to specific controller implementation class. 
      // This is the instance creation where I want injection to happen. 
      IController controller = this.objectMapper.convertValue(jsonNode.get("process"), AbstractBaseController.class); 
      // Controller is added to a HashMap 
      ... 
      final Resource.Builder resourceBuilder = Resource.builder(); 
      resourceBuilder.path(path); 
      final ResourceMethod.Builder methodBuilder = resourceBuilder.addMethod(method); 

      methodBuilder.produces(new MediaType("text", "plain")) 
       handledBy((Inflector)(ctx) -> { 
        // Controller is retrieved from the HashMap 
        controller.execute(new ProcessEvent()); 
        ... 
        return responseResult; 
       }); 
      final Resource resource = resourceBuilder.build(); 
      registerResources(resource); 
     } 
    } 
} 

GetController

public class GetController extends AbstractBaseController { 

    @Config("data.cassandra") 
    String connectionString; // == null, but should be a string injected. 

    public GetController() { 
    } 

    @Override 
    public ProcessEvent process(ProcessEvent event) throws Exception { 

     String uri_param = this.uri_param; 
     event.contentType = "application/json"; 

     event.object = ".Get method of Item endpoint got executed. Cassandra IP: " + getApplicationProperties().getProperty("data.cassandra"); 

     return event; 
    } 

Dependency распознаватель связующий зарегистрирован в DependencyInjectionBinder классе:

public class DependencyInjectionBinder extends AbstractBinder { 

    @Override 
    protected void configure() { 

     bind(ConfigInjectionResolver.class) 
      .to(new TypeLiteral<InjectionResolver<Config>>() {}) 
      .in(Singleton.class); 
    } 
} 

ConfigInjectionResolver реализует InjectionResolver и решить некоторую логику.

ApiServiceConfig в цикле проходит описания и создает конечные точки. Для каждой конечной точки создается построитель ресурсов, заполняется и регистрируется ресурс. При создании конечной точки ресурса класс создается с помощью ДЖЕКСОНА-DataBind:

IController controller = this.objectMapper.convertValue(jsonNode.get("process"), AbstractBaseController.class); 

Этот класс должен получить другой класс впрыскивается. Resolver DependencyInjectionBinder не срабатывает, когда создается экземпляр controller. Если я перенучу экземпляр DependencyInjectionBinder в конструктор ApiServiceConfiguration в качестве первой операции, инъекция свойства в экземпляр controller не произойдет.

Однако, когда я зарегистрировать класс, определенный конечной точки:

resourceConfig.register(AnEndpointClass.class); 

DI разрешителя пинки в и добавляет зависимость.

Как заставить зависимый преобразователь работать для экземпляров классов при программном создании и регистрации конечных точек?

+0

_ "ApiServiceConfig в цикле проходит через описание и создает конечные точки. Для каждого строитель конечных точек ресурсов создаются, заполняется и ресурс зарегистрирован. При создании конечной точки ресурса класс конкретизируются с помощью ДЖЕКСОН-DataBind : "_ - Можете ли вы на самом деле показать это в коде. –

+0

В принципе, вы не предоставили достаточно информации для воспроизведения проблемы. Есть много недостающих вещей. Я не могу догадаться, что вы делаете. –

+0

@peeskillet обновлен. Спасибо за внимание! – Maxim

ответ

1

Чтобы явно ввести объекты, вам нужно будет удержать ServiceLocator и позвонить locator.inject(controller). Вы можете получить ServiceLocator внутри Feature, как указано в this post.

И поскольку вам также необходимо зарегистрировать ресурсы с помощью контроллера, вам также понадобится способ регистрации ресурсов внутри Feature. Для этого вы можете использовать ModelProcessor. Вы можете узнать больше об этом в Jersey documentation. Это позволяет вам изменить модель ресурсов Джерси. Здесь мы можем просто зарегистрировать все ресурсы, которые мы создаем программно.

Ниже приведен полный пример с использованием Jersey Test Framework. Вы можете запускать его, как и любой другой тест JUnit.

import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target; 
import java.util.logging.Logger; 

import javax.annotation.Priority; 
import javax.inject.Singleton; 
import javax.ws.rs.container.ContainerRequestContext; 
import javax.ws.rs.core.Configuration; 
import javax.ws.rs.core.Feature; 
import javax.ws.rs.core.FeatureContext; 
import javax.ws.rs.core.Response; 

import org.glassfish.hk2.api.Injectee; 
import org.glassfish.hk2.api.InjectionResolver; 
import org.glassfish.hk2.api.ServiceHandle; 
import org.glassfish.hk2.api.ServiceLocator; 
import org.glassfish.hk2.api.TypeLiteral; 
import org.glassfish.hk2.utilities.binding.AbstractBinder; 
import org.glassfish.jersey.ServiceLocatorProvider; 
import org.glassfish.jersey.filter.LoggingFilter; 
import org.glassfish.jersey.process.Inflector; 
import org.glassfish.jersey.server.ResourceConfig; 
import org.glassfish.jersey.server.model.ModelProcessor; 
import org.glassfish.jersey.server.model.Resource; 
import org.glassfish.jersey.server.model.ResourceMethod; 
import org.glassfish.jersey.server.model.ResourceModel; 
import org.glassfish.jersey.test.JerseyTest; 

import org.junit.Test; 
import static org.hamcrest.CoreMatchers.is; 
import static org.junit.Assert.assertThat; 

/** 
* Stack Overflow https://stackoverflow.com/q/36410420/2587435 
* 
* Run this like any other JUnit test. Only one required dependency 
* 
* <dependency> 
*  <groupId>org.glassfish.jersey.test-framework.providers</groupId> 
*  <artifactId>jersey-test-framework-provider-inmemory</artifactId> 
*  <version>${jersey2.version}</version> 
*  <scope>test</scope> 
* </dependency> 
* 
* @author Paul Samsotha 
*/ 
public class PropsInjectionTest extends JerseyTest { 

    @Target(ElementType.FIELD) 
    @Retention(RetentionPolicy.RUNTIME) 
    public static @interface Config { 
     String value(); 
    } 

    @Singleton 
    public static class ConfigInjectionResolver implements InjectionResolver<Config> { 

     @Override 
     public Object resolve(Injectee injectee, ServiceHandle<?> root) { 
      if (String.class == injectee.getRequiredType()) { 
       Config anno = injectee.getParent().getAnnotation(Config.class); 
       if (anno != null) { 
        String key = anno.value(); 
        return key + "Value"; 
       } 
      } 
      return null; 
     } 

     @Override 
     public boolean isConstructorParameterIndicator() { return false; } 

     @Override 
     public boolean isMethodParameterIndicator() { return false; } 
    } 


    public static class Controller { 
     @Config("Key") 
     private String prop; 

     public String getProp() { 
      return prop; 
     } 
    } 

    public static class ResourceFeature implements Feature { 

     @Override 
     public boolean configure(FeatureContext ctx) { 
      final ServiceLocator locator = ServiceLocatorProvider.getServiceLocator(ctx); 
      final Controller controller = new Controller(); 
      locator.inject(controller); 

      final Resource.Builder builder = Resource.builder().path("test"); 
      final ResourceMethod.Builder methodBuilder = builder.addMethod("GET"); 
      methodBuilder.handledBy(new Inflector<ContainerRequestContext, String>(){ 
       @Override 
       public String apply(ContainerRequestContext data) { 
        return controller.getProp(); 
       } 
      }); 
      final Resource resource = builder.build(); 
      ctx.register(new MyModelProcessor(resource)); 
      return true;  
     } 

     @Priority(100) 
     static class MyModelProcessor implements ModelProcessor { 
      private final Resource[] resources; 

      public MyModelProcessor(Resource... resources) { 
       this.resources = resources; 
      } 

      @Override 
      public ResourceModel processResourceModel(ResourceModel rm, Configuration c) { 
       final ResourceModel.Builder builder = new ResourceModel.Builder(false); 
       // add any other resources not added in this feature. If there are none, 
       // you can skip this loop 
       for (Resource resource: rm.getResources()) { 
        builder.addResource(resource); 
       } 

       for (Resource resource: this.resources) { 
        builder.addResource(resource); 
       } 

       return builder.build(); 
      } 

      @Override 
      public ResourceModel processSubResource(ResourceModel rm, Configuration c) { 
       return rm; 
      } 
     } 
    } 

    @Override 
    public ResourceConfig configure() { 
     return new ResourceConfig() 
       .register(new ResourceFeature())   
       .register(new LoggingFilter(Logger.getAnonymousLogger(), true)) 
       .register(new AbstractBinder() { 
        @Override 
        protected void configure() { 
         bind(ConfigInjectionResolver.class) 
           .to(new TypeLiteral<InjectionResolver<Config>>(){}) 
           .in(Singleton.class); 
        } 
       }); 
    } 

    @Test 
    public void allShouldBeGood() { 
     final Response response = target("test").request().get(); 
     assertThat(response.readEntity(String.class), is("KeyValue")); 
    } 
} 
+0

Решение работает, спасибо. – Maxim

Смежные вопросы