Binding an interface with Guice to a Proxy that implements it - java

I have an interface class reference and an object I created using a dynamic proxy that implements the interface above.
final Class<?> interfaceType;
final Object interfaceImplementation = Proxy.newProxyInstance(...)`
I want to make the proxy instance available in the Guice application context by binding the interface to the proxy object. I tried but failed to quickly achieve this with Guice.
Is it possible to do something like:
bind(interfaceType).toInstance(interfaceImplementation);
so that I can inject the interface into other classes?

The problem is that the return type of Proxy.newProxyInstance() is just Object, but bind(...).toInstance(...) wants to ensure that the type is the same as the ? from Class<?> interfaceType. At some point, you're going to need to do an unchecked cast to get everything working. Here's how I'd write it:
private <T> T newProxyInstance(ClassLoader loader, InvocationHandler handler, Class<T> primaryInterface, Class<?>... extraInterfaces) {
Class<?>[] allInterfaces = Lists.asList(primaryInterface, extraInterfaces)
.stream()
.toArray(Class<?>[]::new);
#SuppressWarning("unchecked")
T proxy = (T) Proxy.newProxyInstance(loader, allInterfaces, handler);
return proxy;
}
then I think
bind(interfaceType)
.toInstance(newProxyInstance(loader, handler, interfaceType[, ...]));
would work.

Related

How to extend a class with 2 generic Types and get Spring to Autowire instance

I have a class that extends 2 other class as seen below.
public class EJBHandler<T extends EJBObject, I extends EJBHome>
(The EJBObject and EJBHome comes from the javax.ejb artifact)
The idea behind this was so that I could just use Spring to specify the exact instance and Spring would autowire the instance for me like in the following example.
#Autowired
private EJBHandler<SystemUser, SystemUserHome> systemUserEjb;
#Autowired
private EJBHandler<Authentication, AuthenticationHome> authenticationEjb;
But when attempting the above I get the following error:
Expected 1 type argument on generic interface.remote.EJBHandler<?, ?> but found 2
I have searched for a solution but none of them seem to work. Among them that I thought had the best chance of solving the problem was the following method.
In the EJBHandler class have a constructor that would determine the class types using Spring's built-in GenericTypeResolver (as seen below). This solution builds but it seems like when actually used the systemUserEjb is null and never initialized.
protected Class<T> ejbObject;
protected Class<I> ejbHomeClass;
#SuppressWarnings("unchecked")
protected void EJBHandler() {
ejbObject = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), EJBHandler.class);
ejbHomeClass =(Class<I>) GenericTypeResolver.resolveTypeArgument(getClass(), EJBHandler.class);
if(useExternalDependencies) {
try {
Object homeObject = createHomeObject(ejbObject.getSimpleName(), "SomeServerHere");
this.homeObject = (I) PortableRemoteObject.narrow(homeObject, ejbHomeClass);
} catch (NamingException e) {
e.printStackTrace();
}
}
}
protected Object createHomeObject(String jndiName, DeployedServer server) throws NamingException {
Context ctx = new InitialContext(getProperties(server));
Object homeObject = ctx.lookup(jndiName);
log.debug("Found [{}] bean in context on server [{}].", jndiName, server);
return homeObject;
}
What I did then was to create a concrete class to extend the EJBHandler class and in the constructor just inject the class objects and it works then?
My Question is: why since in my head it should do the same thing without having to have a concrete class that does nothing except just tell the superclass the Type of T and I?

Proper way to cast object to generic type

I have an implementation of something like chain of responsibility patterns where common context has method
Object getVariable(String name)
When I get an object from the context I want to validate it with an appropriate validator. Validator interface:
public interface PageValidator<DTO> {
ValidationResult validate(DTO dto);
}
So, when I try to do validation
Object dtoForValidation = getDtoForValidation(delegateExecution);
ValidationResult validationResult = getPageValidator(delegateExecution).validate(dtoForValidation);
it fails with a compilation error
incompatible types: java.lang.Object cannot be converted to capture#1 of ?
So, I wanted to ask what is the proper way to design it?
(I don't really want to let validators accept Object as input argument as it looks ugly)
My suggestion is to change method getValiable(String name). You can pass Class<T> varClass to this method. And your signature will be something like that:
<T> T getVariable(String name, Class<T> varClass);
If this method is placed in 3rd party library, I would recommend to you to create some wrapper for this class.
class Wrapper {
private OriginalClass originalObject;
<T> T getVariable(String name, Class<T> varClass) {
return (T) originalObject.getVariable(name);
}
}
Off the top of my head, you can do one of the following.
1) Modify your getDtoForValidation method to return a DTO
2) Explicitly cast your object using ValidationResult validationResult = getPageValidator(delegateExecution).validate((DTO)dtoForValidation);
3) You mentioned you don't want your validators to accept Object as input argument, but you can do that and in the validator, start by checking Object type.

How to simply handle a factory of multiple parameter-diverging classes with Guice?

I have the following very simple interface:
public interface IDataSource<T> {
Observable<T> observable();
}
Now I'll be having multiple implementations of it. Each of those implementation may rely on varying parameters (different storage objects: JDBC, Cassandra, Redis...). So for instance I'll have:
public class CassandraDataSource implements IDataSource<MyCassandraObject> {
#Inject
public CassandraDataSource(Keyspace ks) {
// ...
}
}
public class OtherCassandraDataSource implements IDataSource<MyOtherCassandraObject> {
#Inject
public OtherCassandraDataSource(Keyspace ks) {
// ...
}
}
public class JDBCDataSource implements IDataSource<MyJdbcObject> {
#Inject
public JDBCDataSource(Database db) {
// ...
}
}
And so on.
What I would like is to reference each of those with a string so that i can call a factory which would return the appropriate IDataSource. Something that would behave like that:
public class DataSourceFactory {
public static final Map<String, Supplier<IDataSource<?>>> map = new HashMap<>();
#SuppressWarnings("unchecked")
public <T> IDataSource<T> get(String ref) {
return (IDataSource<T>) map.get(ref).get();
}
}
I could be giving N Providersas parameters (or direct field injection) to my factory (N being the number of IDataSource I use) and map strings to those in the constructor or use a switch in the get method but this would become quite a mess once I reach 10 to 20 IDataSource (or more!)
Is there some simpler solution I've been overlooking ?
Thanks
If you have to have a factory, then no—as in this question a manual factory is probably the best you can get.
However, if you bind all of your IDataSource implementations to IDataSource with different binding annotations, then you might be able to skip the factory and simply inject #Source("foo") IDataSource and get back the instance you want. The configuration between String and implementation still has to live somewhere, but you can have it live in a Guice module if you'd like. And, by injecting the Injector and supplying a compatible annotation implementation, you can even write a factory like DataSourceFactory that defers to Guice.
As a side note, beware of your DataSourceFactory; get will have no safe way to supply a value for its type parameter T.

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);
}
}

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.

Categories