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.lib.web.axis.blackboardCount;
028:
029: import java.net.URL;
030: import java.util.Collection;
031: import java.util.HashMap;
032: import java.util.Iterator;
033: import java.util.Map;
034:
035: import javax.xml.parsers.DocumentBuilder;
036: import javax.xml.parsers.DocumentBuilderFactory;
037:
038: import org.cougaar.util.GenericStateModelAdapter;
039: import org.cougaar.util.UnaryPredicate;
040: import org.cougaar.core.blackboard.BlackboardClient;
041: import org.cougaar.core.component.Component;
042: import org.cougaar.core.component.ServiceBroker;
043: import org.cougaar.core.service.BlackboardService;
044: import org.cougaar.core.service.BlackboardQueryService;
045: import org.cougaar.core.service.LoggingService;
046: import org.cougaar.core.service.UIDService;
047: import org.cougaar.core.service.WebServicesService;
048: import org.cougaar.core.util.UID;
049: import org.cougaar.core.util.UniqueObject;
050:
051: /**
052: * This component advertises the SOAP "BlackboardCount" and
053: * replies to "getBlackboardCount" requests by querying the
054: * blackboard.
055: * <p>
056: * Load with:<pre>
057: * <component
058: * class="org.cougaar.lib.web.axis.WebServicesProvider"/gt;
059: * <component
060: * class="org.cougaar.lib.web.axis.blackboardCount.BlackboardCountPlugin"/>
061: * </pre>
062: * <p>
063: * Use the {@link BlackboardCountClient} to invoke this plugin's
064: * SOAP service from a command-line client.
065: * <p>
066: * Note that only one instance of this plugin can be loaded per
067: * JVM, since the {@link WebServicesService} is static and our
068: * {@link BlackboardCountHook} is static. To work around this,
069: * a developer could maintain a Map in the "hook" and require
070: * the SOAP caller to specify the agent name, or advertise separate
071: * SOAP paths with separate "hook" implementations.
072: */
073: public class BlackboardCountPlugin extends GenericStateModelAdapter
074: implements Component, BlackboardCount {
075:
076: /**
077: * Axis WSDD, basically copied from StockQuote deploy.wsdd.
078: * <p>
079: * This is a giant string, but we use ".class.getName()" to make
080: * sure the referenced classnames are correct.
081: */
082: private static final String BLACKBOARD_COUNT_WSDD = "<deployment name=\"test\" xmlns=\"http://xml.apache.org/axis/wsdd/\" \n"
083: + " xmlns:java=\"http://xml.apache.org/axis/wsdd/providers/java\">\n"
084: + " <service name=\"urn:Cougaar-blackboard-count\" provider=\"java:RPC\">\n"
085: + " <parameter name=\"className\" value=\""
086: + BlackboardCountHook.class.getName()
087: + "\"/>\n"
088: + " <parameter name=\"allowedMethods\" value=\"getBlackboardCount\"/>\n"
089: + " <parameter name=\"wsdlServicePort\" value=\"BlackboardCount\"/>\n"
090: + " <beanMapping qname=\"myNS:ResultMap\"\n"
091: + " xmlns:myNS=\"urn:BeanService\"\n"
092: + " languageSpecificType=\"java:"
093: + ResultMap.class.getName()
094: + "\"/>\n"
095: + " <beanMapping qname=\"myNS:ResultEntry\"\n"
096: + " xmlns:myNS=\"urn:BeanService\"\n"
097: + " languageSpecificType=\"java:"
098: + ResultEntry.class.getName()
099: + "\"/>\n"
100: + " </service>\n"
101: + "</deployment>";
102:
103: /**
104: * List of common class packages, allowing the client to say:<pre>
105: * getBlackboardCount("Relay");
106: * </pre>instead of:<pre>
107: * getBlackboardCount("org.cougaar.core.relay.Relay");
108: * </pre>
109: */
110: private static final String[] STANDARD_PACKAGES = {
111: "",
112: "org.cougaar.lib.web.axis.blackboardCount.BlackboardCountPlugin$",
113: "org.cougaar.core.util.", "org.cougaar.core.relay.",
114: "org.cougaar.planning.ldm.plan.",
115: "org.cougaar.planning.ldm.asset.", };
116:
117: private ServiceBroker sb;
118:
119: private LoggingService log;
120: private BlackboardQueryService blackboard;
121:
122: public void setServiceBroker(ServiceBroker sb) {
123: this .sb = sb;
124: }
125:
126: public void load() {
127: super .load();
128:
129: log = (LoggingService) sb.getService(this ,
130: LoggingService.class, null);
131:
132: // get the blackboard query service so we can do our count
133: blackboard = (BlackboardQueryService) sb.getService(this ,
134: BlackboardQueryService.class, null);
135: if (blackboard == null) {
136: throw new RuntimeException(
137: "Unable to obtain BlackboardQueryService");
138: }
139:
140: WebServicesService wss = (WebServicesService) sb.getService(
141: this , WebServicesService.class, null);
142: if (wss == null) {
143: throw new RuntimeException(
144: "Unable to obtain WebServicesService");
145: }
146:
147: // create dummy blackboard objects, since this plugin is
148: // typically loaded in the node-agent, which usually has
149: // an empty blackboard
150: createBlackboardTestObjects();
151:
152: // set a pointer to our plugin.
153: //
154: // this allows Axis to call "new BlackboardCountHook()" yet still
155: // have the method calls invoke our plugin instance's methods.
156: BlackboardCountHook.setStatic(this );
157:
158: // tell Axis to process the WSDD.
159: //
160: // note: Axis tends to be silent about some WSDD errors until
161: // the first "/axis/services" servlet attempt
162: if (log.isInfoEnabled()) {
163: log.info("processWSDD(\n" + BLACKBOARD_COUNT_WSDD + "\n)");
164: }
165: try {
166: wss.processWSDD(BLACKBOARD_COUNT_WSDD);
167: } catch (Exception e) {
168: throw new RuntimeException("Unable to processWSDD(\n"
169: + BLACKBOARD_COUNT_WSDD + "\n)", e);
170: }
171:
172: log
173: .shout("Created \"BlackboardCount\" web service.\n"
174: + "Check the \"/axis/services\" servlet to see if the WSDL is listed.\n"
175: + "To invoke, run $CIP/configs/axis/getBlackboardCount.sh\n"
176: + "The expected output is:\n"
177: + " getBlackboardCount(*)={\n" + " "
178: + getClass().getName() + "$TestOne=1,\n"
179: + " " + getClass().getName()
180: + "$TestTwo=2,\n" + " }");
181: }
182:
183: public ResultMap getBlackboardCount(String s) {
184: String classFilter = (s == null ? "*" : s.trim());
185:
186: if (log.isInfoEnabled()) {
187: log.shout("getBlackboardCount(" + classFilter + ")");
188: }
189:
190: // query the blackboard
191: UnaryPredicate pred = createPredicate(classFilter);
192: Collection c = blackboard.query(pred);
193:
194: // convert objects to "class -> int" table
195: Map m = createClassCountMap(c);
196:
197: // wrap in SOAP-friendly object
198: return new ResultMap(m);
199: }
200:
201: private UnaryPredicate createPredicate(String classFilter) {
202: if ("*".equals(classFilter)) {
203: return new UnaryPredicate() {
204: public boolean execute(Object o) {
205: return true;
206: }
207: };
208: }
209: Class cl = null;
210: for (int i = 0; i < STANDARD_PACKAGES.length; i++) {
211: try {
212: String s = STANDARD_PACKAGES[i] + classFilter;
213: cl = Class.forName(s);
214: break;
215: } catch (Exception e) {
216: // ignore, try next package
217: cl = null;
218: }
219: }
220: if (cl == null) {
221: throw new RuntimeException("Unknown class type: "
222: + classFilter);
223: }
224: final Class cl2 = cl;
225: return new UnaryPredicate() {
226: public boolean execute(Object o) {
227: return o != null && cl2.isAssignableFrom(o.getClass());
228: }
229: };
230: }
231:
232: /** convert objects to "class -> int" table */
233: private Map createClassCountMap(Collection c) {
234: int n = (c == null ? 0 : c.size());
235: Map ret = new HashMap(n);
236: if (n > 0) {
237: Iterator iter = c.iterator();
238: for (int i = 0; i < n; i++) {
239: Object o = iter.next();
240: Class cl = (o == null ? null : o.getClass());
241: String clname = (cl == null ? "null" : cl.getName());
242: Counter counter = (Counter) ret.get(clname);
243: if (counter == null) {
244: counter = new Counter();
245: ret.put(clname, counter);
246: }
247: counter.count++;
248: }
249: for (Iterator i2 = ret.entrySet().iterator(); i2.hasNext();) {
250: Map.Entry me = (Map.Entry) i2.next();
251: Counter counter = (Counter) me.getValue();
252: me.setValue(new Integer(counter.count));
253: }
254: }
255: return ret;
256: }
257:
258: /**
259: * publish something on the node's blackboard,
260: * since it is often empty.
261: */
262: private void createBlackboardTestObjects() {
263: UIDService uidService = (UIDService) sb.getService(this ,
264: UIDService.class, null);
265: BlackboardClient bc = new BlackboardClient() {
266: public String getBlackboardClientName() {
267: return getClass().getName();
268: }
269:
270: public long currentTimeMillis() {
271: return -1;
272: }
273: };
274: BlackboardService bs = (BlackboardService) sb.getService(bc,
275: BlackboardService.class, null);
276: if (bs != null) {
277: try {
278: bs.openTransaction();
279: bs.publishAdd(new TestOne(uidService.nextUID()));
280: bs.publishAdd(new TestTwo(uidService.nextUID()));
281: bs.publishAdd(new TestTwo(uidService.nextUID()));
282: } finally {
283: bs.closeTransaction();
284: }
285: sb.releaseService(bc, BlackboardService.class, bs);
286: }
287: sb.releaseService(this , UIDService.class, uidService);
288: }
289:
290: private static final class Counter {
291: public int count;
292: }
293:
294: // trivial test objects for "createBlackboardTestObjects()"
295: private static abstract class TestBase implements UniqueObject {
296: private final UID uid;
297:
298: public TestBase(UID uid) {
299: this .uid = uid;
300: if (uid == null) {
301: throw new IllegalArgumentException("null uid");
302: }
303: }
304:
305: public UID getUID() {
306: return uid;
307: }
308:
309: public void setUID(UID uid) {
310: throw new UnsupportedOperationException("UID already set");
311: }
312:
313: public String toString() {
314: String s = getClass().getName();
315: int i = s.lastIndexOf('.');
316: if (i >= 0 && i < s.length()) {
317: s = s.substring(i + 1);
318: }
319: return "(" + s + " uid=" + uid + ")";
320: }
321:
322: public int hashCode() {
323: return uid.hashCode();
324: }
325:
326: public boolean equals(Object o) {
327: if (o == this ) {
328: return true;
329: } else if (o instanceof TestBase) {
330: return uid.equals(((TestBase) o).uid);
331: } else {
332: return false;
333: }
334: }
335: }
336:
337: private static class TestOne extends TestBase {
338: public TestOne(UID uid) {
339: super (uid);
340: }
341: }
342:
343: private static class TestTwo extends TestBase {
344: public TestTwo(UID uid) {
345: super(uid);
346: }
347: }
348: }
|