I want to use dependency inject only in certain part of my code. I am using HubSpot/dropwizard-guice to integrate drop-wizard and guice. Is there a way I can access guice object instances programmatically without changing the full project. Basically I would replace initialised objects with guice objects, So that I don't have to change everything at once.
Here is my application file
public class HelloWorldApplication extends Application<HelloWorldConfiguration> {
public static void main(String[] args) throws Exception {
new HelloWorldApplication().run(args);
}
#Override
public void initialize(Bootstrap<HelloWorldConfiguration> bootstrap) {
GuiceBundle<HelloWorldConfiguration> guiceBundle = GuiceBundle.<HelloWorldConfiguration>newBuilder()
.addModule(new HelloWorldModule())
.enableAutoConfig(getClass().getPackage().getName())
.setConfigClass(HelloWorldConfiguration.class)
.build();
bootstrap.addBundle(guiceBundle);
}
#Override
public String getName() {
return "hello-world";
}
#Override
public void run(HelloWorldConfiguration helloWorldConfiguration, Environment environment) throws Exception {
}
}
Is there a way I can access guice injector without explicitly passing it as a method param all over my project?
Or Is there a way to directly get class instances from guice.
Related
I have a Job, which should read data from deep storage. I am using Guice DI for my project.
There is a deep store already written and coming as an outer dependencie. I am struggling with instantiating the client in Guice
Here is the code
JobModule
public class JobModule extends AbstractModule {
private Config config;
JobModule(Config config) {
this.config = config;
}
#Override
protected void configure() {
bind(Reader.class).to(DeepStoreReader.class);
}
#Provides
#Named("config")
Config provideConfig() {
return this.config;
}
}
Reader Interface
public interface Reader {
List<String> getData(String path);
}
DeepStoreReader
public class DeepStoreReader implements Reader {
private final DeepStoreClient deepStoreClient;
DeepStoreReader(#Named("config") Config config) {
this.deepStoreClient = new DeepStoreClient(config);
}
#Override
public List<String> getData(String path) {
return this.deepStoreClient.getData(path);
}
}
The issue is I don't want to instantiate DeepStoreClient inside the DeepStoreReader constructor, because it becomes difficult to test DeepStoreReader, since I won't be able to mock DeepStoreClient
What is the preferred way to instantiate a client in such cases? DeepStoreClient is not a Guice module/implementation and is coming as an outer published dependency
PS: I am new to DI and learning Guice
What you want is constructor injection, e.g.:
#Inject
public DeepStoreReader(DeepStoreClient deepStoreClient) {
this.deepStoreClient = deepStoreClient;
}
Guice will take care of instantiating the DeepStoreClient for you.
EDIT:
If DeepStoreClient itself has dependencies, you can also annotate that constructor:
#Inject
public DeepStoreClient(#Named("config") Config config) {
// ... 8< ...
}
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.
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.
In our company we work a lot with small Java applications as service in Windows. To be able to get status reports from these applications we use Jersey to output some JSON data.
To get the needed application data we currently setup the application as a singleton. From the resource handler in Jersey we can access the object via it's static getInstance method.
Now we are upgrading the complete application landscape and have made some changes to our applications. One of the changes is that the applications are no longer singletons. Is there any other way of accessing the application object without it being singleton and without the handler being an inner class?
Here is a simplified version of the code:
public class Main {
protected int data; // a property which has to be accessible by
// the jersey handler
protected Closeable server;
protected ResourceConfig resourceConfig;
public Main() {
// set the jersey handle
resourceConfig = new DefaultResourceConfig(JerseyHandler.class);
// start the jersey server
server = SimpleServerFactory.create("http://0.0.0.0:" + port, resourceConfig);
}
public int getData() {
return data;
}
}
#Path("/")
public class JerseyHandler {
#Path("status")
#GET
public Response status() {
// how to access Main's getData() method from here without
// anything being a singleton or an inner class???
int data = ????;
Response.ok().entity(data).build();
}
}
You should inject Main as dependency with Dependency Injection mechanism that is supported by Jersey. I use Jersey 2.12 and Google Guice 3.0 for the same purpose.
Example:
#Singleton
#Path("language")
#Produces(MediaType.APPLICATION_JSON)
public class LanguageResource {
private final LanguageService langService;
#Inject
public LanguageResource(LanguageService dateService) {
langService = dateService;
}
}
To configure the listener with custom Guice module:
ServletContextHandler handler = new ServletContextHandler();
handler.setServer(server);
handler.addEventListener(new ServletGuiceConfig());
Your Guice config could look like this:
public class ServletGuiceConfig extends GuiceServletContextListener {
protected static Injector injector;
#Override
protected Injector getInjector() {
injector = Guice.createInjector(new ServiceConfig(), new ServletModule() {
#Override
protected void configureServlets() {
bind(LanguageService.class).to(LanguageServiceImpl.class);
}
});
return injector;
}
}
I have some sample code which is using factories. I'd like to clean up the code by removing the factories and use Guice instead. I attempted to do this but I hit a small roadblock. I am really new to Guice, so I am hoping someone can help me out here.
Existing client code (Using factories):
public class MailClient {
public static void main(String[] args) {
MailConfig config = MailConfigFactory.get();
config.setHost("smtp.gmail.com");
Mail mail = MailFactory.get(config);
mail.send();
}
}
My attempt to refactor using Guice:
//Replaces existing factories
public class MailModule extends AbstractModule {
#Override
protected void configure() {
bind(Mail.class)
.to(MailImpl.class);
bind(MailConfig.class)
.to(MailConfigImpl.class);
}
}
public class MailImpl implements Mail {
private final MailConfig config;
#Inject
public MailImpl(MailConfig config) {
this.config = config;
}
public void send() { ... }
}
public class MailClient {
public static void main(String[] args) {
MailModule mailModule = new MailModule();
Injector injector = Guice.createInjector(mailModule);
MailConfig config = injector.getInstance(MailConfig.class);
config.setHost("smtp.gmail.com");
Mail mail = //??
mail.send();
}
}
How would I construct an instance of MailImpl using the object config in my revised MailClient? Should I be using Guice in this way?
Take a look at AssistedInject. It appears to address this problem.
2 solutions are possible:
1) bind the config as a guice object also, including its host parameter. then just inject Mail, in your main method you cna ignore the fact that mail has further dependencies.
2) mail must be configured individually for each send (recipient?). then you have no choice, but create it yourself using MailFactory.
You can do everything in MailModule as follows:
public class MailModule extends AbstractModule {
#Override
protected void configure() {
... // other bindings
}
#Provides
MailConfig getMailConfig( ... ) {
MailConfig config = new MailConfig( ... );
config.setHost("smtp.gmail.com");
config;
}
}
If you want a singleton MailConfig, add the #Singleton annotation to getMailConfig(), and Bob's your uncle.
Note that arguments to getMailConfig must be bound. When you bind commonly used types like String, be sure to add a binding annotation.