All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.dooapp.gaedo.finders.root.InformerClassInvocationHandler Maven / Gradle / Ivy
package com.dooapp.gaedo.finders.root;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.logging.Logger;
import com.dooapp.gaedo.CrudServiceException;
import com.dooapp.gaedo.finders.FieldInformer;
import com.dooapp.gaedo.finders.FieldInformerAPI;
import com.dooapp.gaedo.finders.FieldProjector;
import com.dooapp.gaedo.finders.Informer;
import com.dooapp.gaedo.patterns.Proxied;
import com.dooapp.gaedo.utils.BasicInvocationHandler;
import com.dooapp.gaedo.utils.CallMethodResolver;
import com.dooapp.gaedo.utils.MethodResolver;
import com.dooapp.gaedo.utils.VirtualMethodCreationException;
/**
* This invocation handler will transform calls to synthetic informers getters into calls to the {@link #realInformer}'s {@link Informer#get(String)} method.
*
* @author Nicolas
*
*/
public class InformerClassInvocationHandler, ContainedType> extends BasicInvocationHandler
implements InvocationHandler, Proxied, Validable {
private static Logger logger = Logger
.getLogger(InformerClassInvocationHandler.class.getName());
static final String SYNTHETIC_GETTER_PREFIX = "get";
/**
* Utility method obtaining the field informer associated to a particular method name.
* @param
* @param method method
* @param methodName
* @param realInformer
* @return
*/
public static FieldInformer methodNameToFieldInformer(Method method,
Informer realInformer) {
String methodName = method.getName();
if(methodName.startsWith(SYNTHETIC_GETTER_PREFIX)) {
String fieldName = methodName.substring(SYNTHETIC_GETTER_PREFIX
.length());
if (fieldName.length() > 0) {
String realFieldName = fieldName.substring(0, 1).toLowerCase();
if (fieldName.length() > 1)
realFieldName += fieldName.substring(1);
try {
return realInformer.get(realFieldName);
} catch(NoSuchFieldInHierarchyException e) {
throw new MethodConstructedOnMissingField(method, realFieldName, e);
}
} else {
throw new EmptyGetDontWorkException(method);
}
} else {
throw new BadPrefixException(method);
}
}
/**
* Utility method obtaining the field informer associated to a particular method object (for better error text, use this if you can).
* @param
* @param method method
* @param methodName
* @param realInformer
* @return
*/
public static FieldInformer methodNameToFieldInformer(
String methodName, Informer realInformer) {
if(methodName.startsWith(SYNTHETIC_GETTER_PREFIX)) {
String fieldName = methodName.substring(SYNTHETIC_GETTER_PREFIX
.length());
if (fieldName.length() > 0) {
String realFieldName = fieldName.substring(0, 1).toLowerCase();
if (fieldName.length() > 1)
realFieldName += fieldName.substring(1);
return realInformer.get(realFieldName);
} else {
throw new EmptyGetDontWorkException(methodName);
}
} else {
throw new BadPrefixException(methodName);
}
}
/**
* Class of contained objects, to have reference fields (like previous
* field, I don't really know why it's here)
*/
protected final Class containedType;
/**
* This field is used when trying to project this informer as another class's field (you know, for some method chainings)
*/
protected final Class informerClass;
/**
* Real used informer, all calls will be directed to it
*/
protected final Informer realInformer;
/**
* This class used to eager load all methods, but this generates failures in case when a class refers to
* itself as member (see Tag test bean for an example of such a configuration).
* @param informerClass
* @param containedType
* @param informer
*/
public InformerClassInvocationHandler(Class informerClass,
Class containedType, Informer informer) {
super(informerClass);
this.informerClass = informerClass;
this.containedType = containedType;
this.realInformer = informer;
}
/**
* Create resolver by doing the initial job of code transformation
*/
@Override
protected MethodResolver createResolver(Method method) throws VirtualMethodCreationException {
Class> declaringClass = method.getDeclaringClass();
if(declaringClass.equals(FieldProjector.class)) {
// Oh my god ! The dread projector method
return new CallMethodResolver(method) {
@Override
protected Object callMethod(Method method, Object[] invokedArgs) throws Throwable{
return invokeProjectorMethod(method, invokedArgs);
}
};
} else if (declaringClass.equals(FieldInformerAPI.class)) {
return new CallMethodResolver(method) {
@Override
protected Object callMethod(Method method, Object[] invokedArgs) throws Throwable {
return invokeAPIMethod(method, invokedArgs);
}
};
} else if (declaringClass.equals(Validable.class)) {
return new CallMethodResolver(method) {
@Override
protected Object callMethod(Method method, Object[] invokedArgs) {
return invokeValidableMethod(method, invokedArgs);
}
};
} else if (declaringClass.equals(Proxied.class)) {
return new CallMethodResolver(method) {
@Override
protected Object callMethod(Method method, Object[] invokedArgs) {
return invokeProxiedMethod(method, invokedArgs);
}
};
} else if (declaringClass.isAssignableFrom(Informer.class)) {
return new CallMethodResolver(method) {
@Override
protected Object callMethod(Method method, Object[] invokedArgs) {
return invokeInformerMethod(method, invokedArgs);
}
};
} else {
return createSubInformerResolvers(method);
}
}
/**
* Invokes a method on this object, which IS validable
* @param invokedMethod
* @param invokedArgs
* @return
* @category invoke
*/
protected Object invokeValidableMethod(Method invokedMethod, Object[] invokedArgs) {
try {
return invokedMethod.invoke(this, invokedArgs);
} catch (IllegalArgumentException e) {
throw new InvocationFailedException(e);
} catch (IllegalAccessException e) {
throw new InvocationFailedException(e);
} catch (InvocationTargetException e) {
if(e.getTargetException() instanceof CrudServiceException)
throw (CrudServiceException) e.getTargetException();
throw new InvocationFailedException(e);
}
}
/**
* Create method for sub-informer class declarations. Those are the "magical" ones which bind field names in method name to
* Informers.
* @see #methodNameToFieldInformer(Method, Informer)
* @param method input method
* @return a method resolving call to an informer
*/
private MethodResolver createSubInformerResolvers(Method method) {
// First thing to check is method startup
FieldInformer returned = methodNameToFieldInformer(method, realInformer);
Class> returnType = method.getReturnType();
if (!returnType.isAssignableFrom(returned.getClass())) {
throw new ReturnTypeMismatchException(method,
returned.getClass(), returnType);
}
return new SubInformerMethodResolver(returned, realInformer);
}
public Class getContainedType() {
return containedType;
}
public Class getInformerClass() {
return informerClass;
}
public Informer getRealInformer() {
return realInformer;
}
/**
* Invoke an informer method on {@link #realInformer}
* @category invoke
* @param invokedMethod
* @param invokedArgs
* @return
*/
private Object invokeInformerMethod(Method invokedMethod,
Object[] invokedArgs) {
try {
return invokedMethod.invoke(realInformer, invokedArgs);
} catch (Exception e) {
throw new InvocationFailedException(e);
}
}
/**
* Invoked when user calls a method from the FieldInformerAPI interface on that proxy. It allows us to mimic some behaviours
* (typically the {@link FieldInformerAPI#with(Collection)} method will be abused)
* @param method
* @param invokedArgs
* @return
* @category invoke
* @throws Throwable
*/
protected Object invokeAPIMethod(Method method, Object[] invokedArgs) throws Throwable {
return invokeProjectorMethod(method, invokedArgs);
}
/**
* A new proxy is created, proxying the object returned by {@link Informer#asField(java.lang.reflect.Field)} with the same interface than the source {@link #realInformer}
* @param invokedMethod
* @param invokedArgs
* @return
* @category invoke
*/
private Object invokeProjectorMethod(Method invokedMethod,
Object[] invokedArgs) throws Throwable {
Informer asField = (Informer) invokedMethod.invoke(realInformer, invokedArgs);
return Proxy.newProxyInstance(informerClass.getClassLoader(), new Class>[] {informerClass},
new InformerClassInvocationHandler(informerClass, containedType, asField));
}
/**
* Invoke a method from the proxied interface. In fact, the method is invoked on this object
* @param invokedMethod invoked method
* @param invokedArgs invoked args
* @return
* @category invoke
*/
private Object invokeProxiedMethod(Method invokedMethod,
Object[] invokedArgs) {
try {
return invokedMethod.invoke(this, invokedArgs);
} catch (Exception e) {
throw new InvocationFailedException(e);
}
}
@Override
public String toString() {
StringBuilder sOut = new StringBuilder();
sOut.append(getClass().getName()).append("(");
sOut.append("informerClass:").append(getInformerClass().getName());
sOut.append("; containedType:").append(getContainedType().toString());
sOut.append("; realInformer:").append(getRealInformer().getClass().getName());
sOut.append(")");
return sOut.toString();
}
/**
* Upon validation, all method resolvers are created, which may fail noisily
*/
@Override
public void validate() {
createAllMethodResolvers(informerClass);
}
}