Friday Jun 19, 2009

SLAMD internals - functional

Introduction

SLAMD3 uses a model-view-controller paradigm to separate the view from the internal database action and action processing. The diagram above depicts the flow of browser requests and the SLAMD response, with emphasis on the Struts2 interceptor framework. The operator issues a request via the SLAMD administrative interface which passes through the interceptor stack (from left to right) until it reaches the Action. The Action is selected by the Struts framework based on the URL from the browser request. For example, the URL http://host:port/slamd/ScheduleJob.action results in Struts2 selecting the Action named "ScheduleJob". Actions are comprised of Java code which executes to satisfy the browser request. When the Action completes, it returns a result (in Struts2 the result is a string such as "success" or "failure") which passes back through the interceptor stack, this time from right to left. The result code string determines which JSP is used to generate the HTML that is displayed in the browser.

Interceptors

Interceptors are used to examine the request in a specific order of interception. Interceptors can be grouped together into a "stack", e.g., the "slamdStack". An arbitrary number of interceptors and interceptor stack groupings can be defined in the configuration file struts.xml, which like other configuration files, must be in the CLASSPATH. SLAMD stores the struts.xml file, the slamd.conf file, and the logging configuration file at the root of the CLASSPATH slamd/WEB-INF/classes.

The interceptors in request order are:

  1. the timer interceptor (source: xwork) times the execution of the request flow from the invocation of the timer interceptor until the timer result is returned.
  2. the HTTP create session interceptor (source: Struts2) creates an HTTP session if one does not already exist, and instantiates a session map of type Map<String,Object>. The default interceptor stack will make this session map available to Actions that are SessionAware.
  3. the cookie interceptor (source: Struts2) inserts the value of cookies based on parameters in the struts.xml file.
  4. the default stack (source: Struts2) is a group of interceptors defined by Struts that perform various useful functions associated with the request - more about this stack in another blog post
  5. the history interceptor (source: SLAMD) creates a breadcrumb trail string that is displayed in the browser. The breadcrumbs are implemented as a Map<Session,String> object, allowing each session to have its own breadcrumb trail. Breadcrumbs are useful in tracking the operations performed by the SLAMD administrative interface. The depth of the breadcrumb stack is limited by an interceptor parameter.
  6. the configuration interceptor (source: SLAMD) reads the configuration file slamd.conf and injects a configuration object into the singleton Config object. SLAMD uses Apache commons configuration, which allows developers to use a number of different configuration source types, for example, properties file, XML file, URL, database, LDAP, and others. Apache commons configuration supports auto-save, so CRUD operations are easily managed, and also supports configuration event creation and notification. The configuration interceptor must execute before any of the SLAMD specific Actions that require configuration parameters defined in the slamd.conf file. Understanding the operation of the configuration interceptor is critical if one wishes to understand the internals of SLAMD.
  7. the SLAMD cookies interceptor (source: SLAMD) creates certain cookies used by the administrative interface in the event those cookies do not exist. The SLAMD cookies are used to track operator preferences, current folder name, and other user-specific data.
  8. the authentication interceptor (source: SLAMD) authenticates users if access control is enabled. The authenticator mechanism is controlled by an Authenticator class, defined in the struts.xml file. This version of SLAMD only supports XML-file based authentication with an ELF hash for passwords, very similar to the Tomcat authentication mechanism.
  9. the database interceptor (source: SLAMD) ensures that the database is created and ready for use. An important enhancement to this version of SLAMD is the ability to use a database at a user-defined location - but that database must still be a SLAMD database!
  10. the SLAMD server interceptor (source: SLAMD) ensures that the SLAMD server is running.
  11. the SLAMD scheduler (source: SLAMD) ensures that the SLAMD scheduler is running if the SLAMD server is running.
  12. the security interceptor (source: SLAMD) is deprecated and will be removed.
  13. the request info interceptor (source: SLAMD) is deprecated and will be removed
  14. the job interceptor (source: SLAMD) ensures that job information such as the job class name and job class object are available to Actions.
  15. the job parameter interceptor (source: SLAMD) creates the dynamic job parameters used by SLAMD and makes those parameters available to Actions.

Actions

An Action executes Java code to fulfill the request from the SLAMD administrative interface. Action objects, for example, com.slamd.struts2.actions.JobActions, are created for each request before the interceptor stack executes, therefore, Action initialization cannot rely on any data that would be manipulated by the interceptor stack. Since Actions are created before the interceptor stack executes, interceptors can query actions for data and objects that might be needed for execution of the interceptor. The order of execution of interceptors is vital in determining what data is available to an Action.

Actions not only contain methods that are invoked to perform the task associated with the request, they also interact with JSPs via the ValueStack. Any getter or setter in an Action is accessed via the ValueStack and the results of a get() or set() is available to the JSP that generates HTML. This maintains separation between the Model (Action) and the View (HTML generated by JSPs) and allows changes to either to be made in isolation.

JavaServer Pages

JavaServer Pages are used to generate HTML for display in the browser. JSPs make heavy use of the ValueStack (for access to data in the Action), beans (certain features that do not require SLAMD or the request to be in a certain state), and Struts tags. JSPs are a powerful programming resource that allow the use of dynamic HTML generation by supporting all basic programming constructs (loops, if-then-else) and page generation encapsulation (XML, HTML, character set encoding). Struts tags allow for the automatic conversion of data types such as Maps, Collections, and basic primitive data types to relieve the developer of the burden of the tedious tasks of doing the conversion by hand with special code. The price to pay for this functionality is a somewhat arcane syntax, and that is indeed a small price to pay to ensure isolation and separation of data and views.

Dynamic page generation is used to great effect in SLAMD. Every aspect of page generation is dynamic, for example, page titles, internationalization, CSS inclusion, meta keyword generation, and debugging. Actions can and do influence the title, keywords, CSS media type, and all other aspects of HTML generation.

Javascript is not used in this version of SLAMD, and it is unlikely to be used in the future. This allows the use of any browser, including text based browsers and mobile browsers - all the functionality of the SLAMD administrative interface works on any display device. In the event that Javascript is completely standardized across browser platforms, I'll consider adding it to SLAMD. I consider this unlikely due to complete disagreement between major browser vendors and their insistence that "our way is the right way" to the detriment of programmers and web surfers everywhere. See also browser wars.

Application Servers and Browsers

SLAMD has been tested with GlassFish and Tomcat. SLAMD looks best on Safari, which is not unusual, since everything looks best on Safari, but Firefox, Mozilla, Lynx, and Opera have also been tested.

Tuesday May 19, 2009

Directory Server Log Analyzer 0.0.3.1 released

Version 0.0.3.1 of Directory Server Log Analyzer has been released. This version has a re-worked presentation of the data from an uploaded file. There is also some additional language on the upload page to describe how to use cURL to upload files directly to the application, which is repeated here:


 curl -F 'uploadedFile=@access' \\
    --form-string hostname=example.com \\
    --form-string description="a description of the file" 
    --form-string instance="the name of the instance" \\
    http://www.example.com:8080/dsla/FileUploaded.action > /dev/null

The command line above uploads a file named "access", provides a hostname, description, and the name of the instance where the access file was generated. Hostname, description and instance name could be omitted, in which case suitable defaults are provided. Use the correct application server hostname and port, obviously. The field named "uploadFile" is required, and must be named "uploadFile". Download the application WAR file from the download area at http://kenai.com and deploy into GlassFish or Tomcat under contextroot "dsla".

Thursday Apr 23, 2009

Implementing a custom LDAP server validator in Struts for SLAMD

Implementing a custom LDAP server validator in the Struts2 framework.[Read More]

Saturday Apr 11, 2009

Verbose Descriptions on the "Schedule a New Job" form

I have added a new feature to the SLAMD "Schedule a New Job" form - job parameter descriptions and helpful text above the input field for that job parameter.[Read More]

Wednesday Apr 08, 2009

Progress Report on SLAMD3

SLAMD MVC conversion progress report.[Read More]

Thursday Mar 12, 2009

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.action
through 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 -\*- --%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
          "http://www.w3.org/TR/html4/loose.dtd">

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>

<html lang="en" id="scheduleJob">

  <head>
    <%-- each action should generate its own title --%>
    <title><s:property value="title"/></title>
    <link rel="stylesheet" href="css/slamd.css">
  </head>

  <body id="scheduleJobBody">

    <%-- include the JSP for the sidebar navigation --%>
    <s:include value="navigation.jsp" />

    <%-- include the JSP for the page header --%>
    <s:include value="header.jsp" />

    <div id="content">

      <%-- if no job name was provided to the Action display the list
	   of jobs that have been loaded into the database --%>
      <s:if test="%{jobClassName == @com.slamd.common.Constants@NO_JOB_PARAM}">

	<h2>Schedule a New Job</h2>
	Choose the type of job to schedule from the following list:

	<div class="categorizedJobClassList">

	  <%-- set a variable to the job class param --%>
	  <s:set name="jobClassParam"
		 value="@com.slamd.common.Constants@SERVLET_PARAM_JOB_CLASS"/>

	  <%-- iterate over the keys in the map "jobClassList" --%>
	  <s:iterator value="jobClassList" status="jobClassItStatus">

	    <%-- the key in the map is the name of the job category --%>
	    <s:set name="jobClassCategory" value="key"/>

	    <h3><s:property value="jobClassCategory"/> Job Classes</h3>

	    <%-- The value in the map is the list of job classes.
		 Each element is of type com.slamd.jobs.JobClass. --%>
	    <s:set name="listOfJobs" value="value"/>

	    <div class="noBullets">
	      <ul>

		<%-- iterate of the list of job classes --%>
		<s:iterator value="listOfJobs" status="listItStatus">

		  <%-- set the class name of the job class --%>
		  <s:set name="className"
			 value="getClass().getName()"/>

		  <%-- 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. --%>
		   <li class="appended">
		     <a title='<s:property value="shortDescription"/>'
		       href='ScheduleJob.action?<s:property value="jobClassParam"/>=<s:property value="className"/>'>
		      <s:property value="jobName"/>
		    </a>
		  </li>
		</s:iterator>
	      </ul>
	    </div>
	  </s:iterator>
	</div>
      </s:if>

    </div>

  </body>

</html>

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:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
          "http://www.w3.org/TR/html4/loose.dtd">

<%@ 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":

<html lang="en" id="scheduleJob">

  <head>
    <%-- each action should generate its own title --%>
    <title><s:property value="title"/></title>
    <link rel="stylesheet" href="css/slamd.css">
  </head>

  <body id="scheduleJobBody">

    <%-- include the JSP for the sidebar navigation --%>
    <s:include value="navigation.jsp" />

    <%-- include the JSP for the page header --%>
    <s:include value="header.jsp" />

    <div id="content">

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<String,List<JobClass>> 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 --%>
      <s:if test="%{jobClassName == @com.slamd.common.Constants@NO_JOB_PARAM}">

	<h2>Schedule a New Job</h2>
	Choose the type of job to schedule from the following list:

	<div class="categorizedJobClassList">

	  <%-- set a variable to the job class param --%>
	  <s:set name="jobClassParam"
		 value="@com.slamd.common.Constants@SERVLET_PARAM_JOB_CLASS"/>

	  <%-- iterate over the keys in the map "jobClassList" --%>
	  <s:iterator value="jobClassList" status="jobClassItStatus">

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 --%>
	    <s:set name="jobClassCategory" value="key"/>

	    <h3><s:property value="jobClassCategory"/> Job Classes</h3>

	    <%-- The value in the map is the list of job classes.
		 Each element is of type com.slamd.jobs.JobClass. --%>
	    <s:set name="listOfJobs" value="value"/>
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":

	    <div class="noBullets">
	      <ul>

		<%-- iterate of the list of job classes --%>
		<s:iterator value="listOfJobs" status="listItStatus">

		  <%-- set the class name of the job class --%>
		  <s:set name="className"
			 value="getClass().getName()"/>

		  <%-- 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. --%>
		   <li class="appended">
		     <a title='<s:property value="shortDescription"/>'
		       href='ScheduleJob.action?<s:property value="jobClassParam"/>=<s:property value="className"/>'>
		      <s:property value="jobName"/>
		    </a>
		  </li>
		</s:iterator>
	      </ul>
	    </div>
	  </s:iterator>
	</div>
      </s:if>
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.
About

Sun, LDAP, SLAMD, DSLA, java, Struts, networking, chess, books, cooking, wine, and many other things.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today