I'm developing a library with CDI. This works fine. But when I try to use this library as an included Jar in a project, CDI complains that it can't resolve dependencies on managed Beans defined in the parent Project.
MyProject
- MyLib.jar
- MyManagedBean
So MyLib needs to inject a bean annotated with a stereotype #foo. This stereotype is applied on MyManagedBean.
#foo
public class MyManagedBean {
//...
}
I added beans.xml to my parent project too. But it's like there is 2 different CDI Container and MyLib can't access MyProject.
Any suggestion?
I was able to access the parent class file by using the CDI extension. Documentation and reflection.
This is the code i use :
public class ConfigExtension implements Extension {
void afterBeanDiscovery(#Observes AfterBeanDiscovery abd, BeanManager bm) {
Reflections reflections = new Reflections("");
Set<Class<?>> types = reflections.getTypesAnnotatedWith(Tester.class);
types.remove(info.scandi.fusion.cucumber.Tester.class);
types.addAll(reflections.getTypesAnnotatedWith(Driver.class));
types.addAll(reflections.getTypesAnnotatedWith(Worker.class));
types.forEach(type -> {
abd.addBean(new FusionBean((Class<T>) type, bm));
});
}
}
public class FusionBean<T> implements Bean<T>, Serializable, PassivationCapable {
/**
*
*/
private static final long serialVersionUID = 1L;
private InjectionTarget<T> it;
private Class<T> bean;
private BeanManager bm;
public FusionBean(Class<T> workerClass, BeanManager bm) {
this.bm = bm;
bean = workerClass;
AnnotatedType<T> at = bm.createAnnotatedType(bean);
// use this to instantiate the class and inject dependencies
it = bm.createInjectionTarget(at);
}
#Override
public T create(CreationalContext<T> creationalContext) {
T instance = it.produce(creationalContext);
it.inject(instance, creationalContext);
it.postConstruct(instance);
return instance;
}
#Override
public void destroy(T instance, CreationalContext<T> creationalContext) {
it.preDestroy(instance);
it.dispose(instance);
creationalContext.release();
}
#Override
public Set<Type> getTypes() {
Set<Type> types = new HashSet<>();
//Use Apache Common Lang to get all Interfaces and Superclasses
types.addAll(ClassUtils.getAllInterfaces(bean));
types.addAll(ClassUtils.getAllSuperclasses(bean));
return types;
}
#Override
public Set<Annotation> getQualifiers() {
Set<Annotation> annotations = new HashSet<>();
for (int i = 0; i < bean.getAnnotations().length; i++) {
Class<? extends Annotation> possibleQualifier = bean.getAnnotations()[i].annotationType();
if (bm.isQualifier(possibleQualifier)) {
annotations.add(bean.getAnnotations()[i]);
}
}
return annotations;
}
#Override
public Class<? extends Annotation> getScope() {
for (int i = 0; i < bean.getAnnotations().length; i++) {
Class<? extends Annotation> possibleScope = bean.getAnnotations()[i].annotationType();
if (bm.isStereotype(possibleScope)) {
for (Annotation annotation : possibleScope.getAnnotations()) {
if (bm.isScope(annotation.annotationType())) {
return annotation.annotationType();
}
}
}
}
return null;
}
#Override
public String getName() {
return bean.getName();
}
#Override
public Set<Class<? extends Annotation>> getStereotypes() {
Set<Class<? extends Annotation>> stereotypes = new HashSet<>();
for (int i = 0; i < bean.getAnnotations().length; i++) {
Class<? extends Annotation> possibleStereotype = bean.getAnnotations()[i].annotationType();
if (bm.isStereotype(possibleStereotype)) {
stereotypes.add(possibleStereotype);
}
}
return stereotypes;
}
#Override
public boolean isAlternative() {
for (int i = 0; i < bean.getAnnotations().length; i++) {
if (bean.getAnnotations()[i].equals(Alternative.class)) {
return true;
}
}
return false;
}
#Override
public Class<?> getBeanClass() {
return bean.getClass();
}
#Override
public Set<InjectionPoint> getInjectionPoints() {
return it.getInjectionPoints();
}
#Override
public boolean isNullable() {
return false;
}
#Override
public String getId() {
return UUID.randomUUID().toString();
}
}
The trick is CDI Extension has access to Parent Classpath. Here i use the Reflection api to get Tester, Driver et Worker classes that the user using my framework could specified.
Related
*The implementation module is not being garbage collected after it is de-reference in UI module. I have overridden finalize method in all the classes of implementation. finalize method of none of the objects being called after dereferencing. No Thread is running in implementation module when it is de-referenced.
The below code is only to provide a perspective of project. The Set of WeakReferences in BankConfig, Main class are not part of original project.
The CoreConfig and CoreController belong to implementation module.
*
public class BankConfig {
public final String uuid = UUID.randomUUID().toString();
public final String name;
public Controller controller;
public BankConfig(String name, Controller controller) {
this.name = name;
this.controller = controller;
}
#SuppressWarnings("deprecation")
#Override
protected void finalize() throws Throwable {
System.out.println("garbage collected : "+uuid);
super.finalize();
}
#Override
public String toString() {
return "bank-"+uuid;
}
}
public interface Controller {
public abstract BankConfig getBankConfig();
}
public class CoreConfig extends BankConfig {
public CoreConfig(String name,Controller controller) {
super(name, controller);
}
public CoreConfig(BankConfig bankConfig) {
super(bankConfig.name, bankConfig.controller);
}
public CoreConfig(BankConfig bankConfig, final Controller controller) {
super(bankConfig.name, controller);
}
#Override
public String toString() {
return "core-"+uuid;
}
}
public class CoreController implements Controller {
public final CoreConfig config;
public CoreController(BankConfig config) {
this.config = new CoreConfig(config, this);
}
#Override
public BankConfig getBankConfig() {
return config;
}
}
public class Main {
private static final Set<WeakReference<BankConfig>> WEAK_REFERENCES = new HashSet<>();
public static final ObservableList<BankConfig> banks = FXCollections.observableArrayList();
static {
banks.add(readConfigFromDatabase("krishna"));
banks.add(readConfigFromDatabase("shankar"));
}
public static void main(String[] args) throws InterruptedException {
banks.add(loadController(readConfigFromDatabase("krishna")).getBankConfig());
banks.add(loadController(readConfigFromDatabase("shankar")).getBankConfig());
for (BankConfig bankConfig : banks) {
WEAK_REFERENCES.add(new WeakReference<BankConfig>(bankConfig));
}
banks.clear();
for (int i = 0; i < 5; i++) {
System.out.println("strong references : "+banks);
System.out.println("weak references : "+WEAK_REFERENCES.stream().filter(ref -> ref.get() != null).map(ref -> ref.get()).collect(Collectors.toSet()));
System.gc();
Thread.sleep(5000);
}
}
public static final Controller loadController(final BankConfig config) {
try {
final List<String> moduleNames = List.of("implementation");
final URLClassLoader loader = new URLClassLoader(new URL[0]);
final Configuration configuration = ModuleLayer.boot().configuration().resolveAndBind(ModuleFinder.of(Path.of(new URL("path to implementation jar").toURI())), ModuleFinder.of(), moduleNames);
final ModuleLayer moduleLayer = ModuleLayer.boot().defineModulesWithOneLoader(configuration, loader);
final Optional<Module> module = moduleLayer.findModule("implementation");
final Class<?> controllerClass = module.get().getClassLoader().loadClass("implementation.CoreController");
final Controller controller = (Controller) controllerClass.getConstructors()[0].newInstance(config);
return controller;
} catch (Exception e) {e.printStackTrace();}
return null;
}
public static BankConfig readConfigFromDatabase(final String name) {
if("krishna".equals(name)) return new BankConfig("krishna", null);
else if("shankar".equals(name)) return new BankConfig("shankar", null);
return null;
}
}
The issue was caused by datakernel library used in implementation module caused due to JmxModule registering jmx-compatible components to the global jmx registry.
Reference : datakernel issue #17
I'm trying to create annotations from inner string which contains other annotations.
This is SimpleAnnotation that should be processed:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.SOURCE)
public #interface SimpleAnnotation {
String[] value() default {};
}
This is annotated class
#SimpleAnnotation({
"#com.demo.annotations.Entity(name = \"simple_name\")",
"#com.demo.annotations.CustomAnnotation"
})
public class Simple {
}
The compilation result of annotated class should be
#com.demo.annotations.Entity(name = "simple_name")
#com.demo.annotations.CustomAnnotation
public class Simple {
}
I've tried to use custom annotation processor
that processes class declaration. It gets class modifiers with annotations and analyzes derived annotation as tree
public class SimpleAnnotationProcessor extends AbstractProcessor {
private Messager messager;
private Trees trees;
private ChangeTranslator visitor;
#Override
public Set<String> getSupportedAnnotationTypes() {
return Collections.singleton(SimpleAnnotation.class.getCanonicalName());
}
#Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_8;
}
#Override
public synchronized void init(ProcessingEnvironment processingEnv) {
............
}
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(SimpleAnnotation.class);
for (Element element : elementsAnnotatedWith) {
Name simpleName = element.getSimpleName();
System.out.println(simpleName);
messager.printMessage(Diagnostic.Kind.NOTE, "found with annotation " + simpleName);
JCTree tree = (JCTree) trees.getTree(element);
visitor.setElement(element);
tree.accept(visitor);
}
return true;
}
public class ChangeTranslator extends TreeTranslator {
private JavacProcessingEnvironment javacProcessingEnvironment;
private TreeMaker treeMaker;
private Messager messager;
public ChangeTranslator(JavacProcessingEnvironment javacProcessingEnvironment, TreeMaker treeMaker, Messager messager) {
this.javacProcessingEnvironment = javacProcessingEnvironment;
this.treeMaker = treeMaker;
this.messager = messager;
}
#Override
public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
super.visitClassDef(jcClassDecl);
if (isNeedProcessing(jcClassDecl)) {
JCTree.JCModifiers modifiers = jcClassDecl.getModifiers();
List<JCTree.JCAnnotation> annotations = modifiers.getAnnotations();
List<JCTree.JCAnnotation> jcAnnotations = List.nil();
for (JCTree.JCAnnotation a : annotations) {
if (a.getAnnotationType().toString().contains(SimpleAnnotation.class.getSimpleName())) {
List<JCTree.JCExpression> arguments = a.getArguments();
for (JCTree.JCExpression arg : arguments) {
JCTree.JCNewArray expressions = (JCTree.JCNewArray) ((JCTree.JCAssign) arg).getExpression();
List<JCTree.JCExpression> elems = expressions.elems;
for (JCTree.JCExpression expression : elems) {
// parse annotation from string
String value = (String) ((JCTree.JCLiteral) expression).getValue();
// e.g com.demo.annotations.Entity
String substringName = value.trim().substring(1, 28);
Class<? extends Class> aClass = null;
try {
aClass = Class.forName(substringName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 1 - attribute to create annotation from
Attribute attribute = new Attribute.Compound(aClass, null);
// 2 - place where annotation should be created
treeMaker.Annotation(attribute);
}
}
}
}
modifiers.annotations = jcAnnotations;
System.out.println(result);
}
}
private boolean isNeedProcessing(JCTree.JCClassDecl jcClassDecl) {
return jcClassDecl.getModifiers().toString().contains("#SimpleAnnotation");
}
}
}
The issue is to get information from Class type to create com.sun.tools.javac.code.Type.ClassType which is used to create JCAnnotation.
Any help is appreciated.
public class SimpleAnnotationProcessor extends AbstractProcessor {
...
#Override
public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
...
ListBuffer<JCTree.JCExpression> params = new ListBuffer<JCTree.JCExpression>();
params.append(treeMaker.Assign(treeMaker.Ident(names.fromString("name")), treeMaker.Literal("simple_name")));
JCTree.JCAnnotation entity = treeMaker.Annotation(select("com.demo.annotations.Entity"), params.toList());
JCTree.JCAnnotation customAnnotation = treeMaker.Annotation(select("com.demo.annotations.CustomAnnotation"), List.nil());
// then append annotation to modifiers of you want
// NOTE: List<A>.append() method will return a new List in javac
...
}
JCTree.JCExpression select(String path) {
JCTree.JCExpression expression = null;
int i = 0;
for (String split : path.split("\\.")) {
if (i == 0)
expression = treeMaker.Ident(names.fromString(split));
else {
expression = treeMaker.Select(expression, names.fromString(split));
}
i++;
}
return expression;
}
}
Hope it helps those who have the same problem
How should I do the ValueFactoryProvider binding in order to have two custom injection annotations coexist in Jersey 2? Below I have included an example of my current approach and as you can see the Hello annotation injection "hides" the SmallTalk annotation injection.
Hello annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.PARAMETER})
public #interface Hello {
}
SmallTalk annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.PARAMETER})
public #interface SmallTalk {
}
Hello annotation resolver:
#Singleton
public class HelloResolver {
public static class HelloInjectionResolver extends ParamInjectionResolver<Hello> {
public HelloInjectionResolver() {
super(HelloValueFactoryProvider.class);
}
}
#Singleton
public static class HelloValueFactoryProvider extends AbstractValueFactoryProvider {
#Inject
public HelloValueFactoryProvider(final MultivaluedParameterExtractorProvider extractorProvider,
final ServiceLocator injector) {
super(extractorProvider, injector, UNKNOWN);
}
#Override
protected Factory<?> createValueFactory(final Parameter parameter) {
final Class<?> classType = parameter.getRawType();
if (classType == null || (!classType.equals(String.class))) return null;
return new AbstractContainerRequestValueFactory<String>() {
#Override
public String provide() {
return "Hello!";
}
};
}
}
public static class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(HelloValueFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class);
bind(HelloInjectionResolver.class).to(
new TypeLiteral<InjectionResolver<Hello>>() {
}
).in(Singleton.class);
}
}
}
SmallTalk annotation resolver:
#Singleton
public class SmallTalkResolver {
public static class SmallTalkInjectionResolver extends ParamInjectionResolver<SmallTalk> {
public SmallTalkInjectionResolver() {
super(SmallTalkValueFactoryProvider.class);
}
}
#Singleton
public static class SmallTalkValueFactoryProvider extends AbstractValueFactoryProvider {
#Inject
public SmallTalkValueFactoryProvider(final MultivaluedParameterExtractorProvider extractorProvider,
final ServiceLocator injector) {
super(extractorProvider, injector, UNKNOWN);
}
#Override
protected Factory<?> createValueFactory(final Parameter parameter) {
final Class<?> classType = parameter.getRawType();
if (classType == null || (!classType.equals(String.class))) return null;
return new AbstractContainerRequestValueFactory<String>() {
#Override
public String provide() {
return "Nice weather.";
}
};
}
}
public static class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(SmallTalkValueFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class);
bind(SmallTalkInjectionResolver.class).to(
new TypeLiteral<InjectionResolver<SmallTalk>>() {
}
).in(Singleton.class);
}
}
}
Resource configuration:
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(new HelloResolver.Binder());
register(new SmallTalkResolver.Binder());
registerClasses(HelloResource.class);
}
}
Resource using both injection annotations:
#Path("/")
public class HelloResource {
#GET
#Path("hello")
#Produces("application/json")
public String hello(#Hello final String hello, #SmallTalk final String smallTalk) {
return hello + " " + smallTalk;
}
}
Result when requesting the resource - should have been "Hello! Nice weather.":
Found a solution! I added
if (parameter.getAnnotation(Hello.class) == null) return null;
and
if (parameter.getAnnotation(SmallTalk.class) == null) return null;
to the createValueFactory method of the two value factory providers.
I am developing a small code generator using JDK 6's Annotation Processing API and am stuck trying to get the actual generic type of a field in the class. To be clearer, let's say I have a class like this:
#MyAnnotation
public class User {
private String id;
private String username;
private String password;
private Set<Role> roles = new HashSet<Role>();
private UserProfile profile;
}
and here is my annotation processor class:
#SupportedAnnotationTypes({ "xxx.MyAnnotation" })
#SupportedSourceVersion(SourceVersion.RELEASE_6)
public class MongoDocumentAnnotationProcessor extends AbstractProcessor {
private Types typeUtils = null;
private Elements elementUtils = null;
#Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
typeUtils = processingEnv.getTypeUtils();
elementUtils = processingEnv.getElementUtils();
}
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
debug("Running " + getClass().getSimpleName());
if (roundEnv.processingOver() || annotations.size() == 0) {
return false;
}
for (Element element : roundEnv.getRootElements()) {
if (element.getKind() == ElementKind.CLASS && isAnnotatedWithMongoDocument(element)) {
for (VariableElement variableElement : ElementFilter.fieldsIn(element.getEnclosedElements())) {
String fieldName = variableElement.getSimpleName().toString();
Element innerElement = typeUtils.asElement(variableElement.asType());
String fieldClass = "";
if (innerElement == null) { // Primitive type
PrimitiveType primitiveType = (PrimitiveType) variableElement.asType();
fieldClass = typeUtils.boxedClass(primitiveType).getQualifiedName().toString();
} else {
if (innerElement instanceof TypeElement) {
TypeElement typeElement = (TypeElement) innerElement;
fieldClass = typeElement.getQualifiedName().toString();
TypeElement collectionType = elementUtils.getTypeElement("java.util.Collection");
if (typeUtils.isAssignable(typeElement.asType(), collectionType.asType())) {
TypeVariable typeMirror = (TypeVariable)((DeclaredType)typeElement.asType()).getTypeArguments().get(0);
TypeParameterElement typeParameterElement = (TypeParameterElement) typeUtils.asElement(typeMirror);
// I am stuck here. I don't know how to get the
// full qualified class name of the generic type of
// property 'roles' when the code processes the User
// class as above. What I want to retrieve is the
// 'my.package.Role' value
}
}
}
}
}
}
return false;
}
private boolean isAnnotated(Element element) {
List<? extends AnnotationMirror> annotationMirrors = element.getAnnotationMirrors();
if (annotationMirrors == null || annotationMirrors.size() == 0) return false;
for (AnnotationMirror annotationMirror : annotationMirrors) {
String qualifiedName = ((TypeElement)annotationMirror.getAnnotationType().asElement()).getQualifiedName().toString();
if ("xxx.MyAnnotation".equals(qualifiedName)) return true;
}
return false;
}
}
Any hint would be really appreciated!
Copy-paste of my original answer:
This seems to be a common question so, for those arriving from Google: there is hope.
The Dagger DI project is licensed under the Apache 2.0 License and contains some utility methods for working with types in an annotation processor.
In particular, the Util class can be viewed in full on GitHub (Util.java) and defines a method public static String typeToString(TypeMirror type). It uses a TypeVisitor and some recursive calls to build up a string representation of a type. Here is a snippet for reference:
public static void typeToString(final TypeMirror type, final StringBuilder result, final char innerClassSeparator)
{
type.accept(new SimpleTypeVisitor6<Void, Void>()
{
#Override
public Void visitDeclared(DeclaredType declaredType, Void v)
{
TypeElement typeElement = (TypeElement) declaredType.asElement();
rawTypeToString(result, typeElement, innerClassSeparator);
List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
if (!typeArguments.isEmpty())
{
result.append("<");
for (int i = 0; i < typeArguments.size(); i++)
{
if (i != 0)
{
result.append(", ");
}
// NOTE: Recursively resolve the types
typeToString(typeArguments.get(i), result, innerClassSeparator);
}
result.append(">");
}
return null;
}
#Override
public Void visitPrimitive(PrimitiveType primitiveType, Void v) { ... }
#Override
public Void visitArray(ArrayType arrayType, Void v) { ... }
#Override
public Void visitTypeVariable(TypeVariable typeVariable, Void v)
{
result.append(typeVariable.asElement().getSimpleName());
return null;
}
#Override
public Void visitError(ErrorType errorType, Void v) { ... }
#Override
protected Void defaultAction(TypeMirror typeMirror, Void v) { ... }
}, null);
}
I am busy with my own project which generates class extensions. The Dagger method works for complex situations, including generic inner classes. I have the following results:
My test class with field to extend:
public class AnnotationTest
{
...
public static class A
{
#MyAnnotation
private Set<B<Integer>> _bs;
}
public static class B<T>
{
private T _value;
}
}
Calling the Dagger method on the Element the processor provides for the _bs field:
accessor.type = DaggerUtils.typeToString(element.asType());
The generated source (custom, of course). Note the awesome nested generic types.
public java.util.Set<AnnotationTest.B<java.lang.Integer>> AnnotationTest.A.getBsGenerated()
{
return this._bs;
}
EDIT: adapting the concept to extract a TypeMirror of the first generic argument, null otherwise:
public static TypeMirror getGenericType(final TypeMirror type)
{
final TypeMirror[] result = { null };
type.accept(new SimpleTypeVisitor6<Void, Void>()
{
#Override
public Void visitDeclared(DeclaredType declaredType, Void v)
{
List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
if (!typeArguments.isEmpty())
{
result[0] = typeArguments.get(0);
}
return null;
}
#Override
public Void visitPrimitive(PrimitiveType primitiveType, Void v)
{
return null;
}
#Override
public Void visitArray(ArrayType arrayType, Void v)
{
return null;
}
#Override
public Void visitTypeVariable(TypeVariable typeVariable, Void v)
{
return null;
}
#Override
public Void visitError(ErrorType errorType, Void v)
{
return null;
}
#Override
protected Void defaultAction(TypeMirror typeMirror, Void v)
{
throw new UnsupportedOperationException();
}
}, null);
return result[0];
}
Looks like there are a couple of problems. One, the isAssignable() isnt working as expected. Second, in the above code you are trying to get the generic parameters of the Set type (T), rather than the variable declaration (Role).
Nevertheless, the following code should demonstrate what you need:
#SupportedAnnotationTypes({ "xxx.MyAnnotation" })
#SupportedSourceVersion(SourceVersion.RELEASE_6)
public class MongoDocumentAnnotationProcessor extends AbstractProcessor {
#Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
}
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver() || annotations.size() == 0) {
return false;
}
for (Element element : roundEnv.getRootElements()) {
if (element.getKind() == ElementKind.CLASS && isAnnotatedWithMongoDocument(element)) {
System.out.println("Running " + getClass().getSimpleName());
for (VariableElement variableElement : ElementFilter.fieldsIn(element.getEnclosedElements())) {
if(variableElement.asType() instanceof DeclaredType){
DeclaredType declaredType = (DeclaredType) variableElement.asType();
for (TypeMirror typeMirror : declaredType.getTypeArguments()) {
System.out.println(typeMirror.toString());
}
}
}
}
}
return true; //processed
}
private boolean isAnnotatedWithMongoDocument(Element element) {
return element.getAnnotation(MyAnnotation.class) != null;
}
}
This code should output:
xxx.Role
All the other answers, while having lots of good points. Don't really show you the problem you have and it's solution.
The problem in your code is here
TypeElement collectionType = elementUtils.getTypeElement("java.util.Collection");
if (typeUtils.isAssignable(typeElement.asType(), collectionType.asType())) {
...
Your type is not extending java.util.Collection but rather java.util.Collection<*>. Let's rewrite the above block to reflect this:
WildcardType WILDCARD_TYPE_NULL = this.typeUtils.getWildcardType(null, null);
final TypeElement collectionTypeElement = this.elementUtils.getTypeElement(Collection.class.getName());
TypeMirror[] typex = {WILDCARD_TYPE_NULL};
DeclaredType collectionType=this.typeUtils.getDeclaredType(collectionTypeElement, typex);
if (typeUtils.isAssignable(typeElement.asType(), collectionType)){
...
That should make it work
Using Java 11 you can cast your TypeMirror to Type.ClassType
This code
// classToIntrospect is a TypeMirror of java.util.List<it.firegloves.sragen.Dog>
(ClassType)classToIntrospect
will be evaluated in
I have a lot of classes UNO,HAV,MAS,KOS
I want to create a factory pattern.
validator.load("UNO").validate();
I need dynamically load classes into validator class and return an instance.
(dynamically set name of the class and return an instance)
My problem is: how can I return the instance of a class, if I have incompatible types?
I don't know what to write in return type of method.
The main problem in the Validator CLASS.
public SegmentAbstract load(String str) {
AND
return SegmentAbsClass.forName(identify);
Main class
try{
validator.load("UNO").validate();
}catch(Exception e){
System.out.print("No class ");
}
Abstract Class (SegmentAbstract)
public abstract class SegmentAbstract {
public abstract Boolean validate();
}
Class UNO
public class UNA extends SegmentAbstract{
public Boolean validate() {
System.out.print("UNO!!");
return true;
}
}
Class Validator
public class Validator {
public SegmentAbstract load(String str) {
String identify = str.substring(0, 3);
try {
return SegmentAbsClass.forName(identify);
}
catch(Exception e) {
return this;
}
}
}
Try this :
public interface Validator {
boolean validate(Object obj);
}
public final class ValidatorFactory {
private ValidatorFactory(){}
public static Validator load(String type){
try {
Class<?> clazz = Class.forName(type);
if (Arrays.asList(clazz.getInterfaces()).contains(Validator.class)){
return (Validator) clazz.newInstance();
}
throw new IllegalArgumentException("Provided class doesn't implement Validator interface");
} catch (Exception e) {
throw new IllegalArgumentException("Wrong class provided", e);
}
}
}
Maybe this will help???
I will do something like that:
// ISegment.java
public interface ISegment {
Boolean validate();
}
// Uno.java
public class Uno implements ISegment {
public Boolean validate() {
System.out.print("UNO!!");
return true;
}
}
// SegmentFactory.java
public final class SegmentFactory {
public static enum Supported {
UNO("uno", Uno.class), /* ... */, HAV("hav", Hav.class);
private final Class<?> clazz;
private final String name;
private Supported(final String name, final Class<?> clazz) {
this.name = name;
this.clazz = clazz;
}
public Class<?> getClazz() {
return clazz;
}
public static Supported for(final String name) {
for (final Supported s : values()) {
if (s.name.equals(name) {
return s;
}
}
return null; // a default one
}
}
public static ISegment create(final Supported supp) {
if (supp == null) {
return null;
}
return supp.getClazz.newInstance();
}
private SegmentFactory() {
// avoid instantiation
}
}
usage:
final ISegment sa = SegmentFactory.create(SegmentFactory.Supported.for("uno"));
sa.validate();
Not tested!!
Take a look here. Briefly, the idea is to create a map in your factory class (Map<String,String>, key is identifier, value is fully qualified class name), and add supported classes during initialization. Then you use reflection to instantiate an object in your factory method. Also, you can avoid reflection by using Map<String, SegmentAbstract> instead of Map<String,String> and adding public abstract getNewSegment() to your SegmentAbstract class.