That Java Thing, Part 12: Expanding the Plugin - JAX-RS

Thu Dec 03 15:21:28 EST 2015

Tags: java xpages
  1. Nov 02 2015 - That Java Thing, Part 1: The Java Problem in the Community
  2. Nov 03 2015 - That Java Thing, Part 2: Intro to OSGi
  3. Nov 04 2015 - That Java Thing, Part 3: Eclipse Prep
  4. Nov 05 2015 - That Java Thing, Part 4: Creating the Plugin
  5. Nov 06 2015 - That Java Thing, Part 5: Expanding the Plugin
  6. Nov 08 2015 - That Java Thing, Part 6: Creating the Feature and Update Site
  7. Nov 09 2015 - That Java Thing, Part 7: Adding a Managed Bean to the Plugin
  8. Nov 10 2015 - That Java Thing, Part 8: Source Bundles
  9. Nov 11 2015 - That Java Thing, Part 9: Expanding the Plugin - Jars
  10. Nov 12 2015 - That Java Thing, Part 10: Expanding the Plugin - Serving Resources
  11. Nov 16 2015 - That Java Thing, Interlude: Effective Java
  12. Dec 01 2015 - That Java Thing, Part 11: Diagnostics
  13. Dec 03 2015 - That Java Thing, Part 12: Expanding the Plugin - JAX-RS
  14. Feb 19 2016 - That Java Thing, Part 13: Introduction to Maven
  15. Feb 21 2016 - That Java Thing, Part 14: Maven Environment Setup
  16. Feb 22 2016 - That Java Thing, Part 15: Converting the Projects
  17. Feb 23 2016 - That Java Thing, Part 16: Maven Fallout
  18. Feb 26 2017 - That Java Thing, Part 17: My Current XPages Plug-in Dev Environment

A couple of months back, Toby Samples wrote a series on using Wink in Domino. I'm not going to cover all of that ground here, but it's still worth dipping into the topic a bink, as writing REST services in an OSGi plugin is a great way to not only add capabilities to your XPages apps, but to also start making your data (and programming skills) more accessible from other platforms.

There are two main terms to know here: "JAX-RS" and "Wink".

JAX-RS stands for "Java API for RESTful Web Services". If you're observant, you may notice that there's no "X" in that phrase. This is because the "JAX" part is sort of a legacy hanger-on from the common "JAX" prefix used in JAX-WS and the other Java XML APIs... even though JAX-RS doesn't necessarily involve any XML. In any event, JAX-RS is a specification for a way to write RESTful web services in Java with (mostly) inline configuration using annotations.

Apache Wink is a specific implementation of this standard. For the most part, with these Java standards, the actual implementation shouldn't matter too much, but there are always edge cases. In any event, Wink has historically been the preferred method of doing this on Domino by virtue of the fact that the ExtLib's DAS is built on it and so it ships with Domino, along with some IBM support code.

Setting up JAX-RS services is pretty similar to the addition of the XSP Library and theme provider: we'll need to create the Java classes and add some configuration to the plugin.xml. In this case, we'll do it in reverse, starting with the configuration first before diving into the servlet class.

There are actually two ways to proceed here. In the past, I've added servlets directly to the plugin.xml file. This works fine, but Toby's series took a different and more-interesting route: instead of having the plugin provide a servlet alone, it provides a web application which in turn contains a servlet. Doing it this way brings the configuration more in line with what you'd see in a non-OSGi Java web application.

To start with, we'll need to add dependencies on four more plugins. Open the META-INF/MANIFEST.MF file, go to the "Dependencies" tab, and add "org.apache.wink", "com.ibm.domino.osgi.core", "org.eclipse.equinox.http.registry", and "com.ibm.wink":

One word of caution: when adding "org.eclipse.equinox.http.registry", make sure to add version "1.0.100". Eclipse will likely have a newer version available as well, but that won't be there on Domino. Setting the version requirement too high will cause the plugin to fail to load.

While on this screen, add "lotus.domino" in the "Imported Packages" section of the page. OSGi has two mechanisms for specifying dependencies - until this point, we've used exclusively the "Required Plug-ins" route, which attaches a plugin by name as a dependency. The "Imported Packages" route is a little different: it says "I want this Java package available, but I don't care what plug-in provides it". Outside of the Domino world, the Packages route is very common, but for our needs we usually use Plug-ins to avoid some common nitty-gritty issues with the XPages stack.

The plugin.xml addition is pretty similar to the services we added before, but with some different tags. We're going to add another extension point to the list, along with a couple lines of configuration. With a minor cleanup to the previous lines, the result should look like this:

<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
	<extension point="com.ibm.commons.Extension">
		<service class="com.example.xsp.ExampleLibrary" type="com.ibm.xsp.Library"/>
	</extension>
	<extension point="com.ibm.commons.Extension">
		<service class="com.example.xsp.theme.ExampleStyleKitFactory" type="com.ibm.xsp.stylekit.StyleKitFactory"/>
	</extension>
	<extension point="org.eclipse.equinox.http.registry.servlets">
		<servlet alias="/examplerest" class="com.example.xsp.servlet.ExampleServlet">
			<init-param name="applicationConfigLocation" value="/WEB-INF/exampleapplication"/>
			<init-param name="propertiesLocation" value="/WEB-INF/exampleservlet.properties"/>
			<init-param name="DisableHttpMethodCheck" value="true"/>
		</servlet>
	</extension>
</plugin>

The code inside of the extension point is sort of a compressed version of the kind of servlet configuration you see in JEE's web.xml file.

This is actually an area where Toby's series and these instructions diverge. In his series, he used a different extension point - com.ibm.pvc.webcontainer.application - to provide his servlet as part of a full web application. That is a very interesting route as well, and opens the door to broader Java application development. Either one will get the job done, but the "just the servlet" route saves a bit of cognitive overhead for now.

Now, to create the files mentioned in the configuration. In the src/main/resources folder, create a folder named WEB-INF and add the exampleapplication and exampleservlet.properties files, both plain text:

The exampleservlet.properties file can be left blank for now. It's there to provide Wink-specific initialization properties that are useful as you get further into it, but are not needed now.

The exampleapplication file lists the classes that will provide the individual REST resources, one per line. For now, we'll just have one:

com.example.xsp.servlet.HelloWorldResource

With the configuration set up, it's time to create two Java classes. First, create a new class named "ExampleServlet" in the "com.example.xsp.servlet" package and have it extend "com.ibm.domino.services.AbstractRestServlet":

This class doesn't actually need to do anything other than exist for our purposes, but it can be a useful point for hooking in behavior that occurs for each servlet request. The actual code will go in the "HelloWorldResource" class mentioned in the application file. So create that class in the "com.example.xsp.servlet" package. Unlike the previous class, this one doesn't need to extend anything:

This one contains the core of our business logic (such as it is):

package com.example.xsp.servlet;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

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

import com.ibm.commons.util.StringUtil;
import com.ibm.commons.util.io.json.JsonJavaObject;
import com.ibm.domino.osgi.core.context.ContextInfo;

@Path("/helloworld/{id}")
public class HelloWorldResource {
	
	@GET
	@Produces(MediaType.APPLICATION_JSON)
    public Response get(
    		@Context UriInfo context, @PathParam("id") String id, @QueryParam("echo") String echo
    		) throws NotesException {
    	JsonJavaObject json = new JsonJavaObject();
    	
    	json.put("payload", "Hello world");
    	Session session = ContextInfo.getUserSession();
    	if (session != null) {
    		json.put("userName", session.getEffectiveUserName());
    	}
    	Database database = ContextInfo.getUserDatabase();
    	if(database != null) {
    		json.put("databaseFilePath", database.getFilePath());
    	}
    	if(StringUtil.isNotEmpty(echo)) {
    		json.put("echo", echo);
    	}
    	if(StringUtil.isNotEmpty(id)) {
    		json.put("id", id);
    	}
    	
    	return Response.ok(json.toString()).build();
    }
}

There are quite a few things going on here! Let's start with the JAX-RS annotations. JAX-RS, like other "new-era" Java APIs, using annotations heavily in order to put configuration inline and cut down on the number and size of the previous giant blobs of XML. They look a bit unusual if you haven't encountered Java annotations before, but their intent here is hopefully clear.

At the top, the @Path("/helloworld/{id}") annotation indicates that this class is hooked into "/helloworld" within our servlet, and moreover also expects a path parameter that it will refer to as "id". Then, the @GET annotation indicates that our get method will respond to that HTTP method (the name of the method is arbitrary - it could be "foobar" and still work). The @Produces(MediaType.APPLICATION_JSON) annotation further indicates that the method will supply JSON. There are other ways to accomplish this if the method may return different content types, but we know it's always JSON here.

Inside the method signature, there's a bit more magic going on. We're never going to call this method in any code ourselves, but instead Wink is going to inspect it and its parameter list in order to provide what it requests. By annotating the first parameter with @Context, we're telling Wink that we want to have that populated with the context information for the servlet - this is similar to ExternalContext from XSP development. This object isn't actually used in the example (and could be removed), but I've found that it's handy to have it consistently there. The other two parameters use @PathParam("id") and @QueryParam("echo") to extract the "id" parameter we specified earlier as well as look for "echo=..." in the query string.

The method itself uses IBM's JSON library to produce a simple JSON object containing the information we passed in, as well as some Domino-specific stuff, using the class ContextInfo. This class is sort of like the equivalent of ExtLibUtil for Equinox servlets, at least when it comes to getting access to the current session and database. ExtLibUtil itself, though available, doesn't function properly in a servlet, and so we use this instead. The current session is straightforward enough and works like you'd expect, but the current database is interesting. When you register a servlet via this method, it becomes available not only via "yourserver.com/examplerest", but also within each NSF, like "yourserver.com/foo.nsf/examplerest". By default, that will just provide the same results, but, if your code uses ContextInfo.getCurrentDatabase(), you can get the database the URL is requesting. This is how DAS does its thing, and it allows you to write a servlet that can operate in a contextually-appropriate way in each database.

So phew! Now that this is all written, you can build your update site and deploy it to the server. After restarting HTTP, you should be able to go to a URL like "http://yourserver.com/foo.nsf/examplerest/helloworld/someid?echo=hey", and see a result like this:

{
	"echo": "Hey",
	"userName": "CN=Jesse Gallagher/O=IKSG",
	"id": "someid",
	"databaseFilePath": "foo.nsf",
	"payload": "Hello world"
}

To finish, it's time to commit the changes.

Next up, it will be time to start taking the plunge into Maven. Don't worry; it'll be fun!

That Java Thing, Part 11: Diagnostics

Tue Dec 01 08:43:45 EST 2015

Tags: java xpages
  1. Nov 02 2015 - That Java Thing, Part 1: The Java Problem in the Community
  2. Nov 03 2015 - That Java Thing, Part 2: Intro to OSGi
  3. Nov 04 2015 - That Java Thing, Part 3: Eclipse Prep
  4. Nov 05 2015 - That Java Thing, Part 4: Creating the Plugin
  5. Nov 06 2015 - That Java Thing, Part 5: Expanding the Plugin
  6. Nov 08 2015 - That Java Thing, Part 6: Creating the Feature and Update Site
  7. Nov 09 2015 - That Java Thing, Part 7: Adding a Managed Bean to the Plugin
  8. Nov 10 2015 - That Java Thing, Part 8: Source Bundles
  9. Nov 11 2015 - That Java Thing, Part 9: Expanding the Plugin - Jars
  10. Nov 12 2015 - That Java Thing, Part 10: Expanding the Plugin - Serving Resources
  11. Nov 16 2015 - That Java Thing, Interlude: Effective Java
  12. Dec 01 2015 - That Java Thing, Part 11: Diagnostics
  13. Dec 03 2015 - That Java Thing, Part 12: Expanding the Plugin - JAX-RS
  14. Feb 19 2016 - That Java Thing, Part 13: Introduction to Maven
  15. Feb 21 2016 - That Java Thing, Part 14: Maven Environment Setup
  16. Feb 22 2016 - That Java Thing, Part 15: Converting the Projects
  17. Feb 23 2016 - That Java Thing, Part 16: Maven Fallout
  18. Feb 26 2017 - That Java Thing, Part 17: My Current XPages Plug-in Dev Environment

Though my surprisingly-packed schedule the last few weeks caused a hiatus, it's time to retun to this series with a quick description of some of the diagnostic tools available to you when doing plugin development (outside of connecting the debugger, which I may do eventually).

The primary tool in your "what the heck is going on?" toolbox should be the XPages Log File Reader. This app does a wonderful job providing a web UI for the important diagnostic files you'll likely need to see during plugin development (or normal XPages development as well). Even if you have control over the server and could see the files on the filesystem, having them in one UI is invaluable. By looking through the available tabs and pages, you can usually track down some error message related to your problem. So, if you don't have this installed currently, make a point of adding it.

Your other best friend will be the oddly-named XPages Portable Command Guide. Its purpose isn't as immediately clear as Mastering XPages, but it contains valuable nuggets of wisdom. In particular, for the purposes of developing plugins, it describes how to use the OSGi console commands.

By running tell http osgi followed by an appropriate command on the Domino server console, you can find out a bunch of important diagnostic state information about installed plugins. Things are a little obtuse in there, but eventually you learn enough to glean what you'll need to know. There are three primary commands I use: ss, diag, and bundle.

The ss command allows you to see a status summary of plugins matching a given name prefix. So, for example, if you run it for "com.example", you should get a listing like this:

[1140:00D0-15DC] 12/01/2015 07:44:05 AM  Remote console command issued by Jesse Gallagher/IKSG: tell http osgi ss com.example
[0FA8:0002-0C54] 12/01/2015 07:44:05 AM  Framework is launched.
[0FA8:0002-0C54] 12/01/2015 07:44:05 AM  id State       Bundle
[0FA8:0002-0C54] 12/01/2015 07:44:05 AM  79 RESOLVED    com.example.xsp.plugin.source_1.0.0.201511121147
[0FA8:0002-0C54] 12/01/2015 07:44:05 AM  83 ACTIVE      com.example.xsp.plugin_1.0.0.201511121147

The "State" column's values are states from the OSGi lifecycle, and it's worthwhile to at least read the list on that page. The gist of it is that "INSTALLED" is bad (though one step better than not being listed at all), while "RESOLVED" means "working but not yet activated", "<<LAZY>> means it is waiting to be used (like an XSP library), and "ACTIVE" means all is well (probably). This can be a very useful tool for seeing whether or not a plugin is properly on the server at all, and then whether there is an issue.

The diag command is the next level to drill down into when you want to get information about a misbehaving plugin. If you pass the full name of a plugin (not just the prefix), it will tell you if there are any missing dependencies. Running it on the example plugin, you should get something like this:

[1140:00D0-0D3C] 12/01/2015 08:18:41 AM  Remote console command issued by Jesse Gallagher/IKSG: tell http osgi diag com.example.xsp.plugin
[0FA8:0002-0C54] 12/01/2015 08:18:41 AM  initial@osginsf:osgi-dev.nsf/D3A3F506C30EFAB585257EFB005C40D1/com.example.xsp.plugin_1.0.0.201511121147.jar [83]
[0FA8:0002-0C54] 12/01/2015 08:18:41 AM    No unresolved constraints.

If, however, the plugin has a dependency that is unfulfilled (like this arbitrary one I added for this purpose), you'll get a different message:

[1140:00D0-140C] 12/01/2015 08:26:36 AM  Remote console command issued by Jesse Gallagher/IKSG: tell http osgi diag com.example.xsp.plugin
[0D50:0002-112C] 12/01/2015 08:26:36 AM  initial@osginsf:osgi-dev.nsf/EE0B673EB07C7AC985257F0E0049B171/com.example.xsp.plugin_1.0.0.201512010823.jar [12]
[0D50:0002-112C] 12/01/2015 08:26:36 AM    Direct constraints which are unresolved:
[0D50:0002-112C] 12/01/2015 08:26:36 AM      Missing required bundle org.eclipse.egit.mylyn.ui_4.0.3.

A slight variant of that situation is when there is an optional bundle that is missing, and this is usually fine. For example, it's common to see XPages plugins that have dependencies on "com.ibm.notes.java.api", which is the nicely-packaged version of the lotus.domino classes, but which is not actually on the server (since it uses Notes.jar directly). In that situation, the message is similar, but not a problem:

[1140:00D0-0B70] 12/01/2015 08:29:57 AM  Remote console command issued by Jesse Gallagher/IKSG: tell http osgi diag com.example.xsp.plugin
[0E18:0002-15A4] 12/01/2015 08:29:57 AM  initial@osginsf:osgi-dev.nsf/FB1CC2377927985185257F0E0049F6E8/com.example.xsp.plugin_1.0.0.201512010827.jar [12]
[0E18:0002-15A4] 12/01/2015 08:29:57 AM    Direct constraints which are unresolved:
[0E18:0002-15A4] 12/01/2015 08:29:57 AM      Missing optionally required bundle com.ibm.notes.java.api_9.0.1.

The final command is much more verbose and usually the least useful: bundle. What this does is to spit out a feed of pertinent information about the plugin. The first section of it looks like this:

[1140:00D0-0AF0] 12/01/2015 08:32:10 AM  Remote console command issued by Jesse Gallagher/IKSG: tell http osgi bundle com.example.xsp.plugin
[0E18:0002-15A4] 12/01/2015 08:32:11 AM  initial@osginsf:osgi-dev.nsf/FB1CC2377927985185257F0E0049F6E8/com.example.xsp.plugin_1.0.0.201512010827.jar [12]
[0E18:0002-15A4] 12/01/2015 08:32:11 AM    Id=12, Status=<<LAZY>>    Data Root=C:\Program Files\IBM\Domino\data\domino\workspace\.config\org.eclipse.osgi\bundles\12\data
[0E18:0002-15A4] 12/01/2015 08:32:11 AM    No registered services.
[0E18:0002-15A4] 12/01/2015 08:32:11 AM    No services in use.
[0E18:0002-15A4] 12/01/2015 08:32:11 AM    Exported packages
[0E18:0002-15A4] 12/01/2015 08:32:11 AM      com.example.xsp.beans; version="0.0.0"[exported]
[0E18:0002-15A4] 12/01/2015 08:32:11 AM      org.apache.commons.lang3; version="0.0.0"[exported]
[0E18:0002-15A4] 12/01/2015 08:32:11 AM      org.apache.commons.lang3.builder; version="0.0.0"[exported]
[0E18:0002-15A4] 12/01/2015 08:32:11 AM      org.apache.commons.lang3.concurrent; version="0.0.0"[exported]
[0E18:0002-15A4] 12/01/2015 08:32:11 AM      org.apache.commons.lang3.event; version="0.0.0"[exported]
[0E18:0002-15A4] 12/01/2015 08:32:11 AM      org.apache.commons.lang3.exception; version="0.0.0"[exported]
[0E18:0002-15A4] 12/01/2015 08:32:11 AM      org.apache.commons.lang3.math; version="0.0.0"[exported]
[0E18:0002-15A4] 12/01/2015 08:32:11 AM      org.apache.commons.lang3.mutable; version="0.0.0"[exported]
[0E18:0002-15A4] 12/01/2015 08:32:11 AM      org.apache.commons.lang3.reflect; version="0.0.0"[exported]
[0E18:0002-15A4] 12/01/2015 08:32:11 AM      org.apache.commons.lang3.text; version="0.0.0"[exported]
[0E18:0002-15A4] 12/01/2015 08:32:11 AM      org.apache.commons.lang3.text.translate; version="0.0.0"[exported]
[0E18:0002-15A4] 12/01/2015 08:32:11 AM      org.apache.commons.lang3.time; version="0.0.0"[exported]
[0E18:0002-15A4] 12/01/2015 08:32:11 AM      org.apache.commons.lang3.tuple; version="0.0.0"[exported]
[0E18:0002-15A4] 12/01/2015 08:32:11 AM    Imported packages
[0E18:0002-15A4] 12/01/2015 08:32:11 AM      org.osgi.framework; version="1.4.0"<System Bundle [0]>
[0E18:0002-15A4] 12/01/2015 08:32:11 AM      com.ibm.designer.runtime; version="0.0.0"<update@../../shared/eclipse/plugins/com.ibm.xsp.core_9.0.1.20150605-1000/ [256]>

The "Exported packages" part is usually the most useful: it lets you ensure that the packages you think are being exported are indeed being exported. This won't tell you for sure that all of the classes in those packages are present and working, but it's a start.

The "Imported packages" section, which can run for hundreds of lines, is very verbose, but is potentially useful if you want to track down why code in your plugin that interacts with the outside is misbehaving. Perhaps it hasn't found the right dependent package at runtime, or perhaps the version of the other plugin it's using is not what you expect.

One word of warning about the bundle command: it can very easily display more information than Administrator's remote console will show you, so it's useful to look at the server console directly or the logs for everything. Fortunately, the last couple of lines relate to permission setup, which is not usually useful for this need.


These commands so far have applied to the Domino server, but they also apply to Notes/Designer. To see the OSGi console, though, you need to launch Notes specially, with "-RPARAMS -console" in the command line. I keep a second shortcut on my Start menu around for this purpose. When you launch it, there will be quite a bit of noise in there, since Eclipse is a very chatty thing, but it can be invaluable in a pinch. When using this console, since it's the OSGi console directly and not routed through a server task, you can drop the tell http osgi prefix to the commands and just do things like ss com.example directly.

The client also has useful diagnostic logs, though there's no handy XPages application to go along with it (unless it happens to run in the local web preview, I guess). Instead, if you go to Help → Support in Designer, you have a couple options to view log and trace information. If you're working on a plugin and don't see it in the Xsp Properties page list, this is the place to check.


In the next couple of posts, we'll add a little more code to the plugin, diving into using JAX-RS to serve web services, before marching towards Maven-ization.

That Java Thing, Interlude: Effective Java

Mon Nov 16 08:15:53 EST 2015

Tags: java
  1. Nov 02 2015 - That Java Thing, Part 1: The Java Problem in the Community
  2. Nov 03 2015 - That Java Thing, Part 2: Intro to OSGi
  3. Nov 04 2015 - That Java Thing, Part 3: Eclipse Prep
  4. Nov 05 2015 - That Java Thing, Part 4: Creating the Plugin
  5. Nov 06 2015 - That Java Thing, Part 5: Expanding the Plugin
  6. Nov 08 2015 - That Java Thing, Part 6: Creating the Feature and Update Site
  7. Nov 09 2015 - That Java Thing, Part 7: Adding a Managed Bean to the Plugin
  8. Nov 10 2015 - That Java Thing, Part 8: Source Bundles
  9. Nov 11 2015 - That Java Thing, Part 9: Expanding the Plugin - Jars
  10. Nov 12 2015 - That Java Thing, Part 10: Expanding the Plugin - Serving Resources
  11. Nov 16 2015 - That Java Thing, Interlude: Effective Java
  12. Dec 01 2015 - That Java Thing, Part 11: Diagnostics
  13. Dec 03 2015 - That Java Thing, Part 12: Expanding the Plugin - JAX-RS
  14. Feb 19 2016 - That Java Thing, Part 13: Introduction to Maven
  15. Feb 21 2016 - That Java Thing, Part 14: Maven Environment Setup
  16. Feb 22 2016 - That Java Thing, Part 15: Converting the Projects
  17. Feb 23 2016 - That Java Thing, Part 16: Maven Fallout
  18. Feb 26 2017 - That Java Thing, Part 17: My Current XPages Plug-in Dev Environment

While taking a short breather in my continuing Java series, I think that now is a good time to reiterate my advice for all Domino developers to read Effective Java. It's probably not the best way to learn Java from scratch, but it's an invaluable tour through tons of important Java concepts. Even if you don't use most of the knowledge immediately, reading every section will help immerse you in the language and give you a better appreciation for its texture, which is one of the most important aspects of being a better programmer.

There is one caveat, though, when it comes to serialization. The serialization chapter in the book, though characteristically thorough and accurate, paints a much more dire and complicated picture of serialization than we as XPages developers usually have to worry about. It focuses on long-term storage of serialized objects - say, on the filesystem as a data format - whereas most of it going on in an XPages app is to make sure that your managed beans and data contexts don't throw exceptions when you do a partial refresh. Though we do run into it a bit when storing Java objects in Domino documents with MIMEBean or ODA, a managed bean class can get away with just "implements Serializable" attached and not a second thought.

Now go, make haste to Amazon and purchase the book!

That Java Thing, Part 10: Expanding the Plugin - Serving Resources

Thu Nov 12 12:02:24 EST 2015

Tags: java xpages
  1. Nov 02 2015 - That Java Thing, Part 1: The Java Problem in the Community
  2. Nov 03 2015 - That Java Thing, Part 2: Intro to OSGi
  3. Nov 04 2015 - That Java Thing, Part 3: Eclipse Prep
  4. Nov 05 2015 - That Java Thing, Part 4: Creating the Plugin
  5. Nov 06 2015 - That Java Thing, Part 5: Expanding the Plugin
  6. Nov 08 2015 - That Java Thing, Part 6: Creating the Feature and Update Site
  7. Nov 09 2015 - That Java Thing, Part 7: Adding a Managed Bean to the Plugin
  8. Nov 10 2015 - That Java Thing, Part 8: Source Bundles
  9. Nov 11 2015 - That Java Thing, Part 9: Expanding the Plugin - Jars
  10. Nov 12 2015 - That Java Thing, Part 10: Expanding the Plugin - Serving Resources
  11. Nov 16 2015 - That Java Thing, Interlude: Effective Java
  12. Dec 01 2015 - That Java Thing, Part 11: Diagnostics
  13. Dec 03 2015 - That Java Thing, Part 12: Expanding the Plugin - JAX-RS
  14. Feb 19 2016 - That Java Thing, Part 13: Introduction to Maven
  15. Feb 21 2016 - That Java Thing, Part 14: Maven Environment Setup
  16. Feb 22 2016 - That Java Thing, Part 15: Converting the Projects
  17. Feb 23 2016 - That Java Thing, Part 16: Maven Fallout
  18. Feb 26 2017 - That Java Thing, Part 17: My Current XPages Plug-in Dev Environment

After sharing code, one of the handiest uses of a plugin is sharing web resources - stylesheets, JavaScript files, and so forth. This process is similar to the last couple steps in that, though it is not very complicated on the whole, it's pretty non-obvious how to get around to doing it.

To start with, we'll create some resources to serve up. Expand the src/main/resources folder in your project (it will be slightly more useful to use the "normal" folder version and not the source folder with the brown package icon, due to Eclipse's UI) and go to New → Other, then pick "Folder" within "General". Name the folder "web":

The create within it a folder named "example", and within that folders named "css" and "js":

Then, populate it with some files - I created a basic stylesheet named "style.css" and a JavaScript file name "script.js" and placed them within the css and js folders, respectively. It doesn't matter what they contain, as long as it's something you can test later; you could also drag in any files you have from the filesystem.

As a side note, as I did this, creating folders within the hierarchy, Eclipse got a bit wonky about refreshing the list, presumably because of the combined source/normal folder nature of src/main/resources. This inconvenience is a concession to the way m2e, the Maven integrator for Eclipse, will act later - it makes this folder a source folder automatically, so we may as well get used to it.

Next, create a theme file within src/main/resources directly (which is to say, not in the web folder) and name it "example.theme". When you create it, Eclipse will likely have trouble opening up an editor: it will try to use one from the OS, which will almost definitely be invalid. Instead, right-click the file and choose Open With → Text Editor:

Set its contents to:

<theme extends="webstandard" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="platform:/plugin/com.ibm.designer.domino.stylekits/schema/stylekit.xsd" >
	<resources>
		<styleSheet href="/.ibmxspres/.extlib/example/css/style.css"/>
		<script clientSide="true" src="/.ibmxspres/.extlib/example/js/script.js"/>
	</resources>
</theme>

Those paths look so weird because we'll be piggybacking on the ExtLib's resource-provider system.

Now, back to Java to create the classes that will provide these resources to the platform. Because it's less terrifying, we'll start with the StyleKitFactory, which provides the theme to the server. Right-click on src/main/java and create a new Java class:

  • Set its package to "com.example.xsp.theme"
  • Set its name to "ExampleStyleKitFactory"
  • Add two interfaces: com.ibm.xsp.stylekit.StyleKitFactory and com.ibm.xsp.stylekit.StyleKitListFactory. The former will provide the theme to the server, while the latter will tell Designer about the name it can use

Set the class contents to this:

package com.example.xsp.theme;

import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

import com.ibm.xsp.stylekit.StyleKitFactory;
import com.ibm.xsp.stylekit.StyleKitListFactory;

public class ExampleStyleKitFactory implements StyleKitFactory, StyleKitListFactory {

	private static final String[] THEMES = {
		"example" //$NON-NLS-1$
	};
	private static final List<String> THEMES_LIST = Arrays.asList(THEMES);
	
	@Override
	public String[] getThemeIds() {
		return THEMES;
	}

	@Override
	public InputStream getThemeAsStream(String themeId, int scope) {
		if (scope == StyleKitFactory.STYLEKIT_GLOBAL) {
			if (THEMES_LIST.contains(themeId)) {
				return getThemeFromBundle(themeId + ".theme"); //$NON-NLS-1$
			}
		}
		return null;
	}

	@Override
	public InputStream getThemeFragmentAsStream(String themeId, int scope) {
		return null;
	}
	
	private InputStream getThemeFromBundle(final String fileName) {
		ClassLoader cl = getClass().getClassLoader();
		return cl.getResourceAsStream(fileName);
	}
}

There's quite a bit going on here! Some of it is due to the nature of the task and some of it comes from my own built-up habits that come in handy as the class grows. Towards the top of the class, THEMES contains an array of theme names known by the plugin - in this case, just the one, but it's good to have this standardized. THEMES_LIST contains a List wrapper around that array for programmatic convenience later.

The getThemeIds method is from StyleKitListFactory and is used by Designer to generate its list of available themes in the GUI. Implementing this interface isn't required, but it's a cross-the-Ts sort of thing.

The remaining methods implement StyleKitFactory, which provides the server with the theme data itself. getThemeAsStream is called by the runtime when it's searching for a theme requested by the app, so it contains a couple checks to make sure that the request is indeed intended for a plugin-based (global) theme with a name that this plugin knows about. It then uses a small utility method, getThemeFromBundle, to get the theme as an InputStream from the plugin's internal filesystem. Technically, this could be anything - it could construct the theme on the fly, fetch it from a URL, or get it from anywhere else, as long as it returns an InputStream, but this version is the most common.

getThemeFragmentAsStream is an interesting beast. The Extension Library uses it to hook in extra theme info for its Bootstrap themes on the fly. This is presumably useful for ad-hoc theme hierarchies and avoiding the theme-inheritance cap, but we don't have any need for it here.

Finally, the "$NON-NLS-1$" comment business is because I've developed a habit of enabling Eclipse's translated-strings warnings - the comments denote to the IDE that the hard-coded strings on those lines are not intended to be translated. You can ignore those if you so desire.

Moving on, now it's time to set up our resource provider, which will serve up the web resources. Before that, we'll take a minor detour back to the Activator class to fix up a method signature. Add "public" to the getContext method:

public static BundleContext getContext() {
	return context;
}

Now, we're going to implement the resource-loading class, modeled after the one from Bootstrap4XPages. Create a new class:

  • Set its package to "com.example.xsp.minifier"
  • Set its name to "ExampleLoader"
  • Set its superclass to com.ibm.xsp.extlib.minifier.ExtLibLoaderExtension

This class will have three methods of importance to us: getOSGiBundle (which is obligatory), loadCSSShortcuts, and getResourceURL:

package com.example.xsp.minifier;

import java.net.URL;

import javax.servlet.http.HttpServletRequest;

import org.osgi.framework.Bundle;

import com.example.xsp.Activator;
import com.ibm.commons.util.DoubleMap;
import com.ibm.xsp.extlib.minifier.ExtLibLoaderExtension;
import com.ibm.xsp.extlib.util.ExtLibUtil;

public class ExampleLoader extends ExtLibLoaderExtension {
	
	private static final String[] LIBRARY_RESOURCE_NAMESPACES = {
		"example" //$NON-NLS-1$
	};

	@Override
	public Bundle getOSGiBundle() {
		return Activator.getContext().getBundle();
	}

	@Override
	public void loadCSSShortcuts(DoubleMap<String, String> aliases, DoubleMap<String, String> prefixes) {
		if(prefixes != null) {
			for(int i = 0; i < LIBRARY_RESOURCE_NAMESPACES.length; i++) {
				String namespace = LIBRARY_RESOURCE_NAMESPACES[i];
				prefixes.put("9T0a" + i, "/.ibmxspres/.extlib/" + namespace); //$NON-NLS-1$ //$NON-NLS-2$
			}
		}
	}
	
	@Override
	public URL getResourceURL(HttpServletRequest request, String name) {
		for(String namespace : LIBRARY_RESOURCE_NAMESPACES) {
			if(name.startsWith(namespace)) {
				String path = "/web/" + name; //$NON-NLS-1$
				return ExtLibUtil.getResourceURL(getOSGiBundle(), path);
			}
		}
		return null;
	}
}

Once again, there's a lot going on, but you can see some general similarities to the theme provider. The getOSGiBundle method makes use of the Activator.getContext method we just modified, while the other two methods use the same sort of "internal array of Strings" pattern as before to make it easy to add more entries to the list down the line.

Things are a little strange in the loadCSSShortcuts method. The goal of this method is to provide the shorthand codes used in the minified URLs for resources, and so each plugin should make sure to provide unique ones. However, I don't know of any coordinated enforcer class that does this "making sure" for us, so you kind of just have to make up a unique string of characters that should be unique. Here, I picked "9T0a" as the base, just because they usually look like that.

The getResourceURL method is a little simpler, and is the equivalent of the earlier getThemeAsStream - given the incoming request for a resource name, it checks to see if it falls within its bailiwick and, if so, uses a method to provide a URL to the server environment.

These methods, like the ones in the theme provider, can be generally used as a "just drop them in the plugin" sort of thing without modification. There are other potential tweaks you can make, but it's probably best to keep it simple.

There's two more steps to getting these classes working. The theme provider needs to be registered in the plugin.xml and the resource loader needs to be added in the Activator. First, for the theme provider. Add another extension point entry to the plugin.xml:

Your plugin.xml source should look something like:

<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
   <extension
         point="com.ibm.commons.Extension">
      <service
            class="com.example.xsp.ExampleLibrary"
            type="com.ibm.xsp.Library">
      </service>
   </extension>
   <extension
         point="com.ibm.commons.Extension">
      <service
            class="com.example.xsp.theme.ExampleStyleKitFactory"
            type="com.ibm.xsp.stylekit.StyleKitFactory">
      </service>
   </extension>

</plugin>

For the loader, open the Activator class and add a new line to the start method to add the resource loader to the ExtLib's bank. The class should now look like:

package com.example.xsp;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

import com.example.xsp.minifier.ExampleLoader;
import com.ibm.xsp.extlib.minifier.ExtLibLoaderExtension;

public class Activator implements BundleActivator {

	private static BundleContext context;
	
	public static final Logger log = Logger.getLogger(Activator.class.getPackage().getName());
	static {
		log.setLevel(Level.FINEST);
	}

	public static BundleContext getContext() {
		return context;
	}

	/*
	 * (non-Javadoc)
	 * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
	 */
	public void start(BundleContext bundleContext) throws Exception {
		Activator.context = bundleContext;
		
		if(log.isLoggable(Level.INFO)) {
			log.info("Starting Example XPages Library");
		}
		
		ExtLibLoaderExtension.getExtensions().add(new ExampleLoader());
	}

	/*
	 * (non-Javadoc)
	 * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
	 */
	public void stop(BundleContext bundleContext) throws Exception {
		Activator.context = null;
	}
}

(Despite appearances, the "start" method there is not actually commented out - its blue appearance is a bug in the JavaScript syntax highlighter used in this blog)

Now to see if all this work has paid off: build the update site and install it in Designer and Domino. When you relaunch Designer and open the "Xsp Properties" page of the NSF you're working with, you should see the "example" theme in the list of options (as long as you have a recent ExtLib release installed in Designer - the capability was added post-9.0.1):

Additionally, when you open the app on the web, you should see your CSS and JS files included. One final note about these resources: though the URLs in the theme start with "/.ibmxspres/.extlib/example", URLs references from non-XSP files (say, referencing font files from with CSS) should start instead with "/xsp/.ibmxspres/.extlib/example". The XPages runtime adds that "/xsp" when generating URLs for pages, but it doesn't do any such processing for static references.

So... that was more complicated than it seemed at the start! Still, once it's in place, it's all pretty much "set it and forget it" - in the future, you can add files and themes more easily, just adjusting the static arrays in the appropriate classes as necessary.

Finally, commit the changes and take a relaxing breath.

That Java Thing, Part 9: Expanding the Plugin - Jars

Wed Nov 11 07:50:14 EST 2015

Tags: java xpages
  1. Nov 02 2015 - That Java Thing, Part 1: The Java Problem in the Community
  2. Nov 03 2015 - That Java Thing, Part 2: Intro to OSGi
  3. Nov 04 2015 - That Java Thing, Part 3: Eclipse Prep
  4. Nov 05 2015 - That Java Thing, Part 4: Creating the Plugin
  5. Nov 06 2015 - That Java Thing, Part 5: Expanding the Plugin
  6. Nov 08 2015 - That Java Thing, Part 6: Creating the Feature and Update Site
  7. Nov 09 2015 - That Java Thing, Part 7: Adding a Managed Bean to the Plugin
  8. Nov 10 2015 - That Java Thing, Part 8: Source Bundles
  9. Nov 11 2015 - That Java Thing, Part 9: Expanding the Plugin - Jars
  10. Nov 12 2015 - That Java Thing, Part 10: Expanding the Plugin - Serving Resources
  11. Nov 16 2015 - That Java Thing, Interlude: Effective Java
  12. Dec 01 2015 - That Java Thing, Part 11: Diagnostics
  13. Dec 03 2015 - That Java Thing, Part 12: Expanding the Plugin - JAX-RS
  14. Feb 19 2016 - That Java Thing, Part 13: Introduction to Maven
  15. Feb 21 2016 - That Java Thing, Part 14: Maven Environment Setup
  16. Feb 22 2016 - That Java Thing, Part 15: Converting the Projects
  17. Feb 23 2016 - That Java Thing, Part 16: Maven Fallout
  18. Feb 26 2017 - That Java Thing, Part 17: My Current XPages Plug-in Dev Environment

So it appears that I once again forgot to commit my changes. Well, consider this a cautionary tale, but we can still salvage the situation a bit by committing the previous changes before embarking on an unrelated modification - it's that mixing of different changes that can cause trouble in a source control repository.

For today's post, we'll add a third-party Jar to our plugin in order to use it internally and provide it to external applications (and we'll cover why those are distinct things).

The Jar we'll use is Apache Commons Lang, because it's a nice simple case without complicated dependencies. Embedding it inside a plugin is actually not the ideal deployment strategy for this, since it already contains OSGi metadata, but this setup is often the most expedient.

To start, download the latest binary release from their site and extract the Zip. Now, in Eclipse, right-click the "com.example.xsp.plugin" project, choose New → Folder, and name it "lib". Then, right-click that new folder and choose "Import...":

Then, choose "File System" from within "General":

On the next pane, browse to the extracted folder and, within Eclipse's file-picker list, choose the binary Jar:

Next, we have to make sure that this Jar file gets included in the final build and is available to classes within the Jar. To do that, open the META-INF/MANIFEST.MF file and go to the "Runtime" tab. In the "Classpath" section, click "Add...", and find the newly-added file:

This does two things: it adds it to the plugin's internal classpath (specified in the MANIFEST.MF file) and, as a convenience in Eclipse, adds the file to build.properties (which controls what is included in the build). If you go to the "build.properties" tab, it should look something like this:

source.. = src/main/java/,\
           src/main/resources/
output.. = target/classes/
bin.includes = META-INF/,\
               .,\
               plugin.xml,\
               lib/commons-lang3-3.4.jar

You should also see the Jar within your project list - being at the top level with this "jar of bits" icon means that Eclipse knows that it is included in the project's classpath:

At this point, the Jar's classes are available for use inside the plugin itself, but not exposed to the outside world. So we could add a method like this to the ExampleBean class and then call it from an XPage:

/**
 * @return the Java home directory path according to Apache Commons Lang
 */
public String getJavaHome() {
	return org.apache.commons.lang3.SystemUtils.getJavaHome().getAbsolutePath();
}

As an aside, sometimes Designer doesn't quite obey the normal rules of OSGi visibility, but Domino does, so you can fall into a trap with packages that aren't marked as visible but are still accessible in Designer.

Sometimes we do want to make these classes visible to downstream users, such as when the point of including the Jar is to distribute a set of foundational libraries used across apps. To do this, go back to the MANIFEST.MF file's "Runtime" tab. Now, click the "Add..." in the "Exported Packages" section. You should see all of the Apache commons packages - select them and click OK:

Now, when you build and install this plugin, the classes will be directly available in your XPages applications. This can be a good way to take a handful of Jars you may have sitting around in your jvm/lib/ext folder and distribute them in a better way. It's not the best way, since it misses some OSGi advantages like source and Javadoc for the embedded classes, but it can be much simpler than doing it "right".

Now, commit the changes for today:

In the next post, we'll add some web resources to the plugin and make them available in a theme and able to participate in resource aggregation.

That Java Thing, Part 8: Source Bundles

Tue Nov 10 07:46:28 EST 2015

Tags: java xpages
  1. Nov 02 2015 - That Java Thing, Part 1: The Java Problem in the Community
  2. Nov 03 2015 - That Java Thing, Part 2: Intro to OSGi
  3. Nov 04 2015 - That Java Thing, Part 3: Eclipse Prep
  4. Nov 05 2015 - That Java Thing, Part 4: Creating the Plugin
  5. Nov 06 2015 - That Java Thing, Part 5: Expanding the Plugin
  6. Nov 08 2015 - That Java Thing, Part 6: Creating the Feature and Update Site
  7. Nov 09 2015 - That Java Thing, Part 7: Adding a Managed Bean to the Plugin
  8. Nov 10 2015 - That Java Thing, Part 8: Source Bundles
  9. Nov 11 2015 - That Java Thing, Part 9: Expanding the Plugin - Jars
  10. Nov 12 2015 - That Java Thing, Part 10: Expanding the Plugin - Serving Resources
  11. Nov 16 2015 - That Java Thing, Interlude: Effective Java
  12. Dec 01 2015 - That Java Thing, Part 11: Diagnostics
  13. Dec 03 2015 - That Java Thing, Part 12: Expanding the Plugin - JAX-RS
  14. Feb 19 2016 - That Java Thing, Part 13: Introduction to Maven
  15. Feb 21 2016 - That Java Thing, Part 14: Maven Environment Setup
  16. Feb 22 2016 - That Java Thing, Part 15: Converting the Projects
  17. Feb 23 2016 - That Java Thing, Part 16: Maven Fallout
  18. Feb 26 2017 - That Java Thing, Part 17: My Current XPages Plug-in Dev Environment

Before anything else today, Eric McCormick reminding that yesterday's post missed the final step: committing the changes. So, let's take care of that right now. On my side, since my Windows VM is also being accessed from the Mac side, it's worthwhile to add a .gitignore file to the root of the repo to keep out all the .DS_Store nonsense. GitHub's Java gitignore is a good start, though skipping the part about ignoring Jar files. In your text editor of choice, create a new file named ".gitignore" in the root directory of the Git repository, with this content:

._*
Thumbs.db
.DS_Store

*.class

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
#*.jar
*.war
*.ear

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

Then commit the files:

Now, on to today's topic: adding a source bundle. This is a small topic, but will be very useful as you work down the line. As it stands right now, even though you have access to the source code of your plugin, Designer doesn't, and that means that you don't have nice parameter names, inline Javadoc, or viewable source when working with your classes in an actual XPages application. Since XSP libraries are almost always open-source or internal-use-only, including a source bundle is the fastest way to achieve all of these.

The way to do this is pretty non-obvious, but not complex once you know what to do. The work will be done with a new feature project, so go to File → New → Other and then "Feature Project" within "Plug-in Development". This should be set up very similarly to the original feature:

  • Set the project name to "com.example.xsp.source.feature"
  • Override the location with a folder inside the Git repository
  • Pick a variant on your original feature name, so "Example XSP Library Source Feature"

Unlike before, however, we don't want to select any plugins on the next pane - instead, just hit "Finish". This feature is going to use some Eclipse trickery to automatically generate a "source" version of the existing feature. Once the project is created, open its feature.xml file and go to the "feature.xml" tab. On there, add a bit of XML to manually specify the non-existent source plugin (I've left out the "description", "copyright", and "license" blocks, which are required even if just stubs):

<?xml version="1.0" encoding="UTF-8"?>
<feature
      id="com.example.xsp.source.feature"
      label="Example XSP Library Source Feature"
      version="1.0.0.qualifier">

   <!-- *snip* -->

	<includes
         id="com.example.xsp.plugin.source"
         version="0.0.0"/>
</feature>

Then, go to the "build.properties" tab and add a second line to tell Eclipse's builder to generate the source plugin:

bin.includes = feature.xml
generate.feature@com.example.xsp.plugin.source = com.example.xsp.feature

Next, open the site.xml file in the update site project and add the new feature to the "Managing the Site" section:

Now, save and "Build All". Then, go back to Designer and install the latest version from the update site, which should now have a second feature listed:

When you install that and relaunch Notes/Designer, you'll be able to access the source of your plugin. To see this in action, open your application in Designer and then go to the "Package Explorer" view (you may have to add it via Window → Show Eclipse Views → Package Explorer). Find your NSF's project and expand the "Plug-in Dependencies" category. Towards the bottom, you should see the example plugin Jar file - expand that and double-click on one of the classes:

If all went well, you should see the source to the original class, instead of Eclipse's incomprehensible bytecode dump. Additionally, now, when you access these classes from Java code inside your NSF, you'll get improved inline help, with correct parameter names and Javadoc (if you actually write the Javadoc).

The next step will be another small but useful topic: adding some third-party Jars to your plugin, either to use internally or to expose to your XPages applications.

That Java Thing, Part 7: Adding a Managed Bean to the Plugin

Mon Nov 09 06:37:27 EST 2015

Tags: java xpages
  1. Nov 02 2015 - That Java Thing, Part 1: The Java Problem in the Community
  2. Nov 03 2015 - That Java Thing, Part 2: Intro to OSGi
  3. Nov 04 2015 - That Java Thing, Part 3: Eclipse Prep
  4. Nov 05 2015 - That Java Thing, Part 4: Creating the Plugin
  5. Nov 06 2015 - That Java Thing, Part 5: Expanding the Plugin
  6. Nov 08 2015 - That Java Thing, Part 6: Creating the Feature and Update Site
  7. Nov 09 2015 - That Java Thing, Part 7: Adding a Managed Bean to the Plugin
  8. Nov 10 2015 - That Java Thing, Part 8: Source Bundles
  9. Nov 11 2015 - That Java Thing, Part 9: Expanding the Plugin - Jars
  10. Nov 12 2015 - That Java Thing, Part 10: Expanding the Plugin - Serving Resources
  11. Nov 16 2015 - That Java Thing, Interlude: Effective Java
  12. Dec 01 2015 - That Java Thing, Part 11: Diagnostics
  13. Dec 03 2015 - That Java Thing, Part 12: Expanding the Plugin - JAX-RS
  14. Feb 19 2016 - That Java Thing, Part 13: Introduction to Maven
  15. Feb 21 2016 - That Java Thing, Part 14: Maven Environment Setup
  16. Feb 22 2016 - That Java Thing, Part 15: Converting the Projects
  17. Feb 23 2016 - That Java Thing, Part 16: Maven Fallout
  18. Feb 26 2017 - That Java Thing, Part 17: My Current XPages Plug-in Dev Environment

For today's step, we'll deal more with the meat of the task: putting your own code in the plugin. There are a lot of options for what a plugin can provide, ranging from just storing classes to be accessed from elsewhere to being full-on OSGi web applications. Adding a managed bean certainly falls on the simpler side of this spectrum, but it's also one of the most common uses. These steps should also be very familiar if you've created a managed bean in an XPages NSF before.

Before we get started, it will be useful later to access Extension Library code from this plugin, so there's a quick detour. In order to allow the plugin to "see" ExtLib code, the appropriate ExtLib plugin needs to be added as a dependency. Go to the MANIFEST.MF file's "Dependencies" tab and add com.ibm.xsp.extlib to the list:

Now, create a new class with an appropriate name by right-clicking on the src/main/java folder and selecting New → Class as before. Set its package to "com.example.xsp.beans" and its name to "ExampleBean". You don't have to name your bean classes with "Bean", but sometimes it makes sense. Make sure to add the java.io.Serializable interface via the "Add..." button in that section.

When you create the class, Eclipse will put a squiggly underline beneath the class name, indicating that it has a warning for you. If you hover your cursor over it (or hit Ctrl-1 while the cursor is on the line), you'll get more information and an option to fix it:

What this message is talking about has to do with serialization - the process of storing the Java object outside of memory and retrieving it later - and is something that would come up more in applications that intentionally store data long-term. For a thorough explanation of this and many other things, I can't recommend Effective Java enough. For our purposes as XPages developers, though, it's fine to pick the "default" option - we want more classes Serializable than other Java apps, but we care less about their long-term storage.

Next, add a basic "getter" method that returns a String we can see later, as well as a static method to retrieve the bean from the XPages environment from Java:

package com.example.xsp.beans;

import java.io.Serializable;

import javax.faces.context.FacesContext;

import com.ibm.xsp.extlib.util.ExtLibUtil;

public class ExampleBean implements Serializable {
	private static final long serialVersionUID = 1L;
	
	public static ExampleBean get() {
		return (ExampleBean)ExtLibUtil.resolveVariable(FacesContext.getCurrentInstance(), "exampleBean");
	}

	public String getFoo() {
		return "hello from " + getClass().getSimpleName();
	}
}

Now, in order to make this a managed bean, well have to add a local faces-config file. These files have the same format as the one in the NSF. There's another nod towards Maven here: we'll put the XML file in a separate "resources" folder in the project, which is where Maven expects to find this sort of thing. Right-click the project and choose New → Source Folder:

Name it "src/main/resources":

Right-click on this newly-created source folder and choose New → Other, and within the dialog choose "File" from the "General" section:

On the next screen, name the file "beans.xml" and click Finish:

Most likely, Eclipse will open the file on the "Design" tab, which is pretty useless for this - it's better to switch to the "Source" tab. In this file, fill in the basic faces-config structure and an entry for the managed bean:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
	<managed-bean>
		<managed-bean-name>exampleBean</managed-bean-name>
		<managed-bean-class>com.example.xsp.beans.ExampleBean</managed-bean-class>
		<managed-bean-scope>view</managed-bean-scope>
	</managed-bean>
</faces-config>

Now that we have this file, we have to tell the XPages runtime to actually use it as a faces-config file. To do that, go back to the ExampleLibrary class and implement the getFacesConfigFiles method, which lists the XML files from the plugin to contribute to the environment:

@Override
public String[] getFacesConfigFiles() {
	return new String[] {
			"beans.xml"
	};
}

The "@Override" bit above the method is indicating to the compiler that you are intentionally overriding a method declared in the superclass or interface - this is to help you ensure that you keep the method names correct for any code calling them.

Now, there are two minor remaining items to take care of before we deploy: exposing the class to Java code elsewhere and making sure the newly-created source folder is included in the build.

To do the former, open up the MANIFEST.MF file and go to the "Runtime" tab. Here, we can list the Java packages that should be available to code within the application (or other plugins). Since we'll want to have code that accesses the bean directly, add the "com.example.xsp.beans" package to the "Exported Packages" list:

This is important to do for any new package that is going to be accessed externally.

Next, go to the "build.properties" tab of the MANIFEST.MF editor (or open the build.properties file directly) and add the src/main/resources folder to the list of source folders:

source.. = src/main/java/,\
           src/main/resources/
output.. = target/classes
bin.includes = META-INF/,\
               .,\
               plugin.xml

The syntax of this file takes a little getting used to - the backslashes on many lines indicate that the next line should also be part of the same definition, but is split up for legibility.

With that, we're all set on this side. Head over to the site.xml file in the update site project and click "Build All" to build a new version of the plugin. Once that's done, go to the Update Site NSF for the server and import the new build, and then restart task http on the server console. Similarly, use the File → Application → Install routine in Designer to install the new version of the plugin there, and relaunch Notes.

Now that the bean is installed, you can test it out to make sure it's working by creating a new XPage in an app on the server with this content on the "Source" tab:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
	<xp:text value="#{exampleBean.foo}"/>
</xp:view>

When you launch the page in a browser, it should say "hello from ExampleBean". To try out the static get() method we created, change the XPage source to:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
	<xp:text><xp:this.value><![CDATA[#{javascript:
		"the bean says: " + com.example.xsp.beans.ExampleBean.get().getFoo()
	}]]></xp:this.value></xp:text>
</xp:view>

When you view that, you should see basically the same thing, prefixed with "the bean says: ". Whenever I make a change to test something that should have the same visual result, I like to toss in a small variation to make sure that my changes did, in fact, take effect.

For a lot of cases, this step may be all you need - many XPage libraries consist primarily as storehouses for Java classes and a few managed beans. The next couple steps are going to deal with some variations on this, plus some nice-to-haves.

That Java Thing, Part 6: Creating the Feature and Update Site

Sun Nov 08 10:45:08 EST 2015

Tags: java xpages
  1. Nov 02 2015 - That Java Thing, Part 1: The Java Problem in the Community
  2. Nov 03 2015 - That Java Thing, Part 2: Intro to OSGi
  3. Nov 04 2015 - That Java Thing, Part 3: Eclipse Prep
  4. Nov 05 2015 - That Java Thing, Part 4: Creating the Plugin
  5. Nov 06 2015 - That Java Thing, Part 5: Expanding the Plugin
  6. Nov 08 2015 - That Java Thing, Part 6: Creating the Feature and Update Site
  7. Nov 09 2015 - That Java Thing, Part 7: Adding a Managed Bean to the Plugin
  8. Nov 10 2015 - That Java Thing, Part 8: Source Bundles
  9. Nov 11 2015 - That Java Thing, Part 9: Expanding the Plugin - Jars
  10. Nov 12 2015 - That Java Thing, Part 10: Expanding the Plugin - Serving Resources
  11. Nov 16 2015 - That Java Thing, Interlude: Effective Java
  12. Dec 01 2015 - That Java Thing, Part 11: Diagnostics
  13. Dec 03 2015 - That Java Thing, Part 12: Expanding the Plugin - JAX-RS
  14. Feb 19 2016 - That Java Thing, Part 13: Introduction to Maven
  15. Feb 21 2016 - That Java Thing, Part 14: Maven Environment Setup
  16. Feb 22 2016 - That Java Thing, Part 15: Converting the Projects
  17. Feb 23 2016 - That Java Thing, Part 16: Maven Fallout
  18. Feb 26 2017 - That Java Thing, Part 17: My Current XPages Plug-in Dev Environment

The last post covered turning our nascent plugin into a proper XPages library, so now it's time to fill in the remaining pieces to get it installed.

To do that, we'll need a feature and an update site. The feature will point to our plugin - in a more-complicated setup, this could point to several related plugins, but we'll just need the one. The update site will do similarly, referencing the feature in a way that Eclipse-type platforms know about.

Go to File → New and choose "Feature Project" within "Plug-in Development":

Hit "Next" and fill out the feature details:

  • Set the project name to "com.example.xsp.feature"
  • Override the location with a folder inside the Git repository (remembering to include the feature name in the path, to avoid placing it in the top-level folder)
  • Pick a feature name, such as "Example XSP Library Feature"

Click "Next", find and check "com.example.xsp.plugin" in the list of plugins to include in the feature, and click "Finish":

That's about it for the feature. Next up: the update site. As before, go to File → New and choose "Update Site Project" within "Plug-in Development":

There's not much configuration to do for this one: just name it "com.example.xsp.update" and override its location to be within your Git repository:

Once the update site is created, click "Add Feature.." in the site.xml editor and add the new "com.example.xsp.feature" project:

One last optional step is to set up a category in the update site, which will help organization on the server. To do that, click "New Category" and give it a name and ID. Then, drag the feature in the list onto the category to add it to the group:

Now the update site definition is set up, and the next step is to click "Build All" to have Eclipse package your plugin inside the update site. That will create a number of files within your update site project, which are the actual artifacts we need:

Next is to install the library into Designer. Launch Notes, then Designer (the sequence is important to avoid a bug with NSF update sites, so I stick to this habit), and open Preferences. In there, go to the "Domino Designer" section and check "Enable Eclipse plug-in install":

Hit "OK" to close the preferences and then go to File → Application → Install. In the dialog that pops up (sometimes there's a lengthy delay before it does for some reason), select "Search for new features to install" and click "Next". On the next pane, choose "Add Folder Location..." and browse to the update site project:

The next dialog will prompt for a name, and for now the default will do. Click "OK" in this dialog and "Finish" in the main one. If all goes well, that should bring up another dialog after a couple of seconds, which will allow you to select the feature:

Click "Next" and then "Finish". As it installs, you'll get a prompt to install the unsigned plugin, so select "Install this plug-in" and click "OK". Designer will do its thing and then display a small toast window in the bottom right asking if you want to restart now. It at least used to be the case that clicking this didn't do a proper restart (presumably due to the Notes stuff), so I still avoid clicking it. Instead, close Notes and Designer, and then relaunch them (Notes first again).

Now, to see that everything is working, open an existing application in Designer (or make a new one), go to Application Configuration → Xsp Properties → Page Generation tab, and look for "com.example.xsp.library" in the "XPage Libraries" section:

If it's not there, something went wrong. Unfortunately, debugging this sort of thing can get hairy, so it'd probably be best to ask me or someone else knowledgable about plugins for assistance for now.

Assuming it did work, though: great! Next up is the installation on the server. This is a whole tutorial on its own, and fortunately IBM has done the job for me. The upshot of those instructions is:

  • Create a database on the server using the "Eclipse Update Site" template (updatesite.ntf) and clean up the ACL to something appropriate. To see the template, make sure to click "Show advanced templates".
  • Import com.example.xsp.update by using the "Import Local Update Site" option and pointing to its site.xml file
  • Set the notes.ini property "OSGI_HTTP_DYNAMIC_BUNDLES" on the server to point to the file name of that update site
  • Restart HTTP or the entire server

Once HTTP or the server is restarted, you can test to see if it's working by creating an application on the server that has the library checked in the Xsp Properties page as above, and then visiting an XPage in it with a browser. If it's working, it will load normally; otherwise, it will complain about the application relying on a missing library.

So that covers the build and installation cycle! There's one last change to make in the projects before we commit them to Git. The update site project produces tons of Jar files - two for every "Build All" click - and there's no need to check them into the repository. This is a job for a .gitignore file, which we'll put in the update site project because we don't want to ignore intentional Jars later - .gitignore files cascade hierarchically. Right-click on the update site project in Eclipse and choose New → File:

Name the file ".gitignore" (with the starting dot) and click "Finish". The file contents should be a single line:

*.jar

Now, go to commit the changes to the repository, and you should be able to see the projects and Git ignore file we created, but not the Jars:

Now that we have the library built and deployed to the server, the next step will be to actually make it useful, by adding some Java code and accessing it from the XPages application.

That Java Thing, Part 5: Expanding the Plugin

Fri Nov 06 08:48:55 EST 2015

Tags: java xpages
  1. Nov 02 2015 - That Java Thing, Part 1: The Java Problem in the Community
  2. Nov 03 2015 - That Java Thing, Part 2: Intro to OSGi
  3. Nov 04 2015 - That Java Thing, Part 3: Eclipse Prep
  4. Nov 05 2015 - That Java Thing, Part 4: Creating the Plugin
  5. Nov 06 2015 - That Java Thing, Part 5: Expanding the Plugin
  6. Nov 08 2015 - That Java Thing, Part 6: Creating the Feature and Update Site
  7. Nov 09 2015 - That Java Thing, Part 7: Adding a Managed Bean to the Plugin
  8. Nov 10 2015 - That Java Thing, Part 8: Source Bundles
  9. Nov 11 2015 - That Java Thing, Part 9: Expanding the Plugin - Jars
  10. Nov 12 2015 - That Java Thing, Part 10: Expanding the Plugin - Serving Resources
  11. Nov 16 2015 - That Java Thing, Interlude: Effective Java
  12. Dec 01 2015 - That Java Thing, Part 11: Diagnostics
  13. Dec 03 2015 - That Java Thing, Part 12: Expanding the Plugin - JAX-RS
  14. Feb 19 2016 - That Java Thing, Part 13: Introduction to Maven
  15. Feb 21 2016 - That Java Thing, Part 14: Maven Environment Setup
  16. Feb 22 2016 - That Java Thing, Part 15: Converting the Projects
  17. Feb 23 2016 - That Java Thing, Part 16: Maven Fallout
  18. Feb 26 2017 - That Java Thing, Part 17: My Current XPages Plug-in Dev Environment

In the last post, we created an empty plugin project for our XPages library. Today, we'll flesh that plugin out a bit and add the actual XSP hooks.

The first step is to start filling in some details in the plugin's Manifest file, which is the core description of what the plugin does and what it requires in its environment. To begin with, open the MANIFEST.MF file in the META-INF folder of your project and check the "This plug-in is a singleton" checkbox:

During one of the steps later, Eclipse would have yelled at us to do that anyway. This checkbox means that the plugin expects to be the only one of its kind active on the server. This is because it will contribute to the XSP Library extension point, and it should prevent duplicate contributions of the same library from different versions.

Next, go to the Dependencies tab of the Manifest editor, click "Add..." in the "Required Plug-ins" section on the left, and add "com.ibm.xsp.core":

Do the same with "com.ibm.commons":

Now, save and close the Manifest. The next step is to fix up a minor problem I forgot about in the last post: the base package name. By default, the base package is named after the plugin project, which means it now contains a redundant "plugin" part. That's not a problem per se, but it's not needed, and this will be a good introduction to Eclipse refactoring.

Expand the "src/main/java" (or "src" if you left it as the default) package folder within the project, then right-click on "com.example.xsp.plugin" and choose "Refactor" → "Rename...". Change the name to "com.example.xsp", make sure "Update references" is checked, check "Update fully qualified names in non-Java text files", and click "Preview". This should list a couple changes - Eclipse looks for references to the class in both Java and non-Java files to try to cover all of the bases. In a larger project, there may still be lingering references elsewhere, but in this case it does everything for us. Click "OK".

It's possible that there may be a small detour at this point. On my machine, I noticed a strange problem in Eclipse after making this change: it set the output folder of the project to a nonsense directory within the source. To make sure this isn't happening, double-click on build.properties and go to the "build.properties" tab. Make sure the "output.." line reads output.. = target/classes (or output.. = bin if you left the defaults). This is the file that controls many of the compilation settings for the plugin, and this specifically specifies the location for the compiled Java classes.

Now, time to expand the Activator a bit. Expand "com.example.xsp" and open the "Activator.java" file. The Activator class is an optional but there-by-default class that provides a couple hooks during the lifecycle of the plugin. For XSP libraries, there's usually not too much going on here, but it can serve as a useful coordinating and debugging point.

One such potential use is a centralized logger configuration. At least for now, we'll just use the basic JVM logging classes, since "proper" OSGi logging is a whole thing. With the addition of a logging property and initialization message, the Activator looks like this:

package com.example.xsp;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class Activator implements BundleActivator {

	private static BundleContext context;
	
	public static final Logger log = Logger.getLogger(Activator.class.getPackage().getName());
	static {
		log.setLevel(Level.FINEST);
	}

	static BundleContext getContext() {
		return context;
	}

	/*
	 * (non-Javadoc)
	 * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
	 */
	public void start(BundleContext bundleContext) throws Exception {
		Activator.context = bundleContext;
		
		if(log.isLoggable(Level.INFO)) {
			log.info("Starting Example XPages Library");
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
	 */
	public void stop(BundleContext bundleContext) throws Exception {
		Activator.context = null;
	}

}

The static block that sets the log level is a good trick to know about: that block executes once, when the class is first loaded. It's distinct from a contructor, which turns each time a new object of the class is created. It may help to think of the static portions of a class (the properties, methods, and this block) as a sort of special singleton version of the class created by the runtime. This sort of thing is non-obvious to new Java developers, but it starts to make sense after you do it for a while.

With the Activator in place, the next step is the actual XPages library class, which provides the specific details about the plugin's XSP interactions. Right-click on the "com.example.xsp" package and choose "New" → "Class" (if "Class" doesn't appear in that list, choose "Other" and then find "Class" within the "Java" folder). In the "New Java Class" dialog, click "Browse..." in the "Superclass" line around the middle, and look for the class AbstractXspLibrary:

Set the class name to "Example Library" and leave everything else as the defaults:

When creating the class, it will fill in a single required method: getLibraryId(). This method's job is to return a string that should be unique across XPages libraries, and which will show up as the library's identifier in Designer. By convention, this is related to the Java reverse-DNS name you're using, plus a suffix like ".library":

@Override
public String getLibraryId() {
	return Activator.class.getPackage().getName() + ".library";
}

There are a number of other methods, though, that are worth overriding in a normal plugin, related to the plugin setup, the other XSP libraries it depends on, and its versioning. This is a reasonable baseline for a modern XPages library that will use the Extension Library:

package com.example.xsp;

import java.util.logging.Level;
import java.util.logging.Logger;

import com.ibm.xsp.library.AbstractXspLibrary;

public class ExampleLibrary extends AbstractXspLibrary {
	
	private static final Logger log = Activator.log;
	
	static {
		if(log.isLoggable(Level.FINE)) {
			log.fine(ExampleLibrary.class.getName() + " loaded");
		}
	}

	@Override
	public String getLibraryId() {
		return Activator.class.getPackage().getName() + ".library";
	}

	@Override
	public String getPluginId() {
		return Activator.getContext().getBundle().getSymbolicName();
	}
	
	@Override
	public String getTagVersion() {
		return "1.0.0";
	}
	
	@Override
	public String[] getDependencies() {
		return new String[] {
				"com.ibm.xsp.core.library",
				"com.ibm.xsp.extsn.library",
				"com.ibm.xsp.domino.library",
				"com.ibm.xsp.designer.library",
				"com.ibm.xsp.extlib.library"
		};
	}
	
	@Override
	public boolean isGlobalScope() {
		return false;
	}
}

With that class in place, there's one final step for making this plugin a proper XSP library: the extension point. Extension points are how the XPages runtime knows which plugins provide libraries like this. Open the MANIFEST.MF file again and click on "Extensions" on the right side of the first page:

Eclipse will ask you if you want to show the hidden Extensions panel, which you do. On that tab, click "Add..." and choose "com.ibm.commons.Extension":

Once added, there will be two fields on the right side. In "type", enter "com.ibm.xsp.Library". In "class", click "Browse" and search for the name of the library created earlier, "com.example.xsp.ExampleLibrary". Once this is added, you should be able to click on the "plugin.xml" tab in the editor and see something like this:

<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
   <extension
         point="com.ibm.commons.Extension">
      <service
            class="com.example.xsp.ExampleLibrary"
            type="com.ibm.xsp.Library">
      </service>
   </extension>

</plugin>

There will also be a file named "plugin.xml" in your project.

After all that, we have a functional basis for our plugin. There are definitely a lot of things to remember in this process, but they start to make a sort of sense the more you do it.

With that set, commit your changes, where you can see the moved Activator file and the addition of the Library stuff:

In the next post, we'll create the feature and update-site projects needed to actually install this plugin in Designer and Domino.

That Java Thing, Part 4: Creating the Plugin

Thu Nov 05 09:38:56 EST 2015

Tags: java xpages
  1. Nov 02 2015 - That Java Thing, Part 1: The Java Problem in the Community
  2. Nov 03 2015 - That Java Thing, Part 2: Intro to OSGi
  3. Nov 04 2015 - That Java Thing, Part 3: Eclipse Prep
  4. Nov 05 2015 - That Java Thing, Part 4: Creating the Plugin
  5. Nov 06 2015 - That Java Thing, Part 5: Expanding the Plugin
  6. Nov 08 2015 - That Java Thing, Part 6: Creating the Feature and Update Site
  7. Nov 09 2015 - That Java Thing, Part 7: Adding a Managed Bean to the Plugin
  8. Nov 10 2015 - That Java Thing, Part 8: Source Bundles
  9. Nov 11 2015 - That Java Thing, Part 9: Expanding the Plugin - Jars
  10. Nov 12 2015 - That Java Thing, Part 10: Expanding the Plugin - Serving Resources
  11. Nov 16 2015 - That Java Thing, Interlude: Effective Java
  12. Dec 01 2015 - That Java Thing, Part 11: Diagnostics
  13. Dec 03 2015 - That Java Thing, Part 12: Expanding the Plugin - JAX-RS
  14. Feb 19 2016 - That Java Thing, Part 13: Introduction to Maven
  15. Feb 21 2016 - That Java Thing, Part 14: Maven Environment Setup
  16. Feb 22 2016 - That Java Thing, Part 15: Converting the Projects
  17. Feb 23 2016 - That Java Thing, Part 16: Maven Fallout
  18. Feb 26 2017 - That Java Thing, Part 17: My Current XPages Plug-in Dev Environment

To make a basic XPages library, we'll need to create the trio of OSGi projects: the plugin, the feature, and the update site. For a long time, the XSP Starter Kit has been a great go-to starting point for this sort of thing. It definitely covers almost all of the potential ground, but it can be a bit overkill when you just want to put some classes in a shared place. So, for this exercise, we'll start from scratch.

But before we do that, we should create a local Git repository first. This isn't required, but it's a good idea, and Git repositories are so "cheap", technically, that there's no reason not to. There are a number of ways to do it - you can create and manage them via the command line, via a dedicated tool like SourceTree, or via the embedded Git client in Eclipse. We'll do the last one here.

To work with Git repositories, first add the Git Repositories view (Eclipse's "view" refers to the panes you see in the window) to your Eclipse UI by going to Windows → Show View → Other...:

Once you add it, you can click on "Create a new local Git repository" in the pane - I suggest creating a folder beneath the "git" folder in your home directory, for organizational purposes:

Now, on to creating an actual project. To do that, go to File → New → Project... and find "Plug-in Project" inside "Plug-in Development":

In the form that shows up after you hit Next, fill in some project details:

  • Set the project name to "com.example.xsp.plugin"
  • Override the location with a folder inside the Git repository you created earlier (make sure to include the plugin name in the path, rather than using the top level of the Git repo)
  • Change the source folder to "src/main/java" and output folder to "target/classes". These are nods towards Maven that aren't strictly required, but are a "may as well" thing.
  • Set the target platform to "an OSGi framework" → "Equinox"

On the next page, the only change needed is to set the Execution Environment to "JavaSE-1.6" (at least until Domino gets a newer JVM):

Then, click "Finish". It will ask you if you want to switch to the "Plug-in Development" perspective - "perspectives" are an Eclipse term for groupings+layouts of views for different purposes. You can choose either Yes or No, since the other perspective is very similar to the default J2EE perspective. If you choose "Yes", you'll have to re-add the Git Repositories view as above.

Once you've created the project, it's time to check it in to Git. Right-click on the Git repository in the Git Repositories view and choose "Commit...". The first time you do this, it will prompt you for a name and email address, which are pretty arbitrary, but it's good to keep them consistent across your Git presences. On the commit page, provide a useful message and click the "Select All" button (the middle one in the top right of the "Files" section) to include all of the newly-created files, and hit "Commit":

Now that the plugin's skeleton is in place, the next post will cover some details about it and how to turn it into an XSP Library.