KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > fractal > rmi > RmiBinder


1 /***
2  * Fractal RMI: a binder for remote method calls between Fractal components.
3  * Copyright (C) 2003 France Telecom R&D
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  *
19  * Contact: Eric.Bruneton@rd.francetelecom.com
20  *
21  * Author: Eric Bruneton
22  *
23  * adapted from Jonathan:
24  * org.objectweb.david.libs.binding.iiop.IIOPBinder (author: B. Dumant)
25  * with some comments copied from:
26  * org.objectweb.jonathan.apis.binding.NamingContext (author: B. Dumant)
27  */

28
29 package org.objectweb.fractal.rmi;
30
31 import org.objectweb.fractal.api.control.BindingController;
32
33 import org.objectweb.jonathan.apis.binding.Identifier;
34 import org.objectweb.jonathan.apis.binding.NamingContext;
35 import org.objectweb.jonathan.apis.kernel.Context;
36 import org.objectweb.jonathan.apis.kernel.ContextFactory;
37 import org.objectweb.jonathan.apis.kernel.JonathanException;
38 import org.objectweb.jonathan.apis.presentation.Marshaller;
39 import org.objectweb.jonathan.apis.presentation.UnMarshaller;
40 import org.objectweb.jonathan.apis.protocols.Protocol;
41 import org.objectweb.jonathan.apis.protocols.ProtocolGraph;
42 import org.objectweb.jonathan.apis.protocols.RequestSession;
43 import org.objectweb.jonathan.apis.protocols.SessionIdentifier;
44 import org.objectweb.jonathan.apis.stub_factories.StubFactory;
45
46 import org.objectweb.util.monolog.api.BasicLevel;
47 import org.objectweb.util.monolog.api.Logger;
48 import org.objectweb.util.monolog.api.LoggerFactory;
49
50 import java.util.Properties JavaDoc;
51 import java.util.HashMap JavaDoc;
52 import java.util.Map JavaDoc;
53
54 /**
55  * Provides a very simple binder component to create distributed bindings.
56  * <p>
57  * To export an object, this naming context first exports it through an object
58  * adapter, whose role is to assign an object key to this object, and to return
59  * it in an {@link Identifier}. This identifier is then encapsulated, together
60  * with a host name and a port number, in a new {@link Identifier}, which is
61  * returned to the caller.
62  * <p>
63  * To create a binding to an object identified by such an identifier, this
64  * naming context creates a session identifier with the host name, the port
65  * number and the object key, by using the transport and invocation protocols.
66  * A stub is then created with the session and the object identifiers, by using
67  * the stub factory. Finally this stub is returned to the caller.
68  */

69
70 public class RmiBinder implements NamingContext, BindingController {
71
72   /**
73    * The object adapter used to export objects before exporting them with this
74    * naming context.
75    */

76
77   protected NamingContext adapter;
78
79   /**
80    * The factory used to create bindings to the objects identified by the names
81    * managed by this context.
82    */

83
84   protected StubFactory stubFactory;
85
86   /**
87    * The context factory used to create hints for various methods.
88    */

89
90   protected ContextFactory contextFactory;
91
92   /**
93    * The invocation protocol used to send invocation messages.
94    */

95
96   protected Protocol rmi;
97
98   /**
99    * The transport protocol used by the invocation protocol to send its
100    * messages.
101    */

102
103   protected Protocol tcp;
104
105   /**
106    * The optional logger factory used to get a logger for this component.
107    */

108
109   protected LoggerFactory loggerFactory;
110
111   /**
112    * The logger used to log messages. May be <tt>null</tt>.
113    */

114
115   protected Logger logger;
116
117   /**
118    * A map associating the identifiers of the available sessions to their TCP
119    * port number.
120    */

121
122   private Map JavaDoc sessionIdentifiers;
123
124   /**
125    * Constructs a new {@link RmiBinder}.
126    */

127
128   public RmiBinder () {
129     sessionIdentifiers = new HashMap JavaDoc();
130   }
131
132   // --------------------------------------------------------------------------
133
// Implementation of the BindingController interface
134
// --------------------------------------------------------------------------
135

136   public String JavaDoc[] listFc () {
137     return new String JavaDoc[] {
138       "adapter",
139       "stub-factory",
140       "context-factory",
141       "rmi-protocol",
142       "tcp-protocol",
143       "logger-factory"
144     };
145   }
146
147   public Object JavaDoc lookupFc (final String JavaDoc clientItfName) {
148     if (clientItfName.equals("adapter")) {
149       return adapter;
150     } else if (clientItfName.equals("stub-factory")) {
151       return stubFactory;
152     } else if (clientItfName.equals("context-factory")) {
153       return contextFactory;
154     } else if (clientItfName.equals("rmi-protocol")) {
155       return rmi;
156     } else if (clientItfName.equals("tcp-protocol")) {
157       return tcp;
158     } else if (clientItfName.equals("logger-factory")) {
159       return loggerFactory;
160     }
161     return null;
162   }
163
164   public void bindFc (final String JavaDoc clientItfName, final Object JavaDoc serverItf) {
165     if (clientItfName.equals("adapter")) {
166       adapter = (NamingContext)serverItf;
167     } else if (clientItfName.equals("stub-factory")) {
168       stubFactory = (StubFactory)serverItf;
169     } else if (clientItfName.equals("context-factory")) {
170       contextFactory = (ContextFactory)serverItf;
171     } else if (clientItfName.equals("rmi-protocol")) {
172       rmi = (Protocol)serverItf;
173     } else if (clientItfName.equals("tcp-protocol")) {
174       tcp = (Protocol)serverItf;
175     } else if (clientItfName.equals("logger-factory")) {
176       loggerFactory = (LoggerFactory)serverItf;
177       logger = loggerFactory.getLogger(getClass().getName());
178     }
179   }
180
181   public void unbindFc (final String JavaDoc clientItfName) {
182     if (clientItfName.equals("adapter")) {
183       adapter = null;
184     } else if (clientItfName.equals("stub-factory")) {
185       stubFactory = null;
186     } else if (clientItfName.equals("context-factory")) {
187       contextFactory = null;
188     } else if (clientItfName.equals("rmi-protocol")) {
189       rmi = null;
190     } else if (clientItfName.equals("tcp-protocol")) {
191       tcp = null;
192     } else if (clientItfName.equals("logger-factory")) {
193       loggerFactory = null;
194       logger = null;
195     }
196   }
197
198   // --------------------------------------------------------------------------
199
// Implementation of the NamingContext interface
200
// --------------------------------------------------------------------------
201

202   /**
203    * Creates a new identifier for the object interface designated by the
204    * <tt>obj</tt> parameter. Note that calling the {@link Identifier#resolve()
205    * resolve} method on the returned identifier should return <tt>id</tt>.
206    *
207    * @param obj an object.
208    * @param hints additional information.
209    * @return an identifier managed by the target naming context.
210    * @throws JonathanException if something goes wrong.
211    */

212
213   public Identifier export (
214     final Object JavaDoc obj,
215     final Context hints) throws JonathanException
216   {
217     int port = 0;
218     if (hints != null) {
219       Integer JavaDoc i = (Integer JavaDoc)hints.getValue("port", (char)0);
220       if (i != null) {
221         port = i.intValue();
222       }
223     }
224
225     SessionIdentifier sid = null;
226
227     if (port == 0) {
228       // no port specified -> use the first available session id, if any
229
if (sessionIdentifiers.size() > 0) {
230         sid = (SessionIdentifier)sessionIdentifiers.values().iterator().next();
231       }
232     } else {
233       // finds the session id corresponding to the specified port
234
sid = (SessionIdentifier)sessionIdentifiers.get(new Integer JavaDoc(port));
235     }
236
237     if (sid == null) {
238       Context phints = contextFactory.newContext();
239       phints.addElement("port", Integer JavaDoc.class, new Integer JavaDoc(port), (char)0);
240       ProtocolGraph pgraph = rmi.createProtocolGraph(
241         new ProtocolGraph[] {
242           tcp.createProtocolGraph(new ProtocolGraph[0], phints)
243         },
244         phints);
245       phints.release();
246       sid = pgraph.export(null);
247       Context ctxt = sid.next()[0].getInfo();
248       port = ((Integer JavaDoc)ctxt.getValue("port", (char)0)).intValue();
249       sessionIdentifiers.put(new Integer JavaDoc(port), sid);
250     }
251
252     Context ctxt = sid.next()[0].getInfo();
253     String JavaDoc host = (String JavaDoc)ctxt.getValue("hostname", (char)0);
254     port = ((Integer JavaDoc)ctxt.getValue("port", (char)0)).intValue();
255
256     Identifier id = new Id(host, port, adapter.export(obj, hints));
257     if (logger != null && logger.isLoggable(BasicLevel.INFO)) {
258       logger.log(BasicLevel.INFO, "Object " + obj + " exported with id " + id);
259     }
260     return id;
261   }
262
263   /**
264    * Decodes an identifier from a buffer portion. Since identifiers are likely
265    * to be transmitted on the net, they may have to be encoded and decoded. The
266    * {@link Identifier#encode() encoding} method is borne by the {@link
267    * Identifier} interface, but the decoding methods must be borne by
268    * each naming context. This method creates an identifier (associated
269    * with the target naming context), from the <code>length</code> bytes of
270    * <code>data</code> starting at offset <code>offset</code>.
271    *
272    * @param data the byte array to read the encoded identifier from.
273    * @param offset offset of the first byte of the encoding.
274    * @param length length of the encoding.
275    * @return a decoded identifier.
276    * @throws JonathanException if something goes wrong.
277    */

278
279   public Identifier decode (
280     final byte[] data,
281     final int offset,
282     final int length) throws JonathanException
283   {
284     int port = ((data[offset] & 0xFF) << 24) +
285                ((data[offset + 1] & 0xFF) << 16) +
286                ((data[offset + 2] & 0xFF) << 8) +
287                (data[offset + 3] & 0xFF);
288     int len = ((data[offset + 4] & 0xFF) << 8) +
289                (data[offset + 5] & 0xFF);
290     char[] host = new char[len];
291     for (int i = 0; i < len; ++i) {
292       host[i] = (char)(data[offset + 6 + i] & 0xFF);
293     }
294     Identifier next = adapter.decode(data, offset + 6 + len, length - 6 - len);
295     return new Id(new String JavaDoc(host), port, next);
296   }
297
298   /**
299    * Decodes an identifier from the provided unmarshaller.
300    *
301    * @param u an unmarhaller;
302    * @return an identifier managed by the target naming context;
303    * @throws JonathanException if something goes wrong.
304    */

305
306   public Identifier decode (final UnMarshaller u) throws JonathanException {
307     int port = u.readInt();
308     int len = u.readShort();
309     char[] host = new char[len];
310     for (int i = 0; i < len; ++i) {
311       host[i] = (char)(u.readByte() & 0xFF);
312     }
313     Identifier next = adapter.decode(u);
314     return new Id(new String JavaDoc(host), port, next);
315   }
316
317   // --------------------------------------------------------------------------
318
// Utility class
319
// --------------------------------------------------------------------------
320

321   class Id implements Identifier {
322
323     public String JavaDoc host;
324
325     public int port;
326
327     public Identifier next;
328
329     public Id (
330       final String JavaDoc host,
331       final int port,
332       final Identifier next)
333     {
334       this.host = host;
335       this.port = port;
336       this.next = next;
337     }
338
339     public NamingContext getContext () {
340       return RmiBinder.this;
341     }
342
343     public Object JavaDoc bind (final Identifier[] ref, final Context hints)
344       throws JonathanException
345     {
346       Properties JavaDoc p = new Properties JavaDoc();
347       p.put("object_key", next.encode());
348       p.put("hostname", host);
349       p.put("port", new Integer JavaDoc(port));
350       SessionIdentifier sessionId =
351         rmi.createSessionIdentifier(p, new SessionIdentifier[] {
352           tcp.createSessionIdentifier(p, new SessionIdentifier[] {})
353         });
354       // local optimization
355
if (sessionId.isLocal()) {
356         RequestSession session = (RequestSession)next.bind(null, hints);
357         Object JavaDoc impl = session.getTarget();
358         if (impl != null) {
359           return impl;
360         }
361       }
362       Object JavaDoc s = stubFactory.newStub(sessionId, new Identifier[] {this}, hints);
363       if (logger != null && logger.isLoggable(BasicLevel.INFO)) {
364         logger.log(BasicLevel.INFO, "Stub " + s + " bound to id " + ref[0]);
365       }
366       return s;
367     }
368
369     public void unexport () {
370     }
371
372     public boolean isValid () {
373       return true;
374     }
375
376     public Object JavaDoc resolve () {
377       return null;
378     }
379
380     public byte[] encode () throws JonathanException {
381       short len = (short)host.length();
382       byte[] n = next.encode();
383       byte[] b = new byte[n.length + 6 + len];
384       // port
385
b[0] = (byte)((port >>> 24) & 0xFF);
386       b[1] = (byte)((port >>> 16) & 0xFF);
387       b[2] = (byte)((port >>> 8) & 0xFF);
388       b[3] = (byte)(port & 0xFF);
389       // host
390
b[4] = (byte)((len >>> 8) & 0xFF);
391       b[5] = (byte)(len & 0xFF);
392       for (int i = 0 ; i < len; ++i) {
393         b[i + 6] = (byte)host.charAt(i);
394       }
395       // next id
396
System.arraycopy(n, 0, b, 6 + len, n.length);
397       return b;
398     }
399
400     public void encode (final Marshaller m) throws JonathanException {
401       short len = (short)host.length();
402       m.writeInt(port);
403       m.writeShort(len);
404       for (int i = 0; i < len; ++i) {
405         m.writeByte((byte)host.charAt(i));
406       }
407       next.encode(m);
408     }
409
410     public boolean equals (final Object JavaDoc o) {
411       if (o instanceof Id) {
412         Id id = (Id)o;
413         if (host.equals(id.host) && port == id.port) {
414           return next.equals(id.next);
415         }
416       }
417       return false;
418     }
419
420     public int hashCode () {
421       return host.hashCode()*port*(next.hashCode() + 17);
422     }
423
424     public String JavaDoc toString () {
425       return "Id[" + host + "," + port + "," + next + "]";
426     }
427   }
428 }
429
Popular Tags