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  
19  package jgroup.relacs.gmi;
20  
21  import java.io.IOException;
22  import java.lang.reflect.Constructor;
23  import java.lang.reflect.InvocationHandler;
24  import java.rmi.Remote;
25  import java.rmi.server.ExportException;
26  import java.util.Collection;
27  
28  import jgroup.core.ExternalGMIService;
29  import jgroup.core.GroupManager;
30  import jgroup.core.IID;
31  import jgroup.core.JgroupException;
32  import jgroup.core.MalformedLayerException;
33  import jgroup.core.MemberId;
34  import jgroup.core.MembershipListener;
35  import jgroup.core.MembershipService;
36  import jgroup.core.View;
37  import jgroup.core.Layer.FinalizeLayer;
38  import jgroup.core.multicast.MulticastService;
39  import jgroup.core.registry.DependableRegistry.RegistryEntry;
40  import jgroup.relacs.gmi.protocols.BasicDispatcher;
41  import jgroup.relacs.gmi.protocols.ProtocolDispatcher;
42  import net.jini.jeri.AbstractILFactory;
43  import net.jini.jeri.InvocationDispatcher;
44  import net.jini.jeri.ObjectEndpoint;
45  import net.jini.jeri.ServerCapabilities;
46  
47  import org.apache.log4j.Logger;
48  
49  
50  /**
51   *  This layer implements the external group method invocation service
52   *  based on JERI. <p>
53   * 
54   *  It enables a server to listen for group method invocations from
55   *  clients external to the object group that the server is a member of
56   *  and dispatch these invocations to one or more servers within the group.
57   *
58   *  @author Tor Arve Stangeland
59   *  @author Hein Meling
60   *  @since Jgroup 2.1
61   */
62  public class JeriEGMILayer
63    extends AbstractILFactory
64    implements ExternalGMIService, MembershipListener, FinalizeLayer
65  {
66  
67    ////////////////////////////////////////////////////////////////////////////////////////////
68    // Logger
69    ////////////////////////////////////////////////////////////////////////////////////////////
70  
71    /** Obtain logger for this class */
72    private static final Logger log = Logger.getLogger(JeriEGMILayer.class);
73  
74  
75    ////////////////////////////////////////////////////////////////////////////////////////////
76    // Fields
77    ////////////////////////////////////////////////////////////////////////////////////////////
78  
79    /** The local group manager */
80    private final GroupManager gm;
81  
82    /** The group identifier for this group manager stack */
83    private final int gid;
84  
85    /** Method table for converting hash to method */
86    private final MethodTable methodTable = new MethodTable();
87  
88    /** Request handler */
89    private final GroupRequestHandler requestHandler;
90  
91    /** Invocation dispatcher */
92    private final GroupInvocationDispatcher dispatcher;
93    
94    private final MemberId me;
95  
96  
97    ////////////////////////////////////////////////////////////////////////////////////////////
98    // Constructors
99    ////////////////////////////////////////////////////////////////////////////////////////////
100 
101   private JeriEGMILayer(GroupManager gm, MulticastService mcast, MembershipService pgms)
102   {
103     this.gm = gm;
104     gid = gm.getGroupId();
105     me = pgms.getMyIdentifier();
106     dispatcher = new GroupInvocationDispatcher(gm, methodTable);
107     requestHandler = new GroupRequestHandler(gid, me);
108   }
109 
110 
111   ////////////////////////////////////////////////////////////////////////////////////////////
112   // Static factory
113   ////////////////////////////////////////////////////////////////////////////////////////////
114 
115   public static JeriEGMILayer getLayer(
116       GroupManager gm, MulticastService mcast, MembershipService pgms)
117   {
118     return new JeriEGMILayer(gm, mcast, pgms);
119   }
120 
121 
122   ////////////////////////////////////////////////////////////////////////////////////////////
123   // Layer interface methods
124   ////////////////////////////////////////////////////////////////////////////////////////////
125 
126   /**
127    * Add a server/layer that is listening for external group method invocations.
128    */
129   public void addListener(Object listener)
130   {
131     if (listener instanceof Remote) {
132       methodTable.addMethods(listener);
133       if (log.isDebugEnabled()) {
134         log.debug(listener.getClass().getName() + " implements a Remote interface; "
135             + "added remote methods to the method table.");
136       }
137     } else {
138       if (log.isDebugEnabled()) {
139         log.debug(listener.getClass().getName() + " does not implement a Remote interface");
140       }
141     }
142   }
143 
144   
145   /**
146    * This method is called by the <code>GroupManager</code> after the construction
147    * of all the layers, i.e., all <code>addListener</code> calls have been completed,
148    * and the <code>MethodTable</code> is fully populated.
149    * 
150    * The purpose of this method is to determine which protocols are actually needed
151    * by the server (and its required layers) and from that construct the protocol
152    * instances and add them to the <code>GroupRequestHandler</code> object.  This
153    * method will also pass the server reference to the protocol instances that need
154    * it.
155    * 
156    * @throws JgroupException if a protocol instance could not be constructed,
157    * 
158    * @see jgroup.core.Layer.FinalizeLayer#complete(java.lang.Object)
159    */
160   public void complete(Object server)
161     throws JgroupException
162   {
163     for (MethodDetails method : methodTable) {
164       MethodSemantics semantics = method.getInvocationSemantics();
165       if (!requestHandler.hasProtocol(semantics)) {
166         // Get protocol if the request handler does not have it yet
167         ProtocolDispatcher protocol = getProtocol(semantics);
168         // The protocol implementation may need access to the server object
169         protocol.setServer(server);
170         requestHandler.addProtocol(semantics, protocol);
171       }
172     }
173   }
174 
175 
176   /**
177    * Construct a <code>ProtocolDispatcher</code> instance for the given
178    * method invocation semantics.
179    * 
180    * The protocol instances must follow these rules of design:
181    * <ol>
182    * <li> The protocol implementation must implement the <code>ProtocolDispatcher</code>
183    *      interface, or do so implicitly through inheritence.
184    * <li> The constructor must provide only a single constructor.
185    * <li> The constructor cannot throw checked exceptions.
186    * <li> The parameter types of the constructor must correspond to a *Service.class
187    *      available from the group manager; that is it must be a layer.  In addition,
188    *      one of the parameters may be of type <code>GroupInvocationDispatcher</code>.
189    * </ol>
190    * 
191    * @param semantics the method invocation semantics whose protocol to obtain
192    * @return the protocol instance for the provided method invocation semantics.
193    * @throws JgroupException if the protocol could not be constructed, one of the above
194    *   rules of design was broken.
195    */
196   public ProtocolDispatcher getProtocol(MethodSemantics semantics)
197     throws JgroupException
198   {
199     Class pClass = semantics.getProtocol();
200     if (!ProtocolDispatcher.class.isAssignableFrom(pClass)) {
201       throw new MalformedLayerException(
202           "Protocol class must implement ProtocolDispatcher: " + pClass);
203     }
204     Constructor[] constructors = pClass.getDeclaredConstructors();
205     if (constructors.length > 1) {
206       throw new MalformedLayerException(
207           "Protocol cannot provide multiple constructors: " + pClass);
208     }
209     Constructor constr = constructors[0];
210     if (constr.getExceptionTypes().length > 0) {
211       throw new MalformedLayerException(
212           "Protocol constructor is not allowed to throw checked exceptions: " + pClass);
213     }
214     Class[] parameters = constr.getParameterTypes();
215     Object[] args = new Object[parameters.length];
216     if (parameters.length > 0) {
217       for (int i = 0; i < parameters.length; i++) {
218         if (GroupInvocationDispatcher.class.isAssignableFrom(parameters[i])) {
219           args[i] = dispatcher;
220         } else {
221           args[i] = gm.getService(parameters[i]);
222         }
223       }
224     }
225 
226     try {
227       return (ProtocolDispatcher) constr.newInstance(args);
228     } catch (Exception e) {
229       throw new JgroupException(
230           "Failed to construct protocol for: " + pClass, e);
231     }
232   }
233 
234 
235   ////////////////////////////////////////////////////////////////////////////////////////////
236   // Methods from AbstractILFactory
237   ////////////////////////////////////////////////////////////////////////////////////////////
238   
239   /* (non-Javadoc)
240    * @see net.jini.jeri.AbstractILFactory#createInvocationHandler(Class[], Remote, ObjectEndpoint)
241    */
242   public InvocationHandler createInvocationHandler(Class[] interfaces, Remote impl, ObjectEndpoint oe)
243     throws ExportException
244   {
245     return new GroupInvocationHandler(oe, methodTable);
246   }
247   
248   /* (non-Javadoc)
249    * @see net.jini.jeri.AbstractILFactory#createInvocationDispatcher(Collection, Remote, ServerCapabilities)
250    */
251   public InvocationDispatcher createInvocationDispatcher(Collection methods, Remote impl, ServerCapabilities caps)
252     throws ExportException
253   {
254     return dispatcher;
255   }
256 
257   /**
258    * By overriding this method we may avoid patching the Mahalo
259    * implementation to support our GroupInvocationHandler implementation.
260    */
261   protected Class[] getExtraProxyInterfaces(Remote impl)
262     throws ExportException
263   {
264     if (impl == null) {
265       throw new NullPointerException("impl is null");
266     }
267     return new Class[0];
268   }
269 
270 
271   ////////////////////////////////////////////////////////////////////////////////////////////
272   // Methods from MembershipListener
273   ////////////////////////////////////////////////////////////////////////////////////////////
274 
275   /**
276    * Notify the invocation dispatcher and the request handler
277    * of the most recent server-side view identifier.
278    * 
279    * @see jgroup.core.MembershipListener#viewChange(jgroup.core.View)
280    */
281   @AllowDuplicateViews public void viewChange(View view)
282   {
283 //    requestHandler.enableDispatching();
284 //    dispatcher.enableDispatching();
285     dispatcher.setView(view);
286   }
287 
288   /* (non-Javadoc)
289    * @see jgroup.core.MembershipListener#prepareChange()
290    */
291   public void prepareChange()
292   {
293 //    requestHandler.disableDispatching();
294 //    dispatcher.disableDispatching();
295   }
296 
297   /* (non-Javadoc)
298    * @see jgroup.core.MembershipListener#hasLeft()
299    */
300   public void hasLeft() { }
301 
302 
303   ////////////////////////////////////////////////////////////////////////////////////////////
304   // ExternalGMIService interface methods
305   ////////////////////////////////////////////////////////////////////////////////////////////
306 
307   public IID getIdentifier()
308   {
309     return BasicDispatcher.getIdentifier();
310   }
311 
312 
313   public RegistryEntry getRegistryEntry(String serviceName)
314     throws JgroupException
315   {
316     try {
317       requestHandler.initServerEndpoint();
318       return new JeriRegistryEntry(serviceName, me, methodTable);
319     } catch (IOException ioex) {
320       throw new JgroupException("Could not get Endpoint", ioex);
321     }
322   }
323 
324 
325   public BootstrapInvocationHandler getBootstrapHandler()
326     throws JgroupException
327   {
328     try {
329       requestHandler.initServerEndpoint();
330       GroupEndPoint gep = new GroupEndPoint(gid, me);
331       if (log.isDebugEnabled())
332         log.debug(gep);
333       return new GroupInvocationHandler(gep, methodTable);
334     } catch (IOException ioex) {
335       throw new JgroupException("Could not create bootstrap invocation handler", ioex);
336     }
337   }
338 
339   /* (non-Javadoc)
340    * @see jgroup.core.ExternalGMIService#getProxy(java.rmi.Remote)
341    */
342   public Remote getProxy(Remote impl)
343     throws ExportException
344   {
345     try {
346       requestHandler.initServerEndpoint();
347       GroupEndPoint gep = new GroupEndPoint(gid, me);
348       if (log.isDebugEnabled())
349         log.debug(gep);
350       Instances inst = createInstances(impl, gep, requestHandler.getServerEndpoint());
351       return inst.getProxy(); 
352     } catch (IOException e) {
353       throw new ExportException("Could not export remote object", e);
354     }
355   }
356 
357 } // END JeriEGMILayer