home *** CD-ROM | disk | FTP | other *** search
- <?xml version="1.0" encoding="UTF-8"?>
- <!--
- Copyright 1999-2004 The Apache Software Foundation
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
- <!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.0//EN" "document-v10.dtd">
- <document>
- <header>
- <title>Write a Custom Generator</title>
- <version>0.3</version>
- <authors>
- <person name="Geoff Howard" email="javageoff@yahoo.com" />
- </authors>
- </header>
-
- <body>
- <s1 title="Introduction">
- <p>This Tutorial describes the steps necessary to write a basic Cocoon
- generator. Starting with a quick "Hello World" example and
- progressing to slightly more involved examples should give a good
- start to those whose applications call for extending Cocoon with a
- custom generator.</p>
-
- <p>The intention is to provide:</p>
-
- <ul>
- <li>the basics of creating SAX events in a C2 generator</li>
-
- <li>a little understanding of the Avalon container contract as it
- relates to C2 generators</li>
-
- <li>a little understanding of the factors that would influence
- the decision about which xxxGenerator to extend</li>
- </ul>
-
- <s2 title="Purpose">
- <p>The flexibility to extend the basic "Out of the box"
- functionality of Cocoon will be an important feature for Cocoon's
- viability as a broadly used application framework. Though the
- documentation on
- <link href="../developing/extending.html">"Extending Cocoon"</link>
- (at least at this writing) seems to have a hard time imagining
- applications for custom generators outside of the bizarre, I
- imagine several scenarios which could call for it:</p>
-
- <ul>
- <li>A datasource as yet undeveloped in Cocoon (e.g. event
- logs)</li>
-
- <li>Database driven applications for which XSP is either too
- awkward or holds too many performance questions. The need for
- high scalability will drive some (such as myself) to seek
- optimization in custom generators that just do not seem
- reasonable to expect out of the auto-generated code that XSPs
- produce. The current
- <link href="../performancetips.html">Performance Tips</link>
- documentation seems to lead in this direction.</li>
-
- <li>Customized control over the caching behaviour if not
- provided for by other means.</li>
- </ul>
- </s2>
-
- <s2 title="Important">
- <p>There are other options that should be considered before
- settling on a new generator. One notable consideration is the
- option of writing a Source that would fit your needs. See
- <link href="http://marc.theaimsgroup.com/?t=102571404500001&r=1&w=2">this discussion</link>
-
- from the mailing list for an introduction to the idea. Of course,
- XSP should be considered - I have not seen any performance
- comparisons that quantify the benefit that can be had from a
- custom generator. Finally, be sure you understand the purpose and
- capabilities of all current standard Generators, as well as those
- in the scratchpad (for instance, there is a
- <code>TextParserGenerator</code> in the scratchpad at the moment
- which may be configurable enough to process the event log need
- mentioned above). Cocoon is a rapidly developing technology that
- may have anticipated your need. Because the documentation lags
- behind development, you may find more by examining the source
- directory and searching the
- <link href="http://cocoon.apache.org/community/mail-archives.html">mail archives</link>
- for applicable projects.</p>
- </s2>
-
- <s2 title="Intended Audience">
- <p>This Tutorial is aimed at users who have developed an
- understanding of the basics of Cocoon and have a need to begin
- extending it for their own purposes, or desire a deeper
- understanding of what goes on under the hood.</p>
- </s2>
-
- <s2 title="Prerequisites">
- <p>Generator developers should have:</p>
-
- <ul>
- <li>Read
- <link href="../userdocs/concepts/index.html">Cocoon Concepts</link>
- , as well as
- <link href="../developing/extending.html">Extending Cocoon</link>
-
- , and the broad overview of
- <link href="../developing/avalon.html">Avalon</link>
-
- , the framework upon which Cocoon is built.</li>
-
- <li>An installed version of Cocoon if you want to follow the
- examples yourself (obviously).</li>
-
- <li>A good understanding of Java.</li>
- <li>Java SDK (1.2 or later) "installed".</li>
- </ul>
- </s2>
- </s1>
-
- <s1 title="Diving In">
- <p>Let us start with a simple "Hello World" example:</p>
-
- <s2 title="Simple Example">
- <p>Our goal will be to build the following document (or, more to
- the point, the SAX events that would correspond to this document).
- </p>
-
- <source xml:space="preserve">
- <![CDATA[<example>Hello World!</example>]]>
- </source>
-
- <p>An example of code that will send the correct SAX events down
- the pipeline:</p>
-
- <source xml:space="preserve">
- <![CDATA[
- import org.apache.cocoon.generation.AbstractGenerator;
- import org.xml.sax.helpers.AttributesImpl;
- import org.xml.sax.SAXException;
-
- public class HelloWorldGenerator extends AbstractGenerator
- {
-
- AttributesImpl emptyAttr = new AttributesImpl();
-
- /**
- * Override the generate() method from AbstractGenerator.
- * It simply generates SAX events using SAX methods.
- * I haven't done the comparison myself, but this
- * has to be faster than parsing them from a string.
- */
-
- public void generate() throws SAXException
-
- {
-
- // the org.xml.sax.ContentHandler is inherited
- // through org.apache.cocoon.xml.AbstractXMLProducer
-
- contentHandler.startDocument();
-
- contentHandler.startElement("", "example", "example", emptyAttr);
-
- contentHandler.characters("Hello World!".toCharArray(),0,
- "Hello World!".length());
-
- contentHandler.endElement("","example", "example");
-
- contentHandler.endDocument();
-
- }
- }
- ]]></source>
-
- <p>So, the basic points are that we extend
- <code>AbstractGenerator</code>, override its generate() method,
- call the relevant SAX methods on the contentHandler (inherited
- from <code>AbstractGenerator</code>) to start, fill and end the
- document. For information on the SAX api, see
- <link href="http://www.saxproject.org/">www.saxproject.org</link>
- </p>
-
- <note>A performance tip might be to keep an empty instance of
- <code>AttributesImpl</code> around to reuse for each element
- with no attributes. Also, the characters(char[] chars, int start,
- int end) begs to be overloaded with a version like
- <code>characters(String justPutTheWholeThingIn)</code>
-
- that handles the conversion to a character array and assumes you
- want from beginning to end, as is done in
- <code>org.apache.cocoon.generation.AbstractServerPage</code>.
- If you are not using namespaces, it is easy to imagine overloaded
- convenience implementations of the other SAX methods as well.
- You will probably want to set up a convenient BaseGenerator with
- helpers like this and extend it for your real Generators.</note>
-
- <s3 title="What to Extend?">
- <p>How did we choose to extend <code>AbstractGenerator</code>?
- Generators are defined by the
- <code>org.apache.cocoon.generation.Generator</code> interface.
- The only direct implementation of this of interest to us is
- <code>AbstractGenerator</code>, which gives a basic level of
- functionality. Another option would have been
- <code>ComposerGenerator</code>, which would give us the added
- functionality of implenting the Avalon interface
- <code>Composable</code>
-
- , which would signal the container that handles all the
- components including our generator to give us a handle back to
- the <code>ComponentManager</code>
- during the startup of the container. If we needed to lookup a
- pooled database connection, or some other standard or custom
- Cocoon component, this is what we would do. Most of the out
- of the box Generators extend <code>ComposerGenerator</code>.
- Other abstract Generators you may choose to extend include the
- poorly named (IMHO) <code>ServletGenerator</code>
-
- , and <code>AbstractServerPage</code>
- . While these both introduce functionality specific to their
- eventual purpose - the JSP and XSP generators, they do make a
- convenient starting place for many other Generators.</p>
- </s3>
-
- <s3 title="Running The Sample">
- <p>In order to run this sample, you will need to compile the code,
- deploy it into the cocoon webapp, and modify the sitemap to
- declare our generator and allow access to it via a pipeline.</p>
-
- <s4 title="Compile">
- <p>Save this source as <code>HelloWorldGenerator.java</code>
- and compile it using</p>
-
- <source>javac -classpath %PATH_TO_JARS%\cocoon.jar;%PATH_TO_JARS%\xml-apis.jar
- HelloWorldGenerator.java</source>
-
- <p>Unfortunately for me, the exact name of your cocoon and
- xml-apis jars may vary with exactly which distribution,
- or CVS version you are using, since the community has taken
- to appending dates or versions at the end of the jar name
- to avoid confusion. Be sure to find the correct name on
- your system and substitute it in the classpath. Also, you
- have several options on where to find jars. If you have a
- source version that you built yourself, you may want to
- point to <code>lib\core\</code> for them. If you have only
- the binary version, you can find them in
- <code>WEB-INF\lib\</code></p>
- </s4>
-
- <s4 title="Deploy">
- <p>Simply copy the class file into the
- <code>%TOMCAT_HOME%\webapps\cocoon\WEB-INF\classes</code>
- directory</p>
-
- <note>If memory serves me, there have been occasional
- classloading problems in the past that may affect
- classloading. If your compiled classes are not recognized
- in the classes directory, try
- <code>jar</code>-ing them up and place them in
- <code>WEB-INF\lib\</code> instead. That is probably where
- your real generators would go anyway - with a whole package
- of all your custom classes in one jar.</note>
- </s4>
-
- <s4 title="Sitemap Modifications">
- <p>You need to do two things: in the
- <code>map:generators</code>
-
- section, add an element for your class:</p>
-
- <source><![CDATA[<map:generator name="helloWorld" src="HelloWorldGenerator"/>]]></source>
-
- <p>Then add a pipeline to sitemap.xmap which uses it:</p>
-
- <source xml:space="preserve">
- <![CDATA[...
- <map:match pattern="heyThere.xml">
- <map:generate type="helloWorld"/>
- <map:serialize type="xml"/>
- </map:match>
- ...]]>
- </source>
-
- <p>And finally, our creation should be available at
- <code>http://localhost:8080/cocoon/heyThere.xml</code>
- </p>
-
- <p>Depending on your exact setup, you may need to restart
- Tomcat (or whatever your servlet container is) to get
- there.</p>
-
- <note>Notice that the
- <code>
- <![CDATA[<?xml version="1.0" encoding="UTF-8"?>]]>
- </code>
-
- declaration was added for us by the xml serializer at the
- beginning. If you need to modify this, the generator is not
- the appropriate place. The default encoding of UTF-8 could
- be overridden with iso-8859-1 for example by specifying an
- <code>
- <![CDATA[<encoding>iso-8859-1</encoding>]]>
- </code>
-
- child parameter inside the declaration for the xml
- serializer in your sitemap.</note>
- </s4>
- </s3>
- </s2>
-
- <s2 title="A Less Trivial Example">
- <p>Moving on to a less trivial example, we will take some
- information out of the Request, and construct a slightly more
- involved document. This time, our goal will be the following
- document:</p>
-
- <source xml:space="preserve">
- <![CDATA[<doc>
- <uri>...</uri>
- <params>
- <param value="...">...</param>
- ...
- </params>
- <date>..</date>
- </doc>]]>
- </source>
-
- <p>The values of course will be filled in from the request, and
- will depend on choices we make later.</p>
-
- <source xml:space="preserve"><![CDATA[import org.apache.cocoon.generation.AbstractGenerator;
- import org.xml.sax.helpers.AttributesImpl;
- import org.xml.sax.SAXException;
-
- // for the setup() method
- import org.apache.cocoon.environment.SourceResolver;
- import java.util.Map;
- import org.apache.avalon.framework.parameters.Parameters;
- import org.apache.cocoon.ProcessingException;
- import java.io.IOException;
-
- // used to deal with the request parameters.
- import org.apache.cocoon.environment.ObjectModelHelper;
- import org.apache.cocoon.environment.Request;
- import java.util.Enumeration;
-
- import java.util.Date;
-
-
- public class RequestExampleGenerator extends AbstractGenerator
- {
-
- // Will be initialized in the setup() method and used in generate()
- Request request = null;
- Enumeration paramNames = null;
- String uri = null;
-
- // We will use attributes this time.
- AttributesImpl myAttr = new AttributesImpl();
- AttributesImpl emptyAttr = new AttributesImpl();
-
-
- public void setup(SourceResolver resolver, Map objectModel,
- String src, Parameters par)
- throws ProcessingException, SAXException, IOException
- {
- super.setup(resolver, objectModel, src, par);
- request = ObjectModelHelper.getRequest(objectModel);
- paramNames = request.getParameterNames();
- uri = request.getRequestURI();
- }
-
- /**
- * Implement the generate() method from AbstractGenerator.
- */
-
- public void generate() throws SAXException
- {
-
- contentHandler.startDocument();
-
- contentHandler.startElement("", "doc", "doc", emptyAttr);
-
- // <uri> and all following elements will be nested inside the doc element
- contentHandler.startElement("", "uri", "uri", emptyAttr);
-
- contentHandler.characters(uri.toCharArray(),0,uri.length());
-
- contentHandler.endElement("", "uri", "uri");
-
- contentHandler.startElement("", "params", "params", emptyAttr);
-
- while (paramNames.hasMoreElements())
- {
- // Get the name of this request parameter.
- String param = (String)paramNames.nextElement();
- String paramValue = request.getParameter(param);
-
- // Since we've chosen to reuse one AttributesImpl instance,
- // we need to call its clear() method before each use. We
- // use the request.getParameter() method to look up the value
- // associated with the current request parameter.
- myAttr.clear();
- myAttr.addAttribute("","value","value","",paramValue);
-
- // Each <param> will be nested inside the containing <params> element.
- contentHandler.startElement("", "param", "param", myAttr);
- contentHandler.characters(param.toCharArray(),0,param.length());
- contentHandler.endElement("","param", "param");
- }
-
- contentHandler.endElement("","params", "params");
-
- contentHandler.startElement("", "date", "date", emptyAttr);
-
- String dateString = (new Date()).toString();
- contentHandler.characters(dateString.toCharArray(),0,dateString.length());
-
- contentHandler.endElement("", "date", "date");
- contentHandler.endElement("","doc", "doc");
- contentHandler.endDocument();
- }
-
- public void recycle() {
- super.recycle();
- this.request = null;
- this.paramNames = null;
- this.parNames = null;
- this.uri = null;
- }
- }]]></source>
- <s3 title="Compile and Test">
- <p>Save this code as
- <code>RequestExampleGenerator.java</code>
- and compile as before. You will need to add both
- <code>avalon-framework.jar</code> and
- <code>avalon-excalibur.jar</code> to your classpath
- this time. Besides finding the exact name of the jar
- as described above, you may now also have to ensure
- that you have the version of excalibur targeted to your
- jvm version - there is currently a version for JDK 1.4
- and one for 1.2/1.3</p>
- <p>For your sitemap, you will need to add a definition
- for this generator like
- <code><![CDATA[<map:generator name="requestExample" src="RequestExampleGenerator"/>]]></code>
- and you will need a sitemap pipeline like:</p>
- <source xml:space="preserve"><![CDATA[<map:match pattern="howYouDoin.xml">
- <map:generate type="requestExample"/>
- <map:serialize type="xml"/>
- </map:match>]]>
- </source>
- <p>At this point, you should be able to access the
- example at
- <code>http://localhost:8080/cocoon/howYouDoin.xml?anyParam=OK&more=better</code></p>
- </s3>
- <s3 title="New Concepts">
- <s4 title="Lifecycle">
- <p>First, notice that we now override the
- <code>setup(...)</code> and <code>recycle()</code> methods
- defined in <code>AbstractGenerator</code>.
- The <code>ComponentManager</code> that handles the lifecycle of
- all <code>component</code>s in Cocoon, calls
- <code>setup(..)</code> before each new call to
- <code>generate()</code> to give the Generator information
- about the current request and its environment, and calls
- recycle() when it is done to enable it to clean up resources
- as appropriate. Our example uses only the
- <code>objectModel</code> which abstracts the Request,
- Response, and Context. We get a reference to the Request
- wrapper, and obtain an <code>Enumeration</code> of all the
- GET/POST parameters available.</p>
-
- <p>The <code>src</code> and <code>SourceResolver</code> are
- provided to enable us to look up and use whatever source is
- specified in the pipeline setup. Had we specified
- <code><![CDATA[<map:generate type="helloWorld" src="someSourceString"/>]]></code>
- we would have used the <code>SourceResolver</code> to work
- with "someSourceString", whether it be a file, or url, etc.</p>
-
- <p>We are also given a
- <code>Parameters</code> reference which we would use to obtain
- any parameter names and values which are children elements of
- our <code>map:generate</code> element in the pipeline.</p>
-
- <note>It may be good practice to abstract the source of your parameters so
- that they do not have to come from the Request object. For instance, the
- following code would allow us to abstract the origin of two parameters, param1
- and param2:</note>
-
- <source xml:space="preserve"><![CDATA[In RequestExampleGenerator.java,
- ...
- String param1 = null;
- String param2 = null;
- ...
- public void setup(SourceResolver resolver, Map objectModel,
- String src, Parameters par)
- throws ProcessingException, SAXException, IOException
- {
- ...
- param1 = par.getParameter("param1");
- param2 = par.getParameter("param2");
- }
-
- and in sitemap.xmap,
-
- ...
- <map:match pattern="abstractedParameters.xml"/>
- <map:act type="request">
- <map:parameter name="parameters" value="true"/>
- <map:generate type="requestExample">
- <parameter name="param1" value="{visibleName1}"/>
- <parameter name="param2" value="{visibleName2}"/>
- </map:generate>
- </map:act>
- </map:match>
- ...]]></source>
- <p>As you can see, we have also hidden the internal
- name from the outside world who will use
- <code>?visibleName1=foo&visibleName2=bar</code>
- </p>
- </s4>
- <s4 title="Nested Elements">
- <p>In this example, nested elements are created simply
- by nesting complete
- <code>startElement()</code>/<code>endElement</code>
- pairs within each other. If we had a logic failure in our code and
- sent non-wellformed xml events down the pipeline, nothing in our
- process would complain (try it!). Of course, any transformers later
- in the pipeline would behave in an unpredictable manner.</p>
- </s4>
- <s4 title="Attributes">
- <p>Finally, we've introduced the use of attributes.
- We chose to
- employ one <code>attributesImpl</code>, clearing it before each
- element. Multiple attributes for an element would simply be added
- by repeated calls to <code>addAttribute</code>.</p>
- </s4>
- </s3>
- <s3 title="A Lesson">
- <p>Before moving on, it is worth noting that
- after all this work, there is already a generator provided with
- Cocoon which does much of what we have accomplished here
- - <code>org.apache.cocoon.generation.RequestGenerator</code>
- which in the default configuration is probably available at
- <code>http://localhost:8080/cocoon/request</code></p>
- </s3>
- </s2>
-
- <s2 title="Moving On">
- <p>From here, we will move on to cover handling ugly pseudo-xml
- (like real world html) with CDATA blocks, employing some of the
- Avalon lifecycle method callbacks (Composable/Disposable), Database
- access, and Caching.</p>
- <s3 title="The Employee SQL Example Reworked">
- <p>In the samples included with Cocoon, there is an example of a SQL
- query using XSP and ESQL. We will recreate part of that example
- below using the same HSQL database, which should be automatically
- configured and populated with data in the default build. If you
- find that you do not have that database set up, see the ESQL XSP
- sample for instructions on setting the datasource up. Do note that
- this specific task is handled in the ESQL XSP example in just a few
- lines of code. If your task is really this simple, there may be no
- need to create your own generator.</p>
- <source xml:space="preserve"><![CDATA[import org.apache.cocoon.generation.ComposerGenerator;
- import org.apache.avalon.framework.component.ComponentManager;
- import org.apache.avalon.framework.component.ComponentException;
- import org.apache.avalon.framework.component.ComponentSelector;
- import org.apache.avalon.excalibur.datasource.DataSourceComponent;
- import org.apache.cocoon.environment.SourceResolver;
- import org.apache.avalon.framework.parameters.Parameters;
- import org.apache.cocoon.environment.ObjectModelHelper;
- import org.apache.cocoon.environment.Request;
- import org.apache.cocoon.caching.Cacheable;
- import org.apache.cocoon.caching.CacheValidity;
- import org.apache.cocoon.ProcessingException;
- import org.xml.sax.ContentHandler;
- import org.xml.sax.SAXException;
- import org.xml.sax.helpers.AttributesImpl;
-
- import java.sql.*;
- import java.util.Map;
- import java.util.Date;
- import org.apache.avalon.framework.activity.Disposable;
-
- public class EmployeeGeneratorExample extends ComposerGenerator
- implements Cacheable, Disposable
- {
-
- public void dispose() {
- super.dispose();
- manager.release(datasource);
- datasource = null;
- }
-
- public void recycle() {
- myAttr.clear();
- super.recycle();
- }
-
- public void setup(SourceResolver resolver, Map objectModel,
- String src, Parameters par) {
- // Not neeed for this example, but you would get request
- // and/or sitemap parameters here.
- }
-
-
- public void compose(ComponentManager manager)
- throws ComponentException{
- super.compose(manager);
- ComponentSelector selector = (ComponentSelector)
- manager.lookup(DataSourceComponent.ROLE + "Selector");
- this.datasource = (DataSourceComponent) selector.select("personnel");
- }
-
- public void generate()
- throws SAXException, ProcessingException {
- try {
-
- Connection conn = this.datasource.getConnection();
- Statement stmt = conn.createStatement();
-
- ResultSet res = stmt.executeQuery(EMPLOYEE_QUERY);
-
- //open the SAX event stream
- contentHandler.startDocument();
- myAttr.addAttribute("","date","date","",
- (new Date()).toString());
- //open root element
- contentHandler.startElement("","content",
- "content",myAttr);
-
-
- String currentDept = "";
- boolean isFirstRow = true;
- boolean moreRowsExist = res.next() ? true : false;
-
- while (moreRowsExist) {
- String thisDept = attrFromDB(res, "name");
- if (!thisDept.equals(currentDept)) {
- newDept(res,thisDept,isFirstRow);
- currentDept = thisDept;
- }
- addEmployee(res,attrFromDB(res,"id"),
- attrFromDB(res,"empName"));
- isFirstRow = false;
-
- if (!res.next()) {
- endDept();
- moreRowsExist = false;
- }
- }
-
- //close root element
- contentHandler.endElement("","content","content");
- //close the SAX event stream
- contentHandler.endDocument();
-
- res.close();
- stmt.close();
- conn.close();
- } catch (SQLException e) {
- throw new ProcessingException(e);
- }
- }
-
- public long generateKey()
- {
- // Default non-caching behaviour. We will implement this later.
- return 0;
- }
-
- public CacheValidity generateValidity()
- {
- // Default non-caching behaviour. We will implement this later.
- return null;
- }
-
-
- private DataSourceComponent datasource;
- private AttributesImpl myAttr = new AttributesImpl();
-
- private String EMPLOYEE_QUERY =
- "SELECT department.name, employee.id, employee.name as empName " +
- "FROM department, employee " +
- "WHERE department.id = employee.department_id ORDER BY department.name";
-
- private void endDept() throws SAXException {
- contentHandler.endElement("","dept","dept");
- }
-
- private void newDept(ResultSet res, String dept, boolean isFirstRow)
- throws SAXException {
- if (!isFirstRow) {
- endDept();
- }
- myAttr.clear();
- myAttr.addAttribute("","name","name","",dept);
- contentHandler.startElement("","dept","dept",myAttr);
- }
-
- private void addEmployee(ResultSet res, String id, String name)
- throws SAXException {
- myAttr.clear();
- myAttr.addAttribute("","id","id","",id);
- contentHandler.startElement("","employee","employee",myAttr);
- contentHandler.characters(name.toCharArray(),0,name.length());
- contentHandler.endElement("","employee","employee");
- }
-
- private String attrFromDB(ResultSet res, String column)
- throws SQLException {
- String value = res.getString(column);
- return (res.wasNull())?"":value;
- }
-
- }]]></source></s3>
- <s3 title="Compile and Test">
- <p>To compile this, you will now need the following on your classpath:
- <code>avalon-excalibur.jar, avalon-framework.jar, cocoon.jar,
- xml-apis.jar</code> (using whatever names they have in your
- distribution). When you compile this, you may receive some
- deprecation warnings. Do not worry about them - we will discuss
- that later.</p>
- <p>To test it, copy it over to your <code>WEB-INF\classes\</code>
- directory as before and add something like the following to your
- <code>sitemap.xmap</code> ...</p>
- <source xml:space="preserve"><![CDATA[...
- <map:generator name="employee" src="EmployeeGeneratorExample"/>
- ...
- <map:match pattern="employee.xml">
- <map:generate type="employee"/>
- <map:serialize type="xml"/>
- </map:match>
- ...]]></source>
- </s3>
-
- <s3 title="New Concepts">
- <s4 title="Composable and Disposable">
- <p>We've implemented the Avalon lifecycle interfaces Composable and
- Disposable. When Cocoon starts up (which happens when the servlet
- container starts up) the <code>ComponentManager</code> will call
- <code>compose(ComponentManager m)</code> for our component as it works
- its way through all the components declared in the sitemap. The handle
- to <code>ComponentManager</code> is used to look up any other Avalon
- components that we need. Lookups happen in an abstracted way using a
- ROLE which enables us to change out implementations of each component
- without affecting previously written code. Our generator's ROLE by the
- way was defined in the <code>Generator</code> interface. </p>
- <p>Similarly, when this instance of our generator is disposed of by the
- container, it will call the <code>dispose()</code> method to allow us to
- clean up any resources we held on to between invocations. Note that
- components can be pooled by the container. If we thought that our employee
- generator was going to see a lot of traffic, we might change its definition
- at the top of sitemap.xmap to include attributes like <code>pool-grow="2"
- pool-max="16" pool-min="2"</code> so that multiple overlapping requests
- could be serviced without a log jam.</p>
- </s4>
- <s4 title="Datasource">
- <p>We look up our HSQL database here by its name given in cocoon.xconf.
- If we had multiple datasources (say a backup development database and
- a live one), we could determine which one to use based on a simple
- configuration parameter in sitemap.xmap. We could get at configuration
- parameters using the Avalon interface <code>Configurable</code>.</p>
- <note>Notice that we wait until generate() to request our connection
- from the pool - as we should. The problem is that we lose the benefit
- of using prepared statements since they would be destroyed when we
- returned the instance to the pool. At present, the implementation of
- org.apache.avalon.excalibur.datasource.DataSourceComponent does not
- support the pooling of statements.</note>
- </s4>
- <s4 title="Caching">
- <fixme author="open">Need more content here, or links to other docs.</fixme>
- <note>FIXME: This is still coming.</note>
- <p>Introduce new code to implement Caching, discuss basic logic, and
- deprecation/move to Avalon. I could use some help here from Carsten,
- or someone who can quickly give an overview of the changes and plan.
- </p>
- </s4>
- </s3>
- </s2>
- </s1>
- </body>
- </document>
-
-