2013-05-07 4 views
5

Ich habe eine schwere Zeit, den Einspritzmechanismus von Jersey zu verstehen. Die JAX-RS-Spezifikation (http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-520005) besagt, dass die Injektion über @Context in Anwendungsunterklassen, Stammressourcenklassen und Anbietern möglich ist.Jersey @Context scope

Ich habe jetzt eine Klasse, die beim Start instanziiert wird und eine Methode hat, die bei jeder Anforderung aufgerufen wird. Innerhalb der Methode brauche ich Zugriff auf das aktuelle UriInfo-Objekt. Das Problem ist, dass diese Methode nicht aus meinem Code aufgerufen wird. Also kann ich UriInfo nicht direkt an die Methode übergeben.

Ich möchte eigentlich so etwas wie dies zu tun:

public class MyClass implements ThirdPartyInterface { 

    // not possible because class is no Application subclass, root resource class or provider 
    @Context 
    private UriInfo uriInfo; 

    public void methodCallebByThirdPartyCode() { 
     Uri requestUri = uriInfo.getRequestUri(); 

     // do something 
    } 
} 

ich dies versucht. Offensichtlich ohne Erfolg:

public class MyClass implements ThirdPartyInterface { 

    private UriInfo uriInfo; 

    public MyClass(UriInfo uriInfo) { 
     this.uriInfo = uriInfo; 
    } 

    public void methodCallebByThirdPartyCode() { 
     Uri requestUri = uriInfo.getRequestUri(); 

     // do something 
    } 
} 

@Provider 
@Produces(MediaType.WILDCARD) 
public class MyBodyWriter implements MessageBodyWriter<MyView> { 

    @Context 
    private UriInfo uriInfo; 

    private MyClass myClass; 

    private ThirdPartyClass thirdPartyClass; 

    public MyBodyWriter() { 
     // uriInfo is null at this time :(
     myClass = new MyClass(uriInfo); 

     thirdPartyClass = new ThirdPartyClass(); 
     thirdPartyClass.register(myClass); 
    } 

    public void writeTo(final MyView view, final Class<?> type, /* and so on */) throws IOException, WebApplicationException { 
     // execute() calls MyClass#methodCallebByThirdPartyCode() 
     thirdPartyClass.execute(); 
    } 
} 

Die einzige Problemumgehung, an die ich denken kann, ist diese. Ich glaube nicht, dass es sehr sauber ist:

public class MyClass implements ThirdPartyInterface { 

    private UriInfo uriInfo; 

    public void setUriInfo(final UriInfo uriInfo) { 
     this.uriInfo = uriInfo; 
    } 

    public void methodCallebByThirdPartyCode() { 
     Uri requestUri = uriInfo.getRequestUri(); 

     // do something 
    } 
} 

@Provider 
@Produces(MediaType.WILDCARD) 
public class MyBodyWriter implements MessageBodyWriter<MyView> { 

    @Context 
    private UriInfo uriInfo; 

    private MyClass myClass; 

    private ThirdPartyClass thirdPartyClass; 

    public MyBodyWriter() { 
     myClass = new MyClass(); 

     thirdPartyClass = new ThirdPartyClass(); 
     thirdPartyClass.register(myClass); 
    } 

    public void writeTo(final MyView view, final Class<?> type, /* and so on */) throws IOException, WebApplicationException { 
     myClass.setUriInfo(uriInfo); 

     // execute() calls MyClass#methodCallebByThirdPartyCode() 
     thirdPartyClass.execute(); 

     myClass.setUriInfo(null); 
    } 
} 

Ich hoffe, es gibt eine bessere Lösung, aber vielleicht bin ich völlig auf der falschen Spur.

Danke!

+0

Vielleicht brauchen Sie nur 'ContainerRequestFilter'? – Willy

+0

Ich weiß nicht, ob das in meiner Situation funktioniert. Die Spezifikation besagt, dass Provider-Klassen von der JAX-RS-Laufzeit instanziiert werden. Aber ich brauche einen Hinweis auf das Objekt zur Bauzeit, um es an den Third-Party-Service zu übergeben. –

Antwort

3

Späte Antwort, aber eine gute Frage ... kann so gehen:

Sie ein org.glassfish.hk2.api.Factory und javax.inject.Provider für Injektionszwecke verwenden können. Ich weiß nicht, seit welcher Version es verfügbar ist, also müssen Sie vielleicht Ihre Jersery-Version aktualisieren. Für die folgenden Beispiele habe ich jersey 2.12 verwendet.

Zuerst müssen Sie implementieren und registrieren/binden, um eine Fabrik für Ihre MyClass:

MyClassFactory:

import javax.inject.Inject; 
import javax.ws.rs.core.UriInfo; 
import org.glassfish.hk2.api.Factory; 
// ... 

public class MyClassFactory implements Factory<MyClass> { 

    private final UriInfo uriInfo; 

    // we will bind MyClassFactory per lookup later, so 
    // the constructor will be called everytime we need the factory 
    // meaning, uriInfo is also per lookup 

    @Inject 
    public MyClassFactory(final UriInfo uriInfo) { 
     this.uriInfo = uriInfo; 
    } 

    @Override 
    public MyClass provide() { 
     return new MyClass(uriInfo) 
    } 

    @Override 
    public void dispose(UriInfo uriInfo) { 
     // ignore 
    } 

} 

Anmeldung über ResourceConfig:

import org.glassfish.hk2.api.PerLookup; 
import org.glassfish.hk2.utilities.binding.AbstractBinder; 
import org.glassfish.jersey.server.ResourceConfig; 
// ... 

public class MyResourceConfig extends ResourceConfig { 

    public MyResourceConfig() { 
     register(new AbstractBinder() { 
      @Override 
      protected void configure() { 
       bindFactory(MyClassFactory.class).to(MyClass.class).in(PerLookup.class); 
       // ... bind additional factories here 
      } 
     }); 
     // ... 
    } 

} 

Jetzt können Sie MyClass injizieren per Nachschlagen nach Providern, Ressourcen usw.
Aber Achtung: Afaig gibt es zwei Ansätze es und nur einer wird funktionieren, wie schließlich für Anbieter erwartet ...

import javax.inject.Inject; 
import javax.ws.rs.Produces; 
import javax.ws.rs.ext.MessageBodyWriter; 
import javax.ws.rs.ext.Provider; 
// ... 

@Provider 
@Produces("application/foo-bar") 
public class MyBodyWriter implements MessageBodyWriter<MyView> { 

    // first approache - don't do it! 
    // will only injected once, cause MyBodyWriter is only instantiated once 
    @Inject 
    private MyClass myClass; 

    // second approache - works fine! 
    private final javax.inject.Provider<MyClass> provider; 

    // MyBodyWriter instantiate once 
    // get an inject provider here 
    @Inject 
    public MyBodyWriter(javax.inject.Provider<MyClass> myClassProvider) { 
     this.provider = myClassProvider; 
    } 

    @Override 
    public boolean isWriteable(Class<?> t, Type g, Annotation[] a, MediaType m) { 
     return t == MyView.class; 
    } 

    @Override 
    public long getSize(MyView t, Class<?> c, Type g, Annotation[] a, MediaType m) { 
     // deprecated by JAX-RS 2.0 and ignored by Jersey runtime 
     return 0; 
    } 

    @Override 
    public void writeTo(MyView v, Class<?> c, Type t, Annotation[] a, MediaType m, MultivaluedMap<String, Object> s, OutputStream o) throws IOException, WebApplicationException { 

     // attention: its not per lookup !!! 
     MyClass myClassDirectInjected = myClass; 
     System.out.println(myClassDirectInjected); // same instance everytime 

     // but this is ;) 
     MyClass myClassFromProvider = provider.get(); 
     System.out.println(myClassFromProvider); // it's a new instance everytime 

     // ... 
    } 

} 

Ich hoffe, das war irgendwie hilfreich.