001: /*
002: * This file is part of PFIXCORE.
003: *
004: * PFIXCORE is free software; you can redistribute it and/or modify
005: * it under the terms of the GNU Lesser General Public License as published by
006: * the Free Software Foundation; either version 2 of the License, or
007: * (at your option) any later version.
008: *
009: * PFIXCORE is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public License
015: * along with PFIXCORE; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */
018:
019: package de.schlund.pfixcore.scriptedflow.vm;
020:
021: import java.util.HashMap;
022: import java.util.List;
023: import java.util.Map;
024:
025: import javax.servlet.http.HttpServletRequest;
026:
027: import org.w3c.dom.Document;
028: import org.w3c.dom.Element;
029: import org.w3c.dom.Node;
030:
031: import de.schlund.pfixcore.exception.PustefixApplicationException;
032: import de.schlund.pfixcore.exception.PustefixCoreException;
033: import de.schlund.pfixcore.scriptedflow.vm.pvo.ParamValueObject;
034: import de.schlund.pfixcore.workflow.ExtendedContext;
035: import de.schlund.pfixxml.PfixServletRequest;
036: import de.schlund.pfixxml.PfixServletRequestImpl;
037: import de.schlund.pfixxml.SPDocument;
038:
039: /**
040: * Executes scripts that have been compiled previously.
041: *
042: * @author Sebastian Marsching <sebastian.marsching@1und1.de>
043: */
044: public class ScriptVM {
045: private Instruction[] il;
046:
047: private int ip = 0;
048:
049: private Script script = null;
050:
051: private boolean isRunning;
052:
053: // Do NOT write to this variable but use update...() method
054: private SPDocument registerSPDoc;
055:
056: // Has to be stored between requests
057: private Map<String, String> registerVariables = new HashMap<String, String>();
058:
059: private XPathResolver resolver = new XPathResolver();
060:
061: public void setScript(Script script) {
062: if (this .script != null) {
063: throw new IllegalStateException(
064: "Script can only be set once per VM instance");
065: }
066: this .script = script;
067: this .il = script.getInstructions();
068: this .ip = 0;
069: }
070:
071: public void loadVMState(VMState state) {
072: this .script = state.getScript();
073: this .il = script.getInstructions();
074: this .ip = state.getIp();
075: this .registerVariables.clear();
076: this .registerVariables.putAll(state.getVariables());
077: }
078:
079: public VMState saveVMState() {
080: VMState state = new VMState();
081: state.setScript(script);
082: state.setIp(ip);
083: state.setVariables(registerVariables);
084: return state;
085: }
086:
087: public SPDocument run(PfixServletRequest preq, SPDocument spdoc,
088: ExtendedContext rcontext, Map<String, String> params)
089: throws PustefixApplicationException, PustefixCoreException {
090: isRunning = true;
091:
092: // Make sure resolver and registers are set up
093: // with correct data
094: resolver.setParams(params);
095: resolver.setVariables(this .registerVariables);
096: updateRegisterSPDoc(spdoc);
097:
098: // Check whether a script has been loaded
099: if (script == null) {
100: isRunning = false;
101: throw new IllegalStateException(
102: "Script has to be loaded before running the machine");
103: }
104:
105: mainloop: while (ip < il.length) {
106: Instruction instr = il[ip];
107:
108: if (instr instanceof ExitInstruction) {
109: ExitInstruction in = (ExitInstruction) instr;
110: Map<String, String[]> formparams = new HashMap<String, String[]>();
111: for (String key : in.getParams().keySet()) {
112: List<ParamValueObject> pvolist = in.getParams()
113: .get(key);
114: String[] rlist = new String[pvolist.size()];
115: for (int i = 0; i < pvolist.size(); i++) {
116: rlist[i] = pvolist.get(i).resolve(resolver);
117: }
118: formparams.put(key, rlist);
119: }
120:
121: isRunning = false;
122: return mangleDoc(registerSPDoc, formparams, false,
123: false);
124:
125: } else if (instr instanceof InteractiveRequestInstruction) {
126: InteractiveRequestInstruction in = (InteractiveRequestInstruction) instr;
127: if (registerSPDoc == null) {
128: try {
129: updateRegisterSPDoc(rcontext
130: .handleRequest(preq));
131: } finally {
132: // Make sure scripted flow is canceled on error
133: isRunning = false;
134: }
135: }
136: ip++;
137:
138: Map<String, String[]> formparams = new HashMap<String, String[]>();
139: for (String key : in.getParams().keySet()) {
140: List<ParamValueObject> pvolist = in.getParams()
141: .get(key);
142: String[] rlist = new String[pvolist.size()];
143: for (int i = 0; i < pvolist.size(); i++) {
144: rlist[i] = pvolist.get(i).resolve(resolver);
145: }
146: formparams.put(key, rlist);
147: }
148:
149: isRunning = true;
150: return mangleDoc(registerSPDoc, formparams, false,
151: false);
152:
153: } else if (instr instanceof SetVariableInstruction) {
154: SetVariableInstruction in = (SetVariableInstruction) instr;
155: String varName = in.getVariableName();
156: String varValue = in.getVariableValue().resolve(
157: resolver);
158: registerVariables.put(varName, varValue);
159: ip++;
160:
161: } else if (instr instanceof JumpCondFalseInstruction) {
162: JumpCondFalseInstruction in = (JumpCondFalseInstruction) instr;
163: if (resolver.evalXPathBoolean(in.getCondition())) {
164: ip++;
165: continue;
166: } else {
167: Instruction target = in.getTargetInstruction();
168: for (int i = 0; i < il.length; i++) {
169: if (il[i] == target) {
170: ip = i;
171: continue mainloop;
172: }
173: }
174: isRunning = false;
175: throw new RuntimeException(
176: "Jump references non-existing target!");
177: }
178:
179: } else if (instr instanceof JumpUncondInstruction) {
180: JumpUncondInstruction in = (JumpUncondInstruction) instr;
181: Instruction target = in.getTargetInstruction();
182: for (int i = 0; i < il.length; i++) {
183: if (il[i] == target) {
184: ip = i;
185: continue mainloop;
186: }
187: }
188: isRunning = false;
189: throw new RuntimeException(
190: "Jump references non-existing target!");
191:
192: } else if (instr instanceof NopInstruction) {
193: ip++;
194: continue;
195:
196: } else if (instr instanceof VirtualRequestInstruction) {
197: VirtualRequestInstruction in = (VirtualRequestInstruction) instr;
198: Map<String, String[]> reqParams = new HashMap<String, String[]>();
199: for (String key : in.getParams().keySet()) {
200: List<ParamValueObject> pvolist = in.getParams()
201: .get(key);
202: String[] rlist = new String[pvolist.size()];
203: for (int i = 0; i < pvolist.size(); i++) {
204: rlist[i] = pvolist.get(i).resolve(resolver);
205: }
206: reqParams.put(key, rlist);
207: }
208: boolean dointeractive = in.getDoInteractive();
209: boolean reuseparams = in.getReuseParamsForInteractive();
210: boolean retval = false;
211: try {
212: retval = doVirtualRequest(in.getPagename(),
213: reqParams, preq, rcontext);
214: } finally {
215: // Make sure scripted flow is canceled on error
216: isRunning = false;
217: }
218: isRunning = true;
219: ip++;
220:
221: if (!retval && dointeractive) {
222: if (reuseparams) {
223: return mangleDoc(registerSPDoc, reqParams,
224: true, true);
225: } else {
226: return mangleDoc(registerSPDoc, null, true,
227: true);
228: }
229: }
230:
231: continue;
232: } else {
233: isRunning = false;
234: throw new RuntimeException(
235: "Found instruction not understood by the VM!");
236: }
237: }
238:
239: // Reached end of script
240: isRunning = false;
241: return mangleDoc(registerSPDoc, null, false, false);
242: }
243:
244: public boolean isExitState() {
245: return !isRunning;
246: }
247:
248: private SPDocument mangleDoc(SPDocument spdoc,
249: Map<String, String[]> formparams, boolean removeerrors,
250: boolean removevalues) {
251: Document doc = spdoc.getDocument();
252: if (doc != null) {
253: Element root = doc.getDocumentElement();
254: root.setAttribute("scriptedflowname", script.getName());
255: root.setAttribute("scriptedflowrunning", "" + isRunning);
256:
257: if (removevalues) {
258: Element formvalues = (Element) resolver
259: .evalXPathNode("/formresult/formvalues");
260: Node nextNode;
261: while ((nextNode = formvalues.getFirstChild()) != null) {
262: formvalues.removeChild(nextNode);
263: }
264: }
265: if (removeerrors) {
266: Element formerrors = (Element) resolver
267: .evalXPathNode("/formresult/formerrors");
268: Node nextNode;
269: while ((nextNode = formerrors.getFirstChild()) != null) {
270: formerrors.removeChild(nextNode);
271: }
272: }
273: if (formparams != null) {
274: Element formvalues = (Element) resolver
275: .evalXPathNode("/formresult/formvalues");
276: for (String pname : formparams.keySet()) {
277: String[] values = formparams.get(pname);
278: if (values != null && values.length > 0) {
279: for (String value : values) {
280: Element param = doc.createElement("param");
281: param.setAttribute("name", pname);
282: param
283: .appendChild(doc
284: .createTextNode(value));
285: formvalues.appendChild(param);
286: }
287: }
288: }
289: }
290: }
291: return spdoc;
292: }
293:
294: private boolean doVirtualRequest(String pagename,
295: Map<String, String[]> reqParams,
296: PfixServletRequest origPreq, ExtendedContext rcontext)
297: throws PustefixApplicationException, PustefixCoreException {
298:
299: HttpServletRequest vhttpreq = new VirtualHttpServletRequest(
300: origPreq.getRequest(), pagename, reqParams);
301: PfixServletRequest vpreq = new PfixServletRequestImpl(vhttpreq,
302: System.getProperties());
303:
304: // Send request to the context and use returned SPDocument
305: // for further processing
306: SPDocument newdoc = rcontext.handleRequest(vpreq);
307: if (newdoc != null) {
308: updateRegisterSPDoc(newdoc);
309: }
310:
311: if (resolver.evalXPathBoolean("/formresult/formerrors/error")) {
312: return false;
313: } else {
314: return true;
315: }
316: }
317:
318: private void updateRegisterSPDoc(SPDocument spdoc) {
319: this.registerSPDoc = spdoc;
320: this.resolver.setSPDocument(spdoc);
321: }
322: }
|