Posts for thread "That Java Thing", by date ascending

That Java Thing, Part 1: The Java Problem in the Community

  • Nov 2, 2015

Java has been a bugbear for the Domino community for a long time. Though Notes and Domino have presented a top-to-bottom (mostly) Java environment for years, the monumental inertia of the corporate development community, the platform's tortured history of insufficiently-fleshed-out Java hooks, and IBM's "pay no attention to that man behind the curtain" pitch with regard to the language created an environment where Java is often something for "other developers".

XPages represented a tremendous leap forward for development on the platform, ditching the completely-unique bizarre forms-based web-dev experience in favor of something much closer to modern web development. At the time, the unstructured, SSJS-heavy façade presented by Designer made a sort of sense: the switch to XPages was already a huge change for Domino developers, and making XPage development look sort of like classic Notes/Domino development was a spoonful of sugar with the medicine.

But the critical flaw of XPages development has never been fixed: there's no smooth path from "hello, world" to a well-structured complex application. SSJS, as implemented in XPages, is unsuitable to the task, while Designer's presentation of the Java layer ranges from "non-obvious" to "hostile". Still, IBM did a good job of presenting the next major tier for XPages developers: writing Java first in the NSF and then in OSGi plugins. The problem has always been in figuring out how to get there.

This hurdle started out as an inconvenience just for those who wanted to figure out how the machine worked, but has grown into a significant problem for all Domino developers. While the XPages stack has remained relatively stagnant, the rest of the development world has raced forward, with new technologies giving rise to new frameworks and transforming the old. This is no discredit to the XPages team: they have consistently put in tremendous work across the board, but it's quite an industry to keep up with.

So we're in a tough spot now. The XPages platform isn't going away any time soon, but it's important for us as developers to progress. One option is to abandon ship entirely: pack up and move to some unrelated platform, leaving Domino behind entirely. But most of us, out of personal affection, professional acumen, or (primarily) business requirements, don't want to do that. Unfortunately, the path to improvement with Domino has only gotten more complicated with time. It began with learning about Java, OSGi, and Eclipse proper, but has since expanded to include a whole rogues gallery of other technologies: Maven, Tycho, m2e, Jenkins, Git, Wink/JAX-RS, jQuery, Bootstrap, Angular, and on and on.

There's no time like the present, though. I've walked this path, and I want to help everyone else walk it too. To kick that off, this series is going to cover the process of creating a basic XPages plugin, making it a little more complex, converting it to Maven, and building it with Jenkins.

To kick things off, the next post will provide an introduction to the concept of OSGi and the parts of it that we need to know for Domino development. Things may seem weird along the way, but trust me: it's worthwhile.

That Java Thing, Part 2: Intro to OSGi

  • Nov 3, 2015

OSGi once stood for "Open Services Gateway initiative", but that name slid from "impossibly vague" to "entirely obsolete" rather quickly. For our needs, OSGi is a mechanism for bringing sanity to the "big pile of Jars" that you might otherwise have in a large Java system. It provides a standardized way to describe the name of a library, its version, its dependencies, its capabilities, and its interactions with other libraries. In this way, rather than just "commons-io-2.1.jar", you can conceptually deal with "I require org.apache.commons.io, version 2.0 or above" and the environment can suss out what that means based on what is installed.

As Domino developers, we care about a subset of the OSGi concepts, primarily those related to creating and loading plugins. There is a handful of vital terms involved, some of which I've already been tossing around:

  • JAR: a Java Archive file is a ZIP file aimed at Java, generally containing classes and other resources (text files, images, and so forth). There are variants like "War" (Web Application Archive) and "Ear" (Enterprise Archive), but they're all still ZIP files.
  • Bundle: A "bundle" is, in effect, OSGi's word for a JAR. It expands on a contained file called META-INF/MANIFEST.MF (so named because Java likes to be difficult sometimes) to define the description of the bundle: its name, version, and so forth.
  • Plug-in: This is basically the same thing as a bundle. Presumably, it technically refers to a specialized kind of bundle, but we can use the terms interchangeably. It also technically has that hyphen in it, but I often write it "plugin" anyway.
  • Feature: This excessively-vague term is OSGi's way of grouping plugins together into a single conceptual installable unit. For example, the main feature included in the Extension Library references ten distinct plugins.
  • Update Site: Update sites are to features what features are to plugins: a way to organize these features into a collection, either to install them all at once or to provide a pool of available software to install. Eclipse-the-IDE uses them extensively for the latter case. As Domino developers, we primarily use them to distribute libraries and install them into the server's Update Site NSF.
  • Target Platform: A target platform refers to the set of plugins available in a given environment, which is used to determine whether a given plugin has the dependencies it will need to run. In our case, the target platform is usually the XPages runtime environment, containing OSGi base plugins, Java servlet support plugins, and the XPages runtime plugins themselves (among others). Plugin development environments use these heavily.
  • Extension Point: An extension point is a way for a plugin to either declare that it can either provide or consume a given service. For example, you might have a plugin that says "I can make use of any plugin that provides a color" and then another that says "I can provide the color blue". By using extension points, the first plugin can find the other, even though it may have been developed entirely independently.
  • Java EE: Java Enterprise Edition (sometimes written J2EE thanks to Java's weird relationship with versions) is basically the standard "web server stuff" for Domino (though it covers other aspects). XPages is sort of like a Java EE environment, but the experience is distinct. Notably, a Java EE server is a general platform on which other web-dev toolkits - JSP, JSF, Vaadin, JAX-RS, and so forth - can run. Java EE isn't inherently related to OSGi, but the two eventually bleed into each other with Domino and WebSphere.

OSGi in general goes beyond that, and some other aspects may be useful down the line (such as interacting with the OSGi console), but for now it's enough to think of it as a set of decorations for your projects. With some of the basic terms in line, next I will move on to setting up an Eclipse environment to start development.

That Java Thing, Part 3: Eclipse Prep

  • Nov 4, 2015

Before you dive into OSGi development, you'll need a proper development environment, and that usually means Eclipse. Domino Designer, being Eclipse-based, can technically do the job and has the advantage of some of the setup being done, but it's so out-of-date at this point that it's not worth it (believe me, I tried originally). Newer Eclipse versions have improved noticeably, so it's best to grab a recent version. Currently, that means Mars, or Eclipse 4.5.1.

Before Eclipse, you'll need to install Java if you haven't already. To do that, go to Oracle's site and download the latest release for your platform. Once you install it and find out how many devices run Java, you can move on to installing Eclipse.

Head to the Eclipse download page and download the "Eclipse IDE for Java EE Developers" for your platform. They've recently added an Installer at the top of the page, and that works as well, but adds some new wrinkles that haven't made it into common knowledge yet. For now, you may want to stick with the "...or download an Eclipse Package" section. The "bitness" (32 or 64) doesn't matter too much - you should generally match the bitness of your OS.

Once you have Eclipse installed and running ("installation" generally involves dragging the extracted ZIP file content somewhere), the next step is to teach Eclipse about the XPages artifacts. If you're running on Windows and have a local Domino server, the XPages SDK may be your friend, though it hasn't been fully tested for Mars. If you are running on a non-Windows OS or otherwise don't want to go the full SDK route, there's a quick route that lacks the automation and debugger integration.

Setting up the environment used to be something of a hassle, but it became much simpler ever since IBM released the Update Site for Build Management. Primarily made for Maven-development use (sort of), this Update Site also serves as a quick stop to pick up all of the dependencies you need for XPages development in one place.

To make use of the Update Site, download the file from OpenNTF, extract it, and then extract the UpdateSite.zip file contained therein. You should end up with a folder like this:

In Eclipse, go to the preferences ("Window" → "Preferences" on Windows or "Eclipse" → "Preferences" on a Mac), then go to "Plug-in Development" → "Target Platform":

Edit the existing platform and, in the "Locations" tab of the ensuing dialog, click "Add..." → "Directory" → "Next", browse to that extracted UpdateSite folder, and click "Next" to verify that it finds a bunch of plugins, and then "Finish". Now, you'll be able to reference XPages platform plugins in your projects.

At this point, Eclipse is set up for the essentials, but it may still be a good idea to set up an appropriate JVM for Domino development to make sure you have a consistent baseline and to avoid problems where newer Java releases added classes methods not avilable in Java 6. Fortunately, on Windows (and Linux?), a Notes installation comes with a JVM that does the job nicely. To add it, go back to preferences, then "Java" → "Installed JREs" → "Add...". Choose "Standard VM" → "Next", browse to the "jvm" folder in your Notes (or Domino) installation directory, name it something appropriate, and then click Finish:

On the Mac, you can get a similar Java release from Apple, which nowadays will end up in /Library/Java/JavaVirtualMachines (older installers put it in /System/Library/Java/JavaVirtualMachines).

Now that Eclipse is set up, the next step will be to break ground on making an actual XPages plugin.

That Java Thing, Part 4: Creating the Plugin

  • Nov 5, 2015

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.

That Java Thing, Part 5: Expanding the Plugin

  • Nov 6, 2015

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 6: Creating the Feature and Update Site

  • Nov 8, 2015

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 7: Adding a Managed Bean to the Plugin

  • Nov 9, 2015

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 8: Source Bundles

  • Nov 10, 2015

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 9: Expanding the Plugin - Jars

  • Nov 11, 2015

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 10: Expanding the Plugin - Serving Resources

  • Nov 12, 2015

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, Interlude: Effective Java

  • Nov 16, 2015

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 11: Diagnostics

  • Dec 1, 2015

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, Part 12: Expanding the Plugin - JAX-RS

  • Dec 3, 2015

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.

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 13: Introduction to Maven

  • Feb 19, 2016

I've been laying warnings that this would be coming and you've seen me grouse about it for over a year, but now the time has come to really dive into Maven for Domino developers.

To lead into it, there are two main topics to cover: what Maven is and why you should bother.

What Maven Is

Maven is a build automation tool, primarily for Java applications but able to work with a number of other languages and environments.

The concept of a "build automation tool" is a strange one when you're coming from a Notes/Domino perspective, and it's the source of a lot of consternation when moving to it. In classic Notes, there conceptually was no build phase for an application: certain things would be compiled on save, but there was rarely any need to think about this. Designer was the way to write applications and it took care of it. With XPages came a bit of Eclipse-ism with the notion of "Build" being a separate, not-necessarily-automatic stage, but there still wasn't much user-facing configuration going on: other than maybe adding a source folder, the IDE just kind of took care of it.

Even for OSGi plugin developers, the need seems a little arcane. Eclipse does have project and build configurations, and it runs through build scripts internally when you export Jars or build an update site. Again, though, this is all largely hidden and the user doesn't normally have to think much about it.

Where this comes in, though, is when you want to start expanding your projects in ways beyond the "single bag of code" stage: automatically including pre-packaged dependencies, making the project cleanly available to others down stream, sharing configuration across projects, and, particularly, automating building, testing, and deployment with an environment like Jenkins. Maven (and the alternatives like Gradle) provide important structure and meta-information to do these things and scale them to ever-larger tasks.

Why You Should Bother

I've found the pro-Maven pitch to be kind of a weird one, since it's sort of like unit/integration testing in that, before you do it, it doesn't seem worth the hassle, but then, when you've switched over, it seems crazy to not do it. Like a cult, I guess, but a somewhat better idea.

I'll start with an important reason: it's good for your career. Unless you want to remain on legacy-maintenance duty forever (which, granted, can be a stable gig), it's important to keep improving your skills, and build automation is a big concept that pays dividends in knowledge in Domino programming and beyond. Once you're familiar with a system like Maven, you start recognizing the same patterns elsewhere: in some of OSGi's capabilities, in older systems like Make, and, crucially, in whatever modern JavaScript toolchains are doing lately. Learning something like this opens up doors.

It also makes testing that much more natural. Building and running automated tests is certainly possible in Eclipse, but Maven's structure strongly encourages it (newly-created projects start with JUnit and a tests directory, to nudge you in that direction), and having test running be a phase in building makes it much more foolproof. The virtue of writing tests creeps up on you: even if you don't go whole-hog TDD, getting into the habit of starting each bug fix with a failing-then-successful test case means you now have a bug you'll never have to see again. Granted, as with a lot of other aspects, the nature of Domino development creates some hurdles, but it's still worth it.

And finally: features, features, features. Once you get comfortable with Maven, it becomes much easier to spin up accompanying source and Javadoc packages for your projects, add in other languages, filter content during builds, create alternate build profiles for different situations, bring in remote resources easily, deploy to targets automatically, manage versioning cleanly, and so forth. These are all things that are possible in the absence of a system like Maven, but Maven brings them all together in a way that is understandable both by your IDE of choice and by faceless servers.

What's Next

The next step will move from high-flown concepts to some brass tacks: preparing a Maven environment for Domino development and getting a look at what Maven's configuration files look like.

That Java Thing, Part 14: Maven Environment Setup

  • Feb 21, 2016

Before diving into the task of converting our plugin projects to Maven, there's a bit of setup we need to do. In a basic case, Maven doesn't require much setup beyond the project file itself - it's a "convention over configuration" type of thing that tries to make doing things the default way smooth. However, since it's also a "Java" thing, that means that anything out of the ordinary requires a bunch of XML.

Our big "out of the ordinary" aspect is OSGi. Maven and OSGi are often at loggerheads, but the conflict won't be too great in our situation. Still, it does mean there will be a few hoops to jump through, and one of those hoops is dealing with our dependency on the XPages runtime plugins. Since these plugins are not packaged as fully-Mavenized artifacts (yet (hopefully)), we'll need to configure Tycho to read the p2 (Eclipse) style.

In part 3, we downloaded the Build Management Update Site from OpenNTF, and we'll reuse that here. What we need to do is create a global Maven settings file that, for now, will just contain a definition of a variable to point to this update site. It would also be possible to specify this inside the project itself, but it's better form to use a consistent variable name (the most common convention is notes-platform) in the project files and then have your local settings point to it on your machine.

The global Maven settings file is called settings.xml and is stored in a folder named .m2 in your user's home directory (e.g. C:\Users\someuser\.m2 or /Users/someuser/.m2). Creating a folder named with a leading dot can be a pain in Explorer and the Finder, so it may be necessary to drop into a command line or other tool to do it. One way or another, create this file and set its contents similar to this:

<?xml version="1.0"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
	<profiles>
		<profile>
			<id>main</id>
			<properties>
				<notes-platform>file:///C:/IBM/UpdateSite</notes-platform>
			</properties>
		</profile>
	</profiles>
	<activeProfiles>
		<activeProfile>main</activeProfile>
	</activeProfiles>
</settings>

Adjust the file:// URL as necessary to point to the location on your computer. It has to be a file URL and not a normal path, presumably because repositories are usually expected to be remote HTTP sites.

This is the only configuration we need before getting to the project, but it's a good preview of the sort of "try pasting this big block of XML somewhere" advice you're in for when it comes to Maven use. Over time, the structure of the XML and how it relates to Maven's behavior begins to crystallize, but it's definitely cumbersome to start with, and it will get more opaque before it gets less so.

Depending on your proclivities, this may be a good opportunity to install standalone Maven as well. Eclipse has its own embedded version, so this is not required, but it can be handy sometimes to be able to run Maven from the command line. On your average Linux distribution or OS X with Homebrew, Maven should be installable with a package manager. Otherwise, Maven can be downloaded from maven.apache.org - it doesn't have an installer as such, as it's essentially some scripts around Java classes, but they have tips for adding it to your path.

Next, we'll get to the real meat of this process: actually converting the projects to Maven.