Simple example of Windows native library declaration and usage in XPages

I would like to share a simple sample on how to access (Windows) DLLs from Java / XPages.

Java’s JVM allows you to do many smart things but sometimes you may be forced to directly use external library or writing code in pure java would be very time-consuming comparing it with something more low-leveled.

So, how does it work? The only thing you have to do is to download and import JNA (Java Native Access) to your project and write a simple class.

package de.eknori.c;

import com.sun.jna.Library;
import com.sun.jna.Native;

public class Beeper {
	private Kernel32 lib;

	public interface Kernel32 extends Library {
		// FREQUENCY is expressed in hertz and ranges from 37 to 32767
		// DURATION is expressed in milliseconds
		public boolean Beep(int FREQUENCY, int DURATION);

		public void Sleep(int DURATION);
	}

	public Beeper() {
		lib = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);
	}

	public void test() {
		lib.Beep(500, 500);
		lib.Sleep(500);
		lib.Beep(1000, 500);
	}
}

From your XPage you can now make a call to the class in a button

var beep:de.eknori.c.Beeper = new de.eknori.c.Beeper();
beep.test()

For a simple beep, this solution might be a bit of an overkill; but imagine what else you can do with it. Accessing elements in an application using the C-API for example. 🙂


Setting Page Generation Properties to HTML5

I’m playing around with bootstrap. Bootstrap makes use of certain HTML elements and CSS properties that require the use of the HTML5 doctype. To set the doctype in your XPages project do the following:

  • Open Package Explorer
  • Open xsp.properties under WebContent -> WEB-IN
  • Navigate to the Page Generation Properties tab
  • Set the value in HTML doctype
  • Save the changes

 


Domino Designer – Initially Collapse Comments

If you write tons of code in Java and Javascript, you probably have a huge number of comments in your code. If not, stop reading and review your code …

Sometimes it is a bit annoying when you scroll thru your code looking for a certain piece of code and you also have to scroll thru all the comments. here is a quick tipp that can make your life earsier.

Open the preferences dialog in Domino Designer and search for “Folding” There are two sections for Java and Javascript, where you can configure if comments should be initially collapsed.


Weird issue resulting in application getting corrupt

I had a very bad start into this week. On last week friday, I made some changes to our CRM project. The changes were mostly some modifications to a variable resolver. I removed redundant code by moving some parts of the code to methods within the resolver code. I also added two new methods to an existing bean. All this is far away from rocket science. I then changed the home.xsp to use the new methods from the bean.

After opening the application in the browser, everything looked fine. I refreshed the application in the browser and … BOOOOOOOMMMMMMMMM.

Last thing, I could see on the server console was

err_protected> ERR_PROTECTED ERROR: D:\Programme\IBM\Lotus\Domino\data [nsfsem2.c/ln 1684]
err_protected> ERR_PROTECTED ERROR: D:\Programme\IBM\Lotus\Domino\data [nsfsem2.c/ln 1684]

then the server crashed. I restarted the server and tired again. Same result. Application could be opened and crashed after any other action. To be sure, I removed my modifications. But the server still crashed. I tried everything from compact, fixup , replacing the design with the design of a working copy and even creating a new copy of the application. But nothing helped. The error persists.

The strange thing is, that I could/can open the application in question in Designer and make changes, add, update and delete data documents. The crash only occurs, when working in the browser.

I then replaced the corrupt file with the working copy and everything worked as expected. We made some other modifications to the design and the application stayed stable. Before driving home, I made a copy and put the file onto my Domino at home. As expected, everything was fine.

I then started to implement the same modifications that ended in the file becoming corrupt. And yes, I was able to reproduce the issue.

Phil Riand asked me to send in any crash report and the code that caused the error. I sent him an email yesterday morning. During the night, Phil sent a reply. here is wahat he wrote:

Hey Ulrich,

…. The issue you mention is definitively down in the NSF layer, in the semaphore management code. So I forwarded that to the appropriate team internally, and hope to get feedback soon. The issue might not even happen because of your database, and probably not because of your changes.

This is good news and bad news. Good news because there is something wrong, and it is reproducable. Bad news because if it is not in the database nor in the changes, it can happen again any time. So I hope, that IBM will find the cause and fix it.

 

 


XPages – Print RichText to PDF

With iText it is fairly easy to create PDF documents from Notes Documents in the browser. I have already written an article on how to do this.

Today I would like to share a small piece of code that lets you convert NotesRichText into PDF.

The download contains all the neccessary elements.

If you want to use the code in your own application, make sure that the “Store content as HTML and Mime” is checked for your RTItem.

“Create PDF” – Button code:

de.eknori.HTML code:

The result:

It’s not perfect. Embedded images do not work and aslo some styles do not convert well. This does not mean that it is impossible, but you have to write a few extra lines of code to make it work.

 

 


Domino Designer Performance – Check your AV

Ok, here is the story. My team and I had a very bad day yesterday. We were not able to work on our project because Domino Designer decided to take more than an hour for the build process. I tried to find out, what was wrong. My other team members in the head office did not reported this issue. So I had a weird network issue in mind bacause the issue occurred on all machines in the brnch office but was not able to find out any hint, what caused Designer to obviously wait for some action to complete when building the project. Even changing the code in a Java class was not possible. Each keyboard action took some seconds up to minutes to reflect the changes.

We are all working on local replicas of the files. And, it worked the day before … So WE did not change anything on the machine. Aside from this, we, the developers do not have proper rights to change ANYTHING on the machine. The is done by our friends, THE ADMINS.

Today, I saw a message when starting my computer that the Sophos AV has been successfully updated some files. Never seen that before … I decided to take also AV in consideration and looked into the settings. And there it was. “SCAN ALL FILES” and “SCAN IN ALL DIRS”. Exclusions? Well …. NO.  I managed to tweak the settings to exclude the Notes dir. And, what do you think? Right, everything now works at full speed again.

Shall I say “Thank you, admins” ?


Getting ‘JDBC driver not found error’ while trying to connect DOMSQL from crystal through jdbc

I could do with some help as i’m pretty well stuck.

I’m trying to connect crystal report 11.5 ( XI R2 )  developer to DOMSQL on a remote server via jdbc.

I have copied com.ibm.domino.domsql.driver_1.0.0.jar into “C:\Program Files\Common Files\Business Objects\3.0\java\lib\external”

I then opened the CRConfig.xml in “C:\Program Files\Common Files\Business Objects\3.0\java\lib” and added “C:\Program Files\Common Files\Business Objects\3.0\java/lib/external/com.ibm.domino.domsql.driver_1.0.0.jar;” after the last semicolon just before ${CLASSPATH}

I then open Crystal reports, standerd report wizard, create new connection, JDBC (JNDI), I then enter my DB URL: “jdbc:domsql://serv01/cois_v2/cois_crm.nsf/crm” and driver class “com.ibm.domino.domsql.DomSQLDriver”.

Then next, username and password. When I try to connect, I get the error:

Failed to Open Connection
Details: JDBC Driver not found

Any idea? I think I’ve done everything right

 UPDATE: The problem is in the JDK that is installed with CR.  I have installed an updated version of the Java JDK (c:\jdk) and changed the path for JavaDir in CRConfig.xml. Now I am able to connect to the DomSQL server

UPDATE 2: I have also successfully tested with JRE instead of JDK. here is the exact version, I used

java version “1.6.0_24”
Java(TM) SE Runtime Environment (build 1.6.0_24-b07)
Java HotSpot(TM) 64-Bit Server VM (build 19.1-b02, mixed mode)


What I was working on recently

Aside from doing a lot of XPages development, I recently worked on a tool to add mail signatures. The aim was to provide a solution that adds the signatures on a server to avoid to push information to the profile in the users mailfile.
Using a server based solution also makes sure that the signature will be added to any kind of outgoing mails, regardless if it is send from the Notes Client, WebMail or a mobile device.

A signature configuration can be used for an individual user ( or group of users ), a department ( */dev/emea/us ) or fo all users in the company. Within the signature configuration you can use placeholders that are filled at runtime. You can also use a log0, that can be prepended or appended to the signature. You can also use the logo anywhere inside your signature using a {{LOGO}} tag.

This gives you a high grade of flexibility creating the signature.

 

There can be another signature that is used when sending mails internally.

Signatures are designed using a RichText item; the content is then “translated” to HTML. If you are familiar with HTML/CSS, you can create and style your signature in the HTML item directly.

The tool runs on Windows 2003/2008 32/64bit and Linux ( tested with SLES 11/32Bit )


@Formula Snippet – Strip leading zeros from @NoteId

We use @NoteId in a view to get the NoteId of a document and display the value in a column. Via NotesNavigator, we access this column ( and others ) Unfortunately, the value, that is returned by the formula is something like NT0000903.

When we pass this value to our function, to open the document in a tabContainer, it fails because the function excepts only the number part wthout the NT and leading zeros. ( = 903 ). So I was looking for a way to stip the unwanted part from the string.

Here is, what I came out with

tmp:=@ReplaceSubstring(@NoteID;"NT";"");
@Do(tmp := tmp;@While(@Length(tmp) > 1 & @Begins(tmp; "0");tmp := @Middle(tmp; 1; 8));tmp) ;

If there is a better solution, let me know. We tried many things, but to go thru the value from left to right and checking, if the string begins with a “0” seems to be the best way to achieve the aim.


Notes C API – LNK4070 and LNK4199 warnings in compiler

Recently, I had to work with the C-API. Aside from having some problems to compile, link and successfully run the extension manager from the sample code that comes with the Notes C-API, I always got the warnings mentioned in the subject of this article.

1. warning LNK4070: /OUT:ntrigger.dll directive in .EXP differs from output filename ‘output\w32\ntrigger32.dll’; ignoring directive

This is an easy one to fix. Just make sure that the LIBRARY statement in your exports.def has the same value as the output file. So my exports.def contained LIBRARY “ntrigger” ( which was correct at some point of the dev process ) But I changed the output to ntrigger32.dll. So changing ntrigger to ntrigger32 in exports.def will stop the warning from being displayed.

2. warning LNK4199: /DELAYLOAD:OleAcc.dll ignored; no imports found from OleAcc.dll

This warning seems to be a common one and can be ignored. But I do not like to see warnings or errors during the build process. After doing a little research, I finally found the cause and how to avoid the warning.

First I thought that putting OleAcc.dll as an ignored DLL into the “Ignore Specific Lirary” field in the Linker –> Input section of the project properties would do the trick. But then you will get another warning along with the LNK4199 warning.

In the project’s properties Linker –> Input section, click on the button on the Delay Loaded  DLL. You can see a inheritance of OleAcc.dll. Uncheck the inheritance button. It’ll make it.


No more warnings …


[Book Review] – Schrei unter Wasser

Bei Büchern, die als Bestseller bezeichnet werden, bin ich immer sehr vorsichtig. Oft genug wurde ich enttäuscht und die so hoch gelobten Bücher entpuppten sich als Langewiler erster Güte.
Ich bin ehrlich und gebe zu, daß meine Erwartungen an dieses Buch auch nicht besonders hoch waren. Aber irgend einen Lesestoff braucht man ja im Urlaub. Das Buch wurde auf einslive angekündigt und hat allein deshalb meine Aufmerksamkeit geweckt, weil die Handlung im Norden spielt und ich schon mehrere schwedisch/dänische Krimiverfilmungen gesehen habe, die immer sehr spannend waren.

Schrei unter Wasser baut auf ein gängiges Schema auf. Ein Mord, mehr oder weniger intelligente Ermittler, und irgendwann im Laufe der Erzählung wird der Fall gelöst.

Und doch ist Schrei unter Wasser etwas anders gestrickt. Gleich zu Beginn wird in einer Art Rückblende die glücklicherweise nicht vollendete Ermordung eines Kleinkindes geschildert. Diese Rückblenden erscheinen mehrfach im Laufe der Erzählung. Zwar erkennt man den roten Faden der Entwicklung des Kleinkindes zur erwachsenen Frau, doch ist eine zeitliche Einordnung der Geschehnisse in die eigentliche Geschichte nicht möglich. Die Autoren achten peinlich genau darauf, nicht zu viele Details zu verraten.
Und kaum glaubt der Leser, die Geschichte nun in allen Zügen verstanden zu haben, schon wird er auf den nächsten Seiten eines Besseren belehrt, nimmt die Geschichte doch eine Wendung in eine Richtung, die der Leser nicht erwartet hat.

Schei unter Wasser hat einen lebendigen Erzählstil. Die Einzelheiten der Ermittlung sind in sich schlüssig. Auch hier achten die Autoren sehr genau darauf, den Leser zwar bei der Stange zu halten, aber nicht zuviel zu verraten.
Ohne die Spannung zu nehmen sei soviel verraten, daß man auch als gewiefter Krimileser bei einer überschaubaren Anzahl von Verdächtigen nicht auf den wahren Täter kommt. Zwar hegt man einen Verdacht, doch will es nicht gelingen, das Motiv zu entschlüsseln.

Erst auf den letzten 40 Seiten wird Story in einem dramatischen und temporeichen Finale aufgelöst. Unaufgelöst bleibt allerdings das Schicksal des zunächst des Mordes Verdächtigten.

Schrei unter Wasser ist empfehlenswerter Lesestoff. Es bleibt abzuwarten, wann die Geschichte verfilmt wird. Die Story taugt gut und gerne fĂĽr 90 – 100 Minuten und wird so manche deutsche Tatort Produktion in den Schatten stellen.


Review: XPages Extension Library from IBM Press

Over the past few days I managed to read the “XPages Extension Library” book that has recently been published by IBM Press. The list of authors, contributing authors, technical editors and others reads like a who-is-who in XPages.

I’m very proud to know many of them and also had the chance to attend their sessions at conferences.

The book is very up-to-date, it targets the December 2011 release of the ExtLib, be it in the form of the Notes and Domino 8.5.3 Upgrade Pack 1 or as download of the ExtLib from OpenNTF.

The samples in the book refer to the teamroom template and the ExtLib sample application. So I recommend to download the latest ExtLib from OpenNTF as the download contains both templates.

The aim of the “XPages Extension Library” book is to collate the knowledge of the ExtLib and communicate it to the reader.

Although all authors have a different style of presenting content during conference sessons, the book reads in a very consistent way.

To get the most out of the book, you should at least be familiar with the XPages basics and perhaps, you already have read the Mastering XPages book that will give you a firm ground to understand the ExtLib.

“XPages Extension Library” from IBM Press is a must have, must read.

I strongly recommend that this book, together with Mastering XPages and XPages Portable Command Line Guide should be given to every developer starting with XPages.

 


xGrid And Performance Optimization

Yesterday, I took a closer look at the xGrid custom control, that has recently been posted on OpenNTF by Pablo Solano.
The control uses jqGrid, a jQuery plugin. It lets you display data from a view in a grid. jqGrid has some very nice features. For more details take a look at the demo page.
The download from OpenNTF also contains a set of demo data ( 40.000 person documents ).

I copied the sample application to my server ( Domino 8.5.3 64bit, 16GB RAM, 4 Core AMD ), opened every view in client to create the indexes, signed application and finally opened xContactsSSJS.xsp in Firefox 12.

Next, I took a closer look at the design.

xContactsSSJS.xsp contains a duplicate line of code, which can be deleted
var docs:NotesDocumentCollection = database.search(query);

 

Although the grid was rendered almost immediately in the browser, I wanted to do some time measurement to see how long the retrieval of 40.000 documents would last and if there is some room for optimization.
So I added two lines of code into xJsonContacts.xsp at the beginning and the end of the code that simply calculates the time difference in milliseconds from start to end.

try{
	var start = new Date().getTime();
	var externalContext = facesContext.getExternalContext();
	var writer = facesContext.getResponseWriter();
	var response = externalContext.getResponse();

	// Set content type
	response.setContentType("application/json");
	response.setHeader("Cache-Control", "no-cache");

	// Get all Contacts
	var query = 'Form = "Contact"';

	var docs:NotesDocumentCollection = database.search(query);

	json = "";
	var doc = docs.getFirstDocument()

	while (doc != null) {
		json = json + '{"@unid":"'+ doc.getUniversalID() + '","FirstName":"' + doc.getItemValueString("FirstName") +
		 '","LastName":"' + doc.getItemValueString("FirstName") +  '","State":"' + doc.getItemValueString("State") +
		 '","City":"' + doc.getItemValueString("City") + '"},'		

		// Get next doc and recycle
		tempdoc = docs.getNextDocument();
		doc.recycle();
		doc = tempdoc;
	}

	json  = "[" + @Left(json, @Length(json) - 1) + "]";	

	writer.write(json);
	writer.endDocument();
	var elapsed = new Date().getTime() - start;
	print("xContactsSSJS.xsp ->" + elapsed +" ms");

} catch(e){
	_dump(e);
}

I then opened the page 10 times in the browser. here are the results

The code builds a JSON string at runtime. So, for every element, it has to access a document from the document collection and get the item values one after the other from the document.

I changed the code; first of all, I copied the Contacts view an deleted all columns except one. I put the following code into the column formula.

_fld:="FirstName":"LastName":"State":"City";

"{\"@unid\":\""
+@Text(@DocumentUniqueID)+"\","
+ @Implode (
@Transform (
_fld; "_fn" ; "\"" + _fn + "\":\"" + @Text ( @GetField ( _fn) ) + "\"" ) ; "," ) + "},"

The formula computes a JSON string for every document in the view. This avoids the need to build the string at runtime in javascript.

The code for the XAgent looks like this:

try{
	var start = new Date().getTime();
	var externalContext = facesContext.getExternalContext();
	var writer = facesContext.getResponseWriter();
	var response = externalContext.getResponse();

	// Set content type
	response.setContentType("application/json");
	response.setHeader("Cache-Control", "no-cache");
	json = ""
	  var v:NotesView = database.getView("ContactsSingleCol");
	  //do not do AutoUpdates
	  v.AutoUpdate = false;
	  var nav:NotesViewNavigator = v.createViewNav();
	  nav.setEntryOptions(
	  NotesViewNavigator.VN_ENTRYOPT_NOCOUNTDATA);
	  //enable cache for max buffering
	  nav.BufferMaxEntries = 400
	  var entry:NotesViewEntry = nav.getFirst();

	  while (entry != null) {
	    json=json + entry.getColumnValues().elementAt(0).toString();
	    var tmpentry:NotesViewEntry = nav.getNext(entry);
	    entry.recycle();
	    entry = tmpentry;
	  }

  	writer.write('[' + @Left(json, @Length(json) - 1) + ']');
	writer.endDocument();
	var elapsed = new Date().getTime() - start;
print("xContactsSSJSViewNav.xsp ->" + elapsed +" ms");

} catch(e){
	_dump(e);
}

As you can see, the code uses a NotesViewNavigator to iterate thru the view entries and concat the values from the first column of the view containing the pre-build JSON string.

Once again, I opened the page in the browser; here are the results from my simple time measurement.

Conclusion: Building the JSON in advance and using a NotesViewNavigator speeds up the loading of the data.

In the above code, the new value is added to the existing JSON String using the “+” sign. Using the ‘+’ operator for concatenation isn’t bad per se though.

It’s very readable and it doesn’t necessarily affect performance. Each time you append something via ‘+’ a new String is created, the old stuff is copied, the new stuff is appended, and the old String is thrown away. The bigger the String gets the longer it takes – there is more to copy and more garbage is produced.

An alternative way to concat strings is using a java.lang.StringBuilder. As you might know, you can use Java in your server-side javascript.

xJsonContactsViewNavSb.xsp shows, how to use a stringbuffer

try{
	var start = new Date().getTime();
	var externalContext = facesContext.getExternalContext();
	var writer = facesContext.getResponseWriter();
	var response = externalContext.getResponse();

	// Set content type
	response.setContentType("application/json");
	response.setHeader("Cache-Control", "no-cache");

	  var json:java.lang.StringBuilder = new java.lang.StringBuilder();
	  var v:NotesView = database.getView("ContactsSingleCol");
	  //do not do AutoUpdates
	  v.AutoUpdate = false;
	  var nav:NotesViewNavigator = v.createViewNav();
	  nav.setEntryOptions(
	  NotesViewNavigator.VN_ENTRYOPT_NOCOUNTDATA);
	  //enable cache for max buffering
	  nav.BufferMaxEntries = 400
	  var entry:NotesViewEntry = nav.getFirst();

	  while (entry != null) {
	    json.append( entry.getColumnValues().elementAt(0).toString());
	    var tmpentry:NotesViewEntry = nav.getNext(entry);
	    entry.recycle();
	    entry = tmpentry;
	  }

  writer.write('[' + @Left(json.toString(), @Length(json.toString()) - 1) + ']');
	writer.endDocument();
	var elapsed = new Date().getTime() - start;
print("xContactsSSJSViewNavSb.xsp ->" + elapsed +" ms");

} catch(e){
	_dump(e);
}

Opening the page in the browser shows the following results

Impressive, isn’t it?

As a fazit, even in XPages programming there is room for views and @formulas.

Uses these elements to pre-calculate values and move away this work from the runtime. Use a NotesViewNavigator to access data from the view. Read this article to find out more about NotesViewNavigator.
Conclusion: If you have to concat a large number of strings, use a java.lang.StringBuilder instead of the “+” operator.

Having done all these steps to optimize performance, it is only a small step to use Java instead of javascript to do the heavy lifting.

Here is the code from xJsonContactsJava.xsp

try{
	var start = new Date().getTime();
	var externalContext = facesContext.getExternalContext();
	var writer = facesContext.getResponseWriter();
	var response = externalContext.getResponse();

	// Set content type
	response.setContentType("application/json");
	response.setHeader("Cache-Control", "no-cache");
	var out:de.eknori.ViewColumn = new de.eknori.ViewColumn();
	writer.write(out.getViewColumnValueJSON("ContactsSingleCol",0));
	writer.endDocument();
	var elapsed = new Date().getTime() - start;
	print("xContactsSSJSJava.xsp ->" + elapsed +" ms");

} catch(e){
	_dump(e);
}

The getViewColumnValueJSON() method is located in the de.eknori.ViewColumn class.

package de.eknori;

import static com.ibm.xsp.extlib.util.ExtLibUtil.getCurrentDatabase;
import lotus.domino.NotesException;
import lotus.domino.View;
import lotus.domino.ViewEntry;
import lotus.domino.ViewNavigator;

public class ViewColumn {
	private static final String MSG_STRING_ERROR = "ERROR: ";
	private static final String MSG_STRING_NOT_FOUND = " not found";

	public ViewColumn() {
	}

	public String getViewColumnValueJSON(String viewname, int pos) {
		ViewNavigator nav = null;
		StringBuilder json = new StringBuilder();
		json.append('[');
		String strValue = "";
		try {
			View view = getCurrentDatabase().getView(viewname);
			if (null != view) {
				view.setAutoUpdate(false);
				nav = view.createViewNav();
				nav.setEntryOptions(ViewNavigator.VN_ENTRYOPT_NOCOUNTDATA);
				nav.setBufferMaxEntries(400);
				ViewEntry entry = nav.getFirst();

				while (entry != null) {
					json.append(entry.getColumnValues().elementAt(pos)
							.toString());
					ViewEntry tmpentry = nav.getNext(entry);
					entry.recycle();
					entry = tmpentry;
				}
				strValue = json.toString();
				strValue = strValue.substring(0, strValue.lastIndexOf(","))
						+ "]";
				view.setAutoUpdate(true);
			} else {
				System.out.println(MSG_STRING_ERROR + viewname
						+ MSG_STRING_NOT_FOUND);
			}
		} catch (NotesException e) {
			System.out.println(MSG_STRING_ERROR);
			strValue = "[{}]";
		}
		return strValue;
	}
}

As you can see from the entries in the screenshot, there is another gain in performance when using Java.

If your application has a large amount of data, I strongly advice to use Java instead of JavaScript.

I’m doing Java for no longer than 6 month by now. If you are familiar with LotusScript, you will be able to learn the basic java stuff real quick. There is no excuse to not start learning Java right now.

You can download the application including my modifications here.  (design only!)


xe:switchFacet – Dynamically switch content in XPages

Several roads are leading to Rome and so it is in XPages. There is always a different solution to solve a challenge.
Assume, you are using the Application Layout Control from the eXtension Library. The control offers an easy way to build a standard GUI in a few minutes.
It can have tabs and links and other things that are needed in the application to display content and switch between the different sections of an application.
The sample application, that comes with the eXtension Library shows one way to switch the content when a user clicks title bar tabs.

Here is another solution to achieve the goal. The application uses one XPage, a set of custom controls and a Variable Resolver. The variable resolver is responsible to highlight the selected value and to set the default tab. Switching the content of the left facet in the Application Layout Control is done by evaluating the value submitted via a scoped variable by a xe:switchFacet control.

Here is a picture of what the output in the browser should look like.

And here is the code for the different design elements.

home.xsp

The interesting part is in the beforeRenderResponse event of the page. setDefaultTab is a variable that is evaluated by the variable resolver. If viewScope.selectedTab does nor contain a value, a default value is returned and the according tab gets the focus.

ccLayout

ccLayout also uses the variable resolver. isTab1Selected and isTab2Selected return true or false according to value that is submitted, when the user clicks on a tab. In the onItemClick event, viewScope.selectedTab is set to the value that is defined in the submitValue= of a tab

ccSwitchFacet

The selectedFacet parameter in the xe:switchFacet control consumes the value in viewScope.selectedTab and then displays the content of the facet, tah matches the value.

ccNavTab1 / ccNavTab2

Variable Resolver

And finally, here is the code for the variable resolver. The sceleton for the code has been taken from OpenNTF XSnippets. Paul Withers has submitted the code.

package com.isatweb.jsf.core;

import static com.ibm.xsp.extlib.util.ExtLibUtil.getViewScope;

import java.util.Map;

import javax.faces.context.FacesContext;
import javax.faces.el.EvaluationException;
import javax.faces.el.VariableResolver;

public class VarResolver extends VariableResolver {

private final VariableResolver delegate;
private final String scopeVarSelectedTab = "selectedTab";

public VarResolver(VariableResolver resolver) {
delegate = resolver;
}

@Override
public Object resolveVariable(FacesContext context, String name)
throws EvaluationException {

Map<String, Object> viewScope = getViewScope();
Object sel = viewScope.get(scopeVarSelectedTab);

if ("isTab1Selected".equals(name)) {
if (sel == null) {
sel = false;
}
if (sel.toString().equals("tab1")) {
return true;
}
}

if ("isTab2Selected".equals(name)) {
if (sel == null) {
sel = false;
}
if (sel.toString().equals("tab2")) {
return true;
}
}

// sets the DEFAULT tab
if ("setDefaultTab".equals(name)) {
if (sel == null) {
viewScope.put(scopeVarSelectedTab,"tab1");
}

}

return delegate.resolveVariable(context, name);

}
}

You can download the complete application from here


org.apache.commons.collections – MultiValueMap

Maps, Treemaps and whatever have you, are a great way to store data for easy access the data by a given key. The disadvantage with this objects is that the key has to be unique. This means that you cannot store different values associated with one key.

I needed a solution to store data in a map and each key should be used for multiple values

key = human, value = Batman
key = human, value = Captain America
key = alien, value = Superman

Searching the web, I found org.apache.commons.collections

A MultiMap is similar to a Map, but which may associate multiple values with a single key. If you call put(K, V) twice, with the same key but different values, the multimap contains mappings from the key to both values.

This makes it very easy to filter a map by a given key.

package de.eknori.test;

import org.apache.commons.collections.map.MultiValueMap;

public class MultiMap {

	public static void main(String[] args) {

		MultiValueMap superheroes = new MultiValueMap();
		String key = "human";
		superheroes.put("mutant", "Wolverine");
		superheroes.put("mutant", "Beast");
		superheroes.put("alien", "Superman");
		superheroes.put("human", "Batman");
		superheroes.put("human", "Captain America");

		System.out.println(key + " superheroes: " + superheroes.get(key));
		System.out.println("There are " + superheroes.size()
				+ " different kind of superheroes: " + superheroes.keySet());

		System.out.println("All superheroes: " + superheroes.values());
	}
}

Running the code will show the following on the console:

human superheroes: [Batman, Captain America]
There are 3 different kind of superheroes: [alien, mutant, human]
All superheroes: [Superman, Wolverine, Beast, Batman, Captain America]


Lotus Domino XPages Praxis-Seminar (3 Tage) – Hannover

2Consultants bietet wieder eines ihrer fundierten XPages Seminare an.

Das XPages Praxis Seminar findet statt in Hannover vom 22. bis 24. Juni mit neuem und eigenem Konzept inklusive deutschsprachiger Unterlagen (250 Seiten in Farbe).

Als einen weiteren Vorteil sehe ich die DurchfĂĽhrungsgarantie. Im Seminarpreis enthalten ist auch die Nutzung des “fliegenden Klassenzimmers”.

Wer sich also im Raum Hannover mit dem Gedanken trägt, sich nun endlich mit XPages zu beschäftigen, der sollte jetzt zugreifen und noch heute die Anmeldung ausfüllen.

 

 

 


SSJS Extension – New Function: @GetNextBusinessDayExt()

I have added a new @Function to my SSJS project on OpenNTF.

Syntax

@GetNextBusinessDayExt( [offset] ; [baseDate] ; [excludedDaysOfWeek] ; [excludedDates] )

Parameters

offset:
Number of non-excluded days from the baseDate that the result date will be

basedate:
Date to start counting offset from. If specify “null” defaults to today

excludedDaysOfWeek:
Numer or number list. Optional. Days of the week not counted as business days, where 1 is Sunday and 7 is Saturday.
DEFAULT: 1,7 ( Sunday, Saturday )

excludedDates:
String of date strings (formatted as <CODE>SimpleDateFormat.getDateInstance().format(</CODE>)) which represent specific dates which should be excluded from the counting of days, such as holidays

Return value:
The earliest date which is offset days after the baseDate. In  counting towards this result date, any excludedDaysOfWeek and any excludedDates are excluded from counting towards the offset


Joda to the rescue

Today I had to compare to dates to find out, if the are on the same date. Both dates had a DateTime format. So my first thought was to just strip the date part. I looked at the java.util.Date class and found that most of the methods are deprecated.

Sure, you can pass the java.util.Date objects to a java.util.Calendar and sethours, minutes and seconds to zero.

But I was looking for a smarter way to accomplish the goal. Joda-Time provides a quality replacement for the Java date and time classes. Joda-Time has been created to radically change date and time handling in Java. The JDK classes Date and Calendar are very badly designed, have had numerous bugs and have odd performance effects.

Using Joda, comparison of dates is easy as pie. The toDateMidnight() method for example sets the time part to zero.

Now you can do the compare with a simple date1.compareTo(date2) .

 


Access UserBean from Java

I use the userBean that comes with the extension Library in SSJS very often. It is a convenient way to access various properties of the current user.

Today I wanted to use the UserBean in Java. But I could not figure out, how to do. After some help from Stephan Wissel and a few minutes of try and error, I ended up with the following:

package de.eknori.jsf.core;
import com.ibm.xsp.extlib.beans.*;

public class User {

public static Object getUser() {
return UserBean.get().getField( "effectiveUserName" );
}
}

A UserBean.get(9 will return all fields as shown in the picture below

The getField(String) method lets you access a single value from the bean.