SSJS: Execute remote SSJS Code

I have created a small helper class to run SSJS code from a remote server. The basic idea behind this class is a question on stackoverflow: http://stackoverflow.com/questions/12054733/include-jss-file-from-notes-document-as-resource

As far as I know there is no way to add a SSJS resource via the src attribute, this won’t work:

<xp:this.resources>
   <xp:script src="http://localhost:8080/test.jss"
      clientSide="false" />
</xp:this.resources>

It will always fail, even if the file is available, has the correct file extension etc.

That’s why I wrote the code, it’s only a proof of concept. There are no security features to protect against manipulations, no caching for better performance and whatever.

Here is a demo XPage to demonstrate how to use it:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
   <xp:label id="label1">
      <xp:this.value>
         <![CDATA[#{javascript:
            importPackage( ch.hasselba.xpages.util.ssjs );
            SSJSUtil.executeSSJSFromURL("http://localhost:8080/test.jss");
            test();
         }]]>
      </xp:this.value>
   </xp:label>
</xp:view>

The method executeSSJSFromURL loads a text file from the given URL, creates a method binding with the content and invokes it. Then, the SSJS code is executed directly – all functions, objects and variables defined in the remote code are ready to use from now on. As you can see above, the method test() is called which is defined in the remote SSJS file.

And here is the Java code:

package ch.hasselba.xpages.util.ssjs;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import com.ibm.xsp.util.StreamUtil;
import com.ibm.xsp.page.compiled.ExpressionEvaluatorImpl;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;

/**
 * SSJSUtil
 * 
 * helper class for SSJS operations
 * 
 * @author Sven Hasselbach
 * @version 1.0.2
 * @category SSJS
 * @category Utility
 */

public class SSJSUtil {

    private static final String NEWLINE = "\n";
    private static final String SSJS_EXPRESSION_BEGIN = "#{javascript:";
    private static final String SSJS_EXPRESSION_END = "}";

    /**
     * Loads SSJS code from a given URL and executes it
     * Declared methods and objects are reachable for other SSJS code
     * 
     * @param url of the SSJS code
     * @return Object resulting object from SSJS execution
     * @author Sven Hasselbach
     * @version 1.0.1
     * @category Utility
     */
    public static Object executeSSJSFromURL( final String url ){
        return execute( loadFromURL( url ) );
    }

    /**
     * loads a URL stream and converts it to a string
     * @param url of the resource
     * @return String containing the data loaded from given url
     * @author Sven Hasselbach
     * @version 1.0.1
     * @category Utility
     */
    public static String loadFromURL( final String url ){
        String ret = null;
        try{
            FacesContext fc = FacesContext.getCurrentInstance();
            InputStream in = StreamUtil.getInputStream(fc, url);
            ret = inputStreamToString( in );
        }catch(Exception e){
            e.printStackTrace();
        }
        return ret;
    }

    /**
     * executes given SSJS code and returns the result (if any)
     * functions / libraries are added to runtime 
     * 
     * @param ssjsCode code to execute
     * @return resulting object
     * @author Sven Hasselbach
     * @version 1.0.2
     * @category SSJS
     */
    public static Object execute( final String ssjsCode ){
        Object ret = null;

        try{
            String valueExpr = SSJS_EXPRESSION_BEGIN + ssjsCode + SSJS_EXPRESSION_END;
            FacesContext fc = FacesContext.getCurrentInstance();
            ExpressionEvaluatorImpl evaluator = new ExpressionEvaluatorImpl( fc );
            ValueBinding vb = evaluator.createValueBinding( fc.getViewRoot(), valueExpr, null, null);
            ret = vb.getValue(fc);
        }catch(Exception e){
            e.printStackTrace();
        }
        return ret;
    }

    /**
     * converts the data from a given inputstream to a string
     * 
     * @param in InputStream to convert
     * @return String containing data from input stream
     * @throws IOException
     * @author Sven Hasselbach
     * @version 1.0.1
     * @category Utility
     */
    public static String inputStreamToString(final InputStream inStream) throws IOException {
        BufferedReader bufReader = new BufferedReader( new InputStreamReader(inStream) );
        StringBuilder strBuilder = new StringBuilder();
        String line = null;

        while ((line = bufReader.readLine()) != null) {
            strBuilder.append(line);
            strBuilder.append( NEWLINE );
        }
        bufReader.close();

        return strBuilder.toString();
     }
}

By the way: You cannot use the import method in the remote code.

Dieser Beitrag wurde unter Java, Java Script, ServerSide JavaScript, XPages abgelegt und mit , , , , verschlagwortet. Setze ein Lesezeichen auf den Permalink.

3 Kommentare zu SSJS: Execute remote SSJS Code

  1. Russell Maher sagt:

    Very clever! / Sehr clever! (Sorry for the bad Google translation)

  2. Daniel sagt:

    Hello Sven,

    this is brilliant. Did you ever experiment with that any further? Could you also load CSJS libraries that way and use their functions in SSJS?

    Thank you for sharing!

    Daniel

    • Sure, this is theoretically possible. But keep in mind that the SSJS engine differs from common CSJS engines, some functionality which are available for browsers are not possible with SSJS.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.


*