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    }