You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hivemind.apache.org by hl...@apache.org on 2004/05/26 00:31:42 UTC

cvs commit: jakarta-hivemind/src/documentation skinconf.xml

hlship      2004/05/25 15:31:41

  Modified:    common   jar-module.xml project.xml javacc.xml
                        dependency.xml common.properties common.xml
               framework/src/test/hivemind/test/rules
                        TestEnumerationTranslator.java
                        TestConfigurationTranslator.java
                        TestObjectTranslator.java
               framework build.xml
               framework/src/test/hivemind/test TestMisc.java
               framework/src/test/hivemind/test/impl
                        TestRegistryAssemblyImpl.java
               library  build.xml
  Added:       src/documentation/content/xdocs index.xml sdl.xml site.xml
                        case1.xml links.ent tabs.xml bootstrap.xml
               .        forrest.properties
               common   forrest.xml
               src/documentation/conf cli.xconf
               src/documentation/resources/images InterceptorStack.png
                        AdderExample-ProjectLayout.png jakarta-logo.gif
                        HiveMind-Logo.png
               src/documentation skinconf.xml
  Removed:     common   links.xml
               framework/src/test/hivemind/test/rules MockModule.java
  Log:
  Begin reworking documentation as Forrest xdoc.
  
  Revision  Changes    Path
  1.1                  jakarta-hivemind/src/documentation/content/xdocs/index.xml
  
  Index: index.xml
  ===================================================================
  <?xml version="1.0"?>
  <!-- $Id: index.xml,v 1.1 2004/05/25 22:31:40 hlship Exp $ -->
  <!-- 
     Copyright 2004 The Apache Software Foundation
  
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
  
         http://www.apache.org/licenses/LICENSE-2.0
  
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
  -->
  <!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.2//EN" "./dtd/document-v12.dtd"
  [
  	<!ENTITY % common-links SYSTEM "links.ent">
  	%common-links;
  ]>
  <document>
  
    <header>
      <title>Introduction to HiveMind</title>
  	</header>
  	
    <body>
  <section><title>HiveMind</title>
  
  <warning>
  Still be translated from Maven XDoc.	
  </warning>	
  	
  </section>
  
      
    </body>
  </document>
  
  
  
  1.1                  jakarta-hivemind/src/documentation/content/xdocs/sdl.xml
  
  Index: sdl.xml
  ===================================================================
  <?xml version="1.0"?>
  <!-- $Id: sdl.xml,v 1.1 2004/05/25 22:31:40 hlship Exp $ -->
  <!-- 
     Copyright 2004 The Apache Software Foundation
  
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
  
         http://www.apache.org/licenses/LICENSE-2.0
  
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
  -->
  <!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.2//EN" "./dtd/document-v12.dtd"
  [
  	<!ENTITY % common-links SYSTEM "links.ent">
  	%common-links;
  ]>
  <document>
  	<header>
  		<title>Simple Data Language</title>
  	</header>
  	<body>
  			
  <p>
  One of the frequent criticisms of J2EE is: <em>too much XML</em>. That is, every small aspect
  of a J2EE application requires a big XML deployment descriptor to be generated (by hand, or generated
  from code in some way). What's interesting is what's <em>in</em> those XML files: configuration
  data containing simple strings and identifiers.
  </p>
  
  <p>
  XML is overkill for these purposes: its a <em>markup</em>	 language, designed to add 
  <em>semantic meaning</em> to documents that normally have a <em>literal</em> meaning (that is,
  documents that are supposed to be read primarily by persons, not other programs). Like many technologies,
  its intended use has been co-opted (to what degree is debatable). XML for real documents such as XHTML or SVG make sense.
  The complexity of SOAP mandates an industrial strength syntax to express its complex structure. But for the
  majority of uses of XML within the J2EE stack, it simply is vastly more complex than is necessary.
  </p>
  
  <p>
  The complexity comes at some cost ... XML is very verbose, a tangle of punctuation 
  (such as <code>&lt;</code>, <code>&gt;</code> and quotes) and repetition (start tags and end tags). Even 
  experienced developers often need to take a bit of time to visually and mentally parse an XML snippet.
  </p>
  
  <p>
  Through HiveMind release 1.0-alpha-4, HiveMind was as guilty as the next project in XML usage. HiveMind
  module deployment descriptors would, at least, centralize the XML concerning a service, and enforce
  some amount of uniformity.	
  </p>
  
  <p>
  Release 1.0-alpha-5 introduces <strong>Simple Data Language</strong>, an <em>alternative</em> to the use
  	of XML in HiveMind.  XML will continue to be supported as a first class citizen, but in HiveMind,
  	there is not such a compelling reason to use it!
  </p>
  
  
  <section><title>Goals</title>
  	
  <p>
  The goals of SDL are to provide the bare essentials needed for a hierachical data language, but
  keep is spare and readable. Unecessary typing is to be avoided, so the use of quotes is made
  optional whereever possible. SDL syntax should be reasonably obvious to an interested observer.	
  </p>
  	
  	
  </section>
  
  <section><title>Examples</title>
  
  <p>
  Before getting bogged down in a formal specification for SDL, a few simple examples will explain just
  about everything.	Compare the following two HiveMind module deployment descriptors, which express
  identical information:
  </p>
  
  <p>
  <strong>Traditional XML Format:</strong>
  </p>
  <source><![CDATA[
  <?xml version="1.0"?>
  
  <module id="some.module" version="1.0.0">
    <configuration id="ControlPipeline">
      <schema>
        <element name="processor">
         
           <attribute name="name" required="true"/>
           <attribute name="service-id" required="true" translator="service"/>
           <attribute name="before"/>
           <attribute name="after"/>
           
           <conversion class="some.module.PipelineContribution">
             <map property="controlService" attribute="service-id"/>
           </conversion>
        
        </element>
      </schema>
    </configuration>
  </module>	
  ]]>
  </source>
  
  <p>
  <strong>SDL format:</strong>	
  </p>
  
  <source>
  module (id=some.module version="1.0.0")
  {
    configuration (id=ControlPipeline)
    {
      schema
      {
        element (name=processor)
        {
          attribute (name=name required=true)
          attribute (name=service-id required=true)
          attribute (name=before)
          attribute (name=after)
          
          conversion (class=some.module.PipelineContribution)
          {
            map (property=controlService attribute=service-id translator=service)
          }
        }
      }
    }
  }   
  </source>	
  
  
  <p>
  Some observations:
  </p>
  
  <ul>
  <li>SDL uses open and close braces to denote containment of elements within another element</li>	
  <li>Attributes, as a list of name-value pairs, are placed in parenthesis following the element name</li>
  <li>Elements without attributes can omit the parenthesis (example: <code>schema</code>)</li>
  <li>Elements that do not contain other elements can omit the open and close braces denoting thier
  	body (example: <code>attribute</code>)</li>
  <li>Most common strings do not have to be quoted</li>
  <li><em>All</em> whitespace not inside quotes is ignored</li>
  </ul>	
  
  </section>
  
  	
  <section><title>Whitespace</title>
  
  <p>
  All whitespace (outside of literals) is ingored.  Whitespace is considered to be:
  </p>
  
  <ul>
  	<li>Spaces</li>	
  	<li>Tabs</li>
  	<li>Newlines</li>
  	<li>Carriage Returns</li>
  </ul>	
  
  </section>
  
  <section><title>Comments</title>
  
  <p>
  Comments are in the format traditional in Java and C:	
  </p>	
  
  <source>
  // This is a comment that extends to the end of the current line.
  
  /* This is a multiline
     comment. */	
  </source>
  
  <p>
  Comments may appear anywhere in an SDL document (except within quoted strings) and are always ignored.	
  </p>
  
  </section>
  
  <section><title>Element and Attribute Names</title>
  
  <p>
  Element and attribute names must be <em>simple ids</em>. They must start with a letter (or underscore)
  and may contain only letters, digits, underscores and dashes. They may <em>not</em> be enclosed in quotes.
  </p>	
  
  </section>
  
  <section><title>Literal Values</title>
  	
  <p>
  Attribute values may be literal values.  Literal values are considered one of the following:
  </p>
  	
  <ul>
  <li>simple ids</li>	
  <li>complex ids -- a sequence of simple ids seperated by periods</li>
  <li>numeric values</li>
  <li>Symbol references</li>
  <li>Quoted strings</li>
  <li>Extended literals</li>
  </ul>	
  
  <p>
  Complex ids have the same format as Java class and interface names (but can, additionally, contain
  dash characters which are not allowed in Java).
  </p>
  
  <p>
  Numeric values consist of an optional sign (<code>+</code>	 or <code>-</code>) followed
  by a integer or decimal value.  In the future, a more expansive definition may be provided.
  </p>
  
  <p>
  Symbol references allow Ant-style symbols to be used directly in SDL.  Example:
  </p>
  
  <source>
  . . .
    set-service (service-id=${symbol.for.service-id})
  . . .	
  </source>	
  
  
  <p>
  Support for Ant symbols is a convienience (the same syntax is used heavily within HiveMind).
  There is no difference between <code>${symbol.for.service-id}</code> and
  <code>"${symbol.for.service-id}"</code> ... both will be processed identically.
  </p>
  
  <p>
  Quoted strings are similar to Java string literals. All whitespace within the string is retained as-is,
  including line breaks.  A subset of the Java escape codes are currently supported:
  </p>
  	
  <ul>
    <li>\t (tab)</li>	
    <li>\n (newline)</li>
    <li>\r (carriage return)</li>
    <li>\"  (quote)</li>
    <li>\\  (slash)</li>
  </ul>	
  
  <p>
  Any other sequence is passed through normally (unescaped).
  </p>
  	
  <p>
  Extended literals have a different syntax:	
  </p>
  
  <source><![CDATA[
  . . .
    description =
  << A long, multiline string
  that may contain "quoted" sections. >>	
  . . .
  ]]></source>
  
  <p>
  Extended literals may contain any character sequence (except <code>&gt;&gt;</code>). Escape sequences
  in expanded literals are not interpreted. All whitespace within the delimiters is retained.
  </p>
  </section>
  	
  
  
  <section><title>Literal Gotcha</title>
  
  <p>
  The body of an element may contain literal text data, just as with XML.  Unlike XML, whitespace
  is completely removed.  Thus the following are equivalent:
  </p>
  
  <source><![CDATA[
  first
  { 
    "NowIsTheTime" 
  }
  second
  {
    "Now" "Is" <<The>> <<Time>>
  }
  ]]></source>	
  	
  
  <p>
  This applies to all forms of literals, including numbers. The following are identical:
  </p>
  <source>
  pi1
  { 
    3.14159
  }
  pi2
  {
    3 .14 159
  }
  </source>	
  
  
  <p>
  Inside the body of an element, simple ids are interpreted as <em>elements</em>	 not
  string literals.  In the following example, <code>root1</code> and <code>root2</code> have the
  same structure (each contains three children and no content).  <code>leaf</code> contains no
  children, and its content is <code>child1child2child3</code>.
  </p>
  	
  <source><![CDATA[
  root1 
  {
    child1 {}
    child2 {}
    child3 {}
  }
  root2
  {
    child1 child2 child3
  }
  leaf
  {
    "child1" "child2" "child3"
  }	
  ]]></source>
  	
  </section>
  
  
  <section><title>TO DO</title>
  
  <ul>
  <li>Expand the definition of "character" to properly include Unicode</li>	
  <li>Add Unicode escape patterns in quoted literals</li>
  <li>Expand the definition of numeric literal to include all Java literals</li>
  </ul>
  	
  </section>
  
  
  
  	</body>
  </document>
  
  
  
  1.1                  jakarta-hivemind/src/documentation/content/xdocs/site.xml
  
  Index: site.xml
  ===================================================================
  <?xml version="1.0"?>
  <!-- $Id: site.xml,v 1.1 2004/05/25 22:31:40 hlship Exp $ -->
  <!DOCTYPE site>
  <site xmlns="http://apache.org/forrest/linkmap/1.0" tab="project">
  	<project label="HiveMind Project">
  		<bootstrap label="Bootstrapping the Registry" href="bootstrap.html"/>
  		<case1 label="Case Study #1: Panorama Startup" href="case1.html"/>
  		<sdl label="Simple Data Language" href="sdl.html"/>
  	</project>
  	<hivemind label="Module: hivemind" href="hivemind/">
  		<services label="Services">
  			<hivemind.LoggingInterceptor label="hivemind.LoggingInterceptor"
  				href="LoggingInterceptor.html"/>
  		</services>
  		<configs label="Configurations">
  			<hivemind.ApplicationDefaults label="hivemind.ApplicationDefaults"
  				href="ApplicationDefaults.html"/>
  			<hivemind.FactoryDefaults label="hivemind.FactoryDefaults"
  					href="FactoryDefaults.html"/>
  		</configs>
  	</hivemind>
  	<related label="Related Projects">
  		<item label="Tapestry" href="http://jakarta.apache.org/tapestry/"/>
  	</related>
  </site>
  
  
  
  1.1                  jakarta-hivemind/src/documentation/content/xdocs/case1.xml
  
  Index: case1.xml
  ===================================================================
  <?xml version="1.0"?>
  <!-- $Id: case1.xml,v 1.1 2004/05/25 22:31:40 hlship Exp $ -->
  <!-- 
     Copyright 2004 The Apache Software Foundation
  
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
  
         http://www.apache.org/licenses/LICENSE-2.0
  
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
  -->
  <!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.2//EN" "./dtd/document-v12.dtd"
  [
    <!ENTITY % common-links SYSTEM "links.ent">
    %common-links;
  ]>
  <document>
    <header>
      <title>Case Study #1: Application Startup / Shutdown</title>
    </header>
    <body>
  
  
  <note>
  This case study is based on work done for my prior employer, who has not (yet)
  given approval to mention the project by name. The package names and module
  ids have been changed, and some minor changes and simplifications have been made.
  The actual name of the product has been disguised as <em>Panorama</em>.
  </note>
  
    
  <p>
  The Panorama product is a fairly large J2EE web application deployed into BEA WebLogic.  Panorama consists
  of well over six thousand classes, divided into a large number of tools and services.
  Panorama has been a production project for several years, long before HiveMind was available. 
  HiveMind's introduction into Panorama (on something of a trial basis) was to cleanup the startup
  and shutdown process for the application.
  </p>  
  
  <p>
  Panorama runs inside BEA WebLogic as an enterprise application; however, it is still logically a 
  number of subsystems, many of which require some form of startup or shutdown logic.  For example,
  the Panorama Help service caches help data stored in the database; the Panorama Mail tool sets up
  periodic database cleanup jobs.  All told, there are over 40 startup tasks, and a handful of shutdown
  tasks.
  </p>
  
  <p>
  Prior to HiveMind, a single EJB was the focus of all this startup and shutdown activity.
  A small WebLogic startup class would invoke the EJB, and the EJB implementation would invoke static methods
  on many other classes (some of which would lookup other EJBs and invoke methods on them).  
  This approach had grown quite unwieldy, especially in light of efforts
  to improve and modularize the Panorama build process.  HiveMind was brought in to rationalize
  this aspect of Panorama, with the goal being to make the fewest possible changes
  to existing code.
  </p>
  
  <p>
  An important aspect of startup and shutdown is the order of operations; there are dependencies between
  different tasks that must be honored in terms of which task is executed first.  
  </p>
  
  
  <section><title>Overview</title>
  
  <p>
  The appropriate place to build the registry for an EAR is from the web application; it has
  the widest view of available classes; the web application classloader has visibility to the web application
  and its libraries, all the EJBs deployed in the application, and the system classloader.  
  </p>
  
  <p>
  The overall approach is to provide HiveMind module deployment descriptors for the various
  tools and services of Panorama; each module  contributes tasks to a Startup or Shutdown configuration point.
  </p>
  
  <p>
  A WebLogic shutdown class is still used and the original EJB still exists to allow an orderly shutdown.  
  Ultimately, this is required due to class loader issues; the EJB will have visibility to the HiveMind library,
  but the startup class may not.
  </p>
  
  </section>
  
  <section><title>Module panorama.framework.startup</title>
  
  <p>
  The <code>panorama.framework.startup</code> ("initialization and shutdown") module
  contains the services and configuration points for startup and shutdown.  It also contains
  Java classes corresponding to task contributions.
  </p>  
    
  <source>
  
  module (id=panorama.framework.startup version="1.0.0")
  {
  	description { "Module for startup and shutdown code within Panorama." }
  	
  	schema (id=Task)
  	{
  		element (name=task)
  		{
  			description { "A task which may be executed." }
  			
  			attribute (name=order required=true)
  			{
  				description { "Numeric value used to set the order of execution for tasks." }
  			}
  			
  			attribute (name=title required=true)
  			{
  				description { "Title displayed as task is executed." }
  			}
  			
  			attribute (name=class translator=object)
  			{
  				description { "Name of class implementing the Executable interface." }
  			}
  			
  			attribute (name=service-id translator=service)
  			{
  				description { "Name of service implementing the Executable interface." }
  			}
  			
  			conversion (class=com.panorama.framework.startup.service.Task)
  			{
  				map (attribute=class property=executable)
  				map (attribute=service-id property=executable)
  				
  				// Other attribute map directly
  			}
  			
  			// Nested element
  			
  			element (name=invoke-static)
  			{
  				description { "Used to invoke a public static method of a class." }
  			
  				attribute (name=class required=true)
  				{
  					description { "The name of the class containing the method to invoke." }
  				}
  				
  				attribute (name=method)
  				{
  					description 
  					{
  						"The name of the metehod to invoke. The default method name is "
  						"init."
  					 }
  				}
  				
  				conversion (class=com.panorama.framework.startup.service.StaticTask 
  										parent-method=setExecutable)
  				{
  					map (attribute=class property=className)
  					map (attribute=method property=methodName)
  				}
  			}
  		}	
  	}
  	
  	configuration-point (id=startup schema-id=Task)
  	{
  		description { "Defines startup tasks." }
  	}
  	
  	configuration-point (id=Shutdown schema-id=Task)
  	{
  		description { "Defines shutdown tasks." }
  	}
  	
  	contribution (configuration-id=Startup)
  	{
  		task (title=Python order=50 class=com.panorama.framework.startup.common.PythonStartup)
  	}
  	
  	contribution (configuration-id=Shutdown)
  	{
  		task (title="Update Status" order=100)
  		{
  			invoke-static (class=com.panorama.framework.startup.common.PanoramaStatus
  											method="shutdown)
  		}
  	}
  	
  	service-point (id=Startup interface=java.lang.Runnable)
  	{
  		invoke-factory (service-id=hivemind.BuilderFactory)
  		{
  			construct (class=com.panorama.framework.startup.service.TaskExecutor
  									log-property=log messages-property=messages)
  			{
  				set-configuration (property=tasks configuraton-id=Startup)
  				set (property=kind value="%startup")
  			}
  		}
  		
  		interceptor (service-id=hivemind.LoggingInterceptor)
  	}
  	  
  	service-point (id=Shutdown interface=java.lang.Runnable)
  	{
  		invoke-factory (service-id=hivemind.BuilderFactory)
  		{
  			construct (class=com.panorama.framework.startup.service.TaskExecutor
  									log-property=log messages-property=messages)
  			{
  				set-configuration (property=tasks configuraton-id=Shutdown)
  				set (property=kind value="%shutdown")
  			}
  		}
  		
  		interceptor (service-id=hivemind.LoggingInterceptor)
  	}
  		  
  }
  </source>
  
  <p>
  Notes:
  </p>
  
  <ul>
  <li>
  Extension points, configurations, schemas and services can be specified in any order.  
  </li>
  <li>
  We use the simplest possible interface for the Startup and Shutdown services: <code>java.lang.Runnable</code>.  
  </li>
  </ul>  
  
  <section><title>Startup configuration point</title>
  
  <p>
  The Startup configuration point and the Startup service are closely bound together; the former
  contains contributions from all sorts of modules.  The service uses those contributions
  and executes tasks based on them.  
  </p>  
  
  <p>
  The schema for the Startup configuration point allows a <code>&lt;task&gt;</code>
  to be contributed.  A task always has an <code>order</code> attribute (used to sort
  all the contributed elements into an execution order) and a <code>title</code> attribute
  (used in output).
  </p>
  
  <p>
  The task to execute is specified in one of three ways:
  </p>
  	
  <ul>
  <li>As a Java class implementing the
     <code>com.panorama.framework.startup.service.Executable</code> interface (using the <code>class</code> attribute)</li>
  <li>As a HiveMind service, implementing the service (using the <code>service-id</code> attribute)</li>
  <li>As a public static method of a class (using the enclosed <code>&lt;invoke-static&gt;</code> element)</li>
  </ul>
    
  <p>
  The <code>Executable</code> interface is similar to the <code>java.lang.Runnable</code> interface:
  </p>
  	
  <source>
  package com.panorama.framework.startup.service;
  
  /**
   * Variation of <code>java.lang.Runnable</code> that allows for
   * the invoked method to throw an exception. 
   */
  public interface Executable
  {
      /**
       * Invoked to execute some kind of behavior and possible throw an exception.
       * The caller is responsible for catching and reporting the exception.
       */
      public void execute() throws Exception;
  }
  </source>
  
  <p>
  Adding <code>throws Exception</code> to the method signature allows the caller to be responsible
  for exception reporting, which simplifies the task implementations.
  Shortly, we'll see how the application's master servlet invokes the Startup service.  
  </p>
  
  <p>
  The Shutdown configuration point and service are effectively clones of the Startup configuration point and schema.
  </p>
  
  </section>
  
  <section><title>Task class</title>
  
  <p>
  The Task class is used to hold the information collected by the Startup configuration point.
  </p>  
  
  <source>
  package com.panorama.framework.startup.service;
  
  import org.apache.hivemind.Orderable;
  
  /**
   * Configuration element for the <code>panorama.framework.startup.Startup</code> or
   * <code>panorama.framework.startup.Shutdown</code>
   * configuration points.  Each element has a title,
   * an {@link com.panorama.framework.startup.service.Executable}
   * object, and an order
   * (used to sort the Tasks into an order of execution).
   */
  public class Task implements Orderable, Executable
  {
      private int _order;
      private String _title;
      private Executable _executable;
      
      public void execute() throws Exception
      {
          _executable.execute();
      }
      
      public int getOrder()
      {
          return _order;
      }
  
      public String getTitle()
      {
          return _title;
      }
  
      public void setOrder(int i)
      {
          _order = i;
      }
  
      public void setTitle(String string)
      {
          _title = string;
      }
  
      public Executable getExecutable()
      {
          return _executable;
      }
  
      public void setExecutable(Executable executable)
      {
          _executable = executable;
      }
  }  
  </source>
  
  <p>
  Task implements <code>Executable</code>, simply delegating to its <code>executable</code> property. In addition,
  it implements
  <link href="&apiroot;/Orderable.html">Orderable</link>, which simply defines the <code>order</code>
  property (but simplifies sorting of the elements).
  </p>
  </section>
  
  
  
  <section><title>Startup service</title>
  
  <p>
  The Startup and Shutdown services are very similar: similar enough that a single class, properly configured, can be
  the service implementation for either service.  
  </p>
    
  <source><![CDATA[
  package com.panorama.framework.startup.service;
  
  import java.util.List;
  
  import org.apache.hivemind.HiveMind;
  import org.apache.hivemind.Messages;
  import org.apache.commons.logging.Log;
  
  /**
   * Implementation for the <code>panorama.framework.startup.Startup</code> 
   * and <code>Shutdown</code> services.
   * Reads the corresponding configuration, sorts the elements,
   * and executes each. 
   */
  public class TaskExecutor implements Runnable
  {
      private Log _log;
      private Messages _messages;
      private List _tasks;
  
      private String _kind;
  
      public void run()
      {
          long startTime = System.currentTimeMillis();
  
          List sorted = null;
  
          try
          {
              sorted = HiveMind.sortOrderables(_tasks);
          }
          catch (Exception ex)
          {
              _log.error(_messages.format("initialization-failure", _kind, ex.getMessage()));
  
              return;
          }
  
          int count = sorted.size();
          int failureCount = 0;
  
          for (int i = 0; i < count; i++)
          {
              Task task = (Task)sorted.get(i);
  
              if (execute(task))
                  failureCount++;
          }
  
          Long elapsedTime = new Long(System.currentTimeMillis() - startTime);
  
          if (failureCount > 0)
              _log.warn(
                  _messages.format(
                      "task-failure-summary",
                      new Object[] {
                          Integer.toString(failureCount),
                          Integer.toString(count),
                          _kind,
                          elapsedTime }));
          else
              _log.info(
                  _messages.format("task-summary", Integer.toString(count), _kind, elapsedTime));
      }
  
      /**
       * Executes a single task.
       * @param task the task to execute.
       * @return true if the task fails (throws an exception).
       */
      private boolean execute(Task task)
      {
          if (_log.isInfoEnabled())
              _log.info(_messages.format("executing-task", _kind, task.getTitle()));
  
          try
          {
              task.execute();
  
              return false;
          }
          catch (Exception ex)
          {
              _log.error(_messages.format("task-failure", _kind, task.getTitle(), ex.getMessage()));
  
               return true;
          }
      }
  
      public void setKind(String string)
      {
          _kind = string;
      }
  
      public void setLog(Log log)
      {
          _log = log;
      }
  
      public void setMessages(Messages messages)
      {
          _messages = messages;
      }
  
      public void setTasks(List list)
      {
          _tasks = list;
      }
  
  }
  ]]>
  </source>
  
  <p>
  HiveMind has a static convienience method, <code>sortOrderables()</code>, used to sort a list of Orderable
  objects into order, which is used here. Remember that the contributions to the Startup (and Shutdown)
  configuration points are made from multiple modules and there's no way to predict in what order those contributions
  will show up in the <code>tasks</code> property, which is why explicit sorting is necessary.
  </p>
  
  <p>
  At one time, there was a discussion about using a thread pool to allow execution of some of the tasks in parallel.
  That's
  a premature optimization: even with over forty startup tasks, startup still only takes about forty seconds.  
  </p>
  
  </section>
  
  <section><title>StaticTask class</title>
    
  <p>
  The StaticTask class allows an arbitrary public static method of a class
  to be treated like an <code>Executable</code>.
  </p>
  
  <source>
  package com.panorama.framework.startup.service;
  
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;
  
  import org.apache.hivemind.ApplicationRuntimeException;
  import org.apache.hivemind.impl.BaseLocatable;
  import org.apache.commons.lang.StringUtils;
  
  /**
   * Implementation of
   * {@link com.panorama.framework.startup.service.Executable}
   * that Invokes a static method on a public class.
   */
  public class StaticTask extends BaseLocatable implements Executable
  {
      private String _className;
      private String _methodName = "init";
  
      public void execute() throws Exception
      {
          checkNull("className", _className);
          checkNull("methodName", _methodName);
  
          Class clazz = Class.forName(_className);
          Method m = clazz.getMethod(_methodName, null);
  
          try
          {
              m.invoke(null, null);
          }
          catch (InvocationTargetException ex)
          {
              Throwable t = ex.getTargetException();
  
              if (t instanceof Exception)
                  throw (Exception)t;
  
              throw ex;
          }
      }
  
      private void checkNull(String propertyName, String value)
      {
          if (StringUtils.isBlank(value))
              throw new ApplicationRuntimeException(
                  "Property " + propertyName + " of " + this + " is null.",
                  getLocation(),
                  null);
      }
  
      public String getClassName()
      {
          return _className;
      }
  
      public String getMethodName()
      {
          return _methodName;
      }
  
      /**
       * Sets the name of a class containing a static method that will be executed.
       */
      public void setClassName(String string)
      {
          _className = string;
      }
  
      /**
       * Sets the name of a public static method taking no parameters.  The default is "init".
       * 
       */
      public void setMethodName(String string)
      {
          _methodName = string;
      }
  
  }
  </source>
  
  <p>
  The class implements
  <link href="&apiroot;/Locatable.html">Locatable</link>, which is used
  in method <code>isNull()</code> when reporting errors; the location will be the location
  of the &lt;invoke-static&gt; element the StaticTask instance was created from.
  </p>
  
  </section>
  
  </section>
  
  <section><title>Other Modules</title>
  
  <p>
  Other modules, in their HiveMind module deployment descriptors, make contributions
  into the Startup and Shutdown configuration points of the <code>panorama.framework.startup</code>
  module.  For example:
  </p>
  
  <source>
  module (id=panorama.coreservice.mail version="1.0.0")
  {
  	contribution (configuration-id=panorama.framework.startup.Startup)
  	{
  		task (title=Mail order=2600 class=com.panorama.coreservice.mail.startup.MailStartup)
  	}
  }
  </source>
  
  
  <p>
  Here, the Mail service contributes an instance of class <code>MailStartup</code>.
  Other modules take advantage of the &lt;invoke-static&gt; element:
  </p>
  
  <source>
  module (id=panorama.coreservice.garbagecollection version="1.0.0")
  {
  	contribution (configuration-id=panorama.framework.startup.Startup)
  	{
  		task (title="Scheduling Garbage Collection" order=3900)
  		{
  			invoke-static (class=com.panorama.coreservice.garbagecollection.startup.GarbageCollectionStartup)
  		}
  	}
  }
  </source>
  
  
    
  </section>
  
  <section><title>Other Modules</title>
    
  <p>
  The master servlet for the web application is responsible for constructing the registry
  and storing it so that
  other code may access it.
  </p>
    
  <source><![CDATA[
      public void init() throws ServletException
      {
          LOG.info("*** Bootstrapping HiveMind Registry ***");
  
          if (PanoramaRuntime.getHiveMindRegistry() != null)
          {
              LOG.info(
                  "Registry is already initialized (the application appears to have been redeployed).");
              return;
          }
  
           try
           {
               RegistryBuilder builder = new RegistryBuilder(new RegistryBuilderErrorHandler());
  
               ClassResolver resolver = new DefaultClassResolver();
  
               builder.processModules(resolver);
  
               Registry registry = builder.constructRegistry(Locale.getDefault());
  
               PanoramaRuntime.setHiveMindRegistry(registry);
  
               Runnable startup =
                   (Runnable)registry.getService("panorama.framework.startup.Startup", Runnable.class);
  
               LOG.info("*** Executing panorama.framework.startup.Startup service ***");
  
               startup.run();
           }
           catch (Exception ex)
           {
               LOG.error(
                   "Unable to execute panorama.framework.startup.Startup service: " + ex.getMessage());
           }
       }
  ]]></source>
    
  <p>
  After building the registry, the servlet uses the Startup service to indirectly
  execute all the startup tasks.
  </p>
  
  </section>
  
  <section><title>Handling Shutdown</title>
  	
  <p>
  We take advantage of a WebLogic extension to know when the application server is being shut down.
  </p>
  
  <source>
  package com.panorama.framework.startup;
  
  import javax.naming.InitialContext;
  import javax.rmi.PortableRemoteObject;
  
  import com.panorama.framework.startup.ejb.Shutdown;
  import com.panorama.framework.startup.ejb.ShutdownHome;
  
  /**
   * Shutdown class called by the WebLogic container.
   */
  public class Shutdown
  {
      private final static String EJB_JNDI_NAME =
          "com.panorama.framework.startup.ejb.initshutHome";
  
      /** Prevent instantiation */
      private Shutdown()
      {
      }
  
      /**
       *  Gets the Shutdown EJB and invokes <code>shutdown()</code>.
       */
  
      public static void main(String args[]) throws Exception
      {
          InitialContext context = new InitialContext();
          ShutdownHome home =
              (ShutdownHome)PortableRemoteObject.narrow(
                  context.lookup(EJB_JNDI_NAME),
                  ShutdownHome.class);
                  
          Shutdown bean = (Shutdown)home.create();
          
          bean.shutdown();
      }
  
  }  
  </source>
  
  <p>
  The implementation of the initshut EJB is similarily straight-forward:
  </p>  
  	
  <source><![CDATA[
  package com.panorama.framework.startup.ejb;
  
  import java.rmi.RemoteException;
  
  import javax.ejb.CreateException;
  
  import org.apache.hivemind.HiveMind;
  import org.apache.hivemind.Registry;
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  
  import com.panorama.framework.ejb.BaseSessionBean;
  
  /**
   * Handles shutdown logic.
   *
   */
  
  public class ShutdownEJB extends BaseSessionBean
  {
      private static final Log LOG = LogFactory.getLog(ShutdownEJB.class);
  
      public void ejbCreate() throws RemoteException, CreateException
      {
      }
  
      /**
       * Called by J2EE Container shutdown class for Panorama shutdown processing.
       * 
       * <p>
       * Gets the <code>panorama.framework.startup.Shutdown</code> service and executes it.
       * 
       */
      
      public void shutdown() throws RemoteException
      {
          Registry registry = PanoramaRuntime.getHiveMindRegistry();
  
          if (registry == null)
          {
              LOG.error(
                  "No HiveMind module registry is in place, unable to execute an orderly shutdown.");
              return;
          }
  
          Runnable r =
              (Runnable)registry.getService("panorama.framework.startup.Shutdown", Runnable.class);
  
          r.run();
  
          LOG.info("**** Panorama shutdown complete ****");
      }
  }]]>
  </source>  
  
  </section>
  
  <section><title>Summary</title> 
  
  <p>
  This case study has shown how easy it is to leverage HiveMind for a complex task. A monolithic EJB was broken down into
  tiny, agile contributions to a configuration point. 
  The startup and shutdown logic is kept close to the contributing
  modules, in those modules' HiveMind deployment descriptors.  Contributions are in expressive, easily readable XML.
  </p>
  
  <p>A single class is used to implement multiple, similar services, just by 
  configuring it as needed. Links between different aspects of the system (such as
  the servlet initialization code and the Startup service) are kept simple and agile.
  </p>
  
  <p>
  The small amount of code necessary to orchestrate all this is fully tested in a unit test suite.  
  </p>
  
  <p>
  The end result: an agile, easily extended system.  HiveMind has provided the tools and environment
  to support an elegant, data-driven solution ... replacing the old, code-heavy EJB implementation.  
  </p>
    
  </section>
  
  </body>
  </document>
  
  
  1.1                  jakarta-hivemind/src/documentation/content/xdocs/links.ent
  
  Index: links.ent
  ===================================================================
  <!-- $Id: links.ent,v 1.1 2004/05/25 22:31:40 hlship Exp $ -->
  <!-- 
     Copyright 2004 The Apache Software Foundation
  
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
  
         http://www.apache.org/licenses/LICENSE-2.0
  
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
  -->
  
  <!ENTITY projectroot ''>
  
  <!ENTITY apiroot '&projectroot;hivemind/apidocs/org/apache/hivemind'>
  <!ENTITY hivedoc '&projectroot;hivedocs'>
  
  <!-- Fuykin Maven - - if I remove the &lt; and &gt; from the entities below it breaks
  	   (doesn't render any text within the <a> and </link> tags). Why? Who knows, its Maven. -->
  
  <!ENTITY _module '<code>&lt;module&gt;</code>'>
  <!ENTITY module '<link href="&projectroot;descriptor.html#module">&_module;</link>'>
  
  <!ENTITY _implementation '<code>&lt;implementation&gt;</code>'>
  <!ENTITY implementation '<link href="&projectroot;descriptor.html#implementation">&_implementation;</link>'>
  
  <!ENTITY _configuration-point '<code>&lt;configuration-point&gt;</code>'>
  <!ENTITY configuration-point '<link href="&projectroot;descriptor.html#configuration-point">&_configuration-point;</link>'>
  
  <!ENTITY _conversion '<code>&lt;conversion&gt;</code>'>
  <!ENTITY conversion '<link href="&projectroot;descriptor.html#conversion">&_conversion;</link>'>
  
  <!ENTITY _map '<code>&lt;map&gt;</code>'>
  <!ENTITY map '<link href="&projectroot;descriptor.html#map">&_map;</link>'>
  
  <!ENTITY _create-instance '<code>&lt;create-instance&gt;</code>'>
  <!ENTITY create-instance '<link href="&projectroot;descriptor.html#create-instance">&_create-instance;</link>'>
  
  <!ENTITY _invoke-factory '<code>&lt;invoke-factory&gt;</code>'>
  <!ENTITY invoke-factory '<link href="&projectroot;descriptor.html#invoke-factory">&_invoke-factory;</link>'>
  
  <!ENTITY _interceptor '<code>&lt;interceptor&gt;</code>'>
  <!ENTITY interceptor '<link href="&projectroot;descriptor.html#interceptor">&_interceptor;</link>'>
  
  <!ENTITY _description '<code>&lt;description&gt;</code>'>
  <!ENTITY description '<link href="&projectroot;descriptor.html#description">&_description;</link>'>
  
  <!ENTITY _contribution '<code>&lt;contribution&gt;</code>'>
  <!ENTITY contribution '<link href="&projectroot;descriptor.html#contribution">&_contribution;</link>'>
  
  <!ENTITY _service-point '<code>&lt;service-point&gt;</code>'>
  <!ENTITY service-point '<link href="&projectroot;descriptor.html#service-point">&_service-point;</link>'>
  
  <!ENTITY _schema '<code>&lt;schema&gt;</code>'>
  <!ENTITY schema '<link href="&projectroot;descriptor.html#schema">&_schema;</link>'>
  
  <!ENTITY _parameters-schema '<code>&lt;parameters-schema&gt;</code>'>
  <!ENTITY parameters-schema '<link href="&projectroot;descriptor.html#schema">&_parameters-schema;</link>'>
  
  <!ENTITY _element '<code>&lt;element&gt;</code>'>
  <!ENTITY element '<link href="&projectroot;descriptor.html#element">&_element;</link>'>
  
  <!ENTITY _attribute '<code>&lt;attribute&gt;</code>'>
  <!ENTITY attribute '<link href="&projectroot;descriptor.html#attribute">&_attribute;</link>'>
  
  <!ENTITY _rules '<code>&lt;rules&gt;</code>'>
  <!ENTITY rules '<link href="&projectroot;descriptor.html#rules">&_rules;</link>'>
  
  <!ENTITY _sub-module '<code>&lt;sub-module&gt;</code>'>
  <!ENTITY sub-module '<link href="&projectroot;descriptor.html#sub-module">&_sub-module;</link>'>
  
  <!-- XML entities for the XML processing rules (documented seperately from the rest). -->
  
  <!ENTITY _create-object '<code>&lt;create-object&gt;</code>'>
  <!ENTITY create-object '<link href="&projectroot;rules.html#create-object">&_create-object;</link>'>
  
  <!ENTITY _invoke-parent '<code>&lt;invoke-parent&gt;</code>'>
  <!ENTITY invoke-parent '<link href="&projectroot;rules.html#invoke-parent">&_invoke-parent;</link>'>
  
  <!ENTITY _read-attribute '<code>&lt;read-attribute&gt;</code>'>
  <!ENTITY read-attribute '<link href="&projectroot;rules.html#read-attribute">&_read-attribute;</link>'>
  
  <!ENTITY _read-content '<code>&lt;read-content&gt;</code>'>
  <!ENTITY read-content '<link href="&projectroot;rules.html#read-content">&_read-content;</link>'>
  
  
  <!ENTITY _set-module '<code>&lt;set-module&gt;</code>'>
  <!ENTITY set-module '<link href="&projectroot;rules.html#set-module">&_set-module;</link>'>
  
  <!ENTITY _set-parent '<code>&lt;set-parent&gt;</code>'>
  <!ENTITY set-parent '<link href="&projectroot;rules.html#set-parent">&_set-parent;</link>'>
  
  <!ENTITY _set-property '<code>&lt;set-property&gt;</code>'>
  <!ENTITY set-property '<link href="&projectroot;rules.html#set-property">&_set-property;</link>'>
  
  <!ENTITY _custom '<code>&lt;custom&gt;</code>'>
  <!ENTITY custom '<link href="&projectroot;rules.html#custom">&_custom;</link>'>
  
  <!ENTITY _push-attribute '<code>&lt;push-attribute&gt;</code>'>
  <!ENTITY push-attribute '<link href="&projectroot;rules.html#push-attribute">&_push-attribute;</link>'>
  
  
  
  1.1                  jakarta-hivemind/src/documentation/content/xdocs/tabs.xml
  
  Index: tabs.xml
  ===================================================================
  <?xml version="1.0" encoding="UTF-8"?>
  <!--
     Copyright 2004 The Apache Software Foundation
    
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
    
         http://www.apache.org/licenses/LICENSE-2.0
    
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
  -->
  <!-- $Id: tabs.xml,v 1.1 2004/05/25 22:31:40 hlship Exp $ -->
  <!DOCTYPE tabs PUBLIC "-//APACHE//DTD Cocoon Documentation Tab V1.0//EN" "http://apache.org/forrest/dtd/tab-cocoon-v10.dtd">
  
  <tabs software="HiveMind"
    title="HiveMind Project"
    copyright="Apache Software Foundation"
    xmlns:xlink="http://www.w3.org/1999/xlink">
  
    <!-- The rules are:
      @dir will always have '/@indexfile' added.
      @indexfile gets appended to @dir if the tab is selected. Defaults to 'index.html'
      @href is not modified unless it is root-relative and obviously specifies a
      directory (ends in '/'), in which case /index.html will be added
      
      HLS NOTE: I don't think the following line is accurate!
      
      If @id's are present, site.xml entries with a matching @tab will be in that tab.
    -->
  
    <tab id="project" label="HiveMind Project" dir="" indexfile="index.html"/>
    
    <!-- Tabs just not working right in Forrest 0.5.1 !!! -->
    
    <!-- tab id="hivemind" label="Module: hivemind" dir="hivemind" indexfile="index.html"/ -->
    
   </tabs>
  
  
  
  1.1                  jakarta-hivemind/src/documentation/content/xdocs/bootstrap.xml
  
  Index: bootstrap.xml
  ===================================================================
  <?xml version="1.0"?>
  <!-- $Id: bootstrap.xml,v 1.1 2004/05/25 22:31:40 hlship Exp $ -->
  <!-- 
     Copyright 2004 The Apache Software Foundation
  
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
  
         http://www.apache.org/licenses/LICENSE-2.0
  
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
  -->
  <!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.2//EN" "./dtd/document-v12.dtd"
  [
  	<!ENTITY % common-links SYSTEM "links.ent">
  	%common-links;
  ]>
  <document>
  	
  	<header>
  		<title>Bootstrapping the Registry</title>
  	</header>
  	
  	<body>
  
  
  <p>
  Before you can access the configuration points and services defined in your	many module deployment
  descriptors, you need a registry; here we'll describe how to construct the registry.
  </p>
  
  <p>
  The key class here is
  <link href="&apiroot;/impl/RegistryBuilder.html">RegistryBuilder</link>, which contains
  code for locating and parsing the module deployment descriptors and constructing a registry from the combined
  data.
  </p>
  
  
  <p>
  Let's examine how all this comes together. The layout of the project is shown below.
  </p>
  
  <figure src="images/AdderExample-ProjectLayout.png" alt="[Project Layout]"/>
  
  
  <section> <title>Services, Interfaces, Descriptors</title> 
  
  <p>
  The first step is to define the service interface:
  </p>	
  
  <source>
  package hivemind.examples;
  
  public interface Adder
  {
      public int add(int arg0, int arg1);
  }	
  </source>
  	
  <p>
  Next we need an implementation for that service:	
  </p>	
  
  <source>
  package hivemind.examples.impl;
  
  import hivemind.examples.Adder;
  
  public class AdderImpl implements Adder
  {
  
      public int add(int arg0, int arg1)
      {
          return arg0 + arg1;
      }
  
  }
  </source>
  
  <p>
  Finally, we need the HiveMind module deployment descriptor,
  <code>hivemodule.sdl</code>.  This file is in
  <link href="site:sdl">Simple Data Language</link> format (though equivalent XML
  is supported if the file is named <code>hivemodule.xml</code>).
  </p>
  
  <source>
  module (id=hivemind.examples version="1.0.0")
  {
    service-point (id=Adder interface=hivemind.examples.Adder)
    {
      create-instance (class=hivemind.examples.impl.AdderImpl)
     }
  }	
  </source>
  
  <p>
  Here we've chosen to have the module id, <code>hivemind.examples</code>,	match the package name but
  that is not an absolute requirement.
  </p>
  
  </section>
  
  <section><title>Building the Registry</title>
  
  <p>
  Before your code can access any services (or configuration points), it must construct the registry.	
  </p>	
  	
  
  <source>
  package hivemind.examples;
  
  import java.util.Locale;
  
  import org.apache.hivemind.ClassResolver;
  import org.apache.hivemind.Registry;
  import org.apache.hivemind.impl.DefaultClassResolver;
  import org.apache.hivemind.impl.RegistryBuilder;
  
  public class Main
  {
  
      public static void main(String[] args)
      {
          int arg0 = Integer.parseInt(args[0]);
          int arg1 = Integer.parseInt(args[1]);
  
          ClassResolver resolver = new DefaultClassResolver();
          RegistryBuilder builder = new RegistryBuilder();
  
          builder.processModules(resolver);
  
          Registry registry = builder.constructRegistry(Locale.getDefault());
  
          Adder adder = (Adder) registry.getService("hivemind.examples.Adder", Adder.class);
  
          System.out.println("Result: " + adder.add(arg0, arg1));
      }
  }
  	
  </source>	
  
  <p>
  Building the registry requires four steps:
  
  </p>
  
  
  <ul>
  <li>Create a <link href="&apiroot;/ClassResolver.html">ClassResolver</link> instance. 
  	DefaultClassResolver uses the thread's context class loader.</li>	
  <li>
  Create a <link href="&apiroot;/impl/RegistryBuilder.html">RegistryBuilder</link> instance.
  </li>
  <li>
  Invoke <code>processModules()</code> to find and parse all HiveMind module deployment descriptors visible on
  the classpath (as <code>META-INF/hivemodule.sdl</code> or
  	<code>META-INF/hivemodule.xml</code>).  Here, this will be the master HiveMind module descriptor (for
  the <code>hivemind</code> module), and the descriptor for this example module. You could invoke <code>processModule()</code>
  to parse additional deployment descriptors stored in unusual locations.
  </li>
  <li>
  Invoke <code>constructRegistry()</code>	to integrate and validate all the information in all
  of the HiveMind module deployment descriptors and produce a 
  <link href="&apiroot;/Registry.html">Registry</link> from it.
  </li>
  
  </ul>	
  
  <p>
  Alternately, the static method 
  <link href="&apiroot;/impl/RegistryBuilder.html#constructDefaultRegistry()">constructDefaultRegistry()</link>	can be invoked.  It's just those same four lines of code.
  </p>
  
  
  <p>
  Now that we have the registry, we can use the full id of the Adder service, <code>hivemind.examples.Adder</code>,	
  to get the service implementation. We pass in the class that we'll be casting the service to ... this allows HiveMind 
  to produce a more meaningful error than a ClassCastException.
  </p>
  
  <p>
  Using the reference to the Adder service, we can finally invoke the <code>add()</code>	method.
  </p>
  
  </section>
  
  <section><title>Building the Example</title>
  
  <p>
  Building and running the example using Ant is a snap; all the details are in the <code>build.xml</code>:
  </p>
  
  
  <source><![CDATA[<?xml version="1.0"?>
  
  <project name="HiveMind Adder Example" default="jar">
  
    <property name="java.src.dir" value="src/java"/>
    <property name="conf.dir" value="src/conf"/>
    <property name="meta-inf.dir" value="src/META-INF"/>
    <property name="target.dir" value="target"/>
    <property name="classes.dir" value="${target.dir}/classes"/>
    <property name="example.jar" value="${target.dir}/hivemind-examples.jar"/>
    <property name="lib.dir" value="lib"/>
  
    <path id="build.class.path">
      <fileset dir="${lib.dir}">
        <include name="*.jar"/>
      </fileset>
    </path>
    
    <path id="run.class.path">
      <path refid="build.class.path"/>
      <pathelement location="${example.jar}"/>
      <pathelement location="${conf.dir}"/>
    </path>
      
    <target name="clean" description="Delete all derived files.">
      <delete dir="${target.dir}" quiet="true"/>
    </target>
    
    <target name="compile" description="Compile all Java code.">  
      <mkdir dir="${classes.dir}"/>    
      <javac srcdir="${java.src.dir}" destdir="${classes.dir}" classpathref="build.class.path"/>
    </target>
    
    <target name="jar" description="Construct the JAR file." depends="compile">
      <jar destfile="${example.jar}">
        <fileset dir="${classes.dir}"/>
        <metainf dir="${meta-inf.dir}"/>
      </jar>
    </target>
    
    <target name="run" depends="jar" description="Run the Adder service.">
      <echo>Adding 11 and 23, should get 34.</echo>
    
      <java classname="hivemind.examples.Main" classpathref="run.class.path" fork="true">
        <arg value="11"/>
        <arg value="23"/>
      </java>
    </target>
  
  </project>]]></source>
  
  <p>
  The important part is to package both the classes and the HiveMind module deployment descriptor
  into the JAR.  The latter is accomplished using the &lt;metainf&gt; element.
  </p>
  
  <p>
  The only other oddity was to add <code>src/conf</code>	to the runtime classpath; this is to include
  the <code>log4j.properties</code> configuration file; 
  otherwise Log4J will write console errors about missing configuration.
  </p>
  	
  </section>
  </body>
  </document>
  
  
  1.1                  jakarta-hivemind/forrest.properties
  
  Index: forrest.properties
  ===================================================================
  # 
  
  project.name=HiveMind
  project.build-dir=${project.home}/target/forrest
  project.site-dir=${project.home}/target/docs
  project.temp-dir=target/forrest
  
  project.content-dir=target/forrest-composite
  forrest.echo=on
  project.debuglevel=DEBUG
  
  
  1.3       +7 -6      jakarta-hivemind/common/jar-module.xml
  
  Index: jar-module.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-hivemind/common/jar-module.xml,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- jar-module.xml	21 May 2004 18:46:54 -0000	1.2
  +++ jar-module.xml	25 May 2004 22:31:41 -0000	1.3
  @@ -23,6 +23,7 @@
   	
   	<import file="common.xml"/>
   	<import file="dependency.xml"/>
  +	<import file="forrest.xml"/>
   		
   	<!-- The default classpath for compilation is all external package JARs. -->
   	
  @@ -209,9 +210,9 @@
   	
   	<target name="jar" depends="run-tests" description="Compile classes and package into a JAR.">
   		
  -		<fail unless="jar.name" message="Property jar.name must be set."/>
  +		<fail unless="module.name" message="Property module.name must be set."/>
   		
  -		<property name="complete-jar-path" value="${jar.target.dir}/${jar.name}-${project.version}.jar"/>
  +		<property name="complete-jar-path" value="${jar.target.dir}/${module.name}-${project.version}.jar"/>
   		
   		<mkdir dir="${jar.target.dir}"/>
   		
  @@ -250,7 +251,7 @@
   				version="yes"
   				use="yes"
   				splitindex="yes"
  -				windowtitle="${jar.name} - ${project.version} API">
  +				windowtitle="${module.name} - ${project.version} API">
   				<package name="${javadoc.package}"/>
   				<sourcepath>
   					<pathelement location="${java.src.dir}"/>
  @@ -265,6 +266,6 @@
   	</target>
   	
   	<target name="install" depends="jar" 
  -		description="Synonum for 'jar' invoked by the containing project."/>
  -	
  +		description="Synonym for 'jar' invoked by the containing project."/>
  +			
   </project>
  
  
  
  1.2       +29 -1     jakarta-hivemind/common/project.xml
  
  Index: project.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-hivemind/common/project.xml,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- project.xml	21 May 2004 18:46:54 -0000	1.1
  +++ project.xml	25 May 2004 22:31:41 -0000	1.2
  @@ -19,6 +19,13 @@
   <!-- Top level module used to combine other types of modules. -->
   
   	<import file="common.xml"/>
  +	<import file="dependency.xml"/>
  +	<import file="forrest.xml"/>
  +	
  +	<!-- Typically, project.name is all lower case and the title is mixed case, but
  +		the project name is still a good default. -->
  +		
  +	<property name="project.title" value="${project.name}"/>
   	
   	<macrodef name="reinvoke-ant">
   		<attribute name="target" description="Target to achieve in each module."/>
  @@ -44,4 +51,25 @@
   		<antcall target="common.clean"/>	
   		
   	</target>
  +	
  +	<target name="-setup-forrest-composite">
  +	
  +		<mkdir dir="${root.forrest.composite.dir}"/>
  +			
  +		<!-- Create and empty this file. -->
  +		<mkdir dir="${root.forrest.composite.dir}/content/xdocs"/>
  +		<echo message="" file="${forrest.report-menu.file}"/>
  +						
  +	</target>
  +	
  +	<target name="site" description="Create project documentation." depends="-setup-forrest-composite">
  +	
  +		<antcall target="forrest.site"/>	
  +	</target>
  +	
  +	<target name="marshall-documentation" description="Marshall documentation in the project and in each module.">
  +		<antcall target="forrest.marshall-documentation"/>
  +		<reinvoke-ant target="marshall-documentation"/>
  +	</target>
  +	
   </project>
  
  
  
  1.2       +1 -21     jakarta-hivemind/common/javacc.xml
  
  Index: javacc.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-hivemind/common/javacc.xml,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- javacc.xml	20 May 2004 23:57:44 -0000	1.1
  +++ javacc.xml	25 May 2004 22:31:41 -0000	1.2
  @@ -19,27 +19,7 @@
   		<fail unless="ant.file.common" message="Must import common.xml first."/>
   		<fail unless="ant.file.dependency" message="Must import dependency.xml first."/>
   		
  -		<property name="javacc.home.dir" value="${external.package.dir}/javacchome"/>
   		<property name="javacc.dist.zip" value="${external.package.dir}/JavaCC.zip"/>
  -		
  -		<target name="-expand-javacc-home" unless="exists-javacc-home">
  -			<grabber
  -					src="${maven.ibiblio.url}/javacc/jars/JavaCC.zip"
  -					dest="${javacc.dist.zip}"/>
  -					
  -			<!-- mkdir dir="${javacc.home.dir}"/>
  -			
  -			<unzip src="${javacc.dist.zip}" dest="${javacc.home.dir}"/ -->			
  -		</target>
  -		
  -		<macrodef name="setup-javacc">
  -			<sequential>
  -				<available file="${javacc.home.dir}" type="dir" property="exists-javacc-home"/>
  -				<antcall target="-expand-javacc-home"/>
  -				<download-from-ibiblio jar="javacc-3.2.jar" group-id="javacc"/>			
  -				<mkdir dir="${generated-java.src.dir}"/>
  -			</sequential>		
  -		</macrodef>
   		
   		<macrodef name="run-javacc">
   			<attribute name="input" description="The path to the input (.jj) file to compile."/>
  
  
  
  1.2       +38 -9     jakarta-hivemind/common/dependency.xml
  
  Index: dependency.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-hivemind/common/dependency.xml,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- dependency.xml	20 May 2004 23:57:44 -0000	1.1
  +++ dependency.xml	25 May 2004 22:31:41 -0000	1.2
  @@ -31,20 +31,23 @@
   			
   	<target name="-display-download-warning" unless="download-warning-marker-displayed">
   		<echo>
  -
  -***			
  -*** Dependent libraries will be downloaded.  These are NOT necessarily downloaded from apache.com, and may
  -*** use other licences besides the Apache Software License. Dependencies will use an open-source
  -*** license compatible with the ASL, such as Berkeley Software Distribution (BSD) or
  -*** Mozilla Public License (MPL).
  -***
  -		
  +**************************************************************************************************
  +*                                                                                                *
  +*                             D O W N L O A D   W A R N I N G                                    *
  +*                                                                                                *
  +* Dependent libraries will be downloaded.  These are NOT necessarily downloaded from apache.org, *  
  +* and may use other licences besides the Apache Software License. Dependencies will use an       *
  +* open-source license compatible with the ASL, such as Berkeley Software Distribution (BSD) or   *
  +* Mozilla Public License (MPL).                                                                  *
  +*                                                                                                *
  +**************************************************************************************************
   </echo>
   	
   		<input 
   			validargs="continue" 
   			message="Enter 'continue' to continue with the build:"/>
   	
  +		<mkdir dir="${external.package.dir}"/>
   		<echo file="${download-warning-marker.file}">Download warning accepted.</echo>
   		
   	</target>
  @@ -83,6 +86,32 @@
   			
   		</sequential>
   	</macrodef>
  +	
  +	<available file="${unpack-zip-dir}" type="dir" property="unpacked-zip-dir-exists"/>
  +	
  +	<target name="-unpack-zip-dependency" unless="unpacked-zip-dir-exists" depends="-display-download-warning">
  +		<property name="local-copy-path" value="${external.package.dir}/${unpack-zip-name}"/>
  +		<grabber
  +				src="${unpack-zip-url}"
  +				dest="${local-copy-path}"/>
  +		<unzip src="${local-copy-path}" dest="${external.package.dir}"/>
  +	</target>
  +	
  +	<macrodef name="unpacked-zip-dependency">
  +		<attribute name="url" description="The URL of the folder containing the zip file."/>
  +		<attribute name="zip" description="The name of the zip file itself."/>
  +		<attribute name="dir" description="The name of the directory that will be created."/>
  +		
  +		<sequential>
  +			<antcall target="-unpack-zip-dependency" inheritAll="false">
  +				<param name="unpack-zip-dir" value="@{dir}"/>
  +				<param name="unpack-zip-url" value="@{url}/@{zip}"/>
  +				<param name="unpack-zip-name" value="@{zip}"/>
  +			</antcall>
  +		</sequential>
  +		
  +	</macrodef>
  +
   	
   	<!-- An internal dependency to another library previously created
   		   by a sibiling module.  Assumes that the module is infixed
  
  
  
  1.2       +15 -1     jakarta-hivemind/common/common.properties
  
  Index: common.properties
  ===================================================================
  RCS file: /home/cvs/jakarta-hivemind/common/common.properties,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- common.properties	20 May 2004 23:57:44 -0000	1.1
  +++ common.properties	25 May 2004 22:31:41 -0000	1.2
  @@ -93,3 +93,17 @@
   
   # Directory to which javadoc is generated
   javadoc.target.dir=${doc.target.dir}/api
  +
  +# Directory to which project and module documentation files are ultimately copied (or generated)
  +root.forrest.composite.dir=${root.target.dir}/forrest-composite
  +
  +root.forrest.content.dir=${root.forrest.composite.dir}/content/xdocs
  +
  +# File into which reports can record menu items.
  +forrest.report-menu.file=${root.forrest.composite.dir}/content/xdocs/report-menu.ent
  +
  +# Directory containing documentation to be copied into the composite.
  +forrest.documentation.dir=${src.dir}/documentation
  +
  +
  +
  
  
  
  1.3       +4 -1      jakarta-hivemind/common/common.xml
  
  Index: common.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-hivemind/common/common.xml,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- common.xml	21 May 2004 18:46:54 -0000	1.2
  +++ common.xml	25 May 2004 22:31:41 -0000	1.3
  @@ -38,5 +38,8 @@
   		<delete dir="${external.package.dir}" quiet="true"/>	
   	</target>
   
  +	<presetdef name="invoke-ant">
  +		<ant inheritAll="false"/>
  +	</presetdef>
   
   </project>
  
  
  
  1.1                  jakarta-hivemind/common/forrest.xml
  
  Index: forrest.xml
  ===================================================================
  <?xml version="1.0"?>
  <project name="forrest">
  	
  		<fail unless="ant.file.common" message="Must import common.xml first."/>
  		<fail unless="ant.file.dependency" message="Must import dependency.xml first."/>
  
  		<property name="forrest-package-dir" value="${external.package.dir}/apache-forrest-0.5.1-bin"/>
  
  		<target name="site" description="Build site documentation using Forrest.">
  		
  			<unpacked-zip-dependency
  					url="http://www.apache.org/dist/xml/forrest/binaries"
  					zip="apache-forrest-0.5.1-bin.zip"
  					dir="${forrest-package-dir}"/>
  			
  			<antcall target="marshall-documentation"/>
  			
  			<echo>
  				
  *** Invoking Forrest ...
  				
  </echo>
  			<ant antfile="${forrest-package-dir}/forrest.antproxy.xml" target="site">
  				<property name="project.home" location="${basedir}"/>
  				<property name="forrest.home" location="${forrest-package-dir}"/>
  			</ant>
  			
  			
  		</target>
  		
  		<target name="marshall-documentation"
  			description="Copy static content into the forrest composite, and generate dynamic reports.">
  	
  			<mkdir dir="${root.forrest.composite.dir}"/>	
  						
  			<antcall target="copy-documentation-to-composite"/>
  			<antcall target="run-reports"/>
  			
  		</target>
  			
  		<target name="run-reports" description="Overridden in project or module to run dynamic reports."/>
  		
  		<available file="${forrest.documentation.dir}" type="dir" property="exists-forrest-documentation-dir"/>
  		
  		<target name="copy-documentation-to-composite" if="exists-forrest-documentation-dir">
  			<copy todir="${root.forrest.composite.dir}" includeEmptyDirs="no">
  				<fileset dir="${forrest.documentation.dir}"/>	
  			</copy>
  		</target>
  		
  </project>
  
  
  1.6       +56 -7     jakarta-hivemind/framework/src/test/hivemind/test/rules/TestEnumerationTranslator.java
  
  Index: TestEnumerationTranslator.java
  ===================================================================
  RCS file: /home/cvs/jakarta-hivemind/framework/src/test/hivemind/test/rules/TestEnumerationTranslator.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- TestEnumerationTranslator.java	29 Feb 2004 20:57:09 -0000	1.5
  +++ TestEnumerationTranslator.java	25 May 2004 22:31:41 -0000	1.6
  @@ -19,9 +19,12 @@
   import org.apache.hivemind.ApplicationRuntimeException;
   import org.apache.hivemind.Element;
   import org.apache.hivemind.Location;
  +import org.apache.hivemind.impl.DefaultClassResolver;
   import org.apache.hivemind.impl.ElementImpl;
   import org.apache.hivemind.impl.LocationImpl;
  +import org.apache.hivemind.internal.Module;
   import org.apache.hivemind.schema.rules.EnumerationTranslator;
  +import org.easymock.MockControl;
   
   /**
    * Tests for {@link org.apache.hivemind.schema.rules.EnumerationTranslator}.
  @@ -43,29 +46,54 @@
   
       public void testNull()
       {
  +        MockControl c = MockControl.createStrictControl(Module.class);
  +        Module m = (Module) c.getMock();
  +
  +        c.replay();
  +
           EnumerationTranslator t =
               new EnumerationTranslator("java.lang.Boolean,true=TRUE,false=FALSE");
   
  -        assertEquals(null, t.translate(new MockModule(), null, null));
  +        assertEquals(null, t.translate(m, null, null));
  +
  +        c.verify();
       }
   
       public void testMatch()
       {
  +        MockControl c = MockControl.createStrictControl(Module.class);
  +        Module m = (Module) c.getMock();
  +
  +        m.getClassResolver();
  +        c.setReturnValue(new DefaultClassResolver());
  +
  +        c.replay();
  +
           EnumerationTranslator t =
               new EnumerationTranslator("java.lang.Boolean,true=TRUE,false=FALSE");
   
  -        assertEquals(Boolean.TRUE, t.translate(new MockModule(), null, "true"));
  -        assertEquals(Boolean.FALSE, t.translate(new MockModule(), null, "false"));
  +        assertEquals(Boolean.TRUE, t.translate(m, null, "true"));
  +        assertEquals(Boolean.FALSE, t.translate(m, null, "false"));
  +
  +        c.verify();
       }
   
       public void testBadClass()
       {
  +        MockControl c = MockControl.createStrictControl(Module.class);
  +        Module m = (Module) c.getMock();
  +
  +		m.getClassResolver();
  +		c.setReturnValue(new DefaultClassResolver());
  +
  +        c.replay();
  +
           EnumerationTranslator t =
               new EnumerationTranslator("lava.jang.Boolean,true=TRUE,false=FALSE");
   
           try
           {
  -            t.translate(new MockModule(), null, "true");
  +            t.translate(m, null, "true");
   
               unreachable();
           }
  @@ -74,17 +102,26 @@
               assertExceptionSubstring(ex, "Could not load class lava.jang.Boolean");
           }
   
  +        c.verify();
       }
   
       public void testUnrecognizedValue() throws Exception
       {
  +        MockControl c = MockControl.createStrictControl(Module.class);
  +        Module m = (Module) c.getMock();
  +
  +		m.getClassResolver();
  +		c.setReturnValue(new DefaultClassResolver());
  +
  +        c.replay();
  +
           EnumerationTranslator t =
               new EnumerationTranslator("java.lang.Boolean,true=TRUE,false=FALSE");
   
           try
           {
   
  -            t.translate(new MockModule(), null, "fred");
  +            t.translate(m, null, "fred");
               unreachable();
           }
           catch (ApplicationRuntimeException ex)
  @@ -92,16 +129,26 @@
               assertExceptionSubstring(ex, "'fred' is not a recognized enumerated value.");
           }
   
  +        c.verify();
  +
       }
   
       public void testBadField() throws Exception
       {
  +        MockControl c = MockControl.createStrictControl(Module.class);
  +        Module m = (Module) c.getMock();
  +
  +		m.getClassResolver();
  +		c.setReturnValue(new DefaultClassResolver());
  +
  +        c.replay();
  +
           EnumerationTranslator t =
               new EnumerationTranslator("java.lang.Boolean,true=HONEST_TO_GOD_TRUE,false=FALSE");
   
           try
           {
  -            t.translate(new MockModule(), null, "true");
  +            t.translate(m, null, "true");
               unreachable();
           }
           catch (ApplicationRuntimeException ex)
  @@ -110,6 +157,8 @@
                   ex,
                   "Unable to obtain value for static field java.lang.Boolean.HONEST_TO_GOD_TRUE");
           }
  +
  +        c.verify();
       }
   
   }
  
  
  
  1.8       +14 -5     jakarta-hivemind/framework/src/test/hivemind/test/rules/TestConfigurationTranslator.java
  
  Index: TestConfigurationTranslator.java
  ===================================================================
  RCS file: /home/cvs/jakarta-hivemind/framework/src/test/hivemind/test/rules/TestConfigurationTranslator.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- TestConfigurationTranslator.java	18 May 2004 15:34:01 -0000	1.7
  +++ TestConfigurationTranslator.java	25 May 2004 22:31:41 -0000	1.8
  @@ -14,13 +14,15 @@
   
   package hivemind.test.rules;
   
  -import java.util.List;
  -
   import hivemind.test.FrameworkTestCase;
   
  +import java.util.List;
  +
   import org.apache.hivemind.Registry;
  +import org.apache.hivemind.internal.Module;
   import org.apache.hivemind.internal.RegistryInfrastructure;
   import org.apache.hivemind.schema.rules.ConfigurationTranslator;
  +import org.easymock.MockControl;
   
   /**
    * Tests for {@link org.apache.hivemind.schema.rules.ConfigurationTranslator}.
  @@ -33,9 +35,16 @@
   
       public void testNull()
       {
  +        MockControl c = MockControl.createStrictControl(Module.class);
  +        Module m = (Module) c.getMock();
  +
  +        c.replay();
  +
           ConfigurationTranslator t = new ConfigurationTranslator();
   
  -        assertNull(t.translate(new MockModule(), null, null));
  +        assertNull(t.translate(m, null, null));
  +
  +        c.verify();
       }
   
       public void testConfigurationTranslator() throws Exception
  @@ -54,7 +63,7 @@
   
       public void testFailure() throws Exception
       {
  -        Registry r = (Registry)buildFrameworkRegistry("ConfigurationTranslator.xml");
  +        Registry r = (Registry) buildFrameworkRegistry("ConfigurationTranslator.xml");
   
           interceptLogging();
   
  
  
  
  1.10      +33 -5     jakarta-hivemind/framework/src/test/hivemind/test/rules/TestObjectTranslator.java
  
  Index: TestObjectTranslator.java
  ===================================================================
  RCS file: /home/cvs/jakarta-hivemind/framework/src/test/hivemind/test/rules/TestObjectTranslator.java,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- TestObjectTranslator.java	18 May 2004 15:34:01 -0000	1.9
  +++ TestObjectTranslator.java	25 May 2004 22:31:41 -0000	1.10
  @@ -20,12 +20,14 @@
   
   import org.apache.hivemind.ApplicationRuntimeException;
   import org.apache.hivemind.Location;
  -import org.apache.hivemind.Registry;
  +import org.apache.hivemind.impl.DefaultClassResolver;
   import org.apache.hivemind.impl.ElementImpl;
   import org.apache.hivemind.impl.LocationImpl;
  +import org.apache.hivemind.internal.Module;
   import org.apache.hivemind.internal.RegistryInfrastructure;
   import org.apache.hivemind.schema.rules.ClassTranslator;
   import org.apache.hivemind.schema.rules.ObjectTranslator;
  +import org.easymock.MockControl;
   
   /**
    * Fill in some gaps in
  @@ -52,15 +54,24 @@
           Location l = new LocationImpl(getResource("TestObjectTranslator.class"), 50);
           e.setLocation(l);
   
  +        MockControl c = MockControl.createStrictControl(Module.class);
  +        Module m = (Module) c.getMock();
  +
  +        m.getClassResolver();
  +        c.setReturnValue(new DefaultClassResolver());
  +
  +        c.replay();
  +
           try
           {
  -            t.translate(new MockModule(), null, "bad.class.Name");
  +            t.translate(m, null, "bad.class.Name");
           }
           catch (ApplicationRuntimeException ex)
           {
               assertExceptionSubstring(ex, "Could not load class bad.class.Name");
           }
   
  +        c.verify();
       }
   
       public void testPrivateObject() throws Exception
  @@ -70,9 +81,17 @@
           Location l = new LocationImpl(getResource("TestObjectTranslator.class"), 50);
           e.setLocation(l);
   
  +        MockControl c = MockControl.createStrictControl(Module.class);
  +        Module m = (Module) c.getMock();
  +
  +        m.getClassResolver();
  +        c.setReturnValue(new DefaultClassResolver());
  +
  +        c.replay();
  +
           try
           {
  -            t.translate(new MockModule(), null, PrivateObject.class.getName());
  +            t.translate(m, null, PrivateObject.class.getName());
               unreachable();
           }
           catch (ApplicationRuntimeException ex)
  @@ -102,11 +121,20 @@
   
       public void testClassTranslator() throws Exception
       {
  +        MockControl control = MockControl.createStrictControl(Module.class);
  +        Module m = (Module) control.getMock();
  +
  +        m.getClassResolver();
  +        control.setReturnValue(new DefaultClassResolver());
  +
  +        control.replay();
  +
           ClassTranslator t = new ClassTranslator();
   
  -        Class c = (Class) t.translate(new MockModule(), null, getClass().getName());
  +        Class c = (Class) t.translate(m, null, getClass().getName());
   
           assertEquals(getClass(), c);
   
  +        control.verify();
       }
   }
  
  
  
  1.3       +2 -2      jakarta-hivemind/framework/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-hivemind/framework/build.xml,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- build.xml	21 May 2004 18:46:55 -0000	1.2
  +++ build.xml	25 May 2004 22:31:41 -0000	1.3
  @@ -17,7 +17,7 @@
   -->
   <project name="HiveMind Framework" default="jar" basedir=".">
   
  -	<property name="jar.name" value="hivemind"/>
  +	<property name="module.name" value="hivemind"/>
   	<property name="javadoc.package" value="org.apache.hivemind.*"/>
   
   	<property name="root.dir" value=".."/>
  
  
  
  1.16      +26 -6     jakarta-hivemind/framework/src/test/hivemind/test/TestMisc.java
  
  Index: TestMisc.java
  ===================================================================
  RCS file: /home/cvs/jakarta-hivemind/framework/src/test/hivemind/test/TestMisc.java,v
  retrieving revision 1.15
  retrieving revision 1.16
  diff -u -r1.15 -r1.16
  --- TestMisc.java	19 May 2004 02:30:54 -0000	1.15
  +++ TestMisc.java	25 May 2004 22:31:41 -0000	1.16
  @@ -14,8 +14,6 @@
   
   package hivemind.test;
   
  -import hivemind.test.rules.MockModule;
  -
   import java.util.ArrayList;
   import java.util.Collections;
   import java.util.HashMap;
  @@ -26,12 +24,14 @@
   import org.apache.hivemind.SymbolSource;
   import org.apache.hivemind.impl.ContributionImpl;
   import org.apache.hivemind.impl.CreateClassServiceConstructor;
  +import org.apache.hivemind.impl.DefaultClassResolver;
   import org.apache.hivemind.impl.InvokeFactoryServiceConstructor;
   import org.apache.hivemind.impl.ModuleImpl;
   import org.apache.hivemind.impl.ServicePointImpl;
   import org.apache.hivemind.impl.SystemPropertiesSymbolSource;
   import org.apache.hivemind.internal.Module;
   import org.apache.hivemind.internal.ServicePoint;
  +import org.easymock.MockControl;
   
   /**
    * Additional tests to fill in minor code coverage gaps.
  @@ -112,7 +112,10 @@
   
       public void testCreateClassServiceConstructorAccessors()
       {
  -        Module m = new MockModule();
  +        MockControl control = MockControl.createStrictControl(Module.class);
  +        Module m = (Module) control.getMock();
  +
  +        control.replay();
   
           CreateClassServiceConstructor c = new CreateClassServiceConstructor();
   
  @@ -123,11 +126,19 @@
           c.setInstanceClassName("java.util.HashMap");
   
           assertEquals("java.util.HashMap", c.getInstanceClassName());
  +
  +        control.verify();
       }
   
       public void testCreateClassServiceConstructorTwice()
       {
  -        Module m = new MockModule();
  +        MockControl control = MockControl.createStrictControl(Module.class);
  +        Module m = (Module) control.getMock();
  +
  +		m.getClassResolver();
  +		control.setReturnValue(new DefaultClassResolver());
  +
  +        control.replay();
   
           CreateClassServiceConstructor c = new CreateClassServiceConstructor();
   
  @@ -142,11 +153,19 @@
   
           assertTrue(o1 instanceof HashMap);
           assertTrue(o2 instanceof HashMap);
  +
  +        control.verify();
       }
   
       public void testCreateClassServiceConstructorFailure()
       {
  -        Module m = new MockModule();
  +        MockControl control = MockControl.createStrictControl(Module.class);
  +        Module m = (Module) control.getMock();
  +
  +		m.getClassResolver();
  +		control.setReturnValue(new DefaultClassResolver());
  +
  +        control.replay();
   
           CreateClassServiceConstructor c = new CreateClassServiceConstructor();
   
  @@ -166,6 +185,7 @@
                   "Unable to find a constructor for class hivemind.test.PrivateBean.");
           }
   
  +        control.verify();
       }
   
       public void testSystemPropertiesSymbolSource()
  
  
  
  1.1                  jakarta-hivemind/src/documentation/conf/cli.xconf
  
  Index: cli.xconf
  ===================================================================
  <?xml version="1.0"?>
  
  <!--+
      |  This is the Apache Cocoon command line configuration file. 
      |  Here you give the command line interface details of where
      |  to find various aspects of your Cocoon installation.
      |
      |  If you wish, you can also use this file to specify the URIs
      |  that you wish to generate.
      |
      |  The current configuration information in this file is for
      |  building the Cocoon documentation. Therefore, all links here 
      |  are relative to the build context dir, which, in the build.xml 
      |  file, is set to ${build.context} 
      |
      |  Options:
      |    verbose:            increase amount of information presented
      |                        to standard output (default: false)
      |    follow-links:       whether linked pages should also be 
      |                        generated (default: true)
      |    precompile-only:    precompile sitemaps and XSP pages, but 
      |                        do not generate any pages (default: false)
      |    confirm-extensions: check the mime type for the generated page
      |                        and adjust filename and links extensions
      |                        to match the mime type 
      |                        (e.g. text/html->.html)
      |
      |  Note: Whilst using an xconf file to configure the Cocoon 
      |        Command Line gives access to more features, the use of 
      |        command line parameters is more stable, as there are 
      |        currently plans to improve the xconf format to allow 
      |        greater flexibility. If you require a stable and
      |        consistent method for accessing the CLI, it is recommended 
      |        that you use the command line parameters to configure 
      |        the CLI.</note>
      |
      | CVS: $Id: cli.xconf,v 1.1 2004/05/25 22:31:41 hlship Exp $
      +-->
      
  <cocoon verbose="true"  
          follow-links="true" 
          precompile-only="false" 
          confirm-extensions="false">
  
     <!--+
         |  The context directory is usually the webapp directory
         |  containing the sitemap.xmap file.
         |
         |  The config file is the cocoon.xconf file.
         |
         |  The work directory is used by Cocoon to store temporary
         |  files and cache files.
         |  
         |  The destination directory is where generated pages will
         |  be written (assuming the 'simple' mapper is used, see 
         |  below)
         +-->
     <context-dir>.</context-dir>
     <config-file>WEB-INF/cocoon.xconf</config-file>
     <work-dir>../tmp/cocoon-work</work-dir>
     <!-- Overridden in forrest.build.xml 
     <dest-dir>../docs</dest-dir>
     -->
  
     <!--+
         | Broken link reporting options:
         |   Report into a text file, one link per line:
         |     <broken-links type="text" report="filename"/>
         |   Report into an XML file:
         |     <broken-links type="xml" report="filename"/>
         |   Ignore broken links (default):
         |     <broken-links type="none"/>
         |
         |   Two attributes to this node specify whether a page should
         |   be generated when an error has occured. 'generate' specifies 
         |   whether a page should be generated (default: true) and
         |   extension specifies an extension that should be appended
         |   to the generated page's filename (default: none)
         |
         +-->
     <broken-links type="xml" 
                   file="../brokenlinks.xml"
                   generate="false"
                   extension=".error"/>
     
     <!--+
         |  Load classes at startup. This is necessary for generating
         |  from sites that use SQL databases and JDBC.
         |  The <load-class> element can be repeated if multiple classes
         |  are needed.
         +-->
     <!--
     <load-class>org.firebirdsql.jdbc.Driver</load-class>
     -->
  
     <!--+
         |
         +-->
     <!-- <logging log-kit="WEB-INF/logkit.xconf" logger="cli" level="ERROR" /> -->
  
     <!--+
         | Specifies the filename to be appended to URIs that
         | refer to a directory (i.e. end with a forward slash).
         +-->
     <default-filename>index.html</default-filename>
  
     <!--+
         |  Specifies a user agent string to the sitemap when
         |  generating the site.
         +-->
     <!--
     <user-agent>xxx</user-agent>
     -->
  
     <!--+
         |  Specifies an accept string to the sitemap when generating
         |  the site.
         +-->
     <accept>*/*</accept>
     
     <!--+
         |  Specifies the URIs that should be generated (using <uri>
         |  elements, and (if necessary) what should be done with the
         |  generated pages.
         |
         |  The old behaviour - appends uri to the specified destination
         |  directory (as specified in <dest-dir>):
         |
         |   <uri>documents/index.html</uri>
         |
         |  The "type" attribute specifies one of (append|replace|insert):
         |
         |  append:
         |  Append the generated page's URI to the end of the source URI:
         |
         |   <uri type="append" src-prefix="documents/" src="index.html"
         |   dest="build/dest/"/>
         |
         |  This means that 
         |   (1) the "documents/index.html" page is generated
         |   (2) the file will be written to "build/dest/documents/index.html"
         |
         |  replace:
         |  Completely ignore the generated page's URI - just 
         |  use the destination URI:
         |
         |   <uri type="replace" src-prefix="documents/" src="index.html" 
         |   dest="build/dest/docs.html"/>
         |  
         |  This means that 
         |   (1) the "documents/index.html" page is generated
         |   (2) the result is written to "build/dest/docs.html"
         |   (3) this works only for "single" pages - and not when links
         |       are followed
         |
         |  insert:
         |  Insert generated page's URI into the destination 
         |  URI at the point marked with a * (example uses fictional 
         |  zip protocol)
         |
         |   <uri type="insert" src-prefix="documents/" src="index.html" 
         |   dest="zip://*.zip/page.html"/>
         |
         |  This means that 
         |   (1)
         |
         |  In any of these scenarios, if the dest attribute is omitted,
         |  the value provided globally using the <dest-dir> node will 
         |  be used instead.
         +-->
  
     <!-- Includes and excludes can be used to limit which URLs are rendered -->
     <exclude pattern="**/"/>
     
     <!-- This is generated after the rest. -->
     
     <exclude pattern="hivedocs/**"/>
     <exclude pattern="hivemind/apidocs/**"/>
  
     <!-- Exclude tokens used in URLs to ASF mirrors (interpreted by a CGI) -->
     <exclude pattern="[preferred]/**"/>
     <exclude pattern="[location]"/>
     
     <uri src="favicon.ico"/>
  
     <!--+
         |  File containing URIs (plain text, one per line).
         +-->
     <!--
     <uri-file></uri-file>
     -->
     
  </cocoon>
  
  
  
  
  1.3       +24 -18    jakarta-hivemind/framework/src/test/hivemind/test/impl/TestRegistryAssemblyImpl.java
  
  Index: TestRegistryAssemblyImpl.java
  ===================================================================
  RCS file: /home/cvs/jakarta-hivemind/framework/src/test/hivemind/test/impl/TestRegistryAssemblyImpl.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- TestRegistryAssemblyImpl.java	28 Feb 2004 00:34:38 -0000	1.2
  +++ TestRegistryAssemblyImpl.java	25 May 2004 22:31:41 -0000	1.3
  @@ -14,17 +14,15 @@
   
   package hivemind.test.impl;
   
  -import java.util.List;
  -
   import org.apache.hivemind.Location;
   import org.apache.hivemind.Resource;
  -import org.apache.hivemind.impl.BaseLocatable;
   import org.apache.hivemind.impl.DefaultClassResolver;
   import org.apache.hivemind.impl.LocationImpl;
   import org.apache.hivemind.impl.RegistryAssemblyImpl;
   import org.apache.hivemind.schema.Schema;
   import org.apache.hivemind.test.HiveMindTestCase;
   import org.apache.hivemind.util.ClasspathResource;
  +import org.easymock.MockControl;
   
   /**
    * Suite of tests for {@link TestRegistryAssemblyImpl}.
  @@ -34,15 +32,6 @@
    */
   public class TestRegistryAssemblyImpl extends HiveMindTestCase
   {
  -    private static class MockSchema extends BaseLocatable implements Schema
  -    {
  -
  -        public List getElementModel()
  -        {
  -            return null;
  -        }
  -
  -    }
   
       private static class TestRunnable implements Runnable
       {
  @@ -57,18 +46,26 @@
       public void testAddSchema()
       {
           RegistryAssemblyImpl ra = new RegistryAssemblyImpl();
  -        Schema s = new MockSchema();
  +        MockControl control = MockControl.createStrictControl(Schema.class);
  +        Schema s = (Schema) control.getMock();
  +
  +        control.replay();
   
           ra.addSchema("foo.manchu", s);
   
           assertSame(s, ra.getSchema("foo.manchu"));
  +
  +        control.verify();
       }
   
       public void testAddDupeSchema() throws Exception
       {
           RegistryAssemblyImpl ra = new RegistryAssemblyImpl();
  -        MockSchema s1 = new MockSchema();
  -        MockSchema s2 = new MockSchema();
  +        MockControl c1 = MockControl.createStrictControl(Schema.class);
  +        MockControl c2 = MockControl.createStrictControl(Schema.class);
  +
  +        Schema s1 = (Schema) c1.getMock();
  +        Schema s2 = (Schema) c2.getMock();
   
           Resource r = new ClasspathResource(new DefaultClassResolver(), "/foo/bar");
           Location l1 = new LocationImpl(r, 20);
  @@ -76,8 +73,14 @@
   
           interceptLogging(ra.getClass().getName());
   
  -        s1.setLocation(l1);
  -        s2.setLocation(l2);
  +        s1.getLocation();
  +        c1.setReturnValue(l1);
  +
  +        s2.getLocation();
  +        c2.setReturnValue(l2);
  +
  +		c1.replay();
  +		c2.replay();
   
           ra.addSchema("foo.bar", s1);
           ra.addSchema("foo.bar", s2);
  @@ -85,6 +88,9 @@
           assertLoggedMessagePattern("Schema foo.bar \\(at classpath:/foo/bar, line 97\\) conflicts with existing schema at classpath:/foo/bar, line 20\\.");
   
           assertSame(s1, ra.getSchema("foo.bar"));
  +
  +        c1.verify();
  +        c2.verify();
       }
   
       public void testAddPostProcessor()
  
  
  
  1.1                  jakarta-hivemind/src/documentation/resources/images/InterceptorStack.png
  
  	<<Binary file>>
  
  
  1.1                  jakarta-hivemind/src/documentation/resources/images/AdderExample-ProjectLayout.png
  
  	<<Binary file>>
  
  
  1.1                  jakarta-hivemind/src/documentation/resources/images/jakarta-logo.gif
  
  	<<Binary file>>
  
  
  1.1                  jakarta-hivemind/src/documentation/resources/images/HiveMind-Logo.png
  
  	<<Binary file>>
  
  
  1.2       +2 -2      jakarta-hivemind/library/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-hivemind/library/build.xml,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- build.xml	20 May 2004 23:57:44 -0000	1.1
  +++ build.xml	25 May 2004 22:31:41 -0000	1.2
  @@ -17,7 +17,7 @@
   -->
   <project name="HiveMind Standard Library" default="jar">
   
  -	<property name="jar.name" value="hivemind-lib"/>
  +	<property name="module.name" value="hivemind-lib"/>
   	<property name="javadoc.package" value="org.apache.hivemind.lib.*"/>
   	
   	<property name="root.dir" value=".."/>
  
  
  
  1.1                  jakarta-hivemind/src/documentation/skinconf.xml
  
  Index: skinconf.xml
  ===================================================================
  <?xml version="1.0"?>
  <!--
     Copyright 2004 The Apache Software Foundation
    
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
    
         http://www.apache.org/licenses/LICENSE-2.0
    
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
  -->
  <!-- $Id: skinconf.xml,v 1.1 2004/05/25 22:31:41 hlship Exp $ -->
  <!--
  Skin configuration file. This file contains details of your project, which will
  be used to configure the chosen Forrest skin.
  -->
  
  <!DOCTYPE skinconfig [
  
    <!ENTITY % links.att 'name CDATA #REQUIRED'>
    <!ENTITY % link.att 'name CDATA #REQUIRED href CDATA #REQUIRED'>
    <!ELEMENT skinconfig (disable-search?, disable-print-link?, disable-pdf-link?,
    disable-xml-link?, disable-compliance-links?, searchsite-domain?, searchsite-name?,
    project-name, project-url, project-logo, group-name?, group-url?, group-logo?,
    host-url?, host-logo?, year?, vendor?, trail?, toc?, credits?)*>
    <!ELEMENT credits (credit*)>
    <!ELEMENT credit (name, url, image?, width?, height?)>
    <!-- id uniquely identifies the tool, and role indicates its function -->
    <!ATTLIST credit id   CDATA #IMPLIED
                     role CDATA #IMPLIED>
    <!ELEMENT disable-search (#PCDATA)>
    <!ELEMENT disable-print-link (#PCDATA)>
    <!ELEMENT disable-pdf-link (#PCDATA)>
    <!ELEMENT disable-xml-link (#PCDATA)>    
    <!ELEMENT disable-compliance-links (#PCDATA)>   
    <!ELEMENT searchsite-domain (#PCDATA)>
    <!ELEMENT searchsite-name (#PCDATA)>  
    <!ELEMENT project-name (#PCDATA)>
    <!ELEMENT project-url (#PCDATA)>
    <!ELEMENT project-logo (#PCDATA)>
    <!ELEMENT group-name (#PCDATA)>
    <!ELEMENT group-url (#PCDATA)>
    <!ELEMENT group-logo (#PCDATA)>
    <!ELEMENT host-url (#PCDATA)>
    <!ELEMENT host-logo (#PCDATA)>
    <!ELEMENT year (#PCDATA)>
    <!ELEMENT vendor (#PCDATA)>
    <!ELEMENT trail (link1, link2, link3)>
    <!ELEMENT link1 EMPTY>
    <!-- Seems we can't use param entity refs until this is DTDified -->
    <!ATTLIST link1 name CDATA #REQUIRED href CDATA #IMPLIED>
    <!ELEMENT link2 EMPTY>
    <!ATTLIST link2 name CDATA #REQUIRED href CDATA #IMPLIED>
    <!ELEMENT link3 EMPTY>
    <!ATTLIST link3 name CDATA #REQUIRED href CDATA #IMPLIED>
    <!ELEMENT name (#PCDATA)>
    <!ELEMENT url (#PCDATA)>
    <!ELEMENT image (#PCDATA)>
    <!ELEMENT width (#PCDATA)>
    <!ELEMENT height (#PCDATA)>
    <!ELEMENT toc EMPTY>
    <!ATTLIST toc level CDATA #IMPLIED>
    ]>
  
  <skinconfig>
    <!-- Do we want to disable the Google search box? -->
    <disable-search>true</disable-search>
    <!-- Do we want to disable the print link? -->
    <disable-print-link>true</disable-print-link>  
    <!-- Do we want to disable the PDF link? -->
    <disable-pdf-link>true</disable-pdf-link>
    <!-- Do we want to disable the xml source link? -->
    <disable-xml-link>true</disable-xml-link>
    <!-- Do we want to disable w3c compliance links? -->
    <disable-compliance-links>true</disable-compliance-links>
  
    <searchsite-domain>jakarta.apache.org</searchsite-domain>  
    <searchsite-name>Jakarta</searchsite-name>  
  
    <!-- mandatory project logo
         skin: forrest-site renders it at the top -->
    <project-name>Jakarta HiveMind Project</project-name>
    <project-url>http://jakarta.apache.org/hivemind/</project-url>
    <project-logo>images/HiveMind-Logo.png</project-logo>
    <!-- Alternative static image:
    <project-logo>images/project-logo.gif</project-logo> -->
  
    <!-- optional group logo
         skin: forrest-site renders it at the top-left corner -->
    <group-name>The Apache Jakarta Project</group-name>
    <group-url>http://jakarta.apache.org/</group-url>
    <group-logo>images/jakarta-logo.gif</group-logo>
    <!-- Alternative static image:
    <group-logo>images/group-logo.gif</group-logo> -->
  
    <!-- optional host logo (e.g. sourceforge logo)
         skin: forrest-site renders it at the bottom-left corner -->
    <host-url></host-url>
    <host-logo></host-logo>
  
    <!-- The following are used to construct a copyright statement -->
    <year>2004</year>
    <vendor>The Apache Software Foundation</vendor>
  
    <!-- Some skins use this to form a 'breadcrumb trail' of links. If you don't
    want these, set the attributes to blank. The DTD purposefully requires them.
    -->
    <trail>
      <link1 name="Apache" href="http://www.apache.org/"/>
      <link2 name="Jakarta" href="http://jakarta.apache.org/"/>
      <link3 name="HiveMind" href="http://jakarta.apache.org/hivemind/"/>
    </trail>
  
    <!-- Configure how many "section" levels need to be included in the
    generated Table of Contents (TOC). By default, if no toc element is provided
    below, then 2 levels are included. Level 0 does not generate any TOC at all.
    -->
    <toc level="3"/>
  
    <!-- Credits are typically rendered as a set of small clickable images in the
    page footer -->
    <credits>
      <credit>
        <name>Built with Apache Forrest</name>
        <url>http://xml.apache.org/forrest/</url>
        <image>images/built-with-forrest-button.png</image>
        <width>88</width>
        <height>31</height>
      </credit>
      <!-- A credit with @role='pdf' will have its name and url displayed in the
      PDF page's footer. -->
    </credits>
  
  </skinconfig>
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: hivemind-cvs-unsubscribe@jakarta.apache.org
For additional commands, e-mail: hivemind-cvs-help@jakarta.apache.org