I am trying to re-use a guice module from a Library which has multiple providers, I want to use few providers from the library and provide few in my own module.
Library Module -
public class LibraryModule extends AbstractModule {
#Override
protected void configure() {
}
#Provides
#Singleton
#Named("dbCredentials")
private AWSCredentialsProvider getCredentialsProvider(#Named("app.keySet") String keySet) {
return new SomeCredentialsProvider(keySet);
}
#Provides
#Singleton
private AmazonDynamoDB getDynamoDBClient(#Named("dbCredentials") AWSCredentialsProvider credentialsProvider,
#Named("aws.region") String region) {
return AmazonDynamoDBClientBuilder.standard()
.withCredentials(credentialsProvider)
.withRegion(region)
.build();
}
#Provides
#Singleton
#Named("dbMapper")
private DynamoDBMapper getDynamoDBMapper(AmazonDynamoDB dynamoDBClient) {
return new DynamoDBMapper(dynamoDBClient);
}
...... more providers using dbMapper
Now where I want to use this module I want to give some different implementation of AmazonDynamoDB which uses the default credential provider.
public class MyModule extends AbstractModule {
#Override
protected void configure() {
bind(AmazonDynamoDB.class).toProvider(AmazonDynamoDBClientProvider.class).in(Singleton.class);
install(new LibraryModule());
}
AmazonDynamoDBClientProvider class -
public class AmazonDynamoDBClientProvider implements Provider<AmazonDynamoDB> {
private final String region;
#Inject
public AmazonDynamoDBClientProvider(#Named("aws.region") String region) {
this.region = region;
}
#Override
public AmazonDynamoDB get() {
return AmazonDynamoDBClientBuilder.standard()
.withRegion(region)
.build();
}
}
but when I try to do this it fails in the provider of library while trying to create the AmazonDynamoDB by saying A binding to com.amazonaws.services.dynamodbv2.AmazonDynamoDB was already configured
I wanted to know if it is possible to omit providers for classes which have already been bound in the parent module? If yes how do we do that? I was unable to find a solution for this problem.
If you are in the position to change LibraryModule, then you should give it flexibility to bind either its default implementation or the one you're supplying:
class LibraryModule extends AbstractModule {
// Default implementation based on the code you've shown in LibraryModule
static class DefaultDynamoDBProvider implements Provider<AmazonDynamoDB> {
private final AWSCredentialsProvider credentialsProvider;
private final String region;
#Inject DefaultDynamoDBProvider(
#Named("dbCredentials") AWSCredentialsProvider credentialsProvider,
#Named("aws.region") String region) {
this.credentialsProvider = credentialsProvider;
this.region = region;
}
#Override AmazonDynamoDB get() {
return AmazonDynamoDBClientBuilder
.standard()
.withCredentials(credentialsProvider)
.withRegion(region)
.build();
}
}
private Class<Provider<AmazonDynamoDB>> dynamoDbProviderClass;
// if constructed with this constructor, uses default provider
LibraryModule() {
this(DefaultDynamoDBProvider.class);
}
// here, uses the supplied one. Builder pattern would be better
LibraryModule(Class<Provider<AmazonDynamoDB>> providerClass) {
this.dynamoDbProviderClass = providerClass;
}
protected void configure() {
bind(AmazonDynamoDB.class)
.toProvider(dynamoDbProviderClass)
.in(Singleton.class);
}
}
If however touching LibraryModule is impossible, take a look at
Overriding Binding in Guice, while keeping in mind that overriding bindings is an anti-pattern and should be avoided at all costs:
Modules.override javadoc says "Prefer to write smaller modules that can be reused and tested without overrides," and supplies examples having to do with unit testing
Guice bindings are hard to track down, but at least there's a promise that once you've found the binding, it's the right one. Modules.override() complicate this task further.
Modules are supposed to be independent of one another. In our scenario, someone may have installed LibraryModule and connector to Dynamo just fine, but once he also installed OrderProcessingModule (let's assume that's what you're writing), his connection broke. Now he has no recourse: it's either Dynamo or Order Processing, or yet another even-more-complicated override
Related
I'm trying to create a state machine that uses an enum to specify the states. Since there are a lot of states and the logic implemented by each is pretty complex, I wanted to associate each state with a state handler that will be defined in a different class. Each state handler would implement a common interface (or extend a common abstract class), but each one might have its own set of injected dependencies that the others might not need. So far, something like this....
StateHandler Interface:
public interface StateHandler {
void onActivation();
void onDeactivation();
}
Example StateHandlers:
#Singleton
public class DefaultStateHandler implements StateHandler {
#Inject
public DefaultStateHandler(SomeDependency someDependency) {...}
/** implement onActivation, onDeactivation and state specific logic **/
}
#Singleton
public class OtherStateHandler implements StateHandler {
#Inject
public OtherStateHandler(SomeOtherDependency someOtherDependency) {...}
/** implement onActivation, onDeactivation and state specific logic **/
}
StateManager implementation:
#Singleton
public class StateManager {
private StateType stateType = StateType.DEFAULT;
#Inject
public StateManager() { }
public void changeState(StateType newStateType) {
if (stateType != newStateType) {
stateType.getStateHandler().onDeactivation();
stateType = newStateType;
stateType.getStateHandler().onActivation();
}
}
}
Enum Definition:
public enum StateType {
DEFAULT (/* not sure what to do here */),
OTHER_STATE (...);
private StateHandler stateHandlerInstance;
public getStateHandler { return stateHandlerInstance; }
StateType(/* not sure what to do here */) {
/* assign stateHandlerInstance */
}
}
What I'm trying to figure out is... how do I inject the specific instances of the state handlers when declaring their associated enums? Or if that isn't possible, is there another way of specifying the state handler class for each enum, and then (either in the constructor or by the time its first needed), get the associated state handler instance?
I was originally thinking I needed to inject the state's handler instance into the state enum definition. However, since injection requires public constructors and enums use private constructors, I don't think that approach is feasible.
As mentioned in the comments above, the solution was to use map multibindings.
First the enum StateType is simplified:
public enum StateType {
DEFAULT, OTHER_STATE
}
Now we need a dagger MapKey interface specific to this enum type:
#MapKey
#interface StateTypeKey {
StateType value();
}
Next we need a dagger module which will have provider functions for each StateType / StateHandler combination:
#Module
public StateTypeHandlersModule {
// #Provides #IntoMap // Syntax for dagger >= 2.9
#Provides(type = Provides.Type.MAP) // Syntax for dagger <= 2.8
#StateTypeKey(StateType.DEFAULT)
StateHandler provideDefaultStateHandler(DefaultStateHandler handler) {
return handler;
}
// #Provides #IntoMap // Syntax for dagger >= 2.9
#Provides(type = Provides.Type.MAP) // Syntax for dagger <= 2.8
#StateTypeKey(StateType.OTHER_STATE)
StateHandler provideOtherStateHandler(OtherStateHandler handler) {
return handler;
}
}
Its unfortunately a lot of boilerplate code, which is why I'm using a separate module just for the handlers, and including that in the higher-level state machine module. Note that if you declare two provider functions with the same StateTypeKey, the second handler is ultimately available in the injected map.
Finally, we can inject the Map<StateType, StateHandler> into the StateManager:
#Singleton
public class StateManager {
private Map<StateType, StateHandler> stateHandlerMap;
private StateType stateType = StateType.DEFAULT;
#Inject
public StateManager(Map<StateType, StateHandler> stateHandlerMap) {
this.stateHandlerMap = stateHandlerMap;
}
public void changeState(StateType newStateType) {
if (stateType != newStateType) {
stateHandlerMap.get(stateType).onDeactivation();
stateType = newStateType;
stateHandlerMap.get(stateType).onActivation();
}
}
}
I've implemented a JAX-RS server application using Jersey 2.24.
I use the Guice-HK2 bridge so that the controller classes (those annotated with #Path) are injected with dependencies from Guice, not Jersey/HK2.
However, HK2 still creates instances of the #Path annotated classes itself.
Is there a way I can plug into Jersey/HK2 so that I'm notified when a #Path annotated class is created? Like some sort of lifecycle listener? Every time a #Path annotated class is created by Jersey/HK2 I want to do some registering/logging of that class.
If Guice were doing the actual creation of the #Path annotated class I think I could do it using a generic Provider but that's not available in this case, since Jersey/HK2 is creating the actual instance.
Thank you!!
I think the least intrusive way would be to just use AOP. HK2 offers AOP. What you can do is create a ConstructorInterceptor. Something like
public class LoggingConstructorInterceptor implements ConstructorInterceptor {
private static final Logger LOG
= Logger.getLogger(LoggingConstructorInterceptor.class.getName());
#Override
public Object construct(ConstructorInvocation invocation) throws Throwable {
Constructor ctor = invocation.getConstructor();
LOG.log(Level.INFO, "Creating: {0}", ctor.getDeclaringClass().getName());
// returned instance from constructor invocation.
Object instance = invocation.proceed();
LOG.log(Level.INFO, "Created Instance: {0}", instance.toString());
return instance;
}
}
Then create a InterceptorService to only use the interceptor for classes annotated with #Path
public class PathInterceptionService implements InterceptionService {
private static final ConstructorInterceptor CTOR_INTERCEPTOR
= new LoggingConstructorInterceptor();
private final static List<ConstructorInterceptor> CTOR_LIST
= Collections.singletonList(CTOR_INTERCEPTOR);
#Override
public Filter getDescriptorFilter() {
return BuilderHelper.allFilter();
}
#Override
public List<MethodInterceptor> getMethodInterceptors(Method method) {
return null;
}
#Override
public List<ConstructorInterceptor> getConstructorInterceptors(Constructor<?> ctor) {
if (ctor.getDeclaringClass().isAnnotationPresent(Path.class)) {
return CTOR_LIST;
}
return null;
}
}
Then just register the InterceptionService and ConstructorInterceptor with the DI system
new ResourceConfig()
.register(new AbstractBinder(){
#Override
public void configure() {
bind(PathInterceptionService.class)
.to(InterceptionService.class)
.in(Singleton.class);
bind(LoggingConstructorInterceptor.class)
.to(ConstructorInterceptor.class)
.in(Singleton.class);
}
});
See complete example in this Gist
See Also:
HK2 documentation on AOP
I have a module that acquires and holds an API token (simplified):
#Singleton
public class KeyHolderModule extends AbstractModule {
// This doesn't seem to be injected
private #Inject TokenConnector connector;
private DateTime keyLastRefreshed;
private String key;
private Credentials creds = config.getCreds();
#Override protected void configure() {
this.key = connector.getToken(creds);
this.keyLastRefreshed = DateTime.now();
}
#Provides #Named("apiKey") public String getKey() {
// logic to check key last refreshed and handle generating a new one
return this.key;
}
}
I get a null pointer error on the line where I try to access the connector (this.key = connector.getToken(creds);), so the connector is obviously not getting wired up correctly.
I've tried creating a constructor and using #Inject there, but I'm manually adding these modules via new to a list in my app bootstrap class, so that's sort of out.
Obviously I'm missing something here -- I could probably just new up a TokenConnector in this case since it doesn't have any dependencies itself, but that wouldn't fix my fundamental failure to grasp what's happening here. So if you want to see (simplified) other pieces of code, or less simplified pieces of this code, let me know.
Though you can't use #Inject for a Module (unless you get the Module from another Injector, which I recommend strongly against), you can easily inject into a #Provides method.
public class KeyHolderModule extends AbstractModule {
private DateTime keyLastRefreshed;
private String key;
private Credentials creds = config.getCreds();
#Override protected void configure() {}
#Provides #Named("apiKey") public String getKey(
TokenConnector connector) {
// logic to check key last refreshed and handle generating a new one
this.key = connector.getToken(creds);
this.keyLastRefreshed = DateTime.now();
return this.key;
}
}
The trick here is that a Module is typically manually instantiated at injector creation time, but #Provides methods are invoked when the dependencies they provide are needed. Consequently, the Injector isn't ready to provide anything when the Module is constructed, but #Provides methods invoked throughout the application lifecycle have access to whatever other injector-provided dependencies they might need. When configure is run the Injector is not yet created, the best you can do is call getProvider (though you can't call get on those until the Injector is ready).
I wrote up a variety of other in-Module injection techniques as this SO answer.
I'm designing my REST application architecture using Domain Driven Design and Adapter patter (there are interfaces, and many implementations in the aggregate root). It's all fine as long as don't add HATEOAS to the puzzle. In HATEOAS my value objects (on the bottom of dependency hierarchy) need to depend on resources (in the top layer). This messes up everything. I'm fairly new to HATEOAS so maybe I'm missing something. I'm planning to use Dropwizard and Jersey Declarative Linking.
Here is a diagram of my architecture:
Little clarification - this "Return and attributes types" between interfaces and value objects should actually be "Return and argument types" - It means, that all the interfaces' methods take objects from Value objects module as an arguments and return those objects to the caller.
I can add a piece of code that will show you what's in what module:
REST - JAX-RS Resources
#Component
#Path("/groups")
#Produces(MediaType.APPLICATION_JSON)
public class GroupsResource {
#Autowired
ProcessEngine processEngine; //interface with driver implementation under it
#GET
#Timed
public List<UserGroup> getUserGroups(#Auth BpmUser user) {
return processEngine.getUserGroups(user.id);
}
}
Interface ProcessEngine
public interface ProcessEngine {
void init();
List<UserGroup> getUserGroups(String username);
}
Implementation in drivers module
public class ActivitiProcessEngine implements ProcessEngine {
private org.activiti.engine.ProcessEngine processEngine;
private DataSource dataSource;
private String databaseType;
public ActivitiProcessEngine(String databaseType, DataSource dataSource) {
this.databaseType = databaseType;
this.dataSource = dataSource;
}
#Override
public void init() {
if (processEngine != null)
throw new ProcessEngineAlreadyInitializedException();
try {
processEngine = createProcessEngineConfiguration().buildProcessEngine();
ProcessEngines.registerProcessEngine(processEngine);
} catch (SQLException e) {
throw new ProcessEngineDatabaseException(e);
}
}
#Override
public List<UserGroup> getUserGroups(String username) {
return processEngine
.getIdentityService()
.createGroupQuery()
.groupMember(username)
.list()
.stream()
.map(Group::getId)
.map(UserGroup::new)
.collect(Collectors.toList());
}
...
}
Value object
public class UserGroup {
#JsonProperty
public String name;
//I want to be able add linking to another resources here
public UserGroup(String name){
this.name = name;
}
}
Domain object should never know anything about Controller or any other application logic. So, link controllers to domain object. It will solve your dependency problem.
I have a system (Java with Spring Framework) that exposes 7 different Apache Thrift servlets over HTTP using the TServlet class. Currently they all need their own Servlets, ServletMappings, Processors, Handlers etc. so implementing clients have to also keep an internal list of all the various URLs for the different services.
I understand that Apache Thrift supports multiplexing when using TServer and its derivatives by using TMultiplexingProcessor, however since I am using Spring and my Servlet, Handler and Processor are all Spring Beans that get autowired into one another, I'm unsure how to proceed.
Here's an example of how one of the services gets wired up:
UserServiceHandler.java
#Component
public class UserServiceHandler implements UserService.Iface {
#Override
public User getUser(String userId) throws TException {
// implementation logic goes here
}
}
UserServiceProcessor.java
#Component
public class UserServiceProcessor extends UserService.Processor<UserServiceHandler> {
private UserServiceHandler handler;
#Autowired
public UserServiceProcessor(UserServiceHandler iface) {
super(iface);
handler = iface;
}
public UserServiceHandler getHandler() {
return handler;
}
public void setHandler(UserServiceHandler handler) {
this.handler = handler;
}
}
UserServiceServlet.java
#Component
public class UserServiceServlet extends TServlet {
private UserServiceProcessor processor;
#Autowired
public UserServiceServlet(UserServiceProcessor p) {
super(p, new TBinaryProtocol.Factory());
processor = p;
}
}
Servlet Registration
ServletRegistration.Dynamic userService = servletContext.addServlet("UserServiceServlet", (UserServiceServlet) ctx.getBean("userServiceServlet"));
userService.setLoadOnStartup(1);
userService.addMapping("/api/UserService/*");
// This same block repeated 7 times for each *ServiceServlet with different mappings
I would like to have all 7 service handlers map to a single URL like /api/*. Is this even possible? I suppose I would have to create a single servlet and processor, but I'm unsure what they should look like. My processors extend UserService.Processor and the like.
OK, figured it out. Might not be the best way, so I welcome criticism.
Here were my rough steps:
Keep the handler classes the way they were.
Create a new class that extends TMultiplexedProcessor
Create a new class that extends TServlet
All Processors (e.g. the UserServiceProcessor have a handler property and a corresponding getter and setter
Here is my ApiMultiplexingProcessor:
#Component
public class ApiMultiplexingProcessor extends TMultiplexedProcessor {
UserServiceHandler userServiceHandler;
ReportServiceHandler reportServiceHandler;
// ... more service handlers can go here
#Autowired
public ApiMultiplexingProcessor(UserServiceProcessor userServiceProcessor, ReportServiceProcessor reportServiceProcessor) {
this.registerProcessor("UserService", userServiceProcessor);
this.registerProcessor("ReportService", reportServiceProcessor);
// add more registerProcessor lines here for additional services
userServiceHandler = userServiceProcessor.getHandler();
reportServiceHandler = reportServiceProcessor.getHandler();
// set any additional service handlers here
}
// getters and setters for the handlers
public UserServiceHandler getUserServiceHandler() {
return userServiceHandler;
}
public void setUserServiceHandler(UserServiceHandler userServiceHandler) {
this.userServiceHandler = userServiceHandler;
}
public ReportServiceHandler getReportServiceHandler() {
return reportServiceHandler;
}
public void setReportServiceHandler(ReportServiceHandler reportServiceHandler) {
this.reportServiceHandler = reportServiceHandler;
}
}
So to explain the above a bit, if you add any additional services, you need to add the *ServiceHandler classes as fields on this class, and create the getters and setters etc.
So now that we have that, we can create a new single servlet that will be added to the servlet context.
Here is my ApiServlet:
#Component
public class ApiServlet extends TServlet {
private ApiMultiplexingProcessor processor;
#Autowired
public ApiServlet(ApiMultiplexingProcessor p) {
super(p, new TBinaryProtocol.Factory());
processor = p;
}
}
And then you just add this servlet to the servlet context (from a bean) as before:
ServletRegistration.Dynamic api = servletContext.addServlet("ApiServlet", (ApiServlet) ctx.getBean("apiServlet"));
api.setLoadOnStartup(1);
api.addMapping("/api/*");
// yay now we have a single URL and a single servlet
This all could be helpful to someone else in my situation, so enjoy!
P.S. make sure when adapting your clients you use the TMultiplexedProtocol so that you can pass the service name through when talking to the server e.g.
TTransport transport = new THttpClient(new Uri("https://myapp.com/api/"));
TProtocol protocol = new TBinaryProtocol(transport);
TMultiplexedProtocol mp = new TMultiplexedProtocol(protocol, "UserService");
UserService.Client userServiceClient = new UserService.Client(mp);