|
Groovy example source code file (ProxyGenerator.java)
This example Groovy source code file (ProxyGenerator.java) is included in the DevDaily.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.
The Groovy ProxyGenerator.java source code
/*
* Copyright 2003-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package groovy.util;
import groovy.lang.*;
import org.codehaus.groovy.control.MultipleCompilationErrorsException;
import org.codehaus.groovy.runtime.ConversionHandler;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.codehaus.groovy.runtime.InvokerHelper;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* Classes to generate 'Proxy' objects which implement interfaces,
* maps of closures and/or extend classes/delegates.
*
* @author Paul King
* @author Guillaume Laforge
*/
public class ProxyGenerator {
public static final ProxyGenerator INSTANCE = new ProxyGenerator();
static {
// wrap the standard MetaClass with the delegate
setMetaClass(GroovySystem.getMetaClassRegistry().getMetaClass(ProxyGenerator.class));
}
private ClassLoader override = null;
private boolean debug = false;
private boolean emptyMethods = false;
private List<Method> objectMethods = getInheritedMethods(Object.class, new ArrayList());
private List<Method> groovyObjectMethods = getInheritedMethods(GroovyObject.class, new ArrayList());
public boolean getDebug() {
return debug;
}
/**
* Instructs <code>ProxyGenerator to dump generated Groovy
* source code to standard output during construction. This is useful
* for debugging purposes but should be turned off in production.
*
* @param debug true if you want generated source to be printed
*/
public void setDebug(boolean debug) {
this.debug = debug;
}
public boolean getEmptyMethods() {
return emptyMethods;
}
/**
* Changes generated methods to have empty implementations.
* <p/>
* Methods in generated aggregates not supplied in a closures map or
* base class are given 'default' implementations. The implementation
* will normally throw an <code>UnsupportedOperationException
* but setting this boolean will leave it empty.
*
* @param emptyMethods true if you want generated methods to be empty
*/
public void setEmptyMethods(boolean emptyMethods) {
this.emptyMethods = emptyMethods;
}
public ClassLoader getOverride() {
return override;
}
public void setOverride(ClassLoader override) {
this.override = override;
}
public GroovyObject instantiateAggregateFromBaseClass(Class clazz) {
return instantiateAggregateFromBaseClass((Map) null, clazz);
}
public GroovyObject instantiateAggregateFromBaseClass(Map map, Class clazz) {
return instantiateAggregateFromBaseClass(map, clazz, null);
}
public GroovyObject instantiateAggregateFromBaseClass(Closure cl, Class clazz) {
Map<String, Closure> m = new HashMap();
m.put("*", cl);
return instantiateAggregateFromBaseClass(m, clazz, null);
}
public GroovyObject instantiateAggregateFromBaseClass(Class clazz, Object[] constructorArgs) {
return instantiateAggregate(null, null, clazz, constructorArgs);
}
public GroovyObject instantiateAggregateFromBaseClass(Map map, Class clazz, Object[] constructorArgs) {
return instantiateAggregate(map, null, clazz, constructorArgs);
}
public GroovyObject instantiateAggregateFromInterface(Class clazz) {
return instantiateAggregateFromInterface(null, clazz);
}
public GroovyObject instantiateAggregateFromInterface(Map map, Class clazz) {
List<Class> interfaces = new ArrayList();
interfaces.add(clazz);
return instantiateAggregate(map, interfaces);
}
public GroovyObject instantiateAggregate(List<Class> interfaces) {
return instantiateAggregate(null, interfaces);
}
public GroovyObject instantiateAggregate(Map closureMap, List<Class> interfaces) {
return instantiateAggregate(closureMap, interfaces, null);
}
public GroovyObject instantiateAggregate(Map closureMap, List<Class> interfaces, Class clazz) {
return instantiateAggregate(closureMap, interfaces, clazz, null);
}
public GroovyObject instantiateAggregate(Map closureMap, List<Class> interfaces, Class clazz, Object[] constructorArgs) {
Map map = new HashMap();
if (closureMap != null) {
map = closureMap;
}
List<Class> interfacesToImplement;
if (interfaces == null) {
interfacesToImplement = new ArrayList<Class>();
} else {
interfacesToImplement = interfaces;
}
Class baseClass = GroovyObjectSupport.class;
if (clazz != null) {
baseClass = clazz;
}
boolean hasArgs = constructorArgs != null && constructorArgs.length > 0;
String name = shortName(baseClass.getName()) + "_groovyProxy";
StringBuffer buffer = new StringBuffer();
// add class header and fields
buffer.append("class ").append(name);
if (clazz != null) {
buffer.append(" extends ").append(baseClass.getName());
}
for (int i = 0; i < interfacesToImplement.size(); i++) {
Class thisInterface = interfacesToImplement.get(i);
if (i == 0) {
buffer.append(" implements ");
} else {
buffer.append(", ");
}
buffer.append(thisInterface.getName());
}
buffer.append(" {\n").append(" private closureMap\n ");
// add constructor
buffer.append(name).append("(map");
if (hasArgs) {
buffer.append(", args");
}
buffer.append(") {\n");
buffer.append(" super(");
if (hasArgs) {
buffer.append("*args");
}
buffer.append(")\n");
buffer.append(" this.closureMap = map\n");
buffer.append(" }\n");
// add overwriting methods
Map<String, Method> selectedMethods = new HashMap();
List<Method> publicAndProtectedMethods = getInheritedMethods(baseClass, new ArrayList());
boolean closureIndicator = map.containsKey("*");
for (Method method : publicAndProtectedMethods) {
if (method.getName().indexOf('$') != -1
|| Modifier.isFinal(method.getModifiers())
|| ConversionHandler.isCoreObjectMethod(method)
|| containsEquivalentMethod(selectedMethods.values(), method))
continue;
if (map.containsKey(method.getName()) || closureIndicator) {
selectedMethods.put(method.getName(), method);
addOverridingMapCall(buffer, method, closureIndicator);
} else if (Modifier.isAbstract(method.getModifiers())) {
selectedMethods.put(method.getName(), method);
addMapOrDummyCall(map, buffer, method);
}
}
// add interface methods
List<Method> interfaceMethods = new ArrayList();
for (Class thisInterface : interfacesToImplement) {
getInheritedMethods(thisInterface, interfaceMethods);
}
for (Method method : interfaceMethods) {
if (!containsEquivalentMethod(publicAndProtectedMethods, method)) {
selectedMethods.put(method.getName(), method);
addMapOrDummyCall(map, buffer, method);
}
}
// add leftover methods from the map
for (Object o : map.keySet()) {
String methodName = (String) o;
if (methodName.indexOf('$') != -1 || methodName.indexOf('*') != -1)
continue;
if (selectedMethods.keySet().contains(methodName)) continue;
addNewMapCall(buffer, methodName);
}
// end class
buffer.append("}\n").append("new ").append(name);
buffer.append("(map");
if (hasArgs) {
buffer.append(", constructorArgs");
}
buffer.append(")");
Binding binding = new Binding();
binding.setVariable("map", map);
binding.setVariable("constructorArgs", constructorArgs);
ClassLoader cl = override != null ? override : baseClass.getClassLoader();
if (clazz == null && interfacesToImplement.size() > 0) {
Class c = interfacesToImplement.get(0);
cl = c.getClassLoader();
}
GroovyShell shell = new GroovyShell(cl, binding);
if (debug)
System.out.println("proxy source:\n------------------\n" + buffer.toString() + "\n------------------");
try {
return (GroovyObject) shell.evaluate(buffer.toString());
} catch (MultipleCompilationErrorsException err) {
throw new GroovyRuntimeException("Error creating proxy: " + err.getMessage());
}
}
public GroovyObject instantiateDelegate(Object delegate) {
return instantiateDelegate(null, delegate);
}
public GroovyObject instantiateDelegate(List<Class> interfaces, Object delegate) {
return instantiateDelegate(null, interfaces, delegate);
}
public GroovyObject instantiateDelegate(Map closureMap, List<Class> interfaces, Object delegate) {
return instantiateDelegateWithBaseClass(closureMap, interfaces, delegate, null);
}
public GroovyObject instantiateDelegateWithBaseClass(Map closureMap, List<Class> interfaces, Object delegate) {
return instantiateDelegateWithBaseClass(closureMap, interfaces, delegate, delegate.getClass());
}
public GroovyObject instantiateDelegateWithBaseClass(Map closureMap, List<Class> interfaces, Object delegate, Class baseClass) {
String name = shortName(delegate.getClass().getName()) + "_delegateProxy";
return instantiateDelegateWithBaseClass(closureMap, interfaces, delegate, baseClass, name);
}
public GroovyObject instantiateDelegateWithBaseClass(Map closureMap, List<Class> interfaces, Object delegate, Class baseClass, String name) {
Map map = new HashMap();
if (closureMap != null) {
map = closureMap;
}
List<String> selectedMethods = new ArrayList();
List<Class> interfacesToImplement;
if (interfaces == null) {
interfacesToImplement = new ArrayList<Class>();
} else {
interfacesToImplement = interfaces;
}
StringBuffer buffer = new StringBuffer();
// add class header and fields
buffer.append("import org.codehaus.groovy.runtime.InvokerHelper\nclass ").append(name);
if (baseClass != null) {
buffer.append(" extends ").append(baseClass.getName());
}
for (int i = 0; i < interfacesToImplement.size(); i++) {
Class thisInterface = interfacesToImplement.get(i);
if (i == 0) {
buffer.append(" implements ");
} else {
buffer.append(", ");
}
buffer.append(thisInterface.getName());
}
buffer.append(" {\n").append(" private delegate\n").append(" private closureMap\n ");
// add constructor
buffer.append(name).append("(map, delegate) {\n");
buffer.append(" this.closureMap = map\n");
buffer.append(" this.delegate = delegate\n");
buffer.append(" }\n");
// add interface methods
List<Method> interfaceMethods = new ArrayList();
for (Class thisInterface : interfacesToImplement) {
getInheritedMethods(thisInterface, interfaceMethods);
}
for (Method method : interfaceMethods) {
if (!containsEquivalentMethod(objectMethods, method) &&
!containsEquivalentMethod(groovyObjectMethods, method)) {
selectedMethods.add(method.getName());
addWrappedCall(buffer, method, map);
}
}
List<Method> additionalMethods = getInheritedMethods(delegate.getClass(), new ArrayList());
for (Method method : additionalMethods) {
if (method.getName().indexOf('$') != -1)
continue;
if (!containsEquivalentMethod(interfaceMethods, method) &&
!containsEquivalentMethod(objectMethods, method) &&
!containsEquivalentMethod(groovyObjectMethods, method)) {
selectedMethods.add(method.getName());
addWrappedCall(buffer, method, map);
}
}
// add leftover methods from the map
for (Object o : map.keySet()) {
String methodName = (String) o;
if (selectedMethods.contains(methodName)) continue;
addNewMapCall(buffer, methodName);
}
// end class
buffer.append("}\n").append("new ").append(name);
buffer.append("(map, delegate)");
Binding binding = new Binding();
binding.setVariable("map", map);
binding.setVariable("delegate", delegate);
ClassLoader cl = override != null ? override : delegate.getClass().getClassLoader();
GroovyShell shell = new GroovyShell(cl, binding);
if (debug)
System.out.println("proxy source:\n------------------\n" + buffer.toString() + "\n------------------");
try {
return (GroovyObject) shell.evaluate(buffer.toString());
} catch (MultipleCompilationErrorsException err) {
throw new GroovyRuntimeException("Error creating proxy: " + err.getMessage());
}
}
private void addWrappedCall(StringBuffer buffer, Method method, Map map) {
if (map.containsKey(method.getName())) {
addOverridingMapCall(buffer, method, false);
} else {
Class[] parameterTypes = addMethodPrefix(buffer, method);
addWrappedMethodBody(buffer, method, parameterTypes);
addMethodSuffix(buffer);
}
}
private boolean containsEquivalentMethod(Collection<Method> publicAndProtectedMethods, Method candidate) {
for (Method method : publicAndProtectedMethods) {
if (candidate.getName().equals(method.getName()) &&
candidate.getReturnType().equals(method.getReturnType()) &&
hasMatchingParameterTypes(candidate, method)) {
return true;
}
}
return false;
}
private boolean hasMatchingParameterTypes(Method method, Method candidate) {
Class[] candidateParamTypes = candidate.getParameterTypes();
Class[] methodParamTypes = method.getParameterTypes();
if (candidateParamTypes.length != methodParamTypes.length) return false;
for (int i = 0; i < methodParamTypes.length; i++) {
if (!candidateParamTypes[i].equals(methodParamTypes[i])) return false;
}
return true;
}
private List<Method> getInheritedMethods(Class baseClass, List methods) {
methods.addAll(DefaultGroovyMethods.toList(baseClass.getMethods()));
Class currentClass = baseClass;
while (currentClass != null) {
Method[] protectedMethods = currentClass.getDeclaredMethods();
for (Method method : protectedMethods) {
if (method.getName().indexOf('$') != -1)
continue;
if (Modifier.isProtected(method.getModifiers()) && !containsEquivalentMethod(methods, method))
methods.add(method);
}
currentClass = currentClass.getSuperclass();
}
return methods;
}
private void addNewMapCall(StringBuffer buffer, String methodName) {
buffer.append(" def ").append(methodName).append("(Object[] args) {\n")
.append(" this.@closureMap['").append(methodName).append("'] (*args)\n }\n");
}
private void addOverridingMapCall(StringBuffer buffer, Method method, boolean closureIndicator) {
Class[] parameterTypes = addMethodPrefix(buffer, method);
addMethodBody(buffer, closureIndicator ? "*" : method.getName(), parameterTypes);
addMethodSuffix(buffer);
}
private void addMapOrDummyCall(Map map, StringBuffer buffer, Method method) {
Class[] parameterTypes = addMethodPrefix(buffer, method);
if (map.containsKey(method.getName())) {
addMethodBody(buffer, method.getName(), parameterTypes);
} else if (!emptyMethods) {
addUnsupportedBody(buffer);
}
addMethodSuffix(buffer);
}
private void addUnsupportedBody(StringBuffer buffer) {
buffer.append("throw new UnsupportedOperationException()");
}
private Class[] addMethodPrefix(StringBuffer buffer, Method method) {
buffer.append(" ").append(getSimpleName(method.getReturnType()))
.append(" ").append(method.getName()).append("(");
Class[] parameterTypes = method.getParameterTypes();
for (int parameterTypeIndex = 0; parameterTypeIndex < parameterTypes.length; parameterTypeIndex++) {
Class parameter = parameterTypes[parameterTypeIndex];
if (parameterTypeIndex != 0) {
buffer.append(", ");
}
buffer.append(getSimpleName(parameter)).append(" ")
.append("p").append(parameterTypeIndex);
}
buffer.append(") { ");
return parameterTypes;
}
private void addMethodBody(StringBuffer buffer, String method, Class[] parameterTypes) {
buffer.append("this.@closureMap['").append(method).append("'] (");
for (int j = 0; j < parameterTypes.length; j++) {
if (j != 0) {
buffer.append(", ");
}
buffer.append("p").append(j);
}
buffer.append(")");
}
private void addWrappedMethodBody(StringBuffer buffer, Method method, Class[] parameterTypes) {
buffer.append("\n Object[] args = [");
for (int j = 0; j < parameterTypes.length; j++) {
if (j != 0) {
buffer.append(", ");
}
buffer.append("p").append(j);
}
buffer.append("]\n ");
buffer.append("InvokerHelper.invokeMethod(delegate, '").append(method.getName()).append("', args)\n");
}
private void addMethodSuffix(StringBuffer buffer) {
buffer.append(" }\n");
}
/**
* TODO once we switch to Java 1.5 bt default, use Class#getSimpleName() directly
*
* @param c the class of which we want the readable simple name
* @return the readable simple name
*/
public String getSimpleName(Class c) {
if (c.isArray()) {
int dimension = 0;
Class componentClass = c;
while (componentClass.isArray()) {
componentClass = componentClass.getComponentType();
dimension++;
}
return componentClass.getName().replaceAll("\\$", "\\.") +
DefaultGroovyMethods.multiply("[]", dimension);
} else {
return c.getName().replaceAll("\\$", "\\.");
}
}
public String shortName(String name) {
int index = name.lastIndexOf('.');
if (index == -1) return name;
return name.substring(index + 1, name.length());
}
private static void setMetaClass(final MetaClass metaClass) {
final MetaClass newMetaClass = new DelegatingMetaClass(metaClass) {
@Override
public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {
return InvokerHelper.invokeMethod(INSTANCE, methodName, arguments);
}
};
GroovySystem.getMetaClassRegistry().setMetaClass(ProxyGenerator.class, newMetaClass);
}
}
Other Groovy examples (source code examples)
Here is a short list of links related to this Groovy ProxyGenerator.java source code file:
|