001 package biz.hammurapi.remoting; 002 003 import java.io.Serializable; 004 import java.lang.reflect.InvocationTargetException; 005 import java.lang.reflect.Method; 006 007 import org.apache.xmlbeans.XmlObject; 008 import org.w3c.dom.Element; 009 import org.w3c.dom.Node; 010 011 import biz.hammurapi.convert.ConvertingService; 012 import biz.hammurapi.invocation.Invocation.Arg; 013 import biz.hammurapi.invocation.Invocation.State; 014 import biz.hammurapi.xml.dom.AbstractDomObject; 015 import biz.hammurapi.xml.dom.DOMUtils; 016 import biz.hammurapi.xml.dom.DomSerializable; 017 import biz.hammurapi.xmlbeans.XmlObjectSerializable; 018 019 /** 020 * Method invocation. 021 * @author Pavel 022 */ 023 public class Invocation implements DomSerializable, Serializable, XmlObjectSerializable { 024 025 /** 026 * Interface to inject client state into the server 027 * object before invocation and reset it after. 028 * Implementations shall store state in a thread local 029 * variable. 030 * @author Pavel 031 * 032 */ 033 public interface Stateful { 034 035 /** 036 * Sets client state before method invocation. 037 * @param state 038 */ 039 void setState(Object state); 040 041 /** 042 * Resets client state after invocation. 043 * @param state 044 */ 045 void resetState(); 046 } 047 048 private Object state; 049 private String methodName; 050 private String[] parameterTypes; 051 private Object[] args; 052 private String declaringClass; 053 private String id; 054 055 public Invocation(Object state, String declaringClass, String methodName, String[] parameterTypes, Object[] args, String id) { 056 this.state = state; 057 this.declaringClass = declaringClass; 058 this.methodName = methodName; 059 this.parameterTypes = parameterTypes; 060 this.args = args; 061 this.id = id; 062 } 063 064 public Invocation(Object state, java.lang.reflect.Method method, Object[] args, String id) { 065 this.state = state; 066 this.declaringClass = method.getDeclaringClass().getName(); 067 this.methodName = method.getName(); 068 Class[] mpt = method.getParameterTypes(); 069 this.parameterTypes = new String[mpt.length]; 070 for (int i=0; i<mpt.length; ++i) { 071 parameterTypes[i] = mpt[i].getName(); 072 } 073 this.args = args; 074 this.id = id; 075 } 076 077 public Object getState() { 078 return state; 079 } 080 081 public String getId() { 082 return id; 083 } 084 085 public String getMethodName() { 086 return methodName; 087 } 088 089 public String getDeclaringClass() { 090 return declaringClass; 091 } 092 093 public String[] getParameterTypes() { 094 return parameterTypes; 095 } 096 097 public Object[] getArguments() { 098 return args; 099 } 100 101 public void toDom(Element holder) { 102 holder.setAttribute("method-name", methodName); 103 if (declaringClass!=null) { 104 holder.setAttribute("declaring-class", declaringClass); 105 } 106 holder.setAttribute("type", getClass().getName()); 107 108 if (id!=null) { 109 holder.setAttribute("id", id); 110 } 111 112 if (state!=null) { 113 DOMUtils.toDom(state, "state", holder); 114 } 115 116 if (parameterTypes!=null) { 117 for (int i=0; i<parameterTypes.length; ++i) { 118 AbstractDomObject.addTextElement(holder, "parameter-type", parameterTypes[i]); 119 } 120 } 121 122 if (args!=null) { 123 for (int i=0; i<args.length; ++i) { 124 if (args[i]==null) { 125 AbstractDomObject.addElement(holder, "argument").setAttribute("is-null", "true"); 126 } else { 127 DOMUtils.toDom(args[i], "argument", holder); 128 } 129 } 130 } 131 132 } 133 134 public XmlObject toXmlObject() { 135 biz.hammurapi.invocation.InvocationDocument ret = biz.hammurapi.invocation.InvocationDocument.Factory.newInstance(); 136 biz.hammurapi.invocation.Invocation invocation = ret.addNewInvocation(); 137 if (declaringClass!=null) { 138 invocation.setDeclaringClass(declaringClass); 139 } 140 141 invocation.setMethodName(methodName); 142 143 if (id!=null) { 144 invocation.setId(id); 145 } 146 147 if (parameterTypes!=null) { 148 for (int i=0; i<parameterTypes.length; ++i) { 149 invocation.addParameterType(parameterTypes[i]); 150 } 151 } 152 153 if (state!=null) { 154 State xmlState = invocation.addNewState(); 155 if (state instanceof XmlObject) { 156 xmlState.set((XmlObject) state); 157 } else { 158 Node domNode = xmlState.getDomNode(); 159 DomSerializable sds = (DomSerializable) ConvertingService.convert(state, DomSerializable.class); 160 sds.toDom((Element) domNode); 161 } 162 } 163 164 if (args!=null) { 165 for (int i=0; i<args.length; ++i) { 166 Arg arg = invocation.addNewArg(); 167 if (args[i] instanceof XmlObject) { 168 arg.set((XmlObject) args[i]); 169 } else { 170 Node domNode = arg.getDomNode(); 171 DomSerializable ads = (DomSerializable) ConvertingService.convert(args[i], DomSerializable.class); 172 ads.toDom((Element) domNode); 173 } 174 } 175 } 176 177 return ret; 178 } 179 180 /** 181 * Finds method to invoke. 182 * @param invocation 183 * @param classLoader Classloader, can be null. 184 * @return 185 * @throws ClassNotFoundException 186 */ 187 public static java.lang.reflect.Method findMethod(biz.hammurapi.invocation.Invocation invocation, ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException { 188 Class<? extends Object> declaringClass = classLoader==null ? Class.forName(invocation.getDeclaringClass()) : classLoader.loadClass(invocation.getDeclaringClass()); 189 String[] pta = invocation.getParameterTypeArray(); 190 Class[] parameterTypes = new Class[pta.length]; 191 for (int i=0; i<pta.length; ++i) { 192 if ("int".equals(pta[i])) { 193 parameterTypes[i] = int.class; 194 } else if ("short".equals(pta[i])) { 195 parameterTypes[i] = short.class; 196 } else if ("long".equals(pta[i])) { 197 parameterTypes[i] = long.class; 198 } else if ("boolean".equals(pta[i])) { 199 parameterTypes[i] = boolean.class; 200 } else if ("byte".equals(pta[i])) { 201 parameterTypes[i] = byte.class; 202 } else if ("double".equals(pta[i])) { 203 parameterTypes[i] = double.class; 204 } else if ("float".equals(pta[i])) { 205 parameterTypes[i] = float.class; 206 } else if ("char".equals(pta[i])) { 207 parameterTypes[i] = char.class; 208 } else { 209 parameterTypes[i] = classLoader==null ? Class.forName(pta[i]) : classLoader.loadClass(pta[i]); 210 } 211 } 212 return declaringClass.getMethod(invocation.getMethodName(), parameterTypes); 213 } 214 215 /** 216 * Finds method to invoke. 217 * @param invocation 218 * @param classLoader Classloader, can be null. 219 * @return 220 * @throws ClassNotFoundException 221 */ 222 public java.lang.reflect.Method findMethod(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException { 223 Class<? extends Object> declaringClass = classLoader==null ? Class.forName(getDeclaringClass()) : classLoader.loadClass(getDeclaringClass()); 224 Class[] parameterTypes = new Class[this.parameterTypes.length]; 225 for (int i=0; i<this.parameterTypes.length; ++i) { 226 if ("int".equals(this.parameterTypes[i])) { 227 parameterTypes[i] = int.class; 228 } else if ("short".equals(this.parameterTypes[i])) { 229 parameterTypes[i] = short.class; 230 } else if ("long".equals(this.parameterTypes[i])) { 231 parameterTypes[i] = long.class; 232 } else if ("boolean".equals(this.parameterTypes[i])) { 233 parameterTypes[i] = boolean.class; 234 } else if ("byte".equals(this.parameterTypes[i])) { 235 parameterTypes[i] = byte.class; 236 } else if ("double".equals(this.parameterTypes[i])) { 237 parameterTypes[i] = double.class; 238 } else if ("float".equals(this.parameterTypes[i])) { 239 parameterTypes[i] = float.class; 240 } else if ("char".equals(this.parameterTypes[i])) { 241 parameterTypes[i] = char.class; 242 } else { 243 parameterTypes[i] = classLoader==null ? Class.forName(this.parameterTypes[i]) : classLoader.loadClass(this.parameterTypes[i]); 244 } 245 } 246 return declaringClass.getMethod(getMethodName(), parameterTypes); 247 } 248 249 /** 250 * Method for simple invocation. 251 * @param instance 252 * @return 253 * @throws IllegalAccessException 254 * @throws InvocationTargetException 255 * @throws ClassNotFoundException 256 * @throws NoSuchMethodException 257 */ 258 public Object invoke(Object instance) throws IllegalAccessException, InvocationTargetException, ClassNotFoundException, NoSuchMethodException { 259 if (instance instanceof Stateful) { 260 ((Stateful) instance).setState(state); 261 } 262 try { 263 Method method = findMethod(instance==null ? getClass().getClassLoader() : instance.getClass().getClassLoader()); 264 Object[] cArgs = args==null ? null : new Object[args.length]; 265 Class[] pTypes = method.getParameterTypes(); 266 for (int i=0; args!=null && i<args.length && i<pTypes.length; ++i) { 267 if (args[i]==null) { 268 cArgs[i]=null; 269 } else if (pTypes[i].isInstance(args[i])) { 270 cArgs[i]=args[i]; 271 } else { 272 cArgs[i]=ConvertingService.convert(args[i], pTypes[i]); 273 } 274 } 275 return method.invoke(instance, cArgs); 276 } finally { 277 if (instance instanceof Stateful) { 278 ((Stateful) instance).resetState(); 279 } 280 } 281 } 282 283 }