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.script;
028:
029: import org.cougaar.lib.aggagent.query.AggregationQuery;
030: import org.cougaar.lib.aggagent.query.Alert;
031: import org.cougaar.lib.aggagent.query.QueryResultAdapter;
032: import org.cougaar.lib.aggagent.query.ResultSetDataAtom;
033: import org.python.core.PyFunction;
034: import org.python.core.PyObject;
035: import org.python.util.PythonInterpreter;
036:
037: /**
038: * An implementation of the Alert class that derives its functionality from a
039: * script written in the JPython language. A PythAlert is configured in two
040: * stages: one is to declare a function or class, and the other is to pass
041: * the function or an instance of the class to the controlling Java context.
042: * <br><br>
043: * The necessity of using a two-stage initialization procedure for both types
044: * of PythAlert implementations is due to JPython's resolute refusal to allow
045: * an expression to declare a new function or class (or, indeed, any multiline
046: * construct). One possibility is to use a "magic" function through which the
047: * Java and JPython contexts can communicate. For the sake of uniformity,
048: * this option is used here. The magic function is "instantiate", which the
049: * script should define in the global context as a no-arg function that
050: * returns either an Alert instance or a function designed to act as the
051: * abstract "handleUpdate" method of an Alert.
052: * <br><br>
053: * This class contains a static factory method, parseAlert(), for generating
054: * Alert instances.
055: */
056: public abstract class PythAlert {
057: // This is the JPython instruction evaluated to retrieve the product of the
058: // script. The script is responsible for providing the correct behavior to
059: // the named function.
060: private static String MAGIC_FUNCTION = "instantiate()";
061:
062: // Implementation of an Alert that uses a JPython function as a delegate for
063: // the handleUpdate() method. Arguments are coerced into the JPython
064: // context, where the function is evaluated. The returned value, if any, is
065: // ignored.
066: private static class Func extends Alert {
067: private PyFunction delegateFunction = null;
068:
069: public Func(PyFunction f) {
070: delegateFunction = f;
071: }
072:
073: public void handleUpdate() {
074: delegateFunction._jcall(new Object[] { this });
075: }
076: }
077:
078: /**
079: * Create an Alert from a JPython script. There are two acceptable modes
080: * for the script. It must produce either an instance of a JPython subclass
081: * of Java class Alert or a JPython function that behaves like the method
082: * Alert.handleUpdate (i.e., takes one argument (that being the Alert's own
083: * self), returns no result, and updates the Alert's state to reflect the
084: * current result set data). Either way, the script is required to define
085: * the magic function "instantiate()" to provide the function or predicate
086: * instance to the Java context.
087: *
088: * @param script the executable script that declares classes and variables
089: * @return an Alert instance derived from the JPython scripts
090: */
091: public static Alert parseAlert(String script) {
092: PythonInterpreter pi = new NoErrorPython();
093: if (script != null)
094: pi.exec(script);
095: PyObject product = pi.eval(MAGIC_FUNCTION);
096: if (product instanceof PyFunction) {
097: return new Func((PyFunction) product);
098: } else {
099: Object obj = product.__tojava__(Alert.class);
100: if (obj instanceof Alert)
101: return (Alert) obj;
102: }
103: throw new IllegalArgumentException(
104: "JPython script did not yield a function or an Alert");
105: }
106:
107: // - - - - - - - Testing code below this point - - - - - - - - - - - - - - - - -
108:
109: private static String ALERT_FUNCTION = "def handle (self):\n"
110: + " qra = self.getQueryAdapter()\n"
111: + " self.setAlerted(qra.getResultSet().getAllAtoms().hasNext())\n"
112: + "def instantiate ():\n" + " return handle\n";
113:
114: private static String ALERT_CLASS = "from org.cougaar.lib.aggagent.query import Alert\n"
115: + "class Handler (Alert):\n"
116: + " def handleUpdate (self):\n"
117: + " qra = self.getQueryAdapter()\n"
118: + " self.setAlerted(qra.getResultSet().getAllAtoms().hasNext())\n"
119: + "def instantiate ():\n" + " return Handler()\n";
120:
121: public static void main(String[] argv) {
122: Alert ale_1 = PythAlert.parseAlert(ALERT_FUNCTION);
123: Alert ale_2 = PythAlert.parseAlert(ALERT_CLASS);
124: QueryResultAdapter qra = new QueryResultAdapter(
125: (AggregationQuery) null);
126: qra.addAlert(ale_1);
127: qra.addAlert(ale_2);
128: test(ale_1, "Test 1 (ale_1)", false);
129: test(ale_2, "Test 1 (ale_2)", false);
130:
131: ResultSetDataAtom atom = new ResultSetDataAtom();
132: atom.addIdentifier("name", "random value");
133: atom.addValue("number", "5");
134: // qra.getResultSet().update(atom);
135:
136: ale_1.update();
137: test(ale_1, "Test 2 (ale_1)", true);
138: ale_2.update();
139: test(ale_2, "Test 2 (ale_2)", true);
140: }
141:
142: private static void test(Alert a, String msg, boolean answer) {
143: System.out.println(msg + ": "
144: + (a.isAlerted() == answer ? "Success" : "Failure"));
145: }
146: }
|