I have a problem trying to inject a contract with two services bound to it.
I'm using Jersey, and extending ResourceConfig to configure my app, where I'm binding two different implementations (classes FooImpl1 and FooImpl2) to a same contract (interface Foo), ranking them differently. Each of these implementations is annotated with #Named and its name.
In one of my controllers I want to have access to both implementations, so I inject an IterableProvider<Foo> fooProvider.
If I do not specify anything, the implementation with the highest rank is injected always, which is what I want.
The problem appears when I want a concrete implementation, one of them. When I call fooProvider.named( nameOfTheClass ).get(), is returning me null, but if I iterate over the fooProvider, I can have access to both implementations, so they are injected.
Anybody has an idea of what could I be missing?
Thanks a lot for your help.
Yeah so I'm not sure why it doesn't work with the #Named annotation value, as that's what's stated int the javadoc, but without the need for any annotations, we can configure the name when we do our bindings. We can do so with the named method.
register(new AbstractBinder(){
#Override
public void configure() {
bind(Foo1Impl.class).named("foo1").to(Foo.class);
bind(Foo2Impl.class).named("foo2").to(Foo.class);
}
});
UPDATE
So the above solution has been tested. If you are having problems still, post a complete runnable example that demonstrates it not working, like below (which is working)
Interface and Implementations
public interface Greeter {
String getGreeting(String name);
}
public class EnglishGreeter implements Greeter {
#Override
public String getGreeting(String name) {
return "Hello " + name + "!";
}
}
public class SpanishGreeter implements Greeter {
#Override
public String getGreeting(String name) {
return "Hola " + name + "!";
}
}
Resource
#Path("greeting")
public class GreetingResource {
#Inject
private IterableProvider<Greeter> greeters;
#GET
public Response getResponse(#QueryParam("lang") String lang,
#QueryParam("name") String name) throws Exception {
Greeter greeter = greeters.named(lang).get();
String message = greeter.getGreeting(name);
return Response.ok(message).build();
}
}
Binding. I did it in a Feature, but in a ResourceConfig, it's all the same.
#Provider
public class GreetingFeature implements Feature {
#Override
public boolean configure(FeatureContext context) {
context.register(new AbstractBinder(){
#Override
public void configure() {
bind(EnglishGreeter.class).named("english")
.to(Greeter.class).in(Singleton.class);
bind(SpanishGreeter.class).named("spanish")
.to(Greeter.class).in(Singleton.class);
}
});
return true;
}
}
Result
Related
Implemented a strategy pattern very simple with the help of Spring Boot:
I have an interface:
public interface IOneStrategy {
void executeTheThing();
}
I have an implementation of the strategy One like this:
#Service("FIRST")
public class OneStrategyFirst implements IOneStrategy {
#Override
public void executeTheThing() {
System.out.println("OneStrategyFirst.executeTheThing");
}
}
I have a class which consumes the injected implementations:
#Service
public class ExecuteStrategyOne {
private Map<String, IOneStrategy> strategies;
public void executeStrategyOne(String name) {
if (!strategies.containsKey(name)) {
throw new IllegalArgumentException("The key " + name + " does not exist.");
}
strategies.get(name).executeTheThing();
}
}
and those implementations will be injected by Spring boot automatically by using the name FIRST, 'SECOND' etc. (assuming that this is simply a String etc. works very well.).
But now I want to implement another strategy via second interface:
public interface ITwoStrategy {
void executeTheThing();
}
and the executing service for the strategy:
#Service
public class ExecuteStrategyTwo {
private Map<String, ITwoStrategy> strategies;
...
}
and now the problematic part, because my application uses the same name which should be made part of the key of the above map I want to use the following:
#Service("FIRST")
public class TwoStrategyFirst implements ITwoStrategy {
#Override
public void executeTheThing() {
System.out.println("TwoStrategyFirst.executeTheThing");
}
}
This will of course result into an exception based on the duplicate bean name. The name FIRST is really needed to make the difference between the implementation.
I already found things about #Qualifier which I could use instead of #Service(FIRST)
#Service
#Qualifier(FIRST)
public class TwoStrategyFirst implements ITwoStrategy {
#Override
public void executeTheThing() {
System.out.println("TwoStrategyFirst.executeTheThing");
}
}
which unfortunately does not inject the classes into the map by using the name of the qualifier just by the name of the class which is not what I intended to do.
Does exist a solution to get the key of the map in the strategy execution the same as with the #Service("FIRST")?
I could use a solution via using the Qualifier annotation like this:
#Service
#Qualifier(FIRST)
public class TwoStrategyFirst implements ITwoStrategy {
...
}
And based on that there is a more or less easy solution via a bit of code:
#Service
public class ExecuteStrategyTwo {
private Map<String, ITwoStrategy> strategies;
public ExecuteStrategyOne(List<ITwoStrategy> strategies) {
this.strategies = strategies.stream()
.collect(
Collectors.toMap(k -> k.getClass().getDeclaredAnnotation(Qualifier.class).value(), Function.identity()));
}
This will inject all implementation into the list of the constructor and will be translated into the map by using the qualifier annotation.
Say we have a FileLoader Interface:
public interface FileLoader {
default String loadFile(String fileId) {
// Default business logic
return "Default implementation for FileLoader. Loading file" + fileId;
}
}
And different implementations for different countries:
public class USAFileLoader implements FileLoader {
#Override
public String loadFile(String fileId) {
// ... Specific business logic for USA
return "USA implementation for FileLoader. Loading file" + fileId;
}
}
public class FRAFileLoader implements FileLoader {
#Override
public String loadFile(String fileId) {
// ... Specific business logic for France
return "France implementation for FileLoader. Loading file" + fileId;
}
}
And we create an endpoint to load files:
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class FileUploadController {
FileLoader fileLoader;
#PostMapping("/load/{fileId}/{countryCode}")
public String loadFile(#PathVariable String fileId, #PathVariable String countryCode) {
fileLoader = ... // Inject the right loader based on countryCode
return fileLoader.loadFile(fileId);
}
}
How can I inject the right FileLoader at runtime for every request, based on countryCode? I've found something in Spring called FactoryBean that apparently may work, but I'm now sure if it's the right tool, or if this is the right way to address this problem. Also, I don't know how injection will behave with requests being proccessed at the same time.
The best thing you can do here using run time polymorphism, add one more abstract method in interface FileLoader for country code
public interface FileLoader {
default String loadFile(String fileId) {
// Default business logic
return "Default implementation for FileLoader. Loading file" + fileId;
}
public abstract String getCountryCode();
}
And then implement it in every implementation class with return the appropriate country code
public class USAFileLoader implements FileLoader {
#Override
public String loadFile(String fileId) {
// ... Specific business logic for USA
return "USA implementation for FileLoader. Loading file" + fileId;
}
public String getCountryCode(){
return "USA";
}
}
And then you can Autowire all beans of type FileLoader into List and call loadFile on appropriate bean
#RestController
public class FileUploadController {
#Autowire
List<FileLoader> fileLoaders;
#PostMapping("/load/{fileId}/{countryCode}")
public String loadFile(#PathVariable String fileId, #PathVariable String countryCode) {
return fileLoaders.stream()
.filter(f->f.getCountryCode().equlas(countryCode))
.findFirst()
.map(loader->loader.loadFile(fileId))
.orElse(()-> FileLoader.super.loadFile(fileId)); //calling interface default method
}
}
You can receive a bean with another way at runtime using ApplicationContext::getBean:
#Autowired
ApplicationContext
#PostMapping("/load/{fileId}/{countryCode}")
public String loadFile(#PathVariable String fileId, #PathVariable String countryCode) {
FileLoader fileloader = (FileLoader) applicationContext.getBean(countryCode);
return fileLoader.loadFile(fileId);
}
However, I'd recommend creating a service layer that aggregates the country-specific implementations and uses a factory pattern. There is nothing bad on such implementation.
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 the following setup:
#Applicationscoped
#Transactional(txtype.Requires_new)
Public class querybean {
#Inject ExternalSysrltem externalSystemProxy;
Public Handle gethandleByKey(String key) {
return new Handle(/*do external Systems Query, returns an ExternalHandle Object*/)
}
Public static class Handle {
ExternalHandle eh;
/*protected so that User of class cannot Instantiate it otherwise that by getHandleByKey()*/
Protected Handle(ExternalHandle arg) {
This.eh = arg;
}
Public String getHandleInfo() {
Return This.eh.getName() + "/" + this.eh.getState()..;
/*generally wrap the ExternallHandle with businesslogic to hide direct access to the complex ExternalService's Interface*/
}
}
}
Can I get Handle to be a Managed Bean that can be annotated with #Transactional and still create it in the getHandleByKey Method at Runtime by querying the external System?
A static inner class can be a bean according the the spec.
In your example it is not a bean due to its constructor.
As said in comments you could use a producer, but a produced bean can't be intercepted (with #Transaction here)
If you want to keep your pattern, you'll have to create a very complex extension since it should work at low level to ensure interceptor will be activated.
I suggest that you go for something simpler by deporting your ExternalHandle resolution in Handle Bean, allowing you to use a String to construct it.
First create a qualifier with a non binding member to transmit information to your constructor.
#Target({TYPE, METHOD, PARAMETER, FIELD})
#Retention(RUNTIME)
#Documented
#Qualifier
public #interface Keyed {
#Nonbinding
String key();
}
Then create a literal for your annotation to allow creation of an annotation instance with a given key value.
public class KeyedLiteral extends AnnotationLiteral<Keyed> implements Keyed {
private final String key;
public KeyedLiteral(String key) {
this.key = key;
}
#Override
public String key() {
return key;
}
}
Using programmatic lookup and InjectionPoint to transmit your key value. Your code will be like:
#Applicationscoped
#Transactional(txtype.Requires_new)
Public class querybean {
#Inject
#Any
Instance<Handle> handles;
Public Handle gethandleByKey(String key) {
return instances.select(new KeyedLiteral(key)).get()
}
#Dependent
#Transactional
#Keyed("") //enforce the presence of the annotation for the constructor
Public static class Handle {
ExternalHandle eh;
// needed to make the bean proxyable (mandatory for the interceptor bound))
Protected Handle() {}
#Inject
Protected Handle(InjectionPoint ip, ExternalSysrltem externalSystem) {
String key=ip.getAnnotated().getAnnotation(Keyed.class).key();
eh = /*do external Systems Query, returns an ExternalHandle Object from key and externalSystem*/
}
Public String getHandleInfo() {
Return This.eh.getName() + "/" + this.eh.getState()..;
/*generally wrap the ExternallHandle with businesslogic to hide direct access to the complex ExternalService's Interface*/
}
}
}
Recently, when I played around with Google Guice I was trying to do something like this:
#Override
protected void configure() {
...
bind(Provider.class).to(ViewFactory.class);
...
}
Where ViewFactory was:
public class ViewFactory implements Provider<SomeType> {...}
Of course, Guice didn't let me do that returing error:
1) Binding to Provider is not allowed.
{stacktrace}
What is the reason why it is not possible to bind to provider?
I guess it is because Provider interface is very special to Guice. In fact, all its internal machinery is implemented in term of providers.
Moreover, this could create ambiguities. If bindings to providers were possible:
bind(SomeClass.class).to(SomeClassImpl1.class);
bind(new TypeLiteral<Provider<SomeClass>>() {}).to(() -> new SomeClassImpl2());
then what should Guice inject here?
#Inject
OtherClass(Provider<SomeClass> someClassProvider) { ... }
Should it be a provider which returns SomeClassImpl1 (because of the first binding; remember, direct injections and provider injections are interchangeable in Guice) or should it be a provider which returns SomeClassImpl2 (because of the second binding)?
It really is redundant. Because you can inject SomeClass or Provider<SomeClass> regardless of the actual binding, you can bind the class itself to its provider:
bind(SomeClass.class).toProvider(() -> new SomeClassImpl());
// Either of the following will work
#Inject
OtherClass1(Provider<SomeClass> someClassProvider) { ... }
#Inject
OtherClass2(SomeClass someClass) { ... }
Provider is a special case. Guice does a lot of things behind the scenes with Provider, so they just ban binding to the Provider class entirely. One example is with scoping: your custom Provider might call new every single time, but if you create the provider in the Singleton scope, that should not happen. So Guice doesn't actually inject your provider, it injects a wrapped version. Things like that is why they ban binding to Provider.class directly. Here's a code example:
import com.google.inject.*;
import com.google.inject.name.*;
public class ProviderBindExample {
public static class ProvModule extends AbstractModule {
#Override
protected void configure() {
bind(Foo.class).toProvider(FooProvider.class);
bind(Foo.class).annotatedWith(Names.named("singleton"))
.toProvider(FooProvider.class)
.in(Singleton.class);
}
}
public static interface Foo { }
public static class FooProvider implements Provider<Foo> {
#Override
public Foo get() {
return new Foo() {};
}
}
public static class SomeClass {
#Inject public Provider<Foo> provider;
#Inject #Named("singleton") public Provider<Foo> singletonProvider;
}
public static void main(String... args) {
Injector inj = Guice.createInjector(new ProvModule());
SomeClass s = inj.getInstance(SomeClass.class);
System.out.println("Provider class = " + s.provider.getClass());
System.out.println("Singleton provider class = " + s.singletonProvider.getClass());
Foo first = s.provider.get();
Foo second = s.provider.get();
System.out.printf("regular scope: objects are %s%n", first == second ? "the same" : "different");
first = s.singletonProvider.get();
second = s.singletonProvider.get();
System.out.printf("singleton scope: objects are %s%n", first == second ? "the same" : "different");
}
}
Output:
Provider class = class com.google.inject.internal.InjectorImpl$4
Singleton provider class = class com.google.inject.internal.InjectorImpl$4
regular scope: objects are different
singleton scope: objects are the same