1 /*
2 * Copyright (c) 1998-2002 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.registry;
20
21 import java.lang.reflect.InvocationHandler;
22 import java.rmi.AccessException;
23 import java.rmi.NotBoundException;
24 import java.rmi.Remote;
25 import java.rmi.RemoteException;
26 import java.rmi.registry.Registry;
27 import java.rmi.server.RMIClientSocketFactory;
28 import java.rmi.server.RMIServerSocketFactory;
29
30 import jgroup.core.ConfigManager;
31 import jgroup.core.ConfigurationException;
32 import jgroup.core.ExternalGMIService;
33 import jgroup.core.GroupManager;
34 import jgroup.core.IID;
35 import jgroup.core.JgroupException;
36 import jgroup.core.Layer;
37 import jgroup.core.MemberId;
38 import jgroup.core.MembershipListener;
39 import jgroup.core.MembershipService;
40 import jgroup.core.MergingListener;
41 import jgroup.core.View;
42 import jgroup.core.protocols.Anycast;
43 import jgroup.core.protocols.Multicast;
44 import jgroup.core.registry.BootstrapRegistry;
45 import jgroup.core.registry.DependableRegistry;
46 import jgroup.relacs.config.AppConfig;
47 import jgroup.relacs.gm.LeaseInfo;
48 import jgroup.relacs.rmi.Exporter;
49 import jgroup.util.Abort;
50
51 import org.apache.log4j.Logger;
52
53 /**
54 * This class implements the <code>DependableRegistry</code> interface.
55 *
56 * @author Alberto Montresor
57 * @author Hein Meling
58 * @since Jgroup 0.4
59 */
60 public final class RegistryImpl
61 implements DependableRegistry, MergingListener, MembershipListener
62 {
63
64 ////////////////////////////////////////////////////////////////////////////////////////////
65 // Logger
66 ////////////////////////////////////////////////////////////////////////////////////////////
67
68 /** Obtain logger for this class */
69 private static final Logger log = Logger.getLogger(RegistryImpl.class);
70
71
72 ////////////////////////////////////////////////////////////////////////////////////////////
73 // Fields
74 ////////////////////////////////////////////////////////////////////////////////////////////
75
76 private ExternalGMIService egmis;
77 private MembershipService pgms;
78 private RegistryTable registrytable;
79 private MemberId me;
80
81
82 ////////////////////////////////////////////////////////////////////////////////////////////
83 // Constructors
84 ////////////////////////////////////////////////////////////////////////////////////////////
85
86 /**
87 * Creates and exports a dependable registry replica on the local host that
88 * listen to incoming requests on an anonymous communication port.
89 * Objects contained in the <tt>conf</tt> map are used by Jgroup to
90 * configure the group communication system; the distributed system
91 * description must be included in this map object.
92 *
93 * @exception RemoteException
94 * If the registry could not be exported
95 * @exception JgroupException
96 * Generic exception raised by Jgroup
97 * @exception ConfigurationException
98 * Raised when the distributed system configuration object contains an error
99 */
100 public RegistryImpl()
101 throws RemoteException, JgroupException, ConfigurationException
102 {
103 this(null, null, null);
104 }
105
106 /**
107 * Special constructor for creating the dependable registry as part of
108 * another group (e.g., the replication manager group).
109 *
110 * Creates and exports a dependable registry replica on the local host that
111 * listen to incoming requests on an anonymous communication port.
112 * Objects contained in the <tt>conf</tt> map are used by Jgroup to
113 * configure the group communication system; the distributed system
114 * description must be included in this map object.
115 *
116 * @exception RemoteException
117 * If the registry could not be exported
118 * @exception JgroupException
119 * Generic exception raised by Jgroup
120 * @exception ConfigurationException
121 * Raised when the distributed system configuration object contains an error
122 */
123 public RegistryImpl(GroupManager gm)
124 throws RemoteException, JgroupException, ConfigurationException
125 {
126 this(gm, null, null);
127 /*
128 * Obtain the various layers required by the dependable registry
129 * from the group manager stack of the hosting application, and
130 * add the dependable registry object as a listener to these layers.
131 */
132 Layer layer = (Layer) gm.getService("EGMI");
133 layer.addListener(this);
134 layer = (Layer) gm.getService("SMS");
135 layer.addListener(this);
136 layer = (Layer) gm.getService("PGMS");
137 layer.addListener(this);
138 }
139
140 /**
141 * Creates and exports a dependable registry replica on the local host that
142 * listen to incoming requests on the port specified within the configuration
143 * object. Objects contained in the <tt>conf</tt> map are used by Jgroup to
144 * configure the group communication system; the distributed system
145 * description must be included in this map object.
146 *
147 * The new instance listens to incoming requests using a
148 * <code>ServerSocket</code> created from the supplied
149 * <code>RMIServerSocketFactory</code>. A client
150 * that receives a reference to this registry will use a
151 * <code>Socket</code> created from the supplied
152 * <code>RMIClientSocketFactory</code>.
153 *
154 * @param csf
155 * Client-side <code>Socket</code> factory used to make
156 * connections to the registry
157 * @param ssf
158 * Server-side <code>ServerSocket</code> factory used to
159 * accept connections to the registry
160 * @exception RemoteException
161 * If the registry could not be exported
162 * @exception JgroupException
163 * Generic exception raised by Jgroup
164 * @exception ConfigurationException
165 * Raised when the distributed system configuration object contains an error
166 */
167 public RegistryImpl(RMIClientSocketFactory csf, RMIServerSocketFactory ssf)
168 throws RemoteException, JgroupException, ConfigurationException
169 {
170 this(null, csf, ssf);
171 }
172
173 /**
174 * Creates and exports a dependable registry replica on the local host that
175 * listen to incoming requests on the port specified within the configuration
176 * object. Objects contained in the <tt>conf</tt> map are used by Jgroup to
177 * configure the group communication system; the distributed system
178 * description must be included in this map object.
179 *
180 * The new instance listens to incoming requests using a
181 * <code>ServerSocket</code> created from the supplied
182 * <code>RMIServerSocketFactory</code>. A client
183 * that receives a reference to this registry will use a
184 * <code>Socket</code> created from the supplied
185 * <code>RMIClientSocketFactory</code>.
186 *
187 * @param csf
188 * Client-side <code>Socket</code> factory used to make
189 * connections to the registry
190 * @param ssf
191 * Server-side <code>ServerSocket</code> factory used to
192 * accept connections to the registry
193 * @exception RemoteException
194 * If the registry could not be exported
195 * @exception JgroupException
196 * Generic exception raised by Jgroup
197 * @exception ConfigurationException
198 * Raised when the distributed system configuration object contains an error
199 */
200 public RegistryImpl(GroupManager gm, RMIClientSocketFactory csf, RMIServerSocketFactory ssf)
201 throws RemoteException, JgroupException, ConfigurationException
202 {
203 registrytable = new RegistryTable();
204 if (gm == null) {
205 gm = GroupManager.getGroupManager(this);
206 }
207 egmis = (ExternalGMIService) gm.getService(ExternalGMIService.class);
208 pgms = (MembershipService) gm.getService(MembershipService.class);
209 me = pgms.getMyIdentifier();
210 if (log.isDebugEnabled())
211 log.debug("Obtained identifier: " + me);
212 AppConfig app = AppConfig.getApplication(this);
213 int gid = app.getGroupId();
214 if (GroupManager.contains(gid)) {
215 /* The group manager is for the dependable registry only. */
216 pgms.join(gid);
217 }
218 Registry reg = BootstrapRegistry.getLocalRegistry(csf, ssf);
219 Remote handler = egmis.getBootstrapHandler();
220 reg.rebind(DEPENDABLE_REGISTRY_NAME, handler);
221 }
222
223
224 ////////////////////////////////////////////////////////////////////////////////////////////
225 // Main
226 ////////////////////////////////////////////////////////////////////////////////////////////
227
228 /**
229 * Main method of the class
230 */
231 public static void main(String argv[])
232 {
233 // Start registry
234 try {
235 ConfigManager.init();
236 DependableRegistry registry = new RegistryImpl();
237 log.info("Dependable registry ready...");
238 while (true) {
239 try { Thread.sleep(0x7fffffffffffffffL);
240 } catch(InterruptedException ex) {}
241 }
242 } catch(Exception exception) {
243 Abort.exit("Failed to start the dependable registry", exception, 1);
244 }
245 }
246
247
248 ////////////////////////////////////////////////////////////////////////////////////////////
249 // Methods from DependableRegistry
250 ////////////////////////////////////////////////////////////////////////////////////////////
251
252 @Multicast public IID bind(String service, RegistryEntry entry, Class serverClass)
253 throws RemoteException, AccessException
254 {
255 return bind(service, entry, serverClass, -1);
256 }
257
258 @Multicast public IID bind(String service, RegistryEntry entry, Class serverClass, long leaseTime)
259 throws RemoteException, AccessException
260 {
261 IID iid = egmis.getIdentifier();
262 if (log.isDebugEnabled())
263 log.debug("bind(): " + service + " " + iid.getVmid());
264 checkAccess("Registry.bind");
265 BindingList bl = new BindingList(service, entry, serverClass, iid, leaseTime);
266 registrytable.add(iid.getVmid(), bl);
267 return iid;
268 }
269
270 @Multicast public void unbind(IID iid)
271 throws RemoteException, NotBoundException, AccessException
272 {
273 if (log.isDebugEnabled())
274 log.debug("Registry.unbind() " + iid);
275 checkAccess("Registry.unbind");
276 registrytable.remove(iid.getVmid(), iid.getCounter());
277 }
278
279 @Anycast public String[] list()
280 throws RemoteException
281 {
282 if (log.isDebugEnabled())
283 log.debug("Registry.list()");
284 String[] services = registrytable.list();
285 for (int i = 0; i < services.length; i++) {
286 try {
287 clearExpiredLeases(services[i]);
288 } catch (NotBoundException nbe) {
289 log.error("Internal error: " + nbe.getMessage());
290 }
291 }
292 return registrytable.list();
293 }
294
295 @Anycast public Remote lookup(String serviceName)
296 throws RemoteException, NotBoundException
297 {
298 clearExpiredLeases(serviceName);
299 if (log.isDebugEnabled())
300 log.debug("Registry.lookup(): " + serviceName);
301 ServiceData service = registrytable.getService(serviceName);
302 RegistryEntry entry = service.getRegistryEntry();
303 InvocationHandler handler = entry.createInvocationHandler(serviceName, service.getEntries());
304 return Exporter.getProxy(service.getServerClass(), handler);
305 }
306
307 @Multicast public void refresh(IID iid)
308 throws RemoteException, AccessException, NotBoundException
309 {
310 /* Updates the timestamp in the BindingList node for the given IID. */
311 BindingList bl = registrytable.lookup(iid.getVmid(), iid.getCounter());
312 bl.timestamp = System.currentTimeMillis();
313 }
314
315
316 ////////////////////////////////////////////////////////////////////////////////////////////
317 // Methods from MembershipListener
318 ////////////////////////////////////////////////////////////////////////////////////////////
319
320 public void viewChange(View view)
321 {
322 if (log.isDebugEnabled())
323 log.debug("--- RegistryImpl.viewChange: ---" + view);
324 registrytable.viewChange(view);
325 }
326
327 public void hasLeft() { }
328
329 public void prepareChange() { }
330
331
332 ////////////////////////////////////////////////////////////////////////////////////////////
333 // Methods from ShutdownListener
334 ////////////////////////////////////////////////////////////////////////////////////////////
335
336 public void shutdown()
337 {
338 try {
339 if (log.isDebugEnabled())
340 log.info("RegistryImpl.shutdown: commenced...");
341 BootstrapRegistry.unbind(DEPENDABLE_REGISTRY_NAME);
342 pgms.leave();
343 if (log.isDebugEnabled())
344 log.info("RegistryImpl.shutdown: completed: " + DEPENDABLE_REGISTRY_NAME);
345 } catch (Exception e) {
346 log.warn("Unable to shutdown RegistryImpl", e);
347 }
348 }
349
350
351 ////////////////////////////////////////////////////////////////////////////////////////////
352 // Methods from MergingListener
353 ////////////////////////////////////////////////////////////////////////////////////////////
354
355 public Object getState(MemberId[] dests)
356 {
357 if (log.isDebugEnabled()) {
358 for (int i=0; i < dests.length; i++)
359 log.debug("getState dests["+i+"]: "+dests[i]);
360 }
361 return registrytable.getState(dests, me);
362 }
363
364 public void putState(Object obj, MemberId[] sources)
365 {
366 if (log.isDebugEnabled()) {
367 for (int i=0; i < sources.length; i++)
368 log.debug("putState sources["+i+"]: "+sources[i]);
369 }
370 try {
371 registrytable.putState((MergingData) obj, sources);
372 } catch (Exception e) {
373 log.error("Error when receiving a putState invocation", e);
374 }
375 }
376
377
378 ////////////////////////////////////////////////////////////////////////////////////////////
379 // Private methods
380 ////////////////////////////////////////////////////////////////////////////////////////////
381
382 /**
383 * Remove all expired leases for the given service.
384 *
385 * @param service
386 * The service for which to remove expired leases.
387 * @throws RemoteException
388 * @throws NotBoundException
389 */
390 private void clearExpiredLeases(String service)
391 throws RemoteException, NotBoundException
392 {
393 /* Gets IID and timestamp for the provided service name */
394 LeaseInfo leaseInfo = registrytable.getLookupInf(service);
395 /*
396 * Check if server has refreshed the lease according to given leastime.
397 * If leastime is set to -1, LeaseLayer is not used.
398 */
399 if (leaseInfo.getLeasetime() != -1) {
400 for (int j = 0; j < leaseInfo.size(); j++) {
401 if (leaseInfo.leaseExpired(j)) {
402 unbind(leaseInfo.getIID(j));
403 }
404 }
405 }
406 }
407
408 private static void checkAccess(String s)
409 throws AccessException
410 {
411 // TODO: Currently, bind is always permitted; we need to think if it is necessary to
412 // restrict the access to the hosts in the configuration file.
413 return;
414 }
415
416 } // END RegistryImpl