That Java Thing, Part 17: My Current XPages Plug-in Dev Environment

  • Feb 26, 2017

It's been a while since I started this series on Java development, but I've been meaning for a bit now to crack it back open to discuss my current development setup for plug-ins, since it's changed a bit.

The biggest change is that, thanks to Serdar's work on the latest XPages SDK release, I now have Domino running plug-ins from my OS X Eclipse workspace. Previously, I switched between either running on the Mac and doing manual builds or slumming it in Eclipse in Windows. Having just the main Eclipse environment on the Mac is a surprising boost in developer happiness.

The other main change I've made is to rationalize my target platform configuration a bit. In the early parts of this series, I talked about adding the Update Site for Build Management to the active Target Platform and going from there. I still basically do this, but I'm a little more deliberate about it now. Instead of adding to the running platform, I now tend to create another platform just to avoid the temptation to use plug-ins that are from the surrounding modern Eclipse environment (this only really applies in my workspaces where I don't also have actual-Eclipse plug-in projects).

The fullest form of this occurs in one of my projects that has a private-only repo, which allows me to stash the artifacts I can't distribute publicly. In that case, I have a number of library dependencies beyond just the core XPages site, and I took the approach of writing a target platform definition file and storing it in the root project, with relative references to the packaged dependencies. With this route, I or another developer can just open the platform file and set it as the target platform - that will tell Eclipse about everything it needs. To do this, I right-clicked on the project, chose "New" → "Other..." and then "Target Definition" under "Plug-in Development":

Target Definition

Within that file, I used Eclipse variable references to point to the packaged dependencies. In this repo, there is a folder named "osgi-deps" next to the root Maven project, so I wanted to tell Eclipse to start at the root project, go up one level, and then delve down into there for each folder. I added "directory" type entries for each one:

Target Definition Entries

The reference syntax is ${workspace_loc:some-project-name}../osgi-deps/Whatever. workspace_loc resolves the absolute filesystem path of the named project within the workspace - since I don't know where the workspace will be, but I DO know the name of the project, this gets me a useful starting point. Each of those entries points to the root of a p2-format update site for the project. This setup will tell Eclipse everything it needs.

Unfortunately, this is a spot where Maven (or, more specifically, Tycho) adds a couple caveats: not only does Tycho not allow the use of "directory" type entries in a target platform file like this (meaning it can't be simply re-used), but it also expects repositories it points to to have p2 metadata and not just "plugins" and "features" folders or even a site.xml. So there's a bit of conversion involved. The good news is that Eclipse comes with a tool that will upgrade old-style update sites to p2 in-place; the bad news is that it's completely non-obvious. I have a script that I run to convert each new release of the Extension Library to this format, and I adapt it for each dependency I add:

java -jar
	/Applications/Eclipse/Eclipse.app/Contents/Eclipse/plugins/org.eclipse.equinox.launcher_1.3.100.v20150511-1540.jar
	-application org.eclipse.equinox.p2.publisher.UpdateSitePublisher
	-metadataRepository file:///full/path/to/osgi-deps/ExtLib
	-artifactRepository file:///full/path/to/osgi-deps/ExtLib
	-source /full/path/to/osgi-deps/ExtLib/
	-compress -publishArtifacts

Running this for each directory will create the artifacts.jar and content.jar files Tycho needs to read the directories as repositories. The next step is to add these repositories to the root project pom so they can be resolved at build time. To start with, I create a <properties> entry in the pom to contain the base path for each folder:

<osgi-deps-path>${project.baseUri}../../../osgi-deps</osgi-deps-path>

There may be a better way to do this, but the extra "../.." in there is because this property is re-resolved for each project, and so "project.baseUri" becomes relative to each plugin, not the root project. Following the sort of best practice approach to Tycho layouts, the sub-modules in this project are in "bundles", "features", "releng", and "tests" folders, so the path needs to hop up an extra layer. With that, I add <repositories> entries for each in the same root pom:

<repositories>
    <repository>
        <id>notes</id>
        <layout>p2</layout>
        <url>${osgi-deps-path}/XPages</url>
    </repository>
    <repository>
        <id>oda</id>
        <layout>p2</layout>
        <url>${osgi-deps-path}/ODA</url>
    </repository>
    <repository>
        <id>extlib</id>
        <layout>p2</layout>
        <url>${osgi-deps-path}/ExtLib</url>
    </repository>
	<repository>
		<id>junit-xsp</id>
		<layout>p2</layout>
		<url>${osgi-deps-path}/org.openntf.junit.xsp.updatesite</url>
	</repository>
	<repository>
		<id>bazaar</id>
		<layout>p2</layout>
		<url>${osgi-deps-path}/XPagesBazaar</url>
	</repository>
	<repository>
		<id>eclipse-platform</id>
		<url>http://download.eclipse.org/releases/neon/</url>
		<layout>p2</layout>
	</repository>
</repositories>

The last entry is only needed if you have extra build-time dependencies to resolve - I use it to resolve JUnit 4.x, which for Eclipse I just tossed unstructured into a "plugins" folder in the "Misc" folder, without p2 metadata.

Though parts of this are annoyingly fiddly, it falls under the category of "worth it in the end" - after some initial trial and error, my target platform is more consistent and easier to share among multiple developers and automated build servers.

Slides From My Connect 2017 Presentations

  • Feb 24, 2017

At this year's Connect, Philippe Riand and I co-presented two sessions: one on ways to integrate your apps into the Connections UI and one on Darwino's role for Domino developers. I've uploaded the slides to SlideShare:

DEV-1430 - IBM Connections Integration: Exploring the Long List of Options

DEV-1467 - Give a New Life to Your Notes/Domino Applications and Leverage IBM Bluemix, Watson, & Connections (effectively, "the Darwino session")

The State of Domino App Dev Post-Connect-2017

  • Feb 24, 2017

I'm en route back from this year's IBM Connect in San Francisco, and this plane ride is giving me a good chance to chew over the implications for Domino developers.

First off, I'll put my bias in this matter right up front: Darwino, which I've been working on and discussing quite a bit, is one of the three "chosen" vendors for app enhancement/modernization/what-have-you. So, while this post isn't going to be about Darwino specifically, it's certainly pertinent for me. In any case, I'm aiming to speak exclusively as me personally here.

This event was the fated hour for the "app modernization" story promised over the course of the last year. In general, I'd summarize the pieces we have to pick up as (put as neutrally as possible):

  • The promised feature packs are coming along apace. The big-ticket items for the next two remain Java 8 (and a full refresh of the surrounding Java infrastructure following), exposing ID Vault and user-specific doc encryption to the lsxbe classes and XPages, an expansion of the ExtLib's DAS to support more PIM actions, and then misc. improvements (doc-level summary limit increase, some new @functions, and so forth).
  • A current version of the 9.0.1 ExtLib will be folded in to the main product in FP8, with the implication that that sort of thing may continue to happen. This brings some long-existing features like the Bootstrap renderkit and JDBC data sources into official support.
  • The implication is that Feature Packs will bring features more rapidly than a normal release schedule would.
  • Open-sourcing the UI components of XPages is still on the table.
  • The recently-released OpenNTF project SmartNSF is an encouraged way to write REST services in an NSF and is a candidate for inclusion in FP9 and, sooner, ExtLibX.
  • For modernization/mobile needs, IBM is providing a tool from Panagenda to analyze your existing apps and recommends the products from Aveedo, Sapho, and Darwino.

So... okay. Aside from Java 8 (which is a "rising tide lifts all boats" improvement), it seems like the focus on the additions to Domino is to encourage apps that use Domino rather than run on Domino. The additions to DAS are useful if you use Domino as your mail/calendar/RnR platform and want to integrate it with your other activities. SmartNSF smoothes the process of writing customized services to deal with NSF data in a more structured way than the raw DAS data service. The three encouraged "modernization" vendors each connect to or replicate data from (presumably old) Notes apps to expose it in a new UI, in two cases in order to use a "form builder"-type tool to make an easy app.

I see this as a codifying of the message from MWLUG: "learn something other than XPages". The improvements to the Java stack and various smaller changes will keep XPages apps running, but the focus is clearly not there. Nor is there an implication that there's a big "apps on Domino" revamp beyond the secondary effects of the OSGi update. So I think it's reasonable to consider XPages supported primarily in the "maintenance mode" sense. That stings, but it is what it is.

If you're currently working in XPages, there's no need to stop immediately or anything. You should, hoever, guide your development in the direction of being more adaptable elsewhere: heavier focus on writing REST services, much ligher focus on "Domino/XPages-isms" like embedding business logic right on a page with SSJS, and, if possible, getting used to toolchains like building OSGi libraries. Additionally, even if it's not immediately useful, I implore you: try out other environments. Spend a weekend with an Android or iOS tutorial, give Angular/Vue.js/React a shot in a test app, and so forth. The more you can learn another toolkit - any toolkit - the more you'll be comfortable with what's different elsewhere and what's the same.

It's always been important to do these things, but now it's required. No excuses - get out of your comfort zone.

As I have a chance, I'll be expanding on what Darwino's role is in all this, and shortly I'll be posting the slides from the sessions Philippe and I presented, one of which covered this topic. In the mean time, we're heading towards the weekend - this could be a perfect time to kick back and learn about something new. Maybe take a look at Swift if you haven't before. You don't have to form all of your future strategies right now - just learn a bit more every day.

Reforming the Blog in Darwino, Part 2

  • Feb 16, 2017

During the run-up to Connect next week, I turned my gaze back to my indefinite-term project of reforming this blog in Darwino.

When last I left it publicly, I had set up replication between a copy of the database and a Darwino app. After that post, I did a bit of tinkering in the direction of building a (J)Ruby on Rails front-end for it, next to the "j2ee" project. That side effort may bear fruit in time (as I recall, I got the embedded web app serving default pages, but didn't implement any blog-specific logic), but for now I decided to go for the "just get something running" route.

For that, the most expedient route was to write an Angular app using Darwino's stock document REST APIs. The (now unavailable) Bootstrap theme I use here came packaged with an Angular 1.x example and the Darwino demo apps are largely Angular 1.x as well, so most of the work was adapting what was there.

Unrelated to the front end, there was one change I realized I needed to make. In a fit of "psh, it's an XPages app; I don't need that old crap!", I structured the comments in the blog such that they're related to their post via a "PostID" field with the post UNID, not as actual response documents. While that would work just fine in the new form, this is a good opportunity to clean up the data a bit. Since I haven't (at least not yet) implemented a specific method in the DSL to say "this field is the real parent ID", I modified the Darwino adapter script to set the parent ID on outgoing data after normal conversion:

form('Comment') {
	field 'CommentID'
	field 'PostID'
	field '$$Creator', flags:[NAMES, MULTIPLE]
	field 'AuthorName'
	field 'AuthorEmailAddress'
	field 'AuthorURL'
	field 'Remote_Addr'
	field 'HTTP_User_Agent'
	field 'HTTP_Referer'
	field 'Posted', type:DATETIME
	field 'Body', type:RICHTEXT
  
	// Set the parent ID from the "PostID" field
  	events postConvertDominoToDarwino: { jsonHolder ->
      	jsonHolder.jsonObject.put("_parentid", jsonHolder.jsonObject.get("postid"))
  	}
}

The jsonHolder is a process object that contains the converted JSON to be sent to Darwino as well as a collection of the document's attachments and inline images. So, by setting the special "_parentid" field before the result is sent to the destination database, that value is used as the parent ID reference in the Darwino DB.

The other non-Angular addition I made was to add Gravatar support. This is an area where I'm currently doing it via a one-off utility class in the XPages app that spits out Gravatar images in an EL-compatible way. However, Darwino has a more idiomatic route: its built-in user directory/authentication system is extensible in a couple of ways, and one of those ways is to layer additional data providers on top of the primary directory.

For development purposes, my "directory" is just a static list of users specified in the darwino-beans.xml file, while it will presumably eventually point to my Domino server via LDAP to maintain consistent access. The basic static user bean looks like this:

<bean type="darwino/userdir" name="static" class="com.darwino.config.user.UserDirStatic" alias="demo,default">
	<property name="allowUnknownUsers">true</property>
	<list name="providers">
		<bean class="com.darwino.social.gravatar.GravatarUserProvider">
			<property name="imageSize">128</property>
		</bean>
	</list>
	<list name="users">
		<bean class=".User">
			<property name="dn">cn=Jesse,o=darwino</property>
			<property name="cn">Jesse</property>
			<property name="uid">jesse</property>
			<property name="email">jesse@darwino.com</property>
			<property name="password">secrets!</property>
			<list name="roles">
				<value>admin</value>
			</list>
			<list name="groups">
				<value>darwino</value>
			</list>
		</bean>
	</list>
</bean>

(please don't tell anyone my super-secret password)

The full syntax for Darwino beans is its own subject, but this instantiates a directory using the UserDirStatic class with a couple names - the "default" at the end means it'll be picked up by the stock configuration of a new app. The users are specified as instances of a nested class User with LDAP-like properties.

Separate from the specifics of the static user list, though, are the first two child elements: one tells the app that this directory should be consulted further even when a user doesn't exist in it, and the second instantiates a Gravatar user provider (which is in Darwino core). This user provider in turn tries to determine the user's email address - if the address is provided by the underlying directory, it uses that; otherwise, it tries the DN. These fallback behaviors come into play with comments: those users definitely wouldn't exist in the directory, but they DO have the email addresses entered during posting.

With this configuration in place, I can make image references like this:

<img src="$darwino-social/users/users/cn%3Djesse%2Co%3Ddarwino/content/photo" />

That runs through Darwino's stock social service to provide whatever image it can find from the provider - which in this case is a proxied-in Gravatar image.

So all that leaves now is the implementation of the front end. However, since this post is long enough and the code is currently an embarrassing mess, I'm going to punt on that for now and save it for later.

Connect 2017 Final Stretch

  • Feb 15, 2017

IBM Connect 2017 is less than a week away, and I've been furiously prepping for a couple parts of what is promising to be a busy conference.

On Monday, before the official kickoff of the conference, OpenNTF is co-hosting a Hackathon, where attendees will work on one of several potential projects. The goal is to learn about new development methods, work with new people, and hopefully kick off some useful open-source projects to boot.

During the conference proper, I'll be presenting two sessions, both alongside Philippe Riand:

On Wednesday at 10 AM, we'll be discussing IBM Connections integration - specifically, the numerous hooks provided by Connections locally and on the cloud for integrating your application as seamlessly as possible. That will be "IBM Connections Integration: Exploring the Long List of Options" in room 2006.

Then, on Thursday at 9 AM, we'll be discussing Darwino and its role integrating with and extending Domino applications. This should be a particularly-interesting one, covering what Darwino is, how its bidirectional replication with Domino works, and some example scenarios for reporting on and bringing forward Domino apps. That will be "Give a New Life to Your Notes/Domino Applications and Leverage IBM Bluemix, Watson and Connections" in room 2000.

Even with many of our usual community friends unable to make it to the conference or having moved on to other platforms, Connect is shaping up to be a worthwhile conference, and I'm very much looking forward to seeing everyone who is there!