wissel.net

Usability - Productivity - Business - The web - Singapore & Twins

Make Java code for XAgents easy to test


One of the popular entries in this blog is my introduction into XAgents together with related considerations and practial applications. There are a few patterns, that make XAgents easier to develop and debug.
Despite outstanding tool contribution debugging in XPages is still wanting, so any code that you can incorporate pre-debugged is a bonus. Here is what I do:
  • I try to write my logic in Java. I use the SSJS only to fetch the values I need to call my Java function
  • The functions of the Java class take always the OutputStream (or the ResponseWriter) as a parameter. This is where the output goes
  • For rendering XML or HTML output I use SAX with a little helper (Making one of these for JSON would be an interesting exercise)
  • I try to avoid using XPages specific (e.g. variable resolver) in my class containing the logic, but rather rely on Dependency Injection to ease testing
  • I debug and test the Java parts outside of XPages
A little sample. Imagine I have a class that renders a Notes document into a PDF (with a fixed style). In my XPages code (beforeRenderResponse) I would write:
// The external context and the response object
var exCon = facesContext. getExternalContext ( ) ;
var response = exCon. getResponse ( ) ;

// Deliver uncached PDF
response. setContentType ( "application/pdf" ) ;
response. setHeader ( "Cache-Control" , "no-cache" ) ;
// Force file name and save dialog
response. setHeader ( "Content-Disposition" , "attachment; filename=invoice.pdf" ) ;

// The Output Stream for Binary Output
var out =response. getOutputStream ( ) ;
var pdfProcessor = new com. notessensei. tools. PDFFormatter ( ) ;

// Here the call where "document" is a Notes document
pdfProcessor. renderInvoice (out , document ) ;

// Done
facesContext. responseComplete ( ) ;
out. close ( ) ;
Not really rocket science. The beauty of this approach: I can have a standalone Java application to test my PDFFormatter class. I use Eclipse for that. In Eclipse I have configured the Notes client's JVM as the runtime for the project. This way all Notes Java classes are found. Most likely you need the Notes program directory on your system path (but you know that). A simple testing class can look like this:
public class Tester {

    public static String theURL = "Notes:///8525744100531C7D/F4B82FBB75E942A6852566AC0037F284/034DAEE1CEEE2FB58525744000719185" ;

    public static void main ( String [ ] args ) throws Exception {
        NotesThread. sinitThread ( ) ;
        Session s = NotesFactory. createSession ( ) ;
        Document document = ( Document )session. resolve (theURL ) ;
        PDFFormatter pdfProcessor = new com. notessensei. tools. PDFFormatter ( ) ;
        FileOutputStream out = new FileOutputStream ( new File ( "invoice.pdf" ) ) ;
        pdfProcessor. renderInvoice (out, document ) ;
        out. close ( ) ;
        NotesThread. stermThread ( ) ;
    }
}
Of course that approach works too when you plan to use JUnit or a similar framework. Finally all can be glued together with ANT for a full automated environment, but that's a story for another time.
As usual: YMMV

Posted by on 21 March 2012 | Comments (6) | categories: XPages

Comments

  1. posted by Stephan H. Wissel on Wednesday 21 March 2012 AD:
    Of course this works for any Java class including beans you want to use in your XPages application. When the Java code doesn't use any Notes objects you not even need the Notes JVM or the NotesThread.
    Emoticon biggrin.gif stw
  2. posted by Niklas Heidloff on Thursday 22 March 2012 AD:
    Why do you debug the Java code in a standalone Eclipse IDE? I debug directly from Designer's Java perspective.

    { Link }
  3. posted by sambhavi gargi on Thursday 22 March 2012 AD:
    @2 this is why I doesn't like Java.. why is it there is always many ways to do same thing?

    lotus is nice.
  4. posted by Bill Fox on Tuesday 14 July 2015 AD:
    I'm just a starter in the JAVA space so please bear with me. I can see the SSJS code and have that setup and once I get the JAVA side started I think I can get from there. Not sure how to define the renderIncoice in the JAVA Class. I have this:
    package ca.wfsystems.core;

    import com.itextpdf.*;
    import java.io.Serializable;
    import java.util.*;


    public class renderInvoice implements Serializable, ??????? {

    private static final long serialVersionUID = 1L;
    }

    Not sure how to define the passed parameters to the renderInvoice class

    Think I understand the rest of the process or at least should be able to figure it out and would just add the itextpdf to the passed stream.
  5. posted by Stephan H. Wissel on Tuesday 14 July 2015 AD:
    Bill,<br /><br />renderInvoice in my example isn't a Java class, but a method in the PDFFormatter. So you simply have method arguments.<br /><br />For the sake of your example, you would do:<br /><br />public class RenderInvoice implements Serializable {<br /><br />private static final long serialVersionUID = 1L;<br /><br /> public RenderInvoice(OutputStream out, Document doc) {<br /> }<br />}<br /><br />Then you provide the parameters on class construction (which isn't best practise, since you couldn't use the class as managed bean anymore).<br /><br />To get to Java basics, you might want to pick up a copy of O'Reilly's "Head First Java"
  6. posted by Bill Fox on Tuesday 14 July 2015 AD:
    I missed the PDFFormatter class and now I see how it fits together with renderInvoice(OutputStream out, Document doc).
    Think I can get it from there.
    Thanks
    Emoticon smile.gif