001: /*
002: * $Id: BSFCondition.java,v 1.5 2006/03/10 22:47:14 hengels Exp $
003: * (c) Copyright 2004 con:cern development team.
004: *
005: * This file is part of con:cern (http://concern.org).
006: *
007: * con:cern is free software; you can redistribute it and/or modify
008: * it under the terms of the GNU Lesser General Public License
009: * as published by the Free Software Foundation; either version 2.1
010: * of the License, or (at your option) any later version.
011: *
012: * Please see COPYING for the complete licence.
013: */
014: package org.concern.library.bsf;
015:
016: import org.apache.bsf.BSFException;
017: import org.apache.bsf.BSFManager;
018: import org.concern.controller.AbstractCondition;
019: import org.concern.controller.ConditionEvaluationException;
020:
021: import java.util.*;
022:
023: /**
024: * Allows any BSF script to be used as a con:cern condition. Two environment
025: * variables must be set in order for this to concern: language and script.
026: * "language" should contain the BSF name for the scripting language of the
027: * script, and "script" should contain the script itself. As a convenience,
028: * you may set the "default-script-language" environment variable at the
029: * process level if you plan on using the same scripting language throughout
030: * your process.
031: * <p/>
032: * This implementation assumes that you have already configured BSFManager
033: * with any additional languages you need. This implementation will declare
034: * the following additional beans before executing the script:
035: * <ul>
036: * <li>subject = the subject instance, as passed into the condition</li>
037: * <li>condition = the condition instance</li>
038: * <li>environment = the java.util.Map representing the environment for
039: * this condition.</li>
040: * <li>controller = the controller instance</li>
041: * </ul>
042: * For most scripting languages, these beans will be made available for
043: * direct use. For example (in groovy):
044: * <pre><code> subject.approval == subject.ACCEPTED</code></pre>
045: * <p/>
046: * Note: for those using the Groovy scripting language, testing has shown
047: * that Groovy's current implementation of the BSF scripting engine
048: * (org.codehaus.groovy.bsf.GroovyEngine) is <i>extremely slow</i>,
049: * probably because it is parsing the script for every invocation (by slow,
050: * we mean that we have observed 500-1100 ms execution time <i>per
051: * invocation</i> of a condition). We've found that a good workaround is to
052: * use Groovy's caching BSF scripting engine
053: * (org.codehaus.groovy.bsf.CachingGroovyEngine). Obviously, since
054: * this engine caches based on the script String, it will consume more
055: * memory. However, Con:cern processes tend to use a fairly static set of
056: * small script Strings, which fits well with the caching strategy, bringing
057: * memory consumption in line with typical expectations. Here is an example
058: * configuration:
059: * <pre><code>
060: * BSFManager.registerScriptingEngine("groovy",
061: * "org.codehaus.groovy.bsf.CachingGroovyEngine",
062: * new String[] { "groovy", "gy" });
063: * </code></pre>
064: *
065: * @author Andy DePue
066: */
067: public class BSFCondition extends AbstractCondition {
068: public static final String ENV_KEY_LANGUAGE = "language";
069: public static final String ENV_KEY_SCRIPT = "script";
070: public static final String PROCESS_ENV_KEY_LANGUAGE = "default-script-language";
071:
072: private BSFManager bsfManager;
073: private String sourceName;
074: private String language;
075: private String script;
076:
077: public BSFManager getBsfManager() {
078: if (this .bsfManager == null) {
079: setBsfManager(createBsfManager());
080: }
081: return this .bsfManager;
082: }
083:
084: public void setBsfManager(final BSFManager bsfManager) {
085: this .bsfManager = bsfManager;
086: }
087:
088: protected BSFManager createBsfManager() {
089: return new BSFManager();
090: }
091:
092: public String getLanguage() {
093: if (this .language == null) {
094: if ((this .language = (String) getEnvironment().get(
095: ENV_KEY_LANGUAGE)) == null) {
096: this .language = (String) controller.getProcess()
097: .getEnvironmentEntry(PROCESS_ENV_KEY_LANGUAGE);
098: }
099: }
100: return this .language;
101: }
102:
103: public void setLanguage(String language) {
104: this .language = language;
105: }
106:
107: public Object getScript() {
108: if (script == null)
109: script = (String) getEnvironment().get(ENV_KEY_SCRIPT);
110: return script;
111: }
112:
113: public void setScript(String script) {
114: this .script = script;
115: }
116:
117: public String getSourceName() {
118: if (this .sourceName == null) {
119: this .sourceName = getProcessName().trim().replace(' ', '_')
120: .replace('.', '_').replace(',', '_');
121: }
122: return this .sourceName;
123: }
124:
125: /**
126: * Subclasses can override this method to further prepare the BSFManager
127: * before script execution takes place.
128: *
129: * @param bsfm
130: * @param subject
131: * @throws ConditionEvaluationException
132: * @throws BSFException
133: */
134: protected void declareAdditionalBeans(final BSFManager bsfm,
135: final Object subject) throws ConditionEvaluationException,
136: BSFException {
137: }
138:
139: //
140: // METHODS FROM ABSTRACT CLASS AbstractCondition
141: //
142:
143: public boolean eval(final Object subject)
144: throws ConditionEvaluationException {
145: final BSFManager bsfm = getBsfManager();
146: try {
147: bsfm.declareBean("subject", subject, subject.getClass());
148: bsfm
149: .declareBean("environment", getEnvironment(),
150: Map.class);
151: bsfm.declareBean("controller", this .controller,
152: this .controller.getClass());
153: bsfm.declareBean("condition", this , BSFCondition.class);
154: declareAdditionalBeans(bsfm, subject);
155:
156: final Object ret = bsfm.eval(getLanguage(),
157: getSourceName(), 0, 0, getScript());
158:
159: if (!(ret instanceof Boolean)) {
160: throw new ConditionEvaluationException(
161: getLanguage()
162: + " script must evaluate to a boolean result (result="
163: + ret + ")");
164: }
165: return ((Boolean) ret).booleanValue();
166: } catch (BSFException e) {
167: if (e.getTargetException() != null) {
168: e.initCause(e.getTargetException());
169: }
170: throw new ConditionEvaluationException(e);
171: }
172: }
173: }
|