Source code: com/fetish/toolkit/FetishImpl.java
1 package com.fetish.toolkit;
2
3 import java.util.Hashtable;
4 import java.net.*;
5 import java.rmi.Remote;
6 import java.rmi.RemoteException;
7 import com.fetish.directory.LUSManager;
8 import com.fetish.directory.Directory;
9 import com.fetish.directory.SearchCriteria;
10 import com.fetish.directory.SearchCriteriaInterface;
11 import com.fetish.directory.tool.InterfaceEntry;
12 import net.jini.core.discovery.LookupLocator;
13 import net.jini.core.lookup.ServiceID;
14 import net.jini.core.lookup.ServiceItem;
15 import net.jini.core.lookup.ServiceRegistrar;
16 import net.jini.core.lookup.ServiceRegistration;
17 import net.jini.core.lookup.ServiceTemplate;
18 import net.jini.core.lookup.ServiceMatches;
19 import net.jini.core.entry.Entry;
20 import net.jini.core.lease.Lease;
21 import net.jini.lease.LeaseRenewalManager;
22
23 /**
24 * The Fetish Toolkit helper class.
25 * This class provides with methods that allow an easy use of the Fetish
26 * features, such as registering and deregistering a service and lookup
27 * of Fetish services by ServiceID, attributes and/or interface/s implemented.
28 */
29 public class FetishImpl /*extends Fetish*/ {
30
31 private Hashtable registry = null;
32 private FetishLeaseRenewalManager lrm = null;
33
34 /**
35 * Simple constructor.
36 * Sets up the internal registry and the lease renewal manager.
37 */
38 public FetishImpl() {
39 registry = new Hashtable();
40 lrm = new FetishLeaseRenewalManager( this );
41 }
42
43 /**
44 * Obtains the FetishLeaseRenewalManager for the current toolkit class.
45 * The FetishLeaseRenewalManager is a class that allows renewing of
46 * Fetish leases, a feature that gives Fetish the power of being
47 * self-healing.<p>A class on the service side
48 * must renew the lease for the registered service. If the lease expires
49 * the service will be removed from the Fetish registry.<p>
50 * If a service crashes it will eventually
51 * fail to renew the lease; when it expires the service will be removed,
52 * so nobody can retrieve a reference to it. One or more listeners
53 * can be set to listen for lease expiration events, so when a service
54 * crashes and fails to renew its lease the listener can trigger the
55 * suitable mechanisms to solve the situation (for example, reload
56 * the service with the same ServiceID, notify the administrator, etc.)
57 */
58 private FetishLeaseRenewalManager getLrm() {
59 if( lrm == null ) {
60 lrm = new FetishLeaseRenewalManager( this );
61 }
62 return lrm;
63 }
64
65 /**
66 * Gets the Fetish Toolkit registry of ServiceIDs and leases.
67 * The Fetish Toolkit registry holds the leases for all services
68 * registered using this instance of the toolkit.<br>
69 * Such leases <b>must</b> be in the same JVM the service executes
70 * in, so if the whole JVM crashes the leases will eventually expire.
71 */
72 private Hashtable getRegistry() {
73 if( registry == null ) {
74 registry = new Hashtable();
75 }
76 return registry;
77 }
78
79 /**
80 * Obtains a reference to the Fada node given its url.
81 * This reference can the be used to cancel a lease, thus deleting
82 * the service it belongs to.
83 * @param url The url of the node to be contacted. It must be in the
84 * form <host>[:<port>]. No protocol prefix must be used.
85 */
86 private static LUSManager getLUSManager( String url )
87 throws RemoteException {
88 try {
89 try {
90 // If exception occurs, it already has the protocol jini
91 URL myUrl = new URL( "http://" + url );
92 int port = myUrl.getPort();
93 if( port == -1 ) {
94 url = "jini://" + myUrl.getHost() + ":4160";
95 } else {
96 url = "jini://" + myUrl.getHost() + ":" + myUrl.getPort();
97 }
98 } catch( MalformedURLException mue ) {
99 }
100 LookupLocator locator = new LookupLocator( url );
101 ServiceRegistrar registrar = locator.getRegistrar();
102 Class[] classes = { LUSManager.class };
103 ServiceTemplate template = new ServiceTemplate(
104 null,
105 classes,
106 null
107 );
108 Object lusm = registrar.lookup( template );
109 return ( LUSManager )lusm;
110 } catch( Exception e ) {
111 throw new RemoteException( "Unable to find LUSManager\n" +
112 "Reason: " + e.getMessage() );
113 }
114 }
115
116 /**
117 * Obtains a reference to the Fada node given its url.
118 * This reference can the be used to cancel a lease, thus deleting
119 * the service it belongs to.
120 * @param url The url of the node to be contacted. It must be in the
121 * form <host>[:<port>]. No protocol prefix must be used.
122 */
123 private static Directory getDirectory( String url )
124 throws RemoteException {
125 try {
126 try {
127 // If exception occurs, it already has the protocol jini
128 URL myUrl = new URL( "http://" + url );
129 int port = myUrl.getPort();
130 if( port == -1 ) {
131 url = "jini://" + myUrl.getHost() + ":4160";
132 } else {
133 url = "jini://" + myUrl.getHost() + ":" + myUrl.getPort();
134 }
135 } catch( MalformedURLException mue ) {
136 }
137 LookupLocator locator = new LookupLocator( url );
138 ServiceRegistrar registrar = locator.getRegistrar();
139 Class[] classes = { Directory.class };
140 ServiceTemplate template = new ServiceTemplate(
141 null,
142 classes,
143 null
144 );
145 Object dir = registrar.lookup( template );
146 return ( Directory )dir;
147 } catch( Exception e ) {
148 throw new RemoteException( "Unable to find LUSManager\n" +
149 "Reason: " + e.getMessage() );
150 }
151 }
152
153 /**
154 * Registers a service in the FADA architecture.
155 * This method registers the service as a Jini service in the LUS the
156 * FADA node is registered into, and keeps the Jini lease in the
157 * registry for renewal or user retrieval.
158 * @param url The url of the node to be contacted. It must be in the
159 * form <host>[:<port>]. No protocol prefix must be used.
160 * @param sid The desired ServiceID of the service to be registered.
161 * @param service The proxy for the service to be stored at FADA.
162 * @param entries Array of Jini entries, defining more accurately the
163 * service. It will be used at lookup time by third parties.
164 */
165 public ServiceID register(
166 String url,
167 ServiceID sid,
168 Object service,
169 Entry[] entries
170 //long leaseDuration
171 ) throws RemoteException {
172
173 ServiceRegistration result = null;
174 Hashtable registry = getRegistry();
175 ServiceID resultSid = null;
176 try {
177 Directory lusm = getDirectory( url );
178 ServiceItem serviceItem = new ServiceItem( sid, service, entries );
179 result = lusm.register( serviceItem , Lease.ANY );
180 //result = lusm.register( serviceItem , 30000 );
181 Lease lease = result.getLease();
182 resultSid = result.getServiceID();
183 lrm.renewFor( lease, resultSid, url );
184 RegistrationItem ri = new RegistrationItem( result, url, serviceItem );
185 registry.put( resultSid, ri );
186 } catch( Exception e ) {
187 throw new RemoteException( e.getMessage() );
188 }
189 return resultSid;
190 }
191
192 /**
193 * Registers a service whose lease has expired.
194 * <br>
195 * This method is provided in case the lease object can't be renewed
196 * in time. The main cause for this situation is that network delay
197 * has suddenly become very long, and the Kalman filter hasn't been
198 * able to estimate this very low probable situation. The reregister
199 * method will register the service again, keeping all data as if
200 * has never been deregistered.
201 * @param sid The ServiceID of the service proxy whose lease has expired.
202 * @throws RemoteException if reregistration was not possible.
203 */
204 public void reregister( ServiceID sid ) throws RemoteException
205 {
206 RegistrationItem ri = ( RegistrationItem )registry.get( sid );
207 registry.remove( sid );
208 String url = ri.getUrl();
209 ServiceItem serviceItem = ri.getServiceItem();
210 serviceItem.serviceID = sid;
211 Directory lusm = getDirectory( url );
212 ServiceRegistration result = lusm.register( serviceItem , Lease.ANY );
213 Lease lease = result.getLease();
214 ServiceID resultSid = result.getServiceID();
215 ri = new RegistrationItem(
216 result, url, serviceItem );
217 registry.put( resultSid, ri );
218 lrm.renewFor( lease, resultSid, url );
219 }
220
221 /**
222 * Deletes the service identified by the ServiceID sid.
223 * This method cancels the Jini lease for the service identified by
224 * the ServiceID sid, thus deleting its entry in the Jini registry.
225 * It also deletes the entry in the Fetish registry.
226 * @param url The url of the node to be contacted. It must be in the
227 * form <host>[:<port>]. No protocol prefix must be used.
228 * @param sid The ServiceID of the service to deregister.
229 * @return <code>false</code> if
230 * the deregistration was not possible (unexisting ServiceID,
231 * already deregistered service, communication error with any of the
232 * involved parties, etc); <code>true</code> otherwise.
233 */
234 public boolean unregister(
235 String url,
236 ServiceID sid
237 ) throws RemoteException {
238 boolean result = false;
239 try {
240 getDirectory( url ).unregister( sid );
241 Hashtable reg = getRegistry();
242 if( reg.containsKey( sid ) ) {
243 RegistrationItem ri = ( RegistrationItem )registry.get( sid );
244 Lease l = ri.getSr().getLease();
245 reg.remove( sid );
246 getLrm().cancel( l );
247 }
248 result = true;
249 } catch( Exception e ) {
250 throw new RemoteException(
251 "Unable to unregister: "
252 + e.getMessage()
253 );
254 }
255 return result;
256 }
257
258 /**
259 * Returns an array with the service proxies that match the search criteria.
260 * The FADA node will return an array with all services that match the
261 * search criteria. A service matches the criteria if:<br>
262 * <ul>
263 * <li>It implements the interface <code>service</code> <i>and</i></li>
264 * <li>It has at least one matching entry for all specified
265 * <code>entries</code> <i>and</i></li>
266 * <li>Its service id matches <code>sid</code></li>
267 * </ul>
268 * Any of the above search criteria may be null, meaning
269 * match all (serves as a wildcard).
270 * @param url The url of the node to be contacted. It must be in the
271 * form <host>[:<port>]. No protocol prefix must be used.
272 * @param service The interface of the service we want to look for.
273 * May be null.
274 * @param entries The Jini Entry class array of attributes that may
275 * identify a service. May be null.
276 * @param sid The Jini ServiceID of a previously registered service.
277 * May be null.
278 * @throws RemoteException If there was a communication error with any
279 * of the intervening parties (the FADA node, the Jini LUS, the
280 * physical connection itself, etc)
281 */
282 public static Object[] lookup( String url,
283 Class service,
284 Entry[] entries,
285 ServiceID sid
286 ) throws RemoteException {
287 Object[] result = new Object[0];
288 Directory dir = getDirectory( url );
289 Class[] classServ = new Class[1];
290 if( service != null ) {
291 InterfaceEntry intEnt = new InterfaceEntry( service.getName() );
292 Entry[] newEntries = null;
293 if( entries == null ) {
294 newEntries = new Entry[ 1 ];
295 newEntries[ 0 ] = intEnt;
296 } else {
297 newEntries = new Entry[ entries.length + 1 ];
298 for( int i = 0; i<entries.length; i++ ) {
299 newEntries[i] = entries[i];
300 }
301 newEntries[ entries.length ] = intEnt;
302 }
303 entries = newEntries;
304 } else {
305 }
306 SearchCriteriaInterface sc = new SearchCriteria(
307 null,
308 entries,
309 sid
310 );
311 ServiceMatches sm = null;
312 try {
313 sm = dir.lookup( sc, 65536, 3600000L, 65536 );
314 result = new Object[ sm.totalMatches ];
315 for( int i = 0; i < sm.totalMatches ; i++ )
316 result[i] = sm.items[i].service;
317 } catch( Exception e ) {
318 }
319 return result;
320 }
321
322 /**
323 * Makes a logical connection between any two FADA nodes.
324 * Lookup processes will span through all the graph of FADA nodes.
325 * This method provides with a mechanism to connect FADA nodes to
326 * create a graph.<br>
327 * After execution of this method, all searches started from this node
328 * will also search the rest of the graph, and all services registered
329 * into this node will be accessible from any point in the graph.
330 * @param url1 The url of the node to be contacted. It must be in the
331 * form <host>[:<port>]. No protocol prefix must be used.
332 * @param url2 The url of the node to be contacted. It must be in the
333 * form <host>[:<port>]. No protocol prefix must be used.
334 * @throws RemoteException If there is any communication error with any
335 * of the nodes.
336 */
337 public static boolean connect( String url1, String url2 )
338 throws RemoteException {
339 LUSManager l1 = getLUSManager( url1 );
340 LUSManager l2 = getLUSManager( url2 );
341 return l1.connect( l2 );
342 }
343
344 /**
345 * Obtains the Jini Lease of the service identified by sid.
346 * This Lease can then be administered by some other party, or be
347 * cancelled.
348 * <b>Warning</b>: cancelling a lease obtained by this method removes the
349 * service the lease belongs to from the Jini registry, but <b>NOT</b> from
350 * the Fetish registry. This practice is dangerous and therefore
351 * discouraged.
352 * @param sid The Jini ServiceID of the service we want to obtain the lease
353 * for
354 * @ throws RemoteException If there is any communication error.
355 */
356 public Lease getFetishLease( ServiceID sid ) throws RemoteException {
357 Hashtable registry = getRegistry();
358 if( !registry.containsKey( sid ) )
359 return null;
360 RegistrationItem ri = ( RegistrationItem )registry.get( sid );
361 ServiceRegistration sr = ri.getSr();
362 Lease lease = sr.getLease();
363 try {
364 //getLrm().remove( lease );
365 } catch( Exception e ) {
366 }
367 return lease;
368 }
369
370 /**
371 * Obtains the Jini ServiceID for a registered service.
372 * This ServiceID should be stored by the service for future registrations,
373 * in order to maintain a consistency.
374 * @param sr The Jini ServiceRegistration obtained in the register method.
375 * @return The ServiceID for the registered service.
376 */
377 public static ServiceID getSID( ServiceRegistration sr ) {
378 return sr.getServiceID();
379 }
380
381 private static long parseChar( char c ) {
382 if( ( c >= '0' ) && ( c <= '9' ) ){
383 return (long)(c-'0');
384 } else {
385 return (long)( 10 + (long)( c - 'a' ) );
386 }
387 }
388
389 /**
390 * Converts a string representing a ServiceID into an instance of ServiceID.
391 * <br>
392 * Takes a string representing a valid ServiceID and constructs a
393 * ServiceID instance with it. It won't check the string is a real
394 * ServiceID, so errors may occur.
395 * @param sid The String that represents the ServiceID
396 * @return The ServiceID whose String representation matches the parameter
397 * sid
398 */
399 public static ServiceID parseSid( String sid ) {
400 long l1=0, l2=0;
401 String strippedSid = sid.substring( 0, 8 ) + sid.substring( 9, 13 ) +
402 sid.substring( 14, 18 ) + sid.substring( 19, 23 ) + sid.substring( 24, 36 );
403 int i = 0;
404 for( ; i<16; i++ ) {
405 l1 = ( l1 << 4 ) + parseChar( strippedSid.charAt( i ) );
406 }
407 for( ; i<32; i++ ) {
408 l2 = ( l2 << 4 ) + parseChar( strippedSid.charAt( i ) );
409 }
410 return new ServiceID( l1, l2 );
411 }
412
413 private class RegistrationItem
414 {
415
416 ServiceRegistration sr;
417 String url;
418 ServiceItem si;
419
420 public RegistrationItem( ServiceRegistration sr, String url, ServiceItem si )
421 {
422 this.sr = sr;
423 this.url = url;
424 this.si = si;
425 }
426
427 public ServiceRegistration getSr()
428 {
429 return this.sr;
430 }
431
432 public void setSr( ServiceRegistration sr )
433 {
434 this.sr = sr;
435 }
436
437 public String getUrl()
438 {
439 return this.url;
440 }
441
442 public void setUrl( String url )
443 {
444 this.url = url;
445 }
446
447 public void setServiceItem( ServiceItem si )
448 {
449 this.si = si;
450 }
451
452 public ServiceItem getServiceItem()
453 {
454 return this.si;
455 }
456
457 }
458
459 }