Tuesday, May 29, 2007

Cocoon & Ajax : Populating a suggestion list From DB using JavaSelectionList

A few months back i was looking for an example where a suggestion list was populated from the DB using a java class. Well i was not able to find any good example.
So i thought of writing a small sample about the same for my first blog :)

IF you are reading this blog i am sure you might be familiar with the site map and stuff so i wont go into those details. For the cForms we need a forms , binding and template file. I will be binding this form to a Java bean. The form.xml file is some what like this:



<fd:form fd="http://apache.org/cocoon/forms/1.0#definition" i18n="http://apache.org/cocoon/i18n/2.1">
<fd:widgets>
<fd:field id="user" required="true">
<fd:label>Username:</fd:label>
<fd:hint>Username</fd:hint>
<fd:validation>
<fd:length min="2" max="100"/>
</fd:validation>
<fd:datatype base="string"/>
<fd:suggestion-list type="java" class="com.cforms.UserNameSelectionList"/>
</fd:field>
<fd:submit id="oks>
<fd:label>Submit</fd:label>
</fd:submit>
</fd:widgets>



With the suggestion list we define a class which implements JavaSelectionList. we will have a look at that class later first lets have a look at the binding file. In the binding file we bind the values to a backing bean.


<fb:context fb="http://apache.org/cocoon/forms/1.0#binding" path="/">
<fb:value id="user" path="user/username">
</fb:value>
</fb:context>


so we bind the field username with the bean User with a variable username. This binding takes place in the flowscript. In my next blog i will be wrting about this and also about how to have a generalized flowscript for all the pages.


Finally now we need a template file to display the widgets defined in the form file . The template file looks like this:


<?xml version="1.0"?>
<component xmlns:fi="http://apache.org/cocoon/forms/1.0#instance"
xmlns:ft="http://apache.org/cocoon/forms/1.0#template"
xmlns:jx="http://apache.org/cocoon/templates/jx/1.0"
xmlns:i18n="http://apache.org/cocoon/i18n/2.1">

<body>
<jx:import uri="resource://org/apache/cocoon/forms/generation/jx-macros.xml"/>
<table>
<tr>
<td>
<ft:widget-label id="user"/>
</td>
<td>
<ft:widget id="user" type="suggest"/>
</td>
</tr>
</table>
</body>
</component>

With all this done now we have to add the java class com.cforms.UserNameSelectionList defined in the forms file.



import java.util.List;
import java.util.Locale;

import javax.naming.Context;
import javax.naming.InitialContext;

import org.apache.cocoon.forms.FormsConstants;
import org.apache.cocoon.forms.datatype.Datatype;
import org.apache.cocoon.forms.datatype.FilterableSelectionList;
import org.apache.cocoon.forms.datatype.JavaSelectionList;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

public class UserNameSelectionList implements JavaSelectionList, FilterableSelectionList {

public void generateSaxFragment(ContentHandler out, Locale arg1, String filter) throws SAXException {
try {
Context context;
context = new InitialContext();
UserRemote userremote = (UserRemote) context.lookup("triplebase-ejb3/UserBean/remote");
List list = userremote.getUserNames(filter + "%");
Attributes attr = new AttributesImpl();
out.startElement(FormsConstants.INSTANCE_NS, SELECTION_LIST_EL, SELECTION_LIST_EL, attr);
for (int i = 0; i < list.size(); i++) {
out.startElement(FormsConstants.INSTANCE_NS, ITEM_EL, ITEM_EL, attr);
out.startElement(FormsConstants.INSTANCE_NS, LABEL_EL, LABEL_EL, attr);
Object[] row = (Object[]) list.get(i);
out.characters((row[0].toString() + " (" + row[1] + ")").toCharArray(), 0, (row[0].toString() + " ("
+ row[1] + ")").length());
out.endElement(FormsConstants.INSTANCE_NS, LABEL_EL, LABEL_EL);
out.endElement(FormsConstants.INSTANCE_NS, ITEM_EL, ITEM_EL);
}
out.endElement(FormsConstants.INSTANCE_NS, SELECTION_LIST_EL, SELECTION_LIST_EL);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

In this class the filter is the string which the user types into the field. As the user starts typing the filter comes in and we reuest the Db to give us the usernames based on those filters. There is one another option to use



public class JavaSelectionList extends AbstractJavaSelectionList {

@Override
protected boolean build() throws Exception {
Context context;
context = new InitialContext();
UserRemote userremote = (UserRemote) context.lookup("triplebase-ejb3/UserBean/remote");
List list = userremote.getUserNames("%");
for (String username : list) {
addItem(username,username);
}
return false;
}

}


With the above class it loads the list with all the usernames and filters them based on the text user types in. this option can be used when there are not much entries for the usernames.

And lastly dont forget to add the pipeline for the suggestion as explaind in the cocoon doc.