001: /*
002: * Created on Sep 29, 2005
003: */
004: package uk.org.ponder.reflect;
005:
006: import java.lang.reflect.Constructor;
007: import java.lang.reflect.Method;
008: import java.util.Map;
009:
010: import uk.org.ponder.arrayutil.ArrayUtil;
011: import uk.org.ponder.conversion.StaticLeafParser;
012: import uk.org.ponder.saxalizer.SAXAccessMethod;
013: import uk.org.ponder.util.Logger;
014: import uk.org.ponder.util.UniversalRuntimeException;
015:
016: /**
017: * A cache for simple no-arg methods and constructors, of the sort that are
018: * typically used in bean environments, that are not suitable to be considered
019: * as "bean properties" and hence handled by a SAXAccessMethod.
020: *
021: * The class is capable of resolving methods and constructors with arguments,
022: * but accessors for these members are not cached or accelerated.
023: *
024: * The intention is that **ALL** application-wide reflection will be done either
025: * in this class, or in SAXAccessMethod.
026: *
027: *
028: * This implementation will probably shortly be replaced by a FastClass variant.
029: *
030: * @author Antranig Basman (antranig@caret.cam.ac.uk)
031: *
032: */
033: public abstract class ReflectiveCache {
034: private Map rootmap;
035: private static Class concurrent1mapclass;
036: // The constructor for the oswego n-concurrent map class - we need to handle
037: // this explicitly, since the concurrency level has to be passed in as an argument.
038: private static Constructor concurrentnmapcons;
039: private static boolean nmapisJSR166 = false;
040:
041: public static Class getConcurrent1MapClass() {
042: // OSwego is preferable to JSR166 since our desired concurrency level is
043: // pretty much 1. CRHM is i) optimised for this case, and ii) will be
044: // cheaper in construction since we can use the no-args constructor.
045: Class mapclass = ClassGetter
046: .forName("EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap");
047: if (mapclass == null) {
048: mapclass = ClassGetter
049: .forName("java.util.concurrent.ConcurrentHashMap");
050:
051: }
052: if (mapclass == null) {
053: System.err
054: .println("Fatal: Could not instantiate concurrent map class from either oswego or JDK 1.5 provider");
055: Logger.log
056: .fatal("Could not instantiate concurrent map class from either oswego or JDK 1.5 provider");
057: }
058: return mapclass;
059: }
060:
061: /**
062: * Returns a Constructor capable of constructing a multiple-concurrent-reader
063: * HashMap.
064: */
065: public static Constructor getConcurrentNMapConstructor() {
066: Class mapclass = ClassGetter
067: .forName("EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap");
068: try {
069: if (mapclass == null) {
070: mapclass = ClassGetter
071: .forName("java.util.concurrent.ConcurrentHashMap");
072: if (mapclass != null) {
073: nmapisJSR166 = true;
074: return mapclass.getConstructor(new Class[] {
075: Integer.TYPE, Float.TYPE, Integer.TYPE });
076: }
077: } else {
078: return mapclass
079: .getConstructor(SAXAccessMethod.emptyclazz);
080: }
081: if (mapclass == null) {
082: Logger.log
083: .fatal("Could not instantiate concurrent map class from either oswego or JDK 1.5 provider");
084: }
085: } catch (Exception e) {
086: Logger.log.fatal("Exception finding constructor for "
087: + mapclass, e);
088: }
089: return null;
090: }
091:
092: /**
093: * Get a no-argument constructor for a class
094: * @param clazz the class
095: * @return a no-arg constructor
096: */
097: public static Constructor getConstructor(Class clazz) {
098: try {
099: return clazz.getConstructor(SAXAccessMethod.emptyclazz);
100: } catch (Exception e) {
101: throw UniversalRuntimeException.accumulate(e,
102: "Error getting constructor for " + clazz);
103: }
104: }
105:
106: protected Map getClassMap(Class target) {
107: Map classmap = (Map) rootmap.get(target);
108: if (classmap == null) {
109: classmap = getConcurrentMap(1);
110: rootmap.put(target, classmap);
111: }
112: return classmap;
113: }
114:
115: public static Method getMethod(Class clazz, String name) {
116: return getMethod(clazz, name, SAXAccessMethod.emptyclazz);
117: }
118:
119: public static Method getMethod(Class clazz, String name,
120: Class[] argtypes) {
121: try {
122: return clazz.getMethod(name, argtypes);
123: } catch (Exception e) {
124: throw UniversalRuntimeException.accumulate(e,
125: "Error reflecting for method " + name + " of "
126: + clazz);
127: }
128: }
129:
130: public abstract Object construct(Class clazz);
131:
132: public abstract Object invokeMethod(Object bean, String method);
133:
134: protected abstract Object invokeMethod(Object target, String name,
135: Class[] infer, Object[] args);
136:
137: private static String getInvokeError(Object target, String name,
138: Object[] args) {
139: return name + " in object " + target.getClass() + " with "
140: + args.length + " arguments: "
141: + ArrayUtil.toString(args);
142: }
143:
144: /**
145: * Somewhat inefficient, but this scheme applies simple argument matching
146: * to be able to invoke multi-arg methods.
147: */
148: public Object invokeMethod(Object target, String name, Object[] args) {
149: if (args == null || args.length == 0) {
150: return invokeMethod(target, name);
151: }
152: if (target instanceof MethodInvokingProxy) {
153: return ((MethodInvokingProxy) target).invokeMethod(name,
154: args);
155: }
156: Method[] matching = ReflectUtils.getMatchingMethods(target
157: .getClass(), name, args);
158: if (matching.length == 0) {
159: throw new IllegalArgumentException(
160: "Could not find method named "
161: + getInvokeError(target, name, args));
162:
163: }
164: if (matching.length >= 2) {
165: throw new IllegalArgumentException(
166: "Found ambiguous match with " + matching.length
167: + " equally good matches for "
168: + getInvokeError(target, name, args));
169: }
170: Method match = matching[0];
171: Class[] infer = new Class[args.length];
172: for (int i = 0; i < args.length; ++i) {
173: Class argclazz = match.getParameterTypes()[i];
174: // proved to be the only case, by definition of getMatchingMethods
175: if (!ReflectUtils.isAssignable(argclazz, args[i])) {
176: args[i] = StaticLeafParser.instance().parse(argclazz,
177: (String) args[i]);
178: }
179: infer[i] = args[i].getClass();
180: }
181: return JDKReflectiveCache.invokeMethod(match, target, args);
182: }
183:
184: /**
185: * The key used into per-class reflective caches representing the no-arg
186: * constructor.
187: */
188: public static final String CONSTRUCTOR_KEY = "<init>";
189:
190: /**
191: * The initial size of JSR-166 multi-concurrency hashes (all others are
192: * defaults)
193: */
194: public static final int INIT_MAP_SIZE = 1024;
195:
196: /**
197: * Returns a new concurrent map object with the desired level of concurrency.
198: * If the oswego package is available, any concurrency level other than 1 will
199: * return the ConcurrentHashMap implementation with its hardwired level of 32.
200: * Concurrency level 1 hashes are suitable for static application-wide caches,
201: * e.g. for internal use of this class, to be either the root map or the map
202: * for the default constructors of a particular class.
203: */
204: public Map getConcurrentMap(int concurrency) {
205: // if map class is null, this is the first call for the entire system,
206: // perhaps
207: // from getClassMap. Initialise the root table, and put the constructor for
208: // the concurrent map itself in it as its first entry, manually.
209: // Uninteresting race condition here.
210: // TODO: try to remove downcall to JDKReflector
211: if (concurrency == 1) {
212: // check both of these because of possible race condition.
213: if (concurrent1mapclass == null || rootmap == null) {
214: concurrent1mapclass = getConcurrent1MapClass();
215: Constructor cons = getConstructor(concurrent1mapclass);
216: rootmap = (Map) JDKReflectiveCache
217: .invokeConstructor(cons);
218: Map classmap = (Map) JDKReflectiveCache
219: .invokeConstructor(cons);
220: classmap.put(CONSTRUCTOR_KEY, cons);
221: rootmap.put(concurrent1mapclass, classmap);
222: }
223: return (Map) construct(concurrent1mapclass);
224: } else {
225: if (concurrentnmapcons == null) {
226: concurrentnmapcons = getConcurrentNMapConstructor();
227: }
228: Object togo = null;
229: if (nmapisJSR166) {
230: togo = JDKReflectiveCache.invokeConstructor(
231: concurrentnmapcons, new Object[] {
232: new Integer(INIT_MAP_SIZE),
233: new Float(0.75f),
234: new Integer(concurrency) });
235: } else {
236: togo = JDKReflectiveCache
237: .invokeConstructor(concurrentnmapcons);
238: }
239: return (Map) togo;
240: }
241:
242: }
243:
244: }
|