/**************************************************************************** * Copyright (c) 2004 Composent, Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Composent, Inc. - initial API and implementation *****************************************************************************/ package org.eclipse.ecf.example.collab.share; import java.io.NotSerializableException; import java.io.Serializable; import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import org.eclipse.core.runtime.Assert; import org.eclipse.ecf.core.identity.ID; import org.eclipse.ecf.internal.example.collab.Messages; import org.eclipse.osgi.util.NLS; public class SharedObjectMsg implements Serializable { static final long serialVersionUID = 571132189626558278L; public static final Object[] nullArgs = new Object[0]; public static final Class[] nullTypes = new Class[0]; class SenderID { private final ID id; // No instances other than ones created in SharedObjectMsg.invokeFrom/2 protected SenderID(ID theID) { id = theID; } public ID getID() { return id; } } // Static factory methods for creating SharedObjectMsg instances public static SharedObjectMsg createMsg(String className, String methodName, Object[] param) { Assert.isNotNull(methodName); Assert.isNotNull(param); return new SharedObjectMsg(className, methodName, param); } public static SharedObjectMsg createMsg(String methodName, Object[] param) { return createMsg((String) null, methodName, param); } public static SharedObjectMsg createMsg(String methodName) { return createMsg((String) null, methodName, nullArgs); } public static SharedObjectMsg createMsg(String className, String methodName) { return createMsg(className, methodName, nullArgs); } public static SharedObjectMsg createMsg(String className, String methodName, Object arg) { final Object args[] = {arg}; return createMsg(className, methodName, args); } public static SharedObjectMsg createMsg(String methodName, Object arg) { return createMsg((String) null, methodName, arg); } public static SharedObjectMsg createMsg(String className, String methodName, Object arg1, Object arg2) { final Object args[] = {arg1, arg2}; return createMsg(className, methodName, args); } public static SharedObjectMsg createMsg(String className, String methodName, Object arg1, Object arg2, Object arg3) { final Object args[] = {arg1, arg2, arg3}; return createMsg(className, methodName, args); } public static SharedObjectMsg createMsg(String className, String methodName, Object arg1, Object arg2, Object arg3, Object arg4) { final Object args[] = {arg1, arg2, arg3, arg4}; return createMsg(className, methodName, args); } public static SharedObjectMsg createMsg(String className, String methodName, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) { final Object args[] = {arg1, arg2, arg3, arg4, arg5}; return createMsg(className, methodName, args); } /** * Utility for getting a Class instance from a String class name. Calls * Class.forName(). * * @param loader * the ClassLoader to use to load the given class * @param name * of Class to load * @return Class instance found. If not found, a ClassNotFoundException is * thrown * @exception ClassNotFoundException * thrown if specified class is not found */ public static Class getClass(ClassLoader loader, String name) throws ClassNotFoundException { if (name == null) return null; return Class.forName(name, true, loader); } /** * Get name for given class. * * @param clazz * the Class to retrieve the name from * @return String name of given class */ public static String getName(Class clazz) { return clazz.getName(); } /** * Get array of argument types from array of objects * * @param args * the arguments to get types for * @return Class[] of types for objects in given Object array */ public static Class[] getArgTypes(Object args[]) { Class argTypes[] = null; if (args == null || args.length == 0) argTypes = nullTypes; else { argTypes = new Class[args.length]; for (int i = 0; i < args.length; i++) { if (args[i] == null) argTypes[i] = null; else argTypes[i] = args[i].getClass(); } } return argTypes; } /** * Find a Method instance on given class. This method searches for a method * on the given class (first parameter), of the given name (second * parameter), and of arity defined by the third parameter. Calls * searchForMethod to actually do the searching. * * @param clazz * the Class to look on * @param meth * the method name to look for * @param args * the arguments that will be passed to the method on the invoke * call * @return Method instance found on given class. Null if none found. */ static Method findMethod(final Class clazz, String meth, Class args[]) { Method methods[] = null; try { methods = (Method[]) AccessController.doPrivileged(new PrivilegedExceptionAction() { public Object run() throws Exception { return clazz.getDeclaredMethods(); } }); } catch (final PrivilegedActionException e) { return null; } return searchForMethod(methods, meth, args); } public static Method searchForMethod(Method meths[], String meth, Class args[]) { // Find it from among the given set of Method objects for (int i = 0; i < meths.length; i++) { final Method test = meths[i]; if (test.getName().equals(meth)) { if (test.getParameterTypes().length == args.length) return test; } } return null; } /** * Find a Method instance on given class, and recursively search the class' * superclass tree for given method. * * @param clazz * the Class to look upon * @param meth * the String name of the method to look for * @param args * the array of Object arguments that will be passed to the * method for execution * @return Method instance if found, null if not found */ public static Method findMethodRecursive(Class clazz, String meth, Class args[]) { final Method aMethod = findMethod(clazz, meth, args); if (aMethod == null) { final Class superclazz = clazz.getSuperclass(); if (superclazz != null) return findMethodRecursive(superclazz, meth, args); else return null; } else { return aMethod; } } /** * Check a given msg to verify that all Objects in args array implement the * Serializable interface. * * @param aMsg * the Message to check * @exception NotSerializableException * thrown if any objects in args array do not implement the * Serializable interface */ public static void checkForSerializable(SharedObjectMsg aMsg) throws NotSerializableException { final Object args[] = aMsg.getArgs(); for (int i = 0; i < args.length; i++) { if (args[i] != null && !(args[i] instanceof Serializable)) throw new NotSerializableException(NLS.bind(Messages.SharedObjectMsg_EXCEPTION_NOT_SERIALIZABLE, new Integer(i))); } } // Instance fields /** * @serial myClassName the class name for the message */ String myClassName; /** * @serial myMethodName the method name of the message */ String myMethodName; /** * @serial myArgs arguments */ Object[] myArgs; // Constructor SharedObjectMsg(String className, String methodName, Object[] args) { myClassName = className; myMethodName = methodName; myArgs = args; } /** * Constructor for TypedMsg's. */ SharedObjectMsg(String methodName) { this(null, methodName, null); } public final String getMethodName() { return myMethodName; } public final void setMethodName(String name) { checkAlterMsg(); if (name == null) throw new NullPointerException(Messages.SharedObjectMsg_EXCEPTION_METHOD_NOT_NULL); myMethodName = name; } /** * Check if it is permitted to alter the state of this message (args, class * name, method name). Default: NOP; subclasses should override as * appropriate. To disallow, throw a java.lang.RuntimeException. */ protected void checkAlterMsg() { // NOP; subclasses should override as appropriate } public final String getClassName() { return myClassName; } public final void setClassName(String name) { checkAlterMsg(); myClassName = name; } public Object[] getArgs() // TypedMsg overrides { return myArgs; } public final void setArgs(Object[] args) { checkAlterMsg(); myArgs = (args == null) ? nullArgs : args; } protected Class[] getArgTypes() { return getArgTypes(getArgs()); } protected final Method findMethod(Class clazz) { return findMethod(clazz, getMethodName(), getArgTypes()); } protected final Method findMethodRecursive(Class clazz) { return findMethodRecursive(clazz, getMethodName(), getArgTypes()); } public final Object invoke(Object target) throws Exception { return doInvoke(target); } Object doInvoke(final Object target) // package scope for security throws Exception { if (target == null) throw new NoSuchMethodException(Messages.SharedObjectMsg_EXCEPTION_NULL_TARGET); Method meth = null; if (myClassName == null) { // If not specific class is specified by SharedObjectMsg instance, // then use the target's class meth = findMethodRecursive(target.getClass()); } else { // If it is specified, then load the specified class, using the // target object's classloader meth = findMethod(getClass(target.getClass().getClassLoader(), myClassName)); } // If no method was found, then throw if (meth == null) throw new NoSuchMethodException(getMethodName()); final Method toCall = meth; // Make priveleged call to set the method as accessible AccessController.doPrivileged(new PrivilegedExceptionAction() { public Object run() throws Exception { if (!toCall.isAccessible()) toCall.setAccessible(true); return null; } }); // Actually invoke method return toCall.invoke(target, getArgs()); } public final Object invokeFrom(ID fromID, final Object target) throws Exception { // Setup new array of arguments with the fromID on the front Object[] newParams = null; final SenderID sender = new SenderID(fromID); final Object args[] = getArgs(); if (args == null) { newParams = new Object[1]; newParams[0] = sender; } else { newParams = new Object[myArgs.length + 1]; newParams[0] = sender; System.arraycopy(args, 0, newParams, 1, args.length); } // Reset arguments before method search myArgs = newParams; // Now just call invoke/1 return invoke(target); } public String toString() { final StringBuffer sb = new StringBuffer(); sb.append("SharedObjectMsg[").append(myClassName).append(":").append(myMethodName).append("("); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ if (myArgs == null) { sb.append(myArgs); } else { for (int i = 0; i < myArgs.length; i++) { if (i > 0) sb.append(","); //$NON-NLS-1$ sb.append(myArgs[i]); } } sb.append(")]"); //$NON-NLS-1$ return sb.toString(); } }