Manual
Next chapter: Text-with-scriptlets Documents
Or, go back to the manual's table of contents
Part 4: Entering Executables
In the previous examples, our executable were "plainly" executed, meaning that execution started at the natural starting point of the executable. Such an executable is often referred to as a "script," meaning that it is a series of commands. However, most programming languages also support more sophisticated "entry points": these are called "functions," "methods," "closures," "blocks," etc. in different languages. "Entering" in Scripturian is an abstraction over "invoking," "calling," "sending a message to," etc. in reference to such an "entry point."
Making an Executable Enterable
Entering an executable requires you first to execute it in a special way. Instead of calling "execute()", we'll call "makeEnterable()":
ExecutionContext executionContext = new ExecutionContext(); boolean enterable = false; try { enterable = executable.makeEnterable( "mykey", executionContext ); if( enterable ) this.executable = executable; } finally { if( !enterable ) executionContext.release(); }
Why do we need a different from that of plain execution?
The first difference is that the executable needs to prepare the entry points for us to enter: it needs to define the functions, link the closures to variables, etc. And so, "makeEnterable()" does in fact execute the executable "plainly."
The second difference is that these entry points are stored in the execution context, and so we cannot simply release it. (It's actually more complicated: depending on the programming language and how it handles scope, these entry points might in fact be capturing the scope that was available during their closure.) So, what happens is that if "makeEnterable()" returns true, the execution context is considered to be "consumed" by the executable. To release it, we will need to call "release()" on the executable itself when we no longer need to keep it alive for the purpose of entering.
(In fact, you will not be able to release a consumed execution context even if you tried. Once consumed, it is immutable, and will throw an exception on any attempt to modify it.)
The third requirement explains the "mykey" enterable key used. This is used internally by Scripturian to identify which execution context to use when entering. This would be an issue if there is a you would be calling "makeEnterable()" at other places in your application on the same executabe. Enterable keys provide a powerful feature which, again, allows for scalability: the very same executable can be reused at various parts of your application without each part knowing about the others, as long as they use separate keys.
Note that these keys are unrelated to threads: any number of threads can use the same key. However, if they do, they will be using the same execution context: the same standard out, services, scope, etc.
Entering an Executable
Now that our executable has been made enterable, we can very easily enter it, by specifying the enterable key, then the entry point name, and finally any arguments to send to the entry point:
this.executable.enter( "mykey", "sayHello", 3, "Tal" );
Here is the JavaScript source code:
function sayHello(count, name) { for (var i = 0; i < count; i++) { log.write(name + '\n'); } }
There are a few points to note:
- Scripturian expects all entry point names to be in camel case, but will translate the camel case to the standard convention for the language. For example, the "sayHello" entry point above will be looked for as "say_hello" in Python and Ruby, and "say-hello" in Clojure. This allows you to always use the same entry point name whatever the language used for the executable.
- Scripturian does not do any data type translation. Most JVM languages deal well enough with standard JVM primitives and types, but in some cases you may need to do your own translation.
- Unlike plain execution, enterability demands a decoupled code flow: it's up to you to make sure that you call "release()" on the executable when you no longer need it; you cannot use a simple try/finally. In some cases "release()" will happen only when your service terminates, but other situations might be more dynamic.
- Execution contexts are very lightweight, however there is still some small cost to setting them up and releasing them. Thus, you may prefer to use "enter()" instead of "execute()" to execute your code in situations in which concurrent performance is critical, and you are sure to be repeatedly using the same execution context. As always, avoid premature optimization and make sure that there is a problem before trying to "fix" it.
Next chapter: Text-with-scriptlets Documents
Or, go back to the manual's table of contents