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.

Veröffentlicht unter Java, Java Script, ServerSide JavaScript, XPages | Verschlagwortet mit , , , , | 3 Kommentare

SSJS: What’s „this“?

In Serverside JavaScript the keyword this always refers to the “owner” of the function which is executing,  or rather, to the object that a function is a method of.

This means f.e. that this refers to the UIComponent which contains the SSJS code. If you add a label to a XPage and compute the value…

<?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:return this;}]]>
      </xp:this.value>
   </xp:label>
</xp:view>

this will always return the current corresponding instance of the XspOutputLabel:

But if you are inside a function this will return the current JavaScript object instead:

<?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:
            var arr = [1, 2];

            function callMe(){
               var obj = this;
               for( var p in obj ){
                  print( p + " -> " + obj[p] + " [" + typeof(obj[p]) + "]" );
               }    
            }
            callMe()
         }]]>
      </xp:this.value>
   </xp:label>

</xp:view>

The result on the console looks like this:

If you are using call or apply, you can overwrite the this object:

<?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:
            function callMe(){
               println(this);

               for( p in arguments ){
                  println( arguments[p] );
               } 
            }

            callMe.call("1","2","3");
         }]]>
      </xp:this.value>
   </xp:label>
</xp:view>

Now this is set to „1„. The arguments are „2“ and „3„, as you can see on the server console:

Veröffentlicht unter Allgemein, Java Script, ServerSide JavaScript, XPages | Verschlagwortet mit , , , | Ein Kommentar

Quick-n-Dirty: @ClientType() in XPages

The @ClientType formula provides an interesting behaviour: If you add the value to a label, the result will be as expected. It returns “Web” in Browser and returns “Notes” in XPiNC.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
   <xp:label value="#{javascript:@ClientType()}" id="label1" />
</xp:view>

But if you add it to a computed field on a form and use this form in a datasource, the result in XPiNC is not „Notes„, it is „None“ instead. In the following example, the form „Test“ has a computed field named „ClientType„. The value of the field is @ClientType.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

   <xp:this.data>
      <xp:dominoDocument var="document1" formName="Test" 
         computeWithForm="onload">
      </xp:dominoDocument>
   </xp:this.data>

   <xp:label value="#{javascript:@ClientType()}" id="label1" />
   <xp:br />
   <xp:label id="label2">
      <xp:this.value>
         <![CDATA[#{javascript:
            document1.getItemValueString("ClientType")
         }]]>
     </xp:this.value>
   </xp:label>
</xp:view>

[The computeWithForm property of the datasource has to be set to „onload“ or „onsave„. If set to „onboth“ this will not work.]

This behaviour gives you control about the subforms which are used in the form, depending of the client and/or technology. Just add a computed subform to your form and add a @Formula like this:

@If(
    @ClientType="Web";"WebAccess";
    @ClientType="Notes";"NotesAccess";
    "XPiNCAccess"
)

This allows you to add specific fields to a document, or can help to identify the way how the document was created. If you add a field in the querySave event on the XPage, you can identify if this document was created in the web by XPage or old-school web access of the form.

<xp:this.data>
   <xp:dominoDocument var="document1" formName="Test"
      computeWithForm="onsave">
      <xp:this.querySaveDocument>
         <![CDATA[#{javascript:
            document1.replaceItemValue("ClientTypeXPage", "1"))
         }]]>
      </xp:this.querySaveDocument>
   </xp:dominoDocument>
</xp:this.data>

By extending the subform computation, you can use a subform for every type of access:

@If(
    @ClientType="Web" & ClientTypeXPage="1";"XPageWebAccess";
    @ClientType="Web";"WebAccess";
    @ClientType="Notes";"NotesAccess";
    "XPiNCAccess"
)
Veröffentlicht unter @Formula, Allgemein, ServerSide JavaScript, Web, XPages | Verschlagwortet mit , , , , , | Schreib einen Kommentar

XPages: Additional Facets for Viewpanels

Today I found some additional facets / sections for a normal view panel component which can help to design a layout of an application. They are named like the wind directions and  are equivalent to the position information for the relevant content.

The following screenshot shows all facets in use. In the center you can see the content of the view panel. The pagers are not used, only place holders for a better demonstration:

As you can see there is some HTML source code in the top of the page visible. This happens if you are using the „north“ facet. I don’t know why this happens.

Here is the source code of the demo XPage:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
    xmlns:xc="http://www.ibm.com/xsp/custom">

    <xp:viewPanel rows="10" id="viewPanel1">
        <xp:this.facets>
            <xc:ccDisplayer name="headerPager" color="255,0,0" xp:key="headerPager" />
            <xc:ccDisplayer name="footerPager" color="0,255,255" xp:key="footerPager" />
            <xc:ccDisplayer name="viewTitle" color="255,0,255" xp:key="viewTitle" />
            <xc:ccDisplayer name="south" color="0,255,0" xp:key="south" />
            <xc:ccDisplayer name="southWest" color="96,96,96" xp:key="southWest" />
            <xc:ccDisplayer name="west" color="128,128,128" xp:key="west" />
            <xc:ccDisplayer name="northWest" color="160,160,160" xp:key="northWest" />
            <xc:ccDisplayer name="north" color="255,255,0" xp:key="north" />
            <xc:ccDisplayer name="northEast" color="192,192,192" xp:key="northEast" />
            <xc:ccDisplayer name="east" color="224,224,224" xp:key="east" />
            <xc:ccDisplayer name="southEast" color="255,255,255" xp:key="southEast" />
        </xp:this.facets>
        <xp:this.data>
            <xp:dominoView var="view1" viewName="DemoView"></xp:dominoView>
        </xp:this.data>
        <xp:viewColumn columnName="Col1" id="viewColumn1">
            <xp:viewColumnHeader value="DocUNID" id="viewColumnHeader1"></xp:viewColumnHeader>
        </xp:viewColumn>
    </xp:viewPanel>
</xp:view>

And here is the custom control used.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
    <xp:this.style>
       <![CDATA[#{javascript:"height:100.0px;width:100.0%;background-color:rgb(" + compositeData.color + ")"}]]>
    </xp:this.style>
    <xp:label value="#{javascript:compositeData.name}" id="labelName">
    </xp:label>
</xp:view>
Veröffentlicht unter Allgemein, HTML, XPages | Verschlagwortet mit , , , , | 3 Kommentare

XPages: Access a datasource from one custom control in another one

On stackoverflow.com, a very interesting question was asked: How can you access a document datasource from one custom control in another custom control. And here comes my solution in a small SSJS function.

First you have to add an id to the custom control which contains the datasource. This gives you a handle for other custom controls:

<xc:ccDS1 id="idDSComponent"></xc:ccDS1>

Now it is possible to get the custom control by using the SSJS function getComponent(). The datasources are stored in the data attribute of the custom control. You just have to iterate through them and check every entry for the name you are looking for.

And here is the code snippet to access the datasource in another custom control:

/***
 * getDatasource
 * Resolves a datasource from a custom control
 * 
 * @param componentId    id of the component containing the datasource
 * @param dsName    name of the datasource in the component
 * @author Sven Hasselbach
 */
 function getDatasource( componentId:String, dataSourceName:String ):com.ibm.xsp.model.domino.DominoDocumentData {
    try{
       var data:java.util.ArrayList = getComponent( componentId ).getAttributes().get("data");
       if( data == null )
          return null;
                    
       var it:java.util.Iterator = data.iterator();
       var obj = null;
       while( it.hasNext() ){
          obj = it.next();
          if( obj.getVar() == dataSourceName )
             return obj;
       }
    }catch(e){
       print( e );
    }
}

To use the function you have to use it like this:

getDatasource( "idDSComponent", "document1" )

Here is the link to the original question: How to get document datasource in another Custom Control?

Veröffentlicht unter Java Script, ServerSide JavaScript, XPages | Verschlagwortet mit , , , , | 2 Kommentare

XPages: Access resources and their content (2)

By using the same code as described in the previous article you can access the complied java classes. To get the correct path you have to do the following:

1. Select the java element you want to access

2. Have a look inside the „$ClassIndexItem“ field

3. This is the path of the compiled Java class:

4. Change the variable url to the specific path

var url = "WEB-INF/classes/ch/hasselba/jsf/servlet/MyServletFactory.class";

The first 30 Bytes of the resulting output are for internal use only and have to be skipped. They contain header informations like the length of the java class. The compiled java class starts from &HCAFEBABE.

It is even possible to access the generated XPages code. In this case there are multiple classes in a design document. For the XPage „Demo.xsp“ the two generated classes are:

var url = "WEB-INF/classes/xsp/Demo.class"

and

var url = "WEB-INF/classes/xsp/Demo$DemoPage.class"
Veröffentlicht unter Java, ServerSide JavaScript, XPages | Verschlagwortet mit , , , , , | Schreib einen Kommentar

XPages: Access resources and their content

To access XPages resources during runtime and to get their content as a string, you can use the following SSJS code snippet:

<?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:
            var url = "/WEB-INF/faces-config.xml";
            var data = facesContext.getExternalContext().
                getResourceAsStream( url );
            var txt = "";
            while( data.available() ){
                txt += @Char(data.read());
            }
            txt}]]>
        </xp:this.value>
    </xp:label>

</xp:view>

[This displays the current faces-config.xml]

By using the correct URL path it is for example possible to access the code of SSJS libraries or even the source code of java classes in the database. To access the source code of a XPage, just use the name of the XPage itself. Same procedure for SSJS libraries, just add a „.jss“ at the end of the library’s name. For accessing a java class, the „dots“ in the package names have to be replaced with „slashes“.

To access f.e. the java file for class ch.hasselba.jsf.debug.ResourceUtil the url has to look like this:

var url = "ch/hasselba/jsf/debug/ResourceUtil.java";
Veröffentlicht unter Java, Java Script, ServerSide JavaScript, XPages | Verschlagwortet mit , , , , , | Ein Kommentar

XPages: Run your own Servlets

A really interesting article about running your own servlets on domino server can be found here: http://www.ibm.com/developerworks/cn/lotus/xpage-servlet/index.html

It’s chinese, but you can translate f.e. with Google’s Translator.

With 8.5.3 I had have some problems because the required interface IServletFactory could not be resolved in DDE.

To get the example running, you have to add a JAR to your build path. Open Project properties, select „Java Build Path“ and click „Add External JARs„:

Now you have to add the file „lwpd.domino.adapter.jar„. This can be found in the following folder:

<NOTES>\framework\shared\eclipse\plugins\com.ibm.domino.xsp.adapter_<VERSION>

The <VERSION> string depends on your current installation and Server-Version.

After adding this JAR to the build path, the compilation problems should be resolved.

Veröffentlicht unter Extensibility API, Java, JSF, Server, Web, XPages | Verschlagwortet mit , , , , , , | 6 Kommentare

XPages application events: Create your own ApplicationListener (2)

There is another interface available which provides the additional method applicationRefreshed. This event is always raised if the method refresh() of the Application-Object is fired.

Instead of implement the interface described in the previous posting, you have to use the interface named com.ibm.xsp.application.events.ApplicationListener2.

package ch.hasselba.jsf.debug;

import com.ibm.xsp.application.ApplicationEx;
import com.ibm.xsp.application.events.ApplicationListener2;

public class MyApplicationListener implements ApplicationListener2 {

    public void applicationCreated(ApplicationEx arg0) {
        System.out.println("applicationCreated()");
        System.out.println("ID: " + arg0.getApplicationId());
     }

    public void applicationDestroyed(ApplicationEx arg0) {
        System.out.println("applicationDestroyed()");
        System.out.println("ID: " + arg0.getApplicationId());
    }

    public void applicationRefreshed(ApplicationEx arg0) {
        System.out.println("applicationRefreshed()");
        System.out.println("ID: " + arg0.getApplicationId());
    }

}

You can fire the refresh of the application manually. Here is a simple example how to do this:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
   <xp:button value="Label" id="button1">
      <xp:eventHandler event="onclick" submit="true"
         refreshMode="complete">
            <xp:this.action>
               <![CDATA[#{javascript:
                  facesContext.getApplication().refresh()
               }]]>
            </xp:this.action>
        </xp:eventHandler>
   </xp:button>
</xp:view>

By clicking the button, the refresh is fired:

Veröffentlicht unter Extensibility API, Java, JSF, XPages | Verschlagwortet mit , , , , , , | 5 Kommentare

XPages application events: Create your own ApplicationListener

If you want to get a handle to application events and want to know if a XPages application is created or destroyed (which means the application was destroyed because of a time out), you can implement this by creating your own own application listener.

For this you have to do the following steps:

  1. Create a class which implements the ApplicationListener interface
  2. Activate the class in XPages runtime

To realise the first part, you have to create a class that implements the interface com.ibm.xsp.application.events.ApplicationListener. This interface has two methods: applicationCreated and applicationDestroyed (the method names should be self-describing enough).

Here is an example:

package ch.hasselba.jsf.debug;

import com.ibm.xsp.application.ApplicationEx;
import com.ibm.xsp.application.events.ApplicationListener;

public class MyApplicationListener implements ApplicationListener {

    public void applicationCreated(ApplicationEx arg0) {
        System.out.println("applicationCreated()");
        System.out.println("ID: " + arg0.getApplicationId());
    }

    public void applicationDestroyed(ApplicationEx arg0) {
        System.out.println("applicationDestroyed()");
        System.out.println("ID: " + arg0.getApplicationId());  
    }

}

Now to step two, the activation of the class. To do this, you have to add a special configuration file to your application:

  1. Switch to „Java“ perspective
  2. Create a new folder „META-INF“ in the „Code/Java“ section
  3. Create a sub folder „services
  4. Create a file named „com.ibm.xsp.core.events.ApplicationListener
  5. In this file you add the full name of your MyApplicationListener class (including the package name):

The file structure should now look like this in Java perspective:

If you switch back to Domino perspective, the structure in the „Code/Java“ looks like this:

You can now verify that all works correctly by opening a XPage and have a look on your server console:

[The application timeout was set to one minute.]

Veröffentlicht unter Extensibility API, Java, JSF, XPages | Verschlagwortet mit , , , , , , | Ein Kommentar