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