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