Posts Tagged: ‘Apache Wink’

High Performance REST Applications (4) – Looking into OSGi

4. Mai 2017 Posted by Sven Hasselbach

Before going any deeper into the the servlet project, let’s have a look at the imported projects and talk about some OSGi basics.

First you will notice that for every cloned repository three Eclipse projects have been imported:

  1. A plugin project
  2. A feature project
  3. An updatesite project

The plugin project contains the code and all the relevant resources of the servlet. It defines extension points provided or describes which extension points are used by the plugin.
A feature project is basically a list of plugins and other features which can be understood as a logical separate unit. And an updatesite contains all features you need.

Using an UpdateSite is the preferred way to deploy plugins to Domino. It has a „Build All„-Button which builds all plugins from all features in your updatesite project, and creates the JARs to import into an UpdateSite in Domino.

Update Site „Build All“-Button

A plugin is mainly described by two files: the MANIFEST.MF (stored in the /META-INF folder) and a plugin.xml in the root.

plugin.xml & MANIFEST.MF

The MANIFEST.MF contains meta informations about the Plugin, like the name, imports and exports, other required bundles, the Activator class, the Execution environment and many more.

For example the Domino REST servlet plugin requires the concurrent plugin:

Required Bundles: „ch.hasselba.concurrent.plugin“

If the bundle is loaded, but the requirements are not fullyfied, the bundle won’t start.

The plugin.xml is optional for OSGI bundles, but we need this file, because it describes the extension point used by our plugin:

plugin.xml – Extension Point

Our servlet plugin uses the com.ibm.pvc.webcontainer.application extension point to run a JEE application. The parameter contextRoot is the path our servlet listens (which means it will be reachable at http://example.com/ec2017), and the contentLocation parameter is the path to the static files of the plugin and our web.xml.

WebContent folder

An Activator class is the class executed when a bundle is started or stopped (or the other possibilities a lifecyle of a bundle can have). It is also a good idea to store the BundleContext in the Activator to communicate with the OSGi environment.

Last but not least, there is a build.properties file: This file is for Eclipse and contains all relevant information to build the bundle. This means that if you add a JAR file the plugin requires, you have to add the JAR to both – the MANIFEST.MF and the build.properties (if the classes of the JAR file are used in the project).

In the Domino REST Servlet project, the spymemcached.jar is used, that’s why it is added to the build path and the manifest:

build.properties – spymemcached JAR

MANTIFEST.MF – spymemcached JAR

You have to keep this in your mind, because if you write some code, it is not enough just to add the required JAR to the build path, it also has to be exported in your bundle / plugin.

In the next post, let’s have a look into web.xml, and then go into the real „High Performance“ solution.

Domin & REST: Debug your Plugin

7. März 2017 Posted by Sven Hasselbach

When developing OSGi Plugins, you should have your own development server running on your local machine. Not only because of the faster deployment of changes (a new version of a plugin must always deployed with a HTTP restart), but because of the Java debugging posibilities: Only one Eclipse instance can connect to the JVM, and every request processed by the server will start the debugger. If multiple users a accessing the server while you are debugging, your Eclipse will try to debug every incoming request, and this can lead into a confusing situation for all.

To enable debugging, you first have to add two parameters to the notes.ini:

JavaEnableDebug=1
JavaDebugOptions=transport=dt_socket,server=y,suspend=n,address=8000

This starts the debugging on port 8000. Feel free to change the value to every port you want. Because of security reasons you should not start debugging on a productive machine.

After restarting the domino server, you can connect to the JVM in your Eclipse IDE by creating a new JVM remote debugging session. Create a new debug configuration…

… choose (1) „Remote Java Application„, (2) give a name to it, (3) select the plugin project, (4) enter the port the server listens, and click on (5) „Apply„.

If you want to connect to your server, you need to start debugging by clicking on the project:

After setting a breakpoint and sending a request to the servlet, Eclipse switches to the Debug perspective where you can look what happens with your servlet.

Sometimes you are connecting to the „wrong“ JVM, because a Java agent is running and/or a DOTS task does it’s job. It’s better to disable these tasks on your development server.

During debugging you are able to hotswap your code, but keep in mind that after a restart of the HTTP JVM all your changes are no longer „installed“. You have to build a new plugin or replace your code during runtime again.

In the next blog post, let’s talk about our development tools.

Domino & REST: More about Jackson

3. März 2017 Posted by Sven Hasselbach

When creating a REST API servlet, Jackson provides a huge list of possibilities to manipulate the JSON data, mostly using annotations.

Let’s demonstrate some of them with this little class, which has only two properties:

public class Demo {

    private String foo;
    private String bar;

    public String getFoo() { 
        return foo;
    }
    public void setFoo(String foo) {
        this.foo = foo;
    }

    public String getBar() { 
        return bar;
    }

    public void setBar(String bar) {
        this.bar = bar;
    }
}

The playground converts the content string to a POJO and back to a string:

String content = "{ \"foo\": \"bar\" }";
 
 // init the ObjectMapper
 ObjectMapper mapper = new ObjectMapper();
 
 // build the Object
 Demo test = null;
 try {
     test = mapper.readValue(content, Demo.class);
 } catch (Exception e) {
     e.printStackTrace();
 }

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

 System.out.println( data );

If we run this code, the result is not really spectacular:

{
 "foo" : "bar",
 "bar" : null
}

So let’s ignore the property foo by adding the annotation @JsonIgnoreProperties to the Demo class:

@JsonIgnoreProperties({"foo"})
public class Demo { ... }

Now, foo is no longer in our resulting JSON:

{
    "bar" : null
}

The property bar is null, and we don’t like nulled properties in our JSON. That’s why we add another annotation, @JsonInclude:

@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Demo { ... }

After removing the previously added @JsonIgnoreProperties annotation, our result looks like this (the empty property bar was skipped):

{
    "foo" : "bar"
}

What happens if we change our content string, and add an unknown property?

String content = "{ \"foo\": \"bar\", \"undefined\": \"property\" }";

An error occurs because Jackson does not know how to handle the new property:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "undefined" (class ch.hasselba.JacksonPlayground.Demo), not marked as ignorable (2 known properties: "foo", "bar"])
 at [Source: { "foo": "bar", "undefined": "property" }; line: 1, column: 31] (through reference chain: ch.hasselba.JacksonPlayground.Demo["undefined"])
 at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:51)
 at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:817)
 at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:954)
 at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1315)
 at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1293)
 at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:249)
 at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:136)
 at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3560)
 at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2576)
 at ch.hasselba.JacksonPlayground.App.main(App.java:24)
null

But there are two annotations to the rescue, @JsonAnyGetter@JsonAnySetter. By changing our Demo class and adding the following lines of code…

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);
}

… Jackson now puts all the unknown/undefined properties in the others map (uses the method defined by @JsonSetter). And then it uses the method with the @JsonGetter annotation when producing the JSON from the Demo instance.

{
  "foo" : "bar",
  "bar" : null,
  "undefined" : "property"
}

What if we want to handle multiple „Demo“ objects in a JSON Array?

String content = "[ { \"foo\": \"bar\" }, {\"foo\": \"bar2\" } ]";

In this case we change our reading routine to work with lists:

// build the Object
List<Demo> test = null;
try {
    test = mapper.readValue(content, mapper.getTypeFactory()
            .constructCollectionType(List.class, Demo.class));
} catch (Exception e) {
    e.printStackTrace();
}

In the result all entries are now contained in the list of Demo objects:

[ {
 "foo" : "bar",
 "bar" : null
}, {
 "foo" : "bar2",
 "bar" : null
} ]

Back to our RestApiApplication, have a look at this line:

objMapper.setSerializationInclusion(Include.NON_EMPTY);

This removes all empty properties globally from the generated output of our the servlet. So there is no need to add the @JsonIgnore annotation to any class. You can modifiy the globally used ObjectMapper in your servlet with multiple option, more will follow in another blog post.

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

2. März 2017 Posted by Sven Hasselbach

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

The class gives mainly access to the following methods:

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

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

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

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

Domino & REST: Consuming JSON

1. März 2017 Posted by Sven Hasselbach

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

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

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

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

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

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

… the response contains the message we have posted:

Domino & REST: A basic Servlet

28. Februar 2017 Posted by Sven Hasselbach

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

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

27. Februar 2017 Posted by Sven Hasselbach

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