001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.core.service.wp;
028:
029: import java.util.Iterator;
030: import java.util.Map;
031: import java.util.Set;
032:
033: import org.cougaar.core.component.Service;
034: import org.cougaar.util.log.Logging;
035:
036: /**
037: * The white pages service provides access to the distributed name
038: * server.
039: * <p>
040: * The primary function of the white pages is to allow agents
041: * to register their message transport addresses and lookup the
042: * addresses of other agents. This service is the client-side
043: * resolver and is backed by a cache.
044: * <p>
045: * The main method of this class is:<pre>
046: * public abstract Response submit(Request req);
047: * </pre>
048: * This submits an request with an asynchronous response. The
049: * caller can attach callbacks to the response or block until
050: * the response result is set. All the other methods of the
051: * WhitePagesService are based upon the above "submit" method.
052: * <p>
053: * The white pages service currently does not support a "listener"
054: * API to watch for changes, primarily due to scalability concerns.
055: */
056: public abstract class WhitePagesService implements Service {
057:
058: //
059: // no-timeout variations:
060: //
061: // these methods have all been deprecated (bug 2875). Blocking
062: // calls tie up the threads in the Cougaar thread pool.
063: //
064: // Clients should either use the non-blocking cache-only methods:
065: // get(name, type, -1)
066: // getAll(name, -1)
067: // list(suffix, -1)
068: // flush(name, minAge, ae, uncache, prefetch, -1)
069: // or the asynchronous callback-based methods:
070: // get(name, type, callback)
071: // getAll(name, callback)
072: // list(suffix, callback)
073: // flush(name, minAge, ae, uncache, prefetch, callback)
074: // bind(ae, callback)
075: // rebind(ae, callback)
076: // hint(ae, callback)
077: // unbind(ae, callback)
078: // unhint(ae, callback)
079: //
080: // If the answer is already in the cache then the callback will
081: // be invoked immediately. Flush can use an asynchronous callback,
082: // even though it's guaranteed not to block.
083: //
084: // In Cougaar 10.4.1 the cache-only methods will initiate a
085: // background fetch for the data, which will eventually be entered
086: // into the cache. However, the cache has a fixed LRU size and
087: // will expire entries, so if the result is required then a
088: // callback should be used.
089: //
090: // If the client must block until the result is known, which is
091: // discouraged, it can write a callback like:
092: // Response[] answer = new Response[1];
093: // Callback callback = new Callback() {
094: // public void execute(Response res) {
095: // synchronized (answer) {
096: // answer[0] = res;
097: // answer.notifyAll();
098: // }
099: // }
100: // };
101: // whitePagesService.getAll("testme", callback);
102: // Response res;
103: // synchronized (answer) {
104: // while (answer[0] == null) {
105: // answer.wait();
106: // }
107: // res = answer[0];
108: // }
109: //
110: // In Cougaar 10.4.2+ the following blocking methods will be
111: // removed and the method signatures will be *reused* for the
112: // cache-only APIs (i.e. the calls with a -1 as the last
113: // parameter).
114: //
115:
116: /**
117: * @see Request.Get
118: * @deprecated use a non-blocking cache-only lookup or callback
119: */
120: public final AddressEntry get(String name, String type)
121: throws Exception {
122: return get(name, type, 0);
123: }
124:
125: /**
126: * @see Request.GetAll
127: * @deprecated use a non-blocking cache-only lookup or callback
128: */
129: public final Map getAll(String name) throws Exception {
130: return getAll(name, 0);
131: }
132:
133: /**
134: * @see Request.List
135: * @deprecated use a non-blocking cache-only lookup or callback
136: */
137: public final Set list(String suffix) throws Exception {
138: return list(suffix, 0);
139: }
140:
141: /**
142: * @see Request.Flush
143: * @deprecated use a non-blocking cache-only lookup or callback
144: */
145: public final boolean flush(String name, long minAge,
146: AddressEntry ae, boolean uncache, boolean prefetch)
147: throws Exception {
148: return flush(name, minAge, ae, uncache, prefetch, 0);
149: }
150:
151: /**
152: * @see Request.Bind
153: * @deprecated use a non-blocking cache-only lookup or callback
154: */
155: public final void bind(AddressEntry ae) throws Exception {
156: bind(ae, 0);
157: }
158:
159: /**
160: * @see Request.Bind
161: * @deprecated use a non-blocking cache-only lookup or callback
162: */
163: public final void rebind(AddressEntry ae) throws Exception {
164: rebind(ae, 0);
165: }
166:
167: /**
168: * @see Request.Bind
169: * @deprecated use a non-blocking cache-only lookup or callback
170: */
171: public final void hint(AddressEntry ae) throws Exception {
172: hint(ae, 0);
173: }
174:
175: /**
176: * @see Request.Unbind
177: * @deprecated use a non-blocking cache-only lookup or callback
178: */
179: public final void unbind(AddressEntry ae) throws Exception {
180: unbind(ae, 0);
181: }
182:
183: /**
184: * @see Request.Unbind
185: * @deprecated use a non-blocking cache-only lookup or callback
186: */
187: public final void unhint(AddressEntry ae) throws Exception {
188: unhint(ae, 0);
189: }
190:
191: //
192: // callback variations:
193: //
194:
195: /** @see Request.Get */
196: public final void get(String name, String type, Callback callback) {
197: submit(new Request.Get(Request.NONE, name, type), callback);
198: }
199:
200: /** @see Request.GetAll */
201: public final void getAll(String name, Callback callback) {
202: submit(new Request.GetAll(Request.NONE, name), callback);
203: }
204:
205: /** @see Request.List */
206: public final void list(String suffix, Callback callback) {
207: submit(new Request.List(Request.NONE, suffix), callback);
208: }
209:
210: /** @see Request.Flush */
211: public final void flush(String name, long minAge, AddressEntry ae,
212: boolean uncache, boolean prefetch, Callback callback)
213: throws Exception {
214: submit(new Request.Flush(Request.CACHE_ONLY, name, minAge, ae,
215: uncache, prefetch), callback);
216: }
217:
218: /** @see Request.Bind */
219: public final void bind(AddressEntry ae, Callback callback) {
220: submit(new Request.Bind(Request.NONE, ae, false, false),
221: callback);
222: }
223:
224: /** @see Request.Bind */
225: public final void rebind(AddressEntry ae, Callback callback) {
226: submit(new Request.Bind(Request.NONE, ae, true, false),
227: callback);
228: }
229:
230: /** @see Request.Bind */
231: public final void hint(AddressEntry ae, Callback callback) {
232: submit(new Request.Bind(Request.CACHE_ONLY, ae, true, false),
233: callback);
234: }
235:
236: /** @see Request.Unbind */
237: public final void unbind(AddressEntry ae, Callback callback) {
238: submit(new Request.Unbind(Request.NONE, ae), callback);
239: }
240:
241: /** @see Request.Unbind */
242: public final void unhint(AddressEntry ae, Callback callback) {
243: submit(new Request.Unbind(Request.CACHE_ONLY, ae), callback);
244: }
245:
246: //
247: // timeout variations:
248: //
249: // any timeout duration that's greater than or equal to zero
250: // is deprecated. Only negative timeouts (cache-only) are
251: // not deprecated.
252: //
253:
254: public static final class TimeoutException extends
255: InterruptedException {
256: private final boolean b;
257:
258: public TimeoutException(boolean b) {
259: super ("Timeout on " + (b ? "Request" : "Response"));
260: this .b = b;
261: }
262:
263: /**
264: * @return true if the Request timeout was too short, else
265: * return false if the wait for the Response was too short.
266: */
267: public boolean isRequestTimeout() {
268: return b;
269: }
270: }
271:
272: /**
273: * @see Request.Get
274: * @param timeout non-negative values are deprecated
275: */
276: public final AddressEntry get(String name, String type, long timeout)
277: throws Exception {
278: int options = Request.NONE;
279: if (timeout < 0) {
280: options |= Request.CACHE_ONLY;
281: timeout = 0;
282: }
283: Request.Get req = new Request.Get(options, name, type);
284: Response.Get res = (Response.Get) assertSubmit(req, timeout);
285: return res.getAddressEntry();
286: }
287:
288: /**
289: * @see Request.GetAll
290: * @param timeout non-negative values are deprecated
291: */
292: public final Map getAll(String name, long timeout) throws Exception {
293: int options = Request.NONE;
294: if (timeout < 0) {
295: options |= Request.CACHE_ONLY;
296: timeout = 0;
297: }
298: Request.GetAll req = new Request.GetAll(options, name);
299: Response.GetAll res = (Response.GetAll) assertSubmit(req,
300: timeout);
301: return res.getAddressEntries();
302: }
303:
304: /**
305: * @see Request.List
306: * @param timeout non-negative values are deprecated
307: */
308: public final Set list(String suffix, long timeout) throws Exception {
309: int options = Request.NONE;
310: if (timeout < 0) {
311: options |= Request.CACHE_ONLY;
312: timeout = 0;
313: }
314: Request.List req = new Request.List(options, suffix);
315: Response.List res = (Response.List) assertSubmit(req, timeout);
316: return res.getNames();
317: }
318:
319: /**
320: * @see Request.Flush
321: * @param timeout non-negative values are deprecated
322: */
323: public final boolean flush(String name, long minAge,
324: AddressEntry ae, boolean uncache, boolean prefetch,
325: long timeout) throws Exception {
326: int options = Request.NONE;
327: if (timeout < 0) {
328: options |= Request.CACHE_ONLY;
329: timeout = 0;
330: }
331: Request.Flush req = new Request.Flush(options, name, minAge,
332: ae, uncache, prefetch);
333: Response.Flush res = (Response.Flush) assertSubmit(req, timeout);
334: return res.modifiedCache();
335: }
336:
337: /**
338: * @see Request.Bind
339: * @param timeout non-negative values are deprecated
340: */
341: public final void bind(AddressEntry ae, long timeout)
342: throws Exception {
343: if (timeout < 0) {
344: throw new IllegalArgumentException("Negative bind timeout");
345: }
346: Request.Bind req = new Request.Bind(Request.NONE, ae, false,
347: false);
348: Response.Bind res = (Response.Bind) assertSubmit(req, timeout);
349: if (!res.didBind()) {
350: throw new RuntimeException("Bind failed: " + res);
351: }
352: }
353:
354: /**
355: * @see Request.Bind
356: * @param timeout non-negative values are deprecated
357: */
358: public final void rebind(AddressEntry ae, long timeout)
359: throws Exception {
360: if (timeout < 0) {
361: throw new IllegalArgumentException(
362: "Negative rebind timeout");
363: }
364: Request.Bind req = new Request.Bind(Request.NONE, ae, true,
365: false);
366: Response.Bind res = (Response.Bind) assertSubmit(req, timeout);
367: if (!res.didBind()) {
368: throw new RuntimeException("Rebind failed: " + res);
369: }
370: }
371:
372: /**
373: * @see Request.Bind
374: * @param timeout non-negative values are deprecated
375: */
376: public final void hint(AddressEntry ae, long timeout)
377: throws Exception {
378: if (timeout < 0) {
379: timeout = 0; // timeout doesn't really apply here...
380: }
381: Request.Bind req = new Request.Bind(Request.CACHE_ONLY, ae,
382: true, false);
383: Response.Bind res = (Response.Bind) assertSubmit(req, timeout);
384: if (!res.didBind()) {
385: throw new RuntimeException("Hint failed: " + res);
386: }
387: }
388:
389: /**
390: * @see Request.Unbind
391: * @param timeout non-negative values are deprecated
392: */
393: public final void unbind(AddressEntry ae, long timeout)
394: throws Exception {
395: if (timeout < 0) {
396: throw new IllegalArgumentException(
397: "Negative unbind timeout");
398: }
399: Request.Unbind req = new Request.Unbind(Request.NONE, ae);
400: assertSubmit(req, timeout);
401: }
402:
403: /**
404: * @see Request.Unbind
405: * @param timeout non-negative values are deprecated
406: */
407: public final void unhint(AddressEntry ae, long timeout)
408: throws Exception {
409: if (timeout < 0) {
410: timeout = 0; // timeout doesn't really apply here...
411: }
412: Request.Unbind req = new Request.Unbind(Request.CACHE_ONLY, ae);
413: assertSubmit(req, timeout);
414: }
415:
416: /**
417: * Submit a request and return the response if it is completed
418: * within the request's timeout and successful, otherwise throw
419: * an exception.
420: */
421: public final Response assertSubmit(Request req, long timeout)
422: throws Exception {
423: Response res = submit(req);
424: if (res.waitForIsAvailable(timeout)) {
425: if (res.isSuccess()) {
426: return res;
427: } else if (res.isTimeout()) {
428: throw new TimeoutException(true);
429: } else {
430: throw res.getException();
431: }
432: } else {
433: throw new TimeoutException(false);
434: }
435: }
436:
437: /**
438: * Submit with a callback.
439: * <p>
440: * Equivalent to:<pre>
441: * Response res = submit(req);
442: * res.addCallback(c);
443: * return res;
444: * </pre>
445: */
446: public final Response submit(Request req, Callback c) {
447: Response res = submit(req);
448: res.addCallback(c);
449: return res;
450: }
451:
452: /**
453: * Submit a request, get back a "future reply" response.
454: * <p>
455: * An example cache-only non-blocking usage:<pre>
456: * try {
457: * Map m = wps.getAll("foo", -1);
458: * System.out.println("cached entries for foo: "+m);
459: * } catch (Exception e) {
460: * System.out.println("failed: "+e);
461: * }
462: * </pre>
463: * <p>
464: * An example asynchronous "callback" usage:<pre>
465: * Request req = new Request.GetAll(Request.NONE, "foo");
466: * Response r = wps.submit(req);
467: * Callback callback = new Callback() {
468: * public void execute(Response res) {
469: * // note that (res == r)
470: * if (res.isSuccess()) {
471: * Map m = ((Response.GetAll) res).getAddressEntries();
472: * System.out.println("got all entries for foo: "+m);
473: * } else {
474: * System.out.println("failed: "+res);
475: * }
476: * }
477: * };
478: * // add callback, will execute immediately if
479: * // there is already an answer
480: * r.addCallback(callback);
481: * // keep going
482: * </pre>
483: *
484: * @param req the non-null request
485: * @return a non-null response
486: */
487: public abstract Response submit(Request req);
488:
489: //
490: // deprecated, to be removed in Cougaar 10.4.1+
491: //
492:
493: /** @deprecated use "Map getAll(name)" */
494: public final AddressEntry[] get(String name) throws Exception {
495: return get(name, 0);
496: }
497:
498: /** @deprecated use "get(name,type)" */
499: public final AddressEntry get(String name, Application app,
500: String scheme) throws Exception {
501: return get(name, app, scheme, 0);
502: }
503:
504: /** @deprecated use "Map getAll(name,timeout)" */
505: public final AddressEntry[] get(String name, long timeout)
506: throws Exception {
507: Map m = getAll(name, 0);
508: AddressEntry[] ret;
509: if (m == null || m.isEmpty()) {
510: ret = new AddressEntry[0];
511: } else {
512: int msize = m.size();
513: ret = new AddressEntry[msize];
514: int i = 0;
515: for (Iterator iter = m.values().iterator(); iter.hasNext();) {
516: AddressEntry aei = (AddressEntry) iter.next();
517: ret[i++] = aei;
518: }
519: }
520: return ret;
521: }
522:
523: /** @deprecated use "get(name,type,timeout)" */
524: public final AddressEntry get(String name, Application app,
525: String scheme, long timeout) throws Exception {
526: String origType = app.toString();
527: String type = origType;
528: // backwards compatibility hack:
529: if ("topology".equals(type) && "version".equals(scheme)) {
530: type = "version";
531: }
532: if (type != origType) {
533: Exception e = new RuntimeException("White pages \"get("
534: + name + ", " + origType + ", " + scheme
535: + ")\" should be replaced with" + "\"get(" + name
536: + ", " + type + ")\"");
537: Logging.getLogger(getClass()).warn(null, e);
538: }
539: AddressEntry ae = get(name, type, timeout);
540: if (ae != null && !scheme.equals(ae.getURI().getScheme())) {
541: Exception e = new RuntimeException("White pages \"get("
542: + name + ", " + origType + ", " + scheme
543: + ")\" returned an entry with a different"
544: + " URI scheme: " + ae);
545: Logging.getLogger(getClass()).warn(null, e);
546: }
547: return ae;
548: }
549: }
|