shannon 2002/06/15 11:55:32
Modified: src/documentation/xdocs/tutorial Tag: cocoon_2_0_3_branch
tutorial-rmi-generator.xml
Log:
fixed numerous validation problems
Revision Changes Path
No revision
No revision
1.1.2.2 +98 -104
xml-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.1
retrieving revision 1.1.2.2
diff -u -r1.1.2.1 -r1.1.2.2
--- tutorial-rmi-generator.xml 7 Jun 2002 19:52:38 -0000 1.1.2.1
+++ tutorial-rmi-generator.xml 15 Jun 2002 18:55:32 -0000 1.1.2.2
@@ -1,4 +1,5 @@
<?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>
@@ -25,7 +26,7 @@
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>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>
@@ -33,10 +34,10 @@
<li>Jakarta Tomcat 3.2.3</li>
<li>Cocoon 2.0.2-dev</li>
</ul>
- </p>
+
</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>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>
@@ -50,7 +51,7 @@
<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>
- </p>
+
</s1>
<s1 title="Our planning, step by step">
@@ -62,24 +63,25 @@
</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>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>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>
- So the choice of which class to extend
will depends mostly on which is the level of abstraction required by your
generator.
+ <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">
@@ -107,7 +109,7 @@
<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>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>
@@ -203,10 +205,11 @@
</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>The following variables are defined
in the different interfaces and classes:</p>
<ul>
<li>From <strong>org.apache.avalon.excalibur.pool.Poolable</strong>:
<ul>
@@ -288,16 +291,16 @@
</ul>
</li>
</ul>
+<p>
This gives us a list of variables that we can use throughout our own
generator.
-
- </p>
+</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>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>
@@ -324,12 +327,11 @@
</ul>
</li>
</ul>
- </p>
- <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>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>
@@ -338,7 +340,6 @@
</ul>
</li>
</ul>
- </p>
</s4>
<s4 title="ServletGenerator">
@@ -378,7 +379,7 @@
<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>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>
@@ -410,7 +411,7 @@
</ul>
</li>
</ul>
- </p>
+
</s4>
</s3>
@@ -420,23 +421,22 @@
</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>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>
- </p>
+
<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>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>
-
- <p>First, we will give our code and then we
will explain what it does and why we made these choices.
+ <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;
@@ -465,8 +465,6 @@
]]>
</source>
- </p>
-
<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>
@@ -476,58 +474,59 @@
<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>
- <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>
- <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>
- <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:
- <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>
+ <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>
@@ -536,16 +535,16 @@
<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:
- <ul>
- <li>Under the
<strong>map:generators</strong> element, we added the following:
+ <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>
- </li>
- <li>Under the
<strong>map:pipelines</strong> element, we added the following:
+ <p>Under the
<strong>map:pipelines</strong> element, we added the following:</p>
+
<source><![CDATA[
<map:pipeline>
<map:match pattern="mygenerator.xml">
@@ -559,12 +558,8 @@
</map:pipeline>
]]>
</source>
- 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.
- </li>
- </ul>
- </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>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
@@ -634,9 +629,8 @@
at java.lang.Thread.run(Thread.java:484)
]]>
</source>
- </p>
- <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>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
@@ -652,32 +646,35 @@
...
]]>
</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>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>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>
- <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>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;
@@ -701,14 +698,13 @@
...
]]>
</source>
- </p>
- <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>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>
- 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>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.
@@ -724,7 +720,7 @@
</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>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;
@@ -751,10 +747,11 @@
]]>
</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>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;
@@ -818,19 +815,16 @@
]]>
</source>
-
- </p>
-
+
<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>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>
-
<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>
@@ -839,7 +833,7 @@
</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>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;
@@ -875,15 +869,13 @@
]]>
</source>
-
- </p>
-
<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>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
@@ -894,7 +886,8 @@
MyServer bound in registry
]]>
</source>
-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>
+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:
@@ -920,8 +913,8 @@
at test.Server.main(Server.java, Compiled Code)
]]>
</source>
-
-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>
+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>
@@ -930,14 +923,15 @@
</document>
]]>
</source>
+<p>
The client is started with the following command:
-
+</p>
<source><![CDATA[
[erwin generator]$ java -classpath compiled/ test.Client
]]>
</source>
-This resulted in the following output:
+<p>This resulted in the following output:</p>
<source><![CDATA[
<doc>My First RMI Server!</doc>
@@ -948,11 +942,11 @@
</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><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
@@ -966,6 +960,7 @@
]]>
</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>
@@ -974,7 +969,7 @@
<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>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">
@@ -984,10 +979,11 @@
]]>
</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>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">
@@ -1001,13 +997,15 @@
]]>
</source>
-We define three parameters:
+<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>
@@ -1018,7 +1016,7 @@
</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:
+After a little bit of thinking about how to code all this, we eventually
wrote the following generator:</p>
<source><![CDATA[
package test;
@@ -1129,23 +1127,20 @@
}
]]>
-</source>
-
-
- </p>
-
+</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>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>
@@ -1157,13 +1152,12 @@
</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>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>
<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>
----------------------------------------------------------------------
In case of troubles, e-mail: [EMAIL PROTECTED]
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]