Confused in using java generics proper way - java

I have struck at one point where i am not sure how to move ahead.I have a class with following code
public class MyClass{
private Class<? extends ValidationProvider> providerClass;
//getter ans setter
}
Provider class is being passed at run time and there can be different implementation for that, all it needs to follow the above contract ? extends ValidationProvider.
In my implementation i have provided a property where user can pass the provider name class and based on that i need to move ahead.In order to move ahead i need to get an Instance of provider class using the build in dependency injection mechanism like
public class MyClass{
private Class<? extends ValidationProvider> providerClass;
//getter ans setter
public void setProvider(){
providerClass=container.getInstance("type","name");
}
}
Signature of container.getInstance("type","name"); is
<T> T getInstance(Class<T> type, String name);
i am not sure how to pass type to the container and to get instance of the provider class(since provider class has a signature <? extends ValidationProvider> ).
for simple use case i can do like
container.getInstance(String.class,"my constant");
but not sure how to do it for my use-case
Any help in this regard will really be appreciated.

Let's take a step back... Your requirement is that MyClass should be configurable with different implementations of ValidationProvider, which I am assuming from your syntax is a superclass. I don't think your need generics in MyClass to achieve this:
public class MyClass{
private ValidationProvider provider;
public void setProvider(ValidationProvider _provider){
this.provider = _provider;
}
}
Then if (for example) the configuration is achieved by having the fully-qualified name of the exact provider implementation in a config file, you can set your instance of MyClass as follows:
...
String validatorClassName;
...
// Assign validatorClassName a value from a config file
...
ValidationProvider v = container.getInstance(Class.forName(validatorClassName), "blah");
myClass.setProvider(v);

Related

How can I use Java Enums with Amazon DynamoDB and AWS SDK v2?

I am trying to implement a simple java event-handler lambda for AWS. It receives sqs events and should make appropriate updates to the dynamoDB table.
One of the attributes in this table is a status field that has 4 defined states; therefore I wanted to use an enum class in java and map it to this attribute.
Under AWS SDK v1 I could use the #DynamoDBTypeConvertedEnum annotation. But it does not exist anymore in v2. Instead, there is the #DynamoDbConvertedBy() which receives a converter class reference. There is also an EnumAttributeConverter class which should work nicely with it.
But for some reason, it does not work. The following is a snip from my current code:
#Data
#DynamoDbBean
#NoArgsConstructor
public class Task{
#Getter(onMethod_ = {#DynamoDbPartitionKey})
String id;
...
#Getter(onMethod_ = {#DynamoDbConvertedBy(EnumAttributeConverter.class)})
ExportTaskStatus status;
}
The enum looks as follows:
#RequiredArgsConstructor
public enum TaskStatus {
#JsonProperty("running") PROCESSING(1),
#JsonProperty("succeeded") COMPLETED(2),
#JsonProperty("cancelled") CANCELED(3),
#JsonProperty("failed") FAILED(4);
private final int order;
}
With this, I get the following exception when launching the application:
Class 'class software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.EnumAttributeConverter' appears to have no default constructor thus cannot be used with the BeanTableSchema
For anyone else coming here, it looks do me like just dropping the annotation from the enum altogether works just fine, i.e. the SDK applies the provided attribute converters implicitly. This is also mentioned in this Github issue. My own class looks like this (Brand is an enum here), and the enum is converted without any issues when fetching items.
#Value
#Builder(toBuilder = true)
#DynamoDbImmutable(builder = User.UserBuilder.class)
public class User {
#Getter(onMethod = #__({#DynamoDbPartitionKey}))
String id;
Brand brand;
...
}
How can I use Java Enums with Amazon DynamoDB and AWS SDK v2?
Although the documentation doesn't state it, the DynamoDbConvertedBy annotation requires any AttriuteConverter you supply to contain a parameterles default constructor
Unfortunately for you and me, whoever wrote many of the built-in AttributeConverter classes decided to use static create() methods to instantiate them instead of a constructor (maybe they're singletons under the covers? I don't know). This means anyone who wants to use these helpful constructor-less classes like InstantAsStringAttributeConverter and EnumAttributeConverter needs to wrap them in custom wrapper classes that simple parrot the converters we instantiated using create. For a non-generic typed class like InstantAsStringAttributeConverter, this is easy. Just create an wrapper class that parrots the instance you new up with create() and refer to that instead:
public class InstantAsStringAttributeConverterWithConstructor implements AttributeConverter<Instant> {
private final static InstantAsStringAttributeConverter CONVERTER = InstantAsStringAttributeConverter.create();
#Override
public AttributeValue transformFrom(Instant instant) {
return CONVERTER.transformFrom(instant);
}
#Override
public Instant transformTo(AttributeValue attributeValue) {
return CONVERTER.transformTo(attributeValue);
}
#Override
public EnhancedType<Instant> type() {
return CONVERTER.type();
}
#Override
public AttributeValueType attributeValueType() {
return CONVERTER.attributeValueType();
}
}
Then you update your annotation to point to that class intead of the actual underlying library class.
But wait, EnumAttributeConverter is a generic typed class, which means you need to go one step further. First, you need to create a version of the converter that wraps the official version but relies on a constructor taking in the type instead of static instantiation:
import software.amazon.awssdk.enhanced.dynamodb.AttributeConverter;
import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType;
import software.amazon.awssdk.enhanced.dynamodb.EnhancedType;
import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.EnumAttributeConverter;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
public class EnumAttributeConverterWithConstructor<T extends Enum<T>> implements AttributeConverter<T> {
private final EnumAttributeConverter<T> converter;
public CustomEnumAttributeConverter(final Class<T> enumClass) {
this.converter = EnumAttributeConverter.create(enumClass);
}
#Override
public AttributeValue transformFrom(T t) {
return this.converter.transformFrom(t);
}
#Override
public T transformTo(AttributeValue attributeValue) {
return this.converter.transformTo(attributeValue);
}
#Override
public EnhancedType<T> type() {
return this.converter.type();
}
#Override
public AttributeValueType attributeValueType() {
return this.converter.attributeValueType();
}
}
But that only gets us half-way there-- now we need to generate a version for each enum type we want to convert that subclasses our custom class:
public class ExportTaskStatusAttributeConverter extends EnumAttributeConverterWithConstructor<ExportTaskStatus> {
public ExportTaskStatusAttributeConverter() {
super(ExportTaskStatus.class);
}
}
#DynamoDbConvertedBy(ExportTaskStatusAttributeConverter.class)
public ExportTaskStatus getStatus() { return this.status; }
Or the Lombok-y way:
#Getter(onMethod_ = {#DynamoDbConvertedBy(ExportTaskStatusAttributeConverter.class)})
ExportTaskStatus status;
It's a pain. It's a pain that could be solved with a little bit of tweaking and a tiny bit of reflection in the AWS SDK, but it's where we're at right now.
I am thinking that your annotations might actually be the problem here. I would remove all annotations that mention a constructor, and instead, write out your own constructor(s). For both Task and TaskStatus.
The dynamodb-enhanced SDK does this out of the box.
When you declare a #DynamoDbBean the DefaultAttributeConverterProvider provides a long list of possible ways to convert attributes between java types, including an EnumAttributeConverter which is used if type.rawClass().isEnum() is true. So you don't need to worry about it.
If you ever wanted to extend the number of converters, you would need to add the converterProviders annotation parameter, and declare the default one (or omit it), as well as any other providers you want.
Example:
#DynamoDbBean(converterProviders = { DefaultAttributeConverterProvider.class, MyCustomAttributeConverterProvider.class });
Solution based on watkinsmatthewp Answer:
public class TaskStatusConverter implements AttributeConverter<TaskStatus> {
#Delegate
private final EnumAttributeConverter<TaskStatus> converter;
public TaskStatusConverter() {
converter = EnumAttributeConverter.create(TaskStatus.class);
}
}
Task status attribute looks like this:
#Getter(onMethod_ = {#DynamoDbConvertedBy(TaskStatusConverter.class)})
TaskStatus status;

Java Annotation Processing how to check if VariableElement is a primitive type(int, float) or an object some class

I have a class
public class SomeClass {
#CustomAnnotation1
String stringProperty = "LALALA";
#CustomAnnotation1
int integerProperty;
#CustomAnnotation1
ClassObject classObject;
}
CustomAnnotation1 is a custom annotation defined by me which can be put over any Field. Suppose class ClassObject is something like
public class ClassObject {
#CustomAnnotation1
public String someOtherString;
public String log;
}
What I want to achieve - If my annotation is put on any field which is not a primitive type, I want to access all the fields of that class.
My Approach - Get all the fields annotated with CustomAnnotation1, iterate over all of them and if it is non-primitive, get all the fields inside that class and process.
What I've tried - I am able to get all the elements annotated with my annotation using the below code in my AbstractProcessor class.
Collection<? extends Element> annotatedElements = roundEnvironment.getElementsAnnotatedWith(CustomAnnotation1.class);
List<VariableElement> variableElements = ElementFilter.fieldsIn(annotatedElements);
Questions -
I've researched a lot about the VariableElement class but unable to find a way to check if the field is primitive or not. Can this be done?
Is there any better approach to achieve this?
VariableElement.asType().getKind().isPrimitive()

Configuring a Provider that returns a generic type in Guice

I'm trying to set up a Provider for DAOs created using JDBI. JDBI uses a Handle object (which is a wrapper around a JDBC Connection) and you can get hold of a DAO by using handle.attach(MyDaoType.class). Rather than having to write a separate Provider implementation for every DAO class I thought it would make sense to do this:
public class DaoProvider<T> implements Provider<T> {
private final Class<T> daoType;
private final Handle handle;
#Injected
public DaoProvider(Class<T> daoType, Handle handle) {
this.daoType = daoType;
this.handle = handle;
}
#Override
public T get() {
return handle.attach(daoType);
}
}
But it seems tremendously difficult to wire this up with Guice. I have tried using the #Assisted annotation on the 1st constructor argument as suggested in this answer. I defined a factory like this:
public interface DAOProviderFactory {
<T> DAOProvider<T> create(Class<T> daoType);
}
But it's not clear how I should invoke the FactoryModuleBuilder.implemented method as the whole point is that I don't want to have to extend my provider class.
It also seems a bit crazy that I'd have a factory that returns a provider that returns the thing I actually want!
It strikes me that this would be really easy to do with the Spring DI container so I want to believe it's possible with Guice. Can anyone point me in the right direction?
Thanks to #condit for pointing me at something that enabled me to solve the issue. It's actually very simple. I changed the Provider implementation to use field injection for the Handler like this:
public class DAOProvider<T> implements Provider<T> {
private #Inject Handle handle;
private final Class<T> daoType;
public DAOProvider(Class<T> daoType) {
this.daoType = daoType;
}
#Override public T get() {
return handle.attach(daoType);
}
}
Then in any module or application where I have specific DAO classes I want to bind I can just do something like this:
bind(UserStore.class).toProvider(new DAOProvider<>(UserStore.class));
bind(MessageStore.class).toProvider(new DAOProvider<>(MessageStore.class));
Guice then injects the Handle into the DAOProvider instances automatically.
I think you're making this far too complicated. When you call the get() method, you are storing the result in a reference, which means you know the type of the particular DAO, which means you can write your code knowing the DAO class. I mean, think about how Guice itself works, you call Injector.getInstance(Class<?> type)... in other words, methods like this can't infer the type without you passing the Class anyway, so pass the Class when you use it!
I can understand why you might not want to inject Handle directly though, so why not just make a wrapper, e.g.
public interface DaoProvider {
<T> T provideDao(Class<T> daoType);
}
And then:
public class JdbiDaoProvider implements DaoProvider {
private final Handle handle;
#Inject
JdbiDaoProvider(Handle handle) {
this.handle = handle;
}
public <T> T provideDao(Class<T> daoType) {
return handle.attach(daoType);
}
}

Java CDI: Dynamically selecting an implementation based on qualifier?

I'm trying to make an application extensible by using CDI, but it seems like I'm missing a piece of the puzzle.
What I want:
Have a global configuration that will define which implementation of an interface to use. The implementations would have annotations like #ImplDescriptor(type="type1").
What I tried:
#Produces
public UEInterface createUserExit(#Any Instance<UEInterface> instance, InjectionPoint ip) {
Annotated annotated = ip.getAnnotated();
UESelect ueSelect = annotated.getAnnotation(UESelect.class);
if (ueSelect != null) {
System.out.println("type = " + ueSelect.type());
}
System.out.println("Inject is ambiguous? " + instance.isAmbiguous());
if (instance.isUnsatisfied()) {
System.out.println("Inject is unsatified!");
return null;
}
// this would be ok, but causes an exception
return instance.select(ueSelect).get();
// or rather this:
for (Iterator<UEInterface> it = instance.iterator(); it.hasNext();) {
// problem: calling next() will trigger instantiation which will call this method again :(
UEInterface candidate = it.next();
System.out.println(candidate.getClass().getName());
}
}
This code is close to an example I've seen: The #Produces method will be used to select and create instances and a list of candidates is injected as Instance<E>. If the method simply creates and returns an implementation, it works fine. I just don't know how to examine and select a candidate from the Instance<E>. The only way of looking the the "contents" seems to be an Iterator<E>. But as soon as I call next(), it will try to create the implementation... and unfortunately, calls my #Produces method for that, thereby creating an infinite recursion. What am I missing? How can I inspect the candidates and select one? Of course I want to instantiate only one of them...
Thanks in advance for any help and hints!
I think the issue is you are trying to select the annotation's class rather than using the annotation as a selector qualifier. Using the class directly searches for an implementation that implements that class. You need to create an AnnotationLiteral using the #ImplDescriptor class to perform a select using it as a qualifier. Create a class extending AnnotationLiteral like so.
public class ImplDescriptorLiteral extends AnnotationLiteral<ImplDescriptor> implements ImplDescriptor {
private String type;
public ImplDescriptorLiteral(String type) {
this.type = type;
}
#Override
public String type() {
return type;
}
}
then you can pass an instance of this class to the select method using the type you want.
instance.select(new ImplDescriptorLiteral("type1")).get();
Refer to the Obtaining a contextual instance by programmatic lookup documentation for more information.
Finch, what you have here should work. it assumes though that you have an instance of UEInterface that is annotated #UESelect, e.g.
#UESelect("one")
public class UEOne implements UEInterface {
..
}
Is this how you're expecting it to work?

What's the correct way of setting properties with Guice?

Suppose, we have 2 classes - Main and MainDependency. The second class is used only by Main, and the purpose of using IoC is constructing an instance of Main class.
MainDependency class has a field of integer type. This field is not required to be set (or, let's assume it should always have a default value if nothing else is specified).
The problem: what's the most correct way to set the integer field? One way I see is creating similar field inside my Module and then using that value inside configure module. But I feel it's a wrong way.
Please, share your experience. Thx in advance.
I think you mainly have two options:
1) Inject it using constant binding. The value of MY_CONSTANT could be passed to Module at instantiation time; could be taken from a system property, or maybe some other way.
class MainDependency{
#Inject
public MainDependency(#Named("myConst") int myConst){
//...
}
}
class Module extends AbstractModule{
public void configure(){
bindConstant().annotatedWith(Names.named("myConst").to(MY_CONSTANT);
}
}
2) Use assisted inject to create a factory which will take your value as a parameter and return an instance of MainDependency:
interface MainDependencyFactory{
MainDependency create(int myConst);
}
class MainDependency{
#Inject
public MainDependency(#Assisted int myConst){
//..
}
}
class Module extends AbstractModule{
public void configure(){
bind(MainDependencyFactory.class).toProvider(
FactoryProvider.newFactory(MainDependencyFactory.class, MainDependency.class));
}
}
//to use the above, instantiate your factory (or inject it somewhere)
MainDependencyFactory f = injector.getInstance(MainDependencyFactory.class);
//Then you can create MainDependency with any value
MainDependency md = f.create(MY_CONSTANT);
Note that with assisted inject you don't need to implement MainDependencyFactory. Guice will create it for you.

Categories