New Release: XPages Jakarta EE 3.3.0

Fri Dec 20 16:12:24 EST 2024

Tags: jakartaee

As part of finishing my holiday gift shopping, I published version 3.3.0 of the XPages Jakarta EE project today.

This release contains a number of bug fixes to do with asynchronous and scheduled tasks based on some edge-case and intermittent trouble I ran into while developing some apps with it. Additionally, it has some consistency fixes for the Jakarta NoSQL support - in particular, it improves mapping of object properties to columns, matching the item names case-insensitively and matching special fields like FIELD_CDATE to matching columns with formulas like @Created.

Additionally, I took the occasion to bump some dependencies. While Jakarta EE 11 was pushed to next year, MicroProile 7.0 was released a few months back. This brings some version bumps to specs included in this project, including Rest Client, Open API, and Fault Tolerance. While the changes aren't dramatic, there are some nice refinements in there. I was also able to drop the Apache HttpClient in favor of an implementation that uses URLConnection, and it's always nice to lessen the number of dependencies.

However, there will be some more work to do in future versions when it comes to MicroProfile. The Metrics spec was dropped from MP 7.0 in favor of Telemetry, which I glean hews more closely to common practices in other tools. In XPages JEE 3.3.0, Metrics remains and I have yet to add Telemetry. It may end up being a breaking change, but I'm not sure that alone would warrant a major-version bump for this project, in large part because I don't know how much use the existing Metrics implementation gets here. In any event, my plan is to at least add Telemetry to 3.4.0 or so.

After that, while waiting for JEE 11 for some large updates, I'm a little tempted to dive into the "Better NSF Webapps" idea from my post the other day. That's one I've wanted to do for a while, and it would be really nice to get rid of the "xsp/app" part of URLs for full-Jakarta apps. I kind of doubt that that feature, even if I start working on it, would make it into the next version in a proper way, but it'd at least be interesting to take a swing at.

PSA: XPages Breaking Changes in 14.0 FP3

Mon Dec 16 14:08:27 EST 2024

Tags: xpages
  1. AbstractCompiledPage, Missing Plugins, and MANIFEST.MF in FP10 and V10
  2. Domino 11's Java Switch Fallout
  3. fontconfig, Java, and Domino 11
  4. Notes/Domino 12.0.2 Fallout
  5. Notes/Domino 14 Fallout
  6. PSA: ndext JARs on Designer 14 FP1 and FP2
  7. PSA: XPages Breaking Changes in 14.0 FP3

TL;DR: If you use XPages, don't use Domino 14.0 FP3. To fix the unrelated mail-routing bug, use the IFs for 14.0 FP2 or any earlier version. For the curious, the below bugs are tracked in HCL as SPR #RKRYDC2MHV.

(Updated 2024-12-17 below)

Fix Pack 3 for Domino 14.0 came out last week and, in addition to the usual spate of fixes you'd expect from an FP, it brought a potentially-significant and -breaking change to the way XPages handles style attributes for components. This is presumably in the interest of supporting the pathological strictures of Content-Security-Policy, but it applies whether or not you have taken any other steps to implement CSP.

The specific change is that the XPages renderers will now take the style attribute, externalize it to a class, and then make a <style> block for it. Take this XSP markup:

1
<xp:text style="color: red" value="I should be red"/>

While the above XSP was rendered as <span style="color: red">I should be red</span> on all previous versions, the rendered HTML (with unnecessary bits removed) now looks like this:

1
2
3
4
5
6
7
<span class="xc1">I should be red</span>

<style>
.xc1{
color: red
}
</style>

In this example, the results are equivalent: the text is red. However, this breaks in more-complicated examples. For example, say you have this in your XPage:

1
2
3
4
<xp:text style="color: red" styleClass="foo" value="I should be red"/>
<style>
	body .foo { color: green }
</style>

(The <style> block could just as easily be an external stylesheet for this purpose.)

In this case, the resultant HTML is:

1
2
3
4
5
6
7
8
<span class="xc1 foo">I should be red</span>
<style>body .foo { color: green }</style>
<style>

.xc1{
color: red
}
</style>

...and the text is green, though it should not be. Inline style attributes have a level of specificity that isn't quite equivalent to any other selector construct ("!important" rules included), so this change leads to insidious problems as things get more complicated. While inline styles have always been something of a faux pas, they're extremely common, all the more so for us because Designer encourages it and the official XPages tutorial tells you to use them.

Unfortunately, these changes are not opt-in and, because the problems are not technically errors, you won't see any console or error logs about it, and just looking at a page may not make visual changes (if there are any) immediately obvious. When using afflicted versions, the only real way to see if you need to change anything for now is to check each application manually to see if it breaks.

In the mean time, if you're upgrading 14.0 servers to account for the recent mailing problem and have XPages applications in production, I recommend sticking with Fix Pack 2, which got an Interim Fix 2 release to fix mail. If you're in the 14.5 Early Access program, it would probably also be helpful for you to chime in with your own experiences in the thread I made in the forum.

Update: Discussions in the OpenNTF Discord have highlighted several side effects or related changes that break xe:dialog elements and add extra <span>s around <xp:text>s that previously didn't have them, breaking more apps. Accordingly, my advice to not use 14.0 FP3 is only stronger now.

Large Features I'd Like To Add To XPages JEE

Sun Dec 08 12:05:28 EST 2024

Tags: jakartaee

Lately, the XPages Jakarta EE project is in a very good place: the move to Jakarta EE 10 cleaned up a lot of the codebase, there aren't currently any more looming brick walls, and the app development using it I've been doing has remained exceedingly productive. The product has a long list of issues to work on - a few of them are difficult-to-reproduce bugs, but most are small-to-medium-sized features. There are a handful of things, though, that would be big projects on their own that I'd love to find the time to do, but don't currently have enough of an impetus to devote the time to.

In no particular order:

Jakarta Batch (Or Scheduled Jobs Generally)

The Jakarta Batch spec is a way to define data-processing tasks in a standardized way - think agents but more explicit. It's a bit of a staid spec - it came from IBM and sure looks like it was specifically designed to be a very mainframe-y way of doing things - but that may be fine.

The advantages of this over agents would be that it would run in the OSGi class space and could use app code fully and that the definition language is pretty flexible. Additionally, though the XPages JEE project already has programmatically-defined scheduled tasks by way of Jakarta Concurrency, those tasks are pretty opaque from outside, while Batch jobs could theoretically be described usefully in a server-wide way for administrative purposes.

Jakarta Messaging

The Jakarta Messaging spec defines a consistent way to work with message queues and pub/sub systems, which helps when making an app as part of a larger system. There are a bunch of pretty enterprise-y implementations, but this looks like it could be a pretty good fit for local use on a Domino server, potentially with Domino's own message queues. Being able to have apps send messages to each other (or to tasks outside the HTTP JVM) could have a lot of uses, and having it baked in to the framework would make it worth considering much more than it's currently used.

Code Generation For NoSQL

When using NoSQL entity classes, it's not strictly necessary to have an actual Form design element, but it's a very common case that you'd have one - either because you make a quick-and-dirty Notes UI first or you're building a JEE app on top of an existing Notes app. Accordingly, it'd be neat to have an option in Designer to automatically generate Java classes for existing forms, saving some tedium of manually defining each property.

There are a couple things that would make doing this sort of a PITA, though. For one, it'd involve writing a UI plugin for Designer, which sounds like it'd just be a miserable process. I could probably work primarily or entirely with the Eclipse VFS and project APIs instead of Designer-specific classes, but still. Beyond that, there'd be the matter of trying to correctly describe a form. Text and number fields would be easy enough, but things would get tricky quickly. Should a multi-option field be presented as an Enum if the options are compatible? Should it try to guess boolean-storage fields? What system-type fields - like SaveOptions - should be included in the model as opposed to handled via compute-with-form? Should the display options for date/time fields map exactly to java.time classes? Not impossible, especially since "good enough" would be significantly better than nothing, but still a deep well of potential work.

Quality-of-Life Extension Projects

For the most part, the XPages JEE project focuses on implementing the specs as they are, with only a few things that are Domino-specific. Over time, I've been building up ideas for little features to add: a bean to handle Domino name formatting, supporting alternative HTML templating tools like Thymeleaf, packaged libraries for common tasks like Markdown formatting and RSS feeds, and so forth. These wouldn't really fit in the core project because I don't want it to bloat out of scope, but I could see them being their own additions or a general "extension library" for it.

Better OSGi Webapps

The JEE project has a couple capabilities in the direction of being used in OSGi-based webapps, but constantly bumps into ancient limitations with it. Something I've considered doing is making an alternative extension point to basically do this but better, presenting a Servlet 6 environment for OSGi-wrapped webapps, avoiding the need for wrappers and translation layers between old and new.

This would be a big undertaking, though. A chunk of it is handled by the existing HttpService/ComponentModule system, so it wouldn't be like writing a Java app server from scratch, but there'd be a lot to do as far as managing application lifecycles and so forth. Still, having control over the ComponentModule level would let me handle things that are finicky or impractical currently, like annotation-based Servlets, ServletContainerInitializers, and various listeners that just aren't implemented in the existing stack.

To go along with this, I'd want to look into what I can do with the Jakarta Data/NoSQL support to make it practical to not use Domino-specific interfaces all the time. The idea there would be that you could write an app that uses NoSQL with one of the other supported databases but then, when running on Domino, would store in an NSF instead. This would make it possible to develop entirely outside of Domino (and thus not have to worry about Designer) in a way that's basically the same. There'd be some trouble there in that it's pretty easy to hit a point with Domino as a data store where you need to give hints for views or data storage to make it practical, so it wouldn't always be doable, but it's worth considering.

Better NSF Webapps

Which leads to the last big potential feature: doing a custom ComponentModule but for NSFs. The idea here would be that you would, one way or another, register your NSF as a webapp container, and then it would be handled by a new ComponentModule type that eschews the XPages and legacy parts in favor of exerting full control over events, listeners, and URLs. This would allow apps to skip the "xsp/app" stuff in the URL, make it easier to do things like Filters, and have proper hooks for all app lifecycle listeners.

Like OSGi webapps, this would be a real 80/20 sort of thing, where some of the early steps would be fairly straightforward but would quickly get into the weeds (for example, having to write a custom resource provider for stylesheets, files, etc.). Still, I keep running into limitations of the current container, and this would potentially be a way out. It's probably the one I'd want to do most, but would also be the most work. We'll see.

I may get to some or all of these on my own time anyway, and any of them may end up cropping up as a real client need to bump them up the priority list. It's also just kind of nice having a good stable of "rainy day" projects to tickle the mind.

Quick Tip: Records With MicroProfile Rest Client

Mon Nov 11 15:42:47 EST 2024

One of my favorite features of the XPages JEE Support project is the inclusion of the MicroProfile Rest Client. This framework makes consuming remote APIs (usually JSON, but not always) much, much better than in traditional XPages or even in most other frameworks.

MicroProfile Rest Client

If you're not familiar with it, the way it works is that you can use Jakarta REST annotations to describe the remote API. For example, say you want to connect to a vendor's service at "https://api.example.com/v1/users", which allows for GET requests that list existing users and POST requests to create new ones. With the MP Rest Client, you'd create an interface like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.example.api;

import java.util.List;

import com.example.api.model.User;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("v1")
public interface ExampleApi {
	@Path("users")
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	List<User> getUsers();

	@Path("users")
	@POST
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	User createUser(User newUser);
}

Then, in your Java code, you can use it like this (among other potential ways):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
ExampleApi api = RestClientBuilder.newBuilder()
	.baseUri(URI.create("https://api.example.com"))
	.build(ExamplApi.class);

List<User> existingUsers = api.getUsers();

// ...

User foo = makeSomeUser();
api.createUser(foo);

The MP Rest Client code takes care of all the actual plumbing of turning that interface into a usable REST client, translating between JSON and custom classes (like User there), and other housekeeping work. Things can get much more complicated (authorization, interceptors, etc.), but that will do for now.

Records

What has been a nice addition with Domino 14 and recent releases of the XPages JEE project is that now I can also use Java records with these interfaces. Records were introduced in Java 14 and are meant for a couple of reasons - for our needs, they cut down on boilerplate code, but they also have beneficial implications with multithreading.

In our example above, we might define the User class as:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.example.api.model;

import jakarta.json.bind.annotation.JsonbProperty;

public class User {
	private String username;
	@JsonbProperty("first_name")
	private String firstName;
	@JsonbProperty("last_name")
	private String lastName;

	public String getUsername() {
		return this.username;
	}
	public void setUsername(String username) {
		this.username = username;
	}

	public String getFirstName() {
		return this.firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return this.lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
}

This is a standard old Java bean class, and it's fine. Ideally, we'd add more to it - equals(...), hashCode(), and toString() in particular - and this version only has three fields. It'd keep growing dramatically the more we add: more fields, bean validation annotations, and so forth.

For cases like this, particularly where you likely won't be writing constructor code much (unless they add derivation syntax), records are perfect. In this case, we could translate the class to:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package com.example.api.model;

import jakarta.json.bind.annotation.JsonbProperty;

public record User(
	String username,
	@JsonbProperty("first_name")
	String firstName,
	@JsonbProperty("last_name")
	String lastName
) {
}

This is dramatically smaller and will scale much better, lines-of-code-wise. And, since the important parts of the stack support it, it's nearly a drop-in replacement: JSON conversion knows about it, so it works in the MP Rest Client and in your own REST services, and Expression Language can bind to it, so it's usable in Pages and elsewhere.

Since records are read-only, they're not a universal replacement for bean-format classes (in particular, beans used in XPages forms), but they're extremely convenient when they fit and they're another good tool to have in your toolbelt.

New Releases: XPages JEE 3.2.0 and WebFinger for Domino 2.0.0

Fri Nov 08 12:05:54 EST 2024

This week and last, I uploaded a couple updates for some of my OpenNTF projects.

XPages Jakarta EE 3.2.0

First up is a new version of the XPages Jakarta EE project. This one is primarily about bug fixing and shoring up some of the 3.x line of features some more. Of particular note, it fixed a problem that crept in that caused the use of Concurrency to break the application, which was a bit of a problem.

WebFinger for Domino 2.0.0

About two years ago, I created the WebFinger for Domino project, primarily as a way to publish profile information in a way that works with Mastodon, though the result would work with ActivityPub services generally.

I'm fond of the goals of open formats like WebFinger and the related IndieWeb world. While Domino's licensing has been hostile to this world of personal sites since basically the 90s, it's still a good conceptual fit, and so I like to glom stuff like this on top of it when I can.

Anyway, I was reminded of this project and started thinking about ways to expand it beyond just its original use. For now, I mostly wanted to dust it off and make it extensible. The result is version 2.0.0, which refactors the code to allow for more stuff in the output. Along those lines, I added the ability to specify PGP public keys and have them included in the list, along with a Servlet to host them on your server. I plan to look around for other things people like to include in their profiles and add them in. I don't actually ever do much with PGP, but it's nice to have it in there.

Using Custom DNS Configurations With CertMgr

Thu Oct 24 18:51:20 EDT 2024

The most common way that I expect people use Domino's CertMgr/certstore.nsf is to use Let's Encrypt with the default HTTP-based validation. This is very common in other products too and usually works great, but there are cases when it's not what you want. I hit two recently:

  • My Domino servers are behind Traefik reverse proxies to blend them with other Docker-based services, and by default the HTTP challenge doesn't like when there's already an HTTP->HTTPS redirect in place
  • I also have dev servers at home that aren't publicly-visible at all, and so can't participate in the HTTP flow at all

The first hasn't been trouble until recently, since the reverse proxy is fine, but now I want to have a proper certificate for other services like DRAPI. For the second, I've had a semi-manual process: my [pfSense][(https://www.pfsense.org/)-based router uses its Acme Certificate plugin to do the dns-01 challenge (since it knows about my DNS provider) and then, every three months, I would export that certificate and put it into certstore.nsf.

Re-Enter CertMgr

Domino's CertMgr can handle those DNS challenges just fine, though, and the HCL-TECH-SOFTWARE/domino-cert-manager project on GitHub contains configuration documents for several common providers/protocols.

For historical reasons (namely: I didn't like Network Solutions in 2000), I use joker.com as my registrar, and they're not in the default list. Indeed, it seems like their support for this process is very much a "oh geez, everyone's asking us for this, so let's hack something together" sort of thing. Fortunately, the configuration docs are adaptable with formula (and other methods) - I'll spare you the troubleshooting details and get to the specifics.

DNS Provider Configuration

In certstore.nsf, the "DNS Configuration" view lets you create configuration documents for custom providers. Before I go further, I'll mention that I put the DXL of mine in OpenNTF's Snippets collection - certstore.nsf has a generic "Import DXL" action that lets you point to a file and import it, made for exactly this sort of situation.

Anyway, the meat of the config document happens on the "Operations" tab. This tab has a bunch of options for various lookup/query actions that different providers may need (say, for pre-request authorization flows), but we won't be using most of those here.

Operations

Our type here is "HTTP Request" - there are options to shell out to a command or run an agent if you need even more flexibility, but that type should handle most cases.

The "Status formula" field controls what Domino considers the success/failure state of the request. It contains a formula that will be run in the context of a consistent document used across each phase. If your provider responds with JSON, the JSON will be broken down into JSONPath-ish item names, as you can see in the HCL-provided examples. You can then use that to determine success or failure. Joker replies in a sparse human-readable text format, but does set the HTTP status code nicely, so I set this to ret_AddStatus.

The "DNS provider delay" field indicates how long the challenge check will wait after the "Add" operation, and that latency will depend on your specific provider. I did 20 seconds to be safe, and it's proven fine.

During development, setting "HTTP request tracing" to "Enabled" is helpful for learning how things go, and then "Write trace on error" is likely the best choice once things look good.

HTTP Lookup Request

For Joker, you can leave this whole section blank - its "API" doesn't support this and it's optional, so ignore it.

HTTP Add Request

This section is broken up into two parts, "Query request" and "Request". Set/leave the "Query request type" to "None" or blank, since it's not useful here.

Now we're back into the meat of the configuration. For "Request type", set it to "POST".

"URL formula" should be cfg_URL, which represents the URL configured above. Other providers may have URL permutations for different operations, but Joker has only the one.

Joker is very picky about the Content-Type header, so set the "Header formula" field to "Content-Type: application/x-www-form-urlencoded", which will include that constant string in the upload.

Things get a bit more complicated when it gets to the "Post data formula". For this, we want to match the example from Joker's example, but we also need to do a bit of processing based on the specific name you're asking for. Some DNS providers may want you to send a DNS key value like _acme-challenge.foo.example.com, while others (like Joker) want just _acme-challenge.foo. So we do a check here:

txtName := @If(@Ends(param_DnsTxtName; "."+cfg_DnsZone); @Left(param_DnsTxtName; "."+cfg_DnsZone); param_DnsTxtName);

"username=" + @UrlEncode("Domino"; cfg_UserName) + "&password=" + @UrlEncode("Domino"; cfg_Password) + "&zone=" + @UrlEncode("Domino"; cfg_DnsZone) + "&label=" + @UrlEncode("Domino";txtName) + "&type=TXT&value=" + @UrlEncode("Domino"; param_DnsTxtValue)

In my experience, this covers both single-host certificates and wildcard certificates.

HTTP Delete Request

This is for the cleanup step, so your DNS isn't littered with a bunch of useless TXT challenge records.

As before, make sure "Query request type" is "None" or blank.

Similarly, "Request type", "URL formula", and "Header formula" should all be the same as in the "Add" section.

Finally, the "Post data formula" is almost the same, but sets the value to nothing:

txtName := @If(@Ends(param_DnsTxtName; "."+cfg_DnsZone); @Left(param_DnsTxtName; "."+cfg_DnsZone); param_DnsTxtName);

"username=" + @UrlEncode("Domino"; cfg_UserName) + "&password=" + @UrlEncode("Domino"; cfg_Password) + "&zone=" + @UrlEncode("Domino"; cfg_DnsZone) + "&label=" + @UrlEncode("Domino";txtName) + "&type=TXT&value="

Putting It To Use

Once you have your generic provider configured, you can create a new Account document in the "DNS Providers" view.

In this document, set your "Registered domain" to your, uh, registered domain - in my case, "frostillic.us". This remains the case even if you want to register wildcard certificates for subdomains, like if I wanted "*.foo.frostillic.us". CertMgr will use this to match your request, and matches subdomain wildcards too.

There's a lot of room for special tokens and keys here, but Joker only needs three fields:

"DNS zone" is again your domain name.

"User name" is the user name you get when you go to your DNS configuration and enable Dynamic DNS - it's not your normal account name. This is a good separation, and a lot of other providers will likely have similar "don't use your normal account" stuff.

Similarly, "Password" is the Dynamic-DNS-specific password.

Joker account configuration

TLS Credentials

Your last step is to actually put in the certificate request. This stage is actually pretty much identical to the normal process, with the extra capability that you can now make use of wildcard certificates.

On this step, you can fill in your host name, servers with access, and then your ACME account. Even more than with the normal process, it's safest to start with "LetsEncryptStaging" instead of "LetsEncryptProduction" to avoid them temporarily banning you if you make too many requests.

With a custom provider, I recommend opening up a server console for your CertMgr server before you hit "Submit Request", since then you can see its progress as it goes. You can get potentially more info if you launch CertMgr as load certmgr -d for debug output. Anyway, with that open, you can click "Submit Request" and let it rip.

As it goes, you'll see a couple lines reading "CertMgr: Error parsing JSON Result" and so forth. This is normal and non-fatal - it comes from the default behavior of trying to parse the response as JSON and failing, but it should still put the unparsed response in the document. What you want is something at the end starting "CertMgr: Successfully processed ACME request", and for the document in certstore.nsf to get its nice little lock icon. If it fails, check the error message in the cert document as well as the document in the "DNS Trace Logs" view - that will contain logs of each step, and all of the contextual information written into the doc used by your formulas.

Wrapping Up

This process is, unfortunately, necessarily complicated - since each DNS provider does their own thing, there's not a one-config-fits-all option. But the nice thing is that, once it's configured, it should be good for a long while. You'll be able to generate certificates for non-public servers and wildcard at will, and that makes a lot of things a lot more flexible.

PSA: ndext JARs on Designer 14 FP1 and FP2

Thu Sep 12 11:02:18 EDT 2024

Tags: java xpages
  1. AbstractCompiledPage, Missing Plugins, and MANIFEST.MF in FP10 and V10
  2. Domino 11's Java Switch Fallout
  3. fontconfig, Java, and Domino 11
  4. Notes/Domino 12.0.2 Fallout
  5. Notes/Domino 14 Fallout
  6. PSA: ndext JARs on Designer 14 FP1 and FP2
  7. PSA: XPages Breaking Changes in 14.0 FP3

Back when Notes/Domino 14 came out, I made a post where I described some of the fallout of it. One of the entries was about the upstream removal of the "jvm/lib/ext" directory and the moving of all common extension JARs to the "ndext" directory. The upshot there was that any JARs that you want to add to the filesystem in Designer to match deployment on the server would have to be added to the active JRE in Designer in order to be recognized.

HCL presumably noticed this problem and altered the installation to accommodate it in FP1 and FP2. However, the approach they took is to add all JARs from ndext to the JVM. Thus, a fresh install+upgrade of Notes 14 to FP2 (or 14.5 EAP1) has a JVM that looks like this:

Screenshot of the 'Edit JRE' screen in Designer 14 FP2

This is a problem in a couple ways, but the most immediate is that it includes the toxic "jsdk.jar" I warned about in the earlier post. This JAR contains primordial Servlet classes from the very first addition of Servlet to Domino, predating XPages, and that version lacks even the convenience methods added in the ancient-but-less-so version in XPages. To demonstrate this, you can write this code:

1
2
HttpServletRequest req = null; /* pretend this is assigned to something */
Map param = req.getParameterMap();

This will work in a clean Designer 14 installation but will break on upgrade to 14 FP2, with Designer complaining that the getParameterMap method does not exist. There are others like this too, but basically any "The method foo() is undefined..." error for Servlet classes is a sign of this.

The fix is to go into your JVM definition (Preferences - "Java" - "Installed JREs" - "jvm (default)" - "Edit...") and remove jsdk.jar. While you're in there, I recommend also removing POI and its related JARs (poi-*, xmlbeans, ooxml-schemas, fr.opensagres.poi.*, commons-*) too, unless you also happen to have deployed them to the server, since they're not normally present on Domino and are thus mostly there to lead you astray. Honestly, almost none of the JARs present in there by default are useful for the XPages JVM definition, since the critical ones are contributed via OSGi plugins. I guess guava.jar is important just because it's going to contaminate the server's JVM too, so you want to account for that. Otherwise, it's probably best to treat it like a 14 install and only add the new JARs you've explicitly added and deployed to the server.

Recent Open-Source Project Updates

Fri Sep 06 14:25:34 EDT 2024

I've released a spate of open-source project updates recently, and I figured it'd be good to round up what's new. Most of them are utilitarian in nature - mostly fixes for things that crop up with Domino 14 and Java > 8 - but the first one is larger.

XPages Jakarta EE

Today, I released version 3.1.0 of the XPages JEE project. This is mostly about fixing up some edge-case and sporadic bugs that cropped up in 3.0, but also includes some performance updates and contributions from new contributors. Additionally, it should work on the newly-launched Domino 14.5 EAP1. The use of Java 21 in that version of Domino won't necessarily affect XPages JEE in a while, since JEE 11 targets Java 17, but there's some neat stuff in there for general use.

p2-layout-resolver

The p2-layout-resolver is a plugin that allows the use of p2 (Eclipse-style) repositories as Maven dependencies in non-Tycho projects. I use this in a lot of cases where I move a project from Tycho to maven-bundle-plugin for simplicity in dependency management.

Version 1.9.0 includes a very-useful contribution that fixes dependencies in cases where a bundle has a Bundle-ClassPath entry that references an embedded JAR that doesn't exist. In the Domino world, this cropped up in Domino 14, so it's useful if you're building anything that targets that version of the runtime or above.

p2-maven-plugin

For various Domino-related needs, I maintain a fork of the p2-maven-plugin, which is useful for its additions of things like generating site.xml files (still important for importing into an NSF update site, after all these years) and the <transform>jakarta</transform> option to run JARs through Eclipse Transformer when bundling them, allowing use of pre-Jakarta JEE artifacts in a smooth way.

The 3.1.x versions focused on fixing problems when running on Java > 8 (namely no longer using IBM Commons XML) and improving handling of some other hiccups.

Pretty-Printing JSON in the (Desktop) Notes Client and Domino

Fri Jul 26 10:30:35 EDT 2024

In the OpenNTF Discord (join if you haven't!), Daniel Nashed brought up a task he was facing: in the Notes client, writing pretty-printed JSON. LotusScript has its NotesJSON* classes that can process JSON in their stark way, but the stringify output is meant for machine reading and doesn't include whitespace and line breaks, making it ill-suited for things like configuration files or other things a human might read or edit.

Since the goal is to get it working in the full Notes client and not Nomad, Java is on the table, but Java - for dumb historical reasons - has no proper built-in JSON library. However, as of 12.something HCL shunted IBM Commons down to the global classpath in order to support the "share Java design elements between XPages and agents" feature. Among many other things, IBM Commons includes a JSON library that can suit.

I wrote a post almost a decade ago talking about this library and its limited nature, but it's nonetheless less limited than the LotusScript classes, and it's up to the task. There are a couple ways to go about this, depending on your needs, but for now I'll just cover the basic case here of "I have a string of JSON and want to format it".

To do this, you can make a Script Library of the Java type named, say, "JsonPrettyPrint" and make a new class in the "com.example" package and named "JsonPrettyPrint" with this contents:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package com.example;

import java.io.IOException;

import com.ibm.commons.util.io.json.JsonException;
import com.ibm.commons.util.io.json.JsonGenerator;
import com.ibm.commons.util.io.json.JsonJavaFactory;
import com.ibm.commons.util.io.json.JsonParser;

public class JsonPrettyPrint {
	public String prettyPrint(String json) throws JsonException, IOException {
		Object jsonObj = JsonParser.fromJson(JsonJavaFactory.instanceEx, json);
		return JsonGenerator.toJson(JsonJavaFactory.instanceEx, jsonObj, false);
	}
}

Then, you can instantiate this object with LS2J and pass it a JSON string, such as one from NotesJSONNavigator:

1
2
3
4
5
6
7
8
UseLSX "*javacon"
Use "JsonPrettyPrint"

Sub Initialize
	Dim jsession As New JavaSession, jsonPrinter As JavaObject
	Set jsonPrinter = jsession.GetClass("com.example.JsonPrettyPrint").CreateObject()
	MsgBox jsonPrinter.prettyPrint(|{"foo":{"bar":"baz"}}|)
End Sub

That'll get you something presentable:

Screenshot of a message box showing pretty-printed JSON

While the stringify-parse-stringify process you'd do if you generated your JSON with the NotesJSON* classes is inefficient, it's not too bad, especially for the size of content you're likely to want to emit here. You could alternatively do more work with JsonJavaObject and friends from IBM Commons directly to save a little overhead, but this path is a good way to do the vast majority of work in "normal" LotusScript and then only dip in to Java for the last step.

As mentioned at the start, the presence of Java means this won't work for Nomad, unfortunately. There may be a way to wrangle your way to this result using the primordial JavaScript runtime present in that, but that may not be worth the trouble unless you really need it. Better would be to vote for the Aha idea to add pretty printing to LS.

XPages JEE 3.0

Sun Jun 09 14:45:14 EDT 2024

Today, I uploaded the release version of 3.0.0 of the XPages Jakarta EE Support project. It's been proving stable in my use since the last beta, and so I think this is as good a time as any to release it properly.

Changes

The big-ticket change remains the move to Jakarta EE 10 as the baseline, which brings a handful of new features as well as a new Java version requirement. That means that this release also requires at least Domino 14. Domino 12.x served us well, but its time has passed.

Jakarta EE 10, for its part, is mostly about solving a lot of old business in the JEE community: it continues the gradual deprecation of EJB in favor of CDI, it removes some old stuff like applet requirements, and then also brings in a couple "scratch an itch" features.

Of particular note is the addition of the EntityPart type for REST services. Though it's a small feature, it's a real "finally" one, in that there hadn't been a proper way to deal with multipart/form-data MIME body parts individually, and so each implementation of Jakarta REST would bring in its own, or you'd have to fall back to taking an InputStream and parsing the MIME body yourself. Now, you can do so in a spec-based way:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public String post(@FormParam("part") EntityPart part) throws IOException {
	String mediaType = part.getMediaType();
	String name = part.getName();
	String fileName = part.getFileName();
	MultivaluedMap<String, String> headers = part.getHeaders();
	byte[] data = part.getContent().readAllBytes();
		
	// ...
}

There's also the split of Jakarta NoSQL into that spec plus the new Jakarta Data. In this release of XPages JEE, I mostly aimed to keep the same level of functionality while accounting for the renaming of packages and types, but I'll be interested in building on this in the future.

Finally, there's the project-specific change of condensing the many, many XPages libraries just down to Core, UI, and MicroProfile. That didn't impact functionality as such, but it sure is nice only having three (or six, with the source features) features to say "yes, install this" to when updating it in Designer and only three to check in Xsp Properties. It also allowed me to delete a lot of weird shim and conditional code, and it'll make maintenance of it much easier in the future without having to worry about every permutation of what libraries you have enabled in an NSF.

The Future

Speaking of which, that brings me to some of the next things in the docket. I imagine that the immediate work will be cleaning up any loose ends from the move. For example, Jakarta Concurrency 3.0 brought a bunch of new features, but I haven't actually checked to see if they work or if I need to do more adapting.

Additionally, Jakarta Data is intended to go beyond just NoSQL, and can also layer on top of Jakarta Persistence (née JPA, the API for working with relational DBs) and arbitrary services. I don't know yet if there's a usable implementation beyond the one in Open Liberty, so that may have to wait, but it'll be interesting to tinker with.

There are also a bunch of features I'd like to get cracking on now that this hurdle is done. For example, I'd like to move the NoSQL driver to use JNX, which would let me do a couple things that the Notes.jar classes just can't. Along with that, I'd like to add an option to publish MP Metrics to the Domino statistics store, which adopting JNX would allow easily.

Fortunately, I don't expect that there will be any other breaking-change discontinuities in the future. Jakarta EE 11 has some deprecations and removals, but it's mostly similar to JEE 10 in that it's about classes and idioms that are much, much older than any code built using this project. That should give this 3.x series a good, long period to be a comfortable baseline, even though the next major version of JEE.