Thanks to migration to jersey 2 I need to migrate from guice to HK2. I have an Assisted injection approach for some of my dependencies which I couldn't get my head around to implement in HK2. It looks like it's supposed to be solved via Custom Injection Resolvers but I don't really see how. The examples are not clear enough for me..
Here is how it looks on Guice:
public interface MyFactory {
public MyClass createMyClass(#Assisted String dynamicParameter);
public HisClass createHisClass(#Assisted String dynamicParameter);
...
}
binder.install(new FactoryModuleBuilder().build(MyFactory.class));
public class MyClass {
...
#Inject
public MyClass(#Assisted String dynamicParameter, SomeService someOtherServiceInjectedAutomatically){
...
}
}
How can I implement this on HK2?
After posting the question I thought of doing this:
public class MyFactoryImpl implements MyFactory{
private final SomeService someService;
#Inject
public MyFactoryImpl(SomeService someService){
this.someService = someService;
}
public MyClass createMyClass(String dynamicParameter){
return new MyClass(dynamicParameter, someService);
}
...
}
There is a Guice-Bridge :-D
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>guice-bridge</artifactId>
<version>${hk2.version}</version>
</dependency>
Here is an example using Guice 3.0 and HK2 2.3.0 (which comes bundled with Jersey 2.13). This is just a standalone, but it should work in Jersey environment just the same.
Guice classes
public class GuiceGreeter {
public String getGreeting(String name) {
return "Hello, " + name;
}
}
import com.google.inject.assistedinject.Assisted;
import javax.inject.Inject;
public class Message {
private final String message;
#Inject
public Message(GuiceGreeter greeter, #Assisted String name) {
message = greeter.getGreeting(name);
}
public String getMessage() {
return message;
}
}
public interface GuiceMessageFactory {
public Message getMessage(String name);
}
import com.google.inject.AbstractModule;
import com.google.inject.assistedinject.FactoryModuleBuilder;
public class GuiceMessageModule extends AbstractModule {
#Override
protected void configure() {
install(new FactoryModuleBuilder().build(GuiceMessageFactory.class));
bind(GuiceGreeter.class);
}
}
HK2 service, which injects the Guice factory
import javax.inject.Inject;
public class HK2Service {
private final GuiceMessageFactory messageFactory;
#Inject
public HK2Service(GuiceMessageFactory messageFactory) {
this.messageFactory = messageFactory;
}
public void printMessage(String name) {
System.out.println(messageFactory.getMessage(name).getMessage());
}
}
Main
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.api.ServiceLocatorFactory;
import org.glassfish.hk2.utilities.ServiceLocatorUtilities;
import org.jvnet.hk2.guice.bridge.api.GuiceBridge;
import org.jvnet.hk2.guice.bridge.api.GuiceIntoHK2Bridge;
public class Main {
public static void main(String[] args) {
// Create service locator. In Jersey context, you should be able to
// inject the `ServiceLocator` into the `Application/ResourceConfig`
// subclass constructor, or as a field
ServiceLocatorFactory factory = ServiceLocatorFactory.getInstance();
ServiceLocator locator = factory.create("SimpleServiceLocator");
// bridge the two frameworks to allow Guice injected services
GuiceBridge.getGuiceBridge().initializeGuiceBridge(locator);
Injector injector = Guice.createInjector(new GuiceMessageModule());
GuiceIntoHK2Bridge guiceBridge = locator.getService(GuiceIntoHK2Bridge.class);
guiceBridge.bridgeGuiceInjector(injector);
// Add my HK2 Service
ServiceLocatorUtilities.addClasses(locator, HK2Service.class);
// Look up HK2 service. If this lookup works, `#Inject` in Jersey should.
HK2Service service = locator.getService(HK2Service.class);
service.printMessage("peeskillet");
}
}
This prints out "Hello, peeskillet". See comment below main method to obtain ServiceLocator in Jersey app. And in case you are unfamailiar with the ServiceLocator, all the bindings you add with an AbstractBinder will get put in the service locator context also, so you don't have to explicitly add the class as I am going above with HK2Service.
Related
I want to geta String in my Dropwizard config.yml and access it from a resource class.
I have added the class to the configuration
public class DropwizardBackendConfiguration extends Configuration {
#NotEmpty
private String uploadFileLocation;
#JsonProperty
public String getUploadFileLocation() {
return uploadFileLocation;
}
#JsonProperty
public void setUploadFileLocation(String uploadFileLocation) {
this.uploadFileLocation = uploadFileLocation;
}
}
I am able to get the content in the run method
public void run(
final DropwizardBackendConfiguration configuration, final Environment environment) {
...
System.out.println(configuration.getUploadFileLocation());
}
But how can I get this value in my resource class.
If you want to use the complete DropwizardBackendConfiguration or just the uploadFileLocation in a Jersey Resource, you will have to pass it as a constructor argument.
The Getting Started guide illustrates this with the HelloWorldResource. In this example there are two constructor arguments:
public HelloWorldResource(String template, String defaultName)
An instance of this class is registered in the run method:
#Override
public void run(HelloWorldConfiguration configuration,
Environment environment) {
final HelloWorldResource resource = new HelloWorldResource(
configuration.getTemplate(),
configuration.getDefaultName()
);
environment.jersey().register(resource);
}
Do something similar using your configuration and your resource class.
It may be probably late but you this can be done by dropwizard-guice dependency, this library used Google guice for dependency injection using annotations to configure Java objects. As an extract from this article by Ricky Yim
You could can inject the properties into the resource like below
package com.github.codingricky;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
#Path("/hello")
public class HelloResource {
private final String message;
#Inject
public HelloResource(#Named("message") String message) {
this.message = message;
}
#GET
public String hello() {
return message;
}
}
These values are picked from your .yml configuration using modules,
public class ServerModule implements Module {
#Override
public void configure(Binder binder) {
}
#Provides
#Named("message")
public String provideMessage(ServerConfiguration serverConfiguration) {
return serverConfiguration.getMessage();
}
}
Kindly look at the most recent library
Is there any way to inject dependencies into manually created objects?
public class MyCommand {
#Inject Repository repository;
}
public Repository {
#Inject EntityManager em;
}
MyCommand command = new MyCommand();
Repository is properly registered the jersey ResourceConfig and can be injected in objects that are created through the CDI container for example a resource class.
But since I create the Command myself the #Inject annotation gets ignored.
Is there a way to get a registered class beside #Inject and #Context?
Something like Application.get(Repository.class)
public class MyCommand {
Repository repository;
public MyCommand() {
repository = Application.get(Repository.class);
}
}
----- EDIT -----
Thanks to your help and some rethinking I found a solution for my problem.
The first thing is that it's possible to inject the ServiceLocator without any preperation into you objects.
The second thing is that I moved from normal commands with a execute method to a a command bus system.
The reason for that is I have no controle over the creation of commands so there clean way to get dependencies injected.
The new approach looks like this:
class CommandBus {
private final ServiceLocator serviceLocator;
#Inject
public CommandBus(ServiceLocator serviceLocator) {
this.serviceLocator = serviceLocator;
}
public void dispatch(Command command) {
Class handlerClass = findHandlerClassForCommand(command);
CommandHandler handler = (CommandHandler) serviceLocator.getService(handlerClass);
handler.handle(command);
}
}
interface CommandHandler {
void handle(Command command);
}
interface Command {
}
class ConcreteCommand implements Command {
// I'm just a dto with getters and setters
}
class ConcreteHandler implements CommandHandler {
private final SomeDependency dependency;
#Inject
public ConcreteHandler(SomeDependency dependency) {
this.dependency = dependency;
}
#Override
public void handle(ConcreteCommand command) {
// do some things
}
}
And in my resources I have something like this:
#Path("/some-resource")
class Resource {
#Context
private CommandBus bus;
#POST
#Consumes(MediaType.APPLICATION_JSON)
public void runCommand(ConcreteCommand command) {
bus.dispatch(command);
}
}
As pointed out by jwells - HK2 is an injection framework :)
I spent some time looking into it - I have to say, I find it much more complicated than say guice or spring. Maybe this is due to the fact that I use Dropwizard and it makes it not as easy to access the Service locators.
However, here is how you can do that.
First, you will have to get a reference to your ServiceLocator. It must be the same ServiceLocator that jersey is using as well. You can access it for example like:
How to get HK2 ServiceLocator in Jersey 2.12?
In my example code I will use an event listener, which is due to my Dropwizard Setup.
You now have 2 choices: Register your command with your Service Locator and have the injection framework handle creation, or pass the ServiceLocator to your command in order to use it.
I wrote up a quick example using Dropwizard and jersey:
public class ViewApplication extends io.dropwizard.Application<Configuration> {
#Override
public void run(Configuration configuration, Environment environment) throws Exception {
environment.jersey().register(new ApplicationEventListener() {
#Override
public void onEvent(ApplicationEvent event) {
if (event.getType() == ApplicationEvent.Type.INITIALIZATION_FINISHED) {
ServiceLocator serviceLocator = ((ServletContainer) environment.getJerseyServletContainer())
.getApplicationHandler().getServiceLocator();
ServiceLocatorUtilities.bind(serviceLocator, new AbstractBinder() {
#Override
protected void configure() {
bind(new Repository("test")).to(Repository.class);
bind(MyCommandInjected.class).to(MyCommandInjected.class);
}
});
MyCommandInjected service = serviceLocator.getService(MyCommandInjected.class);
MyCommandManual tmp = new MyCommandManual(serviceLocator);
}
}
#Override
public RequestEventListener onRequest(RequestEvent requestEvent) {
return null;
}
});
}
#Override
public void initialize(Bootstrap<Configuration> bootstrap) {
super.initialize(bootstrap);
}
public static void main(String[] args) throws Exception {
new ViewApplication().run("server", "/home/artur/dev/repo/sandbox/src/main/resources/config/test.yaml");
}
#Path("test")
#Produces(MediaType.APPLICATION_JSON)
public static class HelloResource {
#GET
#Path("asd")
public String test(String x) {
return "Hello";
}
}
public static class Repository {
#Inject
public Repository(String something) {
}
}
public static class MyCommandInjected {
#Inject
public MyCommandInjected(final Repository repo) {
System.out.println("Repo injected " + repo);
}
}
public static class MyCommandManual {
public MyCommandManual(final ServiceLocator sl) {
Repository service = sl.getService(Repository.class);
System.out.println("Repo found: " + service);
}
}
}
In the Run method, i get access to my ServiceLocator. I bind my classes in there (so there is an example of how to do that). You can alternatively also register Binders with jersey directly - they will use the correct ServiceLocator.
The 2 classes MyCommandInjected and MyCommandManual are examples of how you can create this command.
The relevant line for you is probably:
Repository service = sl.getService(Repository.class);
This asks the service locator for a new instance of the Repository.
Now, this is just a quick example. I am much more fond of the guice bridge than using HK2 directly :) I find it much easier to use and much clearer. Using the guice-jersey-bridge you can do everything through guice and it will automatically do the right thing.
Hope that brings some inside,
Artur
You can use the inject method of ServiceLocator in order to inject already created objects. ServiceLocator is the basic registry of HK2 and should be available in your resource.
Java 8, Guice 4.0 and Akka 2.3.9 here. I am trying to figure out how to annotate my actor classes with JSR330-style #Inject annotations, and then wire them all up via Guice.
But literally every single article I have read (some examples below) either uses Scala code examples, a criminally-old version of Guice, or a criminally-old version of Akka:
Let It Crash
Scala-Guice
So, given the following Guice module:
public interface MyService {
void doSomething();
}
public class MyServiceImpl implements MyService {
#Override
public void doSomething() {
System.out.println("Something has been done!");
}
}
public class MyActorSystemModule extends AbstractModule {
#Override
public void configure() {
bind(MyService.class).to(MyServiceImpl.class);
}
}
And given the FizzActor that gets injected with a MyService:
public class FizzActor extends UntypedActor {
private final MyService myService;
#Inject
public FizzActor(MyService myService) {
super();
this.myService = myService;
}
#Override
public void onReceive(Object message) {
// .. Do fizz stuff inside here.
}
}
Then I ask: How do I rig up MyActorSystemModule to create instances of FizzActor and properly inject them with Java (not Scala!)?
Please note: FizzActor is not the only actor in my actor system!
Use Creator to create ActorRefs in provider methods of your guice module. To distinguish between the different ActorRefs, which are untyped, use annotations on your provider methods and injection points as you would any guice system. For example,
In your guice module:
#Override
protected void configure() {
bind(ActorSystem.class).toInstance(ActorSystem.apply());
bind(FizzService.class).toInstance(new FizzServiceImpl());
}
#Provides #Singleton #Named("fizzActor")
ActorRef serviceActorRef(final ActorSystem system, final FizzService fizzService) {
return system.actorOf(Props.create(new Creator<Actor>() {
#Override
public Actor create() throws Exception {
return new FizzActor(fizzService);
}
}));
}
Then to use the actor service, inject a specific ActorRef:
class ClientOfFizzActor {
#Inject
ClientOfFizzActor(#Named("fizzActor") ActorRef fizzActorRef) {..}
}
It looks cleaner if the Props.create(..) clause is a static factory method in your actor class.
Unless you are trying to bind UntypedActor to FizzActor, then you can just inject it into other classes as is:
class SomeOtherClass {
#Inject
public SomeOtherClass(FizzActor fizzActor) {
//do stuff
}
}
If you're trying to bind it to the interface, you'll need to specifically do that in the module:
public class MyActorSystemModule extends AbstractModule {
#Override
public void configure() {
bind(MyService.class).to(MyServiceImpl.class);
bind(UntypedActor.class).to(FizzActor.class);
}
}
Edit:
What about using #Named to distinguish the UntypedActor, e.g.:
class SomeOtherClass {
#Inject
public SomeOtherClass(#Named("fizzActor")UntypedActor fizzActor, #Named("fooActor") UntypedActor fooActor) {
//do stuff
}
}
Then in your module you could do the akka lookups:
public class MyActorSystemModule extends AbstractModule {
ActorSystem system = ActorSystem.create("MySystem");
#Override
public void configure() {
bind(MyService.class).to(MyServiceImpl.class);
}
#Provides
#Named("fizzActor")
public UntypedActor getFizzActor() {
return system.actorOf(Props.create(FizzActor.class), "fizzActor");
}
#Provides
#Named("fooActor")
public UntypedActor getFooActor() {
return system.actorOf(Props.create(FooActor.class), "fooActor");
}
}
Use an akka Creator:
public class GuiceCreator<T> implements Creator<T> {
Class<T> clz;
Module module;
/*Constructor*/
public T create() {
Injector injector = Guice.createInjector(this.module);
return injector.getInstance(this.clz);
}
}
Then use Props.create with your shiny new guice-based creator.
Disclaimer: I don't actually know Akka, the mentioned information comes from browsing the documentation and JavaDoc.
In case anyone found this question, you need to use IndirectActorProducer, I referred to the Spring example and changed it to use Guice instead.
/**
* An actor producer that lets Guice create the Actor instances.
*/
public class GuiceActorProducer implements IndirectActorProducer {
final String actorBeanName;
final Injector injector;
final Class<? extends Actor> actorClass;
public GuiceActorProducer(Injector injector, String actorBeanName, Class<? extends Actor> actorClass) {
this.actorBeanName = actorBeanName;
this.injector = injector;
this.actorClass = actorClass;
}
#Override
public Actor produce() {
return injector.getInstance(Key.get(Actor.class, Names.named(actorBeanName)));
}
#Override
public Class<? extends Actor> actorClass() {
return actorClass;
}
}
In the module
public class BookingModule extends AbstractModule {
#Override
protected void configure() {
// Raw actor class, meant to be used by GuiceActorProducer.
// Do not use this directly
bind(Actor.class).annotatedWith(
Names.named(BookingActor.ACTOR_BEAN_NAME)).to(
BookingActor.class);
}
#Singleton
#Provides
#Named(BookingActor.ACTOR_ROUTER_BEAN_NAME)
ActorRef systemActorRouter(Injector injector, ActorSystem actorSystem) {
Props props = Props.create(GuiceActorProducer.class, injector, BookingActor.ACTOR_BEAN_NAME, actorClass);
actorSystem.actorOf(props.withRouter(new RoundRobinPool(DEFAULT_ROUTER_SIZE)), BookingActor.ACTOR_ROUTER_BEAN_NAME);
}
}
So I have been playing around with Akka and Guice recently alot and I feel that those two don't play too well together.
What I suggest is you take a similar approach what Play is doing.
Kutschkem's answer comes closest to that.
use the ActorCreator interface
make sure you have an argumentless Creator. Don't try to do #AssisstedInject in your Creator as this will imply that you will need a new creator for every Actor that you want to create. Personally I believe that initializing this in the actor is better done through messaging.
let the ActorCreator consume an injector such that you can easily create the Actor Object within the Creator.
Here is a code example using current Akka 2.5. This is the preferred setup we chose for our Akka 2.5 deployment. For brevity I did not provide the Module, but it should be clear from the way the Members are injected, what you want to provide.
Code:
class ActorCreator implements Creator<MyActor>
#Inject
Injector injector;
public MyActor create() {
return injector.getInstance(MyActor.class);
}
}
class MyActor extends AbstractActor {
#Inject
SomeController object;
#Nullable
MyDataObject data;
public ReceiveBuilder createReceiveBuilder() {
return receiveBuilder()
.match(MyDataObject.class, m -> { /* doInitialize() */ })
.build();
}
}
class MyParentActor extends AbstractActor {
#Inject
ActorCreator creator;
void createChild() {
getContext().actorOf(new Props(creator));
}
void initializeChild(ActorRef child, MyDataObject obj) {
child.tell(obj);
}
}
Generic Akka Guice integration without dependency on Play,
keeping in mind, not the only actor should be created in the actor system.
import akka.actor.Actor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import com.google.inject.AbstractModule;
import com.google.inject.Provider;
import com.google.inject.name.Names;
public abstract class AkkaGuiceModule extends AbstractModule {
protected <T extends Actor> void bindActor(Class<T> actorClass, String name) {
bind(actorClass);
Provider<ActorSystem> actorSystemProvider = getProvider(ActorSystem.class);
Provider<T> actorProvider = getProvider(actorClass);
bind(ActorRef.class)
.annotatedWith(Names.named(name))
.toProvider(ActorRefProvider.of(actorSystemProvider, actorProvider, name))
.asEagerSingleton();
}
}
Generic ActorRefProvider to create ActorRef for each Actor
import akka.actor.Actor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.japi.Creator;
import com.google.inject.Provider;
import lombok.Value;
#Value(staticConstructor = "of")
public class ActorRefProvider<T extends Actor> implements Provider<ActorRef> {
private Provider<ActorSystem> actorSystemProvider;
private Provider<T> actorProvider;
private String name;
public final class ActorCreator implements Creator<Actor> {
#Override
public Actor create() {
return actorProvider.get();
}
}
#Override
public ActorRef get() {
return actorSystemProvider.get().actorOf(Props.create(new ActorCreator()), name);
}
}
Usage example
import akka.actor.ActorSystem;
import com.google.inject.Provides;
import com.typesafe.config.Config; // optional
public class MyAkkaModule extends AkkaGuiceModule {
#Provides
#Singleton
ActorSystem actorSystem(Config config) {
return ActorSystem.create("actor-system-name", config);
}
#Override
protected void configure() {
bindActor(SomeActor1.class, "actorName1");
bindActor(SomeActor2.class, "actorName2");
}
}
I am playing with Dagger on Android. I created a model UserPreference, a module called PreferenceModule and another class UserPreferenceTest which is a test of the PreferenceModule. I have below 3 java files
UserPreference.java
package com.sigicn.preference;
import javax.inject.Inject;
import com.sigicn.commonmodels.Application;
public class UserPreference {
public String name, weiboAccount;
#Inject
public Application[] frequentlyUsedApps;
}
Then PreferenceModule.java
package com.sigicn.preference;
import javax.inject.Singleton;
import com.sigicn.commonmodels.Application;
import com.sigicn.utils.MiscUtils;
import dagger.Module;
import dagger.Provides;
#Module(library = true, complete = true)
public class PreferenceModule {
#Provides #Singleton UserPreference provideUserPreference() {
UserPreference userPreference = new UserPreference();
userPreference.frequentlyUsedApps = provideApplications();
return userPreference;
}
#Provides #Singleton Application[] provideApplications() {
return new Application[]{
new Application(
MiscUtils.generateUUID(), "Youtube"),
new Application(
MiscUtils.generateUUID(), "Pixi")
};
}
}
Then UserPreferenceTest.java
package com.sigicn.test.preference;
import javax.inject.Inject;
import com.sigicn.preference.PreferenceModule;
import com.sigicn.preference.UserPreference;
import dagger.Module;
import dagger.ObjectGraph;
import android.test.AndroidTestCase;
public class UserPreferenceTest extends AndroidTestCase {
#Module(injects = {UserPreference.class, UserPreferenceTest.class},
includes = PreferenceModule.class)
static class TestModule {
}
ObjectGraph objectGraph;
#Inject
UserPreference userPreference;
#Override
protected void setUp() throws Exception {
if (objectGraph == null) {
objectGraph = ObjectGraph.create(new TestModule());
}
super.setUp();
}
public void testFrequentlyUsedApps()
{
UserPreference localUserPreference = objectGraph.get(UserPreference.class);
assertNotNull(localUserPreference);
assertEquals(localUserPreference.frequentlyUsedApps.length, 2);
objectGraph.inject(this);
assertNotNull(userPreference);
assertEquals(userPreference.frequentlyUsedApps.length, 2);
assertSame(localUserPreference, userPreference);
assertSame(localUserPreference.frequentlyUsedApps, userPreference.frequentlyUsedApps);
}
}
But don't know why, that the frequentlyUsedApps of UserPreference is not injected as expected. Any idea why?
Update:
I think I have figured out the reason. It's because that I manually create UserPreference and use it in the provider. If I remove the Provider for UserPreference, and let Dagger to wire it automatically, then the field frequentlyUsedApps does get injected. So it is my fault of not understanding Dagger well.
I think you need to add some ObjectGraph#inject calls.
In each class where you have an #Inject annotation, you will also need a call to the inject method of the ObjectGraph you created.
I have had been struggling with this for a while also. I think the basic pattern is:
Annotate your fields to indicate you want to inject them
Create a module to "provide" the instances for those #Injects
Create the graph somewhere (seems like most people are doing that in
the Application class)
In the classes you want to inject stuff from your module, get an
instance of the graph and call inject(this).
I started using a singleton rather than the Application class, because at least for now I have some places were I want to inject the app itself.
So here is what I am currently doing, which seems to work pretty weill
public class Injector {
private static Injector mInjector;
private ObjectGraph mObjectGraph;
private MyApp mApp;
private Injector() {
}
public static Injector getInstance() {
if (mInjector == null) {
mInjector = new Injector();
}
return mInjector;
}
protected List<Object> getModules() {
return Arrays.asList(
new ApplicationModule(mApp),
new AndroidModule(mApp)
);
}
public void inject(Object object) {
getObjectGraph().inject(object);
}
public ObjectGraph getObjectGraph() {
return mObjectGraph;
}
public void initialize(MyApp app) {
mApp = app;
mObjectGraph = ObjectGraph.create(getModules().toArray());
System.out.println(String.format("init object graph = %s",mObjectGraph.toString()));
}
}
Then in my application class I have a constructor like this:
public MyApp() {
System.out.println("myapp construtor");
Injector.getInstance().initialize(this);
Injector.getInstance().inject(this);
}
Then when I want to inject something I do this
#Inject Bus mBus;
public GcmBroadcastReceiver() {
Injector.getInstance().inject(this);
}
I have two modules , one for production and one for test
The production one has this
#Provides #Singleton
public Bus provideBus () {
return BusProvider.getInstance();
}
and the test one has this
#Provides #Singleton
public Bus provideBus () {
return mock(Bus.class);
}
I want to test a Resourse with JerseyTest. I have created the following test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:testApplicationContext.xml")
public class ResourceTest extends JerseyTest
{
#Configuration
public static class Config
{
#Bean
public AObject aObject()
{
return mock(AObject.class);
}
}
#Autowired
public AObject _aObject;
#Test
public void testResource()
{
// configouring mock _aObject
Response response = target("path");
Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
}
#Override
protected Application configure()
{
return new ResourceConfig(Resource.class).property("contextConfigLocation", "classpath:testApplicationContext.xml");
}
}
My Resource also has an AObject reference with #Autowired annotation.
My problem is that my JerseyTest and the Resource (that is configured by the test) have different instances for the Mock object. In the console I see that the testApplicationContext.xml is loaded twice, once for the test and one for the Resource.
How can I force jersey to use the same mock?
After debugging the jersey-spring3 (version 2.9.1) library it seems that the problem lies in the SpringComponentProvider.createSpringContext
private ApplicationContext createSpringContext() {
ApplicationHandler applicationHandler = locator.getService(ApplicationHandler.class);
ApplicationContext springContext = (ApplicationContext) applicationHandler.getConfiguration().getProperty(PARAM_SPRING_CONTEXT);
if (springContext == null) {
String contextConfigLocation = (String) applicationHandler.getConfiguration().getProperty(PARAM_CONTEXT_CONFIG_LOCATION);
springContext = createXmlSpringConfiguration(contextConfigLocation);
}
return springContext;
}
It checks if a property named "contextConfig" exists in the application properties and if not it initializes the spring application context.
Even if you initialized a spring application context in your tests, jersey will create another context and use that one instead. So we have to somehow pass the ApplicationContext from our tests in the Jersey Application class. The solution is the following:
#ContextConfiguration(locations = "classpath:jersey-spring-applicationContext.xml")
public abstract class JerseySpringTest
{
private JerseyTest _jerseyTest;
public final WebTarget target(final String path)
{
return _jerseyTest.target(path);
}
#Before
public void setup() throws Exception
{
_jerseyTest.setUp();
}
#After
public void tearDown() throws Exception
{
_jerseyTest.tearDown();
}
#Autowired
public void setApplicationContext(final ApplicationContext context)
{
_jerseyTest = new JerseyTest()
{
#Override
protected Application configure()
{
ResourceConfig application = JerseySpringTest.this.configure();
application.property("contextConfig", context);
return application;
}
};
}
protected abstract ResourceConfig configure();
}
The above class will take the application context from our tests and pass it to the configured ResourceConfig, so that the SpringComponentProvider will return the same application context to jersey. We also use the jersey-spring-applicationContext.xml in order to include jersey specific spring configuration.
We cannot inherit from JerseyTest because it initializes the Application in the constructor before the test application context is initialized.
You can now use this base class to create your tests for example
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:testContext.xml")
public class SomeTest extends JerseySpringTest
{
#Autowired
private AObject _aObject;
#Test
public void test()
{
// configure mock _aObject when(_aObject.method()).thenReturn() etc...
Response response = target("api/method").request(MediaType.APPLICATION_JSON).get();
Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
}
#Override
protected ResourceConfig configure()
{
return new ResourceConfig(MyResource.class);
}
}
In testContext.xml add the following definition in order to inject a mock AObject.
<bean class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.yourcompany.AObject" />
</bean>
I couldn't get the answer https://stackoverflow.com/a/24512682/156477 from #Grigoris working, although his explanation for why it is happening is correct.
In the end I went for the approach below which exposes a special setter to insert the mock object. Not as 'clean' as the approach above, but worth the tradeoff of exposing the apiProvider I wanted to mock so I could write some tests..
public MyAPITest extends JerseyTest {
// Declare instance of the API I want to test - this will be instantiated in configure()
MyAPI myAPI;
#Override
protected ResourceConfig configure()
{
MockitoAnnotations.initMocks(this);
myAPI = new MyAPI();
ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(MyAPI).property("contextConfig", new ClassPathXmlApplicationContext("classpath:spring.testHarnessContext.xml"));
return resourceConfig;
}
#Mock
private MyAPIProvider mockAPIProvider;
#Before
public void before() {
myAPI.setMockProvider(mockAPIProvider);
}
#Test
public void test() {
// I can now define the mock behaviours and call the API and validate the outcomes
when(mockAPIProvider....)
target().path("....)
}
}
If anyone is interested in the solution https://stackoverflow.com/a/40591082/4894900 from Kevin for Jersey v1:
public MyAPITest extends JerseyTest {
#InjectMocks
MyAPI myAPI;
#Mock
MyApiService myApiService;
#Override
protected AppDescriptorconfigure()
{
MockitoAnnotations.initMocks(this);
ResourceConfig rc = new DefaultResourceConfig();
rc.getSingletons().add(myAPI);
return new LowLevelAppDescriptor.Builder(rc).contextPath("context").build();
}
#Test
public void test() {
// I can now define the mock behaviours
when(myApiService...)
WebResource webResource = resource().path("mypath");
ClientResponse result = webResource.get(ClientResponse.class);
}
}
Further improvising the accepted solution by removing xml dependency. More details available here.
JerseySpringTest abstracting JerseyTest:
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.TestProperties;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
/** Run JerseyTest with custom spring context with mocks Mimics Spring #WebMvcTest with #MockBean */
public abstract class JerseySpringTest extends JerseyTest {
#Override
protected ResourceConfig configure() {
MockitoAnnotations.openMocks(this);
enable(TestProperties.LOG_TRAFFIC);
enable(TestProperties.DUMP_ENTITY);
set(TestProperties.CONTAINER_PORT, "0");
final ResourceConfig resourceConfig =
new ResourceConfig()
.property("contextConfig", createSpringContext(getBeanMap()))
.property(LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER, "WARNING")
.register(getResourceClass());
return serverConfig(resourceConfig);
}
/**
* Gives the test class opportunity to further customize the configuration. Like registering a
* MultiPartFeature if required.
*
* #param config
* #return
*/
protected ResourceConfig serverConfig(final ResourceConfig config) {
return config;
}
/**
* Supplies all the bean objects required to be loaded in the application context for the Resource class
* under test
*
* #return
*/
protected List<Object> getBeans() {
return Collections.emptyList();
}
/**
* Supplies all the bean objects with name qualifier required to be loaded in the application context for the Resource class
* under test
*
* #return
*/
protected Map<String, Object> getQualifiedBeans() {
return Collections.emptyMap();
}
private Map<String, Object> getBeanMap() {
final Map<String, Object> result = new HashMap<>();
CollectionUtils.emptyIfNull(getBeans())
.forEach(obj -> result.put(StringUtils.uncapitalize(obj.getClass().getSimpleName()), obj));
result.putAll(MapUtils.emptyIfNull(getQualifiedBeans()));
return result;
}
/**
* Resource class under test
*
* #return
*/
protected abstract Class<?> getResourceClass();
/**
* Creates & returns a Spring GenericApplicationContext from the given beans with qualified names
*
* #param beans
* #return
*/
public static ApplicationContext createSpringContext(Map<String, Object> beans) {
final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
MapUtils.emptyIfNull(beans).forEach((k, obj) -> beanFactory.registerSingleton(k, obj));
final GenericApplicationContext context = new GenericApplicationContext(beanFactory);
context.refresh();
return context;
}
}
Sample Resource With Test:
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import lombok.RequiredArgsConstructor;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
#Path("/rest")
#Component
#RequiredArgsConstructor
class RestResource {
private final ServiceXYZ serviceXYZ;
private final ServiceABC serviceABC;
#Qualifier("greeter")
private final String greeterName;
#GET
#Path("/serviceXYZ/greet/{name}")
public Response greetByServiceXYZ(#PathParam("name") final String name) {
return Response.ok(serviceXYZ.greet(name) + ", Regards: " + greeterName).build();
}
#GET
#Path("/serviceABC/greet/{name}")
public Response greetByServiceABC(#PathParam("name") final String name) {
return Response.ok(serviceABC.greet(name)+ ", Regards: " + greeterName).build();
}
}
#Service
class ServiceXYZ {
public final String greet(final String name) {
return "Welcome " + name + " to Hello World!";
}
}
#Service
class ServiceABC {
public final String greet(final String name) {
return "Welcome " + name + " to Hello Universe!";
}
}
class ResourceTest extends JerseySpringTest {
#InjectMocks private RestResource subject;
#Mock private ServiceXYZ serviceXYZ;
#Mock private ServiceABC serviceABC;
// only required to override for custom server config, say if the Resource accepts file input
#Override
protected ResourceConfig serverConfig(final ResourceConfig config) {
return config.register(MultiPartFeature.class);
}
#Override
protected Map<String, Object> getQualifiedBeans() {
return Map.of("greeter", "Amith Kumar");
}
#Override
protected List<Object> getBeans() {
return List.of(subject, serviceXYZ, serviceABC);
}
#Override
protected Class<?> getResourceClass() {
return RestResource.class;
}
// only required to override for custom client config, say if the Resource accepts file input
#Override
protected void configureClient(ClientConfig config) {
config.register(MultiPartFeature.class);
}
#Test
void testServiceXYZGreets() {
// ARRANGE
when(serviceXYZ.greet("foo")).thenReturn("Hello foo");
// ACT
Response output = target("/rest/serviceXYZ/greet/foo").request().get();
// ASSERT
assertAll(
() -> assertEquals(Status.OK.getStatusCode(), output.getStatus()),
() -> assertEquals("Hello foo, Regards: Amith Kumar", output.readEntity(String.class)));
}
#Test
void testServiceABCGreets() {
// ARRANGE
when(serviceXYZ.greet("boo")).thenReturn("Hola boo");
// ACT
Response output = target("/rest/serviceABC/greet/boo").request().get();
// ASSERT
assertAll(
() -> assertEquals(Status.OK.getStatusCode(), output.getStatus()),
() -> assertEquals("Hola boo, Regards: Amith Kumar", output.readEntity(String.class)));
}
}