Posts Tagged: ‘HTTP’

High Performance REST Applications (2) – Dev Environment

23. April 2017 Posted by Sven Hasselbach

Before you can start developing a Servlet as an OSGi Plugins, you must set up a development environment first. To do this, download Eclipse IDE (Eclipse IDE for Java EE Developers) and XPages SDK from OpenNTF (The XPages SDK is a helper to create the JRE environment and the Target Platform). For development it is the best to have a (local) development server, because during development you might want to restart and/or modify it, and debugging is a lot easier if have control over the whole server.

After unpacking Eclipse and the XPages SDK, you have to install it and set up the JRE and the Target Platform. Go to „Help > Install New Software“. With „Add…“, you can add the UpdateSite of the XPages SDK to Eclipse:

Install New Software

Add Update Site

Click on „Local…“, and choose the folder „org.openntf.xsp.sdk.updatesite“ from the unpacked archive. Then give it a name for identification. You can choose whatever you want.

Choose the XPages SDK UpdateSite

Now, choose the plugin, click „OK“, accept the license, allow the installation from unsigned content and restart Eclipse after installation is complete.

Install the Plugin

Next step ist to configure the JRE: Go to „Help > Preferences“, „XPages SDK“, and choose the pathes were your Domino Server is installed. Also choose „Automatically create JRE for Domino“:

Then apply it.

If you have FP8 installed, you don’t need the next step: Change the Complier compliance level to 1.6. Again, apply the setting and allow a full rebuild of the Workspace.

Change the compliance level

Switch the default JRE to the newly created XPages Domino JRE:

Now you have to add the target platform: Enter „target“ in the search field, select the „Target Platform“, and click on „Add“. Choose the „Domino Install Target“, create it and choose it as active platform.

Target Definition: „Domino Install Target“

OK, that’s it. You have a working IDE and you are ready to import the starter project from GitHub.

Domino & REST: Accessing Domino’s Environment / Check Authentication

2. März 2017 Posted by Sven Hasselbach

If we want to access Domino’s Environment, it is the ContextInfo class which gives us all we need. Everything you need to do to use the class is described in an earlier blog post.

The class gives mainly access to the following methods:

Method Description
getDataDirectory() Path to notes data directory
getEnvironmentString(String envName) Returns the environment variable
getServerDatabase() The actual database as NAPI object, if any
getServerVariable(String varName) Variables from the Request, i.e. „QUERY_STRING“
getUserDatabase() The actual database as Domino Java object, if any
getUserSession() The session of the actual user performing the request (
isAnonymous() true if the current user is Anonymous

Keep in mind that the incoming request is independently of any underlying Notes database. In our example, the URI http://your.server/dominorestservlet/helloworld/ does not run inside of a NSF, that’s why the getServerDatabase() and the getUserDatabase() methods returns null.

As a consequence, our servlet does not have any access restriction and is reachable as anonymous. If you want to access a database programatically, the „normal“ Domino access control is intervening again, but we can do the access check by ourself.

To prevent the access to the servlet, I have added a „checkAuthentication“ method to the RestApiServlet class. This method checks if the current user is a) not anonymous and b) a member of the group RESTAPIAccessAllowed. The method throws a NotAuthenticatedException which let’s the servlet return a HTTP 403.

Domino & REST: Consuming JSON

1. März 2017 Posted by Sven Hasselbach

Consuming JSON is as easy as pie: Just create a new method to the RestApiServlet,  add a @POST annotation, and declare the object you want to the parameters of the method:

    @POST
    @Path("/helloworld/")
    @Consumes(MediaType.APPLICATION_JSON)
    public Response postHelloWorld(HelloWorld helloWorld) {
        return Response.ok(helloWorld, MediaType.APPLICATION_JSON).build();
    }

In this example we are using the same URI „/helloworld/„; because we now are using another HTTP method, there is no conflict between the two methods.

Have a look at the parameter of the postHelloWorld method: It is the HelloWorld class which now instantiated and filled automatically from the POSTed JSON data! We can access the object in Java, and in this short example the result is directly returned back in the response.

If we now send a JSON request with a „message“ field…

curl -i -X POST -d "{\"message\": \"foo\"}" \ 
    -H "Content-Type: application/json" \
    http://your.server/dominorestservlet/helloworld/

… the response contains the message we have posted:

XPages: Running Google’s Chrome V8 Javascript Engine (2)

10. April 2015 Posted by Sven Hasselbach

A while ago I tried to run Google’s V8 Javascript engine on top of XPages, and today I found the reason why my server crashed after the first click: I have tried to load the engine only once (statically), and that killed Domino completly.

Today I moved the code back into the processAction method, and now it works without problems.

package ch.hasselba.xpages.jav8;

import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class JAV8Test implements javax.faces.event.ActionListener {

    public void processAction(ActionEvent actionEvent)
            throws AbortProcessingException {

        ScriptEngineManager factory = new ScriptEngineManager();
        ScriptEngine engine = factory.getEngineByName("jav8");

        try {
            System.out.println(engine.getClass().getCanonicalName());
            engine.eval("var i=1+1;");
            System.out.println("i = " + engine.get("i"));
        } catch (ScriptException ex) {
            ex.printStackTrace();
        }
    }
}

XPages: Running Google’s Chrome V8 Javascript Engine

9. November 2014 Posted by Sven Hasselbach

After answering a question on Stackoverflow.com about the Prototype problematic in the XPages SSJS engine, I thought of running another Javascript engine on top of Domino.

While you can use the JavaScripting API JSR223, I choosed the jav8 project for a test how this can be realized. So I downloaded the Windows binaries to get the required DLL and imported it into a new database. I also imported the source files of the lu.fler.script package to recompile all required classes.

Then, I registered the factory service by creating a javax.script.ScriptEngineFactory file in the /META-INF/services folder and added the line lu.flier.script.V8ScriptEngineFactory.

The package explorer looked like this:

To prevent collisions, I commented out some names in the V8ScriptEngineFactory class:

For a simple test, I decided to invoke the engine manually when clicking on a button on a XPage. To do this, I created a simple ActionListener in Java which loads the JavaScript Engine and evals a simple ” var i = 1+1″. The Javascript variable is then accessed and printed out to the server console.

package ch.hasselba.xpages.jav8;

import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class JAV8Test implements javax.faces.event.ActionListener {

    static ScriptEngineManager factory = new ScriptEngineManager();
    static ScriptEngine engine = factory.getEngineByName("jav8");

    public void processAction(ActionEvent actionEvent)
            throws AbortProcessingException {
        try {
            System.out.println( engine.getClass().getCanonicalName() );
            engine.eval("var i=1+1;");
            System.out.println( "i = " + engine.get("i") );
        } catch (ScriptException ex) {
            ex.printStackTrace();
        }
    }
}

The XPage to test the ActionListener is rather simple too:

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

    <xp:button
        value="Click Me!"
        id="buttonClickMe">
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="complete">
            <xp:this.actionListeners>
                <xp:actionListener type="ch.hasselba.xpages.jav8.JAV8Test" />
            </xp:this.actionListeners>
        </xp:eventHandler>
    </xp:button>
    
</xp:view>

When the button is clicked, the V8 engine works as it should:

But now comes a hard problems: It works only once! After doing this, my test server crashes completly. During playing with it, I was able to run it as it should, but I have no idea anymore how I did it. I think it is the DLL and static classes, but I am currently to busy to investigate the problem further. The @Override notations added to the methods (which must removed before the code can be compiled) do not override exitisting ones (I checked the bundled javax.script JAR of the binary package), this does not seem to be the problem. Maybe someone else has an idea?

XPages: Execute Events with HTTP Get

30. September 2014 Posted by Sven Hasselbach

To execute an event on the server, you normally have to send a POST request, because actions will be executed in the Invoke Application phase of the JSF lifecycle. A GET request will only process the Restore View and the Render Response phase, that why you can not execute an event with a GET request.

But with the help of a PhaseListener, the execution can be done earlier in the Restore View phase:

package ch.hasselba.xpages.util;

import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import com.ibm.xsp.component.xp.XspEventHandler;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import com.ibm.xsp.util.FacesUtil;

public class ExecuteOnServerPhaseListener implements PhaseListener {

    private static final long serialVersionUID = 1L;

    public void beforePhase(PhaseEvent event) {}

    public PhaseId getPhaseId() {
        return PhaseId.RESTORE_VIEW;
    }

    public void afterPhase(PhaseEvent event) {
        
        FacesContextExImpl = FacesContextExImpl.getCurrentInstance();
        ExternalContext ec = fc.getExternalContext();
        String url = ec.getRequestPathInfo();
        String[] pathes = url.split( "/" );
        
        try{
            if( pathes.length > 2 ){
                if( "executeOnServer".equals( pathes[pathes.length -2 ] ) ){
                    String[] fullId = pathes[ pathes.length - 1 ].split(":");
                    String actionId = fullId[ fullId.length - 1 ];
                    XspEventHandler eventHandler = (XspEventHandler)
                        FacesUtil.findComponentWithFullId( fc.getViewRoot(), actionId );
                    if( eventHandler != null ){
                        eventHandler.getAction().invoke( fc, null );
                        fc.responseComplete();
                    }
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        
    }

}

To activate the PhaseListener, it has to be enabled in the faces-config.xml:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
   <lifecycle>
      <phase-listener>ch.hasselba.xpages.util.ExecuteOnServerPhaseListener</phase-listener>
   </lifecycle>
</faces-config>

The following Javascript snippet extends the XSP object and adds the new function executeOnServerGet to it. The parameter is the if of the event to invoke.

XSP.executeOnServerGet = function( eventId ){
    var viewId = dojo.query('[name="$$viewid"]')[0].value;
    var url = document.forms[0].action;
    url += "/executeOnServer/" + eventId;
    url += "?$$viewid=" + viewId;
    url += "&$$ajaxid=@none";
    dojo.xhrGet({url: url});
}

When calling the function, it sends a GET request and adds the current view id to the request. With the parameter $$ajaxId set to @none, the XPages Engine is forced to send no HTML code back to the client.

And here is an example XPage:

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

    <xp:eventHandler id="helloworld" event="helloworld" submit="false">
        <xp:this.action>
            <![CDATA[#{javascript:
               print("hello world " + java.lang.System.currentTimeMillis() );
            }]]>
        </xp:this.action>
    </xp:eventHandler>


    <xp:scriptBlock id="scriptBlock1">
        <xp:this.value><![CDATA[
            dojo.addOnLoad( function(){
                XSP.executeOnServerGet = function( eventId ){
                      var viewId = dojo.query('[name="$$viewid"]')[0].value;
                    var url = document.forms[0].action;
                    url += "/executeOnServer/" + eventId;
                    url += "?$$viewid=" + viewId;
                    url += "&$$ajaxid=@none";
                    dojo.xhrGet({url: url});
                  }
            });
        ]]></xp:this.value>
    </xp:scriptBlock>
    
    <xp:button value="Execute" id="button1">
        <xp:eventHandler event="onclick" submit="false">
            <xp:this.script>
                <![CDATA[XSP.executeOnServerGet( "#{id:helloworld}" );]]>
            </xp:this.script>
        </xp:eventHandler>
    </xp:button>
</xp:view>

When clicking the button, the following URL is opened in the background:

http://example.com/db.nsf/EventGet.xsp/executeOnServer/view:_id1:helloworld?$$viewid=!dwjldz64w0!&$$ajaxid=@none

A GET request was sent to the server:If you look on the server console, you will see that the Action was invoked:

XPages: File downloads and blocked UI

24. Juli 2013 Posted by Sven Hasselbach

Yesterday Christian asked a very interesting question: He had observed that the browser UI is blocked when clicking a button which generates a PDF on the server and sends the result. For about 30 seconds no button is working, no events are fired. My first thought was that this behaviour is caused by the submit locking during partial refreshs, and after testing a XSP.allowSubmit() in the debug console I could prove myself that I was right.

It looked first like an easy solution, but after thinking a little bit about it, I had no idea how to execute this little piece of CSJS code. Let’s have a look at the PDF generation:

public void exportPdfOutputStream(TemplateData pdfData, String templateFile, String filename) {

   ExternalContext con = facescontext.getExternalContext();
   XspHttpServletResponse response = (XspHttpServletResponse) con.getResponse();
   try {
     ServletOutputStream writer = response.getOutputStream();
     // setting response headers for browser
     response.setContentType(“application/pdf”);
     response.setHeader(“Cache-Control”, “no-cache”);
     response.setDateHeader(“Expires”, -1);
     response.setHeader(“Content-Disposition”, “attachment; filename=\”" + filename + “\”");

     PdfReader pdfTemplate = getPdfReader(templateFile);
     PdfStamper stamper = new PdfStamper(pdfTemplate, writer);
     stamper.setFormFlattening(true);
     setDataToPdfDocument(pdfData, stamper);
     stamper.close();
     pdfTemplate.close();
     writer.flush();
     writer.close();
     facescontext.responseComplete();
    } catch (Exception e) {
      String errorMessage = “PDF Exporter: An error occureed: ” + e.toString();
      try {
         response.sendError(500, errorMessage);
      } catch (Exception e2) {
        e2.printStackTrace();
      }
    }
 }

A full refresh is submitted to the server, the code generates a PDF, adds the result to the output stream as attachment, and then closes the response. The browser receives a file to download and that’s it. Works fine. But how to execute some CSJS code? You cannot use view.postScript nor you cannot send an additional HTML element (a JS script block) and there is no onComplete event.

While I was musing I had the idea to use a repeating interval and check the server response in Javascript. But how can you access this information? When performing a XHR Call (a partial refresh) you can access the HTTP header of the response, but this is not possible when performing a full refresh.

I googled for an answer an found this article about a solution to block the UI: http://geekswithblogs.net/GruffCode/archive/2010/10/28/detecting-the-file-download-dialog-in-the-browser.aspx

It uses a cookie to send the information back to the browser, which is a really amazing idea. Here is my implementation for XPages and Dojo 1.8:

The button to download the PDF first executes a CSJS script which fills in the field fileDownloadToken with the current timestamp. This timestamp is sent to the server and then added to the cookie in the response.

The client checks every 500ms if the cookie exists and if the value equals the original token sent to the server. If the cookie is set correctly, the finishDownload() method is executed which calls XSP.allowSubmit() and removes the cookie.

This is the code of the XPage:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view
        xmlns:xp="http://www.ibm.com/xsp/core">
        <xp:button
                id="buttonDownload"
                value="Download">
                <xp:eventHandler
                        event="onclick"
                        submit="true">

                        <xp:this.action>
                                <![CDATA[#{javascript:
                                  importPackage( ch.hasselba.tools );        
                                  new ch.hasselba.tools.PDFUtil().exportPdfOutputStream("output.pdf");}]]>
                        </xp:this.action>
                        <xp:this.script><![CDATA[
                                require(["dojo/cookie"], function(cookie){
                                          var fileDownloadCheckTimer;
                                          var tokenName = 'fileDownloadToken';

                                        function finishDownload() {
                                                 window.clearInterval(fileDownloadCheckTimer);
                                                 cookie('fileDownloadToken', null, {expire: -1});
                                                 XSP.allowSubmit();                        
                                        }

                                        function startDownload(){
                                                setToken();
                                                fileDownloadCheckTimer = window.setInterval(function () {
                                                      var cookieValue = cookie( tokenName );

                                                      if ( cookieValue == getToken() )
                                                               finishDownload();
                                            }, 500);

                                        }

                                        function getToken(){
                                                return XSP.getElementById( tokenField ).value;
                                        }
                                        function setToken(){
                                                 XSP.getElementById( tokenField ).value = Date.now();
                                        }

                                        startDownload();
                                  })
                        ]]></xp:this.script>
                </xp:eventHandler>
        </xp:button>
        <input type="hidden" id="fileDownloadToken" name="fileDownloadToken" />

</xp:view>

And this is the modified Java code:

package ch.hasselba.tools;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;

import com.ibm.xsp.webapp.XspHttpServletResponse;

public class PDFUtil {
        public void exportPdfOutputStream(String filename) {

                FacesContext fc = FacesContext.getCurrentInstance();
                ExternalContext ex = fc.getExternalContext();

                XspHttpServletResponse response = (XspHttpServletResponse) ex
                                .getResponse();

                // get the token from the request
                String token = (String) ex.getRequestParameterMap().get(
                                "fileDownloadToken");

                try {
                        ServletOutputStream writer = response.getOutputStream();

                        // setting response headers for browser
                        response.setContentType("application/pdf");
                        response.setHeader("Cache-Control", "no-cache");
                        response.setDateHeader("Expires", -1);
                        response.setHeader("Content-Disposition", "attachment; filename=\""
                                        + filename + "\"");

                        // set the cookie to the response
                        response.addCookie(new Cookie("fileDownloadToken", token));

                        //
                        // generate the output
                        //

                        // close the writer and mark response as completed
                        writer.flush();
                        writer.close();
                        fc.responseComplete();

                } catch (Exception e) {
                        e.printStackTrace();
                }
        }
}

XPages: Use a PhaseListener for global URL commands

11. Juli 2013 Posted by Sven Hasselbach

One of my customers wanted an easy way for printing XPages in different formats and with different content, depending of the current XPage opened in the browser. It was a requirement to develope a global solution for every XPage-based application in his company. That is why I created a global Java class which does not require to modify the existing applications at all. But it allows to customize and configure the output for the different needs of the departments and their required reports and print outs.

I decided to use a special PhaseListener to hook into the different applications. This allows the PDF generation depending on some URL parameters, and only runs if the URI contains a special command for printing the current XPage (but works for thw whole application).

The example code is what I have created:

package ch.hasselba.xpages.util;

import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class PhaseListenerPDFGenerator implements PhaseListener {

    private static final long serialVersionUID = 1L;
    private static final String GENERATE_PDF = ".PDF";
    private static final String URL_PARAMETER_FILENAME = "filename";
    private static final String URL_PARAMETER_PDFTYPE = "type";
    private static final String HTTP_HEADER_CONTENTTYPE = "application/pdf";

    public void beforePhase(PhaseEvent event) {}

    public PhaseId getPhaseId() {
        return PhaseId.RESTORE_VIEW;
    }

    public void afterPhase(PhaseEvent event) {

        FacesContext facesContext = event.getFacesContext();

        HttpServletRequest req = (HttpServletRequest) facesContext
                .getExternalContext().getRequest();
        String uri = req.getRequestURI();

        if (uri.endsWith(GENERATE_PDF) == true ) {
            servePDF(facesContext);
        }
    }

    private void servePDF(FacesContext facesContext) {
        Map requestMap = facesContext.getExternalContext()
                .getRequestParameterMap();

        String pdfName = getPDFName(requestMap);
        String pdfType = getPDFType(requestMap);

        HttpServletResponse response = (HttpServletResponse) facesContext
                .getExternalContext().getResponse();

        try {

            response.setContentType( HTTP_HEADER_CONTENTTYPE );
            response.setHeader( "Content-Disposition", "attachment; filename="
                      + pdfName );
            response.setStatus(200);
            ServletOutputStream outputStream = response.getOutputStream();

            // Generate the PDF here
            // and send the data to the outputStream
            //
            //outputStream.write( PDFDATA );
            outputStream.flush();
            outputStream.close();
            facesContext.responseComplete();

        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }

    public static String getPDFName(Map requestMap) {
        String pdfName = (String) requestMap.get( URL_PARAMETER_FILENAME );
        return pdfName;
    }
    public static String getPDFType(Map requestMap) {
        String pdfName = (String) requestMap.get( URL_PARAMETER_PDFTYPE );
        return pdfName;
    }

}

The PhaseListener scans the URI every time a request is sent to the server. As soon the URI ends with .PDF, the parameters fileName and type are extracted. The correct PDF template is identified and the requested PDF can be generated (Code for generating is not included in this example).

After generating the PDF, the output is written, and the response is completed. The file download headers are added to the response. The user receives a file download dialog and can open the generated PDF (and print it if required).

When this PhaseListener is enabled in the faces-config.xml, every XPage can use the URL command. For example if your XPage is named “MyXPage.xsp” the URL to enable the PDF generation instead the generation of the XPage looks like this:

http://example.com/path/to/db.nsf/MyXPage.xsp/.PDF?filename=test.pdf&type=exportAll

This returns a PDF with the filename test.pdf which is generated from the PDF template exportAll, containing the data of the current XPage. It is easy to add a link like this to your application to enable the PDF generation.

This is how the faces-config.xml has to look like:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
  <lifecycle>
      <phase-listener>ch.hasselba.xpages.util.PhaseListenerPDFGenerator</phase-listener>
  </lifecycle>
</faces-config>

XPages: The Outputstream and binary data

12. September 2012 Posted by Sven Hasselbach

If you want to get control over the outputstream of a XPage, you can use the response object from the ExternalContext:

var response = facesContext.getExternalContext().getResponse()

This will give you access to an object of type com.ibm.xsp.webapp.XspHttpServletResponse which allows some basic operations, but the response will always be encoded to UTF-8. You can not return any binary data directly.

But if you access the underlying LCDAdapterHttpServletResponse directly, it is possible to get the full control for the outputstream.

var exCon = facesContext.getExternalContext();
var response = exCon.getResponse().getDelegate();

This allows you to send any data you want to the browser. Here is an example for a JPEG rendered directly in the XPage:

1. The XPage

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

   <xp:this.afterRenderResponse>
      <![CDATA[#{javascript:
         importPackage( ch.hasselba.xpages.demo );
         var exCon = facesContext.getExternalContext();
         var response = .getResponse().getDelegate();
         ch.hasselba.xpages.demo.JPEGGenerator.generate( response );
      }]]>
   </xp:this.afterRenderResponse>

</xp:view>

 

2. The Java object to generate a JPEG

package ch.hasselba.xpages.demo;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import com.sun.image.codec.jpeg.ImageFormatException;
import com.sun.image.codec.jpeg.JPEGCodec;
import java.awt.Color;
import java.awt.Font;

/**
 * JPEG Generator for a simple demonstration of controlling the
 * HttpServletResponse output
 * 
 * @author Sven Hasselbach
 * @category Demo
 * @category Servlet
 * @category Image
 * @version 1.0
 */

public class JPEGGenerator {

    private static final long serialVersionUID = 1L;
    private static final int WIDTH = 800;
    private static final int HEIGHT = 200;
    private static final Color BACKGROUND_COLOR = new Color(224, 224, 224);
    private static final Color COLOR = new Color(0, 0, 0);
    private static final Font FONT = new Font("Times New Roman", Font.BOLD, 46);

    /**
     * generates a JPEG image and sends the result to the outputstream of the
     * HttpServlet
     * 
     * @param response
     *            HttpServletResponse
     * @author Sven Hasselbach
     * @version 1.0
     */
    public static void generate(final HttpServletResponse response) {
        ServletOutputStream out = null;
        try {

            // set the content type for JPEG
            response.setContentType("image/jpg");

            // get the ouput stream
            out = response.getOutputStream();

            // generate a image to convert
            BufferedImage img = createImage();

            // convert image to jpeg and send to output
            JPEGCodec.createJPEGEncoder(out).encode(img);

        } catch (ImageFormatException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * creates a simple "Hello World" image
     * 
     * @return BufferedImage
     * @author Sven Hasselbach
     * @version 1.0
     */
    public static BufferedImage createImage() {
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT,
                BufferedImage.TYPE_BYTE_INDEXED);
        Graphics graphics = image.getGraphics();
        graphics.setColor(BACKGROUND_COLOR);
        graphics.fillRect(0, 0, image.getWidth(), image.getHeight());
        graphics.setColor(COLOR);
        graphics.setFont(FONT);
        graphics.drawString("Hello World!", 10, HEIGHT / 2);

        return image;
    }

}

There is no need for splitting this example in a separated Java class. You can directly access the output in SSJS and send your binary data directly via write method of the ServletOutputStream.

Quick-n-Dirty: Disable Domino’s Cache for easier development

8. September 2012 Posted by Sven Hasselbach

Controlling the HTTP Expires Header

23. Mai 2012 Posted by Sven Hasselbach

After reading a question on stack overflow about setting an own HTTP expires header and the problem that an additional header is generated automatically,  I made some tests how the domino server can be forced to stop this behaviour programmatically.

During my tests I was able to stop it by using  facesContext.responseComplete() but this works only for “headless” XPages (set rendered to false). If you are calling the method in a normal XPage (set rendered to true), the generated output will be discarded and looks like the “headless” version.

Here are two examples including screenshots from firebug console (containing the HTTP response):

  • Adding the header in beforeRenderResponse / beforePageLoad
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
   <xp:this.beforeRenderResponse>
   <![CDATA[#{javascript:
      var ec = facesContext.getExternalContext();
      var response = ec.getResponse();
      var writer = response.getWriter();

      // set headers
      var now = new Date();
      response.setDateHeader("Expires",
         now.getTime() + (60*60*1000));
      response.setHeader("Cache-Control", "public");

      // Output it
      writer.write( now.getTime().toString() );
      }]]>
   </xp:this.beforeRenderResponse>
   <xp:label value="Test!" id="label1"></xp:label>
 </xp:view>

As you can see here, the output from the writer was added to the XPage first (the output will be added before the <HTML>-Tag and causes invalid HTML, but this can be ignored in this demo):

The response contains two HTTP expires header. The -1 is added after the programmatically generated one.

  • Adding the header in afterRenderResponse / afterPageLoad
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
   <xp:this.afterRenderResponse>
   <![CDATA[#{javascript:
      var ec = facesContext.getExternalContext();
      var response = ec.getResponse();
      var writer = response.getWriter();

      // set headers
      var now = new Date();
      response.setDateHeader("Expires",
         now.getTime() + (60*60*1000));
      response.setHeader("Cache-Control", "public");

      // Output it
      writer.write( now.getTime().toString() );
      }]]>
   </xp:this.afterRenderResponse>
   <xp:label value="Test!" id="label1"></xp:label>
</xp:view>

In this scenario, the output is added after the HTML code.

The HTTP expires header was added first.

  • How to remove the header

After some research, I was able to remove the header programmatically:

First, you have to create a new java class which implements com.ibm.xsp.context.RequestParameters.ResponseCacheHeader.

package ch.hasselba.jsf.core;

import javax.servlet.http.HttpServletResponse;
import com.ibm.xsp.context.RequestParameters.ResponseCacheHeader;

public class SHResponseCacheHeader implements ResponseCacheHeader {

   public SHResponseCacheHeader(){}

   public boolean initResponseCacheHeader(HttpServletResponse arg0){
      return true;
   }
}

It is required that the initResponseCacheHeader() method returns true, otherwise this won’t work!

Then, you have to add the responseCacheHeader object to the requestParameters of facesContext:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" rendered="false">
   <xp:this.beforePageLoad>
   <![CDATA[#{javascript:
      importPackage( ch.hasselba.jsf.core );

      var ec = facesContext.getExternalContext();
      var response = ec.getResponse();
      var writer = response.getWriter();

      // set headers
      var now = new Date();
      response.setDateHeader("Expires",
         now.getTime() + (60*60*1000));
      response.setHeader("Cache-Control", "public");

      // set response cache header object
      var reqParam = facesContext.getRequestParameters();
      var resCH = new SHResponseCacheHeader();
      reqParam.setResponseCacheHeader( resCH );

      // Output it
      writer.write( now.getTime().toString() );
      }]]>
   </xp:this.beforePageLoad>
   <xp:label value="Test!" id="label1"></xp:label>
 </xp:view>

The output will be generated as expected, but the useless HTTP expires header won’t be added anymore:

XSnippets: Cancel a partial refresh via SSJS

3. Mai 2012 Posted by Sven Hasselbach

With the assistance of Philippe Riand I was able to shorten the original idea of canceling a partial refresh to a single SSJS function.  By setting the HTTP header “X-XspRefreshId” to “@none” it is possible to get the same result as in the posting before, but there is no “Dojo hack” required.

function cancelPartialRefresh(){
   var response = facesContext.getExternalContext()
      .getResponse();
   response.setHeader("X-XspRefreshId", "@none");
   response.reset();
   response.commitResponse();
   facesContext.responseComplete();
}

To use this function you just have to call it.  Here is the same example like in the previous posting:

<?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="partial" refreshId="label1">
      </xp:eventHandler>
   </xp:button>
   <xp:br/><xp:br/>
   <xp:label id="label1">
      <xp:this.value>
      <![CDATA[#{javascript:
      function cancelPartialRefresh(){
         var response = facesContext.getExternalContext()
            .getResponse();
         response.setHeader("X-XspRefreshId", "@none");
         response.reset();
         response.commitResponse();
         facesContext.responseComplete();
      }

      var ajax = new com.ibm.xsp.ajax.AjaxUtil();
      if( ajax.isAjaxPartialRefresh(facesContext) == true){
         cancelPartialRefresh();
         return; // stop execution of SSJS code
      }
      java.lang.System.currentTimeMillis();}]]>
      </xp:this.value>
   </xp:label>
</xp:view>

I have added the code to the XSnippet Contest. Here is the link to the XSnippet: http://openntf.org/XSnippets.nsf/snippet.xsp?id=cancel-partial-refresh

If you want to read more information about the HTTP header  you can read an earlier posting (Sorry, on german only).

Cancel a partial refresh via SSJS

1. Mai 2012 Posted by Sven Hasselbach

After reading Tim Tripcony’s blog post , I thought about a way how to cancel a partial refresh via server side javascript. To bring this to life, there are just three things to do:

  1. Abort the processing of the request on the server
  2. Give feedback to the client that request is canceled
  3. Stop Dojo to process the XHR request

To stop the processing of a request on the server and to send an empty response to the client, this SSJS code can be used:

var response = facesContext.getExternalContext().getResponse();
response.reset();
response.commitResponse();

The client will receive an empty HTTP response body:

A HTTP Header has to be added to the response to inform Dojo that the request was canceled. In this example it is “X-Partial-Refresh-Cancel“:

response.setHeader("X-Partial-Refresh-Cancel", "1");

The header is now added to the HTTP response and sent back to the client:

The XHR response must be hijacked before it is processed by Dojo. To do this, a new XHR function has to be injected between the existing one. Here is a basic code snippet:

var xhrLoad = null; // placeholder for the original function

if( !dojo._xhr )
   dojo._xhr = dojo.xhr;

dojo.xhr = function(){
   var args = arguments[1];
   xhrLoad = args["load"]; // "save" the original load function
                           // and overwrite with a new one
   args["load"] = function( a, ioArgs ){
      // execute custom code
      // call the original load function:
      xhrLoad( a, ioArgs );
   }

   // do XHR request
   dojo._xhr( arguments[0], arguments[1], arguments[2] );
}

The load function of a Dojo XHR request has a parameter ioArgs. This parameter gives a handle to an object that allows to access the HTTP response headers, so the response can be identified as canceled:

var canceled = ioArgs.xhr &&
   ioArgs.xhr.getResponseHeader("X-Partial-Refresh-Cancel");

if( canceled ){
   XSP.allowSubmit();
   return;
}

If the HTTP header is set, the processing of the request can be stopped. After stopping the request, the XSP object is not allowed to fire the next event. To allow this again, the function XSP.allowSubmit() must be called.

Here is a demonstration XPage with a button and a label. The partial refresh will never be processed, instead a “Canceled” message will occur.

<?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="partial" refreshId="label1">
      </xp:eventHandler>
   </xp:button>
   <xp:br/><xp:br/>
   <xp:label id="label1">
      <xp:this.value>
      <![CDATA[#{javascript:
      var response = facesContext.getExternalContext().getResponse();
      var ajax = new com.ibm.xsp.ajax.AjaxUtil();
      if( ajax.isAjaxPartialRefresh(facesContext) == true){
         response.setHeader("X-Partial-Refresh-Cancel", "1");
         response.reset();
         response.commitResponse();
         return;
      }
      java.lang.System.currentTimeMillis();}]]>
      </xp:this.value>
   </xp:label>
   <xp:br/>
   <xp:br/>
   <xp:scriptBlock id="scriptBlockXHRHandler">
   <xp:this.value><![CDATA[
      var xhrLoad = null;
      dojo.addOnLoad( function(){
         if( !dojo._xhr )
            dojo._xhr = dojo.xhr;

        dojo.xhr = function(){
           try{
              var args = arguments[1];
              xhrLoad = args["load"];
              args["load"] = function( a, ioArgs ){
                 var canceled = ioArgs.xhr &&
                    ioArgs.xhr.getResponseHeader("X-Partial-Refresh-Cancel");
                 if( canceled ){
                    alert("Canceled!");
                    XSP.allowSubmit();
                    return;
                 }
                 xhrLoad( a, ioArgs );
               }
            }catch(e){}
            dojo._xhr( arguments[0], arguments[1], arguments[2] );
         }
      });]]>
      </xp:this.value>
   </xp:scriptBlock>
</xp:view>

Security: Fernsteuerbare DocumentDataSources

12. Januar 2012 Posted by Sven Hasselbach

Analog zu den ViewDataSources lassen sich auch DocumentDataSources über die Adresszeile des Browsers manipulieren: Der URL-Parameter databaseName ermöglicht hierbei die Steuerung der zugrundliegenden Datenbank, und überschreibt einen eventuell in der XPage hinterlegten Wert.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
   <xp:this.data>
      <xp:dominoDocument
         var="document1"
         databaseName="DB1.nsf">
      </xp:dominoDocument>
   </xp:this.data>
     <xp:button value="Label" id="button1">
      <xp:eventHandler event="onclick"
         submit="true" refreshMode="complete">
         <xp:this.action>
            <xp:save></xp:save>
         </xp:this.action>
      </xp:eventHandler>
   </xp:button>
</xp:view>

Wird diese XPage mit dem Parameter databaseName=DB2.nsf aufgerufen und abgeschickt bzw. gespeichert, erfolgt das Anlegen des Dokumentes nicht wie definiert in der DB1.nsf, sondern in DB2.nsf.

Ebenso verhält es sich, wenn ein Dokument geöffnet wird: Auch hierbei lässt sich steuern, aus welcher Datenbank das Dokument stammten soll (in diesem Fall nur, solange keine documentId  in der XPage eingestellt ist.)

Werden mehrere DataSources auf einer XPage verwendet, werden die URL-Parameter dort angewendet, “wo es passt”, d.h. es wird die nächste DataSource (View oder Document) verwendet, die nicht mit ignoreRequestParam=”true” abgesichert wurde.

Security: Fernsteuerbare ViewDataSources

30. Dezember 2011 Posted by Sven Hasselbach

Ist bei einer Datenbank die Option “Don’t allow URL open” gesetzt, ist sie nicht mehr im Web erreichbar. Mit den URL-Parametern databaseName und viewName lässt sich in Verbindung mit einer ViewDatasource dieser Schutzmechanismus jedoch aushebeln, und könnte ein Problem darstellen, wenn sich nur auf diese Option verlassen wurde.

Als Beispiel dient hier eine lokales NAB, dass zu Demonstrationszwecken eine offene ACL hat (Default = Manager), bei dem die genannte Option jedoch aktiviert ist:

Öffnet man diese Datenbank im Browser, so erscheint eine Fehlermeldung:

Legt man jedoch eine XPages mit einer ViewDatasource in einer anderen Datenbank an, kann dieser einfache Schutzmechanismus umgangen werden, da die beiden Paramter die in der XPage definierte Quelle überschreiben; dadurch wird eine “Fernsteuerung” der ViewDataSource ermöglicht.

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

   <xp:viewPanel rows="30" var="viewCol">
      <xp:this.data>
         <xp:dominoView var="view1" viewName="" />
      </xp:this.data>
      <xp:viewColumn
         value="#{javascript:viewCol.getUniversalID()}">
     </xp:viewColumn>
   </xp:viewPanel>
   
</xp:view>

Öffnet man folgende XPage, ist die darzustellende Ansicht natürlich leer…

… hängt man allerdings die passenden URL-Parameter an, sieht die gleiche XPage entsprechend anders aus:

Es handelt sich dabei um die beiden Dokumente aus dem persönlichen Addressbuch.

Zwar müssen immer noch Berechtigungen auf die jeweilige Datenbank gegeben sein, damit Daten ausgelesen werden können, dennoch sollte prinzipiell immer ignoreRequestParams=”true” gesetzt sein.