Posts Tagged: ‘Java’

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.

Ein Plädoyer für Lotus Notes (Teil 2): Das Lego Prinzip

21. März 2016 Posted by Stephan Kopp

…oder wie ich Java und Web Services durch Lotus Notes zu lieben gelernt habe.

Einige meiner Kollegen arbeiten schon seit längerem mit Webservices und haben unter anderem schon ein komplettes User Management für Domino und eine Kalender Schnittstelle für SAP entwickelt. Ich selbst fand das schon immer sehr interessant und zukunftsweisend, habe selbst aber noch nicht damit gearbeitet.

In den letzten Tagen habe ich mich jetzt endlich mal etwas intensiver mit dem Thema beschäftigt. Es ist faszinierend was man mit diesem Lego Prinzip alles erreichen und vereinfachen kann, wenn man es konsequent einsetzt.

Konkretes Fall Beispiel

Es geht um einen relativ großen Kunden, den ich hier nicht nennen möchte. Dieser Kunde migriert seit längerem weg von Lotus Notes. Mail wurde relativ schnell migriert und jetzt fehlt noch der ganze Rest.

Anfang des Monats haben wir dort die bereits erwähnten User Management Webservices live genommen um die Userverwaltung zu vereinfachen. Es werden also jetzt im Self Service Prinzip alle Notes IDs von den Anwendern selbst beantragt, weitere Daten aus dem internen Identity Management System nachgeladen und voll automatisch angelegt. Ein ähnlicher Prozess existiert für die Anlage von Outlook Accounts und Mailboxen, das ganze läuft aber parallel und wurde nicht in einen Prozess integriert. Soweit so gut, alles läuft nahezu perfekt, wir haben bis heute schon fast 200 User accounts in knapp 2 1/2 Wochen über den Prozess angelegt.

Das Problem

Über ein Problem sind wir jedoch gestolpert: Wir erzeugen die Notes IDs, legen sie in den ID-Vault und vergeben dabei ein Initial Passwort. Dieses Passwort senden wir dem User per Mail zu. Das sollte eigentlich kein Problem sein, da das Mail System ja Outlook ist und die User damit an die Passwörter ran kommen (sollte). Jedoch hat sich herausgestellt, dass das Anlegen der Outlook Postfächer in einzelnen Fällen durchaus etwas länger dauern kann und wir deshalb die Passwörter nicht einfach verschicken können sobald wir fertig sind. Ein einfacher Zeitversatz von x Stunden war auch wenig hilfreich, da es nicht nachvollziehbar war, wie lange das Anlegen auf Exchange Seite dauert.

Der Lösungsansatz

Im Laufe des Migrationsprojektes habe ich schon für verschiedene Applikationen per Java Code Verbindungen in Richtung Active Directory und Exchange programmiert. Deshalb wollte ich in unseren User Anlage Prozess einen Workflow Schritt einbauen, der überprüft ob das Outlook Postfach erfolgreich angelegt wurde und wir das Passwort verschicken können. Ich hätte das jetzt natürlich wieder per Java direkt in die Notes Applikation bauen können, es ist aber abzusehen dass wir diese und ähnliche Funktionen in Zukunft noch an vielen Stellen benötigen werden. Deshalb war es eine gute Gelegenheit das Ganze über einen Webservice zu implementieren um es auch für zukünftige Anwendungsfälle ohne große Programmierarbeit zur Verfügung zu stellen.

Was macht den Webservice Ansatz in diesem Beispiel besser?

Natürlich kann ich meinen Code soweit wiederverwendbar machen, dass ich ihn recht einfach in unterschiedliche Applikationen integrieren kann. Es ist aber immer nur Code, den ich wieder verwende. Webservices gehen hier einen Schritt weiter, ich verwende wirklich einen Service, der sehr viel mehr als nur Code zur Verfügung stellt. In unserem konkreten Beispiel greifen wir nicht auf das interne AD, sondern auf das des externen Betreibers der Exchange Umgebung zu. Hierfür benötigen wir Zugangsdaten, Firewall Freischaltungen und weitere Details wie die richtige Searchbase um die User Accounts zu finden. Für jede Applikation muss ich also die Firewall ändern lassen und die Möglichkeit schaffen Zugangsdaten und Optionen sicher zu hinterlegen. Das ist nicht wirklich praktikabel.

Der Webservice Ansatz bietet eine Möglichkeit diesen Zugriff komplett als Service anzubieten. Ich muss also nichts wissen über die weiteren Abhängigkeiten wie Firewall, Zugangsdaten, welche Searchbase ich benötige, etc. Ich greife von meiner Applikation nur auf einen Webservice zu, übergebe als Parameter eine E-Mail Adresse und bekomme ein Ja oder Nein zurück, ob das Outlook Postfach angelegt ist oder eben nicht. Diesen Webservice kann ich dann wirklich sehr einfach in alle Applikationen integrieren, sei es nun eine Notes Applikation oder auch externe Applikationen. Den Zugriff auf meinen Webservice und damit auf die dahinter liegenden Daten kann ich schnell und einfach anhand der Domino ACL steuern und muss nicht die Zugangsdaten an x Stellen hinterlegen.

Ganz konkret sieht eine Integration des Webservices in meinen LotusScript Code genau so aus:

Dim webservice As New ActiveDirectoryWebservice
Dim isExchangeEnabled As Boolean
isExchangeEnabled = webservice.isExchangeEnabled("email@company.com")

Das ist sehr einfach in jeder beliebigen Applikation einzusetzen. Falls es Bug fixes oder Änderungen gibt, muss ich das nur im Webservice machen und nicht in jeder einzelnen Applikation die diesen nutzt.

Java und Webservices in Lotus Notes Applikationen

Ich arbeite schon länger mit Java in Notes Applikationen. Mir gefällt hier vor allem die optimale Nutzung der jeweiligen Vorteile. Java bietet eine Unmenge von Möglichkeiten um in Notes Applikationen sinnvoll zu arbeiten, aber vor allem auch recht einfach auf externe Systeme und Daten zuzugreifen. An Notes Applikationen gefällt mir die Verkapselung in eine NSF und die einfache Datenstruktur und das vorhandene Security Modell. Beides Zusammen bietet viele Vorteile. Ich entwickle komplexe Notes Applikationen und Java Code, aber trotzdem genügt es die NSF zu sichern oder auf einen anderen Server zu kopieren.

Das Manko das ich allerdings habe, ist die schlechte Verbindung von Java und LotusScript. Ja, ich kann Code der jeweils anderen Welt ausführen und integrieren, aber das ist immer sehr speziell und auch Fehler anfällig. Viel besser gefällt mir der Gedanke der Micro Services, also einzelne Teile der eigenen Applikation als Service anzubieten und diesen dann entweder innerhalb der Applikation oder auch von Außen zu verwenden. So kann ich z.B. einzelne Funktionen in Java oder LotusScript entwickeln und als Webservice miteinander verbinden.

Dadurch entsteht das eingangs erwähnte Lego Prinzip. Ich habe viele kleine Teile, die ich schnell und einfach zusammen fügen kann. Zunächst macht es natürlich etwas mehr Arbeit diesen Service Gedanken konsequent umzusetzen, aber langfristig bietet es enorme Vorteile. Die einzelnen Applikationen werden unabhängiger und einfacher zu pflegen und anpassbarer. Kommt man z.B. irgendwann an einen Punkt, an dem man für einzelne Applikationen die Plattform wechseln möchte (oder muss), bietet das enorme Vorteile. Einzelne Legosteine werden auf neue Systeme portiert und für die restlichen ist das nichts anderes als ein Wechsel der URL des Webservices, oder nichtmal das wenn man mit redirect URLs oder ähnlichem arbeitet.


Filed under: IBM Notes/Domino

Javaland 2016

14. März 2016 Posted by Henning Schmidt

Das Phantasialand in Brühl bei Köln war in diesem Jahr zum dritten Mail der Platz für alle, die einer der größten Konferenzen rund um den Java Mikrokosmos beiwohnen wollten. Dem offiziellen Schlachtruf „Jatumba“ folgten heuer mehr als 1.200 Enthusiasten, um sich an zwei Tagen mit den neusten Informationen und Trends zu versorgen. Das Phantasialand öffnet erst […]

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>).

Installationsprobleme mit IBM Notes und Domino 9.0.1 Fix Pack 5

4. Dezember 2015 Posted by Katrin Benteler

 

Eigentlich sollte das Fix Pack 5 für die Version 9.0.1 von IBM Notes und Domino unter anderem eine erneute Sicherheitslücke in der Java SDK Komponente fixen. Leider gibt es aber Installationsprobleme für die Systeme, die bereits auf einem der Hotfixes, Interim Fixes oder Fix Packs der Version 9.0.1 laufen.

Hier endet die Installation mit der Nachricht: "The wizard was interrupted before IBM Notes 9.0.1 FP5 could be completely installed."

Wenn ihr allerdings IBM Notes und Domino in der Basisversion 9.0.1 installiert habt, ist das Update auf das Fix Pack 5 kein Problem. Der offizielle Workaround von IBM lautet daher auch: alle anderen Fix Packs, Hotfixes und Interim Fixes zu deinstallieren und dann von der Basisversion aus das neue Fix Pack zu installieren.

Weitere Infos erhaltet ihr hier: http://www-01.ibm.com/support

Wenn ihr dabei Hilfe benötigt; stehen wir euch wie immer gerne zur Verfügung!

 

IBM Notes und Domino 9.0.1 Fix Pack 5

3. Dezember 2015 Posted by Oliver Regelmann

Das Fix Pack 5 enthält unter anderem eine neue JVM (1.6 SR16FP7), die eine ganze Reihe von Sicherheitslücken behebt. Dazu gibt es jetzt die Möglichkeit, über eine alternative iNotes-Forms-Datenbank den IE “Standard Mode” zu unterstützen.

Das Setup für den Notes Standard Client wurde kurzfristig wieder zurückgezogen, weil es zu Problemen bei Clients führte, die vor der Installation schon ein Fix Pack oder einen Hotfix installiert hatten. Details und Update dazu in dieser Technote.

Für den Mac Client gibt es FP5 nicht. Dort wird statt dessen das Upgrade auf den 64Bit Client empfohlen, der ja für El Capitan sowieso notwendig ist.

Mehr Details in der Release Notice.

 

Für 8.5.3 werden die JVM-Probleme in einem eigenen JVM-Update SR16FP15 adressiert.

Der Beitrag IBM Notes und Domino 9.0.1 Fix Pack 5 erschien zuerst auf n-komm GmbH.

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.

Debug Retrofit REST applications

27. November 2015 Posted by Sven Hasselbach

Today I had some problems with designing a Retrofit REST application, so I needed a way to debug the request and the response from the server. After poking around, I found the HttpLoggingInterceptor from OkHttp.  It provides all the functionality I need and is really easy to implement.

First you have create a OkHttpClient instance, add the interceptor and set the debug level to HttpLoggingInterceptor.Level.BODY:

OkHttpClient client = new OkHttpClient();
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
client.interceptors().add(interceptor);    

Next step is to add the client during the building of the Retrofit instance:

Retrofit retrofit = new Retrofit.Builder().client(client).build();

Now you can see the HTTP data sent over the wire:

2015-11-27 21_11_08

Don’t forget to add the two required dependencies to your pom.xml:

<dependency>
   <groupId>com.squareup.okhttp</groupId>
   <artifactId>okhttp</artifactId>
   <version>2.6.0</version>
</dependency>
<dependency>
   <groupId>com.squareup.okhttp</groupId>
   <artifactId>logging-interceptor</artifactId>
   <version>2.6.0</version>
</dependency>

XPages: Java Interfaces Java and Test Driven Development

15. Oktober 2015 Posted by Bernd Hort

A word of warning: the following article is quite lenghty and abstract. It basically says that using Java Interface in your XPages projects leads to better (that means more maintanable) code and that with the help of the OpenNTF project org.openntf.junit.xsp a developer can utilize Java Interfaces to write better test code for their XPages projects.


I have to confess that when I started with Java Development some time ago I didn't like Java Interfaces. While in some cases I could see that they are useful most of the time they gave me a hard time trying to understand the code of someone else. My usual approach for reading someone else code is to figure out how to invoke the part I'm interested in and then take a close look at the parameters that are passed to the method and the return value. Interfaces due to their nature shield the implementation.. But that the part I was interested in. Searching for the current implementation made it more complicated.

Since working with XPages my preferences had completely changed. Like most of the Notes developer which start with Java my code used to be small. If you write some Notes Agent to invoke a Web Service or generate a PDF document there is normally not much need for an elaborated design of your class hierarchy.

But nowadays as we developed our own XPages framework totally Java based the rules and approaches must cope with the arising complexity. One aspect were Java Interfaces helps us is the Open/closed principle. Simply speaking the Open/close principal state that code should be open for extension but closed for modification. With modification are those meant that leads to changes in calling methods and therefore are costly and error-prone. If you find a better way to implement an algorithm inside your class no one will complain. But if there is change in your API resulting into the necessity to change code in a lot of calling methods things might get messy.

By using Interfaces as parameters and return values it is like having a contract. The caller of your method can rely on whatever will be passed to the method and returned from it will work as long as it implements the Interface. If you write a new class which is way faster than your old code you can use it instead without worrying that you break something. This is true as long as the new code implements the same Interface. If you find a cool new OpenSource library which you would like to use instead and it does not implement the Interface, you can simply write a wrapper for it.

To believe nothing breaks by your change is one thing. To know it, is another thing. This is were Test Driven Development and JUnit Testing comes in. The idea to walk away from any change you made and to be absolute confident that everything works is amazing.

My first steps using JUnit were a little bit frustrating because there is no way to run JUnit test in Domino Designer directly for your XPages Java code due to the virtual file system a NSF is from Eclipse point of view. So I had to export my Java code and then run JUnit tests in a stand alone Eclipse.

I really was delighted when I read about the OpenNTF project org.openntf.junit.xsp. This project allows to execute the JUnit Tests from within an XPage.

One of the challenges with writing test code is that with systems running in a special environment like XPages you have to find a way to have living objects from this environment. A way around this issue is to use a mock object. And again with Java Interfaces this is easy. Instead of basing an existing Notes document to your test code you could pass a mock up object since lotus.domino.Document is a Java Interface. So there is no need to have a Notes Session, a Notes Database and a existing Notes Document that meets your test requirements for now and all the future to pass your JUnit test.

Christian Güdemann did a great job integrating EasyMock into org.openntf.junit.xsp. Read more in his blog post "org.openntf.junit.xsp – now with EasyMock support".

Have a closer look into using Java Interfaces today. They really help you.

Happy coding!

Testing XPages (2): BrowserMob Proxy

17. September 2015 Posted by Sven Hasselbach

When testing XPages or other web applications, you may want to have more control about the requests and responses during the JUnit testing. For example, if you want to test if a specific HTTP header exists in the response, or if it is required to add some HTTP headers to the request. But you cannot doe this out of the box with Selenium. Instead, you have to add a proxy to the Firefox controller, which then gives you a handle to the HTTP data and allows you to control the flow.

An good solution for this is BrowserMob Proxy, which can be used by adding the required dependency to your Maven pom.xml:

<dependency>
    <groupId>net.lightbody.bmp</groupId>
    <artifactId>browsermob-proxy</artifactId>
    <version>2.0.0</version>
</dependency>

[This is version 2.0.0, the latest stable version]

The proxy runs locally on the specified port as soon the JUnit test starts and ends automatically after finishing the tests.  In order to accomplish this, the setUp method has to be modified:

// start the proxy (listens on port 4444)
server = new ProxyServer(4444);
server.start();

// get the Selenium proxy object
Proxy proxy = server.seleniumProxy();

// configure it as a desired capability
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(CapabilityType.PROXY, proxy);

Now, the proxy setting must be added to the Firefox driver:

driver = new FirefoxDriver(capabilities);

In the test class, three global variables must be defined; these are giving you access to the proxy server and latest the request and response during testing:

private ProxyServer server;
private BrowserMobHttpResponse httpResponse;
private BrowserMobHttpRequest httpRequest;

With the help of a ResponseInterceptor, the httpResponse property is always filled with the latest response. To initialize it, an anonymous class in the setUp method has to be created:

// add a response interceptor
ResponseInterceptor interceptor = new ResponseInterceptor() {
    public void process(BrowserMobHttpResponse response, Har har) {
          httpResponse = response;
    }
};

// add the interceptor to the server
server.addResponseInterceptor(interceptor);

For the RequestInterceptor it is the same procedure:

// add a request interceptor
RequestInterceptor requestInterceptor = new RequestInterceptor() {
    public void process(BrowserMobHttpRequest request, Har har) {
       httpRequest = request;
    }
};

server.addRequestInterceptor(requestInterceptor);

Now, it is possible to use it in a JUnit test:

@Test
public void testDemo() throws Exception {
 
    // add a request header
    server.addHeader("X-FOO", "BAR");
 
    // load the page
    reloadPage();

    // TEST RESPONSE STATUS
    HttpResponse httpRawResponse = httpResponse.getRawResponse();
    assertTrue("HTTP/1.1 200 OK".equals(httpRawResponse.getStatusLine()
       .toString()));
 
    // TEST SERVER HEADER
    assertTrue( "Lotus-Domino".equals( httpResponse.getHeader("Server")) );
 
}

The Browsermob-proxy has a lot of additional features: You can modify the allowed speed for up- and downstreams, use basic authentication, upload files, etc.

Testing XPages

16. September 2015 Posted by Sven Hasselbach

When testing XPages with Selenium, you can easily pre-generate the JUnit test code with the browser plugin. But when you then change the structure of the XPage (f.e. by moving the components from an XPage to a custom control), all the IDs of the JUnit test will not work anymore.

That’s why it is better to use CSS selectors to access the generated fields:

driver.findElements(By.cssSelector("input[id*='idOfTheComponent']"));

With the selector „id*=’idOfTheComponent'“ you can access the elements by their component id, idependently of their full generated client id.

Here is an example with a small XPage with a radio group and a listbox:

 

A simple XPage to test (SimpleDemo.xsp)

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

    <xp:radioGroup
        id="radioGroupSimpleDemo"
        defaultValue="1">
        <xp:selectItem itemLabel="1" itemValue="1" />
        <xp:selectItem itemLabel="2" itemValue="2" />
    </xp:radioGroup>


    <xp:listBox id="listBoxSimpleDemo" multiple="true">
        <xp:selectItem itemLabel="1" itemValue="1" />
        <xp:selectItem itemLabel="2" itemValue="2" />
        <xp:selectItem itemLabel="3" itemValue="3" />
    </xp:listBox>
 
</xp:view>

 The JUnit Test

package ch.hasselba.xpages.test.seleniumdemo;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.List;
import java.util.concurrent.TimeUnit;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.Select;

public class SimpleDemo {

	private WebDriver driver;
	private String baseUrl = "http://127.0.0.1/WebTestDemo.nsf/SimpleDemo.xsp";
	private StringBuffer verificationErrors = new StringBuffer();

	@Before
	public void setUp() throws Exception {
		driver = new FirefoxDriver();
		driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
	}

	@After
	public void tearDown() throws Exception {
		driver.quit();
		String verificationErrorString = verificationErrors.toString();
		if (!"".equals(verificationErrorString)) {
			fail(verificationErrorString);
		}
	}

	@Test
	public void testDemo() throws Exception {
		reloadPage();
		List elemRadio = driver.findElements(By
				.cssSelector("input[id*='radioGroupSimpleDemo']"));
		assertTrue(elemRadio.get(0).isSelected());
		assertFalse(elemRadio.get(1).isSelected());

		Select select = new Select(driver.findElement(By
				.cssSelector("select[id*='listBoxSimpleDemo']")));
		List listSelect = select.getOptions();
		for (WebElement listElem : listSelect) {
			assertFalse(listElem.isSelected());
			assertTrue(listElem.isEnabled());
		}

	}

	public void reloadPage() {
		driver.get(baseUrl);
	}
}

The Maven pom.xml file

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
    http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>ch.hasselba.xpages.test</groupId>
    <artifactId>seleniumdemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>seleniumdemo</name>
    <url>http://hasselba.ch</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>2.46.0</version>
        </dependency>
        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-core</artifactId>
            <version>1.3</version>
        </dependency>
        <dependency>
            <groupId>pl.pragmatists</groupId>
            <artifactId>JUnitParams</artifactId>
            <version>1.0.4</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

Create a Heapdump of the JVM running the Notes standard client

18. August 2015 Posted by Ralf Petter

Normally the Garbage Collection of the JVM works without problems, but sometimes caused by programming mistakes the Garbage Collector can not free heap memory and the heap grows and grows till an OutOfMemory Exception is thrown. The best way to find such problems is to use a Heap Analyzer like Eclipse MAT. Before you can use the Heap Analyzer you have to get a heap dump from Notes with these simple steps.

  • Open a command line
  • Change the current directory to the frameworkrcp directory in your notes directory
  • Enter the command "rcplauncher.exe -com.ibm.rcp.core.logger#dump heap -dumps heapdump"


You can find the heap dump in the "..workspacelogs" directory in your Notes data directory.

Create a Heapdump of the JVM running the Notes standard client

18. August 2015 Posted by Ralf Petter

Normally the Garbage Collection of the JVM works without problems, but sometimes caused by programming mistakes the Garbage Collector can not free heap memory and the heap grows and grows till an OutOfMemory Exception is thrown. The best way to find such problems is to use a Heap Analyzer like Eclipse MAT. Before you can use the Heap Analyzer you have to get a heap dump from Notes with these simple steps.

  • Open a command line
  • Change the current directory to the framework\rcp directory in your notes directory
  • Enter the command "rcplauncher.exe -com.ibm.rcp.core.logger#dump heap -dumps heapdump"


You can find the heap dump in the "..\workspace\logs" directory in your Notes data directory.