private Callback createReverseEngineeredCallbackOfProperType( Callback callback, int index, Map callbackIndexMap) { Class iface = null; Class[] interfaces = callback.getClass().getInterfaces(); for (int i = 0; i < interfaces.length; i++) { if (Callback.class.isAssignableFrom(interfaces[i])) { iface = interfaces[i]; if (iface == Callback.class) { ConversionException exception = new ConversionException("Cannot handle CGLIB callback"); exception.add("CGLIB callback type", callback.getClass().getName()); throw exception; } interfaces = iface.getInterfaces(); if (Arrays.asList(interfaces).contains(Callback.class)) { break; } i = -1; } } return (Callback) Proxy.newProxyInstance( iface.getClassLoader(), new Class[] {iface}, new ReverseEngineeringInvocationHandler(index, callbackIndexMap)); }
public static <T> T proxy(Class<T> type, Callback callback) { callback = callback != null ? callback : BLOCKER; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(type); enhancer.setCallbackType(callback.getClass()); @SuppressWarnings("unchecked") Class<T> proxyClass = enhancer.createClass(); @SuppressWarnings("unchecked") T proxy = (T) ObjenesisHelper.newInstance(proxyClass); ((Factory) proxy).setCallback(0, callback); return proxy; }
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { Class type = source.getClass(); boolean hasFactory = Factory.class.isAssignableFrom(type); ExtendedHierarchicalStreamWriterHelper.startNode(writer, "type", type); context.convertAnother(type.getSuperclass()); writer.endNode(); writer.startNode("interfaces"); Class[] interfaces = type.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { if (interfaces[i] == Factory.class) { continue; } ExtendedHierarchicalStreamWriterHelper.startNode( writer, mapper.serializedClass(interfaces[i].getClass()), interfaces[i].getClass()); context.convertAnother(interfaces[i]); writer.endNode(); } writer.endNode(); writer.startNode("hasFactory"); writer.setValue(String.valueOf(hasFactory)); writer.endNode(); Map callbackIndexMap = null; Callback[] callbacks = hasFactory ? ((Factory) source).getCallbacks() : getCallbacks(source); if (callbacks.length > 1) { if (hasFactory) { callbackIndexMap = createCallbackIndexMap((Factory) source); } else { ConversionException exception = new ConversionException( "Cannot handle CGLIB enhanced proxies without factory that have multiple callbacks"); exception.add("proxy superclass", type.getSuperclass().getName()); exception.add("number of callbacks", String.valueOf(callbacks.length)); throw exception; } writer.startNode("callbacks"); writer.startNode("mapping"); context.convertAnother(callbackIndexMap); writer.endNode(); } boolean hasInterceptor = false; for (int i = 0; i < callbacks.length; i++) { final Callback callback = callbacks[i]; if (callback == null) { String name = mapper.serializedClass(null); writer.startNode(name); writer.endNode(); } else { hasInterceptor = hasInterceptor || MethodInterceptor.class.isAssignableFrom(callback.getClass()); ExtendedHierarchicalStreamWriterHelper.startNode( writer, mapper.serializedClass(callback.getClass()), callback.getClass()); context.convertAnother(callback); writer.endNode(); } } if (callbacks.length > 1) { writer.endNode(); } try { final Field field = type.getDeclaredField("serialVersionUID"); field.setAccessible(true); long serialVersionUID = field.getLong(null); ExtendedHierarchicalStreamWriterHelper.startNode(writer, "serialVersionUID", String.class); writer.setValue(String.valueOf(serialVersionUID)); writer.endNode(); } catch (NoSuchFieldException e) { // OK, ignore } catch (IllegalAccessException e) { throw new ObjectAccessException( "Access to serialVersionUID of " + type.getName() + " not allowed"); } if (hasInterceptor) { writer.startNode("instance"); super.doMarshalConditionally(source, writer, context); writer.endNode(); } }
private Map createCallbackIndexMap(Factory source) { Callback[] originalCallbacks = source.getCallbacks(); Callback[] reverseEngineeringCallbacks = new Callback[originalCallbacks.length]; Map callbackIndexMap = new HashMap(); int idxNoOp = -1; for (int i = 0; i < originalCallbacks.length; i++) { Callback callback = originalCallbacks[i]; if (callback == null) { reverseEngineeringCallbacks[i] = null; } else if (NoOp.class.isAssignableFrom(callback.getClass())) { reverseEngineeringCallbacks[i] = NoOp.INSTANCE; idxNoOp = i; } else { reverseEngineeringCallbacks[i] = createReverseEngineeredCallbackOfProperType(callback, i, callbackIndexMap); } } try { source.setCallbacks(reverseEngineeringCallbacks); final Set interfaces = new HashSet(); final Set methods = new HashSet(); Class type = source.getClass(); do { methods.addAll(Arrays.asList(type.getDeclaredMethods())); methods.addAll(Arrays.asList(type.getMethods())); Class[] implementedInterfaces = type.getInterfaces(); interfaces.addAll(Arrays.asList(implementedInterfaces)); type = type.getSuperclass(); } while (type != null); for (final Iterator iterator = interfaces.iterator(); iterator.hasNext(); ) { type = (Class) iterator.next(); methods.addAll(Arrays.asList(type.getDeclaredMethods())); } for (final Iterator iter = methods.iterator(); iter.hasNext(); ) { final Method method = (Method) iter.next(); method.setAccessible(true); if (Factory.class.isAssignableFrom(method.getDeclaringClass()) || (method.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) > 0) { iter.remove(); continue; } Class[] parameterTypes = method.getParameterTypes(); Method calledMethod = method; try { if ((method.getModifiers() & Modifier.ABSTRACT) > 0) { calledMethod = source.getClass().getMethod(method.getName(), method.getParameterTypes()); } callbackIndexMap.put(null, method); calledMethod.invoke( source, parameterTypes == null ? (Object[]) null : createNullArguments(parameterTypes)); } catch (IllegalAccessException e) { throw new ObjectAccessException("Access to " + calledMethod + " not allowed"); } catch (InvocationTargetException e) { // OK, ignore } catch (NoSuchMethodException e) { ConversionException exception = new ConversionException( "CGLIB enhanced proxies wit abstract nethod that has not been implemented"); exception.add("proxy superclass", type.getSuperclass().getName()); exception.add("method", method.toString()); throw exception; } if (callbackIndexMap.containsKey(method)) { iter.remove(); } } if (idxNoOp >= 0) { Integer idx = new Integer(idxNoOp); for (final Iterator iter = methods.iterator(); iter.hasNext(); ) { callbackIndexMap.put(iter.next(), idx); } } } finally { source.setCallbacks(originalCallbacks); } callbackIndexMap.remove(null); return callbackIndexMap; }