001: /*
002: * <copyright>
003: *
004: * Copyright 2002-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.wp.bootstrap;
028:
029: import java.util.HashMap;
030: import java.util.Iterator;
031: import java.util.Map;
032: import java.util.Set;
033:
034: import org.cougaar.core.component.Component;
035: import org.cougaar.core.component.Service;
036: import org.cougaar.core.component.ServiceBroker;
037: import org.cougaar.core.component.ServiceProvider;
038: import org.cougaar.core.mts.AgentStatusService;
039: import org.cougaar.core.mts.SimpleMessageAddress;
040: import org.cougaar.core.node.NodeControlService;
041: import org.cougaar.core.service.LoggingService;
042: import org.cougaar.core.service.ThreadService;
043: import org.cougaar.core.service.wp.WhitePagesService;
044: import org.cougaar.core.thread.Schedulable;
045: import org.cougaar.core.wp.Parameters;
046: import org.cougaar.util.GenericStateModelAdapter;
047:
048: /**
049: * This component advertises the {@link EnsureIsFoundService}, which
050: * allows servers to ensures that their peers are reachable.
051: * <p>
052: * This implementation maintains the {@link Set} of ensured names
053: * and, if non-empty, continually polls (non-blocking) the
054: * local {@link WhitePagesService} to make sure the {@link
055: * WhitePagesService#getAll(String,long)}s return non-empty data,
056: * otherwise it starts the {@link BootstrapService} until the
057: * data is found.
058: * <p>
059: * This is slightly inefficient for a couple reasons:
060: * <ol><li>
061: * We're polling our cache even if the names are not necessary at all
062: * times (e.g. the MTS is still using the data if found a long time
063: * ago)
064: * </li><li>
065: * We're restarting the full bootstrap instead of indicating which
066: * specific name(s) we're looking for (e.g. a discoverer just for
067: * agent "A" can't help us find agent "B").
068: * </li><li>
069: * We wait until the next poll cycle to stop our search instead
070: * of stopping when the cache finds the data.
071: * </li></ol>
072: * However, this code only runs on nodes containing servers, and
073: * the data is typically in the cache, so this shouldn't be a real
074: * problem.
075: *
076: * @property org.cougaar.core.wp.bootstrap.ensureIsFound.checkNamesPeriod
077: * Specify how often to check for WP server names.
078: * Set to 45 seconds by default.
079: *
080: * @property org.cougaar.core.wp.bootstrap.ensureIsFound.checkMtsReachability
081: * Specify if MTS-reachability should be tested when checking WP server names.
082: * Set to true by default. Should never be set to false (see bug 3907).
083: */
084: public class EnsureIsFoundManager extends GenericStateModelAdapter
085: implements Component {
086: private ServiceBroker sb;
087: private LoggingService logger;
088: private ThreadService threadService;
089: private ServiceBroker rootsb;
090:
091: private EnsureIsFoundManagerConfig config;
092:
093: private Schedulable checkNamesThread;
094:
095: private BootstrapService bootstrapService;
096:
097: private WhitePagesService wp;
098:
099: private EnsureIsFoundSP ensureSP;
100:
101: private final Map table = new HashMap();
102:
103: // locked by table
104: private boolean searching;
105:
106: private AgentStatusService agentStatusService;
107:
108: public void setParameter(Object o) {
109: configure(o);
110: }
111:
112: public void setServiceBroker(ServiceBroker sb) {
113: this .sb = sb;
114: }
115:
116: public void setLoggingService(LoggingService logger) {
117: this .logger = logger;
118: }
119:
120: public void setNodeControlService(NodeControlService ncs) {
121: rootsb = (ncs == null ? null : ncs.getRootServiceBroker());
122: }
123:
124: public void setThreadService(ThreadService threadService) {
125: this .threadService = threadService;
126: }
127:
128: public void setBootstrapService(BootstrapService bootstrapService) {
129: this .bootstrapService = bootstrapService;
130: }
131:
132: public void setWhitePagesService(WhitePagesService wp) {
133: this .wp = wp;
134: }
135:
136: private void configure(Object o) {
137: if (config != null) {
138: return;
139: }
140: config = new EnsureIsFoundManagerConfig(o);
141: }
142:
143: public void load() {
144: super .load();
145:
146: configure(null);
147:
148: // get the AgentStatusService in load, *but* it's loaded after the
149: // WP, so we must use a ServiceAvailableLister.
150: ServiceFinder.Callback sfc = new ServiceFinder.Callback() {
151: public void foundService(Service s) {
152: EnsureIsFoundManager.this .foundService(s);
153: }
154: };
155: ServiceFinder.findServiceLater(sb, AgentStatusService.class,
156: null, sfc);
157:
158: // advertise our service
159: ensureSP = new EnsureIsFoundSP();
160: rootsb.addService(EnsureIsFoundService.class, ensureSP);
161: }
162:
163: public void unload() {
164: if (ensureSP != null) {
165: rootsb.revokeService(EnsureIsFoundService.class, ensureSP);
166: ensureSP = null;
167: }
168: if (bootstrapService != null) {
169: sb.releaseService(this , BootstrapService.class,
170: bootstrapService);
171: }
172: if (logger != null) {
173: sb.releaseService(this , LoggingService.class, logger);
174: logger = null;
175: }
176: if (agentStatusService != null) {
177: sb.releaseService(this , AgentStatusService.class,
178: agentStatusService);
179: agentStatusService = null;
180: }
181: super .unload();
182: }
183:
184: private void add(String name) {
185: synchronized (table) {
186: Entry e = (Entry) table.get(name);
187: if (logger.isDetailEnabled()) {
188: logger.detail("add(" + name + "): " + e);
189: }
190: if (e == null) {
191: e = new Entry();
192: table.put(name, e);
193: }
194: e.i++;
195: if (e.i > 1) {
196: return;
197: }
198: if (logger.isInfoEnabled()) {
199: logger.info("Added ensured name: " + name);
200: }
201: if (table.size() > 1) {
202: return;
203: }
204: if (logger.isInfoEnabled()) {
205: logger.info("Starting checkNamesThread");
206: }
207: if (checkNamesThread == null) {
208: Runnable checkNamesRunner = new Runnable() {
209: public void run() {
210: // assert (thread == checkNamesThread);
211: checkNames();
212: }
213: };
214: checkNamesThread = threadService.getThread(this ,
215: checkNamesRunner,
216: "White pages EnsureIsFoundManager");
217: }
218: checkNamesThread.start();
219: }
220: }
221:
222: private void remove(String name) {
223: synchronized (table) {
224: Entry e = (Entry) table.get(name);
225: if (logger.isDetailEnabled()) {
226: logger.detail("remove(" + name + "): " + e);
227: }
228: if (e == null) {
229: return;
230: }
231: e.i--;
232: if (e.i <= 0) {
233: if (logger.isInfoEnabled()) {
234: logger.info("Removed ensured name: " + name);
235: }
236: table.remove(name);
237: }
238: if (!table.isEmpty()) {
239: return;
240: }
241: if (logger.isInfoEnabled()) {
242: logger.info("Cancelling checkNamesThread");
243: }
244: if (searching) {
245: bootstrapService.stopSearching();
246: searching = false;
247: }
248: if (checkNamesThread != null) {
249: checkNamesThread.cancelTimer();
250: }
251: }
252: }
253:
254: // timer fired, see if we've found our servers
255: private void checkNames() {
256: synchronized (table) {
257: if (logger.isDebugEnabled()) {
258: logger.debug((searching ? "Checking" : "Verifying")
259: + " reachability for names[" + table.size()
260: + "]: " + table.keySet());
261: }
262: if (table.isEmpty()) {
263: return;
264: }
265: String missingName = null;
266: boolean missingDueToQueue = false;
267: for (Iterator iter = table.entrySet().iterator(); iter
268: .hasNext();) {
269: Map.Entry me = (Map.Entry) iter.next();
270: String name = (String) me.getKey();
271: Entry e = (Entry) me.getValue();
272: Map m;
273: try {
274: m = wp.getAll(name, -1); // non-blocking wp lookup
275: } catch (Exception ex) {
276: if (logger.isErrorEnabled()) {
277: logger.error("White pages lookup(" + name
278: + ") failed", ex);
279: }
280: m = null;
281: }
282: if (logger.isDetailEnabled()) {
283: logger.detail("wp lookup(" + name + ") found " + m);
284: }
285: if (m == null || m.isEmpty()) {
286: missingName = name;
287: break;
288: }
289: // A successfull White Pages lookup is not sufficient.
290: // It's possible to access the white pages without being able to
291: // send messages. See bug 3907.
292: if (config.checkMtsReachability) {
293: AgentStatusService.AgentState state = (agentStatusService == null ? (null)
294: : agentStatusService
295: .getRemoteAgentState(SimpleMessageAddress
296: .getMessageAddress(name)));
297: if (logger.isDetailEnabled()) {
298: logger
299: .detail("Length of "
300: + name
301: + " MTS queue: "
302: + (state == null ? "AgentStatusService not available yet"
303: : state.queueLength
304: + " messages"));
305: }
306: if (state == null || state.queueLength > 0) {
307: missingDueToQueue = true;
308: missingName = name;
309: break;
310: }
311: }
312: // White pages and MTS reachability tests were successful.
313: e.found = true;
314: }
315: if (missingName == null) {
316: // found wp entries for all names, stop searching
317: if (searching) {
318: if (logger.isInfoEnabled()) {
319: logger.info("Stopping bootstrap search, all "
320: + table.size()
321: + " names are reachable: "
322: + table.keySet());
323: }
324: bootstrapService.stopSearching();
325: searching = false;
326: } else {
327: if (logger.isDebugEnabled()) {
328: logger
329: .debug("Verified that all names are reachable");
330: }
331: }
332: } else {
333: // continue bootstrap search
334: if (searching) {
335: if (logger.isDebugEnabled()) {
336: logger
337: .debug("Keep searching, "
338: + missingName
339: + " was not reachable yet "
340: + (missingDueToQueue ? "due to MTS queue"
341: : "due to failed white pages lookup"));
342: }
343: } else {
344: if (logger.isInfoEnabled()) {
345: logger
346: .info("Starting bootstrap search, "
347: + missingName
348: + " was not reachable yet "
349: + (missingDueToQueue ? "due to MTS queue"
350: : "due to failed white pages lookup"));
351: }
352: bootstrapService.startSearching();
353: searching = true;
354: }
355: }
356: // wake later
357: checkNamesThread.schedule(config.checkNamesPeriod);
358: }
359: }
360:
361: private void foundService(Service s) {
362: if (logger.isDetailEnabled()) {
363: logger.detail("foundService(" + s + "), searching="
364: + searching);
365: }
366: if (s instanceof AgentStatusService) {
367: agentStatusService = (AgentStatusService) s;
368: }
369: }
370:
371: private static class Entry {
372: public int i;
373: public boolean found;
374:
375: public String toString() {
376: return "(entry i=" + i + " found=" + found + ")";
377: }
378: }
379:
380: private class EnsureIsFoundSP implements ServiceProvider {
381: private final EnsureIsFoundService eifs = new EnsureIsFoundService() {
382: public void add(String name) {
383: EnsureIsFoundManager.this .add(name);
384: }
385:
386: public void remove(String name) {
387: EnsureIsFoundManager.this .remove(name);
388: }
389: };
390:
391: public Object getService(ServiceBroker sb, Object requestor,
392: Class serviceClass) {
393: if (!EnsureIsFoundService.class
394: .isAssignableFrom(serviceClass)) {
395: return null;
396: }
397: return eifs;
398: }
399:
400: public void releaseService(ServiceBroker sb, Object requestor,
401: Class serviceClass, Object service) {
402: }
403: }
404:
405: /** config options */
406: private static class EnsureIsFoundManagerConfig {
407: public final long checkNamesPeriod;
408: public final boolean checkMtsReachability;
409:
410: public EnsureIsFoundManagerConfig(Object o) {
411: Parameters p = new Parameters(o,
412: "org.cougaar.core.wp.bootstrap.ensureIsFound.");
413: checkNamesPeriod = p.getLong("checkNamesPeriod", 45000);
414: checkMtsReachability = p.getBoolean("checkMtsReachability",
415: true);
416: }
417: }
418: }
|