Injecting parent classes from other libraries with Guice - java

I am trying to use Guice (4.0) to bootstrap dependencies for my executable from inside my main driver class (perhaps this is a Guice anti-pattern?):
// Groovy pseudo-code
// This Buzz class is located in a 3rd party lib that I don't have access to
class Buzz {
int foobaz
Whistlefeather whistlefeather
// other stuff, include constructor, setters and getters
}
class MyApp extends Buzz {
#Inject
DatabaseClient dbClient
#Inject
FizzRestClient fizzClient
static void main(String[] args) {
MyApp app = Guice.createInjector(new MyAppModule()).getInstance(MyApp)
app.run()
}
private void run() {
// Do your thing, little app!
}
}
class MyAppModule extends AbstractModule {
#Override
void configure() {
bind(DatabaseClient).to(DefaultDatabaseClient)
bind(FizzRestClient).to(DefaultFizzRestClient)
// But how do I configure MyApp's 'foobaz' and 'whistlefeather'
// properties? Again, I don't have access to the code, so I
// can't annotate them with #Inject, #Named, etc.
}
}
So my problem is that MyApp actually extends a base object living in a 3rd party (OSS) JAR. This base class (Buzz) is not set up for use with Javax Inject or Guice. But I would like Guice to be able to configure its foobaz and whistlefeather properties.... any ideas?

You can create and inject any bean with a #Provide method in a Guice module. For example:
#Provides
MyApp externalService(DatabaseClient dbClient, Whistlefeather wf) {
MyApp app = new MyApp();
app.setDatabaseCLient(dbClient);
app.setWhitlefeature(wf);
return app;
}
See #Provides

Related

How inject Hk2 beans with Guice

There is a dropwizard app, which is jersey based.
I rewrote Hk2 bean definitions into Guice and now I can inject Guice beans into Jersey Resources,
but I noticed that Hk2 beans, defined in dropwizard bundles, which I cannot rewrite, are not
visible by Guice and it fails to inject dependencies defined in Hk2.
Guice doesn't see beans defined in Hk2 bundles and Guice creates new uninitialized beans by default.
I disabled this behavior with requireExplicitBindings.
I experimented with HK2IntoGuiceBridge, but its matcher is not invoked for beans I am interested in.
ConfiguredBundleX is located in external artifact.
I tried to copy and translate bean definitions from bundles and stuck with jersey bean Provider<ContainerRequest>, I have no idea where it comes from.
public class ConfiguredBundleX implements ConfiguredBundle<MyAppConf> {
public void run(T configuration, Environment environment) throws Exception {
environment.jersey().register(new AbstractBinder() {
protected void configure() {
this.bind(new MyHk2Bean()).to(MyHk2Bean.class);
}
});
}
}
public class DependsOnHk2Bean { #Inject public DependsOnHk2Bean(MyHk2Bean b) {} }
public class MainModule extends AbstractModule {
private final ServiceLocator locator;
protected void configure() {
binder().requireExplicitBindings();
install(new HK2IntoGuiceBridge(locator));
bind(DependsOnHk2Bean.class);
}
public class GuiceFeature implements Feature {
public boolean configure(FeatureContext context) {
ServiceLocator locator = ServiceLocatorProvider.getServiceLocator(context);
GuiceBridge.getGuiceBridge().initializeGuiceBridge(locator);
Injector injector = Guice.createInjector(
new HK2IntoGuiceBridge(locator),
new MainModule(locator));
GuiceIntoHK2Bridge guiceBridge = locator.getService(GuiceIntoHK2Bridge.class);
guiceBridge.bridgeGuiceInjector(injector);
return true;
}
}
// ...
public void initialize(Bootstrap<X> bootstrap) {
bootstrap.addBundle(new ConfiguredBundleX());
}
public void run(X config, Environment env) {
env.jersey().register(new GuiceFeature());
}
Unfortunately in Guice beans you have to use #HK2Inject rather than #Inject in order to inject hk2 beans into Guice. So in your code above you would do:
public class DependsOnHk2Bean { #HK2Inject public DependsOnHk2Bean(MyHk2Bean b) {} }
This is because of limitation in guice (it may be fixed by now) such that #Inject behavior could not overwritten
I have not tried the above code myself so I'm not sure it'll work, but that was the deal back when the bridge was written...
See HK2Inject and injecting-hk2-services-into-guice-services
After digging Guice and HK2ToGuiceTypeListenerImpl I figured out that there is bindListener to kind of intercept missing bindings and pull them from somewhere. #HKInject code is there, but I noticed that the listener is not called for some bean including the bean I was interested in. Yes HKInject doesn't support constructor injection (4.2.1 version)
So I decided to manually import HK beans and bind them in Guice.
Dropwizard terminology is horrible there are methods get context something, get admin context is totally something different and beans must be get with getService method!
#RequiredArgsConstructor
public class HkModule extends AbstractModule {
private final ServiceLocator locator;
#Override
protected void configure() {
binder().requireExplicitBindings();
Provider<Bar> barProvider = locator.getService(
new TypeLiteral<Provider<Bar>>(){}.getType());
bind(Bar.class).toProvider(barProvider);
bind(Foo.class).toInstance(locator.getService(Foo.class));
}
}

Unit Testing Guice modules where one module installs another module

FYI, I am using Mockito and TestNg
I know how to test my logic in a Guice module by using #Bind to mock my external dependencies.
Here I have a module (say Foo) which has install(new Bar()); in the configure method.
I can bind the various external dependencies in Foo, but I don't know how to deal with things in Bar.
ex)
public class FooTest {
#Bind
#Mock
SomeExternalDependency1 someExternalDependency1;
#Bind
#Mock
SomeExternalDependency2 someExternalDependency2;
#BeforeClass
public void setup() {
MockitoAnnotations.initiMocks(this);
injector = Guice.createInjector(Modules.override(new Foo())with(
new TestFooModule()), BoundFieldModule.of(this));
injector.injectMembers(this);
}
#Test
public void testSomething() {
//asssert something here
}
static class TestFooModule extends AbstractModule {
#Override
protected void configure() { }
}
But when I run this test, it complains about the external dependencies in Bar.
How do I test the Foo module without instantiating the Bar module?
For modules that don't install 'children' modules, this sort of testing works fine.
I needed to bind the #provides from Bar inside of the TestFooModule. That solved my issue.

Java Guice DI error: UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl

I have a simple REST API project using Jersey 2.x. I tried using Google Guice to inject my dependencies, but it doesn't seem to work. I get this error:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=AccountService,parent=AccountsResource,qualifiers={},position=0,optional=false,self=false,unqualified=null,1658198405)
I have this simple resource class
#Path("/accounts")
#Produces(MediaType.APPLICATION_JSON)
public class AccountsResource {
private final AccountService accountService;
#Inject
public AccountsResource(AccountService accountService) {
this.accountService = accountService;
}
#GET
#Path("test")
public String test() {
return this.accountService.test();
}
I want to inject this service into my resource class
public class AccountService {
public AccountService() {}
public String test() {
return "test";
}
}
So, following Guice's guide, I created this module class
import com.google.inject.*;
public class AccountsResourceModule extends AbstractModule {
#Override
protected void configure() {
bind(AccountService.class);
}
}
Finally, I added the injector in my main method
public class TradingServer implements Runnable {
private static final int PORT = 8181;
public static void main(String[] args) {
Injector injector = Guice.createInjector(new AccountsResourceModule());
AccountsResource accountsResource = injector.getInstance(AccountsResource.class);
new TradingServer().run();
}
public void run() {
Server server = new Server(PORT);
ServletContextHandler contextHandler = new ServletContextHandler(server, "/");
ResourceConfig packageConfig = new ResourceConfig().packages("ca.ulaval.glo4002.trading");
ServletContainer container = new ServletContainer(packageConfig);
ServletHolder servletHolder = new ServletHolder(container);
contextHandler.addServlet(servletHolder, "/*");
try {
server.start();
server.join();
} catch (Exception e) {
e.printStackTrace();
} finally {
server.destroy();
}
}
}
When I call my server, I get the error mentioned above. It seems like the dependency injection didn't work. Please help
So Jersey knows nothing about Guice. It already uses it's own DI framework, HK2. There are a couple things you can do. You can either tie Guice together with HK2 so that HK2 can find services that are bound inside Guice, or another way is to just bind your resource classes inside Guice and and register instances of those resources with Jersey.
Tie Guice with HK2
To tie Guice with HK2, you need to use the Guice HK2 Bridge. First you need to add the following dependency
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>guice-bridge</artifactId>
<version>${hk2.version}</version>
</dependency>
To get the hk2.version look at your Jersey dependencies (you can run mvn dependency:tree and see what version of HK2 Jersey is pulling in). You want to make sure you are using the exact same version.
Next thing you need to do is to programmatically link the two systems. One way to do this is inside a Feature.
public class GuiceFeature implements Feature {
#Override
public boolean configure(FeatureContext context) {
// This is the way in Jersey 2.26+ to get the ServiceLocator.
// In earlier versions, use
// ServiceLocatorProvider.getServiceLocator(context);
ServiceLocator locator = InjectionManagerProvider.getInjectionManager(context)
.getInstance(ServiceLocator.class);
Injector injector = Guice.createInjector(new AccountResourceModule());
GuiceBridge.getGuiceBridge().initializeGuiceBridge(locator);
GuiceIntoHK2Bridge guiceBridge = locator.getService(GuiceIntoHK2Bridge.class);
guiceBridge.bridgeGuiceInjector(injector);
return true;
}
}
Then just register the feature with Jersey.
ResourceConfig packageConfig = new ResourceConfig()
.packages("ca.ulaval.glo4002.trading")
.register(GuiceFeature.class);
And that's it. It should work, as I have tested.
Bind resources with Guice
With the above configuration, Jersey will be creating instances of your resource classes (#Path annotated classes). The reason we need the bridge is that Jersey is tightly coupled with HK2, so when we inject our resources classes, when creating the instance, Jersey will call HK2 to try to find all the dependencies for the resource.
In this case though, we will not rely on Jersey to create the instance of the resource. We will bind the resource to Guice and let Guice create the instance when we request it. We will use that instance to register with Jersey.
First bind the resource
public class AccountResourceModule extends AbstractModule {
#Override
protected void configure() {
bind(AccountService.class);
bind(AccountResource.class);
}
}
Also make sure that the #Inject annotation in the resource class is com.google.inject.Inject.
Get instance of resource and register it
Injector injector = Guice.createInjector(new AccountResourceModule());
AccountResource accountResource = injector.getInstance(AccountResource.class);
ResourceConfig config = new ResourceConfig()
.register(accountResource);
You probably have to figure out a cleaner way to do this as you don't want to have to do this for every resource you have. But this is the gist if what you need to do.
Update
So here's a quick implementation to clean up the second solution. What we can do is scan a package recursively to get all the #Path annotated classes and then bind them in Guice and register them with Jersey.
From this SO post, we can use the Reflections library to easily get all the classes. Just add the following dependency
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
</dependency>
Then make a little helper classes
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.Path;
import org.reflections.Reflections;
public class ResourceClassHelper {
private static Set<Class<?>> resourceClasses;
public static Set<Class<?>> getResourceClasses() {
if (resourceClasses != null) {
return resourceClasses;
}
// the package to scan for #Path classes "com.example"
Reflections reflections = new Reflections("com.example");
resourceClasses = reflections.getTypesAnnotatedWith(Path.class);
resourceClasses = Collections.unmodifiableSet(resourceClasses);
return resourceClasses;
}
}
Then in your Guice module
public class AccountResourceModule extends AbstractModule {
#Override
protected void configure() {
bind(AccountService.class);
ResourceClassHelper.getResourceClasses().forEach(this::bind);
}
}
And your resource registration
Injector injector = Guice.createInjector(new AccountResourceModule());
ResourceConfig config = new ResourceConfig();
ResourceClassHelper.getResourceClasses()
.forEach(cls -> config.register(injector.getInstance(cls)));

Inject Configuration in Guice AbstractModule in Dropwizard Application

I am working on an application developed using Guice and Dropwizard, where we are creating different bundles like guice bundle, migrations bundle, etc. and adding them to bootstrap in initialize() method.
I am trying to inject Configuration object in MyModule class, but unable to do so.
Following is the code for Application class:
public class MyApplication extends Application<MyConfiguration> {
public static void main(String args[]) throws Exception {
new MyApplication().run(args);
}
private GuiceBundle<MyConfiguration> guiceBundle = GuiceBundle.<MyConfiguration> newBuilder()
.addModule(new MyModule()).enableAutoConfig(getClass().getPackage().getName())
.setConfigClass(MyConfiguration.class).build(Stage.DEVELOPMENT);
#Override
public void initialize(Bootstrap<MyConfiguration> bootstrap) {
bootstrap.addBundle(guiceBundle);
}
#Override
public void run(MyConfiguration configuration, Environment environment) throws Exception {
...
}
}
Below is Module class which extends AbstractModule:
public class MyModule extends AbstractModule {
#Override
protected void configure() {
}
}
With this approach, I am finding it hard to inject Configuration object in Module class, as Configuration object is not available in initialize() method, but is available in run() method.
Is there any alternative way to do this?
Note: I am aware of another way where you can create an object of Module class in run() method for creating an injector (with configuration and environment object passed as parameters in the constructor of MyModule class). But this would require me to register all Managed objects and all resources in run() method. I want to avoid doing that.
Guice modules are classes that store the configuration, and are resolved when an injector is created. You cannot explicitly inject an object in your module.
I don't think I would be able to tell you much more without looking into internal of GuiceBundle.

How to Mock an injected object that is not declared in Module?

For a dagger2 module
#Module
public class MyModule {
#Provides #Singleton public RestService provideRestService() {
return new RestService();
}
#Provides #Singleton public MyPrinter provideMyPrinter() {
return new MyPrinter();
}
}
We could have the test module as Test
public class TestModule extends MyModule {
#Override public MyPrinter provideMyPrinter() {
return Mockito.mock(MyPrinter.class);
}
#Override public RestService provideRestService() {
return Mockito.mock(RestService.class);
}
}
However if for a class as below that is not declared in the dagger module...
public class MainService {
#Inject MyPrinter myPrinter;
#Inject public MainService(RestService restService) {
this.restService = restService;
}
}
How do I create a mock of MainService as above.
Note, I'm not planning to perform test for MainService as per share in https://medium.com/#fabioCollini/android-testing-using-dagger-2-mockito-and-a-custom-junit-rule-c8487ed01b56#.9aky15kke, but instead, my MainService is used in another normal class that I wanted to test. e.g.
public class MyClassDoingSomething() {
#Inject MainService mainService;
public MyClassDoingSomething() {
//...
}
// ...
public void myPublicFunction() {
// This function uses mainService
}
}
This is definitely not answering your question, but in my honest opinion it is related, it's helpful and too big for a comment.
I'm often facing this question and I end always doing "Constructor dependency injection". What this means is that I no longer do field injection by annotating the field with #Inject but pass the dependencies in the constructor like so:
public class MyClassDoingSomething implements DoSomethig {
private final Service mainService;
#Inject
public MyClassDoingSomething(Service mainService) {
this.mainService = mainService;
}
}
Notice how the constructor now receives the parameter and sets the field to it and is also annotated with #Inject? I also like to make these classes implement an interface (also for MyService) - Amongst several other benefits I find it makes the dagger module easier to write:
#Module
public class DoSomethingModule {
#Provides #Singleton public RestService provideRestService() {
return new RestService();
}
#Provides #Singleton public MyPrinter provideMyPrinter() {
return new MyPrinter();
}
#Provides #Singleton public Service provideMyPrinter(MyService service) {
return service;
}
#Provides #Singleton public DoSomethig provideMyPrinter(MyClassDoingSomething something) {
return something;
}
}
(This assumes that MyService implements or extends Service)
By now it seems you already know that dagger is able to figure out the dependency graph by itself and build all the objects for you. So what about unit testing the class MyClassDoingSomething? I don't even use dagger here. I simply provide the dependencies manually:
public class MyClassDoingSomethingTest {
#Mock
Service service;
private MyClassDoingSomething something;
#Before
public void setUp() throws Exception {
MockitoAnnotations.init(this);
something = new MyClassDoingSomething(service);
}
// ...
}
As you see, the dependency is passed through the constructor manually.
Obviously this doesn't work if you're coding something that doesn't have a constructor that can be invoked by you. Classical examples are android activities, fragments or views. There are ways to achieve that, but personally I still think you can somehow overcome this without dagger. If you are unit testing a view that has a field #Inject MyPresenter myPresenter, usually this field will have package access that works fine in the tests:
public class MyViewTest {
#Mock MyPresenter presenter;
private MyView view;
#Before
public void setUp() throws Exception {
MockitoAnnotations.init(this);
view.myPresenter = presenter;
}
}
Note that this only works if both MyViewTest and MyView are in the same package (which often is the case in android projects).
At the end of the day if you still want to use dagger for the tests, you can always create "test" modules and components that can inject by declaring methods in the component like:
#Inject
public interface MyTestComponent {
void inject(MyClassDoingSomething something);
}
I find this approach ok-ish, but throughout my development years I prefer the first approach. This also has reported issues with Robolectric that some setup in the build.gradle file is required to actually make the dagger-compiler run for the tests so the classes are actually generated.

Categories

Resources