Java

Sessions for #MWLUG2016

Looking over the announced sessions for MWLUG 2016 down in Austin, I’m excited. There’s a good mix of things you can use today and road maps you can apply in the future. I’m a developer, so all the Development and Best Practices sessions look interesting. The tough part will be picking which ones to attend (and record!)

Five sessions that jumped off the page at me are, in no particular order:

Debugging Java In Your Domino Applications with Julian Robichaux — Java just kills me sometimes. Heck, all of XPages does, but learning more about how to debug and troubleshoot is always useful.

Extreme Development: Pair Programming with Devin Olson and Mike McGarel — Now that Elvis Lezcano is aboard at DAI, we might have a chance to do some of this. He’s the smartest developer I’ve worked with, which I why this is the third job we’ve had together.

Think Outside The Box with Karl-Henry Martinsson — We’ve just been breaking into using REST services with jQuery and Bootstrap to present data, so getting someone else’s take on it will expand my ability to combine data from multiple databases to dazzle our users. (see Kathy’s session on dashboards for ideas she’s using for us)

Getting Your Hands on Graphs with Nathan Freeman — I have loved all the conceptual sessions and want to learn more. If Nathan can get me to understand, there’s no telling how far we can go!

A Modernized Developer’s Workflow with Domino/XPages with Eric McCormick — Workflow has always been a great strength of Notes, so getting modernized by someone who’s not spouting theory, but displaying methods in practice is exciting.

Now, before anyone complains about me not mentioning their sessions…. I’d like to sit in on about… 27 sessions. Since Marky has made no progress on the time machine and IBM doesn’t have anyone working replication of people instead of just data and design, I think I only get about a dozen sessions.

Advertisement
Categories: Conferences, Java, Xpages | Tags: , , , , , , , , , , , , , , | Leave a comment

Orphan #Java class files in the Local folder in #XPages

Every once in a while, when I do a design refresh for one of my XPages databases, I notice it adding .class files for XPages that I’ve already deleted.

So, I do another clean & build on the template, but they’re still there. Annoyed, I console myself that the files are never referenced, so I don’t need to worry about it. Of course, I worry anyway.

Vestigal Java FilesSo, in my quest to figure out what was going on, I continued poking around. I checked in the Local folder while looking at the Navigator Eclipse view and saw that the .java files were still sitting there as orphans, with no XPage in the design any more. So, I did just a ‘Clean’ and it eliminated all the files except for those. According to “Mastering XPages”, this doesn’t happen. In warning you not to create Java files in the Local folder, it states that

the incremental builder in Domino Designer would then flush the contents of the Local folder before recompiling all the XPages again. Your custom Java source files would be permanently deleted in this case.

Yet, those files stubbornly remain. Fortunately, when I create a new copy of the template, none of those Local files copy into the new database. So, my habit of creating new files for new versions of the template has helped me by dodging this little, harmless bug.

 

Categories: Java, Xpages | Tags: , , | Leave a comment

A venture into DateTimes in #XPages

Recently, I was trying to change the display of some dates in our application. We’ve found that the simplest way for our international company to display dates is to stick to the dd-MMM-yyyy format since that seems the clearest. No one wonders whether 1/4/16 represent the 4th of January or the 1st of April, because they all see 04-Jan-2016.

This was simplest to fix for all the date fields that use simple inputText controls – just change the convertDateTime pattern.

						<xp:inputText value="#{modDoc.RevPerf1Date}" id="revisedPerformanceDateStart">
							<xp:dateTimeHelper></xp:dateTimeHelper>
							<xp:this.converter>
								<xp:convertDateTime pattern="dd-MMM-yyyy"></xp:convertDateTime>
							</xp:this.converter>
						</xp:inputText>

Then, I noticed that in place I wasn’t letting them edit the dates, it wasn’t using that format. We’re not displaying the inputText, but using a label computed from that control to determine what to display. This code may actually hurt your eyes, but it did convert the date into a US-format date, like 01/04/2016.

<xp:label id="label14" style="color:black;">
	<xp:this.value><![CDATA[#{javascript:if(modDoc.isNewNote()){ 
		if(modDoc.getItemValueDate("PerfDate1") != null) {
			var termBegin = @Text(modDoc.getItemValue("PerfDate1")); 
			if(termBegin != ""){ 
				var dt2:NotesDateTime = session.createDateTime(termBegin); 
				var d = new Date(dt2.toJavaDate()); 
				var mon = ("0" + (d.getMonth() + 1)).slice(-2) 
				var td = ("0" + d.getDate()).slice(-2); 
				var yr = d.getFullYear(); 

				mon + "/" + td + "/" + yr 
			}
		} else { 
			if(sessionScope.POPerformBeginDate != null && sessionScope.POPerformBeginDate != "null" && sessionScope.POPerformBeginDate != ""){ 
				var dt:NotesDateTime = session.createDateTime(sessionScope.POPerformBeginDate); 
				dt.toJavaDate() modDoc.setValue("PerfDate1",dt); 
				var d = new Date(dt.toJavaDate()); 
				var mon = ("0" + (d.getMonth() + 1)).slice(-2) 
				var td = ("0" + d.getDate()).slice(-2); 
				var yr = d.getFullYear(); 

				mon + "/" + td + "/" + yr 
			} 
		} 
	} else { 
		var termBegin = @Text(modDoc.getItemValue("PerfDate1")); 
		if(termBegin != ""){ 
			var dt2:NotesDateTime = session.createDateTime(termBegin); 
			var d = new Date(dt2.toJavaDate()); 
			var mon = ("0" + (d.getMonth() + 1)).slice(-2) 
			var td = ("0" + d.getDate()).slice(-2); 
			var yr = d.getFullYear(); 

			mon + "/" + td + "/" + yr; 
		}
	}}]]></xp:this.value>
</xp:label>

Before we decided to convert to the new format, the ugliness of the code didn’t matter. It was used in one place (printing purchase order modifications) and it worked. Since I didn’t want to invent my own library function for computing the text value of the date in the new format, I searched for a better way to format the dates. I ran across Declan Lynch’s blog entry on using SimpleDateFormat. Unfortunately, that just points in the right direction, rather than providing working code. So, when I tried to implement that for displaying the labels correctly, I just couldn’t get it to work. This frustration led me to the simple solution: use convertDateTime on the labels. Duh!

						<xp:label id="performanceDateStartDisplay" style="color:black;">
							<xp:this.value><![CDATA[#{javascript:getComponent("performanceDateStart").getValue();}]]></xp:this.value>
							<xp:this.converter>
								<xp:convertDateTime pattern="dd-MMM-yyyy"></xp:convertDateTime>
							</xp:this.converter>
						</xp:label>

Now, on the printed purchase order modification, I also had changes in dates detailed in the text as a sentence. So, you’d see To Change the Period of performance from 01/04/2016 to 01/08/2106 to 01/11/2016 to 01/15/2016, which was not using our newly minted date format. I couldn’t figure out a way to use the converters within the text without creating several computed labels (each with a rendered formula) to display the text. Then, I remembered my dalliance with SimpleDateFormatter.

So, within that control, I brought in the package and created a function that gets the field value as a Vector using getItemValueDateTimeArray and formats it using my chosen SimpleDateFormat. The text string gets built with four calls to that function and returns our text To Change the Period of performance from 04-Jan-2016 to 08-Jan-2016 to 11-Jan-2016 to 15-Jan-2016

<xp:text id="revisedPerformanceRange">
	<xp:this.value><![CDATA[#{javascript:function getFormattedDate ( doc:NotesDocument, fieldName:String ) {
	importPackage(java.text);

	var dateFormatter:java.text.SimpleDateFormat = new SimpleDateFormat("dd-MMM-yyyy");
	var d:Date = new Date(@Today());

	if ( doc.hasItem (fieldName) ) {
		var valueVector:java.util.Vector = doc.getItemValueDateTimeArray(fieldName);
		var iterator = valueVector.iterator();

		while (iterator.hasNext()) {
			var itemvalue = iterator.next();
			if ((typeof(itemvalue)).endsWith("DateTime")) {
				d = new Date(itemvalue.toJavaDate());
				return dateFormatter.format(d);
			}
		}
	} else {
		return fieldName + " is not on the document"
	}

}

var modNotesDoc:NotesDocument = modDoc.getDocument();

var revisedPerformanceRangeText = "To Change the Period of performance from ";
	
revisedPerformanceRangeText = revisedPerformanceRangeText + getFormattedDate(modNotesDoc,"PerfDate1") + " to ";
revisedPerformanceRangeText = revisedPerformanceRangeText + getFormattedDate(modNotesDoc,"PerfDate2") + " to ";
revisedPerformanceRangeText = revisedPerformanceRangeText + getFormattedDate(modNotesDoc,"RevPerf1Date") + " to ";
revisedPerformanceRangeText = revisedPerformanceRangeText + getFormattedDate(modNotesDoc,"RevPerf2Date");

return revisedPerformanceRangeText;}]]></xp:this.value>
</xp:text>

Took some fiddling to figure it out, but gave me exactly what I wanted, two different ways.

Categories: Java, Server-Side Javascript, Xpages | Tags: , , , , , | 2 Comments

Writing and speaking about your code might actually make it better

I like to think I’m a smart guy, but I know there are many people out there who are smarter than I am. Sometimes, just thinking about Kathy and Julian showing us what we can do in Java in 10 Lines or Less helps one tighten up some code. Other times, sitting down to write about a code problem or a solution might actually make your code better.

Our workflow application builds a list of approvers at each step that’s built from three fields on the workflow step configuration document. That list is stored on the document being approved so that we don’t have to do a lookup to determine who can take action. Sometimes, the approvers might be in more than one of those fields and thus, with sloppy coding, end up in the approver list more than once. The workflow step configuration allows us to choose which of those three fields is used to notify approvers (it could even be all three fields). Since we recently decided to keep track of who is notified, since they are the primary approver(s), the workflow code is now recording the names of those notified as it sends the message. Because the system is already used in 15 projects and will be used in 70+ projects at time in the future, it must be a highly configurable workflow.

My coding challenge was simple. I wanted to display on the XPage the primary approver(s) and the other (proxy) approvers, as separate lists. This is probably a two-minute exercise for someone who knows what they’re doing, so it took me an hour.

Because I worried about the duplicates, I didn’t use the remove method of the java.util.Vector, which would only take out the first instance of the duplicate. I lamented this, since playing with arrays is a little more involved. Basically, I decided to build the array by looping through looking for matches. Then, I was puzzled about how to find array elements in a Vector, but I got over that when I realized that I could use @Unique to clean both and make them both arrays! OK, not the best idea, but it worked.

<xp:label id="recipientListLabel">
	<xp:this.value><![CDATA[#{javascript:
var recipients=procureDoc.getDocument().getItemValue("RecipientList");
return "Approvers: " + @Unique(@Name("[CN]",recipients)); }]]></xp:this.value>
</xp:label>
<xp:label id="proxyListLabel">
	<xp:this.value><![CDATA[#{javascript:
var recipients = @Unique ( procureDoc.getDocument().getItemValue("RecipientList") );
var allApprovers = @Unique ( procureDoc.getDocument().getItemValue("TSWFCurApprovers") ); 
ar proxies = [];
for ( approver in allApprovers ) { 
	if ( @IsNotMember( allApprovers[approver], recipients) ) { 
		proxies.push ( allApprovers[approver] ); 
	}
} 
var proxyList = @Implode(@Name("[CN]",proxies),", "); 
return "Proxy Approvers: " + proxyList; }]]></xp:this.value>
</xp:label>

I was pretty happy with that, because I thought it would take me a lot of extensive looping and nonsense (since a quick search hadn’t revealed an array minus array code snippet.

But, as I sat down to write about this and see if someone could better it (assuming that it would take someone two minutes), I returned to the documentation. I just wanted to double-check that remove was a “method” and that I wasn’t being foolish and calling it a “function” instead. So, then, I saw it. The code simplifier. removeAll was exactly what I was looking for and, I’d bet, something every Java coder worth their salt could have slapped on the problem in an instant. Learning curve.

<xp:label id="proxyListLabel">
	<xp:this.value><![CDATA[#{javascript:
var recipients:java.util.Vector = procureDoc.getDocument().getItemValue("RecipientList");
var allApprovers:java.util.Vector = procureDoc.getDocument().getItemValue("TSWFCurApprovers");
allApprovers.removeAll(recipients); 

var proxyList = @Implode(@Name("[CN]",allApprovers),", ");
return "Proxy Approvers: " + proxyList; }]]></xp:this.value>
</xp:label>

It’s always good to learn. When I was a Scoutmaster, one of the things I told the older Scouts was that by teaching skills, they ended up learning them even better themselves. So, as I sat down to write this, more to share the experience than to teach, I ended up learning more. It forced me to research a little, to make sure I was covering my bases and allowed me to cut some inelegant code from 8 lines to 5 (though I imagine the daring would simply cut it to 2 lines).

So, next time you or your boss thinks there’s no time for writing up your thoughts on coding, or no time to speak at conferences, or no time to share your ideas at the local user group meeting, remember that you’re likely to end up with better code even if you’re the only one who contributes. The process forces it on you.

Categories: Java, Server-Side Javascript, Xpages | Tags: , , , , , | 2 Comments

MWLUG 2014 video series on Youtube

I took along my videocamera to MWLUG 2014 in Grand Rapids and I’ve created a playlist of the videos that I’ve already uploaded to Youtube. Since I’m a developer, it leans heavily on development sessions.

As more are added, they will appear there and here….

The playlist includes the following videos thus far:

AD104: Build A Bean Workshop – Devin Olson and Mike McGarel

BP107 Java versus Javascript: There really is no competition – Andrew Barickman

AD101: Achieving Developer Nirvana With Codename BlueMix – Ryan Baxter

AD105: Building a Structured App With XPages Scaffolding – Jesse Gallagher

Bonus track: WWII veteran Virgil Westdale in the Opening General Session

Other sessions are waiting on some approvals from the speakers and some editing (one session had live data displayed, so I have to edit that out)

These only give an impression of the event. You get an awful lot more out of it if you attend. So, see you in Atlanta next year!

Categories: Conferences, Java, Videos, Xpages | Tags: , , , , , , , | 2 Comments

Finding user roles in #XPages

I’ve written a piece before on roles in XPages, but since that dealt with using the ACL to limit access to a page and not about the programmatic use of roles, I wanted to return to the issue.

Back in old Notes, if we wanted to hide something based if the user did not have a certain role in the ACL, we could use a pretty simple formula:

!@IsMember(“[roleNameHere]”; @UserRoles)

Remarkably, it’s not that much harder in XPages, but there are some important wrinkles to be concerned about. As noted previously and by Russ Maher on his blog, you must remember to use your brackets [] and also keep in mind the result you want (true or false). Remember that the XPages formulas are ‘rendered’ formulas, meaning you want to ‘true’ to display the result and ‘false’ to hide it, so you’d use:

@IsMember(“[roleNameHere]”, context.getUser().getRoles());

Here’s the more complete source code for the rendered formula:

<xp:this.rendered><![CDATA[${javascript:@IsMember("[Testing]", context.getUser().getRoles());}]]></xp:this.rendered>

Now, I’m not sure what impact of referencing the user roles that way has on performance, but since I now I’m using them in many rendered formulas all over my application, I decided to compute it once and then reuse it many times. I put a few extra lines into a control that’s on my main application layout control to drop it into a sessionscope variable. I suppose I might shave a millisecond off if I only computed that once per session, but I didn’t go that far.

<xp:this.beforePageLoad>
<![CDATA[${javascript:var roles = context.getUser().getRoles();
sessionScope.userRoles = roles;}]]>
</xp:this.beforePageLoad>

Then, in order to check to see if my user has one of three roles when rendering an item, I could use this code:

<xp:this.rendered><![CDATA[${javascript:var manager = @IsMember("[InventoryMgr]", sessionScope.userRoles);
var viewer = @IsMember("[InventoryViewer]", sessionScope.userRoles);
var grantsManager = @IsMember("[InvMgrGrants]", sessionScope.userRoles);
if ( manager || viewer || grantsManager ) { return true };
return false; }]]>
</xp:this.rendered>

Now, I also found that sometimes I need to determine the user’s role in my Java code. That’s also not that hard, except that vectors are not quite arrays. If there is a single value, it’s not the same as a multiple value vector. I’m not sure if this is a Notes implementation issue or if it’s the way Java always handles vectors. That is, if it’s a single value, it puts our brackets [] around the value, but it does NOT for multiple values. So, when I was using the code written for us, it wasn’t always picking up the roles correctly. Once I simply told it to check both ways, our code worked more cleanly. (The reference to ExtLibUtil comes from the original code, so I didn’t modify it.)

public Vector getCurUserRoles() {
	try {
		curUserRoles = ExtLibUtil.getCurrentDatabase().queryAccessRoles(ExtLibUtil.getCurrentSession().getEffectiveUserName());
	} catch (NotesException e) {
		this.debug("getCurUserRoles ERROR: " + e.getMessage(), "error");
		curUserRoles = new Vector();
	}
	return curUserRoles;
}

public boolean hasRole(String role, String uname) {
	try {
		Vector roles = this.getCurUserRoles();
		if (roles.contains(role))
			return true;
		if (roles.contains("["+role+"]"))
			return true;
		return false;
	} catch (Exception e) {
		this.debug("hasRole ERROR: " + e.getMessage(), "error");
		return false;
	}
 }

Note that the debugging uses Mark Leusink‘s DebugToolbar, which I highly recommend to everyone.

Categories: Java, Old Notes, Security, Xpages | Tags: , , , , , , | 2 Comments

Java still not refreshing correctly in #XPages 8.5.3 UP 1

You know, I had started on a post on the great power of comparing design elements, but all I’d written so far was:

With the move to an Eclipse-based Domino Designer Environment, there was at least one significant improvement – the native ability to compare design elements.

I was a big fan of TeamStudio‘s Delta, since I often needed to compare versions of my designs either to document the changes I’d made or, in rare circumstances, do a little troubleshooting to see where I’d accidentally screwed up my designs. It was a great tool, with a lot of smarts about how to report on the designs and it even allowed you to compare documents. They have, of course, updated Delta to provide more capability than is native in Notes – allowing graphical display of design differences rather than just code variances. That said, I don’t have a current license, so I’m using the native tools.

In one of my hotter posts, I’d noted that Java is not behaving well in my environment. The basic behavior is that I’d make changes to my template, do a build and then, when I refreshed the design on the server, things would not refresh properly in Java. In the last few days, the behavior has become very consistent. For some reason, it plagues Mark Leusink‘s DebugToolbar, which is an excellent tool that works fine when my environment behaves.

Basically, the end-user experience of the problem was that, every once in a while, without code changes to the DebugToolbar, I’d start getting Error 500’s that it could not find that class. So, I started to get in the habit of checking in the WebContent/WEB-INF/classes folders using the Navigator view in my designer client to see if the classes were all there.

When builds worked, it would create the DebugToolbar and Message classes, plus the three private classes within DebugToolbar (FieldComparable, FileInfo and MethodComparable). When things didn’t work, sometimes it wouldn’t create the two main classes. Refreshing the design on the server again would never repair it. It would often create 2 or 3 additional copies of each of the private classes, appending what looks like a UNID to the class name. Those tend to have file sizes like -1kb.

Bad Class List

This started in August, as I was making changes to some other Java files. I never changed DebugToolbar, but it would often get corrupted. It settled down as my Java changes ended. I went on vacation for two weeks (Avignon!) and returned to a stable environment. Of course, no one had touched the Java while I was gone and I saw no need until a few weeks ago to make any changes myself.

Now, I had also updated to v3.01 of the DebugToolbar and moved all my Java files into Code/Java from the WebContent/WEB-INF/src folder so that access to them was simpler. However, having been back and forth between versions and back and forth with where the code was stored, I think I can eliminate those as independent problem sources.

Nonetheless, I can consistently see the extra classes and error 500s every time I refresh the design. Deleting the bad classes doesn’t solve anything. Replacing all the class files with the ones from the template also doesn’t work.

If I build the database on the server, the problem goes away.

I’ve been getting frustrated. This might work for me if I’m the only one who makes changes, since the Bethesda server is always a few short hops from me. However, as I may have mentioned before, I work for a very global-oriented company and sometimes our developers are out at project sites where connectivity is slow on good days. Quite frankly, doing a build on the Bethesda server while sitting in Jalalabad is never going to finish in an acceptable time frame. One can build a local template quickly and refresh design far faster.

Fortunately, today I got a new clue.dojo complaint

I started to wonder about corrupt files or problems with the server. So, I created a completely blank file and applied my template (which showed the 5 class files, named normally) to that file. The class files get all corrupted. So, I created a blank file and built as a template from my template. It got corrupt files and also didn’t work as template after building (the same result in the class files). Then, I decided to copy over design elements ad paste them into a blank file. I did a build and got some errors because I’d forgotten to copy over all the libraries and other files hidden under WEB-INF. So, I decided to compare the two databases so I could see what I needed to copy still. Then, I looked at the Java files produced by the XPage builds, just it showed up in the differences between the files and I was shocked. The design on the one I was ‘creating from scratch’ showed all kinds of old versions.

In each of the ‘old’ builds, the Java file created for the XPage would have super(“8.5.3”); but, as shown above, my cut-and-paste built ones would have various complaints, naming other versions and usually with a comment on why. The different line displayed would usually be one of the following:

super("3.0");

super("8.5.2"); // version of xp:view dojoForm

super("8.5.2"); // version of xp:eventHandler disableValidators

super("8.5.1"); // version of xp:eventHandler script

I’d never have noticed if I wasn’t using Compare To Each Other. Now, I need to reinstall Notes and see if the problem goes away or continues. I had Ariwan try and it only worked once for him – and I had to rebuild it on the server – so my hopes are not real high. Since Eric Tomenga has submitted a PMR on the issue, we’re hoping some IBM minds add to my few readers in trying to puzzle out why this is happening.

 

Categories: IDE, Java, Utilities, Xpages | Tags: , , , , , , , , , | 3 Comments

Multi-value Sorting in #Java

I’ve been working on trying to figure out how to sorting Notes views on-the-fly as we’re exporting to Excel and my first thought was, “Oh, that will be in the POI documentation.” I assumed that there would be a simple function in the Apache POI documentation for XSSF to sort rows and that it would also allow us to do multiple column sorts, just like we can manually in Excel. Apparently, doing this is not high on the POI team’s list of tasks.

So, after coming up dry on POI, then on Stack Overflow, I decided to check the interwebs to see what I could find. Sorting collections in Java is actually not all that hard. There are built-in functions for it, so all you have to do is issue a Collections.sort(myObject). Where it gets interesting is in how you compare the objects in the collection.

In order to determine order within the collection, all you have to do is create an integer function within the class named compareTo. Then, it will sort them based on the result returned (comparing one object at to another, not jumping six or seven places at once). So, it you want to sort on multiple values, you return them in reverse order of significance. For example, if I want to sort based on Department, then roll number, then name, I would use the following code:

if ( alphaDepartment == 0 ) {
	if ( rolldifference == 0 ) {
		return alphaName;
	}
	return rolldifference;
}
return alphaDepartment;

When the Department does not match, we return the Department order. If Department matches, we check roll number, returning the difference if it does not match. Finally, if both Department and roll number matched, we return the name order. The bulk of the work is in setting up your compareTo. While this one is hard-coded, I’m sure we’ll be able to figure out how to use variables to identify which value we want to use in our own sorting.

(I’ve dispensed with listing the setters, as they are not needed in this example.)
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@SuppressWarnings({ "unchecked" })
public class Student implements Comparable {

	private String name;
	private String rollNumber;
	private String department;

	// Constructor for the class
	public Student(String name,String rollNumber,String department){
		this.name = name;
		this.rollNumber = rollNumber;
		this.department = department;
	}
	public String toString() {
		return name + ", " + rollNumber + ", " + department;
	}
	public String getName() {
		return this.name;
	}
	public String getRollNumber() {
		return rollNumber;
	}
	public String getDepartment() {
		return this.department;
	}

	//This Function is responsible for sorting.
	public int compareTo(Object student1) {
		if (!(student1 instanceof Student))
			throw new ClassCastException("A Student object expected.");

		Student studentInput = (Student) student1;
		String thisname = this.getName();
		String thatname = studentInput.getName();
		int alphaName = thisname.compareTo(thatname);

		int rollNumb = Integer.parseInt(studentInput.getRollNumber());
		int hostObjrollNumb = Integer.parseInt(this.getRollNumber());
		int rolldifference = hostObjrollNumb - rollNumb;

		String thisDepartment = this.getDepartment();
		String thatDepartment = studentInput.getDepartment();
		int alphaDepartment = thisDepartment.compareTo(thatDepartment);

		if ( alphaDepartment == 0 ) {
			if ( rolldifference == 0 ) {
				return alphaName;
			}
			return rolldifference;
		}
		return alphaDepartment;
	}

	public static void main(String[] args) {
		List<Student> studentList = new ArrayList<Student>();

		//Create our Student objects
		Student s1 = new Student("Tom","3","CS");
		Student s2 = new Student("Jerry","1","Electronics");
		Student s3 = new Student("Merry","4","IT");
		Student s4 = new Student("Tom","2","IT");
		Student s5 = new Student("Jerry","5","IT");
		Student s6 = new Student("Merry","6","Electronics");
		Student s7 = new Student("Tom","1","CS");
		Student s8 = new Student("Jerry","2","IT");
		Student s9 = new Student("Merry","3","Electronics");

		//Add Students to our ArrayList

		studentList.add(s1);
		studentList.add(s2);
		studentList.add(s3);
		studentList.add(s4);
		studentList.add(s5);
		studentList.add(s6);
		studentList.add(s7);
		studentList.add(s8);
		studentList.add(s9);

		//The actual sort command
		Collections.sort(studentList);

		System.out.println("\nThe student list in ascending sequence is:\n");
		for (Student person : studentList) {
			System.out.println(person);
		}
	}
}

As you can see, I included the data in the Java code, since I’m trying to keep it simple. When we move forward to trying this with either the Excel sheet we’ve produced or the data before we export it to Excel, things will be more complicated. Nonetheless, I am enthused about the start I’ve made here.

There are few places that were key in helping me understand this AND from which I borrowed much of the code. On Stack Overflow, there was a question about sorting a multi-dimensional array that got me started (I’ve submitted a correction to the OP’s self-answer, since his self-answer doesn’t actually work.) While that taught me some things, none of that code appeared here. The Student object and my first exposure to how compareTo functioned came in a post on sorting a list in ascending order in Java. Other pages had mentioned compareTo, but it made more sense when I saw it there. The final, clear understanding came with sorting a collection containing user-defined objects, which thankfully also made clear to me how to loop the output properly.

Categories: Java, Utilities | Tags: , , , , , , | 2 Comments

Exporting from #XPages to Excel without Excel, Part 1

As much as I’d love my users to simply access their data in my Notes databases, I know they’re going to need to export it. Mostly, they want to be able to manipulate it to analyze it, but they also want to create static reports and to send those reports to external parties that we don’t want accessing our data live. In old Notes, I could force train people to use the export functions of Notes, or with the advent of Copy As Table, scream at teach them to cut-and-paste. Back when I was at a government agency, helping them email their order via spreadsheet from an outside vendor in a semi-automated process, it required the user to have Excel on their machine (initially, only specific versions of Excel, but I learned to get beyond that). Fortunately, Russ Maher taught me that you don’t have to do it that way in his talk Extending Your XPages Applications with Java at AdminDev 2012.

My favorite part of his talk, since I’d done the aforementioned Notes-to-Excel export, was when he talked about creating Excel files even if the user didn’t have Excel. Apache POI is a Java API for Microsoft documents (download page for the JAR files) and by loading the JAR file into your Notes databases, you can use all of the functionality in your XPages applications. Thus, I can use my Motorola XOOM tablet to access the project database, select the inventory report, generate the Excel file and download it directly to my tablet without having Excel on the tablet. I showed the result to office mate and he came up with the inventory idea — no need to print out the inventory and you can make notes on your tablet as you walk around. Heck, I’m sure I could also write something that would process changes back into the database there as well, but that remains for a future date.

Architectural Information

Before we plunge into the code, let me first explain the architecture and use case involved here. Each of our projects around the world uses one of our applications, which we call TAMIS. In the past, this meant one Notes database for each project,  with perhaps 70 projects active at a time. Each database would start with the same design and then be customized for the project. This has usually meant design changes rather than just configuration, but both methods are used. So, there will be some configuration that is done by local staff and some by the development team. Configuration of these Excel reports is something that has always been done by the development team.

In the XPages version our application, there are actually four databases: Shared Resources, Main, Attachments and Workflow. The design work was done by Scott Good’s folks at Teamwork Solutions, so it bears many marks of their Process It! workflow engine. Shared Resources contains all the XPage design elements, configuration documents and some general information. Main is where the data goes. Attachments is obvious, but Workflow not as much – it contains both the workflow configuration and the workflow tracking documents.

Given all of that, we will have our code in Shared Resources and our data over in Main. Thus, we want to launch from one database, grab data from another and return it all to the user as a seamless download that pops into his machine just by clicking one button on our XPage.

So, how do we do this?

POI JarAdd JAR to database

Well, first thing to do is to put the JAR file into your Notes database. I’m still on 8.5.3, so I have to put it in \WEB-INF\lib, while you can bring it in directly as a design element in Notes 9. As you can see in the image here, I’m using version 3.6 of the JAR file, despite the fact that 3.9 is the current release, but I don’t really mind being behind the times a little bit, as long as it works. It looks like 3.7 and 3.8 added some fixes for handling dates and numbers better, so I will probably upgrade after our next pilot rolls out. We have seen occasional issues with dates, being a very international company.

ReportKeywordCreate the Configuration Form

The was arguably my favorite part of the process. Well, until it actually started spitting out spreadsheets. Why did I enjoy it so much? Is it because it’s ‘old Notes’? I don’t think so. While the form did build off the keyword documents that my old development mentors (Elvis Lezcano and John Mirza) created back in the 1990s when we were all at Exxon-Mobil, the neat part was utilizing some of the knowledge I’d gained about navigating design elements in simple, front-end LotusScript. Basically, the user selects the view (via name or alias) and then can choose the columns right from the view design rather than requiring any pre-configuration by me. While I do need to work on the configuration form to add more choices about sorting and styling, I got pretty happy with the results.

The configuration form supplies the view name, the column headers and numbers, and identifies which columns to total. This gets used by the Export Stream Library when the user initiates the export.

Create your Export XPage

This was the simplest design element, though also, since I’m still so new to XPages, the most challenging. With the configuration document, I was working with LotusScript, so the new wrinkles were just fun extensions of my knowledge. The Export Stream Library was initially just something I took wholesale from Russ’ presentation. While I did start with Russ’ XPage, I ended up, because of the configuration document and the UI things I wanted to do, making myself jump through a few hurdles. Worse yet, when I went to move it from it’s original implementation, I stumbled several times on the fact that the button was…. disabled. Nothing like clicking and checking your code 10 different ways and then examining it using ‘Inspect Element’ to realize the problem is that the disabled attribute simply computes as true every time.

Our Export Xpage allows the user to select from all of the report configurations we’ve created and provide both the filename (to which it will append .xls) and the name for the worksheet. I’m sure that it wouldn’t take much additional work to export multiple worksheets into a single file for a more complex and useful product, but we’re starting pretty simple.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
	xmlns:xp_1="http://www.ibm.com/xsp/coreex">
	<xp:this.resources>
		<xp:script src="/CreateExcelWorkbookStream.jss" clientSide="false">
		</xp:script>
	</xp:this.resources>
	<xp:label id="label1" value="Create Reports"></xp:label>
	<xp:br></xp:br>
	<xp:br></xp:br>
	<xp:table>
		<xp:tr>
			<xp:td>
				<xp:label value="Choose Report:" id="label2"></xp:label>
			</xp:td>
			<xp:td>
				<xp:comboBox id="comboBox1">
					<xp:selectItems id="selectItems1">
						<xp:this.value><![CDATA[#{javascript:var noval = [];
						noval[0] = "Please select a report";
						var forms = @DbColumn(@DbName(),"ExcelReports",1);
						var vals = noval.concat(forms);
						return vals;}]]></xp:this.value>
					</xp:selectItems>
					<xp:eventHandler event="onchange" submit="true"
						refreshMode="complete" refreshId="wbName" id="eventHandler1">
						<xp:this.script><![CDATA[var x= '#{javascript:getClientId("comboBox1")}'; var z= '#{javascript:getClientId("wbName")}'; var tmp = document.getElementById(x).value; document.getElementById(z).value=tmp.replace(/ /g,"_"); ]]></xp:this.script>
					</xp:eventHandler>
				</xp:comboBox>
			</xp:td>
		</xp:tr>
		<xp:tr>
			<xp:td>
				<xp:label value="File Name:" id="label4" for="wbName">
				</xp:label>
			</xp:td>
			<xp:td>
				<xp:inputText id="wbName"
					disableClientSideValidation="true" required="true"
					defaultValue="Sample">
					<xp:this.validators>
						<xp:validateRequired>
							<xp:this.message><![CDATA[#{javascript:return getLabelFor(this).getValue() + " is a required field.";}]]></xp:this.message>
						</xp:validateRequired>
					</xp:this.validators>
				</xp:inputText>
			</xp:td>
		</xp:tr>
		<xp:tr>
			<xp:td>
				<xp:label value="Sheet Name:" id="label5"
					for="sheetName">
				</xp:label>
			</xp:td>
			<xp:td>
				<xp:inputText id="sheetName"
					disableClientSideValidation="true" required="true"
					defaultValue="Report">
					<xp:this.validators>
						<xp:validateRequired>
							<xp:this.message><![CDATA[#{javascript:return getLabelFor(this).getValue() + " is a required field.";}]]></xp:this.message>
						</xp:validateRequired>
					</xp:this.validators>
				</xp:inputText>
			</xp:td>
		</xp:tr>
		<xp:tr>
			<xp:td>

			</xp:td>
			<xp:td>
				<xp:messages id="messages1"></xp:messages>
			</xp:td>
		</xp:tr>
	</xp:table>
	<xp:br></xp:br>
	<xp:br></xp:br>
	<xp:button value="Create Report" id="button1">
		<xp:this.disabled><![CDATA[#{javascript:getComponent("comboBox1").getValue() == "Please select a report";}]]></xp:this.disabled>
		<xp:eventHandler event="onclick" submit="true"
			refreshMode="complete">
			<xp:this.action><![CDATA[#{javascript:
function postValidationError(control, msg) {
    if ((typeof msg) != "string")
            return;
    var msgObj = new javax.faces.application.FacesMessage(javax.faces.application.FacesMessage.SEVERITY_ERROR, msg, msg);
    facesContext.addMessage(control.getClientId(facesContext), msgObj);
    control.setValid(false);
}
var control = getComponent("comboBox1");

var wbName = getComponent("wbName").getValue();
var sheetName = getComponent("sheetName").getValue();
var formName = getComponent("comboBox1").getValue();
var fpapp:NotesDatabase = session.getDatabase(@DbName()[0],@DbName()[1]);
var lkey:java.util.Vector = new java.util.Vector;
lkey.addElement(formName);
var lview = fpapp.getView("ExcelReports");
var doc:NotesDocument = lview.getDocumentByKey(lkey,true);
if ( !@IsNull (doc) ) {
	var viewName = doc.getItemValue("ViewName");
	postValidationError(control,"ViewName: " + viewName);
	var columns = doc.getItemValue("columnNumbers");
	postValidationError(control,"columnNumbers: " + columns);
	var colFields = [];
	for(var i=0;i<=columns.length-1;i++){
		colFields.push(columns[i]);
	}
	labelList = doc.getItemValue("Value");
	var labels = [];
	for(var i=0;i<=labelList.length-1;i++){
		labels.push(labelList[i]);
	}
	var columnsWithTotals = doc.getItemValue("ColumnsWithTotals");
	var totalLabels = [];
	for(var i=0;i<=columnsWithTotals.length-1;i++){
		totalLabels.push(columnsWithTotals[i]);
	}
	createWorkbookStreamWithLabels(wbName,sheetName,colFields,viewName[0],labels,totalLabels);
}
}]]></xp:this.action>
		</xp:eventHandler>
	</xp:button>
	<xp:br></xp:br>
</xp:view>

Create your Export Stream Library

Due to the length of this post, the library is in a second post. I’ve also added a sample database.

Categories: Java, Server-Side Javascript, Xpages | Tags: , , , , , | 7 Comments

Java still vexing me in #xpages

So, as I noted in my last post about Java in XPages, things have not been smooth for me. Updates to my Java class files don’t always seem to work.

I’ve got a database that’s called Shared Resources and contains mostly XPages and configuration documents for our project. Data is stored in Main, workflow configuration and tracking goes into Workflow, and Attachments is self-explanatory. Our intention is that this will be mostly used via XPiNC, since many laptops will be in remote locations without server access.

I’m using templates, creating new ones incrementally so I don’t lose any of my intermediate changes, plus creating roll-backs in case changes snuck in somehow. I’ve got my own ID and a Development Admin ID that I use for signing. The Development Admin ID is required for the production servers, so it’s possible that signing issues have been a part of the problem. I have three database suites that I push the design to, two of those being on my development server (my Sandbox and our test copy) and the third being in production (but not yet even as a pilot, just validation testing in that environment).

I’m not currently changing any code, but trying to determine whether having custom Java code in 8.5.3 can work in either Code/Java or WebComponents/WEB-INF/src and finding that it doesn’t seem to matter. It seems to fail in either place almost at random. I know there must be a pattern, but I have had difficulty finding that pattern and need some help.

So, steps that I have followed….

  1. Clean & Build on template
  2. Refresh design of production database
  3. Marvel at failure of DebugToolbar or partial succcess (load first page, but not requisition page) or complete success on XPiNC (with complete or partial failure in IE)
  1. Clean & Build on local replica of production database
  2. Replicate with server
  3. Marvel at over-write of class files on local from server
  1. Clean & Build on server (go walk dog, practice French in Pimsleur, or contemplate fine wines)
  2. Restart Notes client since it will have cached the XPages
  3. Usually marvel at success but agonize over the idea of knocking everyone off the database while I build
Error while executing JavaScript action expression
javax.faces.FacesException: Can't instantiate class: 'eu.linqed.debugtoolbar.DebugToolbar'.. java.lang.ClassNotFoundException: class java.lang.ClassNotFoundException: eu.linqed.debugtoolbar.DebugToolbar
Can't instantiate class: 'eu.linqed.debugtoolbar.DebugToolbar'.. java.lang.ClassNotFoundException: class java.lang.ClassNotFoundException: eu.linqed.debugtoolbar.DebugToolbar
java.lang.ClassNotFoundException: class java.lang.ClassNotFoundException: eu.linqed.debugtoolbar.DebugToolbar
class java.lang.ClassNotFoundException: eu.linqed.debugtoolbar.DebugToolbar

Expression

   1: #{javascript:if (typeof dBar != "undefined") {
   2: 	dBar.init(compositeData.defaultCollapsed, compositeData.toolbarColor);
   3: }}

It does seem almost random. Since it sometimes works, I know it’s not a problem with the code that is in the files. I know from Stack Overflow links in the last post that Java residing in Code/Java does not always behave, but I seem to have problems in both locations. There are few people who do have designer rights to the database, but they’ve all been staying out of it in Designer since I started reporting problems. We all have de-selected Build Automatically, but some registration of components seems to occur if I open the production database in Designer using my personal ID. I don’t know if that is adding to the problem or not.

I’ve read some people suggest that the problem goes away in Notes 9, but since it shouldn’t have been there in 8.5.3, I don’t know if I should believe that. I also would rather not toss the additional variable of moving to Notes 9 in when we think we’re less than a month from a pilot deployment.

Right now, it works in XPiNC and IE in both of my development instances, and in XPiNC in production, but fails in browsers there. I leave for two weeks in the south of France on Thursday and they can’t really cope with something that breaks for no apparent reason while I’m gone.

Categories: IDE, Java, Xpages | Tags: , , , | 11 Comments

Blog at WordPress.com.

%d bloggers like this: