Richard Pajerski Software development and consulting

Entries tagged [notes]

LS2J as an applet replacement

by Richard Pajerski


Posted on Thursday May 30, 2024 at 10:50PM in Technology


I'm currently reviewing LS2J as an alternative for replacing a number of custom Notes applets I've deployed over the years.  The applets have been very reliable and effective in areas where Notes is typically not that strong such as displaying and editing tabular data.  But with applets' deprecation in Java and their "broken" status in Notes 64-bit versions, I need a viable transition to something that can preserve the existing functionality/investment as much as possible.

Although I don't see any obvious way to embed top-level Java windows (JFrame, JDialog) in the same manner as applets (which can be placed on a form like most other widgets), with LS2J I've found I can call them from behind a simple button and in the case of JDialog, even block the Notes client without breaking things too badly.  I can also call a JFrame from LS2J but the window can quickly lose focus -- in the same way it does when called from a Java agent.  I believe this is due to it being launched on a separate thread.

Below is a contrived example with JDialog but so far so good;  I'm able to replace a form-based applet with a simple button that calls a JDialog, blocks for user input and then transfers the text back to Notes:


This is actually simpler than applets which require Javascript when transferring to/from Notes UI documents (not pleasant).

Our button code (Options):

Click function:

And our associated Script Library:

The JDialog class is a bit more involved but not important in the context of this post.  The important part consists of returning the contents of a JTextArea when "OK" is clicked:

public String getFieldData() {
    return jTextArea1.getText();

 }

And that's it!


Notes/Domino 14 FP1

by Richard Pajerski


Posted on Tuesday April 16, 2024 at 05:17PM in Technology


Fixpack 1 for both Notes 14 client and Domino 14 server are now available: https://my.hcltechsw.com/downloads

Release notes: https://support.hcltechsw.com/csm?id=kb_article&sysparm_article=KB0112431



Three easy-to-use Java 17 features in Notes/Domino 14

by Richard Pajerski


Posted on Tuesday December 19, 2023 at 03:14PM in Technology


As I mentioned previously, having Java 17 available with the latest release of Notes/Domino has brought welcome changes for Java developers.  Just to scratch the surface a bit, here are three language-level niceties that we can immediately benefit from:


1.  Simplified access to file contents
2.  Local-variable type inference (var identifier)
3.  Text blocks


1.  Simplified access to file contents
Java has been (in)famous for offering a number of ways to open/read and write/close files.  As of Java 11, perhaps the simplest way to get text out of a file is:

Files.readString(Path p);



A two-liner in a local Java agent on a Windows 11 client:


import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import lotus.domino.*;

public class JavaAgent extends AgentBase {

    public void NotesMain() {

      try {

          Path p = Paths.get("C:\\Windows\\System32\\drivers\\etc\\hosts");
          System.out.println(Files.readString(p)); 
      } catch(Exception e) {
          e.printStackTrace();
      }
   }
}

Javadoc:  https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/nio/file/Files.html#readString(java.nio.file.Path)


2.  Local-variable type inference (var identifier)

The var type identifier has been available since Java 10 and is intended to help reduce "boilerplate" code:

import lotus.domino.*;

public class JavaAgent extends AgentBase {

    public void NotesMain() {

      try {

         var s = getSession();
         var db = s.getCurrentDatabase();
         System.out.println("Db title: " + db.getTitle());

                /* Contrast:
                     Session s = getSession();
                     Database db = s.getCurrentDatabase();
                   */

      } catch(Exception e) {
          e.printStackTrace();
      }
   }
}

More: Using the Var Type Identifier


3.  Text blocks
In Java 15, Text Blocks were introduced.  In a more developed application, it's probably a better idea to store strings and text outside of source code, especially if they need to be regularly modified.  But using text blocks can be a time saver for quick testing, demos, etc. and provide improved source readability.

import lotus.domino.*;

public class JavaAgent extends AgentBase {

    public void NotesMain() {

      try {

         var textBlock = """

                Using text blocks in Java source

                code simplifies text formatting

                and improves readability!

                """;


         System.out.println(textBlock);


      } catch(Exception e) {
          e.printStackTrace();
      }
   }
}


Note: To use these features, remember to adjust your compiler levels appropriately:


New context-specific Notes 14 client preferences options -- nice touch!

by Richard Pajerski


Posted on Thursday December 07, 2023 at 01:13PM in Technology


Notes 14 is now GA and an improved listing of the Notes "Additional options" (File > Preferences > Basic Notes Client Configuration) is an unexpected but very welcome feature!

Previously, all options were in one long list and so the context where each one applied was not always clear.  Now a categorical description precedes each option entry:




Nice touch, HCL and thank you!


Domino/Notes 14 and Java in the client

by Richard Pajerski


Posted on Wednesday July 26, 2023 at 01:25PM in Technology


Those who have been programming with Java in Notes/Domino over the years will be pleased to learn of the move from Java 8.x to Java 17 which is planned for the next release of ND 14 at the end of the year.  The version gap is rather stark and indeed a lot has changed.  One nice summary of the changes between the two version can be found at The Java Almanac here:  https://javaalmanac.io/jdk/17/apidiff/8/.

I've yet to explore the impact to the ND world in any detail but will try to cover that in some future posts.  A particular area of interest for me that's still "unresolved" is how HCL intends to treat Java applets.  Here I mean applets embedded in Notes client applications, not for use in web browsers.  Applets were deprecated for removal in Java 17 and of course have long been out of use in web browsers.  But will HCL continue to support their use in the Notes client and if so, for how long?


Java applets in Notes 64-bit client not loading

by Richard Pajerski


Posted on Wednesday December 21, 2022 at 07:17PM in Technology


[February 2023 update: DOTS follow-up, SPR for applet bug]

For the first time in the history of Notes, HCL last month released a fully supported 64-bit version of the Notes standard, Domino Designer and Domino Administrator clients with release 12.0.2.  Overall I've found it to be more stable and certainly faster than its 32-bit counterpart and so I now use it regularly.  However, it's not without its issues with reports popping up here and here.  And I have another to add: Java applets won't load in the 12.0.2 64-bit client.  I submitted a ticket to HCL and they could reproduce this so they're preparing an SPR.

A notable aspect of this bug is that there are no error logs (that I could find) to indicate something's wrong.  Nothing in the Java Debug console, the IBM_TECHNICAL_SUPPORT folder, log.nsf, etc.  You will see the Java coffee cup but I think most end users will not immediately interpret it as a problem:



This issue is probably not an inconvenience for most deployments since Java applets were never widely implemented in Notes (in my experience).  But where applets are used, they can bring quite a bit of power (e.g. custom interfaces for any third-party system with a Java API -- databases, message queues and so on).  The good news is that applets do still work in 32-bit Notes 12.0.2 and will likely be supported in Notes as long as Java 8 remains the default runtime.



Notes client pegs one logical processor at 15%

by Richard Pajerski


Posted on Friday June 18, 2021 at 03:38PM in Technology


Having recently installed a Notes/Designer/Admin 12 client on Windows 8.1 Pro on an aging Intel i7 Quad-Core (with eight logical processors), I was surprised to see that after startup, Notes was consistently grabbing one logical processor and pegging it at around 15%.  Windows Task Manager showed that nlnotes.exe was the process and it wouldn't let go of that processor until Notes was closed:



Notes, Designer and Admin clients all worked fine and interaction with local- and server-based applications was normal.  Without giving it too much thought, I tried a few things like removing cache.ndk, stopping a couple Notes-related services and making sure the preference "Enable scheduled local agents" was disabled.

But none of that worked and strangely, Disk and Network utilization were both at 0%:



The client was set up in the normal way by connecting to a Domino server with an existing mail file on the server.  However, this workstation needed to use a Location that sends and receives mail from a POP3 server instead of Domino.  For that purpose I used an existing names.nsf that already had Account and Locations documents in place.  All of that connectivity worked and the mail flowed normally.

The next step was internet search but nothing obvious jumped out and most references to similar problems ended with Notes client crashes which I was not experiencing.

The Location document for this POP3 configuration kicks off replication and runs it every 10 minutes.  I noticed that after starting the client, there was a gap of a few seconds where the nlnotes.exe process was at 0% and didn't go up to 15% until replication started (and then stayed there).  Thinking the culprit was the Replicator, I disabled replication and restarted Notes... but nlnotes.exe was back to 15%!

But this time I could clearly see that process spiked when "Notes configuration settings have been refreshed" scrolled across the Status bar:


Based on that message and the fact that a POP3 configuration like this is not commonly used, I kept pursuing the Location document as the source of the problem.  And the problem was indeed there.

When you configure the Notes client for POP3 mail retrieval, only the "Mail" tab of the Location document needs to be filled out:



The "Servers" tab can remain empty -- and that was the problem!  At a minimum, the "Home/mail server" field MUST have some value in it to calm down the processor:

It doesn't matter if the server is down or the value entered isn't even a Domino server -- nlnotes.exe will report that the server is not responding but it leaves the processor alone after that:



Ok, problem solved, back to work.  :-)


Congratulations Ray Ozzie - 2021 Computer History Museum Fellow Award

by Richard Pajerski


Posted on Saturday March 20, 2021 at 11:16PM in Technology


Ray Ozzie is among the 2021 Computer History Awards honorees recognized "For a lifetime of work in collaborative software and software entrepreneurship".

https://computerhistory.org/press-releases/chm-honors-tech-legends-for-lifetime-of-contributions-and-impact-on-humanity/

A virtual event took place to honor Ray on March 18, 2021 and will be generally available in the coming days.  Thank you, Mr. Ozzie for your contribution to computer history!





One additional little feature...

by Richard Pajerski


Posted on Friday March 12, 2021 at 12:43PM in Technology


In an effort to cover some specific cases, a minor feature was just added to CertMatica (3.6.0) to automate copying LE certificates to additional locations on the server's file system.

The idea is to allow Domino to share the LE certificate with one or more services running on the same machine (such as a reverse proxy like Nginx or an alternate SMTP service) without further manual intervention.


CertMatica 3.6.0

CertMatica 3.6.0 Trial



CertMatica 3.5.1 - important note for Domino 9.x and 10.x servers

by Richard Pajerski


Posted on Friday March 05, 2021 at 08:53PM in Technology


First of all, a big "Thank you!" to all CertMatica customers!

Just out this week, CertMatica 3.5.1 is a maintenance release with simplified switching between Let's Encrypt test and production modes and minor improvements to logging and documentation.  However, for those running on earlier versions of Domino 9.x and 10.x servers, this release also includes an important update to the CertMatica Cacerts Utility which can be used to address potential connectivity errors caused by missing or expired intermediate certificates in the Domino JVM truststore (cacerts).  For information on Let's Encrypt infrastructure changes related to this update see https://letsencrypt.org/2019/04/15/transitioning-to-isrg-root.html.

As always, feedback and commentary is always greatly appreciated!


New product for Domino keyrings: Aperture

by Richard Pajerski


Posted on Monday July 20, 2020 at 11:23AM in Technology



Since many development and administrative tasks in Notes/Domino can conveniently be carried out with great front-end tools like Domino Designer or Administrator, it can sometimes be inconvenient when we're required to use the command line or terminal to get things done.  Working with Domino keyrings is a case in point and one of the reasons why I developed Aperture.

Aperture is a lightweight desktop application that allows you to work with those .kyr files without having to resort to the command line.  It works with both the KYRTool and OpenSSL to allow you to visually create keyrings, view their contents, create Certificate Signing Requests and several other tasks you'd normally being doing on the command line.


Please visit the Aperture product page for more details:  https://www.rhpconsult.com/aperture.html.


As always, comments and suggestions are appreciated!


HCL Notes/Domino 11.0.1 Preview now available

by Richard Pajerski


Posted on Friday February 28, 2020 at 09:37AM in Technology


If you were a Notes/Domino 11 beta participant, the 11.0.1 preview is now available.  Use your existing links to access the downloads from FlexNet and the beta forum.


Eclipse J9 is a big deal

by Richard Pajerski


Posted on Friday March 15, 2019 at 11:59PM in Technology


Having developed with Java for a number of years in various environments (Notes/Domino, Tomcat, ActiveMQ,  Android, desktop, etc.), I was initially skeptical when I read this article and watched the video about the recently-improved Eclipse OpenJ9: https://developer.ibm.com/videos/introduction-to-eclipse-openj9-and-adoptopenjdknet/

Yes, Java has incrementally improved over time but the claims here seemed a bit over the top.  To think I might get both noticeably faster startup *and* up to 50% memory reduction just by switching to J9 seemed to be a bit too optimistic.  But after downloading (adoptopenjdk.net) and giving it a spin, I was not disappointed.

Sure enough, out-of-the-box startup time for Netbeans 8.2 on Windows 8.1 increased dramatically against Oracle Java 1.8.0_191 (running quad-core I7 on SSD).  There was no point in taking measurements -- it was up and ready in three seconds!  This didn't seem possible with Netbeans but there it was.  Everything worked the same as before ... only faster.  Then the real shocker: RAM usage went from roughly 650M down to 268!  Huh?  If I can eliminate that much RAM usage for hosted server side deployments, it's going to translate into real cash savings.

On top of the performance upgrade and memory savings, I immediately noticed that Swing is visually better in J9 than OpenJDK [edit: with the HotSpot VM].  In particular, the default font rendering is really nice!  In the past, OpenJDK has generally lagged behind Oracle Java for desktop applications and still does; but to my eyes, J9 is now at visual parity with Oracle (or perhaps better).

I realize that the J9 has been the JVM in Notes/Domino all these years but I've never attempted to benchmark it against other JVMs since IBM never really promoted it as a JDK for Windows.  I'm currently using 9.0.1 FP10 which uses build 8.0.5.21 of J9:


notesjvm.png

Hopefully, IBM can manage to get the latest J9 into an upcoming fixpack.  I sure have lots of Notes and Domino Java code that could benefit from it.

A big congratulations and thank you to Mark Stoodley and all the other engineers and players behind this release!


IBM Announces Investment in Notes Domino Version 10 and Beyond

by Richard Pajerski


Posted on Wednesday October 25, 2017 at 04:51PM in Technology


IBM has announced a multi-year investment in Notes Domino with a major new release (Notes 10) coming out in 2018. The investment will include closely-related products such as Notes Traveler, IBM Sametime and IBM Verse.

Specific product details are scant at the moment but it's encouraging to see IBM laying out a long-term roadmap for Notes and Domino and broadcasting a commitment to protecting its clients' investments. Also, the new direction allows IBM partners to hope for commercial stability for these products for the foreseeable future. Overall, I'm optimistic about this announcement.

However, the partnership with HCL Technologies for future development raises some questions. Will HCL Technologies be able to innovate the way that Iris Associates once did? Is this merely a cost-cutting measure or does IBM no longer have the internal talent to take Notes/Domino into the future (or both)?

More here:
https://www.ibm.com/blogs/social-business/2017/10/25/ibm-announces-investment-notes-domino-version-10-beyond/


Part 3. Extending the GUI of native Notes apps with Java applets

by Richard Pajerski


Posted on Wednesday April 01, 2015 at 11:12AM in Tutorials


*** July 2018 UPDATE ***
Links to the final database and Java source are included at the bottom of this blog entry.

In Parts 1 and 2, we created a Java applet and incorporated it into a Notes database. In this last part, we're going to code the export functionality in our applet and refresh the applet in Notes.

===================================
Part 3 -- Export Notes data using the applet
===================================

Step 1. Program the Start button. Open the ContactExport class in NetBeans and at the top of the code editor, click the Design button for the visual representation of the applet. Then right-click on the Start button and then Events > Action > actionPerformed. NetBeans will create a method called jButton1ActionPerformed and place our cursor at the appropriate spot in the source where we can add code that executes when the button's clicked. So we start with:


private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
}

When we click the Start button, we want to immediately disable it so only one export operation runs at a time. This would normally just be the following one line of code:


jButton1.setEnabled(false);

However, in Swing, we need to make sure to put any code that updates the UI on the event dispatch thread. So we're going to disable the button in a new thread:


private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
try {
java.awt.EventQueue.invokeAndWait(new Runnable() {
public void run() {
jButton1.setEnabled(false);
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
}


Step 2. Create a separate thread to access our Notes database. Next, we're going to create a separate thread that will connect to Notes. For now, we're going to implement this as a private inner class inside our applet. Toward the bottom of our ContactExample class, add:


private class ExportRunner implements Runnable {

private Thread t;

@Override
public void run() {

}

public void start() {
t = new Thread(this);
t.start();
}
}



We're going to place most of our functional export code in the ExportRunner's run() method which is the method that gets called when we start a Java thread. Add the following block to the ExportRunner's run() method to access the "All Contacts" view and its documents:


@Override
public void run() {
try {
Session s = ContactExport.this.openSession();
Database db = s.getDatabase("", "Contacts.nsf");
View allContactsView = db.getView("AllContacts");
Document d = allContactsView.getFirstDocument();
while (d != null) {
// We access the data here...
Document tmpDoc = allContactsView.getNextDocument(d);
d.recycle();
d = tmpDoc;
}
allContactsView.recycle();
db.recycle();
} catch (NotesException nex) {
nex.printStackTrace();
}
}


Step 3. Cycle through the contact documents and save the contents to internal buffer. First, add a StringBuilder in the ExportRunner class which will serve as the internal buffer to store the comma-delimited data. Also declare a platform-independent line separator.


private class ExportRunner implements Runnable {
private Thread t;
private StringBuilder sb = new StringBuilder();
private String newline = System.getProperty("line.separator");



Back in ExportRunner's run() method, we next want to update the while loop to extract the contact field data and separate the fields with commas and a final carriage return at the end of each line:


while (d != null) {
String firstName = d.getItemValueString("FirstName");
String lastName = d.getItemValueString("LastName");
String phone = d.getItemValueString("Phone");
String city = d.getItemValueString("City");
sb.append(firstName);
sb.append(",");
sb.append(lastName);
sb.append(",");
sb.append(phone);
sb.append(",");
sb.append(city);
sb.append(newline);
Document tmpDoc = allContactsView.getNextDocument(d);
d.recycle();
d = tmpDoc;
}


Step 4. Update the progress bar as we go through the contacts. This may sound straightforward but it takes some setup. Let's start by adding these variables to the ExportRunner class, just below the StringBuilder declaration:


private class ExportRunner implements Runnable {
private Thread t;
private StringBuilder sb = new StringBuilder();
private String newline = System.getProperty("line.separator");
private int contactCount;
private javax.swing.Timer progBarTimer;



NetBeans' GUI builder will have already declared a JProgressBar for us using the variable name jProgressBar1 when we added the progress bar component to our JPanel back in Part 1. But to animate the JProgressBar, we need to create two additional items: a handler class and timer class. We're going to create the handler class as a new private inner class in the ExportRunner class. We'll call it ProgressBarHandler:


private class ProgressBarHandler implements ActionListener {
@Override
public void actionPerformed(ActionEvent event) {
int v = contactCount;
if (v != 0) {
jProgressBar1.setValue(v);
}
}
}


And we'll instantiate the Timer class by inserting this as our first line in the run() method of our ExportRunner class:


progressBarTimer = new javax.swing.Timer(2, new ProgressBarHandler());

The Timer takes our handler class as a parameter and, once started, will call its actionPerformed method every two milliseconds. In actionPerformed, we explicitly set the progress bar's value. When the thread starts, we start the Timer, prime the progress bar with starting and ending values and then update those values as we loop through the documents.

We're also going to add a new method to ExportRunner called isDone() that holds a boolean variable indicating when the export is complete. We set the boolean variable to true when we're done processing the documents and when the handler sees that, it sets the progress bar back to zero and stops the Timer. It also takes care of re-enabling our Start button.

Beyond the progress bar animation, a good GUI practice is to let the user know when the system is busy. We're going to do that with the following commands:


setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));

Place the WAIT_CURSOR command outside the ExportRunner class – at the beginning of the jButton1ActionPerformed method and put the DEFAULT_CURSOR command as the last line in the handler class.

Here's the latest version of ExportRunner:


private class ExportRunner implements Runnable {

private Thread t;
private StringBuilder sb = new StringBuilder();
private String newline = System.getProperty("line.separator");
private int contactCount;
private javax.swing.Timer progressBarTimer;
private boolean done = false;

@Override
public void run() {

try {
progressBarTimer = new javax.swing.Timer(2, new ProgressBarHandler());

Session s = ContactExport.this.openSession();
Database db = s.getDatabase("", "Contacts.nsf");
View allContactsView = db.getView("AllContacts");

// Set the progress bar's minimum and maximum values.
jProgressBar1.setMinimum(0);
// Total contact documents in our view.
jProgressBar1.setMaximum(allContactsView.getEntryCount());

// Start the timer.
progressBarTimer.start();

Document d = allContactsView.getFirstDocument();

while (d != null) {
Thread.sleep(1); // Pause this loop to give other threads a chance to run.
contactCount++; // Increment the number of contacts processed.

String firstName = d.getItemValueString("FirstName");
String lastName = d.getItemValueString("LastName");
String phone = d.getItemValueString("Phone");
String city = d.getItemValueString("City");
sb.append(firstName);
sb.append(",");
sb.append(lastName);
sb.append(",");
sb.append(phone);
sb.append(",");
sb.append(city);
sb.append(newline);
Document tmpDoc = allContactsView.getNextDocument(d);
d.recycle();
d = tmpDoc;
}
allContactsView.recycle();
db.recycle();

} catch (NotesException nex) {
nex.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
done = true;
}
}

public void start() {
t = new Thread(this);
t.start();
}

private boolean isDone() {
return done;
}

private class ProgressBarHandler implements ActionListener {

@Override
public void actionPerformed(ActionEvent event) {
int v = contactCount;
if (v != 0) {
jProgressBar1.setValue(v);
}
if (ExportRunner.this.isDone()) {
progressBarTimer.stop();
jProgressBar1.setValue(0);
jButton1.setEnabled(true);
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
}
}



Finally, to instantiate and run this new class when the Start button is clicked, go back to jButton1ActionPerformed and add these two lines to the run() method:

ExportRunner exportRunner = new ExportRunner();
exportRunner.start();

The actionPerformed method should now look like:


private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
jButton1.setEnabled(false);
ExportRunner exportRunner = new ExportRunner();
exportRunner.start();
}


Step 6. Store the output after prompting the user for a location. Our data is now in our StringBuilder object but in Java, it takes a few steps to get that data into a file. For additional flexibility, we're also going to prompt the user for a file name and location using a JFileChooser. Put this block just after the db.recycle() command in ExportRunner's run() method:


JFileChooser jFileChooser = new JFileChooser();
jFileChooser.setSelectedFile(new File("ExportedContacts.txt"));
int save = jFileChooser.showSaveDialog(ContactExport.this);

if (save == JFileChooser.APPROVE_OPTION) {
FileOutputStream stream = null;
PrintStream out = null;
try {
File file = jFileChooser.getSelectedFile();
stream = new FileOutputStream(file);
out = new PrintStream(stream);
out.print(sb.toString());
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
if (stream != null) {
stream.close();
}
if (out != null) {
out.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}



That's it for the code. Now recompile the applet and refresh it in Notes (see Step 5 of Part 2). Populate the Notes database with some sample contact data and try it out.

Conclusion
Embedding custom Java applets in the Notes client is a powerful way of extending the value of Notes client applications. It's a little more involved than building out similar functionality with traditional Notes design elements but it offers benefits that just aren't available in Notes. Considering the size of the Java API and availability of third party Java-based tools, this example only scratches the surface of the possibilities.

Files and source code
Contacts.nsf.zip
ContactExport-src.zip