diaries, triumphs, failures, and rants
- All
- Animals
- Books
- Chess
- Cigars and the Man-Grill
- Collaboration
- Doctor Who
- General
- Internet
- iPhone
- Java
- LDAP
- mac
- oneLiners
- onThisDay
- slamd
- Star Trek
- Sun
- Travel
SLAMD: job class lists using Struts tags & OGNL
SLAMD version 2 constructs HTML in a huge Java servlet (com.slamd.admin.AdminServlet). One of my first tasks for SLAMD3 is to de-couple the HTML generation from the Java code so that SLAMD will be more flexible in HTML generation. The method I use is based on MVC using Struts2. Hopefully, I'll be able to detail my progress as an ongoing series of blog entries - in my spare time.
The first page I converted was the "Schedule a New Job" page. The way this page works in SLAMD2 is as follows: the com.slamd.admin.AdminServlet class examines parameters passed in the servlet request. If the "job_class" parameter (the String is a static final in com.slamd.common.Constants) is not present in the URL, the servlet constructs a categorized list of jobs and displays them as links in an un-ordered list. The underlying code creates a two dimensional array of com.slamd.job.JobClass objects and constructs HTML from the array:
public class AdminServlet
{
...
void handleScheduleJob(...)
{
JobClass[][] categorizedJobClasses =
slamdServer.getCategorizedJobClasses();
// construct HTML
}
}
To convert this to an MVC model in Struts2, I constructed a JSP called scheduleJob.jsp and mapped it to an Action in the struts.xml file like so:
<action name="ScheduleJob"
class="com.slamd.struts2.actions.ScheduleJobAction">
<interceptor-ref name="slamdStack"/>
<result name="success">/jsps/scheduleJob.jsp</result>
<result name="error">/jsps/error.jsp</result>
</action>
The action definition for "ScheduleJob" causes the dispatcher to route a URL like
http://hostname:port/slamd/ScheduleJob.actionthrough the interceptor stack "slamdStack", and then to the com.slamd.struts2.action.ScheduleJobAction object. the method "execute()" in actions must return a String indicating the result of the action execution. If the String returned by "execute()" is "success", then the view "scheduleJob.jsp" is the view, if "execute()" returns the String "error", then "error.jsp" is the view. ScheduleJobAction sets a field "jobClassName" to "noJob" (com.slamd.common.Constants.NO_JOB_PARAM) if no job parameter was present in the URL, or the job class name otherwise. This is the first use case: create the job class list when the "job_class" parameter is not part of the URL that is sent to the com.slamd.struts2.actions.ScheduleJobAction action.
A simple way to make construction of the job class list a little easier was to map the two dimensional array of JobClass objects to a Collection (java.util.Map) like so:
public class JobClassArrayHelper<T extends JobClass>
{
public Map<String,List<T>> convert2dArrayToMap(T[][] a)
{
Map<String,List<T>> map = null;
if(a != null)
{
map = new HashMap<String,List<T>>();
for(int i = 0; i < a.length; ++i)
{
List<T> list = Arrays.asList(a[i]);
map.put(a[i][0].getJobCategoryName(),list);
}
}
return map;
}
}
/**
* Returns a {@link Map} of job classes that have been
* loaded into the database
*
* @return a map of job classes that have been loaded into the database
*/
public Map<String,List<JobClass>> getJobClassList()
{
JobClass[][] categorizedClasses = common.getSlamdServer().getCategorizedJobClasses();
JobClassArrayHelper<JobClass> helper = new JobClassArrayHelper<JobClass>();
return helper.convert2dArrayToMap(categorizedClasses);
}
The JobClassArrayHelper converts the two-dimensional array of JobClass objects into a Map where the key is the job class category and the value is a java.util.List of JobClass objects. The Map is very easy to access in the JSP, let's take a look at that next.
The scheduleJob.jsp code
<%-- -*- html -*- --%>
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%-- each action should generate its own title --%>
<%-- include the JSP for the sidebar navigation --%>
<%-- include the JSP for the page header --%>
<%-- if no job name was provided to the Action display the list
of jobs that have been loaded into the database --%>
Schedule a New Job
Choose the type of job to schedule from the following list:
<%-- set a variable to the job class param --%>
<%-- iterate over the keys in the map "jobClassList" --%>
<%-- the key in the map is the name of the job category --%>
Job Classes
<%-- The value in the map is the list of job classes.
Each element is of type com.slamd.jobs.JobClass. --%>
<%-- iterate of the list of job classes --%>
<%-- set the class name of the job class --%>
<%-- Construct the link that will cause a new job --
-- class to be scheduled. The link consists of the --
-- ScheduleJob.action, a title that might be
-- rendered as a tooltip, or spoken, and a job
-- class parameter with the parameter value set to --
-- the job class name. --%>
-
This is the entire scheduleJob.jsp file that handles the use case of listing all SLAMD jobs in the database. I'll pick it apart piece by piece.
I like emacs
The following snippet causes Emacs to turn on HTML mode (a major mode):
<%-- -*- html -*- --%>
Page content and taglibs
Set the DOCTYPE, content type, and make 's' the prefix for the Struts tags:
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
Basic page setup
In SLAMD3, each page will have its own id and set its own title. navigation.jsp and header.jsp are the same for each HTML page, so those are included in each page. Divs are used freely to set off sections of the pages like the navigation sidebar and the header from the "content":
<%-- each action should generate its own title --%>
<%-- include the JSP for the sidebar navigation --%>
<%-- include the JSP for the page header --%>
Categorized job class listing
Here's the interesting part. First, test whether the jobClassName is "noJob" (recall that this value comes from a static final in com.slamd.common.Constants) and if so, print a short message about creating a new job, and create a new div called "categorizedJobClassList". The "job_class" URL parameter string is set to a variable using the "set" Struts tag so it can be referenced later - in the hope that the code becomes easier to read. Next create an iterator with the "iterator" tag where the object to iterate over is "jobClassList". Note that Struts uses the bean convention and actually calls ScheduleJobAction.getJobClassList. Recall that the signature of getJobClassList is:
public Map> getJobClassList()
That is, a Map where the key is a String and the value is a List of JobClass objects.
<%-- if no job name was provided to the Action display the list
of jobs that have been loaded into the database --%>
Schedule a New Job
Choose the type of job to schedule from the following list:
<%-- set a variable to the job class param --%>
<%-- iterate over the keys in the map "jobClassList" --%>
iterate over the map
It is not strictly necessary to use the "set" tag as much as is used here, but I think it makes the code easier to read and understand. In this case, the key from the map is assigned to a variable "jobClassCategory", and the value to "listOfJobs".
<%-- the key in the map is the name of the job category --%>
Job Classes
<%-- The value in the map is the list of job classes.
Each element is of type com.slamd.jobs.JobClass. --%>
This part sets a variable "className" by calling the methods to retrieve the name of the class for each SLAMD job class, then constructs a link of the form "ScheduleJob.action?job_class=com.slamd.jobs.JobClass":
<%-- iterate of the list of job classes --%>
<%-- set the class name of the job class --%>
<%-- Construct the link that will cause a new job --
-- class to be scheduled. The link consists of the --
-- ScheduleJob.action, a title that might be
-- rendered as a tooltip, or spoken, and a job
-- class parameter with the parameter value set to --
-- the job class name. --%>
-
And that's it. The next use case - in a separate blog entry - is to present the job class form. This is all baby stuff to hard-core Struts developers, but I had to blog it so I would not forget how to do it, and hopefully others will get some value from this entry also.
Posted at
01:38PM Mar 12, 2009
by tgardner in slamd |
Comments[4]
Thursday Mar 12, 2009
Good to see ongoing SLAMD development. We've been using the 2.0.0alpha at the University of Newcastle (Australia) for various things from webapps to HTTP proxies and even a CIFS server. We look forward to a revamped SLAMD3! :)
Posted by Joshua M. Clulow on March 12, 2009 at 08:36 PM EDT #
note that a new version of SLAMD is available from http://slamd.com and http://slamd2.dev.java.net. This version, call it slamd2-2009, has quite a few worthy changes from the 2.0.0 alpha version. -- terry
Posted by Terry Gardner on March 12, 2009 at 08:42 PM EDT #
Yeah, I noticed that after reading this blog page. The only real bug we've experienced is that occasionally clients hang at the end of jobs so they have to be killed off and then their testing results obviously don't get aggregated into the whole job results. Hopefully that's fixed in 2.0.0 release!
Posted by Joshua M. Clulow on March 12, 2009 at 08:56 PM EDT #
I have noted that long running threads may from time to time not show up in results at the very end. I'll take a look at it.
Posted by Terry Gardner on March 30, 2009 at 12:49 PM EDT #