001: package org.jgroups.blocks;
002:
003: import org.apache.commons.logging.Log;
004: import org.apache.commons.logging.LogFactory;
005:
006: import java.io.Externalizable;
007: import java.io.IOException;
008: import java.io.ObjectInput;
009: import java.io.ObjectOutput;
010: import java.lang.reflect.InvocationTargetException;
011: import java.lang.reflect.Method;
012: import java.util.*;
013:
014: /**
015: * A method call is the JGroups representation of a remote method.
016: * It includes the name of the method (case sensitive) and a list of arguments.
017: * A method call is serializable and can be passed over the wire.
018: * @author Bela Ban
019: * @version $Revision: 1.24 $
020: */
021: public class MethodCall implements Externalizable {
022:
023: private static final long serialVersionUID = 7873471327078957662L;
024:
025: /** The name of the method, case sensitive. */
026: protected String method_name;
027:
028: /** The ID of a method, maps to a java.lang.reflect.Method */
029: protected short method_id = -1;
030:
031: /** The arguments of the method. */
032: protected Object[] args;
033:
034: /** The class types, e.g., new Class[]{String.class, int.class}. */
035: protected Class[] types;
036:
037: /** The signature, e.g., new String[]{String.class.getName(), int.class.getName()}. */
038: protected String[] signature;
039:
040: /** The Method of the call. */
041: protected Method method;
042:
043: /** To carry arbitrary data with a method call, data needs to be serializable if sent across the wire */
044: protected Map payload;
045:
046: protected static final Log log = LogFactory
047: .getLog(MethodCall.class);
048:
049: /** Which mode to use. */
050: protected short mode = OLD;
051:
052: /** Infer the method from the arguments. */
053: protected static final short OLD = 1;
054:
055: /** Explicitly ship the method, caller has to determine method himself. */
056: protected static final short METHOD = 2;
057:
058: /** Use class information. */
059: protected static final short TYPES = 3;
060:
061: /** Provide a signature, similar to JMX. */
062: protected static final short SIGNATURE = 4;
063:
064: /** Use an ID to map to a method */
065: protected static final short ID = 5;
066:
067: /**
068: * Creates an empty method call, this is always invalid, until
069: * <code>setName()</code> has been called.
070: */
071: public MethodCall() {
072: }
073:
074: public MethodCall(Method method) {
075: this (method, null);
076: }
077:
078: public MethodCall(Method method, Object[] arguments) {
079: init(method);
080: if (arguments != null)
081: args = arguments;
082: }
083:
084: /**
085: *
086: * @param method_name
087: * @param args
088: * @deprecated Use one of the constructors that take class types as arguments
089: */
090: public MethodCall(String method_name, Object[] args) {
091: this .method_name = method_name;
092: this .mode = OLD;
093: this .args = args;
094: }
095:
096: public MethodCall(short method_id, Object[] args) {
097: this .method_id = method_id;
098: this .mode = ID;
099: this .args = args;
100: }
101:
102: public MethodCall(String method_name, Object[] args, Class[] types) {
103: this .method_name = method_name;
104: this .args = args;
105: this .types = types;
106: this .mode = TYPES;
107: }
108:
109: public MethodCall(String method_name, Object[] args,
110: String[] signature) {
111: this .method_name = method_name;
112: this .args = args;
113: this .signature = signature;
114: this .mode = SIGNATURE;
115: }
116:
117: private void init(Method method) {
118: this .method = method;
119: this .mode = METHOD;
120: method_name = method.getName();
121: }
122:
123: public int getMode() {
124: return mode;
125: }
126:
127: /**
128: * returns the name of the method to be invoked using this method call object
129: * @return a case sensitive name, can be null for an invalid method call
130: */
131: public String getName() {
132: return method_name;
133: }
134:
135: /**
136: * sets the name for this MethodCall and allowing you to reuse the same object for
137: * a different method invokation of a different method
138: * @param n - a case sensitive method name
139: */
140: public void setName(String n) {
141: method_name = n;
142: }
143:
144: public short getId() {
145: return method_id;
146: }
147:
148: public void setId(short method_id) {
149: this .method_id = method_id;
150: }
151:
152: /**
153: * returns an ordered list of arguments used for the method invokation
154: * @return returns the list of ordered arguments
155: */
156: public Object[] getArgs() {
157: return args;
158: }
159:
160: public void setArgs(Object[] args) {
161: if (args != null)
162: this .args = args;
163: }
164:
165: public Method getMethod() {
166: return method;
167: }
168:
169: public void setMethod(Method m) {
170: init(m);
171: }
172:
173: public synchronized Object put(Object key, Object value) {
174: if (payload == null)
175: payload = new HashMap();
176: return payload.put(key, value);
177: }
178:
179: public synchronized Object get(Object key) {
180: return payload != null ? payload.get(key) : null;
181: }
182:
183: /**
184: *
185: * @param target_class
186: * @return
187: * @throws Exception
188: */
189: Method findMethod(Class target_class) throws Exception {
190: int len = args != null ? args.length : 0;
191: Method m;
192:
193: Method[] methods = getAllMethods(target_class);
194: for (int i = 0; i < methods.length; i++) {
195: m = methods[i];
196: if (m.getName().equals(method_name)) {
197: if (m.getParameterTypes().length == len)
198: return m;
199: }
200: }
201:
202: return null;
203: }
204:
205: /**
206: * The method walks up the class hierarchy and returns <i>all</i> methods of this class
207: * and those inherited from superclasses and superinterfaces.
208: */
209: Method[] getAllMethods(Class target) {
210: Class super class = target;
211: List methods = new ArrayList();
212: int size = 0;
213:
214: while (super class != null) {
215: try {
216: Method[] m = super class.getDeclaredMethods();
217: methods.add(m);
218: size += m.length;
219: super class = super class.getSuperclass();
220: } catch (SecurityException e) {
221: // if it runs in an applet context, it won't be able to retrieve
222: // methods from superclasses that belong to the java VM and it will
223: // raise a security exception, so we catch it here.
224: if (log.isWarnEnabled())
225: log
226: .warn("unable to enumerate methods of superclass "
227: + super class
228: + " of class "
229: + target);
230: super class = null;
231: }
232: }
233:
234: Method[] result = new Method[size];
235: int index = 0;
236: for (Iterator i = methods.iterator(); i.hasNext();) {
237: Method[] m = (Method[]) i.next();
238: System.arraycopy(m, 0, result, index, m.length);
239: index += m.length;
240: }
241: return result;
242: }
243:
244: /**
245: * Returns the first method that matches the specified name and parameter types. The overriding
246: * methods have priority. The method is chosen from all the methods of the current class and all
247: * its superclasses and superinterfaces.
248: *
249: * @return the matching method or null if no mathching method has been found.
250: */
251: Method getMethod(Class target, String methodName, Class[] types) {
252:
253: if (types == null) {
254: types = new Class[0];
255: }
256:
257: Method[] methods = getAllMethods(target);
258: methods: for (int i = 0; i < methods.length; i++) {
259: Method m = methods[i];
260: if (!methodName.equals(m.getName())) {
261: continue;
262: }
263: Class[] parameters = m.getParameterTypes();
264: if (types.length != parameters.length) {
265: continue;
266: }
267: for (int j = 0; j < types.length; j++) {
268: if (!types[j].equals(parameters[j])) {
269: continue methods;
270: }
271: }
272: return m;
273: }
274: return null;
275: }
276:
277: /**
278: * Invokes the method with the supplied arguments against the target object.
279: * If a method lookup is provided, it will be used. Otherwise, the default
280: * method lookup will be used.
281: * @param target - the object that you want to invoke the method on
282: * @return an object
283: */
284: public Object invoke(Object target) throws Throwable {
285: Class cl;
286: Method meth = null;
287: Object retval = null;
288:
289: if (method_name == null || target == null) {
290: if (log.isErrorEnabled())
291: log.error("method name or target is null");
292: return null;
293: }
294: cl = target.getClass();
295: try {
296: switch (mode) {
297: case OLD:
298: meth = findMethod(cl);
299: break;
300: case METHOD:
301: if (this .method != null)
302: meth = this .method;
303: break;
304: case TYPES:
305: //meth=cl.getDeclaredMethod(method_name, types);
306: meth = getMethod(cl, method_name, types);
307: break;
308: case SIGNATURE:
309: Class[] mytypes = null;
310: if (signature != null)
311: mytypes = getTypesFromString(cl, signature);
312: //meth=cl.getDeclaredMethod(method_name, mytypes);
313: meth = getMethod(cl, method_name, mytypes);
314: break;
315: case ID:
316: break;
317: default:
318: if (log.isErrorEnabled())
319: log.error("mode " + mode + " is invalid");
320: break;
321: }
322:
323: if (meth != null) {
324: retval = meth.invoke(target, args);
325: } else {
326: throw new NoSuchMethodException(method_name);
327: }
328: return retval;
329: } catch (InvocationTargetException inv_ex) {
330: throw inv_ex.getTargetException();
331: } catch (NoSuchMethodException no) {
332: StringBuffer sb = new StringBuffer();
333: sb.append("found no method called ").append(method_name)
334: .append(" in class ");
335: sb.append(cl.getName()).append(" with (");
336: if (args != null) {
337: for (int i = 0; i < args.length; i++) {
338: if (i > 0)
339: sb.append(", ");
340: sb.append((args[i] != null) ? args[i].getClass()
341: .getName() : "null");
342: }
343: }
344: sb.append(") formal parameters");
345: log.error(sb.toString());
346: throw no;
347: } catch (Throwable e) {
348: // e.printStackTrace(System.err);
349: if (log.isErrorEnabled())
350: log.error("exception in invoke()", e);
351: throw e;
352: }
353: }
354:
355: public Object invoke(Object target, Object[] args) throws Throwable {
356: if (args != null)
357: this .args = args;
358: return invoke(target);
359: }
360:
361: Class[] getTypesFromString(Class cl, String[] signature)
362: throws Exception {
363: String name;
364: Class parameter;
365: Class[] mytypes = new Class[signature.length];
366:
367: for (int i = 0; i < signature.length; i++) {
368: name = signature[i];
369: if ("long".equals(name))
370: parameter = long.class;
371: else if ("int".equals(name))
372: parameter = int.class;
373: else if ("short".equals(name))
374: parameter = short.class;
375: else if ("char".equals(name))
376: parameter = char.class;
377: else if ("byte".equals(name))
378: parameter = byte.class;
379: else if ("float".equals(name))
380: parameter = float.class;
381: else if ("double".equals(name))
382: parameter = double.class;
383: else if ("boolean".equals(name))
384: parameter = boolean.class;
385: else
386: parameter = Class.forName(name, false, cl
387: .getClassLoader());
388: mytypes[i] = parameter;
389: }
390: return mytypes;
391: }
392:
393: public String toString() {
394: StringBuffer ret = new StringBuffer();
395: boolean first = true;
396: if (method_name != null)
397: ret.append(method_name);
398: else
399: ret.append(method_id);
400: ret.append('(');
401: if (args != null) {
402: for (int i = 0; i < args.length; i++) {
403: if (first)
404: first = false;
405: else
406: ret.append(", ");
407: ret.append(args[i]);
408: }
409: }
410: ret.append(')');
411: return ret.toString();
412: }
413:
414: public String toStringDetails() {
415: StringBuffer ret = new StringBuffer();
416: ret.append("MethodCall ");
417: if (method_name != null)
418: ret.append("name=").append(method_name);
419: else
420: ret.append("id=").append(method_id);
421: ret.append(", number of args=").append(
422: (args != null ? args.length : 0)).append(')');
423: if (args != null) {
424: ret.append("\nArgs:");
425: for (int i = 0; i < args.length; i++) {
426: ret.append("\n[").append(args[i]).append(" (").append(
427: (args[i] != null ? args[i].getClass().getName()
428: : "null")).append(")]");
429: }
430: }
431: return ret.toString();
432: }
433:
434: public void writeExternal(ObjectOutput out) throws IOException {
435: if (method_name != null) {
436: out.writeBoolean(true);
437: out.writeUTF(method_name);
438: } else {
439: out.writeBoolean(false);
440: out.writeShort(method_id);
441: }
442: out.writeObject(args);
443: out.writeShort(mode);
444:
445: switch (mode) {
446: case OLD:
447: break;
448: case METHOD:
449: out.writeObject(method.getParameterTypes());
450: out.writeObject(method.getDeclaringClass());
451: break;
452: case TYPES:
453: out.writeObject(types);
454: break;
455: case SIGNATURE:
456: out.writeObject(signature);
457: break;
458: case ID:
459: break;
460: default:
461: if (log.isErrorEnabled())
462: log.error("mode " + mode + " is invalid");
463: break;
464: }
465:
466: if (payload != null) {
467: out.writeBoolean(true);
468: out.writeObject(payload);
469: } else {
470: out.writeBoolean(false);
471: }
472: }
473:
474: public void readExternal(ObjectInput in) throws IOException,
475: ClassNotFoundException {
476: boolean name_available = in.readBoolean();
477: if (name_available)
478: method_name = in.readUTF();
479: else
480: method_id = in.readShort();
481: args = (Object[]) in.readObject();
482: mode = in.readShort();
483:
484: switch (mode) {
485: case OLD:
486: break;
487: case METHOD:
488: Class[] parametertypes = (Class[]) in.readObject();
489: Class declaringclass = (Class) in.readObject();
490: try {
491: method = declaringclass.getDeclaredMethod(method_name,
492: parametertypes);
493: } catch (NoSuchMethodException e) {
494: throw new IOException(e.toString());
495: }
496: break;
497: case TYPES:
498: types = (Class[]) in.readObject();
499: break;
500: case SIGNATURE:
501: signature = (String[]) in.readObject();
502: break;
503: case ID:
504: break;
505: default:
506: if (log.isErrorEnabled())
507: log.error("mode " + mode + " is invalid");
508: break;
509: }
510:
511: boolean payload_available = in.readBoolean();
512: if (payload_available) {
513: payload = (Map) in.readObject();
514: }
515: }
516:
517: }
|