2016-04-26 10 views
1

Ich bin derzeit Refaktorierung einer Jersey Web App und möchte einige cross-cutting Bedenken in ihren eigenen Klassen bündeln und Annotationen verwenden, um Methoden abzufangen. Zum Beispiel gibt es viele Methoden, bei denen ich prüfen muss, ob der Benutzer der Eigentümer der Entität ist, die er ändern möchte (in meinem Fall ist dies ein Projekt). Daher muss ich innerhalb des Interceptors Datenbankaufrufe durchführen und obwohl die entsprechenden DAOs injiziert werden, wäre dies der beste Weg.@Inject funktioniert nicht in MethodInterceptor verwaltet von HK2

Derzeit meine Abfangjäger sieht wie folgt aus:

public class ProjectOwnerCheckInterceptor implements MethodInterceptor { 

     @Inject 
     private EntityManager em; 

     @Inject 
     private UserProvider userProvider; 

     @Inject 
     private RMUserDAO rmUserDAO; 

     @Inject 
     private ProjectDAO projectDAO; 

     public ProjectOwnerCheckInterceptor() { 
     // TODO Auto-generated constructor stub 
     } 

     @Override 
     public Object invoke(MethodInvocation arg0) throws Throwable { 

      // First of all let's get the annotation 
      ProjectOwnerCheck check = arg0.getMethod().getAnnotation(ProjectOwnerCheck.class); 

      // if there is no check, then just proceed! 
      if (check == null) 
       arg0.proceed(); 

      long projectId = (long) arg0.getArguments()   [check.projectIdIndex()]; 

      // Handling ownership!! 
      Project project = getProjectOrThrow(projectId); 

      return arg0.proceed(); 

     } 
    } 

Die benutzerdefinierte Anmerkung gerade nach vorne ist. Ich brauche ein paar kleine Informationen, die Argumentposition der EntityID in dem Verfahren ist als Anzahl von Parametern und Typen überprüfen muss hinzufügen variieren:

@Retention(RetentionPolicy.RUNTIME) 
@Target({ ElementType.METHOD }) 
public @interface ProjectOwnerCheck { 

    int projectIdIndex() default -1; 

} 

Für Jersey/HK2 zu wissen, was mit den Abfangjäger zu tun, die ich geschaffen habe ein Filter Umsetzung InterceptionService:

public class HK2InterceptorFilter implements InterceptionService { 

    private final static MethodInterceptor PROJECT_CHECK_METHOD_INTERCEPTOR = new ProjectOwnerCheckInterceptor(); 
    private final static List<MethodInterceptor> PROJECT_CHECK_METHOD_LIST = Collections 
      .singletonList(PROJECT_CHECK_METHOD_INTERCEPTOR); 

    public HK2InterceptorFilter() { 
     // TODO Auto-generated constructor stub 
    } 

    @Override 
    public Filter getDescriptorFilter() { 

     return BuilderHelper.allFilter(); 

    } 

    @Override 
    public List<MethodInterceptor> getMethodInterceptors(Method method) { 

     if (method.isAnnotationPresent(ProjectOwnerCheck.class)) 
      return PROJECT_CHECK_METHOD_LIST; 

     return null; 

    } 

    @Override 
    public List<ConstructorInterceptor> getConstructorInterceptors(Constructor<?> constructor) { 
     // TODO Auto-generated method stub 
     return null; 
    } 

} 

ich diese Filter in meiner JerseyApplication Klasse bin Bindung:

register(new AbstractBinder() { 
      @Override 
      protected void configure() { 
       try { 

        bind(HK2InterceptorFilter.class).to(InterceptionService.class).in(Singleton.class); 
        bind(getPasswordStorage()).to(PasswordStorage.class); 
        bind(getDocumentService()).to(DocumentService.class); 
        bind(UserManagementAccessor.getUserProvider()).to(UserProvider.class); 
       } catch (Exception e) { 
        throw new InternalServerErrorException(e); 
       } 
      } 
     }); 

Wenn Sie einen Haltepunkt in meinem Interceptor gesetzt Ich kann sehen, dass es richtig instanziiert ist und die Methode gots aufgerufen hat. Aber was ich total vermisse, sind all die @Inject-Felder, die ich haben muss, um meinen Check zu machen. Fehle ich etwas oder ist das in HK2 nicht möglich? Ich habe mit Guice gearbeitet und dort funktioniert es (ich bin - aufgrund der Tatsache, dass die Code-Basis der App ziemlich groß ist, aber zeitlich begrenzt - an HK2 gebunden :)).

Vielen Dank für Ihre Hilfe im Voraus!

PS:

I Jersey bin mit 2,17

Antwort

1

Das Problem ist der Abfangjäger nie durch den DI-Lebenszyklus geht, denn wenn kein Service im Behälter ist. Sie instanziieren es selbst. Wenn Sie dies in einem DI-Framework tun, können Sie das meiste erwarten.

Was Sie können tun, obwohl explizit injizieren Sie es selbst mit HK2 Container, die ServiceLocator. Sie injizieren den Locator in die InterceptionService und rufen Sie dann locator.inject(interceptor). Diese Methode ist eine allgemeine Methode zur expliziten Injektion von beliebigen. So könnte man es etwas ändern wie

private final List<MethodInterceptor> PROJECT_CHECK_METHOD_LIST; 

@Inject 
public HK2InterceptorFilter(ServiceLocator locator) { 
    final MethodIntercator i = new ProjectOwnerCheckInterceptor(); 
    locator.inject(i) 
    PROJECT_CHECK_METHOD_LIST = Collections.singletonList(i); 
} 

Das andere Problem, das Sie konfrontiert werden, ist, dass die Abfangjäger ein Singleton ist, aber alle Service, den Sie versuchen, in Blick zu injizieren, wie sie sind Anfrage scoped. Dies ist ein Problem, da sie auf Anfrage geändert werden müssen. Dafür können wir sie zu Proxies machen. Wir können dies einfach tun, indem Sie ein paar Methode in der Bindung

Verkettungs Auch
bind(getPasswordStorage()) 
    .to(PasswordStorage.class) 
    .proxy(true) 
    .proxyForSameScope(false) 
    .in(RequestScoped.class); 

Siehe:Injecting Request Scoped Objects into Singleton Scoped Object with HK2 and Jersey

Im Folgenden ein vollständiges Beispiel ist Jersey Test Framework verwenden.

import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target; 
import java.lang.reflect.Constructor; 
import java.lang.reflect.Method; 
import java.util.Collections; 
import java.util.List; 
import java.util.logging.Logger; 

import javax.inject.Inject; 
import javax.inject.Singleton; 
import javax.ws.rs.GET; 
import javax.ws.rs.Path; 
import javax.ws.rs.core.Context; 
import javax.ws.rs.core.HttpHeaders; 
import javax.ws.rs.core.Response; 

import org.aopalliance.intercept.ConstructorInterceptor; 
import org.aopalliance.intercept.MethodInterceptor; 
import org.aopalliance.intercept.MethodInvocation; 
import org.glassfish.hk2.api.Filter; 
import org.glassfish.hk2.api.InterceptionService; 
import org.glassfish.hk2.api.ServiceLocator; 
import org.glassfish.hk2.utilities.BuilderHelper; 
import org.glassfish.hk2.utilities.binding.AbstractBinder; 
import org.glassfish.jersey.filter.LoggingFilter; 
import org.glassfish.jersey.process.internal.RequestScoped; 
import org.glassfish.jersey.server.ResourceConfig; 
import org.glassfish.jersey.test.JerseyTest; 
import org.junit.Test; 

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

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

    public static interface HeaderProvider { 
     String getXCustomHeader(); 
    } 

    public static class HeaderProviderImpl implements HeaderProvider { 
     @Context 
     private HttpHeaders headers; 

     @Override 
     public String getXCustomHeader() { 
      return headers.getHeaderString("X-Custom-Header"); 
     } 
    } 

    @Target(ElementType.METHOD) 
    @Retention(RetentionPolicy.RUNTIME) 
    public static @interface Intercept { 
    } 

    public static class MyMethodInterceptor implements MethodInterceptor { 

     @Inject 
     private HeaderProvider provider; 

     @Override 
     public Object invoke(MethodInvocation invocation) throws Throwable { 
      return provider.getClass().getName() + ":" + provider.getXCustomHeader(); 
     } 

    } 

    public static class InterceptionHandler implements InterceptionService { 

     private final List<MethodInterceptor> interceptors; 

     @Inject 
     public InterceptionHandler(ServiceLocator locator) { 
      final MethodInterceptor interceptor = new MyMethodInterceptor(); 
      locator.inject(interceptor); 
      interceptors = Collections.singletonList(interceptor); 
     } 

     @Override 
     public Filter getDescriptorFilter() { 
      return BuilderHelper.allFilter(); 
     } 

     @Override 
     public List<MethodInterceptor> getMethodInterceptors(Method method) { 
      if (method.isAnnotationPresent(Intercept.class)) { 
       return interceptors; 
      } 
      return null; 
     } 

     @Override 
     public List<ConstructorInterceptor> getConstructorInterceptors(Constructor<?> c) { 
      return null; 
     } 
    } 

    public static class Binder extends AbstractBinder { 
     @Override 
     protected void configure() { 
      bind(InterceptionHandler.class) 
        .to(InterceptionService.class) 
        .in(Singleton.class); 
      bind(HeaderProviderImpl.class) 
        .to(HeaderProvider.class) 
        .proxy(true) 
        .proxyForSameScope(false) 
        .in(RequestScoped.class); 
     } 
    } 

    @Path("intercept") 
    public static class TestResource { 

     @GET 
     @Intercept 
     public String get() { 
      return null; 
     } 
    } 

    @Override 
    public ResourceConfig configure() { 
     return new ResourceConfig(TestResource.class) 
       .register(new Binder()) 
       .register(new LoggingFilter(Logger.getAnonymousLogger(), true));   
    } 

    @Test 
    public void shouldReturnHeaderAndProxyClass() { 
     Response response = target("intercept").request() 
       .header("X-Custom-Header", "Value1") 
       .get(); 
     assertThat(response.getStatus(), is(200)); 
     String entity = response.readEntity(String.class); 
     response.close(); 
     assertThat(entity, containsString("Value1")); 
     assertThat(entity, containsString("Proxy")); 

     // Change header to make sure we aren't getting the same HttpHeaders instance 
     response = target("intercept").request() 
       .header("X-Custom-Header", "Value2") 
       .get(); 
     assertThat(response.getStatus(), is(200)); 
     entity = response.readEntity(String.class); 
     response.close(); 
     assertThat(entity, containsString("Value2")); 
     assertThat(entity, containsString("Proxy")); 
    } 
} 
+0

Dies löste es !! VIELEN DANK!! – Inkvine