A document has been updated:
http://cocoon.zones.apache.org/daisy/documentation/688.html
Document ID: 688
Branch: main
Language: default
Name: Creating a Generator (unchanged)
Document Type: Document (unchanged)
Updated on: 9/11/05 11:58:33 AM
Updated by: Geoff Howard
A new version has been created, state: publish
Parts
=====
Content
-------
This part has been updated.
Mime type: text/xml (unchanged)
File name: (unchanged)
Size: 15485 bytes (previous version: 15565 bytes)
Content diff:
(57 equal lines skipped)
</p>
<p>To realize these requirements we have to find a bean, and then "render
it".
--- In this case the XML rendering of the bean will be recursive. The general
--- approach will use Java's reflection mechanisms, and only worry about
--- properties. There will be a certain amount of risk involved with a complex
bean
--- that includes references to other beans in that if you have two beans
referring
--- to each other you will have an infinite loop. Detecting these is outside
the
--- scope of what we are trying to do, and that is generally bad design anyway
so we
--- won't worry too much about it. Yes there is overhead with the beans
--- Introspector but we are writing for the general case.</p>
+++ In this case the XML rendering of the bean will be recursive. The general
+++ approach will use Java's reflection mechanisms, and only worry about
properties.
+++ There will be a certain amount of risk involved with a complex bean that
+++ includes references to other beans in that if you have two beans referring
to
+++ each other you will have an infinite loop. Detecting these is outside the
scope
+++ of what we are trying to do, and that is generally bad design anyway so we
won't
+++ worry too much about it. Yes there is overhead with the beans Introspector
but
+++ we are writing for the general case.</p>
<p>To set up our generator, we need to use a serializer that shows us what
the
results are, so we will set up our sitemap to use our generator like
this:</p>
(6 equal lines skipped)
<p>Even though it is generally bad design to have a static anything in a
Cocoon
application we are going to use a helper class called "BeanPool" with the
get()
--- and put() methods that are familiar from the HashMap. So that it is easier
for
+++ and put() methods that are familiar from the HashMap. So that it is easier
for
you to change the behavior of the BeanGenerator, we will provide a nice
protected method called <tt>findBean()</tt> which is meant to be overridden
with
something more robust.</p>
(48 equal lines skipped)
</pre>
<p>As you can see, we have our simplified <tt>findBean()</tt> method which
can
--- be replaced with something more robust later. All you need to do to
populate
--- the BeanPool is to call the <tt>BeanPool.put(String key, Object bean)</tt>
--- method from somewhere else.</p>
+++ be replaced with something more robust later. All you need to do to
populate the
+++ BeanPool is to call the <tt>BeanPool.put(String key, Object bean)</tt>
method
+++ from somewhere else.</p>
<h3>Setting up to Generate</h3>
(11 equal lines skipped)
<p>What we did is call the setup method from AbstractGenerator which
populates
some class fields for us (like the <tt>source</tt> field), then we tried to
find
--- the bean using the key provided. If the bean is <tt>null</tt>, then we
follow
+++ the bean using the key provided. If the bean is <tt>null</tt>, then we
follow
the principle of least surprise and throw the <tt>ResourceNotFoundException
</tt>so the Sitemap knows that we simply don't have the bean available
instead
--- of generating some lame 500 server error. That's all we have to do to set
up
--- this particular Generator. Oh, and since we do have the <tt>m_bean</tt>
field
--- populated we do want to clean up after ourselves properly. Let's add the
+++ of generating some lame 500 server error. That's all we have to do to set up
+++ this particular Generator. Oh, and since we do have the <tt>m_bean</tt>
field
+++ populated we do want to clean up after ourselves properly. Let's add the
recycle() method from Recyclable so that we don't give an old result when a
bean
can't be found:</p>
(7 equal lines skipped)
<h3>The Caching Clues</h3>
--- <p>We are going to make the caching for the BeanGenerator really simple.
--- Ideally we would have something that listens for changes and invalidates the
--- SourceValidity if there is a change to the bean we are rendering.
Unfortunately
+++ <p>We are going to make the caching for the BeanGenerator really simple.
Ideally
+++ we would have something that listens for changes and invalidates the
+++ SourceValidity if there is a change to the bean we are rendering.
Unfortunately
that is outside our scope, and we will set up the key so that it never
expires
--- unless it is done manually. Since we are using the source property from the
+++ unless it is done manually. Since we are using the source property from the
Sitemap as our key, let's just use that as our cache key:</p>
<pre> public Serializable getKey()
(11 equal lines skipped)
</pre>
<p>Using this approach is a bit naive in the sense that it is very possible
that
--- the beans will have changed. We could use an ExpiresValidity instead to
make
+++ the beans will have changed. We could use an ExpiresValidity instead to make
things a bit more resilient to change, but that is an excersize for you,
dear
reader.</p>
<h3>Generating Output</h3>
--- <p>Now that we have our bean, we are ready to generate our output. The
+++ <p>Now that we have our bean, we are ready to generate our output. The
AbstractXMLProducer base class (AbstractGenerator inherits from that)
stores the
--- target in a class field named <tt>contentHandler</tt>. Simple enough.
We'll
+++ target in a class field named <tt>contentHandler</tt>. Simple enough. We'll
start by implementing the generate() method, but we already know we need to
--- handle beans differently than the standard String and primitive types. So
let's
--- stub out the method we will use for recursive serialization. Here we
go:</p>
+++ handle beans differently than the standard String and primitive types. So
let's
+++ stub out the method we will use for recursive serialization. Here we go:</p>
<pre> public void generate()
{
(5 equal lines skipped)
}
</pre>
--- <p>All we did was call the start and end document for the whole XML
Document.
--- That is enough for a basic XML document with no content. The
+++ <p>All we did was call the start and end document for the whole XML
Document.
+++ That is enough for a basic XML document with no content. The
<tt>renderBean()</tt> method is where the magic happens:</p>
<pre> public void renderBean(String root, Object bean)
(17 equal lines skipped)
}
</pre>
--- <p>So far we created the root element and started iterating over the
--- properties. Our root element consists of a namespace a name and a qName.
Our
--- implementation is using the <tt>source</tt> for the initial root element so
as
--- long as we never have any special characters like a colon (':') we should be
--- OK. Without going through the individual properties, a java.awt.Dimension
--- object with a source of "dim" will be redered like this:</p>
+++ <p>So far we created the root element and started iterating over the
properties.
+++ Our root element consists of a namespace a name and a qName. Our
implementation
+++ is using the <tt>source</tt> for the initial root element so as long as we
never
+++ have any special characters like a colon (':') we should be OK. Without
going
+++ through the individual properties, a java.awt.Dimension object with a
source of
+++ "dim" will be redered like this:</p>
<pre><dim:dim xmlns:dim="java:java.awt.Dimension"/>
</pre>
(30 equal lines skipped)
</pre>
<p>This method is a little more complex in that we have to figure out if the
--- property is readable, and is a type we can handle. In this case, we don't
read
+++ property is readable, and is a type we can handle. In this case, we don't
read
indexed properties (if you want to support that, you'll have to extend this
code
--- to do that), and we don't read any properties where there is no read
method. We
--- use the property name for the elements surrouding the property values. We
get
--- the value, and then we call the start and end elements for the property.
Inside
+++ to do that), and we don't read any properties where there is no read
method. We
+++ use the property name for the elements surrouding the property values. We
get
+++ the value, and then we call the start and end elements for the property.
Inside
of the calls, we determine if the item is a bean, and if so we render the
bean
using the renderBean method (the recursive aspect); otherwise we render the
--- content as text as long as it is not null. Once the <tt>isBean()</tt>
method is
+++ content as text as long as it is not null. Once the <tt>isBean()</tt>
method is
implemented, our Dimension example above will produce the following
result:</p>
<pre><dim:dim xmlns:dim="java:java.awt.Dimension">
(22 equal lines skipped)
</pre>
<p>The isBean() method will treat all primitives, Strings, Dates, and
anything
--- in "java.lang" as value objects. This captures the boxed versions of
primitives
--- as well as the unboxed versions. Everything else is treated as a bean.</p>
+++ in "java.lang" as value objects. This captures the boxed versions of
primitives
+++ as well as the unboxed versions. Everything else is treated as a bean.</p>
<h2>Summary</h2>
<p>Generators aren't too difficult to write, but the tricky parts are there
due
--- to namespaces. As long as you are familiar with the SAX API you should not
have
--- any problems. The complexity in our generator is really from the reflection
--- logic used to discover how to render an object. You might ask why we
didn't use
--- the XMLEncoder in the java.beans package. The answer has to do with the
fact
+++ to namespaces. As long as you are familiar with the SAX API you should not
have
+++ any problems. The complexity in our generator is really from the reflection
+++ logic used to discover how to render an object. You might ask why we didn't
use
+++ the XMLEncoder in the java.beans package. The answer has to do with the fact
that the facility is based on IO streams, and can't be easily adapted to XML
--- streams. At any rate, we have something that can work with a wide range of
--- classes. Our XML is easy to understand. Here is a snippet from a more
complex
+++ streams. At any rate, we have something that can work with a wide range of
+++ classes. Our XML is easy to understand. Here is a snippet from a more
complex
example:</p>
<pre><line:line xmlns:line="java:com.mycompany.shapes.Line">
(13 equal lines skipped)
</line:line>
</pre>
--- <p>Our therhetical line object contained a name and two java.awt.Point
objects
--- which in turn had an x and a y property. It is easier to understand when
you
--- have domain specific beans that are backed to a database. Nevertheless, we
have
+++ <p>Our theoretical line object contained a name and two java.awt.Point
objects
+++ which in turn had an x and a y property. It is easier to understand when you
+++ have domain specific beans that are backed to a database. Nevertheless, we
have
a generator that satisfies a general purpose and can be extended later on to
support our needs as they change.</p>
(2 equal lines skipped)
Fields
======
no changes
Links
=====
no changes
Custom Fields
=============
no changes
Collections
===========
no changes