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:
027: package org.cougaar.lib.aggagent.session;
028:
029: import java.io.Serializable;
030: import java.util.Collection;
031: import java.util.Iterator;
032: import java.util.LinkedList;
033:
034: import org.cougaar.util.UnaryPredicate;
035:
036: /**
037: * A RemoteSession represents a contract for a COUGAAR agent to provide data
038: * to another based on a Subscription to the local blackboard. The primary
039: * purpose of this class is to provide standard algorithms for handling a
040: * SubscriptionAccess and any errors in the UnaryPredicate or IncrementFormat
041: * scripts.
042: */
043: public abstract class RemoteSession implements Serializable {
044: // collecting error reports
045: private Collection newErrors = new LinkedList();
046:
047: // identify this session
048: private String key = null;
049:
050: // identify the query that spawned this session
051: private String queryId = null;
052:
053: // format the output for transmission
054: private IncrementFormat formatter = null;
055:
056: // ID of the local COUGAAR agent
057: private String agentId = null;
058:
059: /**
060: * Create a new RemoteSession instance. It carries an ID for itself and
061: * the associated client-side query. It also has an IncrementFormat for
062: * encoding data sets to return to the client and a String identifier
063: * indicating which agent initiated this session (and to whom responses
064: * should be sent).
065: */
066: protected RemoteSession(String k, String q, IncrementFormat f) {
067: key = k;
068: queryId = q;
069: formatter = f;
070: }
071:
072: /**
073: * Specify the name of the local COUGAAR agent.
074: */
075: protected void setAgentId(String a) {
076: agentId = a;
077: }
078:
079: /**
080: * Report the query ID associated with this session. This method is useful
081: * for looking up sessions by client-supplied ID.
082: */
083: public String getQueryId() {
084: return queryId;
085: }
086:
087: /**
088: * For purposes of tabulation, this key identifies the session existing
089: * between this Object and the remote client. Requests concerning the
090: * session (such as ending it, checking its status, etc.) should use this
091: * key.
092: */
093: public String getKey() {
094: return key;
095: }
096:
097: /**
098: * Get the SubscriptionAccess implementation containing the data to be
099: * encoded and sent to a client. Different concrete subclasses will
100: * probably manage different types of these.
101: */
102: protected abstract SubscriptionAccess getData();
103:
104: /**
105: * Encode the data contained in the local SubscriptionAccess and send the
106: * results back to the client. If an error is detected during the gathering
107: * process, then an error report is returned in lieu of a data set.
108: */
109: protected UpdateDelta createUpdateDelta() {
110: UpdateDelta del = new UpdateDelta(agentId, queryId, key);
111:
112: Iterator errors = getErrorCollection().iterator();
113: if (errors.hasNext()) {
114: del.setErrorReport((Throwable) errors.next());
115: } else {
116: try {
117: formatter.encode(del, getData());
118: } catch (Throwable err) {
119: if (err instanceof ThreadDeath)
120: throw (ThreadDeath) err;
121: del.setErrorReport(err);
122: }
123: }
124:
125: return del;
126: }
127:
128: // called during update processing--return errors and clear them out
129: private Collection getErrorCollection() {
130: Collection errors = newErrors;
131: newErrors = new LinkedList();
132: return errors;
133: }
134:
135: // called by the ErrorTrapPredicate to record errors
136: private void error(Throwable err) {
137: newErrors.add(err);
138: }
139:
140: /**
141: * This UnaryPredicate implementation is used as a wrapper for predicates
142: * derived from possibly buggy scripts. Whenever an Exception or Error is
143: * encountered, it is recorded for later examination. ThreadDeath is,
144: * of course, exempted from this treatment. ErrorTrapPredicate::execute
145: * always returns false if an error is detected.
146: * Note: It may be desirable to include some information about the object
147: * on which the predicate failed. This may be added in the future.
148: */
149: protected class ErrorTrapPredicate implements UnaryPredicate {
150: private UnaryPredicate delegate = null;
151:
152: public ErrorTrapPredicate(UnaryPredicate p) {
153: delegate = p;
154: }
155:
156: public boolean execute(Object o) {
157: try {
158: return delegate.execute(o);
159: } catch (Throwable err) {
160: if (err instanceof ThreadDeath)
161: throw (ThreadDeath) err;
162: error(err);
163: }
164: return false;
165: }
166: }
167: }
|