image001

 

 

Support for server-side scripts, such as JavaScript, in Panther HTML templates

                     

 

This new feature adds support for a new Panther Web template tag, {{jsr223: engine-name}}, where engine-name is the name of a script engine that is installed in the JVM for the Jserver.  The text immediately following this tag is not interpreted by the template parser until the end tag is reached, {{end:}}.  The text between the two tags is sent to a new function, sm_jsr223_eval().

 

sm_jsr223_eval() is passed two arguments.  The first is the name of the script engine to use.  The second is the script text to be executed by that script engine.

 

As its name implies, sm_jsr223_eval() makes use of the Java API's of javax.script, as defined in the Java specification, JSR-223.  Specifically it uses the eval() method of the designated script engine in order to process the script.

 

In order to support output from a script engine, this patch provides a new Java class within pro5.jar, com.prolifics.jni.WebWriter.  This class implements the abstract class, java.io.Writer.  Note that a javax.script.ScriptEngine implementation uses a java.io.Writer to write output for script API's that would normally write to stdout.

 

A JavaScript script engine is built into J2SE, and it supports a global print() function.  Third party script engines may use other global functionality or mechanisms for output.  This patch configures the designated script engine to use com.prolifics.jni.WebWriter for output from such mechanisms.  WebWriter uses the output socket that the Jserver uses for writing output back to the client.

 

The present patch also adds support for several of the Panther Web prototyped functions as methods of a new Java interface and a new class of the package com.prolifics.jni.  These are WBFunctionsInterface and WBFunctions, respectively.  The new Java API's come as a byproduct of implementing scripting support within templates by means of a JVM.


 

 

Here is the Java source code of WBFunctionsInterface.java:

 

package com.prolifics.jni;

 

public interface WBFunctionsInterface

{

 

        /* Entries from file: */

        /* #define SM_SCCSID "@(#)smuweb.h      77.8 13/10/22 15:52:40" */

 

        int sm_web_push_array(String a1, String a2);

        int sm_web_unsave_all_globals();

        String sm_web_get_cookie(String a1);

        void sm_web_set_http_header(String a1);

        void sm_web_invoke_targeted_url(String a1, String a2);

        int sm_web_push(String a1, String a2);

        int sm_web_unsave_global(String a1);

        int sm_web_set_onevent(String a1);

        void sm_web_invoke_url(String a1);

        void sm_web_log_error(String a1);

        int sm_web_save_global(String a1);

        void sm_web_set_cookie(String a1);

}

 

The implementation class for this interface, WBFunctions, defines these as native methods.  The new classes are provided within pro5.jar.  Panther's latest documentation set provides Javadoc documentation for all available classes and interfaces of the com.prolifics.jni package, including for the new class and interface.  Further information may be found in the Panther Library Functions section of the Programming Guide, which documents the usage of the C functions that correspond to available Java native methods by the same name.

 

Note that in order to use a third party product that provides a JAR file for use with its JSR 223 compliant script engine, you must add that JAR file to your CLASSPATH, as defined in your Panther Web application's INI file.  Any JSR 223 compliant script engine may be used with Panther Web.  However, be aware that Prolifics does not recommend use of the script engines for PHP scripting that are provided by the PHP/Java Bridge product.   Prolifics has evaluated version 7.2.1 of this product and found it to contain security issues.


 

 

Further Considerations

 

Scripts embedded within templates are executed during template evaluation, which occurs only when the Jserver has begun to return output to the client.  This occurs after normal screen processing, but just before the closing of any open screens and normal screen exit processing.  Therefore, some Panther functionality, while possible to execute within a script, may not be appropriate for that late stage of processing.  Nevertheless, Panther Web enforces no restrictions on what Panther functions may be called from a script.  A script engine, such as the Nashorn script engine for JavaScript, provided with J2SE, allows for full access to Panther's Java APIs.  Since scripts are executed inline while processing a template, the most appropriate use is for server-side mashups that merge the output from a Panther Web application with that of other server-side technologies, in order to produce a single output document that combines the content from both.  Prolifics recommends the use of control strings, validation functions, and other normal screen processing functionality over server-side template scripting where possible.

 

Multiple {{jsr223:}} tags may be used within the same template document.  Be aware that a variable defined within the global context for a previous script may or may not still be defined and retain its value within the current script.  It will not be defined for Rhino and Nashorn, but this behavior may be different for different kinds of script engines, and a vendor may provide some custom mechanism to make objects global across script executions and/or ScriptEngine instances.  In order to make use of such a capability or to negate it, you may need to know that the Jserver will attempt to reuse a previously acquired ScriptEngineManager instance, making it global to the JVM.  In other words the same ScriptEngineManager instance is used not only across scripts within the same template, but also across all requests that execute scripts by any user for the same Jserver.  This is a feature that improves performance, but may need to be considered in order to prevent unwanted sharing of objects across requests to the Jserver.  Fortunately, it is not a problem for JavaScript scripts using the built-in support for JSR 223 in J2SE.

 

Note that for each script that is processed, the ScriptEngineManager's getEngineByName() method will be called by the Jserver, potentially causing it to acquire a new or reinitialized ScriptEngine instance.  Also, if a ScriptEngine implementation class includes a close() method (non standard), the Jserver will call that method after a script is processed.

 

The functionality discussed above is implemented within the function sm_jsr223_eval(). This function is declared in smproto.h.  Although it was described briefly in this document, no further documentation is provided for it, and it is not available by default from JPL.  Nevertheless, there is no restriction to prevent application developers from prototyping sm_jsr223_eval() in order to call it from JPL.  Prolifics does not recommend doing so, however. Its functionality can be easily implemented directly in Java, and it is more efficient for applications to invoke scripts directly from Java than to call sm_jsr223_eval().  It should also be apparent that without any of the changes implemented for supporting the new feature enabling scripts to be embedded within Panther templates, scripts can be used in any type of Panther application that loads a JVM, Panther Web or otherwise, simply by making use of the JVM's built-in support for JSR 223 and Panther's existing support for Java.


 

 

Examples

 

Here is the most basic example of a script embedded within a Panther Web template.  Note that the color coding is artificial, and used only for clarity in this document.

 

Example 1:

 

<!DOCTYPE html>

<html>

<head>

</head>

<body>

{{form:tag}}

{{form:info}}

{{form:messages}}

<p id="demo"></p>

{{jsr223:JavaScript}}

print("Hello, World");

{{end:}}

</form>

</body>

</html>

 


 

 

The second example is not meant to demonstrate a realistic use case, but is contrived to demonstrate various capabilities of server-side JavaScript usage and syntax within a Panther template.

 

Example 2:

 

<!DOCTYPE html>

<html>

<body>

{{form:messages}}

{{form:tag}}

<!--

   It is assumed that myfield is a field on the screen that uses this template.

-->

<p>{{myfield}}</p>

 

{{jsr223:JavaScript}}

//Start of 1st Script

 

/* We can use JavaScript comment syntax here, because only the script engine

 * sees what is between the {{jsr223:JavaScript}} tag and end tag.  We

 * may not use any HTML or other template tags here.

 *

 * The commented lines below are unnecessary using either Rhino or Nashorn.

 * They are left commented in order to show that there are differences between

 * the two scripting engines.  Rhino is used for J2SE 6 and earlier, while

 * Nashorn is used for J2SE 7 and later.

 */

 

/*

var usingNashorn = typeof Java !== "undefined" && Java && typeof Java.type === "function";

if (!usingNashorn) {

        // The following is supported by only Rhino

        importPackage(com.prolifics.jni);

        importClass(com.prolifics.jni.Application);

} else {

        // The following is supported by only Nashorn

        var Application = Java.type("com.prolifics.jni.Application");

}

*/

 

/* Here's how you can work with Panther's Java APIs */

var app = com.prolifics.jni.Application.getInstance();

var cfuncs = app.getCFunctions();

 

/* Here we show that we may put content generated by this script into fields,

  * whose content will be displayed by template tags that appear further down

  * in the template.

  */

cfuncs.sm_n_putfield("script_output", "<p>Hello, from server-side JavaScript!</p>");

 

/* We may use Panther's WebWriter class directly */

var writer = new com.prolifics.jni.WebWriter;

writer.write("<p>Hello, WebWriter!</p>");

 

/* We can display the content of the global 'this' object */

print("<p><b>Content of 'this' " + this.toString() + ":</b></p><p>");

for (var x in this) {

        print(x.toString() + " ");

}

print("</p>");

 

{{end:}}  <!- end of 1st Script -->

 

<!--

   It is assumed that myfield is some field on the screen that uses this template.

  The tag below and the script that follows are meant to demonstrate that HTML

  and template tags may be interleaved with scripts and their output.

-->

<p>{{myfield}}</p>

 

 

{{jsr223:JavaScript}}

//Start of 2nd Script

 

/* The following is meant to show whether a variable defined within the global

  * context for the previous script is defined within the present script.

  * It will not be defined for the Rhino and Nashorn JavaScript script engines,

  * though for other script engines, possibly for other kinds of scripts, it

  * may be implementation dependent.

  */

if (typeof cfuncs == "undefined")

        print("cfuncs is undefined");

else

        print("cfuncs is still defined");

 

/* Here we'll display the content of 'this.context'. */

print("<p><b>Content of 'this.context' " + this.context.toString() +

      ":</b></p><p>");

for (var x in this.context) {

        print(x.toString() + " ");

}

print("</p>");

 

{{end:}}  <!- end of 2nd Script -->

 

<!--

   Here we embed the content of the field we populated earlier.

-->

{{raw:script_output}}

{{form:messages}}

</form>

</body>

</html>

 


 

Output Shown in Browser Tab

The following is the output shown in a web browser for Example 2, as used with a simple screen:

Content of myfield on Panther screen.
Hello, WebWriter!
Content of 'this' [object Global]:
app cfuncs println context writer print x org edu javax com net
Content of myfield on Panther screen.
cfuncs is undefined
Content of 'this.context' javax.script.SimpleScriptContext@e83912:
reader notifyAll setBindings removeAttribute setErrorWriter setWriter equals notify class getScopes scopes getWriter wait errorWriter toString hashCode setReader writer getAttribute setAttribute getErrorWriter getAttributesScope getBindings getReader getClass
Hello, from server-side JavaScript!

 

 

 


 

 

 

 


Output shown in Source View Window of Browser

 

The following is the output generated by the jserver from the template shown in Example 2, as used with a simple screen:

 

 

<!DOCTYPE html>

<html>

<body>

 

<form method=post action="http://solaris10.ad.prolifics.com/cgi-bin/curdev_gs/jstest.jam"

 onSubmit="__submitting__ = 1;if (typeof document.forms[0].__pushed__ != 'undefined') document.forms[0].__pushed__.value = 0;">

 

<!--

   It is assumed that myfield is some field on the screen that uses this template.

-->

<p>Content of myfield on Panther screen.</p>

 

<p>Hello, WebWriter!</p><p><b>Content of 'this' [object Global]:</b></p><p>app cfuncs println context writer print x org edu javax com net </p> <!- end of 1st Script -->

 

<!--

   It is assumed that myfield is some field on the screen that uses this template.

  The tag below and the script that follows are meant to demonstrate that HTML

  and template tags may be interleaved with scripts and their output.

-->

<p>Content of myfield on Panther screen.</p>

 

cfuncs is undefined<p><b>Content of 'this.context' javax.script.SimpleScriptContext@5483cd:</b></p><p>reader notifyAll setBindings removeAttribute setErrorWriter setWriter equals notify class getScopes scopes getWriter wait errorWriter toString hashCode setReader writer getAttribute setAttribute getErrorWriter getAttributesScope getBindings getReader getClass </p> <!- end of 2nd Script -->

 

<!--

   Here we embed the content of the field we populated earlier.

-->

<p>Hello, from server-side JavaScript!</p>

 

</body>

</html>