Entwicklercamp 2017: Meine Vorträge & Hands-On

19. Januar 2017 Posted by Sven Hasselbach

Dieses Jahr spreche ich wieder auf dem Entwicklercamp, und halte neben den Vorträgen auch noch eine Hands-On Session:

Im Laufe des Jahres 2016 kamen einige Themen hinzu, und dank FP8 wird es wohl auch einige Neuerungen geben, über die man mal reden müsste…

Hochperformante REST Schnittstellen entwickeln, die auf dem Domino Server laufen? Der Vortrag gibt Antworten auf die essentiellen Fragen des „Warum“ und des „Wieso“, und beleuchtet an praktischen Beispielen vor allem ausführlich die Frage des „Wie“.

Die praktische Umsetzung des Vortrages. Learning by doing.

Zur Anmeldung geht es hier entlang. Bis morgen (20.01.2017) gibt es auch noch den Frühbucherrabatt.

SNoUG 2017: Hochperformante REST Schnittstellen für Domino

17. Januar 2017 Posted by Sven Hasselbach

Dieses Jahr spreche ich zum ersten Mal auf der SNoUG am 22. März diesen Jahres.

Wie der Name des Vortrages vermuten lässt, geht es um die Entwicklung hochperformanter REST Schnittstellen auf dem Domino Server, und der gesammelten Erfahrungen der letzten Jahre.

Es ehrt mich, auf diese kleine, aber feine Konferenz eingeladen worden zu sein, und ich freue mich auf weitere interessante Vorträge hochkarätiger Sprecher.

Zur Registrierung geht es hier entlang.

DomiNodeJS: Node.js on Domino

26. August 2016 Posted by Sven Hasselbach

A while ago I started a new project which allows a seamless integration of Node.js into the Domino server.

The project is hosted on GitHub: https://github.com/hasselbach/dominodejs

The Node.js server is running as a DOTS task, and under the hood the J2V8 project is used for the V8 integration.

Currently, the project is a pre alpha, and it it is more for fun than for profit. There are a lot of open questions about the architecture, but it shows what cool things can be done with Domino.

Aus aktuellem Anlass: Quo vadis, Domino?

25. August 2016 Posted by Sven Hasselbach

Schon mal darüber nachgedacht, was nach 2021 kommt? Migrationen dauern lange, sind niemals vollständig, und irgend ein wichtiges Detail wird übersehen. Und in der Zwischenzeit?

Gerne biete ich meine Unterstützung an: Bei der Suche nach Alternativen für IBM Notes / Domino, bei Strategien für den Übergang, oder bei der Kopplung an neue Systeme.

Einfach mal Kontakt aufnehmen. Gern auch per Mail: contact <at> hasselba.ch

Java NAPI: Enable / Disable Recycling of C Handles

6. Juli 2016 Posted by Sven Hasselbach

Initialize Java NAPI

com.ibm.domino.napi.c.C.initLibrary( "" );

Disable Recycling

com.ibm.domino.napi.c.BackendBridge.setNoRecycle( session, session, true );

Enable Recycling

com.ibm.domino.napi.c.BackendBridge.setNoRecycle( session, session, false );

To use these methods in an agent, you have to import some Jars first.

XPages & Domino JNA

24. Juni 2016 Posted by Sven Hasselbach

Karsten Lehmann has published a very promising project named „Domino JNA„, which allows access to the underlying IBM Domino/Notes C API from Java.

If you want to use the project in a XPages, you have to add some Java permissions to the java.pol file on your server:

grant {
    permission java.util.PropertyPermission "jnidispatch.path", "write";
    permission java.util.PropertyPermission "jna.loaded", "write";
}

Additionally, you have to import the following JARS from „domino-jnatargetlib“ (after a successfull Maven build) to a directory on the server (jvm/lib/ext) or import it into an NSF:

  • commons-collections4-4.0.jar
  • jna-4.2.2.jar
  • joda-time-2.9.2.jar

Vaadin In XPages: A UIComponent for embedding Vaadin applications

3. Mai 2016 Posted by Sven Hasselbach

I have created an UIComponent to embed Vaadin applications into XPages. It allows to run both technologies on the same site by adding it to your XPage:

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

    <!-- XPages Application -->
    <xp:form>
        <xp:div id="refreshMe">
            <h1>XPage</h1>
            <xp:label
                value="#{javascript:java.lang.System.nanoTime()}"
                id="label1">
            </xp:label>
        </xp:div>
        <xp:button
            value="refresh"
            id="button1">
            <xp:eventHandler
                event="onclick"
                submit="true"
                refreshMode="partial"
                refreshId="refreshMe">
            </xp:eventHandler>
        </xp:button>
    </xp:form>
    
    <!--  VAADIN Application -->
    <vaadin:Vaadin
        id="Vaadin1"
        url = "/vaadin/"
        divId="addressbook"
        version="7.3.8"
        forceResize="true" />
        
</xp:view>

This little example shows the XPage above including the the Adressbook application:

2016-05-03 12_31_17-VaadinInXPages

The Vaadin application is reachable at „http://localhost/vaadin/“ (hosted by Domino), that’s why the property url is set to „/vaadin/„. The property divId contains the name of the application which is used as id in the DOM tree.

It is required that the VaadinComponent is not inside a xp:form element. If it detects a surrounding form, a runtime exception is thrown. That’s why you have to set createForm to false and add your own xp:form to your application, as shown in the example.

2016-05-03 12_50_11-Runtime Error

If the parameter forceResize is set to true, a CSS style sheet is added to the XPage which resizes the height of html and the body node to 100%. Otherwise the Vaadin application will not be displayed correctly.

2016-05-03 12_54_35-view-source_localhost_VaadinInXPages.nsf_index.xsp

Resource Aggregation must be disabled, otherwise it won’t work.

The sources can be found at https://github.com/hasselbach/VaadinInXPages

EntwicklerCamp 2016: XPages erweitern und ausbauen

15. April 2016 Posted by Sven Hasselbach

Die PDF Version meiner Entwickler Camp 2016 Session „XPages erweitern und ausbauen“ gibt es hier: ec2016-Votrag-XPagesErweiternUndAusbauen.pdf

Die verwendeten Sources finden sich in diesem Repository auf GitHub: ec2016-xpages-erweitern

Bei Fragen/Problemen einfach eine kurze Info an mich.

EntwicklerCamp 2016: Ein Leben ohne Notes Client

14. April 2016 Posted by Sven Hasselbach

Hier gibt es die PowerPoint Präsentation von meiner Entwickler Camp 2016 Session „Ein Leben ohne Notes Client“: ec2016-Votrag-LebenOhneNotesClient.pptx

Die kompletten Sources finden sich in diesem Repository auf GitHub: ec2016-ein-leben-ohne-notesclient

Bei Fragen/Problemen einfach eine kurze Info an mich.

Kurze Werbeunterbrechung

8. März 2016 Posted by Sven Hasselbach

Probleme bei der XPages-Entwicklung? Schulung gefällig?
Fehlt die eine oder andere Komponente? Oder wird ein neuer Renderer benötigt, der zur Abwechslung mal den HTML Code generiert, den man gerne hätte?

Ich biete gerne meine Unterstützung an.
Einfach mal Kontakt aufnehmen. Gern auch per Mail: contact <at> hasselba.ch

How To Crash a Domino Server in 500ms

21. Februar 2016 Posted by Sven Hasselbach

How To Crash a Domino Server in 500ms

1. Create a Java agent and do something in your finally routine (or in a ThreadDeath exception handling) which runs longer than 500ms

import lotus.domino.AgentBase;

public class JavaAgent extends AgentBase {

    public void NotesMain() {
        try {
            int i = 0;
            while (i < 1000) {
                i++;
                System.out.println("Round " + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {}
            }
        } finally {
            try {
                Thread.sleep(1000);
            } catch (Exception e) {}
        }
    }
}

2. Run the agent on the server

2016-02-20 11_31_17-SH Domain - Dev01_Hasselba_CH - IBM Domino Administrator1

3. Quit the Agent Manager

2016-02-20 11_31_17-SH Domain - Dev01_Hasselba_CH - IBM Domino Administrator2

4. And then restart it

2016-02-20 11_31_17-SH Domain - Dev01_Hasselba_CH - IBM Domino Administrator3

5. Enjoy the silence

2016-02-20 11_31_17-SH Domain - Dev01_Hasselba_CH - IBM Domino Administrator4

Fun fact: This works well in a IBM Notes Client too! Just start the agent and cancel it a few times (<CTRL> + <BREAK>).

Vaadin IBM Challenge

14. Dezember 2015 Posted by Sven Hasselbach

Since two weeks the Vaadin IBM challenge is over. While I was only able to do the bare minimum, I really enjoyed the different tasks (see here for list of steps).

Congrats to the winners! And thanks to the Vaading Team & IBM for this exciting experience.

Things I never blogged about: XPages & Google’s EventBus

1. Dezember 2015 Posted by Sven Hasselbach

This is another topic I wanted to blog about for a long time: The use of Google’s EventBus in XPages applications. EventBus is a replacement for the Java in-process event distribution. It makes life a lot easier.

My first plan was to add the guava.jar into an OSGi plugin because of security reasons (otherwise you have to use the „grant{ permission java.security.AllPermission;}„) but I never had the time. That’s why I created a simple example which uses the jar in the lib/ext folder instead.

To use the EventBus in an application, you first have to define an event. This simple example has a message only, but you can add any properties you want.

package ch.hasselba.xpages;

public class FrontendEvent {

    private final String message;

    public FrontendEvent(final String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

}

Then we need a subscriber. For this we have to add the annotation @Subscribe to a method of a managed bean which should handle an event. The class type of the parameter tells the EventBus what kind of event our method should handle.

This is a managed bean which stores the last message sent over the EventBus:

package ch.hasselba.xpages;

import com.google.common.eventbus.Subscribe;

public class EventSubscriber {

    private String message;

    @Subscribe
    public void handleEvent(FrontendEvent event) {
        this.message = event.getMessage();
    }

    public void getMessage(String msg) {
        this.message = msg;
    }

    public String getMessage() {
        return this.message;
    }
}

Then we have to create a managed bean which has an instance of EventBus, the FrontendEventBus. The bus has a managed property subscribers. As soon the bean is instantiated, the managed property is called and the list of objects is registered to the EventBus instance.

package ch.hasselba.xpages;

import java.io.Serializable;
import java.util.ArrayList;

import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.SubscriberExceptionContext;
import com.google.common.eventbus.SubscriberExceptionHandler;

@SuppressWarnings("serial")
public class FrontendEventBus implements SubscriberExceptionHandler,
        Serializable {

    final EventBus eventBus = new EventBus();
    ArrayList<Object> subscribers;

    /**
     * registers all subscribing managed beans from faces-config.xml
     * 
     * @param subscribers
     *            ArrayList with references to managed beans
     */
    public void setSubscribers(ArrayList<Object> subscribers) {
        this.subscribers = subscribers;
        for (Object subscriber : subscribers) {
            register(subscriber);
        }
    }

    public void post(final Object event) {
        eventBus.post(event);
    }

    public void register(final Object object) {
        eventBus.register(object);
    }

    public void unregister(final Object object) {
        eventBus.unregister(object);
    }

    public final void handleException(Throwable exception,
            SubscriberExceptionContext context) {
        exception.printStackTrace();

    }

}

There is only one problem: If a bean is in a lower scope, the initialization will fail. In this case we have to do the opposite to attach a subscriber to the bus. Instead of telling the bus which subscriber we want to register, we are telling the subscriber which bus to attach to.

package ch.hasselba.xpages;

public class EventSubscriberRequestScope extends EventSubscriber {

    /**
     * registers the current instance to the event bus
     * 
     * @param eventBus
     *            the frontend event bus
     */
    public void setEventBus(FrontendEventBus eventBus) {
        eventBus.register(this);
    }

}

As soon the bean is instantiated, the managed property will have an instance of our FrontendEventBus and the bean can register itself.

The faces-config.xml looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config>

    <managed-bean>
        <managed-bean-name>frontendEventBus</managed-bean-name>
        <managed-bean-class>ch.hasselba.xpages.FrontendEventBus</managed-bean-class>
        <managed-bean-scope>session</managed-bean-scope>
        <managed-property>
            <property-name>subscribers</property-name>
            <list-entries>
                <value-class>java.lang.Object</value-class>
                <value>#{eventSubscriber}</value>
            </list-entries>
        </managed-property>
    </managed-bean>
    
    
    <managed-bean>
        <managed-bean-name>eventSubscriber</managed-bean-name>
        <managed-bean-class>ch.hasselba.xpages.EventSubscriber</managed-bean-class>
        <managed-bean-scope>session</managed-bean-scope>
    </managed-bean>
    
    
    <managed-bean>
        <managed-bean-name>eventSubscriberRequestScope</managed-bean-name>
        <managed-bean-class>ch.hasselba.xpages.EventSubscriberRequestScope</managed-bean-class>
        <managed-bean-scope>request</managed-bean-scope>
        <managed-property>
            <property-name>eventBus</property-name>
            <value>#{frontendEventBus}</value>
        </managed-property>
    </managed-bean>
    
</faces-config>

The first subscribing bean is in the same scope as the FrontendEventBus, but the second one is a request scoped bean.

Now it’s time to send an event. This can be realized by creating a new instance of our FrontendEvent we have created earlier and posting it to the bus.

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

    <xp:button
        id="button1" value="Click me">
        
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="complete">
            <xp:this.action>
                <![CDATA[#{javascript:importPackage( ch.hasselba.xpages );
                    var msg = "Timestamp: " + java.lang.System.currentTimeMillis();
                    var se = new ch.hasselba.xpages.FrontendEvent( msg );
                    frontendEventBus.post( se );
                }]]>
            </xp:this.action>
        </xp:eventHandler>
    
    </xp:button>
    
    <br />
    <br />
    
    <xp:label
        value="#{eventSubscriber.message}"
        id="labelSubscriberMessage">
    </xp:label>
    
    <br />
    
    <xp:label
        value="#{eventSubscriberRequestScope.message}"
        id="labelSubscriberRequestScope">
    </xp:label>
    
</xp:view>

As soon the button is clicked, both beans are receiving the event and updating their message.

2015-12-01 14_04_04-Mozilla Firefox

Hope someone finds it usefull.

Things I never blogged about: The XPagesExecutor Service

29. November 2015 Posted by Sven Hasselbach

The XPages engine has its own executor service to run jobs concurrently in another thread: the XPagesExecutor service. Under the hood the service uses a ThreadPoolExecutor for executing tasks, so it allows to use Runnables or Callables/Futures for asynchronous computation. For fun, you can even run a server instance, the started threads can run as long as you want (e.g. they are not bound to the XPages lifecycle).

Here is a small XPage with a button to start a Runnable via the Executor:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
    
    <xp:button
        value="run the job"
        id="buttonDoIt">
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="complete">
            <xp:this.action>
                <![CDATA[#{javascript:
                importPackage(ch.hasselba.xpages);
                new ch.hasselba.xpages.DemoExecutor().doIt();}]]>
            </xp:this.action>
        </xp:eventHandler>
    </xp:button>
    
</xp:view>

To use the Service, you have to aquire it first, then you can execute a Runnable like in this code snippet:

package ch.hasselba.xpages;

import java.util.concurrent.ExecutorService;

import lotus.domino.NotesException;
import lotus.domino.Session;

import com.ibm.domino.xsp.module.nsf.NSFComponentModule;
import com.ibm.domino.xsp.module.nsf.NotesContext;
import com.ibm.domino.xsp.module.nsf.SessionCloner;
import com.ibm.xsp.application.XPagesExecutor;

public class DemoExecutor {
    private final NSFComponentModule module;
    private final SessionCloner sessionCloner = SessionCloner
            .getSessionCloner();

    public DemoExecutor() {
        // get the current NSFComponentModule
        this.module = NotesContext.getCurrent().getModule();
    }

    public void doIt() {
        // aquire the Executor
        ExecutorService exService = XPagesExecutor.acquire();
        
        // run the Runnable
        exService.execute(new DemoRunnable(this.module));
    }

    class DemoRunnable implements Runnable {
        private final NSFComponentModule module;

        public DemoRunnable(NSFComponentModule module) {
            this.module = module;
        }

        public void run() {
            try {

                // init the NotesThread
                NotesContext context = new NotesContext(this.module);
                NotesContext.initThread(context);

                // now do the job...
                System.out.println("Starting the job...");
                Session session = null;
                try {
                    // grab the session
                    session = sessionCloner.getSession();
                    System.out.println("Running as "
                            + session.getEffectiveUserName());
                    Thread.sleep(10000);
                } catch (NotesException e) {
                    e.printStackTrace();
                } finally {
                    DominoUtils.recycle( session );
                }
                System.out.println("Done!");

            } catch (InterruptedException ie) {
                // handle interruption here
            } finally {
                // kill the Notes thread
                NotesContext.termThread();
            }
        }

    }

}

2015-11-29 08_54_13-SH Domain - Dev01_Hasselba_CH - IBM Domino Administrator

Callables must be submitted instead:

   public long doItWithCallable() {

        ExecutorService exService = XPagesExecutor.acquire();
        Future<Long> result = exService.submit(new MyCallable(this.module));
        long returnValue = (-1);

        try {
            returnValue = result.get();
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        } catch (ExecutionException ee) {
            ee.printStackTrace();
        }

        return returnValue;
    }

    class DemoCallable implements Callable {
        private final NSFComponentModule module;

        public DemoCallable(NSFComponentModule module) {
            this.module = module;
        }

        public Long call() throws Exception {
            try {

                // init the NotesThread
                NotesContext context = new NotesContext(this.module);
                NotesContext.initThread(context);

                // now do the job...
                System.out.println("Starting the job...");
                Session session = null;
                try {
                    // grab the session
                    session = sessionCloner.getSession();
                    System.out.println("Running as "
                            + session.getEffectiveUserName());
                    Thread.sleep(10000);
                } catch (NotesException e) {
                    e.printStackTrace();
                } finally {
                    DominoUtils.recycle( session );
                }
                System.out.println("Done!");

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // kill the Notes thread
                NotesContext.termThread();
            }

            return System.currentTimeMillis();
        }

    }

Hope someone finds it usefull.

[Discontinued] Testing XPages(3): Testing the Business Logic

28. November 2015 Posted by Sven Hasselbach

I will not develop my planned XPages Testing Framework further because for me it has no business case anymore. While there never was enough time to make it to a „real project“ for the daily development (full JUnit integration, deployed as an OSGi plugin etc) and this code is only an example, I used this technique with a similar code base in some projects. It was a big help during development, because it allows to „look inside“ the XPages engine while testing.

How does it work?

In a JUnit test, a local proxy is started which fetches all request / responses between the Selenium WebDriver and the Domino server. Only the first request is relevant, the other requests are the resources like Javascript files, Images and CSS.

During the test, a special HTTP Header is added to the request (X-XPAGES-TEST). It contains EL code and the JSF phase when to execute it. A phase listener (in the application, better in an OSGi plugin) intercepts the HTTP header and executes the code. The result it posted back to the client (encapsulated in another HTTP header). It can then be tested with an assert.

Because of the use of special headers instead of injecting the results into the HTML markup, this can be used with Diffy or similar products to verify the application while refactoring the code base.

Here is the JUnit Test application:


package ch.hasselba.xpages.test.XPagesTester;

import java.util.LinkedList;
import java.util.concurrent.TimeUnit;

import net.lightbody.bmp.core.har.Har;
import net.lightbody.bmp.proxy.ProxyServer;
import net.lightbody.bmp.proxy.http.BrowserMobHttpRequest;
import net.lightbody.bmp.proxy.http.BrowserMobHttpResponse;
import net.lightbody.bmp.proxy.http.RequestInterceptor;
import net.lightbody.bmp.proxy.http.ResponseInterceptor;

import org.apache.http.HttpResponse;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriver.Options;
import org.openqa.selenium.WebDriver.Timeouts;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.remote.DesiredCapabilities;

public class App
{
  private ProxyServer server;
  private LinkedList httpResponses;
  private LinkedList httpRequests;
  private WebDriver driver;
  private String baseUrl = "http://localhost/Demo.nsf/Test.xsp";
  private StringBuffer verificationErrors = new StringBuffer();
  private final String HTTP_HEADER_NAME = "X-XPAGES-TESTS";
	
  @Before
  public void setUp() throws Exception {
    this.server = new ProxyServer(4444);
    this.server.start();

    Proxy proxy = this.server.seleniumProxy();

    DesiredCapabilities capabilities = new DesiredCapabilities();
    capabilities.setCapability("proxy", proxy);

    ResponseInterceptor interceptor = new ResponseInterceptor() {
      public void process(BrowserMobHttpResponse response, Har har) {
    	  httpResponses.add( response );
      }
    };
    RequestInterceptor requestInterceptor = new RequestInterceptor() {
      public void process(BrowserMobHttpRequest request, Har har) {
    	  httpRequests.add( request );
      }
    };
    this.server.addRequestInterceptor(requestInterceptor);

    this.server.addResponseInterceptor(interceptor);
    this.driver = new FirefoxDriver(capabilities);
    this.driver.manage().timeouts().implicitlyWait(10L, TimeUnit.SECONDS);
  }
  @After
  public void tearDown() throws Exception {
    this.driver.quit();
    String verificationErrorString = this.verificationErrors.toString();
    if (!"".equals(verificationErrorString))
      Assert.fail(verificationErrorString);
  }

  @Test
  public void testDemo() throws Exception
  {
    StringBuffer strBuffer = new StringBuffer();
    strBuffer.append("[");
    strBuffer.append("{");
    strBuffer.append(""code": "#{javascript:java.lang.System.currentTimeMillis()}"}]");
    this.server.addHeader(HTTP_HEADER_NAME, strBuffer.toString() );
    
    reloadPage();

    System.out.println( "X-PAGES-TESTS: " + getXPageResponse().getHeader(HTTP_HEADER_NAME) );
    
    HttpResponse httpRawResponse = getXPageResponse().getRawResponse();
    Assert.assertTrue("HTTP/1.1 200 OK".equals(httpRawResponse.getStatusLine()
      .toString()));

    Assert.assertTrue("Lotus-Domino".equals(getXPageResponse().getHeader("Server")));

  }

  public void reloadPage()
  {
    this.httpRequests = new LinkedList();
    this.httpResponses = new LinkedList();
    this.driver.get(this.baseUrl);
  }
  
  public BrowserMobHttpResponse getXPageResponse(){
    return this.httpResponses.get(0);
  }
}

And here comes the phase listener:


package ch.hasselba;

import java.util.HashSet;
import java.util.Iterator;

import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.servlet.http.HttpServletRequest;

import com.ibm.commons.util.io.json.JsonEmptyFactory;
import com.ibm.commons.util.io.json.JsonException;
import com.ibm.commons.util.io.json.JsonJavaFactory;
import com.ibm.commons.util.io.json.JsonJavaObject;
import com.ibm.commons.util.io.json.JsonParser;
import com.ibm.xsp.application.ApplicationEx;
import com.ibm.xsp.context.ExternalContextEx;
import com.ibm.xsp.context.FacesContextExImpl;

@SuppressWarnings("serial")
public class TestPhaseListener implements PhaseListener {

  private final String HTTP_HEADER_NAME = "X-XPAGES-TESTS";
	
  public void afterPhase(PhaseEvent event) {}

  public void beforePhase(PhaseEvent event) {
    FacesContextExImpl fc = (FacesContextExImpl) event.getFacesContext();
    ExternalContextEx ec = (ExternalContextEx) fc.getExternalContext();
    HttpServletRequest request = (HttpServletRequest) ec.getRequest();
    ApplicationEx app = fc.getApplicationEx();
    HashSet results = new HashSet();
    try {
      String jsonData = request.getHeader(HTTP_HEADER_NAME);
      // Create the JAVA json factory
      JsonJavaFactory factory = JsonJavaFactory.instanceEx;
      // Turn the sort parameter string into an array of JSON objects
      Object jsonTests;

      jsonTests = JsonParser.fromJson(factory, jsonData);
      // Loop through the JSON objects and do something
      for (Iterator<Object> oSort = factory.iterateArrayValues(jsonTests); oSort
					.hasNext();) {
         JsonJavaObject o = (JsonJavaObject) oSort.next();
	 //String phaseId  o.getJsonProperty("phaseId") );
	 String code = (String) o.getJsonProperty("code");

         try {
           Object result = app.createMethodBinding(code, null).invoke(fc,
						null);
           results.add( result.toString() );
         }catch( Exception e) {
	   results.add( e.getMessage() );
         }
      }

      StringBuffer buffer = new StringBuffer();
      buffer.append( "[" );
      int pos = 0;
      for( String res : results ) {
         buffer.append( "{ "result" : "" + res + ""}");
         pos++;
	 if( pos < results.size() )
	   buffer.append( "," );
      }
      buffer.append( "]");
			
      fc.getXspResponse().addHeader(HTTP_HEADER_NAME, buffer.toString() );
			
    } catch (JsonException e) {
      e.printStackTrace();
      fc.getXspResponse().addHeader(HTTP_HEADER_NAME,
					e.getLocalizedMessage());
    } catch (Exception e) {
      e.printStackTrace();
      fc.getXspResponse().addHeader(HTTP_HEADER_NAME,
					e.getLocalizedMessage());
    }

  }

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

}

Hope someone finds it usefull.