1 /* UnicastRef.java --
2    Copyright (c) 1996, 1997, 1998, 1999, 2002, 2005, 2006
3    Free Software Foundation, Inc.
4 
5 This file is part of GNU Classpath.
6 
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11 
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING.  If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA.
21 
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library.  Thus, the terms and
24 conditions of the GNU General Public License cover the whole
25 combination.
26 
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module.  An independent module is a module which is not derived from
34 or based on this library.  If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so.  If you do not wish to do so, delete this
37 exception statement from your version. */
38 
39 
40 package gnu.java.rmi.server;
41 
42 import gnu.java.rmi.dgc.LeaseRenewingTask;
43 
44 import java.io.DataInputStream;
45 import java.io.DataOutputStream;
46 import java.io.IOException;
47 import java.io.ObjectInput;
48 import java.io.ObjectInputStream;
49 import java.io.ObjectOutput;
50 import java.io.ObjectOutputStream;
51 import java.lang.reflect.InvocationTargetException;
52 import java.lang.reflect.Method;
53 import java.rmi.ConnectException;
54 import java.rmi.Remote;
55 import java.rmi.RemoteException;
56 import java.rmi.dgc.Lease;
57 import java.rmi.server.ObjID;
58 import java.rmi.server.Operation;
59 import java.rmi.server.RMIClientSocketFactory;
60 import java.rmi.server.RemoteCall;
61 import java.rmi.server.RemoteObject;
62 import java.rmi.server.RemoteRef;
63 import java.rmi.server.UID;
64 
65 public class UnicastRef
66     implements RemoteRef, ProtocolConstants
67 {
68 
69   /**
70    * Use serial version UID for iteroperability
71    */
72   private static final long serialVersionUID = 1;
73 
74   public ObjID objid;
75 
76   UnicastConnectionManager manager;
77 
78   /**
79    * Used by serialization, and let subclass capable of having default
80    * constructor
81    */
82   // must be public otherwise java.rmi.RemoteObject cannot instantiate this
83   // class
84   // -- iP
UnicastRef()85   public UnicastRef()
86   {
87   }
88 
UnicastRef(ObjID objid, String host, int port, RMIClientSocketFactory csf)89   public UnicastRef(ObjID objid, String host, int port,
90                     RMIClientSocketFactory csf)
91   {
92     this(objid);
93     manager = UnicastConnectionManager.getInstance(host, port, csf);
94   }
95 
UnicastRef(ObjID objid)96   public UnicastRef(ObjID objid)
97   {
98     this.objid = objid;
99   }
100 
invoke(Remote obj, Method method, Object[] params, long opnum)101   public Object invoke(Remote obj, Method method, Object[] params, long opnum)
102       throws Exception
103   {
104     // Check if client and server are in the same VM, then local call can be
105     // used to
106     // replace remote call, but it's somewhat violating remote semantic.
107     Object svrobj = manager.serverobj;
108 
109     // Make sure that the server object is compatible. It could be loaded from a
110     // different
111     // classloader --iP
112     if (svrobj != null && method.getDeclaringClass().isInstance(svrobj))
113       {
114         // local call
115         Object ret = null;
116         try
117           {
118             ret = method.invoke(svrobj, params);
119           }
120         catch (InvocationTargetException e)
121           {
122             throw (Exception) e.getTargetException();
123           }
124         // System.out.println("\n\n ***** local call: " + method + "\nreturn: "
125         // + ret + "\n\n");
126         return ret;
127       }
128     // System.out.println("***************** remote call:" +
129     // manager.serverPort);
130     return (invokeCommon(obj, method, params, - 1, opnum));
131   }
132 
133   /**
134    * The ordinary number of the DGC messages.
135    */
136   static long dgcSequence;
137 
138   /**
139    * The DGC object id, also serves as a synchronization target to increment the
140    * dgcSequence safely.
141    */
142   static final ObjID dgcId = new ObjID(ObjID.DGC_ID);
143 
144   ObjID[] this_id;
145 
146   /**
147    * The number of the method "dirty" in the DGC.
148    */
149   static int DIRTY = 1;
150 
151   /**
152    * The DGC interface hash code.
153    */
154   static final long dgcInterfaceHash = - 669196253586618813L;
155 
156   /**
157    * Notify the DGC of the remote side that we still hold this object.
158    */
notifyDGC(Lease lease)159   public Lease notifyDGC(Lease lease) throws Exception
160   {
161     long seq;
162     synchronized (dgcId)
163       {
164         seq = dgcSequence++;
165       }
166 
167     if (this_id == null)
168       this_id = new ObjID[] { objid };
169 
170     UnicastConnection conn;
171     try
172       {
173         conn = manager.getConnection();
174       }
175     catch (IOException e1)
176       {
177         throw new RemoteException("connection failed to host: "
178                                   + manager.serverName, e1);
179       }
180 
181     ObjectOutputStream out;
182     DataOutputStream dout;
183     try
184       {
185         dout = conn.getDataOutputStream();
186         dout.writeByte(MESSAGE_CALL);
187 
188         out = conn.startObjectOutputStream(); // (re)start ObjectOutputStream
189 
190         dgcId.write(out);
191         // The number of the operation is 1 ("dirty")
192         out.writeInt(DIRTY);
193         out.writeLong(dgcInterfaceHash);
194 
195         RMIObjectOutputStream rout = (RMIObjectOutputStream) out;
196 
197         rout.writeValue(this_id, this_id.getClass());
198         rout.writeLong(seq);
199         rout.writeValue(lease, lease.getClass());
200 
201         out.flush();
202       }
203     catch (IOException e2)
204       {
205         throw new RemoteException("DGC call failed: ", e2);
206       }
207 
208     int returncode;
209     Object returnval;
210     DataInputStream din;
211     ObjectInputStream in;
212     UID ack;
213     try
214       {
215         din = conn.getDataInputStream();
216 
217         if ((returncode = din.readUnsignedByte()) != MESSAGE_CALL_ACK)
218           {
219             conn.disconnect();
220             throw new RemoteException("DGC Call not acked:" + returncode);
221           }
222 
223         in = conn.startObjectInputStream(); // (re)start ObjectInputStream
224         returncode = in.readUnsignedByte();
225         ack = UID.read(in);
226 
227         if (returncode == RETURN_NACK)
228           {
229             returnval = in.readObject(); // get Exception
230 
231           }
232         else
233           {
234             returnval = ((RMIObjectInputStream) in).readValue(Lease.class);
235           }
236       }
237     catch (IOException e3)
238       {
239         throw new RemoteException("DGC call return failed: ", e3);
240       }
241 
242     manager.discardConnection(conn);
243 
244     if (returncode != RETURN_ACK && returnval != null)
245       {
246         if (returncode == RETURN_NACK)
247           throw (Exception) returnval;
248         else
249           throw new RemoteException("DGC unexpected returncode: " + returncode);
250       }
251 
252     return (Lease) returnval;
253   }
254   /**
255    * Invoke the remote method on the given object. This part is overridden by
256    * the activatable objects.
257    */
invokeCommon(Remote obj, Method method, Object[] params, int opnum, long hash)258   protected Object invokeCommon(Remote obj, Method method, Object[] params,
259                                 int opnum, long hash) throws Exception
260   {
261     UnicastConnection conn;
262     try
263       {
264         conn = manager.getConnection();
265         return invokeCommon(conn, obj, method, params, opnum, hash);
266       }
267     catch (IOException e1)
268       {
269         throw new RemoteException("connection failed to host: "
270                                   + manager.serverName, e1);
271       }
272   }
273 
274   /**
275    * Invoke the remote method on the given object when connection is already
276    * established.
277    */
invokeCommon(UnicastConnection conn, Remote obj, Method method, Object[] params, int opnum, long hash)278   protected Object invokeCommon(UnicastConnection conn, Remote obj,
279                                 Method method, Object[] params, int opnum,
280                                 long hash) throws Exception
281   {
282     ObjectOutputStream out;
283     DataOutputStream dout;
284     try
285       {
286         dout = conn.getDataOutputStream();
287         dout.writeByte(MESSAGE_CALL);
288 
289         out = conn.startObjectOutputStream(); // (re)start ObjectOutputStream
290 
291         objid.write(out);
292         out.writeInt(opnum);
293         out.writeLong(hash);
294 
295         // must handle primitive class and their wrapper classes
296         Class clss[] = method.getParameterTypes();
297         for (int i = 0; i < clss.length; i++)
298           ((RMIObjectOutputStream) out).writeValue(params[i], clss[i]);
299 
300         out.flush();
301       }
302     catch (IOException e2)
303       {
304         throw new RemoteException("call failed: ", e2);
305       }
306 
307     int returncode;
308     Object returnval;
309     DataInputStream din;
310     ObjectInputStream in;
311     UID ack;
312     try
313       {
314         din = conn.getDataInputStream();
315 
316         if ((returncode = din.readUnsignedByte()) != MESSAGE_CALL_ACK)
317           {
318             conn.disconnect();
319             throw new RemoteException("Call not acked:" + returncode);
320           }
321 
322         in = conn.startObjectInputStream(); // (re)start ObjectInputStream
323         returncode = in.readUnsignedByte();
324         ack = UID.read(in);
325 
326         Class cls = method.getReturnType();
327 
328         if (returncode == RETURN_NACK)
329           {
330             returnval = in.readObject(); // get Exception
331 
332           }
333         else if (cls == Void.TYPE)
334           {
335             returnval = null;
336             // in.readObject() // not required! returntype 'void' means no field
337             // is returned.
338           }
339         else
340           {
341             returnval = ((RMIObjectInputStream) in).readValue(cls); // get
342             // returnvalue
343           }
344       }
345     catch (IOException e3)
346       {
347         // for debug: e3.printStackTrace();
348         throw new RemoteException("call return failed: ", e3);
349       }
350 
351     /*
352      * if DGCAck is necessary?? //According to RMI wire protocol, send a DGCAck //
353      * to indicate receiving return value dout.writeByte(MESSAGE_DGCACK);
354      * ack.write(dout); out.flush();
355      */
356 
357     manager.discardConnection(conn);
358 
359     if (returncode != RETURN_ACK && returnval != null)
360       {
361         if (returncode == RETURN_NACK)
362           throw (Exception) returnval;
363         else
364           throw new RemoteException("unexpected returncode: " + returncode);
365       }
366 
367     return (returnval);
368   }
369 
370   /**
371    * @deprecated
372    */
newCall(RemoteObject obj, Operation[] op, int opnum, long hash)373   public RemoteCall newCall(RemoteObject obj, Operation[] op, int opnum,
374                             long hash) throws RemoteException
375   {
376     UnicastConnection conn;
377 
378     try
379       {
380         conn = manager.getConnection();
381       }
382     catch (IOException e1)
383       {
384         throw new ConnectException("connection failed to host: "
385                                    + manager.serverName, e1);
386       }
387 
388     // obj: useless?
389 
390     return (new UnicastRemoteCall(conn, objid, opnum, hash));
391   }
392 
393   /**
394    * @deprecated
395    */
invoke(RemoteCall call)396   public void invoke(RemoteCall call) throws Exception
397   {
398     UnicastRemoteCall c = (UnicastRemoteCall) call;
399     call.executeCall();
400   }
401 
402   /**
403    * @deprecated
404    */
done(RemoteCall call)405   public void done(RemoteCall call) throws RemoteException
406   {
407     UnicastRemoteCall c = (UnicastRemoteCall) call;
408     try
409       {
410         c.done();
411       }
412     catch (IOException e)
413       {
414       }
415     UnicastConnection conn = c.getConnection();
416     manager.discardConnection(conn);
417   }
418 
writeExternal(ObjectOutput out)419   public void writeExternal(ObjectOutput out) throws IOException
420   {
421     if (manager == null)
422       {
423         throw new IOException("no connection");
424       }
425     manager.write(out);
426     objid.write(out);
427     // This byte is somewhat confusing when interoperating with JDK
428     out.writeByte(0); // RETURN_ACK);
429   }
430 
readExternal(ObjectInput in)431   public void readExternal(ObjectInput in) throws IOException,
432       ClassNotFoundException
433   {
434     manager = UnicastConnectionManager.read(in);
435     objid = ObjID.read(in);
436     byte ack = in.readByte();
437     // This byte is somewhat confusing when interoperating with JDK
438     if (ack != RETURN_ACK && ack != 0/* jdk ack value */)
439       {
440         throw new IOException("no ack found");
441       }
442 
443     // Notify the DGC of the remote side that we hold the reference to the
444     // received object. Do not notify if the client and server are on the
445     // same virtual machine.
446     if (manager.serverobj == null)
447       LeaseRenewingTask.scheduleLeases(this);
448   }
449 
remoteEquals(RemoteRef ref)450   public boolean remoteEquals(RemoteRef ref)
451   {
452     throw new Error("Not implemented");
453   }
454 
remoteHashCode()455   public int remoteHashCode()
456   {
457     throw new Error("Not implemented");
458   }
459 
getRefClass(ObjectOutput out)460   public String getRefClass(ObjectOutput out)
461   {
462     return ("UnicastRef");
463   }
464 
465   /**
466    * Return the string representing the remote reference information.
467    */
remoteToString()468   public String remoteToString()
469   {
470     if (manager!=null)
471       return manager.toString();
472     else
473       return "null manager";
474   }
475 
dump(UnicastConnection conn)476   public void dump(UnicastConnection conn)
477   {
478     try
479       {
480         DataInputStream din = conn.getDataInputStream();
481         for (;;)
482           {
483             int b = din.readUnsignedByte();
484             System.out.print(Integer.toHexString(b));
485             if (b >= 32 && b < 128)
486               {
487                 System.out.print(": " + (char) b);
488               }
489             System.out.println();
490           }
491       }
492     catch (IOException _)
493       {
494       }
495   }
496 
497   /**
498    * Check if this UnicastRef points to the object as the passed UnicastRef.
499    * Both the object Id and manager must be the same.
500    *
501    * @return true if the passed reference points to the same remote object as
502    *         this reference, false otherwise.
503    */
equals(Object other)504   public boolean equals(Object other)
505   {
506     if (other instanceof UnicastRef)
507       {
508         UnicastRef r = (UnicastRef) other;
509         return r.manager.equals(manager) && r.objid.equals(objid);
510       }
511     else
512       return false;
513   }
514 
515   /**
516    * Get the hash code of this UnicastRef, combining hash code of the manager
517    * with hash code of the object id.
518    */
hashCode()519   public int hashCode()
520   {
521     return manager.hashCode() ^ objid.hashCode();
522   }
523 
524 }
525