vgritsenko    2002/07/18 07:16:41

  Modified:    src/documentation/xdocs/tutorial Tag: cocoon_2_0_3_branch
                        tutorial-rmi-generator.xml
  Log:
  fix CR/LF
  
  Revision  Changes    Path
  No                   revision
  
  
  No                   revision
  
  
  1.1.2.4   +1166 
-1166xml-cocoon2/src/documentation/xdocs/tutorial/tutorial-rmi-generator.xml
  
  Index: tutorial-rmi-generator.xml
  ===================================================================
  RCS file: 
/home/cvs/xml-cocoon2/src/documentation/xdocs/tutorial/tutorial-rmi-generator.xml,v
  retrieving revision 1.1.2.3
  retrieving revision 1.1.2.4
  diff -u -r1.1.2.3 -r1.1.2.4
  --- tutorial-rmi-generator.xml        7 Jul 2002 05:20:41 -0000       1.1.2.3
  +++ tutorial-rmi-generator.xml        18 Jul 2002 14:16:41 -0000      1.1.2.4
  @@ -1,1166 +1,1166 @@
  -<?xml version="1.0" encoding="UTF-8"?>
  -<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.0//EN" 
"../dtd/document-v10.dtd">
  -<document>
  -     <header>
  -             <title>Writing a Cocoon 2 generator</title>
  -             <version>0.1</version>
  -             <type>Tutorial</type>
  -             <authors>
  -                     <person email="[EMAIL PROTECTED]" name="Erwin Hermans"/>
  -             </authors>
  -     </header>
  -     
  -     <body>
  -             <s1 title="Preface">
  -                     <p>This document is written in context of the thesis of 
Carolien Coenen and Erwin Hermans at the department of Computer Science at the 
Katholieke Universiteit Leuven (<link 
href="http://www.cs.kuleuven.ac.be/cwis-cs/frames/index-E.shtml";>Department of 
Computer Science</link>).
  -                     </p>
  -                     
  -                     <p>At some point in our thesis the need for extending 
the functionality of Cocoon 2 became imminent. At that moment, we worked with 
an application that generated XML documents from Java source files (a sort of 
JavaDoc tool). We wrote some XSL stylesheets for transforming these documents 
into HTML, so they could easily be served by Cocoon 2. But <strong>every 
time</strong> the documentation comments changed in the Java source files, 
these XML documents <strong>required (re)generation</strong>. The ultimate goal 
of this project was to be able to do all the intermediate steps of the document 
generation in memory. This way the HTML documents would change whenever the 
Java source files were modified, <strong>without the need to regenerate the XML 
documents</strong>.
  -             </p>
  -             
  -             <p>To reach this goal, we made some modifications in our 
program. Our output documents were built in memory using the JDOM API (<link 
href="http://www.jdom.org";>JDOM.org</link>). At the time of writing this API 
supported 3 output methods, which are <strong>DOM</strong> (<link 
href="http://www.w3.org/TR/REC-DOM-Level-1/";>Document Object Model (DOM) Level 
1 Specification, Version 1.0, W3C Recommendation 1 October 1998</link>), 
<strong>SAX</strong> (<link href="http://www.saxproject.org/";>The official SAX 
website</link>) and <strong>XML</strong> (<link 
href="http://www.w3.org/TR/REC-xml";>Extensible Markup Language (XML) 1.0 
(Second Edition), W3C Recommendation 6 October 2000</link>). The DOM output 
method outputs a DOM tree from an existing JDOM tree. The XML output method 
outputs an XML file to an OutputStream or a Writer. But the most interesting of 
these three outputmethods is SAX. The generators that come with Cocoon 2 don't 
supply a method (or at least we haven't found any) to take the SAX output of an 
arbitrary Java program and feed it into the transformers (in our case). That's 
why we wanted to write a generator that would be able to supply SAX events that 
were outputted by our (or an arbitrary) Java program and to feed this SAX 
events into the transformer pipeline. This generator would be responsible for 
starting the Java program and delegating the received SAX events to the Cocoon 
2 transformation pipeline in some way.
  -             </p>
  -             
  -             <p>
  -To accomplish this task we decided to write our own generator and this 
documentation in parallel, as there was no documentation available, except for 
the following phrase on Apache's Cocoon 2 website (<link 
href="../index.html">Cocoon2 van The Apache XML project</link>): "A Generator 
generates XML content as SAX events and initializes the pipeline processing."   
  -Apache's Cocoon 2 website also enumerates the available Generators, which 
package contains them, which interfaces and helper classes there are in that 
package and which of those should be implemented/extended. But at that point, 
the documentation stopped. So the only way to actually be able to write a 
generator ourselves, was by studying the Cocoon 2 code (more specific the 
Generators) and figure out for ourselves how to write a generator. Because we 
want to make a contribution to the Cocoon 2 community, we have written this 
document. We think our experiences may come in handy for developers who are 
also thinking about extending Cocoon 2 with their own generators, .
  -             </p>
  -             
  -             <p>The writing of this generator and all the testing of our 
code were performed with the following configuration:</p>
  -                     <ul>
  -                             <li>Compaq/Digital Personal Workstation 500a 
(Alpha 500MHz processor)</li>
  -                             <li>Redhat Linux 7.1</li>
  -                             <li>Compaq JDK 1.3.1-1 (Classic VM (build 
1.3.1-1, native threads, jit))</li>
  -                             <li>Jakarta Tomcat 3.2.3</li>
  -                             <li>Cocoon 2.0.2-dev</li>
  -                     </ul>           
  -             
  -     </s1>
  -     <s1 title="Planning">
  -             <p>Here you'll find a list of consequent steps that we expect 
will be necessary to write our own Generator. It is of course possible that in 
this first draft of our planning we have forgotten a few steps or that some 
steps actually form one step.</p>
  -             <ul>
  -                     <li>Find out which classes should be extended and which 
interfaces implemented.</li>
  -                     <li>Examine these superclasses and interfaces and find 
which methods should be actually implemented and what is excepted from these 
methods.</li>
  -                     <li>Write a first Generator as a <strong>proof of 
concept</strong> to see if our conlusions in relation to the methods are 
correct, and let this Generator generate some SAX events (hard coded in the 
Generator) to 'feed' to Cocoon2.</li>
  -                     <li>Find out how to feed the SAXOutput of a JDOM 
document (hardcoded in the Generator) to Cocoon2.</li>
  -                     <li>Modify our program so it can generate SAXOutput 
when this is requested.</li>
  -                     <li>Feed the SAXOutput from our program to the 
Generator (we think it shall require running our program from the Generator). 
This SAXOutput shall then be passed to Cocoon 2. Again, every call to our 
program and the parameters will be hardcoded in our Generator.</li>
  -                     <li>Find out how we can use the sitemap to pass 
parameter values to our Generator (= examing how the sitemap is transformed 
into Java code and how we can read the parameter values from this code). This 
will no longer be hardcoded then.</li>
  -                     <li>Examine how we can define in the most general way 
the syntax from the sitemap, so that it is possible to define which class 
should be called to generate SAXOutput and with which values and methods this 
class should be called. This also implies that we must study if this is 
possible in Java.</li>
  -                     <li>This will be tested with our program and our 
Generator (hopefully we'll get this far) will become heavily parameterized.</li>
  -                     <li>Modify our program once again, so that it satisfies 
our final needs.</li>
  -                     <li>Submit our generator, and this document to the 
Cocoon 2 project.</li>
  -             </ul>
  -             
  -     </s1>
  -     
  -     <s1 title="Our planning, step by step">
  -             <s2 title="Classes and interfaces to be extended/implemented">
  -                     <p>In this section, we'll discuss which classes 
should/can be extended to implement our own Generator. Also, we'll take a 
closer look at these classes to see which functionality they provide and (try 
to) discuss their functionality. Let it be clear that it is <strong>not 
required</strong> to extend one of the existing (abstract) generator classes. 
But by doing so, it'll make your work a great deal easier.
  -                     </p>
  -                     
  -                     <p>The second part of this section will discuss the 
interface(s) that have to be implemented to write our own generator. We'll look 
at what the methods that are defined in the interface should do. There is one 
interface that has to be implemented if you write your own generator: the 
<strong>org.apache.cocoon.generation.Generator</strong> interface. 
  -                     </p>
  -                     
  -                     <s3 title="Classes">
  -                             <p>According to the Cocoon 2 website at the 
time of writing (21st november 2001) there are four helper classes in the 
org.apache.cocoon.generation package that can be extended. These four are (they 
will be discussed later):</p>
  -                                     <ul>
  -                                             <li>AbstractGenerator</li>
  -                                             <li>AbstractServerPage</li>
  -                                             <li>ComposerGenerator</li>
  -                                             <li>ServletGenerator</li>
  -                                     </ul>
  -  <p>                                        
  -Java only supports single inheritance, so you'll have to choose one of these 
for your Generator. We want to use the AbstractGenerator (in our first 
attempt), but to help the reader of this document in making a well motivated 
choice, we'll discuss each of these options briefly as to what specific 
functionality they provide.
  -                             </p>
  -                             
  -                             <p>There is a hierarchy between these classes, 
namely:</p>
  -                                     <ul>
  -                                             <li>AbstractGenerator</li>
  -                                             <li>ComposerGenerator extends 
AbstractGenerator</li>
  -                                             <li>ServletGenerator extends 
ComposerGenerator</li>
  -                                             <li>AbstractServerPage extends 
ServletGenerator</li>
  -                                     </ul>
  -                                     <p>So the choice of which class to 
extend will depends mostly on which is the level of abstraction required by 
your generator.
  -                             </p>
  -                             
  -                             <s4 title="AbstractGenerator">
  -                                     <p><strong>Extend this one for easier 
building of your own Generator</strong><br/>
  -                                     </p>
  -                             
  -                                     <p>This <strong>abstract class</strong> 
extends the class <strong>org.apache.cocoon.xml.AbstractXMLProducer</strong> 
and implements the interface 
<strong>org.apache.cocoon.generation.Generator</strong>.
  -                                     </p>
  -                                     
  -                                     <p>The <strong>Generator</strong> 
interface extends the interfaces 
<strong>org.apache.cocoon.xml.XMLProducer</strong> and 
<strong>org.apache.cocoon.sitemap.SitemapModelComponent</strong>.
  -                                     </p>
  -                                     
  -                                     <p>The interface 
<strong>XMLProducer</strong> is a top-level interface.
  -                                     </p>
  -                                     
  -                                     <p>The interface 
<strong>SitemapModelComponent</strong> extends the interface 
<strong>org.apache.avalon.framework.component.Component</strong>, which in turn 
is a top-level interface.
  -                                     </p>
  -                                     
  -                                     <p>The abstract class 
<strong>AbstractXMLProducer</strong> extends the abstract class 
<strong>org.apache.avalon.framework.logger.AbstractLoggable</strong> and 
implements the interfaces <strong>org.apache.cocoon.xml.XMLProducer</strong> 
and <strong>org.apache.avalon.excalibur.pool.Recyclable</strong>.
  -                                     </p>
  -                                     
  -                                     <p><strong>AbstractLoggable</strong> is 
a top-level abstract class to provide logging capabilities. This is deprecated 
and <strong>AbstractLogEnabled</strong> should be used instead. 
AbstractLoggable implements the interface 
<strong>org.apache.avalon.framework.logger.Loggable</strong>, but as mentioned 
in the API docs <strong>org.apache.avalon.framework.logger.LogEnabled</strong> 
should be used instead.
  -                                     </p>
  -                                     
  -                                     <p>The interface 
<strong>Recyclable</strong> extends the interface 
<strong>org.apache.avalon.excalibur.pool.Poolable</strong>, which is a 
top-level interface.
  -                                     </p>
  -                                     
  -                                     <p>The following methods are defined 
for <strong>AbstractGenerator</strong>, some of which already have an 
implementation:</p>
  -<ul>
  -     <li>From <strong>org.apache.avalon.excalibur.pool.Poolable</strong>:
  -             <ul>
  -                     <li>None</li>
  -             </ul>
  -             This interface is implemented bij components if it is 
reasonable to Pool the object. It marks the component Poolable.
  -     </li>
  -     
  -     <li>From <strong>org.apache.avalon.excalibur.pool.Recyclable</strong>:
  -             <ul>
  -                     <li><code>public void recycle()</code>: this method 
should be implemented to remove all costly resources in the object. These 
resources can be object references, database connections, threads, etc. What is 
categorised as "costly" resources is determined on a case by case analysis.
  -                     </li>
  -             </ul>
  -     </li>
  -     
  -     <li>From <strong>org.apache.avalon.framework.logger.Loggable</strong> 
(Deprecated):
  -             <ul>
  -                     <li><code>public void setLogger (org.apache.log.Logger 
logger)</code>: provide component with a logger.
  -                     </li>
  -             </ul>
  -             Interface can be implemented by Components that need to log.
  -     </li>
  -     
  -     <li>From 
<strong>org.apache.avalon.framework.logger.AbstractLoggable</strong> 
(Deprecated):
  -             <ul>
  -                     <li><code>protected org.apache.log.Logger 
getLogger()</code>: Helper method to allow sub-classes to acquire logger 
(implemented).
  -                     </li>
  -                     
  -                     <li><code>protected void setupLogger(java.lang.Object 
component)</code>: Helper method to setup other components with the same logger 
(implemented).
  -                     </li>
  -                     
  -                     <li><code>protected void setupLogger(java.lang.Object 
component, org.apache.log.Logger logger)</code>: Helper method to setup other 
components with logger (implemented).
  -                     </li>
  -                     
  -                     <li><code>protected void setupLogger(java.lang.Object 
component, java.lang.String subCategory)</code>: Helper method to setup other 
components with logger (implemented).
  -                     </li>
  -             </ul>
  -             This is a utility class to allow the construction of easy 
components that will perform logging.
  -     </li>
  -     
  -     <li>From <strong>org.apache.cocoon.xml.XMLProducer</strong>
  -             <ul>
  -                     <li><code>void setConsumer(XMLConsumer 
xmlconsumer)</code>: set the XMLConsumer that will receive XML data. The 
XMLConsumer interface extends <strong>org.xml.sax.ContentHandler</strong> and 
<strong>org.xml.sax.ext.LexicalHandler</strong>.
  -                     </li>
  -             </ul>
  -             This interface identifies classes that produce XML data, 
sending SAX events to the configured XMLConsumer.
  -     </li>
  -     
  -     <li>From <strong>org.apache.cocoon.xml.AbstractXMLProducer</strong>:
  -             <ul>
  -                     <li><code>void setConsumer(XMLConsumer 
xmlconsumer)</code>: set the XMLConsumer that will receive XML data 
(implemented).
  -                     </li>
  -                     
  -                     <li><code>public void setContentHandler(ContentHandler 
consumer)</code>: Set the ContentHandler that will receive XML data 
(implemented).
  -                     </li>
  -                     
  -                     <li><code>public void setLexicalHandler(LexicalHandler 
handler)</code>: Set the LexicalHandler that will receive XML data 
(implemented).
  -                     </li>
  -                     
  -                     <li><code>public void recycle()</code>: Recycle the 
producer by removing references (implemented).
  -                     </li>
  -             </ul>
  -     </li>
  -     
  -     <li>From <strong>org.apache.cocoon.generation.Generator</strong>:
  -             <ul>
  -                     <li><code>void generate()</code>: generate the SAX 
events to initialize a pipeline.
  -                     </li>
  -             </ul>
  -     </li>
  -     
  -     <li>From 
<strong>org.apache.cocoon.sitemap.SitemapModelComponent</strong>:
  -             <ul>
  -                     <li><code>void setup(SourceResolver resolver, Map 
objectmodel, String src, Parameters par)</code>: set the SourceResolver, 
objectmodel Map, the source and sitemap Parameters used to process the request.
  -                     </li>
  -             </ul>
  -     </li>
  -     
  -     <li>From 
<strong>org.apache.avalon.framework.component.Component</strong>:
  -             <ul>
  -                     <li>None</li>
  -             </ul>
  -             When a class implements this interface, it allows itself to be 
managed by a ComponentManager and by an outside element called a Composable. 
The Composable must know what type of Component it is accessing, so it will 
re-cast the Component into the type it needs.
  -     </li>
  -     
  -     <li>From <strong>AbstractGenerator</strong> itself:
  -             <ul>
  -                     <li><code>void setup(SourceResolver resolver, Map 
objectmodel, String src, Parameters par)</code>: set the SourceResolver, 
objectmodel Map, the source and sitemap Parameters used to process the request 
(implemented).
  -                     </li>
  -                     
  -                     <li><code>public void recycle()</code>: Recycle the 
generator by removing references (override implementation).
  -                     </li>
  -             </ul>
  -     </li>
  -</ul>
  -<p>
  -If we carefully analyse this list, we see that the only method left 
unimplemented is the <strong>generate()</strong> method. So if we extend the 
<strong>AbstractGenerator</strong> class to make our own generator, the only 
method we'll have to implement is the <strong>generate()</strong> method.
  -                                     </p>
  -                             
  -                                     <p>The following variables are defined 
in the different interfaces and classes:</p>
  -<ul>
  -     <li>From <strong>org.apache.avalon.excalibur.pool.Poolable</strong>:
  -             <ul>
  -                     <li>None</li>
  -             </ul>
  -     </li>
  -     
  -     <li>From <strong>org.apache.avalon.excalibur.pool.Recyclable</strong>:
  -             <ul>
  -                     <li>None</li>
  -             </ul>
  -     </li>
  -     
  -     <li>From 
<strong>org.apache.avalon.framework.logger.AbstractLoggable</strong> 
(Deprecated):
  -             <ul>
  -                     <li>None</li>
  -             </ul>
  -     </li>
  -     
  -     <li>From 
<strong>org.apache.avalon.framework.logger.AbstractLoggable</strong> 
(Deprecated):
  -             <ul>
  -                     <li><code>private org.apache.log.Logger 
m_logger</code>: the base logger instance. Provides component with a logger. 
The <strong>getLogger()</strong> method should be used to aquire the logger.
  -                     </li>
  -             </ul>
  -     </li>
  -     
  -     <li>From <strong>org.apache.cocoon.xml.XMLProducer</strong>
  -             <ul>
  -                     <li>None</li>
  -             </ul>
  -     </li>
  -     
  -     <li>From <strong>org.apache.cocoon.xml.AbstractXMLProducer</strong>:
  -             <ul>
  -                     <li><code>protected XMLConsumer xmlConsumer</code>: The 
XMLConsumer receiving SAX events. Can be accessed via 
<strong>super.xmlConsumer</strong>.
  -                     </li>
  -                     
  -                     <li><code>protected ContentHandler 
contentHandler</code>: The ContentHandler receiving SAX events. Can be accessed 
via <strong>super.ContentHandler</strong>. 
  -                     </li>
  -                     
  -                     <li><code>protected LexicalHandler 
lexicalHandler</code>: The LexicalHandler receiving SAX events. Can be accessed 
via <strong>super.LexicalHandler</strong>. 
  -                     </li>
  -             </ul>
  -             We here access these variables via the <strong>super.</strong> 
qualifier, this is only done for clarity. They can be equally well accessed via 
using the <strong>this.</strong> qualifier (or omitting this). For reasons of 
clarity, if we access such a variable in our code, we will use the 
<strong>super.</strong> qualifier, although when summing up all the variables 
we will use <strong>this.</strong>.
  -     </li>
  -     
  -     <li>From <strong>org.apache.cocoon.generation.Generator</strong>:
  -             <ul>
  -                     <li><code>String ROLE = 
"org.apache.cocoon.generation.Generator"</code>
  -                     </li>
  -             </ul>
  -     </li>
  -     
  -     <li>From 
<strong>org.apache.cocoon.sitemap.SitemapModelComponent</strong>:
  -             <ul>
  -                     <li>None</li>
  -             </ul>
  -     </li>
  -     
  -     <li>From 
<strong>org.apache.avalon.framework.component.Component</strong>:
  -             <ul>
  -                     <li>None</li>
  -             </ul>
  -     </li>
  -     
  -     <li>From <strong>AbstractGenerator</strong> itself:
  -             <ul>
  -                     <li><code>protected SourceResolver resolver = 
null</code>: The current SourceResolver. Can be accessed via 
<strong>this.resolver</strong>.
  -                     </li>
  -                     
  -                     <li><code>protected Map objectModel = null</code>: The 
current Map objectModel. Can be accessed via <strong>this.objectModel</strong>.
  -                     </li>
  -                     
  -                     <li><code>protected Parameters parameters = 
null</code>: The current Parameters. Can be accessed via 
<strong>this.parameters</strong>.
  -                     </li>
  -                     
  -                     <li><code>protected String source = null</code>: The 
source URI associated with the request or <strong>null</strong>. Can be 
accessed via <strong>this.source</strong>.
  -                     </li>
  -             </ul>
  -     </li>
  -</ul>
  -<p>
  -This gives us a list of variables that we can use throughout our own 
generator.
  -</p>
  -                             </s4>
  -                             
  -                             <s4 title="ComposerGenerator">
  -                                     <p><strong>Can be used as base class if 
you want your Generator to be an Avalon Composer</strong><br/>
  -                                     </p>
  -                                     
  -                                     <p>This <strong>abstract class</strong> 
extends <strong>org.apache.cocoon.generation.AbstractGenerator</strong> and 
extends the interfaces 
<strong>org.apache.avalon.framework.component.Composable</strong> and 
<strong>org.apache.avalon.framework.activity.Disposable</strong>.</p><p>In 
addition to all the methods introduced in the 
<strong>AbstractGenerator</strong> class, these two interfaces introduce som 
new methods:</p>
  -<ul>
  -     <li>From 
<strong>org.apache.avalon.framework.component.Composable</strong>:
  -             <ul>
  -                     <li><code>public void compose(ComponentManager 
componentManager)</code>: Pass the ComponentManager to the composer. The 
Composable implementation should use the specified ComponentManager to acquire 
the components it needs for execution.                                 
  -                     </li>
  -             </ul>
  -     </li>
  -     
  -     <li>From 
<strong>org.apache.avalon.framework.activity.Disposable</strong>:
  -             <ul>
  -                     <li><code>public void dispose()</code>: The dispose 
operation is called at the end of a components lifecycle. Components use this 
method to release and destroy any resources that the Component owns.
  -                     </li>
  -             </ul>
  -             The Disposable interface is used when components need to 
deallocate and dispose resources prior to their destruction.
  -     </li>
  -     
  -     <li>From <strong>ComposerGenerator</strong> itself:
  -             <ul>
  -                     <li><code>public void compose(ComponentManager 
componentManager)</code>: Pass the ComponentManager to the composer. The 
Composable implementation should use the specified ComponentManager to acquire 
the components it needs for execution. (implemented)
  -                     </li>
  -                     
  -                     <li><code>public void dispose()</code>: The dispose 
operation is called at the end of a components lifecycle. Components use this 
method to release and destroy any resources that the Component owns. 
(implemented - implementation sets the ComponentManager to null)
  -                     </li>
  -             </ul>
  -     </li>
  -</ul>
  -                                     
  -                                     
  -                                     <p>We see that this class provides a 
default implementation of the methods introduced by the two new interfaces. The 
only method that needs to be implemented remains the 
<strong>generate()</strong> method, if we are satisfied with the default 
implementations.</p>
  -                                     
  -                                     <p>Besides these methods, also a new 
variable is introduced:</p>
  -<ul>
  -     <li>From <strong>ComposerGenerator</strong> itself:
  -             <ul>
  -                     <li><code>protected ComponentManager manager = 
null</code>: the component manager instance, can be accessed via 
<strong>this.manager</strong>.
  -                     </li>
  -             </ul>
  -     </li>
  -</ul>
  -                             </s4>
  -                             
  -                             <s4 title="ServletGenerator">
  -                                     <p><strong>If you want to generate 
servlets. This is the base class for the ServerPagesGenerator</strong><br/>
  -                                     </p>
  -                                     
  -                                     <p>The 
<strong>ServletGenerator</strong> extends <strong>ComposerGenerator</strong>. 
  -                                     </p>
  -
  -                                     <p>We are not giving a more elaborate 
description of this component at this time. We have not experimented with this 
component and we would not like to risk making any wrong assumptions.
  -                                     </p>
  -                             </s4>
  -                             
  -                             <s4 title="AbstractServerPage">
  -                                     <p><strong>[FIXME: This seems to be 
intended as basis for the ServerPagesGenerator, but it seems to be obsolete 
now?]</strong><br/>
  -                                     </p>
  -
  -                                     <p>The 
<strong>AbstractServerPage</strong> extends <strong>ServletGenerator</strong> 
and implements the <strong>org.apache.cocoon.caching.Cacheable</strong> and 
<strong>org.apache.cocoon.components.language.generator.CompiledComponent</strong>
 interfaces.
  -                                     </p>
  -
  -                                     <p>We are not giving a more elaborate 
description of this component at this time. We have not experimented with this 
component and we would not like to risk making any wrong assumptions.
  -                                     </p>
  -                             </s4>
  -                     </s3>
  -                     
  -                     <s3 title="Interface(s)">
  -                             <p>Following the somewhat little pointers on 
develop-part of the Cocoon-website <link 
href="../developing/extending.html">Extending Apache Cocoon van The Apache XML 
Project</link>), we find that the only interface that should be implemented is 
the Generator interface in the <strong>org.apache.cocoon.generation</strong> 
package, so we will try to give an overview as complete as possible for now.
  -                             </p>
  -                             
  -                             <s4 title="Generator">
  -                                     <p>As mentioned earlier this public 
interface is situated in the <strong>org.apache.cocoon.generation</strong> 
package. This interface in its turn extends the 
<strong>org.apache.cocoon.xml.XMLProducer</strong> and the 
<strong>org.apache.cocoon.sitemap.SitemapModelComponent</strong> interfaces.
  -                                     </p>
  -                                     
  -                                     <p>The interface 
<strong>XMLProducer</strong> is a top-level interface.
  -                                     </p>
  -                                     
  -                                     <p>The interface 
<strong>SitemapModelComponent</strong> extends the interface 
<strong>org.apache.avalon.framework.component.Component</strong>, which in turn 
is a top-level interface.
  -                                     </p>
  -                                     
  -                                     <p>Analyzing these interfaces tells us 
that the following methods should be implemented when implementing the 
<strong>Generator</strong> interface:</p>
  -                                     
  -<ul>
  -     <li>From <strong>org.apache.cocoon.xml.XMLProducer</strong>
  -             <ul>
  -                     <li><code>void setConsumer(XMLConsumer 
xmlconsumer)</code>: set the XMLConsumer that will receive XML data. The 
XMLConsumer interface extends <strong>org.xml.sax.ContentHandler</strong> and 
<strong>org.xml.sax.ext.LexicalHandler</strong>.
  -                     </li>
  -             </ul>
  -             This interface identifies classes that produce XML data, 
sending SAX events to the configured XMLConsumer.
  -     </li>
  -     
  -     <li>From 
<strong>org.apache.cocoon.sitemap.SitemapModelComponent</strong>:
  -             <ul>
  -                     <li><code>void setup(SourceResolver resolver, Map 
objectmodel, String src, Parameters par)</code>: set the SourceResolver, 
objectmodel Map, the source and sitemap Parameters used to process the request.
  -                     </li>
  -             </ul>
  -     </li>
  -     
  -     <li>From 
<strong>org.apache.avalon.framework.component.Component</strong>:
  -             <ul>
  -                     <li>None</li>
  -             </ul>
  -             When a class implements this interface, it allows itself to be 
managed by a ComponentManager and by an outside element called a Composable. 
The Composable must know what type of Component it is accessing, so it will 
re-cast the Component into the type it needs.
  -     </li>
  -     
  -     <li>From <strong>org.apache.cocoon.generation.Generator</strong> itself:
  -             <ul>
  -                     <li><code>void generate()</code>: generate the SAX 
events to initialize a pipeline.
  -                     </li>
  -             </ul>
  -     </li>
  -</ul>
  -                                     
  -                             </s4>
  -                     </s3>
  -                     
  -                     <p>We decided that the easiest way of writing a custom 
generator was to extend the <strong>AbstractGenerator</strong> class. The only 
method required to implement was the <strong>generate</strong> method, for now, 
we would settle with the provided default implementations of the other methods.
  -                     </p>
  -                     
  -             </s2>
  -             
  -             <s2 title="Writing a test generator">
  -                     <p>After making these decisions, and looking at the 
implementations of the classes, we could begin the implementation keeping in 
mind the following:</p>
  -                             <ul>
  -                                     <li>We have to provide SAX events to 
the XMLConsumer, that is set via the <strong>setConsumer</strong> method.</li>
  -                                     <li>We can access the XMLConsumer via 
<strong>super.xmlConsumer</strong> (analysis of code of 
<strong>FileGenerator</strong> and definition of the 
<strong>xmlConsumer</strong> variable as <strong>protected</strong> in the 
<strong>AbstractXMLProducer</strong> class). The <strong>super.</strong> 
modifier is only used for clarity, since it can also be accessed via 
<strong>this.xmlConsumer.</strong></li>
  -                                     <li>We will extend the 
<strong>org.apache.cocoon.generation.AbstractGenerator</strong> 
class.</li><li>We have to implement the <strong>generate</strong> method which 
purpose is to produce SAX events and feed them to the XMLConsumer.</li>
  -                             </ul>
  -                     
  -                     
  -                     <s3 title="The code of our first generator">
  -                             <p>As a first test we decided to parse a string 
containing the following XML content and feed the SAX events to the 
XMLConsumer:</p>
  -
  -<source><![CDATA[
  -<doc>My first Cocoon 2 generator!</doc>
  -]]>  
  -</source>
  -                             <p>First, we will give our code and then we 
will explain what it does and why we made these choices.</p>
  -<source><![CDATA[
  -package test;
  -
  -import java.io.IOException;
  -import java.io.StringReader;
  -
  -import org.xml.sax.XMLReader;
  -import org.xml.sax.InputSource;
  -import org.xml.sax.SAXException;
  -import org.xml.sax.XMLReaderFactory;
  -
  -import org.apache.cocoon.ProcessingException;
  -import org.apache.cocoon.generation.AbstractGenerator;
  -
  -public class MyGenerator extends AbstractGenerator {
  -  public void generate () throws IOException, SAXException,
  -    ProcessingException {
  -      String message = "<doc>My first Cocoon 2 generator!</doc>";
  -      
  -      XMLReader xmlreader = XMLReaderFactory.createXMLReader();
  -      xmlreader.setContentHandler(super.xmlConsumer);
  -      InputSource source = new InputSource(new StringReader(message));
  -      xmlreader.parse(source);
  -  }
  -}
  -
  -]]>
  -</source>
  -                             <p>First of all, in our working directory (may 
be any directory) we made a directory "test" and in that directory we created 
the Java source file <strong>MyGenerator.java</strong>. We also decided to put 
this class in a package and named that package <strong>test</strong>. This can 
be easily changed afterwards.
  -                             </p>
  -                             
  -                             <p>The obvious <strong>import</strong> 
statements are those to import the <strong>AbstractGenerator</strong> class and 
those to import the exceptions thrown by the <strong>generate</strong> method. 
The other import statements serve to parsing our string and generating SAX 
events.
  -                             </p>
  -                             
  -                             <p>The code itself is pretty straightforward. 
We have our class definition containing one method definition. First of all, in 
the <strong>generate</strong> method, we define the variable 
<strong>message</strong> containing the XML content we want to generate SAX 
events for.
  -                             </p>
  -                             
  -<source><![CDATA[
  -XMLReader xmlreader = XMLReaderFactory.createXMLReader();
  -]]>
  -</source>
  -<p>
  -Here we make a new <strong>XMLReader</strong> via the 
<strong>XMLReaderFactory</strong>. Since XMLReader is an interface, the 
XMLReaderFactory has to provide us with a class that implements the XMLReader 
interface, commonly known as a <strong>SAXParser</strong>. Therefore the 
XMLReaderFactory uses the system variable <strong>org.xml.sax.driver</strong> 
to determine which class to instantiate to provide us with an XMLReader. An 
example of how this is done is provided after we have discussed the rest of the 
code.
  -                             </p>
  -                             
  -<source><![CDATA[
  -xmlreader.setContentHandler(super.xmlConsumer);
  -]]>
  -</source>
  -<p>
  -With this line of code, we tell the XMLReader which object will receive the 
SAX events that will be generated when parsing. You can see that we use 
<strong>super.xmlConsumer</strong> to receive the SAX events.
  -                             </p>
  -                             
  -<source><![CDATA[
  -InputSource source = new InputSource(new StringReader(message));
  -xmlreader.parse(source);
  -]]>
  -</source>
  -<p>
  -With the second line we tell the XMLReader to start parsing the source, 
provided as argument of the <strong>parse</strong> method. This parse method 
can only be supplied with an <strong>org.xml.sax.InputSource</strong> argument 
or a <strong>String</strong> that represents a system identifier or URI. To 
parse our string we must encapsulate it in an InputSource object. Since the 
InputSource class can not be passed an XML document that is contained in a 
string, we first must encapsulate our string into another object, which we then 
pass to an InputSource object. In this example we have chosen for a 
<strong>StringReader</strong>. A StringReader can be given as argument when 
constructing an InputSource object and a StringReader can be given a String 
object as argument for its construction. This way we succeed in parsing our 
string.
  -                             </p>
  -                             
  -                             <p>The next step is compiling our newly written 
class. We give here an overview of the our work environment and show how we 
compiled this Java-file. All the commands from this example were carried out on 
a PC running Linux, but with respect to a few minor modifications, these 
commands will also work on a PC running Windows. The commands were carried out 
in the directory "/home/erwin/cocoon2/generator/". This directory has three 
subdirectories:</p>
  -     <ul>
  -             <li>"test/": directory containing the source files
  -                     <ul>
  -                             <li><strong>MyGenerator.java</strong>: source 
file for our generator
  -                             </li>
  -                     </ul>
  -             </li>
  -             
  -             <li>"jar/": directory containing the necessary jar (Java 
Archive) files
  -                     <ul>
  -                             <li><strong>xerces.jar</strong>: Xerces has an 
implementation for the XMLReader interface which we use</li>
  -                             <li><strong>cocoon.jar</strong>: contains the 
classes from Cocoon 2.0.2-dev, needed to extend AbstractGenerator. This is in 
fact a symbolic link to 
$TOMCAT_HOME/webapps/cocoon/WEB-INF/lib/cocoon-2.0.2.jar. Under Windows you 
will have to copy this file or point directly to this file.</li>
  -                     </ul>
  -             </li>
  -             
  -             <li>"compiled/": outputdirectory for javac. The compiled files 
will end up in this directory
  -                     <ul>
  -                             <li><strong>test/MyGenerator.class</strong>: 
after compiling, we will have this file here</li>
  -                     </ul>
  -             </li>
  -     </ul>
  -<source><![CDATA[
  -javac -classpath .:jar/cocoon.jar:jar/xerces.jar \
  --d compiled test/MyGenerator.java
  -]]>
  -</source>
  -<p>
  -Now we have our compiled class, we can make the big step of putting it to 
work. To make sure there were no errors in our code, we tested our code by 
using another class as the ContentHandler of our generator. After these tests 
were completed (without errors), we tried to deploy our generator from within 
Cocoon 2. 
  -                             </p>
  -                     </s3>
  -                     
  -                     <s3 title="Deploying MyGenerator">
  -                             <p>The next step is deploying our custom 
written generator. First of all we stopped the Tomcat engine (and thus Cocoon 
2). We also emptied the <strong>work</strong> directory, located at 
"$TOMCAT_HOME/work/". Experience learned that this is something you have to do 
every time you want to try something like this with Cocoon 2.
  -                             </p>
  -                             
  -                             <p>For the next step, we changed the main 
sitemap to be able to use or generator in the following way:</p>
  -                                     <p>Under the 
<strong>map:generators</strong> element, we added the following:</p>
  -
  -<source><![CDATA[                                            
  -<map:generator name="mygenerator" src="test.MyGenerator"/>
  -]]>  
  -</source>
  -                                             
  -                                             <p>Under the 
<strong>map:pipelines</strong> element, we added the following:</p>
  -
  -<source><![CDATA[
  -<map:pipeline>
  -  <map:match pattern="mygenerator.xml">
  -    <map:generate type="mygenerator"/>
  -    <map:serialize type="xml"/>
  -  </map:match>
  -  <map:handle-errors>
  -    <map:transform src="stylesheets/system/error2html.xsl"/>
  -    <map:serialize status-code="500"/>
  -  </map:handle-errors>
  -</map:pipeline>
  -]]>
  -</source>
  -                                             <p>If the page 
<strong>mygenerator.xml</strong> is requested, we tell Cocoon 2 to use our 
generator, which we have named <strong>mygenerator</strong>. We do not define 
the <strong>src</strong> attribuut, since we do not use it in our generator. 
Once we get the content, we serialize it as xml, so we can check if the input 
matches the output. In the event that an error occurs, we use one of the 
stylesheets of Cocoon 2 or another pipeline to signal the error to the user.</p>
  -                             <p>After the changes to the sitemap, we added 
the directory "/home/erwin/cocoon2/generator/" to the classpath. After these 
changes, we restarted Tomcat and tried to access the page 
"http://localhost:8080/cocoon/mygenerator.xml";. After waiting a while, we 
received a fatal error. Inspection of the log-files (which is something you 
should always do when receiving an error that is not so clear) showed that the 
following exception was the cause of that fatal error:</p>
  -<source><![CDATA[
  -ERROR   (2002-03-27) 23:21.40:190   [sitemap] (/cocoon/)
  -Thread-23/Handler: Error compiling sitemap
  -java.lang.NoClassDefFoundError:
  -org/apache/cocoon/generation/AbstractGenerator
  -        at java.lang.ClassLoader.defineClass0(Native Method)
  -        at java.lang.ClassLoader.defineClass
  -(ClassLoader.java, Compiled Code)
  -        at java.security.SecureClassLoader.defineClass
  -(SecureClassLoader.java, Compiled Code)
  -        at java.net.URLClassLoader.defineClass
  -(URLClassLoader.java, Compiled Code)
  -        at java.net.URLClassLoader.access$100
  -(URLClassLoader.java, Compiled Code)
  -        at java.net.URLClassLoader$1.run
  -(URLClassLoader.java, Compiled Code)
  -        at java.security.AccessController.doPrivileged
  -(Native Method)
  -        at java.net.URLClassLoader.findClass
  -(URLClassLoader.java, Compiled Code)
  -        at java.lang.ClassLoader.loadClass
  -(ClassLoader.java, Compiled Code)
  -        at sun.misc.Launcher$AppClassLoader.loadClass
  -(Launcher.java, Compiled Code)
  -        at java.lang.ClassLoader.loadClass
  -(ClassLoader.java, Compiled Code)
  -        at org.apache.tomcat.loader.AdaptiveClassLoader.loadClass
  -
  -(AdaptiveClassLoader.java, Compiled Code)
  -        at java.lang.ClassLoader.loadClass
  -(ClassLoader.java, Compiled Code)
  -        at java.lang.ClassLoader.loadClass
  -(ClassLoader.java, Compiled Code)
  -        at org.apache.cocoon.util.ClassUtils.loadClass
  -
  -(ClassUtils.java, Compiled Code)
  -        at org.apache.cocoon.sitemap.AbstractSitemap.load_component
  -
  -(AbstractSitemap.java, Compiled Code)
  -        at org.apache.cocoon.www.sitemap_xmap
  -$Configurer.configGenerators(sitemap_xmap.java, Compiled Code)
  -        at org.apache.cocoon.www.sitemap_xmap.configure
  -(sitemap_xmap.java, Compiled Code)
  -        at org.apache.avalon.excalibur.component.
  -DefaultComponentFactory.newInstance
  -(DefaultComponentFactory.java, Compiled Code)
  -        at org.apache.avalon.excalibur.component.
  -ThreadSafeComponentHandler.initialize
  -(ThreadSafeComponentHandler.java, Compiled Code)
  -     at org.apache.cocoon.components.language.generator.
  -GeneratorSelector.addGenerator
  -(GeneratorSelector.java, Compiled Code)
  -        at org.apache.cocoon.components.language.generator.
  -ProgramGeneratorImpl.addCompiledComponent
  -(ProgramGeneratorImpl.java, Compiled Code)
  -        at org.apache.cocoon.components.language.generator.
  -ProgramGeneratorImpl.generateResource
  -(ProgramGeneratorImpl.java, Compiled Code)
  -        at org.apache.cocoon.components.language.generator.
  -ProgramGeneratorImpl.createResource
  -(ProgramGeneratorImpl.java, Compiled Code)
  -        at org.apache.cocoon.components.language.generator.
  -ProgramGeneratorImpl.load
  -(ProgramGeneratorImpl.java, Compiled Code)
  -        at org.apache.cocoon.sitemap.Handler.run
  -(Handler.java, Compiled Code)
  -        at java.lang.Thread.run(Thread.java:484)
  -]]>
  -</source>
  -                             
  -                             <p>Puzzled by this error, we mailed to the 
cocoon-users mailinglist ([email protected]) and explained our 
situation. The answer we received was to put our generator in the 
"$TOMCAT_HOME/webapps/cocoon/WEB-INF/classes/". We stopped Tomcat, emptied the 
work-directory, removed the directory "/home/erwin/cocoon2/generator/" from the 
classpath and made a directory "test/" under the 
"$TOMCAT_HOME/webapps/cocoon/WEB-INF/classes/" and placed 
<strong>MyGenerator.class</strong> in that directory. We then restarted Tomcat 
and once again tried to access "http://localhost:8080/cocoon/mygenerator.xml";. 
But after making that request in our browser, we got a message from the browser 
saying that the server could not be reached. Looking at the xterm from which we 
started Tomcat, we saw the following error:</p>
  -<source><![CDATA[
  -IGSEGV   11*  segmentation violation
  -si_signo [11]: SIGSEGV   11*  segmentation violation
  -si_errno [0]: Success
  -si_code [128]: unknown siginfo
  -sc_pc: 0x20010164f08, r26: 0x200001e19e0
  -              
  -thread pid: 26157
  -stackpointer=0x3fffc5f69b8
  -                        
  -Full thread dump Classic VM (1.3.1-1, native threads):
  -...
  -...
  -]]>
  -</source>
  -<p>
  -Removing our class (and commenting out our changes in the sitemap for 
safety) would resolve the problem, but then we can't use our generator.
  -                             </p>
  -                             
  -                             <p>Somewhere on the Web we had read a mail from 
someone who was also having <strong>NoClassDefFoundError</strong>s that he was 
able to solve by unzipping all the jar-files (a jar is basically a zip file 
containing the compiled classes) from 
"$TOMCAT_HOME/webapps/cocoon/WEB-INF/lib/" into the 
"$TOMCAT_HOME/webapps/cocoon/WEB-INF/classes/" directory. We stopped Tomcat, 
emptied the work-directory and started Tomcat again.
  -                             </p>
  -                             
  -                             <p>After restarting Tomcat we had our hopes up 
that this time it would work. We also started our browser and tried to access 
"http://localhost:8080/cocoon/mygenerator.xml";, again. After waiting a while 
(Cocoon 2 had to recompile its sitemap and some other components) we got the 
see our XML file. Cocoon 2 produced the following XML document:</p>
  -
  -<source><![CDATA[
  -<?xml version="1.0" encoding="UTF-8"?>
  -<doc>My first Cocoon 2 generator!</doc>
  -]]>
  -</source>
  -<p>
  -So, after a bit of struggling, we finally succeeded in deploying our own 
generator.
  -                             </p>
  -                     </s3>
  -                     
  -                     <s3 title="Considerations afterwards">
  -                             <p>After seeing our example and having some 
experience with Cocoon 2 one might ask why we reinvented the wheel by 
instantiating a parser and not using the one provided by Cocoon 2. It is 
evident that a start of a pipeline is a generator that fires SAX events, there 
must be a SAXParser available throughout Cocoon 2 that can be easily accessed. 
This is in fact the case. There are a number of reasons why we had not chosen 
that approach the first time around:</p>
  -                                     <ul>
  -                                             <li>Limited knowledge of the 
whole underlying architecture, not really enhanced by the documentation.</li>
  -                                             <li>We wanted to keep the 
time-to-test as short as possible, so we didn't spend time finding this 
information in the source in the first phase.</li>
  -                                             <li>We didn't see any other 
possibility of testing our code before we tried to integrate it with the Cocoon 
2 project.</li>
  -                                     </ul>
  -                             
  -                             
  -                             <p>We would still like to point the reader to 
an alternative solution, i.e. the solution that is used throughout Cocoon 2. We 
will give the code fragments here and we will then explain what it does.</p>
  -<source><![CDATA[
  -...
  -import org.apache.avalon.excalibur.xml.Parser;
  -...
  -Parser parser = null;
  -
  -  try {
  -    parser = (Parser)this.manager.lookup(Parser.ROLE);
  -    parser.parse(this.getInputSource(),handler);
  -  } catch (SAXException e) {
  -    // Preserve original exception
  -    throw e;
  -  } catch (Exception e) {
  -    throw new ProcessingException("Exception during processing of " +
  -    this.getSystemId(),e);
  -  } finally {
  -    if (parser != null) {
  -      this.manager.release(parser);
  -    }
  -  }
  -...
  -]]>
  -</source>
  -                             
  -                             <p>An extra <strong>import</strong> statement 
is added. The <strong>Parser</strong> interface of the Avalon/Excalibur project 
(<link 
href="http://jakarta.apache.org/avalon/excalibur/index.html";>Avalon/Excalibur 
van The Jakarta Project</link>) defines the following method:</p>
  -                                     <ul>
  -                                             <li><code>void 
parse(InputSource in, ContentHandler consumer)</code>: the implementation of 
this method should parse the <strong>InputSource</strong> and send the SAX 
events to the <strong>consumer</strong>. The consumer can be an 
<strong>XMLConsumer</strong> or an object that implements 
<strong>LexicalHandler</strong> as well.
  -                                             </li>
  -                                     </ul>
  -                                     <p>This interface defines a variable 
<strong>ROLE</strong> of the type String that is given the value 
<strong>org.apache.avalon.excalibur.xml.Parser</strong>. This variable is used 
to ask the <strong>ComponentManager</strong>, which is accessed by 
<strong>this.manager</strong>, to <strong>lookup</strong> a 
<strong>Component</strong> that has that role. The returned Component is then 
casted to a <strong>Parser</strong> type. We can then apply the parse method to 
any <strong>org.xml.sax.InputSource</strong> object and to an object that 
implements the <strong>ContentHandler</strong> interface. Finally, we have to 
tell the ComponentManager that we are finished using the parser. This allows 
the ComponentManager to handle the End-Of-Life Lifecycle events associated with 
this Component.
  -                             </p>
  -                             
  -                             <p><strong>NOTE:</strong> if you want to use 
this method to obtain a parser, it would be better to extend the 
<strong>ComposerGenerator</strong> class, instead of the 
<strong>AbstractGenerator</strong> class. The ComposerGenerator is defined to 
make use of a <strong>ComponentManager</strong>, while this is not the case for 
the <strong>AbstractGenerator</strong> class. You should envisage the given 
code as part of a class that extends the <strong>ComposerGenerator</strong> 
class or one of its children.
  -                             </p>
  -                     </s3>
  -             </s2>
  -             
  -             <s2 title="Going the distance">
  -                     <p>We have succeeded in implementing a first test to 
find out how everything works, but a generator that only sends a fixed string 
to Cocoon 2 is not that interesting. Since we have written an application that 
can serve XML documents contained in a String object (using JDOM (<link 
href="http://www.jdom.org";>JDOM.org</link>)), we want to be able to retrieve 
these documents through our browser, which sends this request to Cocoon 2. 
Cocoon 2 then fires up our generator to retrieve the requested XML document and 
can start the pipeline for processing that document.
  -                     </p>
  -                     
  -                     <p>Since we had experimented with Java RMI in one of 
our courses, we decided to try a setup where our generator was a client for the 
document server and the communication would happen via RMI. For this section, 
we will first look at setting up the server, next we will look at accessing the 
server from within MyGenerator and finally we will put it all together. If we 
get this to work, we then can ponder about looking up parameters defined in the 
sitemap to use in MyGenerator. We used (<link 
href="http://java.sun.com/products/jdk/1.2/docs/guide/rmi/getstart.doc.html";>Getting
 Started Using RMI</link>) as a basis for getting started with RMI. If you have 
never used RMI, we recommend that you read this document to develop a basic 
understanding of working with RMI.
  -                     </p>
  -                     
  -                     <s3 title="Setting up a RMI server">
  -                             <p>After reading the document (<link 
href="http://java.sun.com/products/jdk/1.2/docs/guide/rmi/getstart.doc.html";>Getting
 Started Using RMI</link>) and having deployed the example, we started writing 
our own interface, called <strong>Serverfunctions</strong> that defines the 
methods that should be implemented by a program that wishes to serve as a 
server for <strong>MyGenerator</strong>. This interface looks like this:</p>
  -<source><![CDATA[
  -package test;
  -
  -import java.rmi.Remote;
  -import java.rmi.RemoteException;
  -
  -public interface ServerFunctions extends Remote {
  -  /**
  -    This method returns a String, containing 
  -    a well-formed XML fragment/document. This String 
  -    contains information about the application implementing 
  -    this interface. Choosing what information is put 
  -    into this String is left to the application designer.
  -  */
  -  String sayHello () throws RemoteException;
  -
  -  /**
  -    This method returns a String, containing a well-formed XML
  -    fragment/document. To determine the information that should be
  -    returned, a systemId is passed to this method.
  -  */
  -  String getResource (String systemId) throws RemoteException;
  -}
  -
  -]]>
  -</source>
  -<p>
  -This interface defines two methods that should be implemented. Since these 
methods can be invoked via RMI we must declare that these methods can throw a 
RemoteException. These methods should return well-formed XML, as specified.
  -                             </p>
  -                             
  -                             <p>With interfaces alone we cannot build an 
application. We also must have a class that implements this interface. The 
following example demonstrates how this can be implemented. We used JDOM (<link 
href="http://www.jdom.org";>JDOM.org</link>) for reading in a XML document and 
converting it to a String.</p>
  -<source><![CDATA[
  -package test;
  -
  -import java.rmi.Naming;
  -import java.rmi.RemoteException;
  -import java.rmi.RMISecurityManager;
  -import java.rmi.server.UnicastRemoteObject;
  -
  -import org.jdom.Document;
  -import org.jdom.JDOMException;
  -import org.jdom.input.SAXBuilder;
  -import org.jdom.output.XMLOutputter;
  -
  -import test.ServerFunctions;
  -
  -public class Server extends UnicastRemoteObject implements
  -  ServerFunctions {
  -
  -  
  -  public Server () throws RemoteException {
  -    super();
  -  }
  -
  -  public String sayHello () {
  -    return "<doc>My First RMI Server!</doc>";
  -  }
  -
  -  public String getResource (String systemId) {
  -    try {
  -      SAXBuilder sb = new SAXBuilder();
  -      Document newdoc = sb.build(systemId);
  -      return (new XMLOutputter()).outputString(newdoc);
  -    } catch (JDOMException jde) {
  -      System.out.println("JDOM error: " + jde.getMessage());
  -      jde.printStackTrace();
  -      // Rethrow the exception so the other
  -      // side knows something is wrong
  -      throw new RemoteException("JDOMException while processing " +
  -        systemId,jde);
  -    }
  -  }
  -
  -  public void main (String args[]) {
  -    // Create and install a security manager
  -    // For testing purposes only, set this to null
  -    System.setSecurityManager(null);
  -    
  -    try {
  -      Server obj = new Server();
  -      // Bind this object instance to the name "MyServer"
  -      Naming.rebind("MyServer",obj);
  -      System.out.println("MyServer bound in registry");
  -    } catch (Exception e) {
  -      System.out.println("Server error: " + e.getMessage());
  -      e.printStackTrace();
  -  }
  -}
  -}
  -
  -
  -
  -]]>
  -</source>
  -                                                             
  -                             <p>We first have the necessary 
import-statements. This class implements the <strong>ServerFunctions</strong> 
interface we defined before. We also extend the 
<strong>UnicastRemoteObject</strong>. The Java API docs ((<link 
href="http://java.sun.com/j2se/1.3/docs/api/index.html";>Java 2 Platform, SE 
v1.3 API documentation</link>)) tell us the following about 
UnicastRemoteObject: "The UnicastRemoteObject class defines a non-replicated 
remote object whose references are valid only while the server process is 
alive. Objects that require remote behavior should extend RemoteObject, 
typically via UnicastRemoteObject." This allows us, by calling the constructor 
of this superclass, to use the behavior of the UnicastRemoteObject for our 
RMIServer. This is typically done by calling the <strong>super()</strong> 
constructor in the constructor of our class.
  -                             </p>
  -                             
  -                             <p>Next, we have the implementation of the two 
methods defined in our interface. The <strong>sayHello</strong> method just 
returns a string representing the following XML fragment:</p>
  -
  -<source><![CDATA[
  -<doc>My First RMI Server!
  -]]>  
  -</source>
  -                             <p>We then also implement the 
<strong>getResource</strong> method. In the body of the try-block we first 
build a JDOM Document using the given systemId. This means that an XML file, at 
the given location, is read and a JDOM Document object is created. Next, we use 
the method <strong>outputString(Document doc)</strong> of the 
<strong>XMLOutputter</strong> class to convert the JDOM Document to a string. 
It is this string that is returned to the client. In the event that there may 
be an error building the document, a <strong>JDOMException</strong> is thrown. 
If this is the case, we print the info to stdout and rethrow the exception, 
encapsulated in a RemoteException.
  -                             </p>
  -                             
  -                             <p>We then only need a <strong>main</strong> 
method to have a Java application at hand. The first thing we do is disabling 
the <strong>SecurityManager</strong>. For security reasons, this should only be 
done only for testing purposes on an isolated system and in production 
environments. We did this so we could bind this server in the rmiregistry 
without rewriting any Java policy files. Next, we make a new 
<strong>Server</strong> object and bind this in the rmiregistry, where it is 
associated with the name <strong>MyServer</strong>. We end with printing out a 
line that we have bound this object in the rmiregistry.
  -                             </p>
  -                     </s3>
  -                     
  -                     <s3 title="Setting up a RMI client">
  -                             <p>The next step in the process is to implement 
a Java application that can connect to our RMI server and invoke its methods. 
Once again, we will first give our code and then explain what it does.</p>
  -<source><![CDATA[
  -package test;
  -
  -import java.rmi.Naming;
  -import java.rmi.RemoteException;
  -
  -import test.ServerFunctions;
  -
  -public class Client {
  -
  -  public void main (String args[]) {
  -    String message = "blank";
  -
  -    
  -    try {
  -      // "obj" is the identifier that we'll use to refer
  -      // to the remote object that implements the
  -      // "ServerFunctions" interface
  -      ServerFunctions obj = 
  -      (ServerFunctions)Naming.lookup("//myhost.com/MyServer");
  -      
  -      message = obj.sayHello();
  -      System.out.println(message);
  -      
  -      message = obj.getResource("index.xml");
  -      System.out.println(message);
  -    } catch (Exception e) {
  -      System.out.println("Server exception: " + e.getMessage());
  -      e.printStackTrace();
  -    }
  -  }
  -}
  -
  -]]>
  -</source>
  -                             <p>Our client only defines a 
<strong>main</strong> method. We first initialize the variable, to which we 
will assign the return value of the <strong>sayHello</strong> method. Next, we 
try to <strong>lookup</strong> an object that is bound to 
"//myhost.com/MyServer" (note that myhost.com is a random chosen example). The 
lookup method returns an object, that is casted to the 
<strong>ServerFunctions</strong> type. We then invoke the sayHello method on 
the object and we print this message out. We also invoke the 
<strong>getResource</strong> method and print the result out. If this succeeds, 
we know everything works correctly. If an exception occurs, we print out the 
message from this exception plus its stack trace.
  -                             </p>
  -                     </s3>
  -                     
  -                     <s3 title="Testing the RMI components">
  -                             <p>We will first test if the RMI communication 
works. If it doesn't work there is no point in trying to integrate RMI 
communication in MyGenerator.Located in the directory 
"/home/erwin/cocoon2/generator/", which has the subdirectory "test/" containing 
our files, we execute the following commands:</p>
  -
  -<source><![CDATA[
  -javac -classpath .:jar/jdom.jar:jar/xerces.jar -d compiled/ test/*.java
  -rmic -classpath .:jar/jdom.jar:jar/xerces.jar -d compiled/ test.Server
  -rmiregistry &amp;
  -java -classpath compiled/:jar/jdom.jar:jar/xerces.jar \
  
--Djava.rmi.server.codebase=http://myhost.com/~erwin/cocoon2/generator/compiled/
 \
  -test.Server
  -MyServer bound in registry
  -]]>
  -</source>
  -<p>
  -If you forget to define the <strong>java.rmi.server.codebase</strong> system 
property or give it a wrong value, you are most likely to get the following 
exception:</p>
  -
  -<source><![CDATA[
  -HelloImpl err: RemoteException occurred in server thread; nested exception 
is:
  -        java.rmi.UnmarshalException: error unmarshalling arguments; 
  -nested exception is:
  -        java.lang.ClassNotFoundException: test.Server_Stub
  -java.rmi.ServerException: RemoteException occurred in server thread; 
  -nested exception is:
  -        java.rmi.UnmarshalException: error unmarshalling arguments; 
  -nested exception is:
  -        java.lang.ClassNotFoundException: test.Server_Stub
  -java.rmi.UnmarshalException: error unmarshalling arguments; 
  -nested exception is:
  -        java.lang.ClassNotFoundException: test.Server_Stub
  -java.lang.ClassNotFoundException: test.Server_Stub
  -        at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer
  -(StreamRemoteCall.java, Compiled Code)
  -        at sun.rmi.transport.StreamRemoteCall.executeCall
  -(StreamRemoteCall.java, Compiled Code)
  -        at sun.rmi.server.UnicastRef.invoke(UnicastRef.java, Compiled Code)
  -        at sun.rmi.registry.RegistryImpl_Stub.rebind(Unknown Source)
  -        at java.rmi.Naming.rebind(Naming.java, Compiled Code)
  -        at test.Server.main(Server.java, Compiled Code)
  -]]>
  -</source>
  -<p>
  -We now can start the client to test if everything works. Notice that the 
resource requested in the code is in fact a relative URI. It is relative to the 
path from where we started the server application. The file index.xml contains 
the following information:</p>
  -<source><![CDATA[
  -<?xml version="1.0"?>
  -<document>
  -  <title>This is a document</title>
  -  <para>This is the first paragraph.</para>
  -</document>
  -]]>
  -</source>
  -<p>
  -The client is started with the following command:
  -</p>
  -<source><![CDATA[
  -[erwin generator]$ java -classpath compiled/ test.Client
  -]]>
  -</source>
  -
  -<p>This resulted in the following output:</p>
  -
  -<source><![CDATA[
  -<doc>My First RMI Server!</doc>
  -<?xml version="1.0" encoding="UTF-8"?>
  -<document>
  -  <title>This is a document</title>
  -  <para>This is the first paragraph.</para>
  -</document>
  -]]>
  -</source>
  -<p>
  -This is exactly the output we expected, except for the encoding attribute. 
But this is something that is added by JDOM.
  -                             </p>
  -                             
  -                             <p><strong>NOTE:</strong> we would like to 
conclude this section with a final note about the RMI server application. If 
you wish to deploy an RMI server application in the real world, you may wish to 
delete the code that disables the SecurityManager. If no other settings are 
changed, you may get the following error when starting your server application 
(depending on the configuration in your <strong>java.policy</strong> file):</p>
  -                             
  -<source><![CDATA[
  -HelloImpl err: access denied 
  -(java.net.SocketPermission 127.0.0.1:1099 connect,resolve)
  -java.security.AccessControlException: access denied 
  -(java.net.SocketPermission 127.0.0.1:1099 connect,resolve)
  -        at java.security.AccessControlContext.checkPermission
  -(AccessControlContext.java, Compiled Code)
  -     ...
  -        at test.Server.main(Server.java, Compiled Code)      
  -]]>
  -</source>
  -
  -<p>
  -The most likely reason is that the default policy does not permit your 
server to bind its name in the rmiregistry. You have to change the security 
policy specified in the "$JAVA_HOME/jre/lib/security/java.policy" file. Since 
we are no experts in security we cannot give you any advice in this matter, but 
a general advice in security related matters is that you are better safe then 
sorry.
  -                             </p>
  -                     </s3>
  -                     
  -                     <s3 title="Putting the pieces together">
  -                             <p>We now have been able to setup a generator 
and use RMI communication, now it is time to integrate these two pieces so we 
have a fully blown RMIGenerator for Cocoon 2. But before we do that, we will 
look how we can access the parameters and source that are passed from the 
sitemap to MyGenerator.
  -                             </p>
  -                             
  -                             <p>We have seen that the method 
<strong>setup</strong> is implemented in the <strong>AbstractGenerator</strong> 
class. One of the arguments of this method is String src. The value of the 
<strong>src</strong> attribute in the sitemap is passed via this argument and 
the variable <strong>source</strong> will be assigned this value. If for 
instance the following is a small part of the sitemap:</p>
  -                             
  -<source><![CDATA[
  -<map:match pattern="mygenerator.xml">
  -  <map:generate type="mygenerator" src="example.xml"/>
  -  <map:serialize type="xml"/>
  -</map:match>
  -]]>
  -
  -</source>
  -<p>
  -If we request "$FULL_URL_PATH/mygenerator.xml", the value of the 
<strong>src</strong> attribute will be passed to <strong>MyGenerator</strong> 
using the setup method. This value, <strong>example.xml</strong> can then be 
accessed via the <strong>this.source</strong> variable in our code.
  -                             </p>
  -                             
  -                             <p>As for now, we still have hardcoded in 
MyGenerator to which RMI server our generator should connect and also which 
bindname should be looked up. This is not desirable, we wish to have a 
configurable generator. "Compile once, run many" is maybe the way you could 
describe this. We wish to pass these values as parameters to the generator. 
Clearly, these values should be specified in the sitemap. Amongst the elements 
allowed in the sitemap there is a <strong>parameter</strong> element. If we 
want to use this element to pass parameters to our generator this element has 
to appear as a child of the <strong>generate</strong> element. Our sitemap 
fragment will then look like this:</p>
  -                             
  -<source><![CDATA[
  -<map:match pattern="mygenerator.xml">
  -  <map:generate type="mygenerator" src="example.xml">
  -    <map:parameter name="host" value="myhost.com"/>
  -    <map:parameter name="port" value="1099"/>
  -    <map:parameter name="bindname" value="MyServer"/>
  -  </map:generate>
  -  <map:serialize type="xml"/>
  -</map:match>
  -]]>
  -
  -</source>
  -<p>
  -We define three parameters:</p>
  -
  -<ul>
  -     <li><strong>host</strong>: tells the generator at which host the RMI 
server application is running. <strong>REQUIRED</strong>.</li>
  -     <li><strong>port</strong>: tells the generator at which port at the 
remote host the rmiregistry process is running. If no value is specified Java 
uses the default port (1099). <strong>OPTIONAL</strong>.</li>
  -     <li><strong>bindname</strong>: tells the generator which name should be 
looked up in the remote registry to obtain access to the RMI server object. 
<strong>REQUIRED</strong>.</li>
  -</ul>
  -<p>
  -We only need these three parameters to define the remote server object. We 
do not need to specify which methods should be invoked since we demand that a 
remote server implements the <strong>ServerFunctions</strong> interface. This 
is something that may be considered in the future.
  -                             </p>
  -                             
  -                             <p>We now have defined the host, port and 
bindname parameters, but how can we access the value of these parameters in our 
code? The setup method has an argument Parameters par. It is via this argument 
that the parameters defined in the sitemap will be passed to the generator. 
This argument will be assigned to the <strong>parameters</strong> variable 
defined in AbstractGenerator. To obtain the value of each parameter we can 
invoke the following method on the parameters variable: <code>public 
java.lang.String getParameter(java.lang.String name)</code>. This method 
returns the value of the specified parameter, or throws an exception if there 
is no such parameter.
  -                             </p>
  -                             
  -                             <p>With all this in mind, we can finally build 
our configurable RMIGenerator. Also, this time we are going to extend the 
<strong>ComposerGenerator</strong> instead of the 
<strong>AbstractGenerator</strong> class. This way, we can make use of the 
<strong>ComponentManager</strong> to obtain a SAXParser. 
  -                             </p>
  -                             
  -                             <p>At this moment we decide that if there is no 
value given to the src attribute in the sitemap (<strong>source is 
null</strong>), we will invoke the <strong>sayHello</strong> method and 
otherwise the getResource with the appropriate parameter. When the value of the 
src attribute is the <strong>empty string</strong>, the 
<strong>getResource</strong> method is invoked, so this should be 
<strong>handled by the RMI server application</strong>.
  -After a little bit of thinking about how to code all this, we eventually 
wrote the following generator:</p>
  -
  -<source><![CDATA[
  -package test;
  -
  -// import the necessary classes from the java.io package
  -import java.io.IOException;
  -import java.io.StringReader;
  -
  -// import the necessary classes from the java.rmi package
  -import java.rmi.Naming;
  -import java.rmi.RemoteException;
  -import java.rmi.NotBoundException;
  -
  -// import the necessary SAX classes
  -import org.xml.sax.InputSource;
  -import org.xml.sax.SAXException;
  -
  -// import of the classes used from Cocoon 2
  -import org.apache.cocoon.ProcessingException;
  -import org.apache.cocoon.generation.ComposerGenerator;
  -
  -// Avalon Framework
  -import org.apache.avalon.framework.parameters.Parameters;
  -import org.apache.avalon.framework.parameters.ParameterException;
  -import org.apache.avalon.framework.component.ComponentException;
  -
  -// needed for obtaining parser in Cocoon
  -import org.apache.avalon.excalibur.xml.Parser;
  -
  -import test.ServerFunctions;
  -
  -public class MyGenerator extends ComposerGenerator {
  -  public void generate () throws IOException, SAXException,
  -    ProcessingException {
  -      String host;
  -
  -      // lookup parameter 'host'
  -      try {
  -        host = parameters.getParameter("host");
  -        // test if host is not the empty string
  -        if (host == "") {
  -          throw new ParameterException(
  -          "The parameter 'host' may not be the empty string");
  -        }
  -      } catch (ParameterException pe) {
  -        // rethrow as a ProcessingException
  -        throw new ProcessingException(
  -        "Parameter 'host' not specified",pe);
  -      }
  -
  -      String bindname;
  -      
  -      // lookup parameter 'bindname'
  -      try {
  -        bindname = parameters.getParameter("bindname");
  -        // test if bindname is not the empty string
  -        if (bindname == "") {
  -          throw new ParameterException(
  -          "The parameter 'bindname' may not be the empty string");
  -        }
  -      } catch (ParameterException pe) {
  -        // rethrow as a ProcessingException
  -        throw new ProcessingException(
  -        "Parameter 'bindname' not specified",pe);
  -      }
  -
  -      String port = "";
  -      
  -      // lookup parameter 'port'
  -      try {
  -        port = parameters.getParameter("port");
  -        port = ":" + port;
  -      } catch (ParameterException pe) {
  -        // reset port to the empty string
  -        // port is not required
  -        port = "";
  -      }
  -
  -      try {
  -        ServerFunctions obj = 
  -        (ServerFunctions)Naming.lookup("//" +
  -          host + port + "/" + bindname);
  -        String message = "";
  -     
  -        // determine the method to invoke
  -        // depending on value of source
  -     if (this.source == null) {
  -       message = obj.sayHello();
  -     } else {
  -       message = obj.getResource(source);
  -     }
  -
  -     Parser parser = null;
  -     parser = (Parser)this.manager.lookup(Parser.ROLE);
  -     
  -     InputSource inputSource = new InputSource(
  -     new StringReader(message));
  -     parser.parse(inputSource,super.xmlConsumer);
  -     
  -      } catch (NotBoundException nbe) {
  -     throw new ProcessingException("
  -     Error looking up the RMI server application",nbe);
  -      } catch (ComponentException ce) {
  -        throw new ProcessingException("
  -        Error obtaining a SAXParser",ce);
  -      }
  -    }
  -}
  -      
  -]]>
  -</source>                            
  -                             <p>Since we have already explained every step 
that happens in this generator, we are confident that everyone will understand 
the code. We are now ready to deploy this generator.
  -                             </p>
  -                     </s3>
  -                     
  -                     <s3 title="The final step: deployment">
  -                             <p>We can now compile our classes and put the 
generator, along with the ServerFunctions interface, in the right place. For 
compiling, we used the following command:</p>
  -<source><![CDATA[
  -javac -classpath .:jar/xerces.jar:jar/cocoon.jar:jar/framework.jar: \
  -jar/excalibur.jar:jar/exc-scratchpad.jar \
  --d compiled/ test/ServerFunctions.java test/MyGenerator.java
  -]]>
  -</source>
  -<p>
  -where xerces.jar is a symbolic link to 
"$TOMCAT_HOME/webapps/cocoon/WEB-INF/lib/xercesImpl-2.0.0.jar", framework.jar 
to "$TOMCAT_HOME/webapps/cocoon/WEB-INF/lib/avalon-framework-4.1.2.jar", 
excalibur.jar to 
"$TOMCAT_HOME/webapps/cocoon/WEB-INF/lib/avalon-excalibur-4.1.jar" and 
exc-scratchpad.jar to 
"$TOMCAT_HOME/webapps/cocoon/WEB-INF/lib/avalon-excalibur-scratchpad-20020212.jar".
 This is valid for Cocoon 2.0.2-dev. If you use another version of Cocoon 2, 
you might have to change some of these names.
  -If your platform does not allow the use of symbolic links, you should use 
the complete path to the corresponding jar-files.
  -                             </p>
  -                             
  -                             <p>Now that these classes are compiled we can 
place them in the $TOMCAT_HOME/webapps/cocoon/WEB-INF/classes/" directory as 
before. Now all that is left is shutting down Tomcat/Cocoon, emptying the work 
directory, modifying the sitemap, setting up a RMI server application and 
starting Tomcat/Cocoon.
  -                             </p>
  -                     </s3>
  -             </s2>
  -     </s1>
  -     
  -     <s1 title="Future plans">
  -             <p>The first version of this generator was written as a 
proof-of-concept. The latest version (as given here, extending the 
ComposerGenerator) only foresees in the <strong>generate</strong> method. There 
are a number of plans we still have to extend the functionality and thus 
usability of this generator:</p>
  -                     <ul>
  -                             <li>allow passing of a (J)DOM document instance 
as a resource to our generator. JDOM does require an additional entry in the 
classpath.</li>
  -                             <li>supply a possibility for caching 
documents</li>
  -                             <li>if the RMI server application can generate 
SAX events, try to pass the xmlConsumer to the server application as the 
ContentHandler</li>
  -                     </ul>
  -             
  -             <p>These are some of the extensions we have in mind for this 
generator. Our goal is to complete these steps within a few weeks (it will 
probably be a bit longer since the deadline of our thesis is only three weeks a 
way at the time of writing).
  -             </p>
  -     </s1>
  -</body>
  -</document>
  +<?xml version="1.0" encoding="UTF-8"?>
  +<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.0//EN" 
"../dtd/document-v10.dtd">
  +<document>
  +     <header>
  +             <title>Writing a Cocoon 2 generator</title>
  +             <version>0.1</version>
  +             <type>Tutorial</type>
  +             <authors>
  +                     <person email="[EMAIL PROTECTED]" name="Erwin Hermans"/>
  +             </authors>
  +     </header>
  +     
  +     <body>
  +             <s1 title="Preface">
  +                     <p>This document is written in context of the thesis of 
Carolien Coenen and Erwin Hermans at the department of Computer Science at the 
Katholieke Universiteit Leuven (<link 
href="http://www.cs.kuleuven.ac.be/cwis-cs/frames/index-E.shtml";>Department of 
Computer Science</link>).
  +                     </p>
  +                     
  +                     <p>At some point in our thesis the need for extending 
the functionality of Cocoon 2 became imminent. At that moment, we worked with 
an application that generated XML documents from Java source files (a sort of 
JavaDoc tool). We wrote some XSL stylesheets for transforming these documents 
into HTML, so they could easily be served by Cocoon 2. But <strong>every 
time</strong> the documentation comments changed in the Java source files, 
these XML documents <strong>required (re)generation</strong>. The ultimate goal 
of this project was to be able to do all the intermediate steps of the document 
generation in memory. This way the HTML documents would change whenever the 
Java source files were modified, <strong>without the need to regenerate the XML 
documents</strong>.
  +             </p>
  +             
  +             <p>To reach this goal, we made some modifications in our 
program. Our output documents were built in memory using the JDOM API (<link 
href="http://www.jdom.org";>JDOM.org</link>). At the time of writing this API 
supported 3 output methods, which are <strong>DOM</strong> (<link 
href="http://www.w3.org/TR/REC-DOM-Level-1/";>Document Object Model (DOM) Level 
1 Specification, Version 1.0, W3C Recommendation 1 October 1998</link>), 
<strong>SAX</strong> (<link href="http://www.saxproject.org/";>The official SAX 
website</link>) and <strong>XML</strong> (<link 
href="http://www.w3.org/TR/REC-xml";>Extensible Markup Language (XML) 1.0 
(Second Edition), W3C Recommendation 6 October 2000</link>). The DOM output 
method outputs a DOM tree from an existing JDOM tree. The XML output method 
outputs an XML file to an OutputStream or a Writer. But the most interesting of 
these three outputmethods is SAX. The generators that come with Cocoon 2 don't 
supply a method (or at least we haven't found any) to take the SAX output of an 
arbitrary Java program and feed it into the transformers (in our case). That's 
why we wanted to write a generator that would be able to supply SAX events that 
were outputted by our (or an arbitrary) Java program and to feed this SAX 
events into the transformer pipeline. This generator would be responsible for 
starting the Java program and delegating the received SAX events to the Cocoon 
2 transformation pipeline in some way.
  +             </p>
  +             
  +             <p>
  +To accomplish this task we decided to write our own generator and this 
documentation in parallel, as there was no documentation available, except for 
the following phrase on Apache's Cocoon 2 website (<link 
href="../index.html">Cocoon2 van The Apache XML project</link>): "A Generator 
generates XML content as SAX events and initializes the pipeline processing."   
  +Apache's Cocoon 2 website also enumerates the available Generators, which 
package contains them, which interfaces and helper classes there are in that 
package and which of those should be implemented/extended. But at that point, 
the documentation stopped. So the only way to actually be able to write a 
generator ourselves, was by studying the Cocoon 2 code (more specific the 
Generators) and figure out for ourselves how to write a generator. Because we 
want to make a contribution to the Cocoon 2 community, we have written this 
document. We think our experiences may come in handy for developers who are 
also thinking about extending Cocoon 2 with their own generators, .
  +             </p>
  +             
  +             <p>The writing of this generator and all the testing of our 
code were performed with the following configuration:</p>
  +                     <ul>
  +                             <li>Compaq/Digital Personal Workstation 500a 
(Alpha 500MHz processor)</li>
  +                             <li>Redhat Linux 7.1</li>
  +                             <li>Compaq JDK 1.3.1-1 (Classic VM (build 
1.3.1-1, native threads, jit))</li>
  +                             <li>Jakarta Tomcat 3.2.3</li>
  +                             <li>Cocoon 2.0.2-dev</li>
  +                     </ul>           
  +             
  +     </s1>
  +     <s1 title="Planning">
  +             <p>Here you'll find a list of consequent steps that we expect 
will be necessary to write our own Generator. It is of course possible that in 
this first draft of our planning we have forgotten a few steps or that some 
steps actually form one step.</p>
  +             <ul>
  +                     <li>Find out which classes should be extended and which 
interfaces implemented.</li>
  +                     <li>Examine these superclasses and interfaces and find 
which methods should be actually implemented and what is excepted from these 
methods.</li>
  +                     <li>Write a first Generator as a <strong>proof of 
concept</strong> to see if our conlusions in relation to the methods are 
correct, and let this Generator generate some SAX events (hard coded in the 
Generator) to 'feed' to Cocoon2.</li>
  +                     <li>Find out how to feed the SAXOutput of a JDOM 
document (hardcoded in the Generator) to Cocoon2.</li>
  +                     <li>Modify our program so it can generate SAXOutput 
when this is requested.</li>
  +                     <li>Feed the SAXOutput from our program to the 
Generator (we think it shall require running our program from the Generator). 
This SAXOutput shall then be passed to Cocoon 2. Again, every call to our 
program and the parameters will be hardcoded in our Generator.</li>
  +                     <li>Find out how we can use the sitemap to pass 
parameter values to our Generator (= examing how the sitemap is transformed 
into Java code and how we can read the parameter values from this code). This 
will no longer be hardcoded then.</li>
  +                     <li>Examine how we can define in the most general way 
the syntax from the sitemap, so that it is possible to define which class 
should be called to generate SAXOutput and with which values and methods this 
class should be called. This also implies that we must study if this is 
possible in Java.</li>
  +                     <li>This will be tested with our program and our 
Generator (hopefully we'll get this far) will become heavily parameterized.</li>
  +                     <li>Modify our program once again, so that it satisfies 
our final needs.</li>
  +                     <li>Submit our generator, and this document to the 
Cocoon 2 project.</li>
  +             </ul>
  +             
  +     </s1>
  +     
  +     <s1 title="Our planning, step by step">
  +             <s2 title="Classes and interfaces to be extended/implemented">
  +                     <p>In this section, we'll discuss which classes 
should/can be extended to implement our own Generator. Also, we'll take a 
closer look at these classes to see which functionality they provide and (try 
to) discuss their functionality. Let it be clear that it is <strong>not 
required</strong> to extend one of the existing (abstract) generator classes. 
But by doing so, it'll make your work a great deal easier.
  +                     </p>
  +                     
  +                     <p>The second part of this section will discuss the 
interface(s) that have to be implemented to write our own generator. We'll look 
at what the methods that are defined in the interface should do. There is one 
interface that has to be implemented if you write your own generator: the 
<strong>org.apache.cocoon.generation.Generator</strong> interface. 
  +                     </p>
  +                     
  +                     <s3 title="Classes">
  +                             <p>According to the Cocoon 2 website at the 
time of writing (21st november 2001) there are four helper classes in the 
org.apache.cocoon.generation package that can be extended. These four are (they 
will be discussed later):</p>
  +                                     <ul>
  +                                             <li>AbstractGenerator</li>
  +                                             <li>AbstractServerPage</li>
  +                                             <li>ComposerGenerator</li>
  +                                             <li>ServletGenerator</li>
  +                                     </ul>
  +  <p>                                        
  +Java only supports single inheritance, so you'll have to choose one of these 
for your Generator. We want to use the AbstractGenerator (in our first 
attempt), but to help the reader of this document in making a well motivated 
choice, we'll discuss each of these options briefly as to what specific 
functionality they provide.
  +                             </p>
  +                             
  +                             <p>There is a hierarchy between these classes, 
namely:</p>
  +                                     <ul>
  +                                             <li>AbstractGenerator</li>
  +                                             <li>ComposerGenerator extends 
AbstractGenerator</li>
  +                                             <li>ServletGenerator extends 
ComposerGenerator</li>
  +                                             <li>AbstractServerPage extends 
ServletGenerator</li>
  +                                     </ul>
  +                                     <p>So the choice of which class to 
extend will depends mostly on which is the level of abstraction required by 
your generator.
  +                             </p>
  +                             
  +                             <s4 title="AbstractGenerator">
  +                                     <p><strong>Extend this one for easier 
building of your own Generator</strong><br/>
  +                                     </p>
  +                             
  +                                     <p>This <strong>abstract class</strong> 
extends the class <strong>org.apache.cocoon.xml.AbstractXMLProducer</strong> 
and implements the interface 
<strong>org.apache.cocoon.generation.Generator</strong>.
  +                                     </p>
  +                                     
  +                                     <p>The <strong>Generator</strong> 
interface extends the interfaces 
<strong>org.apache.cocoon.xml.XMLProducer</strong> and 
<strong>org.apache.cocoon.sitemap.SitemapModelComponent</strong>.
  +                                     </p>
  +                                     
  +                                     <p>The interface 
<strong>XMLProducer</strong> is a top-level interface.
  +                                     </p>
  +                                     
  +                                     <p>The interface 
<strong>SitemapModelComponent</strong> extends the interface 
<strong>org.apache.avalon.framework.component.Component</strong>, which in turn 
is a top-level interface.
  +                                     </p>
  +                                     
  +                                     <p>The abstract class 
<strong>AbstractXMLProducer</strong> extends the abstract class 
<strong>org.apache.avalon.framework.logger.AbstractLoggable</strong> and 
implements the interfaces <strong>org.apache.cocoon.xml.XMLProducer</strong> 
and <strong>org.apache.avalon.excalibur.pool.Recyclable</strong>.
  +                                     </p>
  +                                     
  +                                     <p><strong>AbstractLoggable</strong> is 
a top-level abstract class to provide logging capabilities. This is deprecated 
and <strong>AbstractLogEnabled</strong> should be used instead. 
AbstractLoggable implements the interface 
<strong>org.apache.avalon.framework.logger.Loggable</strong>, but as mentioned 
in the API docs <strong>org.apache.avalon.framework.logger.LogEnabled</strong> 
should be used instead.
  +                                     </p>
  +                                     
  +                                     <p>The interface 
<strong>Recyclable</strong> extends the interface 
<strong>org.apache.avalon.excalibur.pool.Poolable</strong>, which is a 
top-level interface.
  +                                     </p>
  +                                     
  +                                     <p>The following methods are defined 
for <strong>AbstractGenerator</strong>, some of which already have an 
implementation:</p>
  +<ul>
  +     <li>From <strong>org.apache.avalon.excalibur.pool.Poolable</strong>:
  +             <ul>
  +                     <li>None</li>
  +             </ul>
  +             This interface is implemented bij components if it is 
reasonable to Pool the object. It marks the component Poolable.
  +     </li>
  +     
  +     <li>From <strong>org.apache.avalon.excalibur.pool.Recyclable</strong>:
  +             <ul>
  +                     <li><code>public void recycle()</code>: this method 
should be implemented to remove all costly resources in the object. These 
resources can be object references, database connections, threads, etc. What is 
categorised as "costly" resources is determined on a case by case analysis.
  +                     </li>
  +             </ul>
  +     </li>
  +     
  +     <li>From <strong>org.apache.avalon.framework.logger.Loggable</strong> 
(Deprecated):
  +             <ul>
  +                     <li><code>public void setLogger (org.apache.log.Logger 
logger)</code>: provide component with a logger.
  +                     </li>
  +             </ul>
  +             Interface can be implemented by Components that need to log.
  +     </li>
  +     
  +     <li>From 
<strong>org.apache.avalon.framework.logger.AbstractLoggable</strong> 
(Deprecated):
  +             <ul>
  +                     <li><code>protected org.apache.log.Logger 
getLogger()</code>: Helper method to allow sub-classes to acquire logger 
(implemented).
  +                     </li>
  +                     
  +                     <li><code>protected void setupLogger(java.lang.Object 
component)</code>: Helper method to setup other components with the same logger 
(implemented).
  +                     </li>
  +                     
  +                     <li><code>protected void setupLogger(java.lang.Object 
component, org.apache.log.Logger logger)</code>: Helper method to setup other 
components with logger (implemented).
  +                     </li>
  +                     
  +                     <li><code>protected void setupLogger(java.lang.Object 
component, java.lang.String subCategory)</code>: Helper method to setup other 
components with logger (implemented).
  +                     </li>
  +             </ul>
  +             This is a utility class to allow the construction of easy 
components that will perform logging.
  +     </li>
  +     
  +     <li>From <strong>org.apache.cocoon.xml.XMLProducer</strong>
  +             <ul>
  +                     <li><code>void setConsumer(XMLConsumer 
xmlconsumer)</code>: set the XMLConsumer that will receive XML data. The 
XMLConsumer interface extends <strong>org.xml.sax.ContentHandler</strong> and 
<strong>org.xml.sax.ext.LexicalHandler</strong>.
  +                     </li>
  +             </ul>
  +             This interface identifies classes that produce XML data, 
sending SAX events to the configured XMLConsumer.
  +     </li>
  +     
  +     <li>From <strong>org.apache.cocoon.xml.AbstractXMLProducer</strong>:
  +             <ul>
  +                     <li><code>void setConsumer(XMLConsumer 
xmlconsumer)</code>: set the XMLConsumer that will receive XML data 
(implemented).
  +                     </li>
  +                     
  +                     <li><code>public void setContentHandler(ContentHandler 
consumer)</code>: Set the ContentHandler that will receive XML data 
(implemented).
  +                     </li>
  +                     
  +                     <li><code>public void setLexicalHandler(LexicalHandler 
handler)</code>: Set the LexicalHandler that will receive XML data 
(implemented).
  +                     </li>
  +                     
  +                     <li><code>public void recycle()</code>: Recycle the 
producer by removing references (implemented).
  +                     </li>
  +             </ul>
  +     </li>
  +     
  +     <li>From <strong>org.apache.cocoon.generation.Generator</strong>:
  +             <ul>
  +                     <li><code>void generate()</code>: generate the SAX 
events to initialize a pipeline.
  +                     </li>
  +             </ul>
  +     </li>
  +     
  +     <li>From 
<strong>org.apache.cocoon.sitemap.SitemapModelComponent</strong>:
  +             <ul>
  +                     <li><code>void setup(SourceResolver resolver, Map 
objectmodel, String src, Parameters par)</code>: set the SourceResolver, 
objectmodel Map, the source and sitemap Parameters used to process the request.
  +                     </li>
  +             </ul>
  +     </li>
  +     
  +     <li>From 
<strong>org.apache.avalon.framework.component.Component</strong>:
  +             <ul>
  +                     <li>None</li>
  +             </ul>
  +             When a class implements this interface, it allows itself to be 
managed by a ComponentManager and by an outside element called a Composable. 
The Composable must know what type of Component it is accessing, so it will 
re-cast the Component into the type it needs.
  +     </li>
  +     
  +     <li>From <strong>AbstractGenerator</strong> itself:
  +             <ul>
  +                     <li><code>void setup(SourceResolver resolver, Map 
objectmodel, String src, Parameters par)</code>: set the SourceResolver, 
objectmodel Map, the source and sitemap Parameters used to process the request 
(implemented).
  +                     </li>
  +                     
  +                     <li><code>public void recycle()</code>: Recycle the 
generator by removing references (override implementation).
  +                     </li>
  +             </ul>
  +     </li>
  +</ul>
  +<p>
  +If we carefully analyse this list, we see that the only method left 
unimplemented is the <strong>generate()</strong> method. So if we extend the 
<strong>AbstractGenerator</strong> class to make our own generator, the only 
method we'll have to implement is the <strong>generate()</strong> method.
  +                                     </p>
  +                             
  +                                     <p>The following variables are defined 
in the different interfaces and classes:</p>
  +<ul>
  +     <li>From <strong>org.apache.avalon.excalibur.pool.Poolable</strong>:
  +             <ul>
  +                     <li>None</li>
  +             </ul>
  +     </li>
  +     
  +     <li>From <strong>org.apache.avalon.excalibur.pool.Recyclable</strong>:
  +             <ul>
  +                     <li>None</li>
  +             </ul>
  +     </li>
  +     
  +     <li>From 
<strong>org.apache.avalon.framework.logger.AbstractLoggable</strong> 
(Deprecated):
  +             <ul>
  +                     <li>None</li>
  +             </ul>
  +     </li>
  +     
  +     <li>From 
<strong>org.apache.avalon.framework.logger.AbstractLoggable</strong> 
(Deprecated):
  +             <ul>
  +                     <li><code>private org.apache.log.Logger 
m_logger</code>: the base logger instance. Provides component with a logger. 
The <strong>getLogger()</strong> method should be used to aquire the logger.
  +                     </li>
  +             </ul>
  +     </li>
  +     
  +     <li>From <strong>org.apache.cocoon.xml.XMLProducer</strong>
  +             <ul>
  +                     <li>None</li>
  +             </ul>
  +     </li>
  +     
  +     <li>From <strong>org.apache.cocoon.xml.AbstractXMLProducer</strong>:
  +             <ul>
  +                     <li><code>protected XMLConsumer xmlConsumer</code>: The 
XMLConsumer receiving SAX events. Can be accessed via 
<strong>super.xmlConsumer</strong>.
  +                     </li>
  +                     
  +                     <li><code>protected ContentHandler 
contentHandler</code>: The ContentHandler receiving SAX events. Can be accessed 
via <strong>super.ContentHandler</strong>. 
  +                     </li>
  +                     
  +                     <li><code>protected LexicalHandler 
lexicalHandler</code>: The LexicalHandler receiving SAX events. Can be accessed 
via <strong>super.LexicalHandler</strong>. 
  +                     </li>
  +             </ul>
  +             We here access these variables via the <strong>super.</strong> 
qualifier, this is only done for clarity. They can be equally well accessed via 
using the <strong>this.</strong> qualifier (or omitting this). For reasons of 
clarity, if we access such a variable in our code, we will use the 
<strong>super.</strong> qualifier, although when summing up all the variables 
we will use <strong>this.</strong>.
  +     </li>
  +     
  +     <li>From <strong>org.apache.cocoon.generation.Generator</strong>:
  +             <ul>
  +                     <li><code>String ROLE = 
"org.apache.cocoon.generation.Generator"</code>
  +                     </li>
  +             </ul>
  +     </li>
  +     
  +     <li>From 
<strong>org.apache.cocoon.sitemap.SitemapModelComponent</strong>:
  +             <ul>
  +                     <li>None</li>
  +             </ul>
  +     </li>
  +     
  +     <li>From 
<strong>org.apache.avalon.framework.component.Component</strong>:
  +             <ul>
  +                     <li>None</li>
  +             </ul>
  +     </li>
  +     
  +     <li>From <strong>AbstractGenerator</strong> itself:
  +             <ul>
  +                     <li><code>protected SourceResolver resolver = 
null</code>: The current SourceResolver. Can be accessed via 
<strong>this.resolver</strong>.
  +                     </li>
  +                     
  +                     <li><code>protected Map objectModel = null</code>: The 
current Map objectModel. Can be accessed via <strong>this.objectModel</strong>.
  +                     </li>
  +                     
  +                     <li><code>protected Parameters parameters = 
null</code>: The current Parameters. Can be accessed via 
<strong>this.parameters</strong>.
  +                     </li>
  +                     
  +                     <li><code>protected String source = null</code>: The 
source URI associated with the request or <strong>null</strong>. Can be 
accessed via <strong>this.source</strong>.
  +                     </li>
  +             </ul>
  +     </li>
  +</ul>
  +<p>
  +This gives us a list of variables that we can use throughout our own 
generator.
  +</p>
  +                             </s4>
  +                             
  +                             <s4 title="ComposerGenerator">
  +                                     <p><strong>Can be used as base class if 
you want your Generator to be an Avalon Composer</strong><br/>
  +                                     </p>
  +                                     
  +                                     <p>This <strong>abstract class</strong> 
extends <strong>org.apache.cocoon.generation.AbstractGenerator</strong> and 
extends the interfaces 
<strong>org.apache.avalon.framework.component.Composable</strong> and 
<strong>org.apache.avalon.framework.activity.Disposable</strong>.</p><p>In 
addition to all the methods introduced in the 
<strong>AbstractGenerator</strong> class, these two interfaces introduce som 
new methods:</p>
  +<ul>
  +     <li>From 
<strong>org.apache.avalon.framework.component.Composable</strong>:
  +             <ul>
  +                     <li><code>public void compose(ComponentManager 
componentManager)</code>: Pass the ComponentManager to the composer. The 
Composable implementation should use the specified ComponentManager to acquire 
the components it needs for execution.                                 
  +                     </li>
  +             </ul>
  +     </li>
  +     
  +     <li>From 
<strong>org.apache.avalon.framework.activity.Disposable</strong>:
  +             <ul>
  +                     <li><code>public void dispose()</code>: The dispose 
operation is called at the end of a components lifecycle. Components use this 
method to release and destroy any resources that the Component owns.
  +                     </li>
  +             </ul>
  +             The Disposable interface is used when components need to 
deallocate and dispose resources prior to their destruction.
  +     </li>
  +     
  +     <li>From <strong>ComposerGenerator</strong> itself:
  +             <ul>
  +                     <li><code>public void compose(ComponentManager 
componentManager)</code>: Pass the ComponentManager to the composer. The 
Composable implementation should use the specified ComponentManager to acquire 
the components it needs for execution. (implemented)
  +                     </li>
  +                     
  +                     <li><code>public void dispose()</code>: The dispose 
operation is called at the end of a components lifecycle. Components use this 
method to release and destroy any resources that the Component owns. 
(implemented - implementation sets the ComponentManager to null)
  +                     </li>
  +             </ul>
  +     </li>
  +</ul>
  +                                     
  +                                     
  +                                     <p>We see that this class provides a 
default implementation of the methods introduced by the two new interfaces. The 
only method that needs to be implemented remains the 
<strong>generate()</strong> method, if we are satisfied with the default 
implementations.</p>
  +                                     
  +                                     <p>Besides these methods, also a new 
variable is introduced:</p>
  +<ul>
  +     <li>From <strong>ComposerGenerator</strong> itself:
  +             <ul>
  +                     <li><code>protected ComponentManager manager = 
null</code>: the component manager instance, can be accessed via 
<strong>this.manager</strong>.
  +                     </li>
  +             </ul>
  +     </li>
  +</ul>
  +                             </s4>
  +                             
  +                             <s4 title="ServletGenerator">
  +                                     <p><strong>If you want to generate 
servlets. This is the base class for the ServerPagesGenerator</strong><br/>
  +                                     </p>
  +                                     
  +                                     <p>The 
<strong>ServletGenerator</strong> extends <strong>ComposerGenerator</strong>. 
  +                                     </p>
  +
  +                                     <p>We are not giving a more elaborate 
description of this component at this time. We have not experimented with this 
component and we would not like to risk making any wrong assumptions.
  +                                     </p>
  +                             </s4>
  +                             
  +                             <s4 title="AbstractServerPage">
  +                                     <p><strong>[FIXME: This seems to be 
intended as basis for the ServerPagesGenerator, but it seems to be obsolete 
now?]</strong><br/>
  +                                     </p>
  +
  +                                     <p>The 
<strong>AbstractServerPage</strong> extends <strong>ServletGenerator</strong> 
and implements the <strong>org.apache.cocoon.caching.Cacheable</strong> and 
<strong>org.apache.cocoon.components.language.generator.CompiledComponent</strong>
 interfaces.
  +                                     </p>
  +
  +                                     <p>We are not giving a more elaborate 
description of this component at this time. We have not experimented with this 
component and we would not like to risk making any wrong assumptions.
  +                                     </p>
  +                             </s4>
  +                     </s3>
  +                     
  +                     <s3 title="Interface(s)">
  +                             <p>Following the somewhat little pointers on 
develop-part of the Cocoon-website <link 
href="../developing/extending.html">Extending Apache Cocoon van The Apache XML 
Project</link>), we find that the only interface that should be implemented is 
the Generator interface in the <strong>org.apache.cocoon.generation</strong> 
package, so we will try to give an overview as complete as possible for now.
  +                             </p>
  +                             
  +                             <s4 title="Generator">
  +                                     <p>As mentioned earlier this public 
interface is situated in the <strong>org.apache.cocoon.generation</strong> 
package. This interface in its turn extends the 
<strong>org.apache.cocoon.xml.XMLProducer</strong> and the 
<strong>org.apache.cocoon.sitemap.SitemapModelComponent</strong> interfaces.
  +                                     </p>
  +                                     
  +                                     <p>The interface 
<strong>XMLProducer</strong> is a top-level interface.
  +                                     </p>
  +                                     
  +                                     <p>The interface 
<strong>SitemapModelComponent</strong> extends the interface 
<strong>org.apache.avalon.framework.component.Component</strong>, which in turn 
is a top-level interface.
  +                                     </p>
  +                                     
  +                                     <p>Analyzing these interfaces tells us 
that the following methods should be implemented when implementing the 
<strong>Generator</strong> interface:</p>
  +                                     
  +<ul>
  +     <li>From <strong>org.apache.cocoon.xml.XMLProducer</strong>
  +             <ul>
  +                     <li><code>void setConsumer(XMLConsumer 
xmlconsumer)</code>: set the XMLConsumer that will receive XML data. The 
XMLConsumer interface extends <strong>org.xml.sax.ContentHandler</strong> and 
<strong>org.xml.sax.ext.LexicalHandler</strong>.
  +                     </li>
  +             </ul>
  +             This interface identifies classes that produce XML data, 
sending SAX events to the configured XMLConsumer.
  +     </li>
  +     
  +     <li>From 
<strong>org.apache.cocoon.sitemap.SitemapModelComponent</strong>:
  +             <ul>
  +                     <li><code>void setup(SourceResolver resolver, Map 
objectmodel, String src, Parameters par)</code>: set the SourceResolver, 
objectmodel Map, the source and sitemap Parameters used to process the request.
  +                     </li>
  +             </ul>
  +     </li>
  +     
  +     <li>From 
<strong>org.apache.avalon.framework.component.Component</strong>:
  +             <ul>
  +                     <li>None</li>
  +             </ul>
  +             When a class implements this interface, it allows itself to be 
managed by a ComponentManager and by an outside element called a Composable. 
The Composable must know what type of Component it is accessing, so it will 
re-cast the Component into the type it needs.
  +     </li>
  +     
  +     <li>From <strong>org.apache.cocoon.generation.Generator</strong> itself:
  +             <ul>
  +                     <li><code>void generate()</code>: generate the SAX 
events to initialize a pipeline.
  +                     </li>
  +             </ul>
  +     </li>
  +</ul>
  +                                     
  +                             </s4>
  +                     </s3>
  +                     
  +                     <p>We decided that the easiest way of writing a custom 
generator was to extend the <strong>AbstractGenerator</strong> class. The only 
method required to implement was the <strong>generate</strong> method, for now, 
we would settle with the provided default implementations of the other methods.
  +                     </p>
  +                     
  +             </s2>
  +             
  +             <s2 title="Writing a test generator">
  +                     <p>After making these decisions, and looking at the 
implementations of the classes, we could begin the implementation keeping in 
mind the following:</p>
  +                             <ul>
  +                                     <li>We have to provide SAX events to 
the XMLConsumer, that is set via the <strong>setConsumer</strong> method.</li>
  +                                     <li>We can access the XMLConsumer via 
<strong>super.xmlConsumer</strong> (analysis of code of 
<strong>FileGenerator</strong> and definition of the 
<strong>xmlConsumer</strong> variable as <strong>protected</strong> in the 
<strong>AbstractXMLProducer</strong> class). The <strong>super.</strong> 
modifier is only used for clarity, since it can also be accessed via 
<strong>this.xmlConsumer.</strong></li>
  +                                     <li>We will extend the 
<strong>org.apache.cocoon.generation.AbstractGenerator</strong> 
class.</li><li>We have to implement the <strong>generate</strong> method which 
purpose is to produce SAX events and feed them to the XMLConsumer.</li>
  +                             </ul>
  +                     
  +                     
  +                     <s3 title="The code of our first generator">
  +                             <p>As a first test we decided to parse a string 
containing the following XML content and feed the SAX events to the 
XMLConsumer:</p>
  +
  +<source><![CDATA[
  +<doc>My first Cocoon 2 generator!</doc>
  +]]>  
  +</source>
  +                             <p>First, we will give our code and then we 
will explain what it does and why we made these choices.</p>
  +<source><![CDATA[
  +package test;
  +
  +import java.io.IOException;
  +import java.io.StringReader;
  +
  +import org.xml.sax.XMLReader;
  +import org.xml.sax.InputSource;
  +import org.xml.sax.SAXException;
  +import org.xml.sax.XMLReaderFactory;
  +
  +import org.apache.cocoon.ProcessingException;
  +import org.apache.cocoon.generation.AbstractGenerator;
  +
  +public class MyGenerator extends AbstractGenerator {
  +  public void generate () throws IOException, SAXException,
  +    ProcessingException {
  +      String message = "<doc>My first Cocoon 2 generator!</doc>";
  +      
  +      XMLReader xmlreader = XMLReaderFactory.createXMLReader();
  +      xmlreader.setContentHandler(super.xmlConsumer);
  +      InputSource source = new InputSource(new StringReader(message));
  +      xmlreader.parse(source);
  +  }
  +}
  +
  +]]>
  +</source>
  +                             <p>First of all, in our working directory (may 
be any directory) we made a directory "test" and in that directory we created 
the Java source file <strong>MyGenerator.java</strong>. We also decided to put 
this class in a package and named that package <strong>test</strong>. This can 
be easily changed afterwards.
  +                             </p>
  +                             
  +                             <p>The obvious <strong>import</strong> 
statements are those to import the <strong>AbstractGenerator</strong> class and 
those to import the exceptions thrown by the <strong>generate</strong> method. 
The other import statements serve to parsing our string and generating SAX 
events.
  +                             </p>
  +                             
  +                             <p>The code itself is pretty straightforward. 
We have our class definition containing one method definition. First of all, in 
the <strong>generate</strong> method, we define the variable 
<strong>message</strong> containing the XML content we want to generate SAX 
events for.
  +                             </p>
  +                             
  +<source><![CDATA[
  +XMLReader xmlreader = XMLReaderFactory.createXMLReader();
  +]]>
  +</source>
  +<p>
  +Here we make a new <strong>XMLReader</strong> via the 
<strong>XMLReaderFactory</strong>. Since XMLReader is an interface, the 
XMLReaderFactory has to provide us with a class that implements the XMLReader 
interface, commonly known as a <strong>SAXParser</strong>. Therefore the 
XMLReaderFactory uses the system variable <strong>org.xml.sax.driver</strong> 
to determine which class to instantiate to provide us with an XMLReader. An 
example of how this is done is provided after we have discussed the rest of the 
code.
  +                             </p>
  +                             
  +<source><![CDATA[
  +xmlreader.setContentHandler(super.xmlConsumer);
  +]]>
  +</source>
  +<p>
  +With this line of code, we tell the XMLReader which object will receive the 
SAX events that will be generated when parsing. You can see that we use 
<strong>super.xmlConsumer</strong> to receive the SAX events.
  +                             </p>
  +                             
  +<source><![CDATA[
  +InputSource source = new InputSource(new StringReader(message));
  +xmlreader.parse(source);
  +]]>
  +</source>
  +<p>
  +With the second line we tell the XMLReader to start parsing the source, 
provided as argument of the <strong>parse</strong> method. This parse method 
can only be supplied with an <strong>org.xml.sax.InputSource</strong> argument 
or a <strong>String</strong> that represents a system identifier or URI. To 
parse our string we must encapsulate it in an InputSource object. Since the 
InputSource class can not be passed an XML document that is contained in a 
string, we first must encapsulate our string into another object, which we then 
pass to an InputSource object. In this example we have chosen for a 
<strong>StringReader</strong>. A StringReader can be given as argument when 
constructing an InputSource object and a StringReader can be given a String 
object as argument for its construction. This way we succeed in parsing our 
string.
  +                             </p>
  +                             
  +                             <p>The next step is compiling our newly written 
class. We give here an overview of the our work environment and show how we 
compiled this Java-file. All the commands from this example were carried out on 
a PC running Linux, but with respect to a few minor modifications, these 
commands will also work on a PC running Windows. The commands were carried out 
in the directory "/home/erwin/cocoon2/generator/". This directory has three 
subdirectories:</p>
  +     <ul>
  +             <li>"test/": directory containing the source files
  +                     <ul>
  +                             <li><strong>MyGenerator.java</strong>: source 
file for our generator
  +                             </li>
  +                     </ul>
  +             </li>
  +             
  +             <li>"jar/": directory containing the necessary jar (Java 
Archive) files
  +                     <ul>
  +                             <li><strong>xerces.jar</strong>: Xerces has an 
implementation for the XMLReader interface which we use</li>
  +                             <li><strong>cocoon.jar</strong>: contains the 
classes from Cocoon 2.0.2-dev, needed to extend AbstractGenerator. This is in 
fact a symbolic link to 
$TOMCAT_HOME/webapps/cocoon/WEB-INF/lib/cocoon-2.0.2.jar. Under Windows you 
will have to copy this file or point directly to this file.</li>
  +                     </ul>
  +             </li>
  +             
  +             <li>"compiled/": outputdirectory for javac. The compiled files 
will end up in this directory
  +                     <ul>
  +                             <li><strong>test/MyGenerator.class</strong>: 
after compiling, we will have this file here</li>
  +                     </ul>
  +             </li>
  +     </ul>
  +<source><![CDATA[
  +javac -classpath .:jar/cocoon.jar:jar/xerces.jar \
  +-d compiled test/MyGenerator.java
  +]]>
  +</source>
  +<p>
  +Now we have our compiled class, we can make the big step of putting it to 
work. To make sure there were no errors in our code, we tested our code by 
using another class as the ContentHandler of our generator. After these tests 
were completed (without errors), we tried to deploy our generator from within 
Cocoon 2. 
  +                             </p>
  +                     </s3>
  +                     
  +                     <s3 title="Deploying MyGenerator">
  +                             <p>The next step is deploying our custom 
written generator. First of all we stopped the Tomcat engine (and thus Cocoon 
2). We also emptied the <strong>work</strong> directory, located at 
"$TOMCAT_HOME/work/". Experience learned that this is something you have to do 
every time you want to try something like this with Cocoon 2.
  +                             </p>
  +                             
  +                             <p>For the next step, we changed the main 
sitemap to be able to use or generator in the following way:</p>
  +                                     <p>Under the 
<strong>map:generators</strong> element, we added the following:</p>
  +
  +<source><![CDATA[                                            
  +<map:generator name="mygenerator" src="test.MyGenerator"/>
  +]]>  
  +</source>
  +                                             
  +                                             <p>Under the 
<strong>map:pipelines</strong> element, we added the following:</p>
  +
  +<source><![CDATA[
  +<map:pipeline>
  +  <map:match pattern="mygenerator.xml">
  +    <map:generate type="mygenerator"/>
  +    <map:serialize type="xml"/>
  +  </map:match>
  +  <map:handle-errors>
  +    <map:transform src="stylesheets/system/error2html.xsl"/>
  +    <map:serialize status-code="500"/>
  +  </map:handle-errors>
  +</map:pipeline>
  +]]>
  +</source>
  +                                             <p>If the page 
<strong>mygenerator.xml</strong> is requested, we tell Cocoon 2 to use our 
generator, which we have named <strong>mygenerator</strong>. We do not define 
the <strong>src</strong> attribuut, since we do not use it in our generator. 
Once we get the content, we serialize it as xml, so we can check if the input 
matches the output. In the event that an error occurs, we use one of the 
stylesheets of Cocoon 2 or another pipeline to signal the error to the user.</p>
  +                             <p>After the changes to the sitemap, we added 
the directory "/home/erwin/cocoon2/generator/" to the classpath. After these 
changes, we restarted Tomcat and tried to access the page 
"http://localhost:8080/cocoon/mygenerator.xml";. After waiting a while, we 
received a fatal error. Inspection of the log-files (which is something you 
should always do when receiving an error that is not so clear) showed that the 
following exception was the cause of that fatal error:</p>
  +<source><![CDATA[
  +ERROR   (2002-03-27) 23:21.40:190   [sitemap] (/cocoon/)
  +Thread-23/Handler: Error compiling sitemap
  +java.lang.NoClassDefFoundError:
  +org/apache/cocoon/generation/AbstractGenerator
  +        at java.lang.ClassLoader.defineClass0(Native Method)
  +        at java.lang.ClassLoader.defineClass
  +(ClassLoader.java, Compiled Code)
  +        at java.security.SecureClassLoader.defineClass
  +(SecureClassLoader.java, Compiled Code)
  +        at java.net.URLClassLoader.defineClass
  +(URLClassLoader.java, Compiled Code)
  +        at java.net.URLClassLoader.access$100
  +(URLClassLoader.java, Compiled Code)
  +        at java.net.URLClassLoader$1.run
  +(URLClassLoader.java, Compiled Code)
  +        at java.security.AccessController.doPrivileged
  +(Native Method)
  +        at java.net.URLClassLoader.findClass
  +(URLClassLoader.java, Compiled Code)
  +        at java.lang.ClassLoader.loadClass
  +(ClassLoader.java, Compiled Code)
  +        at sun.misc.Launcher$AppClassLoader.loadClass
  +(Launcher.java, Compiled Code)
  +        at java.lang.ClassLoader.loadClass
  +(ClassLoader.java, Compiled Code)
  +        at org.apache.tomcat.loader.AdaptiveClassLoader.loadClass
  +
  +(AdaptiveClassLoader.java, Compiled Code)
  +        at java.lang.ClassLoader.loadClass
  +(ClassLoader.java, Compiled Code)
  +        at java.lang.ClassLoader.loadClass
  +(ClassLoader.java, Compiled Code)
  +        at org.apache.cocoon.util.ClassUtils.loadClass
  +
  +(ClassUtils.java, Compiled Code)
  +        at org.apache.cocoon.sitemap.AbstractSitemap.load_component
  +
  +(AbstractSitemap.java, Compiled Code)
  +        at org.apache.cocoon.www.sitemap_xmap
  +$Configurer.configGenerators(sitemap_xmap.java, Compiled Code)
  +        at org.apache.cocoon.www.sitemap_xmap.configure
  +(sitemap_xmap.java, Compiled Code)
  +        at org.apache.avalon.excalibur.component.
  +DefaultComponentFactory.newInstance
  +(DefaultComponentFactory.java, Compiled Code)
  +        at org.apache.avalon.excalibur.component.
  +ThreadSafeComponentHandler.initialize
  +(ThreadSafeComponentHandler.java, Compiled Code)
  +     at org.apache.cocoon.components.language.generator.
  +GeneratorSelector.addGenerator
  +(GeneratorSelector.java, Compiled Code)
  +        at org.apache.cocoon.components.language.generator.
  +ProgramGeneratorImpl.addCompiledComponent
  +(ProgramGeneratorImpl.java, Compiled Code)
  +        at org.apache.cocoon.components.language.generator.
  +ProgramGeneratorImpl.generateResource
  +(ProgramGeneratorImpl.java, Compiled Code)
  +        at org.apache.cocoon.components.language.generator.
  +ProgramGeneratorImpl.createResource
  +(ProgramGeneratorImpl.java, Compiled Code)
  +        at org.apache.cocoon.components.language.generator.
  +ProgramGeneratorImpl.load
  +(ProgramGeneratorImpl.java, Compiled Code)
  +        at org.apache.cocoon.sitemap.Handler.run
  +(Handler.java, Compiled Code)
  +        at java.lang.Thread.run(Thread.java:484)
  +]]>
  +</source>
  +                             
  +                             <p>Puzzled by this error, we mailed to the 
cocoon-users mailinglist ([email protected]) and explained our 
situation. The answer we received was to put our generator in the 
"$TOMCAT_HOME/webapps/cocoon/WEB-INF/classes/". We stopped Tomcat, emptied the 
work-directory, removed the directory "/home/erwin/cocoon2/generator/" from the 
classpath and made a directory "test/" under the 
"$TOMCAT_HOME/webapps/cocoon/WEB-INF/classes/" and placed 
<strong>MyGenerator.class</strong> in that directory. We then restarted Tomcat 
and once again tried to access "http://localhost:8080/cocoon/mygenerator.xml";. 
But after making that request in our browser, we got a message from the browser 
saying that the server could not be reached. Looking at the xterm from which we 
started Tomcat, we saw the following error:</p>
  +<source><![CDATA[
  +IGSEGV   11*  segmentation violation
  +si_signo [11]: SIGSEGV   11*  segmentation violation
  +si_errno [0]: Success
  +si_code [128]: unknown siginfo
  +sc_pc: 0x20010164f08, r26: 0x200001e19e0
  +              
  +thread pid: 26157
  +stackpointer=0x3fffc5f69b8
  +                        
  +Full thread dump Classic VM (1.3.1-1, native threads):
  +...
  +...
  +]]>
  +</source>
  +<p>
  +Removing our class (and commenting out our changes in the sitemap for 
safety) would resolve the problem, but then we can't use our generator.
  +                             </p>
  +                             
  +                             <p>Somewhere on the Web we had read a mail from 
someone who was also having <strong>NoClassDefFoundError</strong>s that he was 
able to solve by unzipping all the jar-files (a jar is basically a zip file 
containing the compiled classes) from 
"$TOMCAT_HOME/webapps/cocoon/WEB-INF/lib/" into the 
"$TOMCAT_HOME/webapps/cocoon/WEB-INF/classes/" directory. We stopped Tomcat, 
emptied the work-directory and started Tomcat again.
  +                             </p>
  +                             
  +                             <p>After restarting Tomcat we had our hopes up 
that this time it would work. We also started our browser and tried to access 
"http://localhost:8080/cocoon/mygenerator.xml";, again. After waiting a while 
(Cocoon 2 had to recompile its sitemap and some other components) we got the 
see our XML file. Cocoon 2 produced the following XML document:</p>
  +
  +<source><![CDATA[
  +<?xml version="1.0" encoding="UTF-8"?>
  +<doc>My first Cocoon 2 generator!</doc>
  +]]>
  +</source>
  +<p>
  +So, after a bit of struggling, we finally succeeded in deploying our own 
generator.
  +                             </p>
  +                     </s3>
  +                     
  +                     <s3 title="Considerations afterwards">
  +                             <p>After seeing our example and having some 
experience with Cocoon 2 one might ask why we reinvented the wheel by 
instantiating a parser and not using the one provided by Cocoon 2. It is 
evident that a start of a pipeline is a generator that fires SAX events, there 
must be a SAXParser available throughout Cocoon 2 that can be easily accessed. 
This is in fact the case. There are a number of reasons why we had not chosen 
that approach the first time around:</p>
  +                                     <ul>
  +                                             <li>Limited knowledge of the 
whole underlying architecture, not really enhanced by the documentation.</li>
  +                                             <li>We wanted to keep the 
time-to-test as short as possible, so we didn't spend time finding this 
information in the source in the first phase.</li>
  +                                             <li>We didn't see any other 
possibility of testing our code before we tried to integrate it with the Cocoon 
2 project.</li>
  +                                     </ul>
  +                             
  +                             
  +                             <p>We would still like to point the reader to 
an alternative solution, i.e. the solution that is used throughout Cocoon 2. We 
will give the code fragments here and we will then explain what it does.</p>
  +<source><![CDATA[
  +...
  +import org.apache.avalon.excalibur.xml.Parser;
  +...
  +Parser parser = null;
  +
  +  try {
  +    parser = (Parser)this.manager.lookup(Parser.ROLE);
  +    parser.parse(this.getInputSource(),handler);
  +  } catch (SAXException e) {
  +    // Preserve original exception
  +    throw e;
  +  } catch (Exception e) {
  +    throw new ProcessingException("Exception during processing of " +
  +    this.getSystemId(),e);
  +  } finally {
  +    if (parser != null) {
  +      this.manager.release(parser);
  +    }
  +  }
  +...
  +]]>
  +</source>
  +                             
  +                             <p>An extra <strong>import</strong> statement 
is added. The <strong>Parser</strong> interface of the Avalon/Excalibur project 
(<link 
href="http://jakarta.apache.org/avalon/excalibur/index.html";>Avalon/Excalibur 
van The Jakarta Project</link>) defines the following method:</p>
  +                                     <ul>
  +                                             <li><code>void 
parse(InputSource in, ContentHandler consumer)</code>: the implementation of 
this method should parse the <strong>InputSource</strong> and send the SAX 
events to the <strong>consumer</strong>. The consumer can be an 
<strong>XMLConsumer</strong> or an object that implements 
<strong>LexicalHandler</strong> as well.
  +                                             </li>
  +                                     </ul>
  +                                     <p>This interface defines a variable 
<strong>ROLE</strong> of the type String that is given the value 
<strong>org.apache.avalon.excalibur.xml.Parser</strong>. This variable is used 
to ask the <strong>ComponentManager</strong>, which is accessed by 
<strong>this.manager</strong>, to <strong>lookup</strong> a 
<strong>Component</strong> that has that role. The returned Component is then 
casted to a <strong>Parser</strong> type. We can then apply the parse method to 
any <strong>org.xml.sax.InputSource</strong> object and to an object that 
implements the <strong>ContentHandler</strong> interface. Finally, we have to 
tell the ComponentManager that we are finished using the parser. This allows 
the ComponentManager to handle the End-Of-Life Lifecycle events associated with 
this Component.
  +                             </p>
  +                             
  +                             <p><strong>NOTE:</strong> if you want to use 
this method to obtain a parser, it would be better to extend the 
<strong>ComposerGenerator</strong> class, instead of the 
<strong>AbstractGenerator</strong> class. The ComposerGenerator is defined to 
make use of a <strong>ComponentManager</strong>, while this is not the case for 
the <strong>AbstractGenerator</strong> class. You should envisage the given 
code as part of a class that extends the <strong>ComposerGenerator</strong> 
class or one of its children.
  +                             </p>
  +                     </s3>
  +             </s2>
  +             
  +             <s2 title="Going the distance">
  +                     <p>We have succeeded in implementing a first test to 
find out how everything works, but a generator that only sends a fixed string 
to Cocoon 2 is not that interesting. Since we have written an application that 
can serve XML documents contained in a String object (using JDOM (<link 
href="http://www.jdom.org";>JDOM.org</link>)), we want to be able to retrieve 
these documents through our browser, which sends this request to Cocoon 2. 
Cocoon 2 then fires up our generator to retrieve the requested XML document and 
can start the pipeline for processing that document.
  +                     </p>
  +                     
  +                     <p>Since we had experimented with Java RMI in one of 
our courses, we decided to try a setup where our generator was a client for the 
document server and the communication would happen via RMI. For this section, 
we will first look at setting up the server, next we will look at accessing the 
server from within MyGenerator and finally we will put it all together. If we 
get this to work, we then can ponder about looking up parameters defined in the 
sitemap to use in MyGenerator. We used (<link 
href="http://java.sun.com/products/jdk/1.2/docs/guide/rmi/getstart.doc.html";>Getting
 Started Using RMI</link>) as a basis for getting started with RMI. If you have 
never used RMI, we recommend that you read this document to develop a basic 
understanding of working with RMI.
  +                     </p>
  +                     
  +                     <s3 title="Setting up a RMI server">
  +                             <p>After reading the document (<link 
href="http://java.sun.com/products/jdk/1.2/docs/guide/rmi/getstart.doc.html";>Getting
 Started Using RMI</link>) and having deployed the example, we started writing 
our own interface, called <strong>Serverfunctions</strong> that defines the 
methods that should be implemented by a program that wishes to serve as a 
server for <strong>MyGenerator</strong>. This interface looks like this:</p>
  +<source><![CDATA[
  +package test;
  +
  +import java.rmi.Remote;
  +import java.rmi.RemoteException;
  +
  +public interface ServerFunctions extends Remote {
  +  /**
  +    This method returns a String, containing 
  +    a well-formed XML fragment/document. This String 
  +    contains information about the application implementing 
  +    this interface. Choosing what information is put 
  +    into this String is left to the application designer.
  +  */
  +  String sayHello () throws RemoteException;
  +
  +  /**
  +    This method returns a String, containing a well-formed XML
  +    fragment/document. To determine the information that should be
  +    returned, a systemId is passed to this method.
  +  */
  +  String getResource (String systemId) throws RemoteException;
  +}
  +
  +]]>
  +</source>
  +<p>
  +This interface defines two methods that should be implemented. Since these 
methods can be invoked via RMI we must declare that these methods can throw a 
RemoteException. These methods should return well-formed XML, as specified.
  +                             </p>
  +                             
  +                             <p>With interfaces alone we cannot build an 
application. We also must have a class that implements this interface. The 
following example demonstrates how this can be implemented. We used JDOM (<link 
href="http://www.jdom.org";>JDOM.org</link>) for reading in a XML document and 
converting it to a String.</p>
  +<source><![CDATA[
  +package test;
  +
  +import java.rmi.Naming;
  +import java.rmi.RemoteException;
  +import java.rmi.RMISecurityManager;
  +import java.rmi.server.UnicastRemoteObject;
  +
  +import org.jdom.Document;
  +import org.jdom.JDOMException;
  +import org.jdom.input.SAXBuilder;
  +import org.jdom.output.XMLOutputter;
  +
  +import test.ServerFunctions;
  +
  +public class Server extends UnicastRemoteObject implements
  +  ServerFunctions {
  +
  +  
  +  public Server () throws RemoteException {
  +    super();
  +  }
  +
  +  public String sayHello () {
  +    return "<doc>My First RMI Server!</doc>";
  +  }
  +
  +  public String getResource (String systemId) {
  +    try {
  +      SAXBuilder sb = new SAXBuilder();
  +      Document newdoc = sb.build(systemId);
  +      return (new XMLOutputter()).outputString(newdoc);
  +    } catch (JDOMException jde) {
  +      System.out.println("JDOM error: " + jde.getMessage());
  +      jde.printStackTrace();
  +      // Rethrow the exception so the other
  +      // side knows something is wrong
  +      throw new RemoteException("JDOMException while processing " +
  +        systemId,jde);
  +    }
  +  }
  +
  +  public void main (String args[]) {
  +    // Create and install a security manager
  +    // For testing purposes only, set this to null
  +    System.setSecurityManager(null);
  +    
  +    try {
  +      Server obj = new Server();
  +      // Bind this object instance to the name "MyServer"
  +      Naming.rebind("MyServer",obj);
  +      System.out.println("MyServer bound in registry");
  +    } catch (Exception e) {
  +      System.out.println("Server error: " + e.getMessage());
  +      e.printStackTrace();
  +  }
  +}
  +}
  +
  +
  +
  +]]>
  +</source>
  +                                                             
  +                             <p>We first have the necessary 
import-statements. This class implements the <strong>ServerFunctions</strong> 
interface we defined before. We also extend the 
<strong>UnicastRemoteObject</strong>. The Java API docs ((<link 
href="http://java.sun.com/j2se/1.3/docs/api/index.html";>Java 2 Platform, SE 
v1.3 API documentation</link>)) tell us the following about 
UnicastRemoteObject: "The UnicastRemoteObject class defines a non-replicated 
remote object whose references are valid only while the server process is 
alive. Objects that require remote behavior should extend RemoteObject, 
typically via UnicastRemoteObject." This allows us, by calling the constructor 
of this superclass, to use the behavior of the UnicastRemoteObject for our 
RMIServer. This is typically done by calling the <strong>super()</strong> 
constructor in the constructor of our class.
  +                             </p>
  +                             
  +                             <p>Next, we have the implementation of the two 
methods defined in our interface. The <strong>sayHello</strong> method just 
returns a string representing the following XML fragment:</p>
  +
  +<source><![CDATA[
  +<doc>My First RMI Server!
  +]]>  
  +</source>
  +                             <p>We then also implement the 
<strong>getResource</strong> method. In the body of the try-block we first 
build a JDOM Document using the given systemId. This means that an XML file, at 
the given location, is read and a JDOM Document object is created. Next, we use 
the method <strong>outputString(Document doc)</strong> of the 
<strong>XMLOutputter</strong> class to convert the JDOM Document to a string. 
It is this string that is returned to the client. In the event that there may 
be an error building the document, a <strong>JDOMException</strong> is thrown. 
If this is the case, we print the info to stdout and rethrow the exception, 
encapsulated in a RemoteException.
  +                             </p>
  +                             
  +                             <p>We then only need a <strong>main</strong> 
method to have a Java application at hand. The first thing we do is disabling 
the <strong>SecurityManager</strong>. For security reasons, this should only be 
done only for testing purposes on an isolated system and in production 
environments. We did this so we could bind this server in the rmiregistry 
without rewriting any Java policy files. Next, we make a new 
<strong>Server</strong> object and bind this in the rmiregistry, where it is 
associated with the name <strong>MyServer</strong>. We end with printing out a 
line that we have bound this object in the rmiregistry.
  +                             </p>
  +                     </s3>
  +                     
  +                     <s3 title="Setting up a RMI client">
  +                             <p>The next step in the process is to implement 
a Java application that can connect to our RMI server and invoke its methods. 
Once again, we will first give our code and then explain what it does.</p>
  +<source><![CDATA[
  +package test;
  +
  +import java.rmi.Naming;
  +import java.rmi.RemoteException;
  +
  +import test.ServerFunctions;
  +
  +public class Client {
  +
  +  public void main (String args[]) {
  +    String message = "blank";
  +
  +    
  +    try {
  +      // "obj" is the identifier that we'll use to refer
  +      // to the remote object that implements the
  +      // "ServerFunctions" interface
  +      ServerFunctions obj = 
  +      (ServerFunctions)Naming.lookup("//myhost.com/MyServer");
  +      
  +      message = obj.sayHello();
  +      System.out.println(message);
  +      
  +      message = obj.getResource("index.xml");
  +      System.out.println(message);
  +    } catch (Exception e) {
  +      System.out.println("Server exception: " + e.getMessage());
  +      e.printStackTrace();
  +    }
  +  }
  +}
  +
  +]]>
  +</source>
  +                             <p>Our client only defines a 
<strong>main</strong> method. We first initialize the variable, to which we 
will assign the return value of the <strong>sayHello</strong> method. Next, we 
try to <strong>lookup</strong> an object that is bound to 
"//myhost.com/MyServer" (note that myhost.com is a random chosen example). The 
lookup method returns an object, that is casted to the 
<strong>ServerFunctions</strong> type. We then invoke the sayHello method on 
the object and we print this message out. We also invoke the 
<strong>getResource</strong> method and print the result out. If this succeeds, 
we know everything works correctly. If an exception occurs, we print out the 
message from this exception plus its stack trace.
  +                             </p>
  +                     </s3>
  +                     
  +                     <s3 title="Testing the RMI components">
  +                             <p>We will first test if the RMI communication 
works. If it doesn't work there is no point in trying to integrate RMI 
communication in MyGenerator.Located in the directory 
"/home/erwin/cocoon2/generator/", which has the subdirectory "test/" containing 
our files, we execute the following commands:</p>
  +
  +<source><![CDATA[
  +javac -classpath .:jar/jdom.jar:jar/xerces.jar -d compiled/ test/*.java
  +rmic -classpath .:jar/jdom.jar:jar/xerces.jar -d compiled/ test.Server
  +rmiregistry &amp;
  +java -classpath compiled/:jar/jdom.jar:jar/xerces.jar \
  
+-Djava.rmi.server.codebase=http://myhost.com/~erwin/cocoon2/generator/compiled/
 \
  +test.Server
  +MyServer bound in registry
  +]]>
  +</source>
  +<p>
  +If you forget to define the <strong>java.rmi.server.codebase</strong> system 
property or give it a wrong value, you are most likely to get the following 
exception:</p>
  +
  +<source><![CDATA[
  +HelloImpl err: RemoteException occurred in server thread; nested exception 
is:
  +        java.rmi.UnmarshalException: error unmarshalling arguments; 
  +nested exception is:
  +        java.lang.ClassNotFoundException: test.Server_Stub
  +java.rmi.ServerException: RemoteException occurred in server thread; 
  +nested exception is:
  +        java.rmi.UnmarshalException: error unmarshalling arguments; 
  +nested exception is:
  +        java.lang.ClassNotFoundException: test.Server_Stub
  +java.rmi.UnmarshalException: error unmarshalling arguments; 
  +nested exception is:
  +        java.lang.ClassNotFoundException: test.Server_Stub
  +java.lang.ClassNotFoundException: test.Server_Stub
  +        at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer
  +(StreamRemoteCall.java, Compiled Code)
  +        at sun.rmi.transport.StreamRemoteCall.executeCall
  +(StreamRemoteCall.java, Compiled Code)
  +        at sun.rmi.server.UnicastRef.invoke(UnicastRef.java, Compiled Code)
  +        at sun.rmi.registry.RegistryImpl_Stub.rebind(Unknown Source)
  +        at java.rmi.Naming.rebind(Naming.java, Compiled Code)
  +        at test.Server.main(Server.java, Compiled Code)
  +]]>
  +</source>
  +<p>
  +We now can start the client to test if everything works. Notice that the 
resource requested in the code is in fact a relative URI. It is relative to the 
path from where we started the server application. The file index.xml contains 
the following information:</p>
  +<source><![CDATA[
  +<?xml version="1.0"?>
  +<document>
  +  <title>This is a document</title>
  +  <para>This is the first paragraph.</para>
  +</document>
  +]]>
  +</source>
  +<p>
  +The client is started with the following command:
  +</p>
  +<source><![CDATA[
  +[erwin generator]$ java -classpath compiled/ test.Client
  +]]>
  +</source>
  +
  +<p>This resulted in the following output:</p>
  +
  +<source><![CDATA[
  +<doc>My First RMI Server!</doc>
  +<?xml version="1.0" encoding="UTF-8"?>
  +<document>
  +  <title>This is a document</title>
  +  <para>This is the first paragraph.</para>
  +</document>
  +]]>
  +</source>
  +<p>
  +This is exactly the output we expected, except for the encoding attribute. 
But this is something that is added by JDOM.
  +                             </p>
  +                             
  +                             <p><strong>NOTE:</strong> we would like to 
conclude this section with a final note about the RMI server application. If 
you wish to deploy an RMI server application in the real world, you may wish to 
delete the code that disables the SecurityManager. If no other settings are 
changed, you may get the following error when starting your server application 
(depending on the configuration in your <strong>java.policy</strong> file):</p>
  +                             
  +<source><![CDATA[
  +HelloImpl err: access denied 
  +(java.net.SocketPermission 127.0.0.1:1099 connect,resolve)
  +java.security.AccessControlException: access denied 
  +(java.net.SocketPermission 127.0.0.1:1099 connect,resolve)
  +        at java.security.AccessControlContext.checkPermission
  +(AccessControlContext.java, Compiled Code)
  +     ...
  +        at test.Server.main(Server.java, Compiled Code)      
  +]]>
  +</source>
  +
  +<p>
  +The most likely reason is that the default policy does not permit your 
server to bind its name in the rmiregistry. You have to change the security 
policy specified in the "$JAVA_HOME/jre/lib/security/java.policy" file. Since 
we are no experts in security we cannot give you any advice in this matter, but 
a general advice in security related matters is that you are better safe then 
sorry.
  +                             </p>
  +                     </s3>
  +                     
  +                     <s3 title="Putting the pieces together">
  +                             <p>We now have been able to setup a generator 
and use RMI communication, now it is time to integrate these two pieces so we 
have a fully blown RMIGenerator for Cocoon 2. But before we do that, we will 
look how we can access the parameters and source that are passed from the 
sitemap to MyGenerator.
  +                             </p>
  +                             
  +                             <p>We have seen that the method 
<strong>setup</strong> is implemented in the <strong>AbstractGenerator</strong> 
class. One of the arguments of this method is String src. The value of the 
<strong>src</strong> attribute in the sitemap is passed via this argument and 
the variable <strong>source</strong> will be assigned this value. If for 
instance the following is a small part of the sitemap:</p>
  +                             
  +<source><![CDATA[
  +<map:match pattern="mygenerator.xml">
  +  <map:generate type="mygenerator" src="example.xml"/>
  +  <map:serialize type="xml"/>
  +</map:match>
  +]]>
  +
  +</source>
  +<p>
  +If we request "$FULL_URL_PATH/mygenerator.xml", the value of the 
<strong>src</strong> attribute will be passed to <strong>MyGenerator</strong> 
using the setup method. This value, <strong>example.xml</strong> can then be 
accessed via the <strong>this.source</strong> variable in our code.
  +                             </p>
  +                             
  +                             <p>As for now, we still have hardcoded in 
MyGenerator to which RMI server our generator should connect and also which 
bindname should be looked up. This is not desirable, we wish to have a 
configurable generator. "Compile once, run many" is maybe the way you could 
describe this. We wish to pass these values as parameters to the generator. 
Clearly, these values should be specified in the sitemap. Amongst the elements 
allowed in the sitemap there is a <strong>parameter</strong> element. If we 
want to use this element to pass parameters to our generator this element has 
to appear as a child of the <strong>generate</strong> element. Our sitemap 
fragment will then look like this:</p>
  +                             
  +<source><![CDATA[
  +<map:match pattern="mygenerator.xml">
  +  <map:generate type="mygenerator" src="example.xml">
  +    <map:parameter name="host" value="myhost.com"/>
  +    <map:parameter name="port" value="1099"/>
  +    <map:parameter name="bindname" value="MyServer"/>
  +  </map:generate>
  +  <map:serialize type="xml"/>
  +</map:match>
  +]]>
  +
  +</source>
  +<p>
  +We define three parameters:</p>
  +
  +<ul>
  +     <li><strong>host</strong>: tells the generator at which host the RMI 
server application is running. <strong>REQUIRED</strong>.</li>
  +     <li><strong>port</strong>: tells the generator at which port at the 
remote host the rmiregistry process is running. If no value is specified Java 
uses the default port (1099). <strong>OPTIONAL</strong>.</li>
  +     <li><strong>bindname</strong>: tells the generator which name should be 
looked up in the remote registry to obtain access to the RMI server object. 
<strong>REQUIRED</strong>.</li>
  +</ul>
  +<p>
  +We only need these three parameters to define the remote server object. We 
do not need to specify which methods should be invoked since we demand that a 
remote server implements the <strong>ServerFunctions</strong> interface. This 
is something that may be considered in the future.
  +                             </p>
  +                             
  +                             <p>We now have defined the host, port and 
bindname parameters, but how can we access the value of these parameters in our 
code? The setup method has an argument Parameters par. It is via this argument 
that the parameters defined in the sitemap will be passed to the generator. 
This argument will be assigned to the <strong>parameters</strong> variable 
defined in AbstractGenerator. To obtain the value of each parameter we can 
invoke the following method on the parameters variable: <code>public 
java.lang.String getParameter(java.lang.String name)</code>. This method 
returns the value of the specified parameter, or throws an exception if there 
is no such parameter.
  +                             </p>
  +                             
  +                             <p>With all this in mind, we can finally build 
our configurable RMIGenerator. Also, this time we are going to extend the 
<strong>ComposerGenerator</strong> instead of the 
<strong>AbstractGenerator</strong> class. This way, we can make use of the 
<strong>ComponentManager</strong> to obtain a SAXParser. 
  +                             </p>
  +                             
  +                             <p>At this moment we decide that if there is no 
value given to the src attribute in the sitemap (<strong>source is 
null</strong>), we will invoke the <strong>sayHello</strong> method and 
otherwise the getResource with the appropriate parameter. When the value of the 
src attribute is the <strong>empty string</strong>, the 
<strong>getResource</strong> method is invoked, so this should be 
<strong>handled by the RMI server application</strong>.
  +After a little bit of thinking about how to code all this, we eventually 
wrote the following generator:</p>
  +
  +<source><![CDATA[
  +package test;
  +
  +// import the necessary classes from the java.io package
  +import java.io.IOException;
  +import java.io.StringReader;
  +
  +// import the necessary classes from the java.rmi package
  +import java.rmi.Naming;
  +import java.rmi.RemoteException;
  +import java.rmi.NotBoundException;
  +
  +// import the necessary SAX classes
  +import org.xml.sax.InputSource;
  +import org.xml.sax.SAXException;
  +
  +// import of the classes used from Cocoon 2
  +import org.apache.cocoon.ProcessingException;
  +import org.apache.cocoon.generation.ComposerGenerator;
  +
  +// Avalon Framework
  +import org.apache.avalon.framework.parameters.Parameters;
  +import org.apache.avalon.framework.parameters.ParameterException;
  +import org.apache.avalon.framework.component.ComponentException;
  +
  +// needed for obtaining parser in Cocoon
  +import org.apache.avalon.excalibur.xml.Parser;
  +
  +import test.ServerFunctions;
  +
  +public class MyGenerator extends ComposerGenerator {
  +  public void generate () throws IOException, SAXException,
  +    ProcessingException {
  +      String host;
  +
  +      // lookup parameter 'host'
  +      try {
  +        host = parameters.getParameter("host");
  +        // test if host is not the empty string
  +        if (host == "") {
  +          throw new ParameterException(
  +          "The parameter 'host' may not be the empty string");
  +        }
  +      } catch (ParameterException pe) {
  +        // rethrow as a ProcessingException
  +        throw new ProcessingException(
  +        "Parameter 'host' not specified",pe);
  +      }
  +
  +      String bindname;
  +      
  +      // lookup parameter 'bindname'
  +      try {
  +        bindname = parameters.getParameter("bindname");
  +        // test if bindname is not the empty string
  +        if (bindname == "") {
  +          throw new ParameterException(
  +          "The parameter 'bindname' may not be the empty string");
  +        }
  +      } catch (ParameterException pe) {
  +        // rethrow as a ProcessingException
  +        throw new ProcessingException(
  +        "Parameter 'bindname' not specified",pe);
  +      }
  +
  +      String port = "";
  +      
  +      // lookup parameter 'port'
  +      try {
  +        port = parameters.getParameter("port");
  +        port = ":" + port;
  +      } catch (ParameterException pe) {
  +        // reset port to the empty string
  +        // port is not required
  +        port = "";
  +      }
  +
  +      try {
  +        ServerFunctions obj = 
  +        (ServerFunctions)Naming.lookup("//" +
  +          host + port + "/" + bindname);
  +        String message = "";
  +     
  +        // determine the method to invoke
  +        // depending on value of source
  +     if (this.source == null) {
  +       message = obj.sayHello();
  +     } else {
  +       message = obj.getResource(source);
  +     }
  +
  +     Parser parser = null;
  +     parser = (Parser)this.manager.lookup(Parser.ROLE);
  +     
  +     InputSource inputSource = new InputSource(
  +     new StringReader(message));
  +     parser.parse(inputSource,super.xmlConsumer);
  +     
  +      } catch (NotBoundException nbe) {
  +     throw new ProcessingException("
  +     Error looking up the RMI server application",nbe);
  +      } catch (ComponentException ce) {
  +        throw new ProcessingException("
  +        Error obtaining a SAXParser",ce);
  +      }
  +    }
  +}
  +      
  +]]>
  +</source>                            
  +                             <p>Since we have already explained every step 
that happens in this generator, we are confident that everyone will understand 
the code. We are now ready to deploy this generator.
  +                             </p>
  +                     </s3>
  +                     
  +                     <s3 title="The final step: deployment">
  +                             <p>We can now compile our classes and put the 
generator, along with the ServerFunctions interface, in the right place. For 
compiling, we used the following command:</p>
  +<source><![CDATA[
  +javac -classpath .:jar/xerces.jar:jar/cocoon.jar:jar/framework.jar: \
  +jar/excalibur.jar:jar/exc-scratchpad.jar \
  +-d compiled/ test/ServerFunctions.java test/MyGenerator.java
  +]]>
  +</source>
  +<p>
  +where xerces.jar is a symbolic link to 
"$TOMCAT_HOME/webapps/cocoon/WEB-INF/lib/xercesImpl-2.0.0.jar", framework.jar 
to "$TOMCAT_HOME/webapps/cocoon/WEB-INF/lib/avalon-framework-4.1.2.jar", 
excalibur.jar to 
"$TOMCAT_HOME/webapps/cocoon/WEB-INF/lib/avalon-excalibur-4.1.jar" and 
exc-scratchpad.jar to 
"$TOMCAT_HOME/webapps/cocoon/WEB-INF/lib/avalon-excalibur-scratchpad-20020212.jar".
 This is valid for Cocoon 2.0.2-dev. If you use another version of Cocoon 2, 
you might have to change some of these names.
  +If your platform does not allow the use of symbolic links, you should use 
the complete path to the corresponding jar-files.
  +                             </p>
  +                             
  +                             <p>Now that these classes are compiled we can 
place them in the $TOMCAT_HOME/webapps/cocoon/WEB-INF/classes/" directory as 
before. Now all that is left is shutting down Tomcat/Cocoon, emptying the work 
directory, modifying the sitemap, setting up a RMI server application and 
starting Tomcat/Cocoon.
  +                             </p>
  +                     </s3>
  +             </s2>
  +     </s1>
  +     
  +     <s1 title="Future plans">
  +             <p>The first version of this generator was written as a 
proof-of-concept. The latest version (as given here, extending the 
ComposerGenerator) only foresees in the <strong>generate</strong> method. There 
are a number of plans we still have to extend the functionality and thus 
usability of this generator:</p>
  +                     <ul>
  +                             <li>allow passing of a (J)DOM document instance 
as a resource to our generator. JDOM does require an additional entry in the 
classpath.</li>
  +                             <li>supply a possibility for caching 
documents</li>
  +                             <li>if the RMI server application can generate 
SAX events, try to pass the xmlConsumer to the server application as the 
ContentHandler</li>
  +                     </ul>
  +             
  +             <p>These are some of the extensions we have in mind for this 
generator. Our goal is to complete these steps within a few weeks (it will 
probably be a bit longer since the deadline of our thesis is only three weeks a 
way at the time of writing).
  +             </p>
  +     </s1>
  +</body>
  +</document>
  
  
  

----------------------------------------------------------------------
In case of troubles, e-mail:     [EMAIL PROTECTED]
To unsubscribe, e-mail:          [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to