-
1. Re: dynamic commands
estaub Sep 18, 2007 5:44 PM (in response to gogoasa)Adrian,
Very cool! Groovy, even ;-)
I've been noodling over how to make commands more extensible - this is definitely the ticket.
-Ed Staub -
2. Re: dynamic commands
kukeltje Sep 19, 2007 6:20 AM (in response to gogoasa)Yes, it is more extensible, but personally I'd prefere that people who develop 'real' commands, 'donate' these to the project so everybody can benefit. With to much flexibility, that will probably done less :-(
-
3. Re: dynamic commands
gogoasa Sep 19, 2007 10:52 AM (in response to gogoasa)I guess donations should be accepted for scripted commands too :)
I think the big problem with the set of static commands is finding a case -- in production -- that you can't manage because the command does not exist.
For instance, right now, if I'm not mistaken, there is no command that allows moving a token to an arbitrary node (without signalling). -
4. Re: dynamic commands
kukeltje Sep 19, 2007 4:39 PM (in response to gogoasa)I know and I do not totally disagree, but you can easilly add commands implemented in java as well.... the source IS open you know. And in our dev/test/acc/prod process we'd have to deploy all anyway, so adding a real command does not differ that much from adding a new dynamic command.
-
5. Re: dynamic commands
gogoasa Sep 21, 2007 6:01 AM (in response to gogoasa)Here is my current implementation of BshCommand in case anybody needs it.
It supports passing optional parameters to scripts (in case you keep your script as a resource and want to pass it a parameter without having to String.replaceAll(...):package org.jbpm.command; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.jbpm.JbpmContext; import bsh.Interpreter; /** * this Command executes a BeanShell script, optionally binding some variables. * This way you can dynamically execute arbitrary code on your jBPM installation. */ public class BshCommand implements Command { String JBPMCONTEXT_BINDING_VARIABLE = "jbpmContext"; // the script code protected String code; // variables to bind protected Map scriptVariables; public BshCommand(String code) { this(code, new HashMap()); } public BshCommand(String code, Map scriptVariables) { this.setCode(code); this.setScriptVariables(scriptVariables); } /** * as Java does not have inline constructors for Maps but it does have constructors for arrays, you may prefer to provide * arguments using this constructor. * * @see BshCommand(String code, Map scriptVariables) */ public BshCommand(String code, String[] keys, Object[] values) { if (keys == null || values == null) throw new NullPointerException("cannot accept null keys or values"); if (keys.length != values.length) throw new IllegalArgumentException("keys and values arrays must be of the same length"); Map map = new HashMap(); for (int i = 0; i < keys.length; i++) map.put(keys, values); this.setCode(code); this.setScriptVariables(map); } /** * convenient alias for BshCommand(String code, String[] keys, Object[] values) if you only want to pass one argument to * your script */ public BshCommand(String code, String key, Object value) { this(code, new String[] {key}, new Object[] {value}); } public Object execute(JbpmContext jbpmContext) throws Exception { Interpreter interpreter = new Interpreter(); interpreter.set(JBPMCONTEXT_BINDING_VARIABLE, jbpmContext); if (this.scriptVariables != null) { for (Iterator i = this.scriptVariables.keySet().iterator(); i.hasNext();) { String varName = (String) i.next(); if (varName.equals(JBPMCONTEXT_BINDING_VARIABLE)) continue; Object o = this.scriptVariables.get(varName); interpreter.set(varName, o); } } Object result = interpreter.eval(this.getCode()); return result; } // accessors public String getCode() { return code; } public void setCode(String code) { this.code = code; } public Map getScriptVariables() { return scriptVariables; } public void setScriptVariables(Map scriptVariables) { this.scriptVariables = scriptVariables; } private static final long serialVersionUID = 1L; }
some usage example. Suppose you have a script like this as a classpath resource (do not call it script.bsh if you deploy under jboss because jboss will think it must deploy it as an ejb. Call it moveTokenToNodeAndSignal.bsh.command for example):// forcibly move a process instance's token to "System error" node and signal it to "restart". pi = jbpmContext.getProcessInstance(pid); tk = pi.getRootToken(); pdef = pi.getProcessDefinition(); node = pdef.getNode("System error"); tk.addLog(new org.jbpm.logging.log.MessageLog("forcing restart of process that has no 'restart' leaving transition")); tk.setNode(node); tk.signal("restart");
String script = IOUtils.toString(this.getClass().getClassLoader().getResourceAsStream("moveTokenToNodeAndSignal.bsh.command")); commandService.execute(script, "pid", 4);
-
6. Re: dynamic commands
kukeltje Sep 21, 2007 7:13 AM (in response to gogoasa)cool... can you make a wiki page for this?
-
7. Re: dynamic commands
gogoasa Sep 21, 2007 8:54 AM (in response to gogoasa)Here it is : http://wiki.jboss.org/wiki/Wiki.jsp?page=BshCommand
-
8. Re: dynamic commands
kukeltje Sep 21, 2007 9:51 AM (in response to gogoasa)thanks... one more question....
can you put it in a page called JbpmBshCommand ? -
9. Re: dynamic commands
gogoasa Oct 10, 2007 10:19 AM (in response to gogoasa)Mark Richards about the Command pattern -- which is heavily used by jBPM: http://www.nofluffjuststuff.com/media.jsp?mediaId=28
Noteworthy: he uses a DTO view of the Command for the client; the CommandImpl that contains the actual code only resides on the server ; he hates dynamic commands too :) -
10. Re: dynamic commands
kukeltje Oct 10, 2007 7:26 PM (in response to gogoasa)Yeah... now I'm not alone anymore and even better, I'm in good company ;-)