Design patterns in xPages: The repository Pattern (some sort..)

For my current home project I’m busy exploring the Java side of xPages more and more because of my general love for this language. The project involves a little website that should display the current standings and results of hockey teams ( wheelchair hockey ) during the season.

A little feature list

– Support for seasons: Create seasons add teams to a season
– Support for teams: basic crud actions
– Support for competition days and games: basic crud actions
– A list of current standings and results: Realtime calculations for teams etc.

I wanted to use the objectdata datasource for this one. So I searched a bit on the net, remembered the splendid presention from Thimo Janssen and wrote a little code.

Because I didn’t want to fill my pojo’s with the code about how to read and save them I decided to write something called a Repository class. A Repository class is a class that knows all about saving/retrieving an object to and from an datasource for instance a Domino database!

This way we can keep the Object (pojo) itself neat and clean. Now for some code:

I’ll start by showing you the code for the xPage

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
	xmlns:xc="http://www.ibm.com/xsp/custom"
	xmlns:xe="http://www.ibm.com/xsp/coreex">

	<xp:messages id="messages1"></xp:messages>
	<xp:this.data>
		<xe:objectData var="season" scope="request"
			saveObject="#{javascript:seasonService.set(season)}"
			createObject="#{javascript:seasonService.get(param.id);}">

		</xe:objectData>
	</xp:this.data>

	<xp:label for="txtDescription">
		<xp:this.value><![CDATA[#{javascript:if(season.isNew())
	return "Nieuw seizoen";

return "Seizoen aanpassen";
 }]]></xp:this.value>
	</xp:label>
	<ul>

		<xp:inputText value="#{season.description}" id="inputText1">
			<xp:eventHandler event="onkeypress" submit="true"
				refreshMode="complete" disableValidators="true">
				<xp:this.script><![CDATA[return (event.keyCode==13);]]></xp:this.script>
				<xp:this.action>
					<xp:saveDocument var="season"></xp:saveDocument>
				</xp:this.action>
			</xp:eventHandler>
		</xp:inputText>

		<xp:repeat id="seasonlist" value="#{seasonService.all}"
			var="season">
			<li>
				<xp:image id="isCurrentSeason">
					<xp:this.url><![CDATA[#{javascript:var path = "/icons/vwicn032.gif";

if(applicationScope.currentSeason == season.getKey()){
	path ="/icons/vwicn031.gif";
}

var url:XSPUrl = new XSPUrl(database.getHttpURL());
url.setPath(path);
return url.toString();}]]></xp:this.url>
					<xp:eventHandler event="onclick" submit="true"
						refreshMode="partial" refreshId="seasonlist" execMode="partial">
						<xp:this.action><![CDATA[#{javascript:applicationScope.put("currentSeason",season.getKey());
}]]></xp:this.action>
					</xp:eventHandler>
				</xp:image>
				<xp:link text="#{season.description}">
					<xp:this.value><![CDATA[#{javascript:return "/Administrator.xsp?p=season&id="+season.getKey();}]]></xp:this.value>
				</xp:link>
			</li>
		</xp:repeat>
	</ul>

	<xc:ccCompetitieDagenList></xc:ccCompetitieDagenList>
</xp:view>

In the above piece of code you can see the createObject and saveObject properties who are both using the same object called  “seasonService”. This service is the so called Repository class. As said this object is responsible for retrieving and saving an object to the database ( in this case Domino ).

Lets have a look at the code of the Repository Object.

package eu.jeroensomhorst.hockeymanager.services;

import java.util.Vector;

import lotus.domino.Document;

import lotus.domino.Database;
import lotus.domino.NotesException;
import lotus.domino.ViewEntry;
import eu.jeroensomhorst.cms.util.DominoUtil;
import eu.jeroensomhorst.domino.api.ViewEntryEx;
import eu.jeroensomhorst.hockeymanager.model.Entity;
import eu.jeroensomhorst.hockeymanager.model.Season;

public class SeasonService extends AbstractService {

	private static final long serialVersionUID = -7353982216625578326L;

	public SeasonService() {
		super.FORM = "Season";
	}

	protected final Season fromEntry(ViewEntry entry) {
		ViewEntryEx vex = new ViewEntryEx(entry);
		System.out.println("Retrieve season from entry");

		Season s = new Season();
		s.setDescription((String) vex.getColumnValue("Description"));
		s.setKey((String) vex.getColumnValue("Key"));

		try {
			entry.recycle();
		} catch (NotesException e) {

		}
		return s;
	}

	public boolean delete(Entity t) {
		// TODO Auto-generated method stub
		return false;
	}

	public Season get(String key) {

		if (key != null && !"".equals(key)) {
			Vector<String> viewKey = new Vector<String>();
			viewKey.add("Season");
			viewKey.add(key);

			try {
				ViewEntry entry;
				entry = DominoUtil.getEntryByKey(".AllDocuments", viewKey);
				return this.fromEntry(entry);
			} catch (NotesException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
		return new Season();
	}

	public Entity set(Entity t) {
		System.out.println("Saving entity");
		if(t instanceof Season){
			Season season = (Season) t;
			Database db = DominoUtil.getCurrentDatabase();
			Document doc = null;
			if(season.isNew()){
				try{
				doc = db.createDocument();
				doc.replaceItemValue("Form","Season");
				doc.computeWithForm(true,false);
				doc.replaceItemValue("Description",season.getDescription());
				doc.save(true,false);
				season.setKey(doc.getUniversalID());
				}catch(NotesException e){

				}finally{
					try {
						doc.recycle();
					} catch (NotesException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}else{
				db = DominoUtil.getCurrentDatabase();
				try{
					doc = db.getDocumentByID(season.getKey());
					doc.replaceItemValue("Description",season.getDescription());
					doc.save(true,false);		
				}catch(NotesException e){

				}finally{
					try {
						doc.recycle();
					} catch (NotesException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

			}
		}
		return t;
	}

}

The most important parts for now are the GET and the SET methods. As you can see the GET method is capable of retrieving an entry by Key or create a new object (and what you also see is that when an object by key is not found a new object is created… #bug!). If no key is provided a new empty instance of the season class is returned.

When a key is provided the code will do a getentrybykey search on a predefined view. With this retrieved entry the season object is constructed using the fromEntry method. The fromEntry method is there because on another place in the code we want to retrieve data using a view entry as well. Namely the getAll Method. This retrieves ALL season objects using a predefined key / view name and from every viewentry a season object is constructed. Because I’m lazy and don’t want to write / update the same code on several places I created the fromEntry method.

Secondly the SET method. The set Method accepts an Entity object ( because we defined an interface for the repository object. We will talk about them later in another post.. maybe ). At the beginnning we check if the given object is of type Season. If so we create or update the datastore ( aka the notesdocument ). The update takes place only when the object is not new in other words when a key ( uniqueid ) is provided in the object. If the object doesn’t have such a key we assume the object is new and create a new document and save it. When the save succeeds the universalid of the document is saved into the season object. At last the season object is returned.

This code is not perfect. For instance what to do when the update/save of the season creates an error? Should we throw an error or should we return a null reference to tell the ui the code made a booboo? Anyways.. all comes to an end so this will conclude this little post about Java and xPages. I will , when the project progresses add some more info about Java and xPages. In the meantime. Stay tuned!

 

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.