Generic CDI producer method not working as expected - java

I have a CDI producer method which - depending on some conditions not relevant to this example - creates objects of different types:
public class TestProducer {
#Produces #TestQualifier
public Object create(InjectionPoint ip) {
if(something) {
return "a String";
} else {
return Integer.valueOf(42);
}
}
but when using this producer, I always get an error in the followin situation:
#Named("test")
public class TestComponent {
...
#Inject public void setA(#TestQualifier String stringValue) {
...
#Inject public void setB(#TestQualifier Integer integerValue) {
It only works when the create method of the producer has the expected type in the method signature:
public class TestProducer {
#Produces #SpringBean
public String create(InjectionPoint ip) {
Now the String get's injected correctly, but I have no way to also generate an integer from the producer method. But this is exactly what I want to avoid, since the producer itself should be completely generic.
Am I doing something wrong or is there no way to achieve the behaviour I want?

All CDI documentation makes it clear that CDI does typesafe dependency injection - and it is an exalted property of CDI. IMHO, what you are trying to do is just what CDI tries to avoid. You want the container to cast Object to each type and CDI does not work that way.
The injections points stringValue and integerValue can only receive a bean which has java.lang.String and java.lang.Integer in its list of bean types respectively. java.lang.Object does not satisfy this criterion.
I have two suggestions. First, since you have two or more injection points of different types, create two or more producer methods for that types:
public class TestProducer {
#Produces #TestQualifier
public String createString(InjectionPoint ip) {
if(something) {
return "a String";
} else {
// Some other value
}
}
#Produces #TestQualifier
public int createInt(InjectionPoint ip) {
if(something) {
return 42;
} else {
// Some other value
}
}
// ...
It works if the something condition is just to check the type of the injection point (what I am betting is the case).
However, if the something condition does decide the type using other criteria than the type of the injection point, I'd suggestion to do the "dirty job" yourself: inject the returned value in an Object-typed injection point and does the cast manually:
#Named("test")
public class TestComponent {
...
#Inject public void setA(#TestQualifier Object value) {
String stringValue = (String) value;
...
#Inject public void setB(#TestQualifier Object value) {
int intValue = (Integer) value;
The main point is that, unlike some other DI frameworks, CDI does not work against the Java type system - on the contrary, it heavily uses it. Do not try to fight against it but use this aspect of CDI in your favor :)

A producer for Object is strange anyway. I'm not sure if this is forbidden by the spec, or it's a bug, but I think you can make some clever workaround:
public class ValueHolder<T> {
private T value;
public T getValue() {
return value;
}
}
And then inject a ValueHolder<String> and ValueHolder<Integer>

Its possible create generic objects with CDI produces like that:
// the wrapper class
public class Wrapper<T> {
public final T bean;
public Wrapper(T bean){
this.bean = bean;
}
}
// the producer inside some class
#Produces
public <T> Wrapper<T> create(InjectionPoint p){
// with parameter 'p', it is possible retrieve the class type of <T>, at runtime
}
// the bean example 1
public class BeanA {
public void doFoo(){
// ...
}
}
// the bean example 2
public class BeanB {
public void doBar(){
// ...
}
}
// the class that uses the produced beans
public class SomeBean{
//// There on producer method, do you can retrieve the Class object of BeanA and BeanB, from type parameters of Wrapper.
#Inject
private Wrapper<BeanA> containerA;
#Inject
private Wrapper<BeanB> containerB;
public void doSomeThing(){
containerA.doFoo();
containerB.doBar();
}
}
Works on weld 2.2.0.
I think that works on some previous versions as well.

Your initializer methods will look for a managed bean with API types String and Integer, but your producer method bean only has API type (in case of producer method, return type) Object.
You can therefore only use Object in your initializer method injected fields and then discriminate between the types int the body of the receiver, or simply wrap them and the producer method in an actual type that can return Strings or Int (but I'd avoid the generics)

Related

Convert automatically into a centralized bean for multiple domain objects

I am creating a project which will respond to collect multiple bean object, save it to the database and return the status of the transaction. There can be multiple objects that can be sent from the client. For each object, they are having separate database thus separate controller.
So I planned to create a framework that can accept multiple objects from multiple controllers and send only one centralized object. But I am not sure how to use a centralized object as a return type in the controller(currently I returned them as Object). Below is my code:
Controller:
#RestController
#RequestMapping("/stat/player")
public class PlayerController {
#Autowired
private StatService<PlayerValue> statPlayer;
#RequestMapping("/number/{number}")
public Object findByNumber(#PathVariable String number) { // Here returning Object seem odd
return statPlayer.findByNumber(number);
}
}
Service:
#Service
#Transactional(isolation = Isolation.READ_COMMITTED)
public class PlayerServiceImpl implements StatService<PlayerValue> {
#Autowired
private PlayerRepository repository;
#Override
public PlayerValue findByNumber(String number) {
Optional<PlayerEntity> numberValue = repository.findByNumber(number);
return numberValue.map(PlayerEntity::toValue).orElse(null);
}
}
In service I returned the PlayerValue object but I want to wrap this object into a centralized bean ResponseValue. I created an aspect for that
#Aspect
#Component
public class Converter {
private static final Logger LOG = LoggerFactory.getLogger(Converter.class);
#Pointcut("within(#org.springframework.web.bind.annotation.RestController *)")
public void restControllerClassMethod() {}
private <T> ResponseValue<T> convert(List<T> results) {
String message = results.isEmpty() ? "No result found" : ResponseValueStatus.OK.toString();
return new ResponseValue<>(ResponseValueStatus.OK, message, results);
}
#Around("restControllerClassMethod()")
#SuppressWarnings("unchecked")
public <T> ResponseValue<T> convert(ProceedingJoinPoint joinPoint) {
ResponseValue value;
try {
Object findObject = joinPoint.proceed();
List<Object> objects = toList(findObject);
value = convert(objects);
} catch (NullPointerException e) {
throw new StatException(String.format("Exception thrown from %s from %s method with parameter %s", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), joinPoint.getArgs()[0].toString()));
//this exception will go in a controller advice and create a response value with this message
} catch (Throwable e) {
LOG.error("Exception occurred while converting the object", e);
throw new StatException(String.format("Exception thrown from %s from %s method with parameter %s with exception message %s", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), joinPoint.getArgs()[0].toString(), e.getMessage()));
}
return value;
}
private List<Object> toList(Object findObject) {
List<Object> objects = new ArrayList<>();
if (findObject instanceof List) {
((List) findObject).forEach(item -> objects.add(findObject));
} else {
objects.add(findObject);
}
return objects;
}
}
To sum up, There could be multiple entity similar to PlayerValue. I need a way to return the result in a centralized bean. Above process work, BUT for this I have to give return type as Object in Controller. Does anybody has an idea how can I use return type as List or T in controller. Also I know it can be done by implementing a ValueConverter interface, but this conversion is straightforward. So it would be nice if any other developer don't have to implement the ValueConverter everytime he want to add a different controller.
Also feel free to review the implementation and let me know if anyone has some alternative idea or some comments.
Note: I reduce a lot of code in the question so that it can be easier to understandable without understanding the actual requirement context. Please do let me know if anyone need more info.
After some research I came across to a better design solution for the framework (but of course with flaws) to achieve conversion to a centralized bean for multiple domain objects is to use a marker interface.
Marker interface can provide a centralized type for all the bean. The main rule need to be followed by the client is to implement that marker interface. So the basic solution is
Marker interface:
public interface StateResponseValue<T> {
}
Implement the interface in all the bean.
public class PlayerValue implements StateResponseValue<PlayerValue> {
}
public class ResponseValue<T> implements StateResponseValue<T> {
//fields and their getter and setter
}
Change the return type in service and controller.
public interface StatService<T> {
StateResponseValue<T> findByNumber(String number);
}
Change the return type in controller
#RestController
#RequestMapping("/stat/player")
public class PlayerController {
#Autowired
private StatService<PlayerValue> statPlayer;
#RequestMapping("/number/{number}")
public StateResponseValue<T> findByNumber(#PathVariable String number) { // Here returning Object seem odd
return statPlayer.findByNumber(number);
}
}
Note: The main drawback I feel is that whenever we want to access the field client need to explicitly cast the object to ResponseValue which is still pretty ugly.
What if you create an AbstractStatController which is generic ?
Generic interface StatService:
public interface StatService<T> {
T findByNumber(String number);
}
Generic abstract class AbstractStatController:
public abstract class AbstractStatController<T> {
abstract StatService<T> getStatService();
#RequestMapping("/number/{number}")
public T findByNumber(#PathVariable String number) {
return getStatService().findByNumber(number);
}
}
Concrete class PlayerController:
#RestController
#RequestMapping("/stat/player")
public class PlayerController extends AbstractStatController<Player> {
private final PlayerService playerService;
public PlayerController(PlayerService playerService) {
this.playerService = playerService;
}
#Override
StatService<Player> getStatService() {
return playerService;
}
}

enum returning spring beans dynamically

Is it possible for an enum to return different spring beans for different values?
If possible, we can create an enum holding different values, and we can return different type of spring component for a different value.
I wanted to achieve something like this:
public enum MyFactory {
BEAN1 {
#Autowired
Bean1 bean1;
#Override
public MyBean getMyBean() {
return bean1;
}
},
BEAN2 {
#Autowired
Bean1 bean2;
#Override
public MyBean getMyBean() {
return bean2;
}
};
public abstract MyBean getMyBean();
}
Thanks,
Java enums are designed to be constant values.
But to achieve you requirement, you should set the enum states after their creation or passing as parameter the ApplicationContext to the enum method or to an enum method initialization.
It defeats the enum purpose : constant values.
I think that it is make more sense to define a bean class that provides beans defined once and without way to change them.
#Component
public class MyEnumClass{
#Autowired
private Value valueA;
#Autowired
private Value valueB;
public Value getValueA(){
return valueA;
}
public Value getValueB(){
return valueB;
}
...
}
Where Value class is preferably not mutable.
Do not use enums to conditionally inject.
Spring is more than capable of doing that on its own.
This runs somewhat counter to the whole benefit of dependency injection; so long as your components define a common interface between them, you can inject whichever one you want at will without the need for (self-managed) conditions.
Supposing that you had components AComponent and BComponent. They are related and share a common-enough interface that it makes sense to codify it as an interface. You can then define this.
public interface Component {
Integer generateValue(String foo, List<Integer> bar);
}
public class AComponent implements Component {}
public class BComponent implements Component {}
Then, you can inject it in at will:
private Component component;
#Autowired
public MyService(#Qualifier("bComponent") Component component) {
this.component = component;
}
Spring is perfectly capable of managing the dynamic wiring for you here.
Alternatively, if these components are completely unrelated to one another, then you'll have to wire them in individually anyway as opposed to dynamically selecting one based on your needs.

Guice FactoryModuleBuilder an instance with constructor parameters

I´m using Guice to initalize a class with some arguments from a config file
#Provides
#Singleton
RetryServiceCaller provideMaxRetryAttempts(#Named("config") JsonObject config) throws IOException {
JsonObject retryDetails = config.getJsonObject("retry_details");
return new RetryServiceCaller(retryDetails.getInteger("maxRetryAttempts"), retryDetails.getInteger("upperBoundary"), retryDetails.getInteger("lowerBoundary"),
retryDetails.getLong("multiplicationFactor"), retryDetails.getInteger("timeout"), retryDetails.getInteger("increaseTimeout"));
}
This class is injected in another class which is singleton as well.
class A{
#Inject private RetryServiceCaller retryServiceCaller;
}
But now the problem is that since this new class A is singleton, I need to clone the retryServiceCaller every time that somebody use this class A.
I´ve been investigating FactoryModuleBuilder to use it and create a factory for this class. But since the class has parameters from the config file I could not find the way to make it works.
Something like this
class A{
#Inject private RetryServiceCaller.Factory retryServiceCallerFactory;
}
Then in my RetryServiceCaller implement this
public interface Factory {
#Inject
RetryServiceCaller create();
}
#Inject
public RetryServiceCaller(int maxRetryAttempts, int upperBoundary, int lowerBoundary, long multiplicationFactor, int timeout, int incrementTimeout) {
this.maxRetryAttempts = maxRetryAttempts;
this.upperBoundary = upperBoundary;
this.lowerBoundary = lowerBoundary;
this.multiplicationFactor = multiplicationFactor;
this.timeout = timeout;
this.incrementTimeout = incrementTimeout;
}
But guice throw me errors saying
No implementation for com.proxy.handlers.RetryServiceCaller$Factory was bound
Guice can automatically provide a zero-argument factory: Instead of injecting Foo, you can always inject Provider<Foo>. This allows you to call fooProvider.get() to create an instance whenever and wherever you'd like. You don't have to bind to a Provider or use a Provides method to get access to this; you can inject Foo or Provider<Foo> whether you use a bind(...).to(...) type binding, a toProvider binding, a toInstance binding, a #Provides method, or anything else, and Guice will call get or return an internal Provider automatically.
(The returned Provider will also respect scopes, so you'll need to drop your #Singleton scope in order to get more than one instance, and be aware that toInstance bindings will always return the same instance.)
This is not a job for FactoryModuleBuilder; only use FactoryModuleBuilder when you need to mix injected and non-injected constructor parameters in the same type.
Your finished binding should look like this:
#Provides
/* NOT #Singleton */
RetryServiceCaller provideMaxRetryAttempts(#Named("config") JsonObject config) throws IOException {
JsonObject retryDetails = config.getJsonObject("retry_details");
return new RetryServiceCaller(retryDetails.getInteger("maxRetryAttempts"), retryDetails.getInteger("upperBoundary"), retryDetails.getInteger("lowerBoundary"),
retryDetails.getLong("multiplicationFactor"), retryDetails.getInteger("timeout"), retryDetails.getInteger("increaseTimeout"));
}
And in your class:
#Inject public YourCallerConsumer(Provider<RetryServiceCaller> callerProvider) {
this.callerProvider = callerProvider;
}
public void doAction() {
RetryServiceCaller newCaller = callerProvider.get();
// interact with caller
}
Your first approach should work just fine. If you don't want the RetryServiceCaller to be a singleton, remove the #Singleton annotation from the provider method, and a new instance will be created for every injection point.
Assisted inject could work here too, but it's overkill. If you want to go that route:
interface RetryServiceCallerFactory {
RetryServiceCaller create(String configParam1, String configParam2);
}
public class RetryServiceCaller {
#AssistedInject
public RetryServiceCaller(String configParam1, String configParam2) {}
}
then, in your module
install(new FactoryModuleBuilder().build(Factory.class);
and in your injection points
#Inject RetryServiceCallerFactory factory;
RetryServiceCaller create(JsonObject config) {
return factory.create(config.getFirstParam(), config.getSecondParam());
}
You can refer to the documentation for more extensive examples.

How to pass parameter to injected class from another class in CDI?

I am new to CDI, tried to find solution for this question, but, couln't found any. Question is Suppose I have one class which is being injected(A) from, where some value(toPass) is getting injected, now I want to pass this same value(toPass) to class B, which is getting injected from class A.
public class A
{
String toPass = "abcd"; // This value is not hardcoded
#Inject
private B b;
}
public class B
{
private String toPass;
public B(String toPass)
{
toPass = toPass;
}
}
Can anybody please help me in this?
Note: we cannot initialize the toPass variable of B in the same way as we have initialized in A, there is some restriction to it. Basically in Spring we could have done it easily, but, I wanted to do it in CDI.
You have options:
1.
Set toPass variable to b from #PostConstruct method of bean A:
#PostConstruct
public void init() {
b.setToPass(toPass);
}
or
2.
Create producer for toPass variable and inject it into bean A and B.
Producer:
#Produces
#ToPass
public String produceToPass() {
...
return toPass;
}
Injection:
#Inject
#ToPass
String toPass;
or
3.
If bean A is not a dependent scoped bean you can use Provider interface to obtain an instance of bean A:
public class B
{
#Inject
Provider<A> a;
public void doSomeActionWithToPass() {
String toPass = a.get().getToPass());
...
}
But you should not use toPass from constructor or from #PostConstruct method.
I need to say before that injection happens just after the object is created and therefore in case toPass is going to change during the life of A object, this change will not have any effect on the already injected B object.
(It would be probably possible to overcome this with some hacky things like creating your own producer method and producing some kind of proxy that would lazily initialize the B instance... But that would be probably not nice )
public class A
{
String toPass = "abcd"; // This value is not hardcoded
private B b;
#Inject
public void setB(B b) {
this.b = b;
b.pass(toPass);
}
}

How to assign a default 'fallback' provider for a specific type in Google Guice

The use case I have is this:
An object requests a unbound #Named injection of a specific type. I want Guice to tell my 'fallback' provider what the name value was (or give me the entire annotation) so my provider can still provide an implementation based on the name value.
The #Named value is thus used as a key to find the correct implementation at runtime. If an implementation is found based on the #Named, then the provider should not be consulted.
Guice is basically a map from a (annotation, type) tuple to a Provider<T>. There is no more a way to bind a default binding to all unbound #Named instances than there is to bind an entire range of doubles to a single string in a Map<Double, String>.
If you have a convenient array or Collection of all #Namedvalues you want, you could loop through them and bind them all to some kind of default provider, which you could then hack using Modules.override() or an if block within the loop:
public class DefaultNameModule extends AbstractModule() {
#Override public void configure() {
for (String name : YOUR_LIST_OF_NAMES) {
if (properties.contains(name)) {
bindConstant(properties.get(name)).annotatedWith(Names.named(name));
} else {
bind(Foo.class)
.annotatedWith(Names.named(name))
.toProvider(new MyProvider(name));
}
}
}
private static class MyProvider extends Provider<Foo> {
final String name;
MyProvider(String name) {
this.name = name;
}
#Override public Foo get() {
return someValueBasedOn(name);
}
}
}
If this is too complex for your taste, remember that you can always inject an instance of a one-method object that wraps the Properties access for you, calculating a default if needed.

Categories

Resources