View Javadoc

1   /*
2    * Copyright (c) 1998-2003 The Jgroup Team.
3    *
4    * This program is free software; you can redistribute it and/or modify
5    * it under the terms of the GNU Lesser General Public License version 2 as
6    * published by the Free Software Foundation.
7    *
8    * This program is distributed in the hope that it will be useful,
9    * but WITHOUT ANY WARRANTY; without even the implied warranty of
10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11   * GNU Lesser General Public License for more details.
12   *
13   * You should have received a copy of the GNU Lesser General Public License
14   * along with this program; if not, write to the Free Software
15   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16   *
17   */
18  package jgroup.relacs.gmi;
19  
20  import java.io.Externalizable;
21  import java.io.IOException;
22  import java.io.ObjectInput;
23  import java.io.ObjectInputStream;
24  import java.io.ObjectOutput;
25  import java.io.ObjectOutputStream;
26  import java.io.OutputStream;
27  import java.lang.reflect.Method;
28  import java.lang.reflect.Proxy;
29  import java.rmi.RemoteException;
30  
31  import jgroup.core.GroupManager;
32  import jgroup.core.IID;
33  import jgroup.core.JgroupException;
34  import jgroup.core.ExternalGMIService.BootstrapInvocationHandler;
35  import jgroup.relacs.gm.TimestampService;
36  import jgroup.relacs.gm.TimestampService.Timestamp;
37  import jgroup.relacs.rmi.IIDFactory;
38  import jgroup.util.log.Eventlogger;
39  import net.jini.jeri.ObjectEndpoint;
40  import net.jini.jeri.OutboundRequest;
41  import net.jini.jeri.OutboundRequestIterator;
42  
43  import org.apache.log4j.Logger;
44  
45  import com.sun.jini.jeri.internal.runtime.Util;
46  
47  
48  /**
49   * Invocation handler (the proxy) at client side.
50   * Marshalls call and passes it down to lower layers.
51   *
52   * @author Tor Arve Stangeland
53   * @author Hein Meling
54   */
55  public class GroupInvocationHandler
56    implements BootstrapInvocationHandler, Externalizable
57  {
58  
59    ////////////////////////////////////////////////////////////////////////////////////////////
60    // Logger
61    ////////////////////////////////////////////////////////////////////////////////////////////
62  
63    /** Obtain logger for this class */
64    private static final Logger log = Logger.getLogger(GroupInvocationHandler.class);
65  
66  
67    ////////////////////////////////////////////////////////////////////////////////////////////
68    // Static section
69    ////////////////////////////////////////////////////////////////////////////////////////////
70  
71    private static final long serialVersionUID = 6090074369501693886L;
72  
73    /** Invocation handler type */
74    public static final String TYPE = "GroupInvocationHandler";
75  
76  
77    ////////////////////////////////////////////////////////////////////////////////////////////
78    // Fields
79    ////////////////////////////////////////////////////////////////////////////////////////////
80  
81    /** GroupEndPoint associated with this invocation handler */
82    private GroupEndPoint gep;
83  
84    /** Caching of method hash */
85    private MethodTable methodTable;
86  
87    /** IID generator factory */
88    private IIDFactory generator;
89  
90    /** The view identifier of the server group known to the client */
91    private long clientViewId = -1;
92  
93    /** The timestamp service obtained from the client group */
94    private TimestampService timestampService;
95  
96  
97    ////////////////////////////////////////////////////////////////////////////////////////////
98    // Constructors
99    ////////////////////////////////////////////////////////////////////////////////////////////
100 
101   /**
102    * Constructor for externalization
103    */
104   public GroupInvocationHandler() {}
105 
106   /**
107    * Construct a new GroupInvocationHandler for the given group ObjectEndpoint
108    * @param oe the group object endpoint
109    * @param methodTable the table of remote methods accessible through this
110    *   invocation handler. 
111    */
112   public GroupInvocationHandler(ObjectEndpoint oe, MethodTable methodTable)
113   {
114     this.gep = (GroupEndPoint) oe;
115     this.methodTable = methodTable;
116     generator = new IIDFactory();
117   }
118 
119 
120   ////////////////////////////////////////////////////////////////////////////////////////////
121   // Methods from InvocationHandler
122   ////////////////////////////////////////////////////////////////////////////////////////////
123 
124   /* (non-javadoc)
125    * @see java.lang.reflect.InvocationHandler#invoke(Object, Method, Object[])
126    */
127   public Object invoke(Object proxy, Method method, Object[] args)
128     throws Exception
129   {
130     if (method.getDeclaringClass() == Object.class) {
131       return invokeObjectMethod(proxy, method, args);
132     } else 
133       return invokeRemoteMethod(proxy, method, args);    
134   }
135 
136 
137   ////////////////////////////////////////////////////////////////////////////////////////////
138   // Instance methods
139   ////////////////////////////////////////////////////////////////////////////////////////////
140 
141   /**
142    * Handles java.lang.Object methods.
143    */
144   private Object invokeObjectMethod(Object proxy, Method method, Object[] args) 
145   {
146     String name = method.getName();
147 
148     if (name.equals("hashCode")) {
149       return new Integer(hashCode());      
150     } else if (name.equals("equals")) {
151       Object obj = args[0];
152       boolean b = (proxy == obj) ||
153         ((obj != null) && Util.sameProxyClass(proxy, obj) &&
154           equals(Proxy.getInvocationHandler(obj)));
155       return Boolean.valueOf(b);
156     } else if (name.equals("toString")) {
157       return proxyToString(proxy);
158     } else {
159       throw new IllegalArgumentException("Unexpected object method: " + method);
160     }
161   }
162 
163 
164   /**
165    * Returns a hash code value for this invocation handler.
166    */
167   public int hashCode() 
168   {
169     return gep.hashCode();  
170   }
171 
172 
173   /**
174    * Returns a string representation for a proxy that uses this invocation
175    * handler.
176    **/
177   private String proxyToString(Object proxy) 
178   {
179     Class[] interfaces = proxy.getClass().getInterfaces();
180 
181     if (interfaces.length == 0) {
182       return "Proxy[" + this + "]";
183     }
184     String iface = interfaces[0].getName();
185     int dot = iface.lastIndexOf('.');
186 
187     if (dot >= 0) {
188       iface = iface.substring(dot + 1);
189     }
190     return "Proxy[" + iface + "," + gep + "]";
191   }
192 
193 
194   /**
195    * Handles remote methods.
196    */
197   private Object invokeRemoteMethod(Object proxy, Method method, Object[] args)
198     throws Exception
199   {
200     IID iid = generator.getId();
201     MethodDetails info = methodTable.get(method);
202     boolean invoked = false;
203     InvocationResult result = null;
204 
205     do {
206       // Get request iterator
207       OutboundRequestIterator iter = gep.newCall(info.getInvocationSemantics());
208       while (result == null && iter.hasNext()) {
209         try {
210           result = invokeRequest(iid, info, args, iter.next());
211         } catch (IOException ioex) {
212           /*
213            * We just log it; the GroupEndPoint is responsible for
214            * checking if group members are reachable, and updating
215            * them if they are not.
216            */
217           if (Eventlogger.ENABLED)
218             Eventlogger.logEvent("FailureDetected");
219           if (log.isDebugEnabled())
220             log.debug("IOException caught", ioex);
221         }
222       }
223       
224       if (result != null)
225         invoked = true;
226     } while (!invoked); // Keep retrying until exception is thrown or we succeed
227     gep.updateClientView(result.getServerView());
228     clientViewId = result.getServerViewId();
229 //    gep.notifyView(clientViewId);
230     generator.addReply(iid);
231     return result.getResult();    
232   }
233 
234 
235   /**
236    * Try to invoke method using given request
237    * @param iid Invocation Identifier for invocation
238    * @param minfo Method to be invoked
239    * @param args Arguments to pass to method
240    * @param req OutboundRequest to use
241    */
242   protected InvocationResult invokeRequest(IID iid, MethodDetails minfo, Object[] args, OutboundRequest req)
243     throws IOException, JgroupException
244   {
245     if (log.isDebugEnabled())
246       log.debug("Invoking: " + minfo + " with iid=" + iid);
247 
248     OutputStream os = req.getRequestOutputStream();
249     /*
250      * Only the invocation semantics is written to the output stream,
251      * since this is required to be unwrapped at the receiver.
252      */
253     os.write(minfo.getInvocationSemantics().ordinal());
254     /*
255      * The following is written to an object output stream that is
256      * not unwrapped immediately at the receiver (in case it needs to
257      * be multicast to the other group members.)  That is, if the method
258      * needs to be multicasted, the following object stream is passed on
259      * to the other members without unmarshalling and remarshalling.
260      */
261     ObjectOutputStream out = new ObjectOutputStream(os);
262     // Write IID for this request
263     out.writeObject(iid);
264 
265     // Write the timestamp object
266     if (timestampService != null) {
267       synchronized (timestampService) {
268         // Write the timestamp object
269         Timestamp ts = timestampService.getTimestamp(gep.getGroupId());
270         if (log.isDebugEnabled())
271           log.debug("GroupInvocationHandler.invoke ts = " + ts + " on " + minfo);
272         out.writeObject(ts);
273       }
274     } else {
275       out.writeObject(null);
276     }
277     // Write the client-side view
278     out.writeLong(clientViewId);
279     // Write method hash
280     out.writeLong(minfo.getHash());    
281     // Write args
282     for (int i = 0; args != null && i < args.length; ++i) 
283       out.writeObject(args[i]);
284     out.flush();
285     out.close();
286 
287     if (log.isDebugEnabled())
288       log.debug("Data sent for " + minfo);
289 
290     // Execute call and await reply now that we have closed OutboundRequest.OutputStream.
291     RemoteException remex = gep.executeCall(req);
292     if (remex != null)
293       throw remex;
294 
295     // Unmarshall results
296     ObjectInputStream in = new ObjectInputStream(req.getResponseInputStream());
297     if (log.isDebugEnabled())
298       log.debug("Result received for " + minfo);
299     try {
300       return (InvocationResult) in.readObject();
301     } catch (ClassNotFoundException cnfex) {
302       throw new JgroupException("Returned class could not be deserialized!", cnfex);
303     }
304   }
305 
306 
307   public GroupEndPoint getGroupEndPoint()
308   {
309     return gep;
310   }
311 
312 
313   /**
314    * Initialize the timestamp service.  This is required when the proxy
315    * is being used by a replicated client group.
316    * 
317    * @param gm
318    *   The group manager of the client group.
319    */
320   public void setGroupManager(GroupManager gm)
321   {
322     timestampService = (TimestampService) gm.getService(TimestampService.class);
323   }
324 
325 
326   ////////////////////////////////////////////////////////////////////////////////////////////
327   // Methods from Externalization
328   ////////////////////////////////////////////////////////////////////////////////////////////
329 
330   /* (non-Javadoc)
331    * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
332    */
333   public void readExternal(ObjectInput in)
334     throws IOException, ClassNotFoundException
335   {
336     gep = new GroupEndPoint();
337     gep.readExternal(in);
338     methodTable = (MethodTable) in.readObject();
339     generator = new IIDFactory();
340   }
341 
342   /* (non-Javadoc)
343    * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
344    */
345   public void writeExternal(ObjectOutput out)
346     throws IOException
347   {
348     gep.writeExternal(out);
349     out.writeObject(methodTable);
350   }
351 
352 
353   ////////////////////////////////////////////////////////////////////////////////////////////
354   // Methods from BootstrapInvocationHandler
355   ////////////////////////////////////////////////////////////////////////////////////////////
356 
357   public String getType()
358   {
359     return TYPE;
360   }
361 
362   public void merge(BootstrapInvocationHandler handler)
363     throws JgroupException, RemoteException
364   {
365     if (!handler.getType().equals(TYPE))
366       throw new JgroupException("Incompatible type: " + handler.getType());
367     GroupInvocationHandler h = (GroupInvocationHandler) handler;
368     gep.addMembers(h.gep.getMembers());
369   }
370 
371 } // END GroupInvocationHandler