Inject Configuration in Guice AbstractModule in Dropwizard Application - java

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.

Related

Quarkus. Registering CDI Beans classes from a third party library

I'm using quarkus to implement a rest-api defined in a 3rd party library.
This library contains providers that implement ParamConverterProvider, ExceptionMapper, ConstraintValidator, a default implementation of resource interfaces, and so on. The library is missing beans.xml and jandex index.
Problem: It is required to selectively initialize some of the classes present in the library. Please advise how this can be done.
What I tried:
Initialization of all beans of the quarkus.index-dependency property library. The method is not suitable, because there are many providers in the library that do not require.
These and the following methods do not work:
#ApplicationScoped
public class AppConfig extends ResourceConfig {
public AppConfig() {
register(UnsupportedOperationExceptionMapper.class);
}
#ApplicationScoped
public class MyResteasyBootstrap extends ResteasyBootstrap {
#Override
public void contextInitialized(ServletContextEvent event) {
super.contextInitialized(event);
ResteasyProviderFactory factory = ResteasyProviderFactory.getInstance();
factory.registerProvider(UnexpectedErrorExceptionMapper.class);
}
#ApplicationScoped
public class MyContextResolver implements ContextResolver<ResteasyProviderFactory> {
private ResteasyProviderFactory factory = ResteasyProviderFactory.getInstance();
#PostConstruct
void initialize() {
factory.registerProvider(UnsupportedOperationExceptionMapper.class);
}
#Override
public ResteasyProviderFactory getContext(Class type) {
return factory;
}
#QuarkusMain
public class MyQuarkusApplication {
public static void main(String ... args) {
ResteasyProviderFactory factory = ResteasyProviderFactory.getInstance();
factory.registerProvider(UnsupportedOperationExceptionMapper.class);
Quarkus.run(args);
}
}
I created a ticket on gihub, in which they suggested 2 options for solving the problem, both were useful for different situations.

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));
}
}

Access guice container in Dropwiard application

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.

Injecting parent classes from other libraries with Guice

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

Injecting singleton in guice module

I have written some modules with guice. These are working great.
I have also some singletons or a logger I need in my modules which I want to inject into these modules.
For example I have my JpaModule where I need my Configuration.
ConfigurationModule:
#Singleton
public class ConfigurationModule extends AbstractModule {
#Override
protected void configure() {
bind(Configuration.class).toProvider(ConfigurationProvider.class).in(Singleton.class);
}
}
JpaModule:
public class JpaDaoModule extends AbstractModule {
#Inject
Configuration config;
#Override
protected void configure() {
// ... Read config and do something
}
}
Call to Guice:
Guice.createInjector(new ConfigurationModule(), new JpaDaoModule());
How can I accomplish this? Or how can I provide the configuration to the JpaModule the guicy way?
/Kind regards
Christian
This is not possible. In the configure() method you set up your bindings. You cannot expect them to be available already. Also, modules are not eligible for injection per se. You can, however, get access to Guice-managed instances in providers or #Provides methods.
#Provides
#Named("myConfigItem")
String provideSomeConfigItem(Configuration config) {
return config.get("myConfigItem");
}

Categories

Resources