001: /*
002: * <copyright>
003: *
004: * Copyright 2003-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: package org.cougaar.lib.aggagent.client;
027:
028: import java.net.URLEncoder;
029: import java.util.Collection;
030: import java.util.LinkedList;
031: import java.util.Timer;
032:
033: import org.cougaar.lib.aggagent.query.AggregationQuery;
034: import org.cougaar.lib.aggagent.query.AggregationResultSet;
035: import org.cougaar.lib.aggagent.query.Alert;
036: import org.cougaar.lib.aggagent.query.AlertDescriptor;
037: import org.cougaar.lib.aggagent.query.QueryResultAdapter;
038: import org.cougaar.lib.aggagent.util.XmlUtils;
039: import org.cougaar.lib.aggagent.util.Enum.QueryType;
040: import org.cougaar.util.log.Logger;
041: import org.cougaar.util.log.Logging;
042: import org.w3c.dom.Element;
043: import org.w3c.dom.NodeList;
044:
045: /**
046: * The AggregationPSPClient provides a client-side communication abstraction
047: * to a specific Aggregation Servlet. It provides a simple interface to the
048: * functionality provided by the Aggregation Agent utilizing the Aggregation
049: * PSP's XML interface. It manages both transient requests and persistent
050: * sessions using both transient and keep alive connection(s) with the
051: * Aggregation Servlet.<BR><BR>
052: *
053: * The core functions provided by the Aggregation Agent that can be accessed via
054: * this interface follow:
055: * <UL>
056: * <LI>Creation and management of both transient and persistent queries that
057: * can gather and aggregate any type of data from a set of clusters. </LI>
058: * <LI>Creation and management of Alerts which can be used to watch for a
059: * particular condition to be met in a persistent query's result set.</LI>
060: * <LI>Event driven incremental monitoring of changing result sets and alerts
061: * on the Aggregation Agent's blackboard</LI>
062: * </LI>
063: * </UL>
064: */
065: public class AggregationClient {
066: private String aggregationURL = null;
067: private String keepAliveURL = null;
068: private Timer pullTimer = new Timer();
069: private Logger logger;
070:
071: /**
072: * Create a new client interface to a specific Aggregation Agent.
073: *
074: * @param clusterURL text url of the aggregation agent's cluster
075: * @param aggregationName name given to aggregation servlet on given
076: * aggregation agent. (typical: "aggregator")
077: * @param keepAliveName name given to keep alive aggregation servlet on the
078: * given aggregation agent.
079: * (typical: "aggregatorkeepalive")
080: */
081: public AggregationClient(String clusterURL, String aggregationName,
082: String keepAliveName) {
083: logger = Logging.getLogger(this );
084:
085: aggregationURL = clusterURL + "/" + aggregationName
086: + "?THICK_CLIENT=1";
087: keepAliveURL = clusterURL + "/" + keepAliveName
088: + "?KEEP_ALIVE=1";
089:
090: // check url
091: String response = XmlUtils.requestString(aggregationURL
092: + "&CHECK_URL=1", null);
093:
094: if (response == null) {
095: throw new NullPointerException(
096: "Cannot contact aggregation agent at "
097: + aggregationURL);
098: }
099: }
100:
101: /**
102: * Returns a collection of all Query Result Adapters on the aggregation
103: * agent's log plan. Each query result adapter includes a query, a
104: * result set, and a set of alerts. This is basically a snapshot of
105: * everything on the aggregation agent's blackboard. Only persistent queries
106: * will be found.
107: *
108: * @return a collection of all query result adapters on the aggregation
109: * agent's log plan.
110: */
111: public Collection getActiveQueries() {
112: Element root = XmlUtils.requestXML(aggregationURL
113: + "&GET_QUERIES=1", null);
114:
115: NodeList queryNodes = root
116: .getElementsByTagName(QueryResultAdapter.QUERY_RESULT_TAG);
117:
118: LinkedList queries = new LinkedList();
119: for (int i = 0; i < queryNodes.getLength(); i++) {
120: queries.add(new QueryResultAdapter((Element) queryNodes
121: .item(i)));
122: }
123:
124: return queries;
125: }
126:
127: /**
128: * Returns a collection of Alert Descriptors for all alerts on the
129: * aggregation agent's log plan. Alert descriptors are client-side
130: * descriptions of Alerts. These alert descriptors will be orphans
131: * (i.e. getQueryResultAdapter() == null) but they will include a reference
132: * to the query id of their query result adaptor on the aggregation agent
133: * (accessed via getQueryId()).
134: *
135: * @return a collection of Alert Descriptors for all alerts on the
136: * aggregation agent's log plan.
137: */
138: public Collection getActiveAlerts() {
139: Element root = XmlUtils.requestXML(aggregationURL
140: + "&GET_ALERTS=1", null);
141:
142: NodeList alertNodes = root
143: .getElementsByTagName(Alert.ALERT_TAG);
144:
145: LinkedList alerts = new LinkedList();
146: for (int i = 0; i < alertNodes.getLength(); i++) {
147: alerts
148: .add(new AlertDescriptor((Element) alertNodes
149: .item(i)));
150: }
151:
152: return alerts;
153: }
154:
155: /**
156: * Request the creation of a query on aggregation agent.
157: *
158: * @param aq aggregation query object that fully describes the query. Query
159: * can be either transient or persistent.
160: *
161: * @return If query is a persistent query, a queryId String will be returned.
162: * If query is transient an AggregationResultSet will be returned.
163: */
164: public Object createQuery(AggregationQuery aq) {
165: Object response = null;
166: String taggedURL = aggregationURL + "&CREATE_QUERY=1";
167: if (aq.getType() == QueryType.PERSISTENT) {
168: response = XmlUtils.requestString(taggedURL, aq.toXml())
169: .trim();
170: } else {
171: Element root = XmlUtils.requestXML(taggedURL, aq.toXml());
172: response = new AggregationResultSet(root);
173: }
174: return response;
175: }
176:
177: /**
178: * Request the creation of an alert on aggregation agent.
179: *
180: * @param ad alert descriptor that fully describes the alert. This alert
181: * descriptor must include the query id of the query result adapter
182: * to which this alert should be added.
183: *
184: * @return true if successful; false otherwise
185: */
186: public boolean createAlert(AlertDescriptor ad) {
187: String response = null;
188: String taggedURL = aggregationURL + "&CREATE_ALERT=1";
189: response = XmlUtils.requestString(taggedURL, ad.toXml()).trim();
190:
191: return response.equals("0");
192: }
193:
194: /**
195: * Request a list of all clusters in the society excluding the aggregation
196: * agent. These can all be used as source clusters in future queries.
197: *
198: * @return a collection of clusterId Strings.
199: */
200: public Collection getClusterIds() {
201: Element root = XmlUtils.requestXML(aggregationURL
202: + "&GET_CLUSTERS=1", null);
203:
204: NodeList clusterNodes = root.getElementsByTagName("cluster_id");
205:
206: LinkedList clusters = new LinkedList();
207: for (int i = 0; i < clusterNodes.getLength(); i++) {
208: clusters.add(clusterNodes.item(i).getFirstChild()
209: .getNodeValue().trim());
210: }
211:
212: return clusters;
213: }
214:
215: /**
216: * Get an updated result set for an active persistent query from blackboard
217: * of aggregation agent.
218: *
219: * @param queryId id of query result adapter for needed result set.
220: *
221: * @return an updated result set for an active persistent query from
222: * blackboard of aggregation agent. Returns null of query is not
223: * found.
224: */
225: public AggregationResultSet getUpdatedResultSet(String queryId) {
226: String loadedURL = aggregationURL
227: + "&GET_RESULT_SET=1&QUERY_ID=" + queryId;
228: Element root = XmlUtils.requestXML(loadedURL, null);
229: if (root.getNodeName().equals(
230: AggregationResultSet.RESULT_SET_TAG))
231: return new AggregationResultSet(root);
232:
233: // result set not found
234: return null;
235: }
236:
237: /**
238: * Request cancelation of an active persistent query. Removes query from
239: * aggregation agent's blackboard. Cancels all collection activity related
240: * to this query. Also cancels all alerts attached to this query.
241: *
242: * @param queryId id of query result adapter to be cancelled.
243: *
244: * @return true if successful; false otherwise
245: */
246: public boolean cancelQuery(String queryId) {
247: String loadedURL = aggregationURL + "&CANCEL_QUERY=1&QUERY_ID="
248: + queryId;
249: String response = XmlUtils.requestString(loadedURL, null);
250: return response.equals("0");
251: }
252:
253: /**
254: * Send an update to an active persistent query. Updates query on
255: * aggregation agent's blackboard.
256: *
257: * @param qra the query result adapter to be updated.
258: *
259: * @return true if successful; false otherwise
260: */
261: public boolean updateQuery(QueryResultAdapter qra) {
262:
263: String loadedURL = aggregationURL + "&UPDATE_QUERY=1&QUERY_ID="
264: + qra.getID();
265: String response = XmlUtils.requestString(loadedURL, qra
266: .getQuery().toXml().trim());
267: return response.equals("0");
268: }
269:
270: /**
271: * Request cancelation of an active alert.
272: *
273: * @param queryId id of alert's query result adapter
274: * @param alertName name of alert
275: *
276: * @return true if successful; false otherwise
277: */
278: public boolean cancelAlert(String queryId, String alertName) {
279: boolean success = false;
280: try {
281: String encodedAlertName = URLEncoder.encode(alertName,
282: "UTF-8");
283: String loadedURL = aggregationURL
284: + "&CANCEL_ALERT=1&QUERY_ID=" + queryId
285: + "&ALERT_NAME=" + encodedAlertName;
286: String response = XmlUtils.requestString(loadedURL, null);
287: success = response.equals("0");
288: } catch (Exception e) {
289: e.printStackTrace();
290: }
291: return success;
292: }
293:
294: /**
295: * Create an monitor object that can be used to keep a set of monitored
296: * alerts current by using a keep alive session which sends incremental
297: * updates from the AggregationPSP. Monitored alerts will be updated on the
298: * client as soon as they change on the aggregation agent.
299: *
300: * @return an object that can be used to monitor alerts on the aggregation
301: * agent's log plan.
302: */
303: public AlertMonitor createAlertMonitor() {
304: // setup keep alive task on the client
305: return new AlertMonitor(keepAliveURL, Monitor.KEEP_ALIVE_METHOD);
306: }
307:
308: /**
309: * Create an monitor object that can be used to keep a set of monitored
310: * alerts current by periodically pulling from a passive session on
311: * AggregationPSP. By using this type of monitor, instead of a keep alive
312: * monitor, the load can be reduced on both the client and the aggregation
313: * agent. Increasing the wait peroid will reduce the load even more.
314: * Updates to objects on the aggregation agent might be reported as long as
315: * the wait period late.
316: *
317: * @param waitPeriod wait period between client pulls (update requests)
318: * from aggregation agent in seconds.
319: *
320: * @return an object that can be used to monitor alerts on the aggregation
321: * agent's log plan.
322: */
323: public AlertMonitor createAlertMonitor(int waitPeriod) {
324: // Setup pull task on client
325: AlertMonitor alertMonitor = new AlertMonitor(aggregationURL,
326: Monitor.PULL_METHOD);
327: pullTimer.scheduleAtFixedRate(alertMonitor.getPullTask(), 0,
328: waitPeriod * 1000);
329: return alertMonitor;
330: }
331:
332: /**
333: * Create an monitor object that can be used to keep a set of monitored
334: * result sets current by using a keep alive session which sends incremental
335: * updates from the AggregationPSP. Monitored result sets will be updated on
336: * the client as soon as they change on the aggregation agent.
337: *
338: * @return an object that can be used to monitor result sets on the
339: * aggregation agent's log plan.
340: */
341: public ResultSetMonitor createResultSetMonitor() {
342: // setup keep alive task on the client
343: return new ResultSetMonitor(keepAliveURL,
344: Monitor.KEEP_ALIVE_METHOD);
345: }
346:
347: /**
348: * Create an monitor object that can be used to keep a set of monitored
349: * result sets current by periodically pulling from a passive session on
350: * AggregationPSP. By using this type of monitor, instead of a keep alive
351: * monitor, the load can be reduced on both the client and the aggregation
352: * agent. Increasing the wait peroid will reduce the load even more.
353: * Updates to objects on the aggregation agent might be reported as long as
354: * the wait period late.
355: *
356: * @param waitPeriod wait period between client pulls (update requests)
357: * from aggregation agent in seconds.
358: *
359: * @return an object that can be used to monitor result sets on the
360: * aggregation agent's log plan.
361: */
362: public ResultSetMonitor createResultSetMonitor(int waitPeriod) {
363: // Setup pull task on client
364: ResultSetMonitor resultSetMonitor = new ResultSetMonitor(
365: aggregationURL, Monitor.PULL_METHOD);
366: pullTimer.scheduleAtFixedRate(resultSetMonitor.getPullTask(),
367: 0, waitPeriod * 1000);
368: return resultSetMonitor;
369: }
370:
371: /**
372: * Get the value of a system property from the aggregation agent's
373: * environment. (e.g. "org.cougaar.core.agent.startTime")
374: *
375: * @param propertyName the name of the property.
376: * @return the value of the property.
377: */
378: public String getSystemProperty(String propertyName) {
379: String propertyValue = XmlUtils.requestString(aggregationURL
380: + "&GET_SYSTEM_PROPERTY=1&PROPERTY_NAME="
381: + propertyName, null);
382: return propertyValue.trim();
383: }
384: }
|