package org.jwebsocket.client.plugins.rpc; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; import java.util.Map; import javolution.util.FastList; import javolution.util.FastMap; import org.jwebsocket.plugins.rpc.CommonRpcPlugin; import org.jwebsocket.plugins.rpc.MethodMatcher; import org.jwebsocket.token.Token; import org.jwebsocket.token.TokenFactory; public class RPCPlugin { private static boolean annotationAllowed = true ; public static void setAnnotationAllowed (boolean aAnnotationAllowedValue){ annotationAllowed = aAnnotationAllowedValue; } private static Map<String, Map<String, List<MethodRPCCallable>>> mListOfMethod = new FastMap<String, Map<String, List<MethodRPCCallable>>>(); /** * Add a Method to the list of rpc granted. rrpc authorization is default: false. * @param aMethod */ public static void addRrpcMethod (Method aMethod) { addRrpcMethod (aMethod, false); } /** * Add a Method to the list of rrpc granted method. * If aRrpcCallable is true, allow other client to access to this method using a rrpc. * If false, only server rpc will be allowed * @param aMethod * @param aRrpcCallable */ public synchronized static void addRrpcMethod (Method aMethod, boolean aRrpcCallable) { String lClassName = aMethod.getDeclaringClass().getName(); if (!mListOfMethod.containsKey(lClassName)) { mListOfMethod.put(lClassName, new FastMap<String, List<MethodRPCCallable>>()); } Map<String, List<MethodRPCCallable>> lMap = mListOfMethod.get(lClassName) ; if (!lMap.containsKey(aMethod.getName())) { lMap.put(aMethod.getName(), new FastList<MethodRPCCallable>()); } List<MethodRPCCallable> lList = lMap.get(aMethod.getName()); MethodRPCCallable lMethodRPCCallable = new MethodRPCCallable(aMethod, aRrpcCallable); if(!lList.contains(lMethodRPCCallable)) { lList.add(lMethodRPCCallable); } } /** * Process a rrpc call. * TODO: doesn't send back any answer. Do smthg with lMsg & lResponseToken. * @param aClassName * @param aMethodName * @param aArgs */ public static Token processRrpc(String aClassName, String aMethodName, List aArgs, String aSourceId) { boolean lRrpcFromServer = CommonRpcPlugin.SERVER_ID.equals(aSourceId); Token lResponseToken = null; String lMsg = ""; if (aClassName != null && aMethodName != null) { if (mListOfMethod.containsKey(aClassName)) { if (mListOfMethod.get(aClassName).containsKey(aMethodName)) return call(aClassName, aMethodName, aArgs, lRrpcFromServer); } if (annotationAllowed){ //We try to load the method and check if it has the annotation try { Class lClass = Class.forName(aClassName); Method[] lMethods = lClass.getMethods(); for (Method lMethod : lMethods) { if (lMethod.getName().equals(aMethodName) && lMethod.isAnnotationPresent(RPCCallable.class)) { addRrpcMethod(lMethod, lMethod.getAnnotation(RPCCallable.class).C2CAuthorized()); //Add the method to the list to grant a faster access. MethodMatcher lMethodMatcher = new MethodMatcher(lMethod); if (lMethodMatcher.isMethodMatchingAgainstParameter(aArgs)) { lResponseToken = call(aClassName, aMethodName, aArgs, lRrpcFromServer); } } } if (lResponseToken != null) { return lResponseToken; } } catch (ClassNotFoundException ex) { lMsg = "ClassNotFoundException calling '" + aMethodName + "' for class " + aClassName + ": " + ex.getMessage(); } } else { lMsg = "Class not found in the list (annotation are not allowed) calling '" + aMethodName + "' for class " + aClassName + ": " ; } } if (lMsg.equals("")) { lMsg = "ClassName or Method name is probably null calling '" + aMethodName + "' for class " + aClassName + ": " ; } lResponseToken = TokenFactory.createToken(CommonRpcPlugin.NS_RPC_DEFAULT, CommonRpcPlugin.RPC_TYPE); lResponseToken.setString("msg", lMsg); return lResponseToken; } private static Token call (String aClassName, String aMethodName, List aArgs, boolean lRrpcFromServer) { Token lResponseToken = TokenFactory.createToken(CommonRpcPlugin.NS_RPC_DEFAULT, CommonRpcPlugin.RPC_TYPE); String lMsg = ""; List<MethodRPCCallable> lListOfMethod = mListOfMethod.get(aClassName).get(aMethodName); try { for (MethodRPCCallable lMethod : lListOfMethod) { MethodMatcher lMethodMatcher = new MethodMatcher(lMethod.getMethod()); //If lArg is not null, means the method match if (lMethodMatcher.isMethodMatchingAgainstParameter(aArgs)) { if (lRrpcFromServer || (!lRrpcFromServer && lMethod.isRrpcCallable())) { //We cast the intance to the correct class. Object lObj = lMethod.getMethod().invoke(null, lMethodMatcher.getMethodParameters()); lResponseToken.setValidated("result", lObj); return lResponseToken; } else { lMsg = "Only the server can invoke this method. Right isn't granted for a C2C call (only S2C)."; break; } } } } catch (IllegalArgumentException ex) { lMsg = "IllegalAccessException calling '" + aMethodName + "' for class " + aClassName + ": " + ex.getMessage(); } catch (IllegalAccessException ex) { lMsg = "IllegalAccessException calling '" + aMethodName + "' for class " + aClassName + ": " + ex.getMessage(); } catch (InvocationTargetException ex) { lMsg = "InvocationTargetException calling '" + aMethodName + "' for class " + aClassName + ": " + ex.getMessage(); } catch (Exception ex) { lMsg = "InvocationTargetException calling '" + aMethodName + "' for class " + aClassName + ": " + ex.getMessage(); } lResponseToken.setInteger("code", -1); lResponseToken.setString("msg", lMsg); return lResponseToken; } private static class MethodRPCCallable { private boolean rrpcCallable; private Method method; MethodRPCCallable (Method aMethod, boolean aRrpcCallable) { method = aMethod ; rrpcCallable = aRrpcCallable ; } public boolean isRrpcCallable() { return rrpcCallable; } public Method getMethod() { return method; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((method == null) ? 0 : method.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; MethodRPCCallable other = (MethodRPCCallable) obj; if (method == null) { if (other.method != null) return false; } else if (!method.equals(other.method)) return false; return true; } } }