blob: 13c822f3be366b6d505c4629a07e81b8dc9214cf [file] [log] [blame]
/****************************************************************************
* 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();
}
}