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

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.

Veröffentlicht unter Apache Wink, Jackson, Java, JEE, REST | Verschlagwortet mit , , , , , , , | 3 Kommentare

Domino & REST: Consuming JSON

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:

Veröffentlicht unter Apache Wink, Jackson, Java, JEE, REST | Verschlagwortet mit , , , , , | Ein Kommentar

Domino & REST: A basic Servlet

To have a good starting point when creating RESTful applications on top of Domino, I have created a „Hello World“ example of a JEE Application, based on Apache Wink & Jackson 2.5.0.

The Jackson AnnotaionProcessor is registered into Apache Wink application and is enabled by default, the JAXB processor is also included as the secondary AnnotationProcessor. The benefit of this is that only one global ObjectMapper instance is created and is reused, wich allows a better performance as when you create an own instance of an ObjectMapper for every request.

The servlet is deployed as a Plugin, so you need a working XPages plugin development environment first before you can build it (I won’t get in the details at this time). The code of the servlet can be found in the domino-rest-servlet.plugin project.

First have a look into plugin.xml: This file contains the extension point used by the plugin, which means that this tells the Domino server what is inside and what to do next with the plugin. The extension point is of type „com.ibm.pvc.webcontainer.application“ and allows to run our own JEE application, independently of the XPages runtime. The contextRoot is the URI part on which our application „listens“. The contentLocation is where files are searched when accessed from the browser, and points to the WebContent folder (as you already know, it is the same as in XPages projects when using the package explorer view). Here you can store static files and/or resources if required.

The web.xml file is the servlet configuration and the place where the servlet is registered. With servlet-mappings you can define URI pathes *inside* of the JEE application.

In this example, every request is mapped to the RestServlet servlet when it is send to „http://your.server/dominorestservlet/„. It will be now processed by the class ch.hasselba.dominorestservlet.RestApiApplication, which „knows“ the RestApiServlet because of the @Path annotations.

Our „Hello World“ example is registered for the URI „/helloworld/“ and only for HTTP GET requests. As soon you are opening „http://your.server/dominorestservlet/helloworld/„, an instance of HelloWorld is created and transformed to JSON. The result looks like this:

{"message":"Hello World!"}

During the next blog posts, I will expand this example and explain step by step how a seamless integration in the Domino environment works.

You can find the code here: https://github.com/hasselbach/domino-rest-servlet

Veröffentlicht unter Jackson, Java, JEE, REST, Web | Verschlagwortet mit , , , , , | Ein Kommentar

Domino & REST: Listeners for Initialization & Destroying of a Servlet

If you need to know when your Servlet is initialized or destroyed, you can use a ServletContextListener in your application.

First, create the class AppServletContextListener and implement the javax.servlet.ServletContextListener interface. This provides two methods for capturing the events: contextInitialized and contextDestroyed:

package ch.hasselba.servlet;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class AppServletContextListener
               implements ServletContextListener{

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        System.out.println("ServletContextListener destroyed");
    }

    @Override
    public void contextInitialized(ServletContextEvent event) {
        System.out.println("ServletContextListener started");
    }
}

The ServletContextEvent allows you to access the ServletContext if required.

Now you have to register the listener in your web.xml:

<web-app>
     <listener>
         <listener-class>
             ch.hasselba.servlet.AppServletContextListener
         </listener-class>
    </listener>
</web-app>

As soon as you start or stop the HTTP task, the Listener is called, and you can execute some application specific code (i.e. shutdown threads, bring out the garbage, etc.)

Veröffentlicht unter Java, JEE, REST, Server | Verschlagwortet mit , , , , , | 2 Kommentare

Jackson: Skip Objects conditionally

I had a simple problem when implementing this brilliant solution in one of my REST applications: As soon I was using @JsonAnyGetter / @JsonAnySetter and the HidableSerializer together, a NPE was thrown during serialization. The Problem occured in Jackson 1.9.13 and even 2.5.0, the latest version usable with the actual Domino JVM.

  • Stack Trace
org.codehaus.jackson.map.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: ch.hasselba.Test["[anySetter]"])
null
    at org.codehaus.jackson.map.JsonMappingException.wrapWithPath(JsonMappingException.java:218)
    at org.codehaus.jackson.map.JsonMappingException.wrapWithPath(JsonMappingException.java:183)
    at org.codehaus.jackson.map.ser.std.SerializerBase.wrapAndThrow(SerializerBase.java:140)
    at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:158)
    at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112)
at ch.hasselba.HidableSerializer.serialize(HidableSerializer.java:29)
    at org.codehaus.jackson.map.ser.StdSerializerProvider._serializeValue(StdSerializerProvider.java:610)
    at org.codehaus.jackson.map.ser.StdSerializerProvider.serializeValue(StdSerializerProvider.java:256)
    at org.codehaus.jackson.map.ObjectMapper._configAndWriteValue(ObjectMapper.java:2575)
    at org.codehaus.jackson.map.ObjectMapper.writeValueAsString(ObjectMapper.java:2097)
    at ch.hasselba.Demo.main(Demo.java:54)
Caused by: java.lang.NullPointerException
    at org.codehaus.jackson.map.ser.std.MapSerializer.serializeFields(MapSerializer.java:243)
    at org.codehaus.jackson.map.ser.AnyGetterWriter.getAndSerialize(AnyGetterWriter.java:41)
    at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:154)
    ... 7 more

After hours of investigation, a simple solution came up on SO: The default serializer must be resolved, and then the code will work. I just had to override resolve method of my HidableSerializer:

@Override
public void resolve(SerializerProvider serializerProvider) throws JsonMappingException {
    if(defaultSerializer instanceof ResolvableSerializer) {
         ((ResolvableSerializer)defaultSerializer).resolve(serializerProvider);
    }
}

Here is a complete working example:

 

  • The Demo Code
package ch.hasselba;

import org.codehaus.jackson.Version;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.introspect.BasicBeanDescription;
import org.codehaus.jackson.map.module.SimpleModule;
import org.codehaus.jackson.map.ser.BeanSerializerModifier;

public class Demo {

    public static void main(String[] args) {

        ObjectMapper mapper = new ObjectMapper();

        // register the module
        Version version = new Version(1, 0, 0, "SNAPSHOT");
        mapper.registerModule(new SimpleModule("HidableModule", version) {
            @Override
            public void setupModule(SetupContext context) {
                super.setupModule(context);
                context.addBeanSerializerModifier(new BeanSerializerModifier() {
                    @SuppressWarnings("unchecked")
                    @Override
                    public JsonSerializer<?> modifySerializer(SerializationConfig config, BasicBeanDescription desc,
                        JsonSerializer<?> serializer) {
                        if (IHidable.class.isAssignableFrom(desc.getBeanClass())) {
                            return new HidableSerializer<Object>((JsonSerializer<Object>) serializer);
                        }
                        return serializer;
                    }
                });
            }
        });

        // the data
        String content = "{ \"foo\": \"bar\" }";

        // build the Object
        Test test = null;
        try {
            test =  mapper.readValue(content, Test.class);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // and now convert it back to a String
        String data = null;
        try {
             data = mapper.writeValueAsString(test);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println( data );

    }

}

 

  • Test Class
package ch.hasselba;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.codehaus.jackson.annotate.JsonAnyGetter;
import org.codehaus.jackson.annotate.JsonAnySetter;

public class Test implements IHidable {

    private Map<String, Object> others = new ConcurrentHashMap<String, Object>();

    @JsonAnyGetter
    public Map<String, Object> getOthers() {
        return this.others;
    }

    @JsonAnySetter
    public void addOther(final String name, final Object value) {   
        this.others.put(name, value);
    }

    @Override
    public boolean isHidden() {
        return false;
    }

}

 

  • Hideable Serializer
package ch.hasselba;

import java.io.IOException;

import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;

public class HidableSerializer<T> extends JsonSerializer<T>  implements ResolvableSerializer {

    private JsonSerializer<T> defaultSerializer;

    public HidableSerializer(JsonSerializer<T> serializer) {
        defaultSerializer = serializer;
    }

    @Override
    public void serialize(T value, JsonGenerator jgen, SerializerProvider provider)
            throws IOException, JsonProcessingException {

        if( value instanceof IHidable ){
            IHidable hidableValue = (IHidable) value;
            if( hidableValue.isHidden() )
                return;
        }
        defaultSerializer.serialize(value, jgen, provider);
    }

   @Override
   public void resolve(SerializerProvider serializerProvider) throws JsonMappingException {
       if(defaultSerializer instanceof ResolvableSerializer) {
            ((ResolvableSerializer)defaultSerializer).resolve(serializerProvider);
       }
   }
}

 

  • IHidable Interface
package ch.hasselba;

public interface IHidable {
    boolean isHidden();
}
Veröffentlicht unter Jackson, REST | Verschlagwortet mit , , , , | Schreib einen Kommentar

Java NAPI: Enable / Disable Recycling of C Handles

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.

Veröffentlicht unter Java | Verschlagwortet mit | Schreib einen Kommentar

XPages & Domino JNA

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-jna\target\lib“ (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
Veröffentlicht unter Java, Security, Server, XPages | Ein Kommentar

Vaadin In XPages: A UIComponent for embedding Vaadin applications

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

Veröffentlicht unter Vaadin, XPages | Verschlagwortet mit , | 2 Kommentare

EntwicklerCamp 2016: XPages erweitern und ausbauen

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.

Veröffentlicht unter EC 2016, XPages | Verschlagwortet mit , | Schreib einen Kommentar

EntwicklerCamp 2016: Ein Leben ohne Notes Client

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.

UPDATE: Hier die PDF Version.

Veröffentlicht unter EC 2016, Java, JavaFX | Verschlagwortet mit , , | Schreib einen Kommentar