Proxy in Java with a dynamic interface - java

I'm trying to port some C# code to Java and I'm having trouble with a proxy implementation.
I have this class:
public class Comic
{
public int ComicID;
private List<Volume> _volumes;
public List<Volume> GetVolumes()
{
return _volumes;
}
public void SetVolumes(List<Volume> volumes)
{
_volumes = volumes;
}
}
And I'm trying to add an interceptor to a specific Method call for this entity, but I also need access to it's fields since I'll be using them.
After looking around for how to implement proxies in Java, I got this:
public void Load(Class<?> type)
{
// - type is a Comic.class
ClassLoader appLoader = this.getClass().getClassLoader();
MyProxyHandler proxyHandler = new MyProxyHandler();
Object proxy = Proxy.newProxyInstance(appLoader, new Class[] { ComicInterface.class }, proxyHandler);
}
The problem is, the type is dynamic so I don't know which will be, and I don't want the code to have a requirement of having interfaces of everything, so I looked up on how to build a dynamic interface:
public class InterfaceLoader extends ClassLoader
{
public InterfaceLoader(ClassLoader loader)
{
super(loader);
}
public Class<?> GetInterface(Class<?> type) throws Exception
{
String interfaceName = type.getName() + "$Proxy";
Class<?> interfaceType = null;
interfaceType = findLoadedClass(interfaceName);
if (interfaceType != null) { return interfaceType; }
// - According to the link
byte[] classData = new InterfaceBuilder().BuildInterface(interfaceName, type.getDeclaredMethods());
return defineClass(interfaceName, classBytes, 0, classBytes.length);
}
}
And then I'd use it with the Proxy:
public void Load(Class<?> type)
{
// - type is a Comic.class
ClassLoader appLoader = this.getClass().getClassLoader();
InterfaceLoader loader = new InterfaceLoader(appLoader);
Class<?> dynamicInterface = loader.GetInterface(type);
// - dynamicInterface on debug is "console.Comic$Proxy", seems fine
MyProxyHandler proxyHandler = new MyProxyHandler();
Object proxy = Proxy.newProxyInstance(appLoader, new Class[] { dynamicInterface }, proxyHandler);
}
The exception I get is
java.lang.IllegalArgumentException: interface console.Comic$Proxy is not visible from class loader
I've looked up the exception but only found two solutions, which is to make sure the name doesn't conflict with any type (and I'm sure it doesn't) and to use this.getClass().getClassLoader() instead of type.getClass().getClassLoader() (same error).
What am I doing wrong?
And another issue is, how can I get the "original" object so I can get/set values from fields like ComicID? Using the above proxy method I can intercept methods fine, but I lose access to it's fields.
I've read about it here to get the InvocationHandler, but I don't know how to get the object from the handler and I couldn't find an example with it.

There is only one classLoader that can see your dynamically generated interface, and that is the InterfaceLoader loader. You can probably make the error go away by passing that to newProxyInstance.
But, I'm pretty sure this won't do you any good. None of the java code you are writing is loaded by that class loader, so you won't be able to call through that interface except by reflection.
If you want to use a proxy, then you'll have to make Comic implement an interface, and then use that interface everywhere. In that case, the answer to your next question is that you would pass an instance of the original object to your proxy handler constructor.
If you really want to do this dynamic interception thing, you probably want to look at using ASM (http://asm.ow2.org/) or Javaassist to build a dynamic subclass of Comic that overrides the method you want to intercept, instead of using a proxy.

Related

How efficiently create Java proxy object using CGLib (or any other reflection/byte-code library)

Objective: (as the question title says): to efficiently create proxy (domain) objects using CGLib or any other reflection library (e.g. ByetBuddy?)
Having our domain class (please note Lombok annotations):
#Getter
#Setter
#RequiredArgsConstructor
public class DomainFoo {
#NonNull
private final Integer id;
// some other fields, final or otherwise!
public void doSomething() {
// do something here!
}
}
I'm trying to create a proxy object of type DomainFoo which responds only to getId method with the given ID (see below), otherwise (calling any other method) it throws an UnsupportedOperationException.
I managed to do this using GCLib (Spring version if it matters):
public static final ObjenesisStd OBJENESIS = new ObjenesisStd();
public static Factory newFoo(Integer id) {
val enhancer = new Enhancer();
enhancer.setSuperclass(domainClass);
enhancer.setCallbackType(MethodInterceptor.class);
val proxyClass = enhancer.createClass();
// Since there's no default constructor in domains:
val instantiator = OBJENESIS.getInstantiatorOf(proxyClass);
val proxyInstance = instantiator.newInstance();
val factory = (Factory) proxyInstance;
factory.setCallbacks(new Callback[]{new DomainProxyInterceptor(id)});
return factory; // see below for why it's called factory!
}
where DomainProxyInterceptor is:
#RequiredArgsConstructor
private class DomainProxyInterceptor implements MethodInterceptor {
#NonNull
private final Integer id;
#Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.getDeclaringClass() != Object.class && method.getName().equals("getId")) {
return id;
} else {
throw new NotSupportedException(method.getName());
}
}
}
Everything works just fine. Now to make it efficient, I'm trying to cache factory and actually use it a factory:
// newFooFactory is the above method rename!
private static final Factor FOO_FACTORY = newFooFactory(0);
public static DomainFoo newFoo(Integer id) {
val callbacks = new Callback[]{new DomainProxyInterceptor(id)};
return (DomainFoo) FOO_FACTORY.newInstance(callbacks);
}
But calling the above method throws a NoSuchMethodError:
Exception in thread "main" java.lang.NoSuchMethodError: com/example/demo/DomainFoo$$EnhancerByCGLIB$$8423c3c4.<init>()V (loaded from file:/Users/rad/works/demo/target/classes/ by jdk.internal.loader.ClassLoaders$AppClassLoader#85a856f6) called from class com.example.demo.DomainFoo$$EnhancerByCGLIB$$8423c3c4 (loaded from file:/Users/rad/works/demo/target/classes/ by jdk.internal.loader.ClassLoaders$AppClassLoader#85a856f6).
at com.example.demo.DomainFoo$$EnhancerByCGLIB$$8423c3c4.newInstance(<generated>)
at com.example.demo.DomainFactory.proxyFoo(DomainFactory.java:28)
at com.example.demo.DemoApplication.main(DemoApplication.java:6)
Which presumably is because Factory.newInstance tries to use the normal constructor but it's not there (because we created the object using Objenesis?).
I also tried ReflectionFactory to create the factory instance, but it has the same issue.
Questions (they're kinda related):
Do I need to worry about efficiency at all? (Those objects are normal domains, so there's usually too many of them.)
Is there a better way to do the above? In terms of efficiency or otherwise? Specifically what's the right way to instantiate using GCLib when there's no default constructor?
Is there a way to fix the above issue? Resolving NoSuchMethodError failure?
Any other library or solution I could try?
Thanks.
Cglib and ByteBuddy are 2 good libraries to create a proxy. Objenesis will do something more on top. It will prevent calling a constructor of the proxied class. You can look at org.easymock.internal.ClassProxyFactory or use it to create such a proxy. It is quite similar to your code.

How to explicitly invoke default method from a dynamic Proxy?

Since Java 8 interfaces could have default methods.
I know how to invoke the method explicitly from the implementing method, i.e.
(see Explicitly calling a default method in Java)
But how do I explicitly invoke the default method using reflection for example on a proxy?
Example:
interface ExampleMixin {
String getText();
default void printInfo(){
System.out.println(getText());
}
}
class Example {
public static void main(String... args) throws Exception {
Object target = new Object();
Map<String, BiFunction<Object, Object[], Object>> behavior = new HashMap<>();
ExampleMixin dynamic =
(ExampleMixin) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class[]{ExampleMixin.class}, (Object proxy, Method method, Object[] arguments) -> {
//custom mixin behavior
if(behavior.containsKey(method.getName())) {
return behavior.get(method.getName()).apply(target, arguments);
//default mixin behavior
} else if (method.isDefault()) {
//this block throws java.lang.IllegalAccessException: no private access for invokespecial
return MethodHandles.lookup()
.in(method.getDeclaringClass())
.unreflectSpecial(method, method.getDeclaringClass())
.bindTo(target)
.invokeWithArguments();
//no mixin behavior
} else if (ExampleMixin.class == method.getDeclaringClass()) {
throw new UnsupportedOperationException(method.getName() + " is not supported");
//base class behavior
} else{
return method.invoke(target, arguments);
}
});
//define behavior for abstract method getText()
behavior.put("getText", (o, a) -> o.toString() + " myText");
System.out.println(dynamic.getClass());
System.out.println(dynamic.toString());
System.out.println(dynamic.getText());
//print info should by default implementation
dynamic.printInfo();
}
}
Edit: I know a similar question has been asked in How do I invoke Java 8 default methods refletively, but this has not solved my problem for two reasons:
the problem described in that question aimed on how to invoked it via reflection in general - so no distinction between default and overriden method was made - and this is simple, you only need an instance.
one of the answers - using method handles - does only work with nasty hack (imho) like changing access modifiers to fields of the lookup class, which is the same category of "solutions" like this: Change private static final field using Java reflection: it's good to know it's possible, but I wouldn't use it in production - I'm looking for an "official" way to do it.
The IllegalAccessException is thrown in unreflectSpecial
Caused by: java.lang.IllegalAccessException: no private access for invokespecial: interface example.ExampleMixin, from example.ExampleMixin/package
at java.lang.invoke.MemberName.makeAccessException(MemberName.java:852)
at java.lang.invoke.MethodHandles$Lookup.checkSpecialCaller(MethodHandles.java:1568)
at java.lang.invoke.MethodHandles$Lookup.unreflectSpecial(MethodHandles.java:1227)
at example.Example.lambda$main$0(Example.java:30)
at example.Example$$Lambda$1/1342443276.invoke(Unknown Source)
I've been troubled by similar issues as well when using MethodHandle.Lookup in JDK 8 - 10, which behave differently. I've blogged about the correct solution here in detail.
This approach works in Java 8
In Java 8, the ideal approach uses a hack that accesses a package-private constructor from Lookup:
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Constructor;
import java.lang.reflect.Proxy;
interface Duck {
default void quack() {
System.out.println("Quack");
}
}
public class ProxyDemo {
public static void main(String[] a) {
Duck duck = (Duck) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[] { Duck.class },
(proxy, method, args) -> {
Constructor<Lookup> constructor = Lookup.class
.getDeclaredConstructor(Class.class);
constructor.setAccessible(true);
constructor.newInstance(Duck.class)
.in(Duck.class)
.unreflectSpecial(method, Duck.class)
.bindTo(proxy)
.invokeWithArguments(args);
return null;
}
);
duck.quack();
}
}
This is the only approach that works with both private-accessible and private-inaccessible interfaces. However, the above approach does illegal reflective access to JDK internals, which will no longer work in a future JDK version, or if --illegal-access=deny is specified on the JVM.
This approach works on Java 9 and 10, but not 8
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Proxy;
interface Duck {
default void quack() {
System.out.println("Quack");
}
}
public class ProxyDemo {
public static void main(String[] a) {
Duck duck = (Duck) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[] { Duck.class },
(proxy, method, args) -> {
MethodHandles.lookup()
.findSpecial(
Duck.class,
"quack",
MethodType.methodType(void.class, new Class[0]),
Duck.class)
.bindTo(proxy)
.invokeWithArguments(args);
return null;
}
);
duck.quack();
}
}
Solution
Simply implement both of the above solutions and check if your code is running on JDK 8 or on a later JDK and you'll be fine. Until you're not :)
If you use a concrete impl class as lookupClass and caller for the invokeSpecial it should correctly invoke the default implementation of the interface (no hack for private access needed):
Example target = new Example();
...
Class targetClass = target.getClass();
return MethodHandles.lookup()
.in(targetClass)
.unreflectSpecial(method, targetClass)
.bindTo(target)
.invokeWithArguments();
This of course only works if you have a reference to a concrete object implementing the interface.
Edit: this solution will only work if the class in question (Example in the code above), is private accessible from the caller code, e.g. an anonymous inner class.
The current implementation of the MethodHandles/Lookup class will not allow to call invokeSpecial on any class that is not private accessible from the current caller class. There are various work-arounds available, but all of them require the use of reflection to make constructors/methods accessible, which will probably fail in case a SecurityManager is installed.
In Java 16 (from the documentation, which also has more complex examples):
Object proxy = Proxy.newProxyInstance(loader, new Class[] { A.class },
(o, m, params) -> {
if (m.isDefault()) {
// if it's a default method, invoke it
return InvocationHandler.invokeDefault(o, m, params);
}
});
}
If all you have is an interface, and all you have access to is a class object is an interface that extends your base interface, and you want to call the default method without a real instance of a class that implements the interface, you can:
Object target = Proxy.newProxyInstance(classLoader,
new Class[]{exampleInterface}, (Object p, Method m, Object[] a) -> null);
Create an instance of the interface, and then construct the MethodHandles.Lookup using reflection:
Constructor<MethodHandles.Lookup> lookupConstructor =
MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, Integer.TYPE);
if (!lookupConstructor.isAccessible()) {
lookupConstructor.setAccessible(true);
}
And then use that lookupConstructor to create a new instance of your interface that will allow private access to invokespecial. Then invoke the method on the fake proxy target you made earlier.
lookupConstructor.newInstance(exampleInterface,
MethodHandles.Lookup.PRIVATE)
.unreflectSpecial(method, declaringClass)
.bindTo(target)
.invokeWithArguments(args);
Use:
Object result = MethodHandles.lookup()
.in(method.getDeclaringClass())
.unreflectSpecial(method, method.getDeclaringClass())
.bindTo(target)
.invokeWithArguments();
We can see how spring process default method.
try invoke public method MethodHandles.privateLookupIn(Class,Lookup) first. This should success on jdk9+.
try create a Lookup with package private constructor MethodHandles.Lookup(Class).
fallback to MethodHandles.lookup().findSpecial(...)
https://github.com/spring-projects/spring-data-commons/blob/2.1.8.RELEASE/src/main/java/org/springframework/data/projection/DefaultMethodInvokingMethodInterceptor.java
T. Neidhart answer almost worked but I got the java.lang.IllegalAccessException: no private access for invokespecial
Changing to use MethodHandles.privateLookup() solved it
return MethodHandles.privateLookupIn(clazz,MethodHandles.lookup())
.in(clazz)
.unreflectSpecial(method, clazz)
.bindTo(proxy)
.invokeWithArguments(args);
Here's a full example, the idea is that a user that extends a provided IMap can access nested nested map's with he's custom interface
interface IMap {
Object get(String key);
default <T> T getAsAny(String key){
return (T)get(key);
}
default <T extends IMap> T getNestedAs(String key, Class<T> clazz) {
Map<String,Object> nested = getAsAny(key);
return (T)Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{clazz}, (proxy, method, args) -> {
if (method.getName().equals("get")){
return nested.get(args[0]);
}
return MethodHandles.privateLookupIn(clazz, MethodHandles.lookup())
.in(clazz)
.unreflectSpecial(method, clazz)
.bindTo(proxy)
.invokeWithArguments(args);
}
);
}
}
interface IMyMap extends IMap{
default Integer getAsInt(String key){
return getAsAny(key);
}
default IMyMap getNested(String key){
return getNestedAs(key,IMyMap.class);
}
}
#Test
public void test(){
var data =Map.of("strKey","strValue", "nstKey", Map.of("intKey",42));
IMyMap base = data::get;
IMyMap myMap = base.getNested("nstKey");
System.out.println( myMap.getAsInt("intKey"));
}
Lukas' answer works on Android 8+ (earlier releases do not have default methods) but relies on a private API that was blocked in later Android releases. Fortunately, the alternative constructor also works and is in grey list (unsupported) for now. The example (written in Kotlin) can be seen here.
#get:RequiresApi(26)
private val newLookup by lazy #TargetApi(26) {
MethodHandles.Lookup::class.java.getDeclaredConstructor(Class::class.java, Int::class.java).apply {
isAccessible = true
}
}
#RequiresApi(26)
fun InvocationHandler.invokeDefault(proxy: Any, method: Method, vararg args: Any?) =
newLookup.newInstance(method.declaringClass, 0xf) // ALL_MODES
.unreflectSpecial(method, method.declaringClass)
.bindTo(proxy)
.invokeWithArguments(*args)

How to implement a wrapper decorator in Java?

The problem is to create a dynamic enhanced version of existing objects.
I cannot modify the object's Class. Instead I have to:
subclass it
wrap the existing object in the new Class
delegate all the original method calls to the wrapped object
implement all methods that are defined by another interface
The interface to add to existing objects is:
public interface EnhancedNode {
Node getNode();
void setNode(Node node);
Set getRules();
void setRules(Set rules);
Map getGroups();
void setGroups(Map groups);
}
With Byte Buddy I managed to subclass and to implement my interface. The problem is the delegation to the wrapped object. The only way to do this that I found is using reflection what is too slow (I have heavy load on the application and performance is critical).
So far my code is:
Class<? extends Node> proxyType = new ByteBuddy()
.subclass(node.getClass(), ConstructorStrategy.Default.IMITATE_SUPER_TYPE_PUBLIC)
.method(anyOf(finalNode.getClass().getMethods())).intercept(MethodDelegation.to(NodeInterceptor.class))
.defineField("node", Node.class, Visibility.PRIVATE)
.implement(EnhancedNode.class).intercept(FieldAccessor.ofBeanProperty())
.defineField("groups", Map.class, Visibility.PRIVATE)
.implement(EnhancedNode.class).intercept(FieldAccessor.ofBeanProperty())
.defineField("rules", Set.class, Visibility.PRIVATE)
.implement(EnhancedNode.class).intercept(FieldAccessor.ofBeanProperty())
.make()
.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
enhancedClass = (Class<N>) proxyType;
EnhancedNode enhancedNode = (EnhancedNode) enhancedClass.newInstance();
enhancedNode.setNode(node);
where Node is the object to subclass/wrap. The NodeInterceptor forwards the invoked methods to the getNode property.
Here the code of the NodeInterceptor:
public class NodeInterceptor {
#RuntimeType
public static Object intercept(#Origin Method method,
#This EnhancedNode proxy,
#AllArguments Object[] arguments)
throws Exception {
Node node = proxy.getNode();
Object res;
if (node != null) {
res = method.invoke(method.getDeclaringClass().cast(node), arguments);
} else {
res = null;
}
return res;
}
}
Everything is working but the intercept method is too slow, I'm planning to use ASM directly to add the implementation of every method of Node but I hope there is a simpler way using Byte Buddy.
You probably want to use a Pipe rather than the reflection API:
public class NodeInterceptor {
#RuntimeType
public static Object intercept(#Pipe Function<Node, Object> pipe,
#FieldValue("node") Node proxy) throws Exception {
return proxy != null
? pipe.apply(proxy);
: null;
}
}
In order to use a pipe, you first need to install it. If you have Java 8 available, you can use java.util.Function. Otherwise, simply define some type:
interface Function<T, S> { S apply(T t); }
yourself. The name of the type and the method are irrelevant. The install the type:
MethodDelegation.to(NodeInterceptor.class)
.appendParameterBinder(Pipe.Binder.install(Function.class));
Are you however sure that the reflection part is the critical point of your application's performance problems? Are you caching the generated classes correctly and is your cache working efficiently? The reflection API is faster than its reputation, especially since use Byte Buddy tends to imply monomorphic call sites.
Finally, some general feedback. You are calling
.implement(EnhancedNode.class).intercept(FieldAccessor.ofBeanProperty())
multiple times. This has no effect. Also, method.getDeclaringClass().cast(node) is not necessary. The reflection API does the cast for you.

Add field to Proxy class created with Javassist

I am creating a Proxy class using Javassist ProxyFactory with the following code:
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(entity.getClass());
factory.setInterfaces(new Class[] { MyCustomInterface.class });
.....
Class clazz = factory.createClass();
Object result = clazz.newInstance();
The problem is that I also need to add a field to the class. But if I do CtClass proxy = ClassPool.getDefault().get(clazz.getName()); it gaves a NotFoundException
How can I add a field the class created with createClass? Is there a better way to do what I am trying to do?
This is based in your reply to my comment.
You can indeed use MyCustomInterface and your proxyClass to create sort of a mixin in Java. But you'll still have to cast from proxy class to MyCustomInterface to be able to call the methods.
Let's get started.
Creating your proxy
First you create your proxy has you already were doing:
// this is the code you've already posted
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(entity.getClass());
factory.setInterfaces(new Class[] { MyCustomInterface.class });
Method handler: Doing the magic
Javassist proxies allow you to add a MethodHandler. It basically acts has a an InvocationHandler would in a regular Java Proxy, that means it works as a method interceptor.
The method handler will be your mixin! First you create a new MethodHandler with the custom field you actually want to add to the class, along with the entity object you've started proxying:
public class CustomMethodHandler implements MethodHandler {
private MyEntity objectBeingProxied;
private MyFieldType myCustomField;
public CustomMethodHandler(MyEntity entity) {
this.objectBeingProxied = entity;
}
// code here with the implementation of MyCustomInterface
// handling the entity and your customField
public Object invoke(Object self, Method method, Method proceed, Object[] args) throws Throwable {
String methodName = method.getName();
if(methodNameFromMyCustomInterface(methodName)) {
// handle methodCall internally:
// you can either do it by reflection
// or if needed if/then/else to dispatch
// to the correct method (*)
}else {
// it's just a method from entity let them
// go. Notice we're using proceed not method!
proceed.invoke(objectBeingProxied,args);
}
}
}
(*) Notice that even if I say in the comment to handle the call internally you can have the interface implementation in another place that isn't your method handler and just call it from here.
Getting everything together
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(entity.getClass());
factory.setInterfaces(new Class[] { MyCustomInterface.class });
Class cls = factory.createClass();
// bind your newly methodHandler to your proxy
((javassist.util.proxy.Proxy) cls).setHandler(new CustomMethodHandler(entity));
EntityClass proxyEntity = cls.newInstance();
You should now be able to do ((MyCustomInterface)proxyEntity).someMethodFromTheInterface() and let it be handled by your CustomMethodHandler
Summing up
You create a proxy using Proxy Factory from javassist
You create your own MethodHandler class that can receive your proxied entity and the field you want to operate
You bind the methodHandler to your proxy so you can delegate interface implementation
Keep in mind that these approach isn't perfect, being one of the short comings the code in Entity class cannot refer to the interface unless you first create the proxy.
If anything wasn't very clear for you, just comment and I'll do my best to clarify you.

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?

Categories