KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > invocation > MarshalledInvocation


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.invocation;
23
24 import java.io.DataOutputStream JavaDoc;
25 import java.io.ByteArrayOutputStream JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.lang.reflect.Method JavaDoc;
28 import java.security.DigestOutputStream JavaDoc;
29 import java.security.MessageDigest JavaDoc;
30 import java.security.Principal JavaDoc;
31 import java.security.PrivilegedAction JavaDoc;
32 import java.security.AccessController JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.Iterator JavaDoc;
35 import java.util.HashMap JavaDoc;
36 import java.util.WeakHashMap JavaDoc;
37 import javax.transaction.Transaction JavaDoc;
38
39 /**
40  * The MarshalledInvocation is an invocation that travels. As such it serializes
41  * its payload because of lack of ClassLoader visibility.
42  * As such it contains Marshalled data representing the byte[] of the Invocation object it extends
43  * Besides handling the specifics of "marshalling" the payload, which could be done at the Invocation level
44  * the Marshalled Invocation can hold optimization and needed code for distribution for example the
45  * TransactionPropagationContext which is a serialization of the TX for distribution purposes as
46  * well as the "hash" for the methods that we send, as opposed to sending Method objects.
47  * Serialization "optimizations" should be coded here in the externalization implementation of the class
48  *
49  * @author <a HREF="mailto:marc@jboss.org">Marc Fleury</a>
50  * @author Bill.Burke@jboss.org
51  * @author Scott.Stark@jboss.org
52  * @version $Revision: 43739 $
53  */

54 public class MarshalledInvocation
55         extends Invocation
56         implements java.io.Externalizable JavaDoc
57 {
58    // Constants -----------------------------------------------------
59

60    /** Serial Version Identifier. */
61    static final long serialVersionUID = -718723094688127810L;
62    /** A flag indicating if the full hash format that includes the interface
63     * should be used
64     */

65    static boolean useFullHashMode = true;
66    /** WeakHashMap<Class, HashMap<String, Long>> of declaring class to hashes */
67    static Map JavaDoc hashMap = new WeakHashMap JavaDoc();
68
69    /** The Transaction Propagation Context for distribution */
70    protected Object JavaDoc tpc;
71
72    /** The Map of methods used by this Invocation */
73    protected transient Map JavaDoc methodMap;
74
75    // These are here to avoid unneeded hash lookup
76
protected transient long methodHash = 0;
77    protected transient MarshalledValue marshalledArgs = null;
78
79    /** Get the full hash mode flag.
80     * @return the full hash mode flag.
81     */

82    public static boolean getUseFullHashMode()
83    {
84       return useFullHashMode;
85    }
86    /** Set the full hash mode flag. When true, method hashes are calculated
87     * using the getFullInterfaceHashes which is able to differentiate methods
88     * by declaring class, return value, name and arg signature, and exceptions.
89     * Otherwise, the getInterfaceHashes method uses, and this is only able to
90     * differentiate classes by return value, name and arg signature. A
91     * useFullHashMode = false is compatible with 3.2.3 and earlier.
92     *
93     * This needs to be set consistently on the server and the client.
94     *
95     * @param flag the full method hash calculation mode flag.
96     */

97    public static void setUseFullHashMode(boolean flag)
98    {
99       useFullHashMode = flag;
100    }
101
102    /** Calculate method hashes. This algo is taken from RMI with the
103     * method string built from the method name + parameters + return type. Note
104     * that this is not able to distinguish type compatible methods from
105     * different interfaces.
106     *
107     * @param intf - the class/interface to calculate method hashes for.
108     * @return Map<String, Long> mapping of method string to method desc hash
109     */

110    public static Map JavaDoc getInterfaceHashes(Class JavaDoc intf)
111    {
112       // Create method hashes
113
Method JavaDoc[] methods = null;
114       if( System.getSecurityManager() != null )
115       {
116          DeclaredMethodsAction action = new DeclaredMethodsAction(intf);
117          methods = (Method JavaDoc[]) AccessController.doPrivileged(action);
118       }
119       else
120       {
121          methods = intf.getDeclaredMethods();
122       }
123
124       HashMap JavaDoc map = new HashMap JavaDoc();
125       for (int i = 0; i < methods.length; i++)
126       {
127          Method JavaDoc method = methods[i];
128          Class JavaDoc[] parameterTypes = method.getParameterTypes();
129          String JavaDoc methodDesc = method.getName() + "(";
130          for (int j = 0; j < parameterTypes.length; j++)
131          {
132             methodDesc += getTypeString(parameterTypes[j]);
133          }
134          methodDesc += ")" + getTypeString(method.getReturnType());
135
136          try
137          {
138             long hash = 0;
139             ByteArrayOutputStream JavaDoc bytearrayoutputstream = new ByteArrayOutputStream JavaDoc(512);
140             MessageDigest JavaDoc messagedigest = MessageDigest.getInstance("SHA");
141             DataOutputStream JavaDoc dataoutputstream = new DataOutputStream JavaDoc(new DigestOutputStream JavaDoc(bytearrayoutputstream, messagedigest));
142             dataoutputstream.writeUTF(methodDesc);
143             dataoutputstream.flush();
144             byte abyte0[] = messagedigest.digest();
145             for (int j = 0; j < Math.min(8, abyte0.length); j++)
146                hash += (long) (abyte0[j] & 0xff) << j * 8;
147             map.put(method.toString(), new Long JavaDoc(hash));
148          }
149          catch (Exception JavaDoc e)
150          {
151             e.printStackTrace();
152          }
153       }
154
155       return map;
156    }
157
158    /** Calculate method full hashes. This algo is taken from RMI with the full
159     * method string built from the method toString() which includes the
160     * modifiers, return type, declaring class, name, parameters and exceptions.
161     *
162     * @param intf - the class/interface to calculate method hashes for.
163     * @return Map<String, Long> mapping of method string to method desc hash
164     */

165    public static Map JavaDoc getFullInterfaceHashes(Class JavaDoc intf)
166    {
167       // Create method hashes
168
Method JavaDoc[] methods = null;
169       if( System.getSecurityManager() != null )
170       {
171          DeclaredMethodsAction action = new DeclaredMethodsAction(intf);
172          methods = (Method JavaDoc[]) AccessController.doPrivileged(action);
173       }
174       else
175       {
176          methods = intf.getDeclaredMethods();
177       }
178
179       HashMap JavaDoc map = new HashMap JavaDoc();
180       for (int i = 0; i < methods.length; i++)
181       {
182          Method JavaDoc method = methods[i];
183          String JavaDoc methodDesc = method.toString();
184
185          try
186          {
187             long hash = 0;
188             ByteArrayOutputStream JavaDoc bytearrayoutputstream = new ByteArrayOutputStream JavaDoc(512);
189             MessageDigest JavaDoc messagedigest = MessageDigest.getInstance("SHA");
190             DataOutputStream JavaDoc dataoutputstream = new DataOutputStream JavaDoc(new DigestOutputStream JavaDoc(bytearrayoutputstream, messagedigest));
191             dataoutputstream.writeUTF(methodDesc);
192             dataoutputstream.flush();
193             byte abyte0[] = messagedigest.digest();
194             for (int j = 0; j < Math.min(8, abyte0.length); j++)
195                hash += (long) (abyte0[j] & 0xff) << j * 8;
196             map.put(method.toString(), new Long JavaDoc(hash));
197          }
198          catch (Exception JavaDoc e)
199          {
200             e.printStackTrace();
201          }
202       }
203
204       return map;
205    }
206
207    /** Calculate method hashes. This algo is taken from RMI with the full
208     * method string taken from the method.toString to include the declaring
209     * class.
210     *
211     * @param c the class/interface to calculate method hashes for.
212     * @return Map<Long, Method> mapping of method hash to the Method object.
213     */

214    public static Map JavaDoc methodToHashesMap(Class JavaDoc c)
215    {
216       // Create method hashes
217
Method JavaDoc[] methods = null;
218       if( System.getSecurityManager() != null )
219       {
220          DeclaredMethodsAction action = new DeclaredMethodsAction(c);
221          methods = (Method JavaDoc[]) AccessController.doPrivileged(action);
222       }
223       else
224       {
225          methods = c.getDeclaredMethods();
226       }
227
228       HashMap JavaDoc map = new HashMap JavaDoc();
229       for (int i = 0; i < methods.length; i++)
230       {
231          Method JavaDoc method = methods[i];
232          String JavaDoc methodDesc = method.toString();
233
234          try
235          {
236             long hash = 0;
237             ByteArrayOutputStream JavaDoc bytearrayoutputstream = new ByteArrayOutputStream JavaDoc(512);
238             MessageDigest JavaDoc messagedigest = MessageDigest.getInstance("SHA");
239             DataOutputStream JavaDoc dataoutputstream = new DataOutputStream JavaDoc(new DigestOutputStream JavaDoc(bytearrayoutputstream, messagedigest));
240             dataoutputstream.writeUTF(methodDesc);
241             dataoutputstream.flush();
242             byte abyte0[] = messagedigest.digest();
243             for (int j = 0; j < Math.min(8, abyte0.length); j++)
244                hash += (long) (abyte0[j] & 0xff) << j * 8;
245             map.put(new Long JavaDoc(hash), method);
246          }
247          catch (Exception JavaDoc e)
248          {
249             e.printStackTrace();
250          }
251       }
252
253       return map;
254    }
255
256    static String JavaDoc getTypeString(Class JavaDoc cl)
257    {
258       if (cl == Byte.TYPE)
259       {
260          return "B";
261       }
262       else if (cl == Character.TYPE)
263       {
264          return "C";
265       }
266       else if (cl == Double.TYPE)
267       {
268          return "D";
269       }
270       else if (cl == Float.TYPE)
271       {
272          return "F";
273       }
274       else if (cl == Integer.TYPE)
275       {
276          return "I";
277       }
278       else if (cl == Long.TYPE)
279       {
280          return "J";
281       }
282       else if (cl == Short.TYPE)
283       {
284          return "S";
285       }
286       else if (cl == Boolean.TYPE)
287       {
288          return "Z";
289       }
290       else if (cl == Void.TYPE)
291       {
292          return "V";
293       }
294       else if (cl.isArray())
295       {
296          return "[" + getTypeString(cl.getComponentType());
297       }
298       else
299       {
300          return "L" + cl.getName().replace('.', '/') + ";";
301       }
302    }
303
304    /*
305    * The use of hashCode is not enough to differenciate methods
306    * we override the hashCode
307    *
308    * The hashes are cached in a static for efficiency
309    */

310    public static long calculateHash(Method JavaDoc method)
311    {
312       Map JavaDoc methodHashes = (Map JavaDoc) hashMap.get(method.getDeclaringClass());
313
314       if (methodHashes == null)
315       {
316          // Add the method hashes for the class
317
if( useFullHashMode == true )
318             methodHashes = getFullInterfaceHashes(method.getDeclaringClass());
319          else
320             methodHashes = getInterfaceHashes(method.getDeclaringClass());
321          synchronized (hashMap)
322          {
323             hashMap.put(method.getDeclaringClass(), methodHashes);
324          }
325       }
326
327       Long JavaDoc hash = (Long JavaDoc) methodHashes.get(method.toString());
328       return hash.longValue();
329    }
330
331
332    /** Remove all method hashes for the declaring class
333     * @param declaringClass a class for which a calculateHash(Method) was called
334     */

335    public static void removeHashes(Class JavaDoc declaringClass)
336    {
337       synchronized (hashMap)
338       {
339          hashMap.remove(declaringClass);
340       }
341    }
342
343    // Constructors --------------------------------------------------
344
public MarshalledInvocation()
345    {
346       // For externalization to work
347
}
348
349    public MarshalledInvocation(Invocation invocation)
350    {
351       this.payload = invocation.payload;
352       this.as_is_payload = invocation.as_is_payload;
353       this.method = invocation.getMethod();
354       this.objectName = invocation.getObjectName();
355       this.args = invocation.getArguments();
356       this.invocationType = invocation.getType();
357    }
358
359
360    public MarshalledInvocation(
361            Object JavaDoc id,
362            Method JavaDoc m,
363            Object JavaDoc[] args,
364            Transaction JavaDoc tx,
365            Principal JavaDoc identity,
366            Object JavaDoc credential)
367    {
368       super(id, m, args, tx, identity, credential);
369    }
370
371    public Method JavaDoc getMethod()
372    {
373       if (this.method != null)
374          return this.method;
375
376       // Try the hash, the methodMap should be set
377
this.method = (Method JavaDoc) methodMap.get(new Long JavaDoc(methodHash));
378
379       // Keep it in the payload
380
if (this.method == null)
381       {
382          throw new IllegalStateException JavaDoc("Failed to find method for hash:" + methodHash + " available=" + methodMap);
383       }
384       return this.method;
385    }
386
387    public void setMethodMap(Map JavaDoc methods)
388    {
389       methodMap = methods;
390    }
391
392    // The transaction propagation context for the Invocation that travels (distributed tx only)
393
public void setTransactionPropagationContext(Object JavaDoc tpc)
394    {
395       this.tpc = tpc;
396    }
397
398    public Object JavaDoc getTransactionPropagationContext()
399    {
400       return tpc;
401    }
402
403    // Invocation overwrite -----------------------------------------
404

405    /** A Marshalled invocation has serialized data in the form of
406     MarshalledValue objects. We overwrite the "getValue" to deserialize the
407     data, this assume that the thread context class loader has visibility
408     on the classes.
409     */

410    public Object JavaDoc getValue(Object JavaDoc key)
411    {
412
413       Object JavaDoc value = super.getValue(key);
414
415       // The map may contain serialized values of the fields
416
if (value instanceof MarshalledValue)
417       {
418          try
419          {
420             MarshalledValue mv = (MarshalledValue) value;
421             value = mv.get();
422          }
423          catch (Exception JavaDoc e)
424          {
425             JBossLazyUnmarshallingException ise = new JBossLazyUnmarshallingException("getValue failed");
426             ise.initCause(e);
427             throw ise;
428          }
429       }
430       return value;
431    }
432
433    /** A Marshalled invocation has serialized data in the form of
434     MarshalledValue objects. We overwrite the "getValue" to deserialize the
435     data, this assume that the thread context class loader has visibility
436     on the classes.
437     */

438    public Object JavaDoc getPayloadValue(Object JavaDoc key)
439    {
440
441       Object JavaDoc value = getPayload().get(key);
442
443       // The map may contain serialized values of the fields
444
if (value instanceof MarshalledValue)
445       {
446          try
447          {
448             MarshalledValue mv = (MarshalledValue) value;
449             value = mv.get();
450          }
451          catch (Exception JavaDoc e)
452          {
453             JBossLazyUnmarshallingException ise = new JBossLazyUnmarshallingException("getPayloadValue failed");
454             ise.initCause(e);
455             throw ise;
456          }
457       }
458       return value;
459    }
460
461    public Object JavaDoc[] getArguments()
462    {
463       if (this.args == null)
464       {
465          try
466          {
467             this.args = (Object JavaDoc[]) marshalledArgs.get();
468          }
469          catch (Exception JavaDoc e)
470          {
471             JBossLazyUnmarshallingException ise = new JBossLazyUnmarshallingException("getArguments failed");
472             ise.initCause(e);
473             throw ise;
474          }
475       }
476       return args;
477    }
478
479    // Externalizable implementation ---------------------------------
480
public void writeExternal(java.io.ObjectOutput JavaDoc out)
481            throws IOException JavaDoc
482    {
483       // TODO invocationType should be removed from as is payload
484
// for now, it is in there for binary compatibility
485
getAsIsPayload().put(InvocationKey.TYPE, invocationType);
486       // FIXME marcf: the "specific" treatment of Transactions should be abstracted.
487
// Write the TPC, not the local transaction
488
out.writeObject(tpc);
489
490       long methodHash = this.methodHash;
491       if(methodHash == 0)
492       {
493          methodHash = calculateHash(this.method);
494       }
495
496       out.writeLong(methodHash);
497
498       out.writeObject(this.objectName);
499
500       if(this.args == null && this.marshalledArgs != null)
501       {
502          out.writeObject(this.marshalledArgs);
503       }
504       else
505       {
506          out.writeObject(new MarshalledValue(this.args));
507       }
508
509
510       // Write out payload hashmap
511
// Don't use hashmap serialization to avoid not-needed data being
512
// marshalled
513
// The map contains only serialized representations of every other object
514
// Everything else is possibly tied to classloaders that exist inside the
515
// server but not in the generic JMX land. they will travel in the payload
516
// as MarshalledValue objects, see the Invocation getter logic
517
//
518
if (payload == null)
519          out.writeInt(0);
520       else
521       {
522          out.writeInt(payload.size());
523          Iterator JavaDoc keys = payload.keySet().iterator();
524          while (keys.hasNext())
525          {
526             Object JavaDoc currentKey = keys.next();
527
528             // This code could be if (object.getClass().getName().startsWith("java")) then don't serialize.
529
// Bench the above for speed.
530

531             out.writeObject(currentKey);
532             Object JavaDoc value = payload.get(currentKey);
533             // no reason to marshall an already marshalled value
534
if(!(value instanceof MarshalledValue))
535             {
536                value = new MarshalledValue(value);
537             }
538
539             out.writeObject(value);
540          }
541       }
542
543       // This map is "safe" as is
544
//out.writeObject(as_is_payload);
545
if (as_is_payload == null)
546          out.writeInt(0);
547       else
548       {
549          out.writeInt(as_is_payload.size());
550
551          Iterator JavaDoc keys = as_is_payload.keySet().iterator();
552          while (keys.hasNext())
553          {
554             Object JavaDoc currentKey = keys.next();
555             out.writeObject(currentKey);
556             out.writeObject(as_is_payload.get(currentKey));
557          }
558       }
559    }
560
561    public void readExternal(java.io.ObjectInput JavaDoc in)
562            throws IOException JavaDoc, ClassNotFoundException JavaDoc
563    {
564       tpc = in.readObject();
565       this.methodHash = in.readLong();
566
567       this.objectName = in.readObject();
568
569       marshalledArgs = (MarshalledValue) in.readObject();
570
571       int payloadSize = in.readInt();
572       if (payloadSize > 0)
573       {
574          payload = new HashMap JavaDoc();
575          for (int i = 0; i < payloadSize; i++)
576          {
577             Object JavaDoc key = in.readObject();
578             Object JavaDoc value = in.readObject();
579             payload.put(key, value);
580          }
581       }
582
583       int as_is_payloadSize = in.readInt();
584       if (as_is_payloadSize > 0)
585       {
586          as_is_payload = new HashMap JavaDoc();
587          for (int i = 0; i < as_is_payloadSize; i++)
588          {
589             Object JavaDoc key = in.readObject();
590             Object JavaDoc value = in.readObject();
591             as_is_payload.put(key, value);
592          }
593       }
594       // TODO invocationType should be removed from as is payload
595
// for now, it is in there for binary compatibility
596
invocationType = (InvocationType)getAsIsValue(InvocationKey.TYPE);
597    }
598
599    private static class DeclaredMethodsAction implements PrivilegedAction JavaDoc
600    {
601       Class JavaDoc c;
602       DeclaredMethodsAction(Class JavaDoc c)
603       {
604          this.c = c;
605       }
606       public Object JavaDoc run()
607       {
608          Method JavaDoc[] methods = c.getDeclaredMethods();
609          c = null;
610          return methods;
611       }
612    }
613 }
614
Popular Tags