How do you organize your workspace?

Notes users differ in their tasks so my usage of the Notes workspace as a developer is completely different from a user that mainly works with documents within an application.

As always “With great power there must also come — great responsibility” so the Notes client lets us completely free how to setup our workflow via the workspace.

My activities focus mainly on adding code, distribute these changes into several environments (from DEV to PRODUCTION) and also store this code in templates.

If you have similar activities, how do you organize your workspace?

Debugging utility in XPages

Time to re-open an previous XPages project and add some new functionalities for the users. When I did this today I noticed some programming that I never wrote about before, so I do this now 🙂

In an application I wanted to monitor individual classes and write the findings to the console or OpenLog. All managed from a web UI 🙂

The printing method in this app is pretty simple:

public void printToConsole(String msg){
String callerClassName = getCallerCallerClassName();
if (debugClasses.containsKey(callerClassName)){
if (false != debugClasses.get(callerClassName)){
if (true == this.openLog){
OpenLogUtil.logEvent(new Throwable(), msg, Level.INFO, null);
}
else{
System.out.println(msg);
}
}
}
}

The getCallerClass method identifies which class is calling this method:

public static String getCallerCallerClassName() {
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
String callerClassName = null;
for (int i=1; i<stElements.length; i++) {
StackTraceElement ste = stElements[i];
if (!ste.getClassName().equals(KDebug.class.getName())&& ste.getClassName().indexOf(“java.lang.Thread”)!=0) {
if (callerClassName==null) {
callerClassName = ste.getClassName();
} else if (!callerClassName.equals(ste.getClassName())) {
return ste.getClassName();
}
}
}
return null;
}

Here is the KDebug class :

package com.wordpress.kwintessential.myproject.utils;

public class KDebug {
public static String getCallerClassName() {
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
for (int i=1; i<stElements.length; i++) {
StackTraceElement ste = stElements[i];
if (!ste.getClassName().equals(KDebug.class.getName()) && ste.getClassName().indexOf(“java.lang.Thread”)!=0) {
return ste.getClassName();
}
}
return null;
}
}

debugClasses is declared as followed:

private HashMap<String,Boolean> debugClasses = new HashMap<String,Boolean>();

And it is set when I initiate my utility class that contains the printing method:

public void setDebugClasses(){

try {
Properties debugs = this.getDebugProperties();
Enumeration<String> enums = (Enumeration<String>) debugs.propertyNames();

while (enums.hasMoreElements()) {
Boolean boolean1 = false;
String key = enums.nextElement();
String value = debugs.getProperty(key);
boolean1 = Boolean.valueOf(value);
debugClasses.put(key, boolean1);
}
} catch (IOException e) {
OpenLogUtil.logError(e);
}

}

method getDebugProperties reads a properties file that is stored in the WebContent directory:

monitor

public Properties getDebugProperties() throws IOException {
InputStream input = FacesContext.getCurrentInstance().getExternalContext().getResourceAsStream(“monitor.properties”);
Properties debugs = new Properties();
debugs.load(input);
return debugs;
}

Note that I have to place the file in the WebContent folder and I could not place it as a Resource design element.

The monitor.properties file contains key values pairs e.g.:

com.wordpress.kwintessential.myproject.dao.DecisionDominoDAO=true
com.wordpress.kwintessential.myproject.dao.VisitorDominoDAO=false

So with this code above I can:

  • check which class is calling the print method
  • check if this class should be monitored (read: print data to the console or OpenLog)

So all great! But in my case I wanted to prevent that the design had to be opened and rebuild every time the monitor settings should be altered.

So in this project I created a web UI (XPage) to read and change the monitor.properties file:

<?xml version=”1.0″ encoding=”UTF-8″?>
<xp:view xmlns:xp=”http://www.ibm.com/xsp/core”&gt;

<xp:this.beforePageLoad><![CDATA[#{javascript:var file = param.get(“filename”)
if (null == file || file == “”){
ctx = facesContext.getExternalContext();
ctx.redirect(facesContext.getExternalContext().getRequest().getRequestURL() + “?filename=monitor.properties”);
}}]]></xp:this.beforePageLoad>
<xp:panel styleClass=”container”>

<h1>Debug <small> Monitor Java classes</small></h1>

FileName:
<xp:inputText
id=”inputTextFileName”
value=”#{fileBean.fileName}”
defaultValue=”#{param.filename}”
disabled=”true” />
<xp:br />
FileData:

<xp:inputTextarea
id=”inputTextarea1″
value=”#{fileBean.fileData}”
rows=”20″
cols=”80″ />
<xp:br />
<xp:panel styleClass=”btnBar”>
<xp:button value=”Load” id=”buttonLoad”
styleClass=”btn-primary”>
<xp:eventHandler event=”onclick” submit=”true”
refreshMode=”complete” action=”#{fileBean.loadData}”>
</xp:eventHandler>
</xp:button>
<xp:button value=”Save” id=”buttonSave”>
<xp:eventHandler event=”onclick” submit=”true”
refreshMode=”complete” action=”#{fileBean.saveData}”>
</xp:eventHandler>
</xp:button>
</xp:panel>
</xp:panel>
</xp:view>

Here is how it looks in a browser:

web ui initial.JPG

When I press the Load button the content of the monitor.properties file get’s loaded:

web ui loaded

And of course when I press the Save button the monitor.properties file get’s updated 🙂

Now where is the magic in this? I got two methods fileBean.loadData and fileBean.saveData.

Here is what the fileDataBean class looks like:

import java.io.IOException;
import java.io.Serializable;
import java.util.Properties;

import lotus.domino.NotesException;

import com.paulwithers.openLog.OpenLogUtil;

public class FileDataBean implements Serializable {

private static final long serialVersionUID = 1L;
private String fileName;
private String fileData;
private String dbPath;
private String dbServer;

private Utils utils;
Properties props;

public FileDataBean() throws NotesException{
utils = new Utils();
utils.printToConsole(this.getClass().getSimpleName().toString() + ” – FileDataBean() // constructor”);
try {
props = utils.getDataSourceProperties();
dbServer = props.getProperty(“server_namis_notesname”);
dbPath = utils.getSession().getCurrentDatabase().getFilePath();
} catch (IOException e) {
OpenLogUtil.logError(e);
}
}

public String getDbPath() {
return dbPath;
}

public void setDbPath(String dbPath) {
this.dbPath = dbPath;
}

public String getDbServer() {
return dbServer;
}

public void setDbServer(String dbServer) {
this.dbServer = dbServer;
}

public void setFileData(String fileData) {
this.fileData = fileData;
}

public String getFileData() {
return fileData;
}

public void setFileName(String fileName) {
this.fileName = fileName;
}

public String getFileName() {
return fileName;
}

public void loadData() {
this.fileData = NAPIUtils.loadFile(this.dbServer, this.dbPath, this.fileName);
}

public void saveData() {
NAPIUtils.saveFile(this.dbServer, this.dbPath, this.fileName, this.fileData);
}
}

The NAPIUtils looks as followed:

import java.io.InputStream;
import com.ibm.designer.domino.napi.NotesAPIException;
import com.ibm.designer.domino.napi.NotesDatabase;
import com.ibm.designer.domino.napi.NotesNote;
import com.ibm.designer.domino.napi.NotesObject;
import com.ibm.designer.domino.napi.NotesSession;
import com.ibm.designer.domino.napi.design.FileAccess;
import com.paulwithers.openLog.OpenLogUtil;

public class NAPIUtils {

/**
* loads a given WebContent file and returns the result as String
*
* @param serverName
* the server to use
* @param dbPath
* the database path
* @param fileName
* the file to load
* @return the file data as String
*/
static public String loadFile(final String serverName, final String dbPath, final String fileName) {
NotesSession nSession = null;
NotesDatabase nDatabase = null;
NotesNote nNote = null;
try {
nSession = new NotesSession();
// open database
nDatabase = nSession.getDatabaseByPath(serverName + “!!” + dbPath);
nDatabase.open();
// load existing data
nNote = FileAccess.getFileByPath(nDatabase, fileName);
// get Filedate and return String
InputStream is = FileAccess.readFileContentAsInputStream(nNote);

return convertStreamToString(is);
} catch (NotesAPIException e) {
OpenLogUtil.logError(e);
} finally {
// recycle NAPI objects
recycleNAPIObject(nNote, nDatabase, nSession);
}

return fileName;
}

/**
* loads a given WebContent file and returns the result as String
*
* @param serverName
* the server to use
* @param dbPath
* the database path
* @param fileName
* the file to load
* @param fileData
* the data of the file
*/
static public void saveFile(final String serverName, final String dbPath, final String fileName, final String fileData) {

NotesSession nSession = null;
NotesDatabase nDatabase = null;
NotesNote nNote = null;

try {
nSession = new NotesSession();
// open database
nDatabase = nSession.getDatabaseByPath(serverName + “!!” + dbPath);
nDatabase.open();
// load existing data
nNote = FileAccess.getFileByPath(nDatabase, fileName);
// store them to note
FileAccess.saveData(nNote, fileName, fileData.getBytes());
} catch (NotesAPIException e) {
OpenLogUtil.logError(e);
} finally {
// recycle NAPI objects
recycleNAPIObject(nNote, nDatabase, nSession);
}
}

/**
* converts an input stream to a string
*
* @param is
* the input stream to convert
* @return String
*/
static String convertStreamToString(java.io.InputStream is) {
java.util.Scanner s = new java.util.Scanner(is).useDelimiter(“\\A”);
return s.hasNext() ? s.next() : “”;
}

/**
* recycleNAPIObject helper method for recycling NAPI objects
*
* @param nObjects
* the NAPI objects to recycle
*/
static void recycleNAPIObject(NotesObject… nObjects) {
for (NotesObject nObject : nObjects) {
if (nObject != null) {
try {
(nObject).recycle();
} catch (NotesAPIException ne) {
OpenLogUtil.logError(ne);
}
}
}
}
}

From this class I use the loadFile and saveFile methods. Look at the import of the com.ibm.designer.domino.napi classes which enables me to access files within the NSF!

Hopefully this writing gives you some new ideas for your development projects. Happy coding 🙂

Lesson learned in ReplaceItemValue vs NotesItem

Introduction

In a project I am working on I am modernizing a Domino application with XPages and JAVA MVC. To support the workflow in the application a “main” document must have at least 1 approved “sub” document for 9 mandatory areas. These “sub” documents appear then in a Notes view and an agent is keeping the overview if all 9 areas have approved documents .

The selection formula for this view is something as followed:

SELECT Form=”formA” & status=”40″ & notAuthorized = “”

replaceItemValue

To add a field to a document I normally use ReplaceItemValue as described in the XPages wiki.  The Help in Designer says about this method:

Replaces all items of the specified name with one new item, which is assigned the specified value. If the document does not contain an item with the specified name, this method creates a new item and adds it to the document.

So that sounded that I was good to go. Except… the documents created by my script did not appear in my view.

Document properties

When I compared the documents with ScanEZ / looked at the Document Properties tab / created on output via Document Viewer I could not find an explanation why the documents should not match the View selection formula 😕

Modifying the selection formula to something outrageous like

SELECT Form=”formA” & status=”40″ & notAuthorized != “SOMEOUTRAGEOUSCONDITION”

resulted that the documents appear in the view.

But I would rather like to keep the current data model of the View as is in place so the current code/UI could not become disturbed in any way.

Notes Item

Then I decided to change my code from

doc.replaceItemValue(“notAuthorized”, “”);

into

Item textItem = doc.replaceItemValue(“notAuthorized”, null);

textItem.setValueString(“”);

textItem.setSummary(true);

and I noticed the documents appear now in the Notes View.

When I compare the two methods in the replaceItemValue method the field notAuthorized is empty but has a data-length of 1. In the Item method the field is also empty BUT the data-length is 0.

So something is added to the field. But what?

IsSummary

First I thought perhaps it’s due to the Summary property but in both methods the Summary property set (at least it looks like that).

Here is some more information from Designer Help:

 

The IsSummary property of the new item defaults to true, which means that the item value can be displayed in a view or folder.

So I am wondering if the replaceItemValue (in Java) is properly setting this property or maybe not?

 

Presentation JUMP Session: IBM Domino Applications on Cloud

Abstract

IBM Domino Applications on Cloud is a subscription service that offers IBM hosted solution for Domino Apps over IBM Bluemix Cloud. The service offers a structured and planned migration process, avoiding business disruptions.

In this session you will be introduced to the offering, how it is structured and works including the pre-requisites, configurations and start-up options.

The video recording can be found here.

#Domino2025 Jam ended- Some first thoughts

Today I participated in the Domino2025 Jam here in Stockholm. The Jam is organised via a set of workshops around Application Development and Mail & Chat. I will not go into much details to cause a possible spoiler for you but I just want to mention a particular suggestion by an IBM Business Partner.

The suggestion was to tackle all the discussion / lost space / future of corporate mail to simply abandon the area and leave the space solely to the competitor.

What should you think of that? To me it would say that if IBM is not capable of providing a qualitative and competitive solution (mail & calendaring client + server)  for Mail I seriously doubt what the capabilities of IBM are.

The Notes client on expeditor has never been the desired success (slow startup, significant code base, complexity to governance in combination with bad or lack of marketing and complexity in the UX) but developing a HTML5 (Domino) Mail client with a ID vault to ensure encryption would be doable right?

This would prevent the competitor to walk freely through the door of every customer and spreading their other competitive products. It is also a slap in the face for customers who have invested heavily in the IBM portfolio.

The idea to separate Mail from the Applications I believe is a widely accepted alternative future for the client.

Domino has some excellent goodies regarding mail & calendaring. A too big waste to throw overboard that easily. But that is not what this business partner specialized in migration wants to broadcast via these DominoJams.

So be alert!

Thank you IBM for the invitation and gathering awareness of the continuity of the Notes/Domino roadmap.

The IBM Notes and Domino roadmap mantra *sigh*

I guess it was at IconUK Sept 2016 that IBM presented on their roadmap for Notes/Domino since a long time. The “big” news was that Q4 2016 IBM would come with an announcement regarding “updated tooling explored”.

What they meant with that was not clear to me, but “updated tooling” refers to me to what exists today, and that is currently just XPages if you ask me.

So last week during a webinar co-hosted by TeamStudio and TLCC the results of this exploration was expected to be presented as part of the roadmap for Notes and Domino. To my surprise instead of taking the opportunity and time to explain the findings of this exploration IBM simply repeats “updated tooling  explored” but adds the line “more during Connect 2017”. WHAT?!?

Didn’t you first come with a press notification that in Q4 2016 you will explain more about application modernization plans and invite people to come and listen to your views and then during this press report you simply say that people may wait another quarter?

And what does “more” in “more during Connect 2017” mean? How much more can people expect?

I am not sure what it means in English but it Dutch it sounds to me like a “zoethoudertje” but it has a negative interpretation. Probably the best translation is “compensation” It never satisfies any party in the long run.

Customers and developers want to move on and modernize their applications. And I am doubting if IBM is delivering it for Application Development in Notes Domino. If I look at the job market, the market is dead. If I read the modern development forums it is JavaScript (Node JS, Angular, React), Java, NonSQL databases where data is stored directly in JSON, GraphQL or any other Graph db, Websocket connection streams etcetera. Hell yeah blend in a couple of services from Bluemix if you like.

So yes, I am very disappointed in this announced “delay” and this unprofessional “procastination” by IBM. I am not sure what you are doing at your offices. Talk with your customers and development team. Write the technical opportunities down in a living document. Keep the document fresh and fruitful. Allocate sufficient resources so you come to deliverables.

Nobody wants to spend 2 hours listening to a roadmap-mantra that brings “nothing more on updated tooling explored” except Java 1.8, probably.