home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / xampp / xampp-cocoon-addon-1.4.9-installer.exe / tutorial-generator.xml < prev    next >
Encoding:
Extensible Markup Language  |  2004-07-12  |  32.1 KB  |  791 lines

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!--
  3.   Copyright 1999-2004 The Apache Software Foundation
  4.  
  5.   Licensed under the Apache License, Version 2.0 (the "License");
  6.   you may not use this file except in compliance with the License.
  7.   You may obtain a copy of the License at
  8.  
  9.       http://www.apache.org/licenses/LICENSE-2.0
  10.  
  11.   Unless required by applicable law or agreed to in writing, software
  12.   distributed under the License is distributed on an "AS IS" BASIS,
  13.   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.   See the License for the specific language governing permissions and
  15.   limitations under the License.
  16. -->
  17. <!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.0//EN" "document-v10.dtd">
  18. <document>
  19.    <header>
  20.       <title>Write a Custom Generator</title>
  21.       <version>0.3</version>
  22.       <authors>
  23.          <person name="Geoff Howard" email="javageoff@yahoo.com" />
  24.       </authors>
  25.    </header>
  26.  
  27.    <body>
  28.       <s1 title="Introduction">
  29.          <p>This Tutorial describes the steps necessary to write a basic Cocoon
  30.           generator. Starting with a quick "Hello World" example and
  31.           progressing to slightly more involved examples should give a good
  32.           start to those whose applications call for extending Cocoon with a
  33.           custom generator.</p>
  34.  
  35.          <p>The intention is to provide:</p>
  36.  
  37.          <ul>
  38.             <li>the basics of creating SAX events in a C2 generator</li>
  39.  
  40.             <li>a little understanding of the Avalon container contract as it
  41.              relates to C2 generators</li>
  42.  
  43.             <li>a little understanding of the factors that would influence
  44.              the decision about which xxxGenerator to extend</li>
  45.          </ul>
  46.  
  47.          <s2 title="Purpose">
  48.            <p>The flexibility to extend the basic "Out of the box"
  49.             functionality of Cocoon will be an important feature for Cocoon's
  50.             viability as a broadly used application framework. Though the
  51.             documentation on 
  52.             <link href="../developing/extending.html">"Extending Cocoon"</link>
  53.             (at least at this writing) seems to have a hard time imagining
  54.             applications for custom generators outside of the bizarre, I
  55.             imagine several scenarios which could call for it:</p>
  56.  
  57.             <ul>
  58.               <li>A datasource as yet undeveloped in Cocoon (e.g. event
  59.                logs)</li>
  60.  
  61.               <li>Database driven applications for which XSP is either too
  62.                awkward or holds too many performance questions. The need for
  63.                high scalability will drive some (such as myself) to seek
  64.                optimization in custom generators that just do not seem
  65.                reasonable to expect out of the auto-generated code that XSPs
  66.                produce. The current 
  67.                <link href="../performancetips.html">Performance Tips</link>
  68.                documentation seems to lead in this direction.</li>
  69.  
  70.                <li>Customized control over the caching behaviour if not
  71.                 provided for by other means.</li>
  72.             </ul>
  73.          </s2>
  74.  
  75.          <s2 title="Important">
  76.            <p>There are other options that should be considered before
  77.             settling on a new generator. One notable consideration is the
  78.             option of writing a Source that would fit your needs. See 
  79.             <link href="http://marc.theaimsgroup.com/?t=102571404500001&r=1&w=2">this discussion</link>
  80.  
  81.             from the mailing list for an introduction to the idea. Of course,
  82.             XSP should be considered - I have not seen any performance
  83.             comparisons that quantify the benefit that can be had from a
  84.             custom generator. Finally, be sure you understand the purpose and
  85.             capabilities of all current standard Generators, as well as those
  86.             in the scratchpad (for instance, there is a
  87.             <code>TextParserGenerator</code> in the scratchpad at the moment
  88.             which may be configurable enough to process the event log need
  89.             mentioned above). Cocoon is a rapidly developing technology that
  90.             may have anticipated your need. Because the documentation lags
  91.             behind development, you may find more by examining the source
  92.             directory and searching the 
  93.             <link href="http://cocoon.apache.org/community/mail-archives.html">mail archives</link>
  94.             for applicable projects.</p>
  95.          </s2>
  96.  
  97.          <s2 title="Intended Audience">
  98.            <p>This Tutorial is aimed at users who have developed an
  99.             understanding of the basics of Cocoon and have a need to begin
  100.             extending it for their own purposes, or desire a deeper
  101.             understanding of what goes on under the hood.</p>
  102.          </s2>
  103.  
  104.          <s2 title="Prerequisites">
  105.             <p>Generator developers should have:</p>
  106.  
  107.             <ul>
  108.               <li>Read 
  109.               <link href="../userdocs/concepts/index.html">Cocoon Concepts</link>
  110.                , as well as 
  111.                <link href="../developing/extending.html">Extending Cocoon</link>
  112.  
  113.                , and the broad overview of 
  114.                <link href="../developing/avalon.html">Avalon</link>
  115.  
  116.                , the framework upon which Cocoon is built.</li>
  117.  
  118.                <li>An installed version of Cocoon if you want to follow the
  119.                 examples yourself (obviously).</li>
  120.  
  121.                <li>A good understanding of Java.</li>
  122.                <li>Java SDK (1.2 or later) "installed".</li>
  123.             </ul>
  124.          </s2>
  125.       </s1>
  126.  
  127.       <s1 title="Diving In">
  128.          <p>Let us start with a simple "Hello World" example:</p>
  129.  
  130.          <s2 title="Simple Example">
  131.            <p>Our goal will be to build the following document (or, more to
  132.             the point, the SAX events that would correspond to this document).
  133.            </p>
  134.  
  135. <source xml:space="preserve">
  136. <![CDATA[<example>Hello World!</example>]]>
  137. </source>
  138.  
  139.            <p>An example of code that will send the correct SAX events down
  140.             the pipeline:</p>
  141.  
  142. <source xml:space="preserve">
  143. <![CDATA[
  144. import org.apache.cocoon.generation.AbstractGenerator;
  145. import org.xml.sax.helpers.AttributesImpl;
  146. import org.xml.sax.SAXException;
  147.  
  148. public class HelloWorldGenerator extends AbstractGenerator 
  149. {
  150.  
  151.     AttributesImpl emptyAttr = new AttributesImpl();
  152.  
  153.     /**
  154.      * Override the generate() method from AbstractGenerator.
  155.      * It simply generates SAX events using SAX methods.  
  156.      * I haven't done the comparison myself, but this 
  157.      * has to be faster than parsing them from a string.
  158.      */
  159.  
  160.     public void generate() throws SAXException
  161.     
  162.     {
  163.        
  164.       // the org.xml.sax.ContentHandler is inherited 
  165.       // through org.apache.cocoon.xml.AbstractXMLProducer 
  166.  
  167.       contentHandler.startDocument();
  168.       
  169.       contentHandler.startElement("", "example", "example", emptyAttr);
  170.       
  171.       contentHandler.characters("Hello World!".toCharArray(),0,
  172.                                   "Hello World!".length());
  173.  
  174.       contentHandler.endElement("","example", "example");
  175.  
  176.       contentHandler.endDocument();
  177.  
  178.     }
  179. }
  180. ]]></source>
  181.  
  182.            <p>So, the basic points are that we extend
  183.            <code>AbstractGenerator</code>, override its generate() method,
  184.            call the relevant SAX methods on the contentHandler (inherited
  185.            from <code>AbstractGenerator</code>) to start, fill and end the
  186.            document. For information on the SAX api, see 
  187.             <link href="http://www.saxproject.org/">www.saxproject.org</link>
  188.             </p>
  189.  
  190.             <note>A performance tip might be to keep an empty instance of
  191.              <code>AttributesImpl</code> around to reuse for each element
  192.              with no attributes. Also, the characters(char[] chars, int start,
  193.              int end) begs to be overloaded with a version like 
  194.             <code>characters(String justPutTheWholeThingIn)</code>
  195.  
  196.             that handles the conversion to a character array and assumes you
  197.             want from beginning to end, as is done in
  198.             <code>org.apache.cocoon.generation.AbstractServerPage</code>.
  199.             If you are not using namespaces, it is easy to imagine overloaded
  200.             convenience implementations of the other SAX methods as well.
  201.             You will probably want to set up a convenient BaseGenerator with
  202.             helpers like this and extend it for your real Generators.</note>
  203.  
  204.             <s3 title="What to Extend?">
  205.               <p>How did we choose to extend <code>AbstractGenerator</code>?
  206.                Generators are defined by the
  207.                <code>org.apache.cocoon.generation.Generator</code> interface.
  208.                The only direct implementation of this of interest to us is
  209.                <code>AbstractGenerator</code>, which gives a basic level of
  210.                functionality. Another option would have been
  211.                <code>ComposerGenerator</code>, which would give us the added
  212.                functionality of implenting the Avalon interface 
  213.                <code>Composable</code>
  214.  
  215.                , which would signal the container that handles all the
  216.                components including our generator to give us a handle back to
  217.                the <code>ComponentManager</code>
  218.                during the startup of the container. If we needed to lookup a
  219.                pooled database connection, or some other standard or custom
  220.                Cocoon component, this is what we would do. Most of the out
  221.                of the box Generators extend <code>ComposerGenerator</code>.
  222.                Other abstract Generators you may choose to extend include the
  223.                poorly named (IMHO) <code>ServletGenerator</code>
  224.  
  225.                , and <code>AbstractServerPage</code>
  226.                . While these both introduce functionality specific to their
  227.                eventual purpose - the JSP and XSP generators, they do make a
  228.                convenient starting place for many other Generators.</p>
  229.             </s3>
  230.  
  231.             <s3 title="Running The Sample">
  232.               <p>In order to run this sample, you will need to compile the code,
  233.                deploy it into the cocoon webapp, and modify the sitemap to
  234.                declare our generator and allow access to it via a pipeline.</p>
  235.  
  236.                <s4 title="Compile">
  237.                 <p>Save this source as <code>HelloWorldGenerator.java</code>
  238.                  and compile it using</p>
  239.  
  240. <source>javac -classpath %PATH_TO_JARS%\cocoon.jar;%PATH_TO_JARS%\xml-apis.jar
  241.    HelloWorldGenerator.java</source>
  242.  
  243.                  <p>Unfortunately for me, the exact name of your cocoon and
  244.                    xml-apis jars may vary with exactly which distribution,
  245.                    or CVS version you are using, since the community has taken
  246.                    to appending dates or versions at the end of the jar name
  247.                    to avoid confusion. Be sure to find the correct name on
  248.                    your system and substitute it in the classpath. Also, you
  249.                    have several options on where to find jars. If you have a
  250.                    source version that you built yourself, you may want to
  251.                    point to <code>lib\core\</code> for them. If you have only
  252.                    the binary version, you can find them in
  253.                    <code>WEB-INF\lib\</code></p>
  254.                </s4>
  255.  
  256.                <s4 title="Deploy">
  257.                   <p>Simply copy the class file into the
  258.                   <code>%TOMCAT_HOME%\webapps\cocoon\WEB-INF\classes</code>
  259.                   directory</p>
  260.  
  261.                   <note>If memory serves me, there have been occasional
  262.                    classloading problems in the past that may affect
  263.                    classloading. If your compiled classes are not recognized
  264.                    in the classes directory, try 
  265.                   <code>jar</code>-ing them up and place them in
  266.                   <code>WEB-INF\lib\</code> instead. That is probably where
  267.                   your real generators would go anyway - with a whole package
  268.                   of all your custom classes in one jar.</note>
  269.                </s4>
  270.  
  271.                <s4 title="Sitemap Modifications">
  272.                   <p>You need to do two things: in the 
  273.                   <code>map:generators</code>
  274.  
  275.                   section, add an element for your class:</p>
  276.  
  277.                   <source><![CDATA[<map:generator name="helloWorld" src="HelloWorldGenerator"/>]]></source>
  278.  
  279.                   <p>Then add a pipeline to sitemap.xmap which uses it:</p>
  280.  
  281. <source xml:space="preserve">
  282. <![CDATA[...
  283.    <map:match pattern="heyThere.xml">
  284.       <map:generate type="helloWorld"/>
  285.       <map:serialize type="xml"/>
  286.    </map:match>
  287. ...]]>
  288. </source>
  289.  
  290.                   <p>And finally, our creation should be available at 
  291.                   <code>http://localhost:8080/cocoon/heyThere.xml</code>
  292.                   </p>
  293.  
  294.                   <p>Depending on your exact setup, you may need to restart
  295.                    Tomcat (or whatever your servlet container is) to get
  296.                    there.</p>
  297.  
  298.                   <note>Notice that the 
  299.                   <code>
  300.                      <![CDATA[<?xml version="1.0" encoding="UTF-8"?>]]>
  301.                   </code>
  302.  
  303.                   declaration was added for us by the xml serializer at the
  304.                   beginning. If you need to modify this, the generator is not
  305.                   the appropriate place. The default encoding of UTF-8 could
  306.                   be overridden with iso-8859-1 for example by specifying an 
  307.                   <code>
  308.                      <![CDATA[<encoding>iso-8859-1</encoding>]]>
  309.                   </code>
  310.  
  311.                   child parameter inside the declaration for the xml
  312.                   serializer in your sitemap.</note>
  313.                </s4>
  314.             </s3>
  315.          </s2>
  316.  
  317.          <s2 title="A Less Trivial Example">
  318.             <p>Moving on to a less trivial example, we will take some
  319.              information out of the Request, and construct a slightly more
  320.              involved document. This time, our goal will be the following
  321.              document:</p>
  322.  
  323. <source xml:space="preserve">
  324. <![CDATA[<doc>
  325. <uri>...</uri>
  326. <params>
  327.     <param value="...">...</param>
  328.     ...
  329. </params>
  330. <date>..</date>
  331. </doc>]]>
  332. </source>
  333.  
  334.             <p>The values of course will be filled in from the request, and
  335.              will depend on choices we make later.</p>
  336.  
  337. <source xml:space="preserve"><![CDATA[import org.apache.cocoon.generation.AbstractGenerator;
  338. import org.xml.sax.helpers.AttributesImpl;
  339. import org.xml.sax.SAXException;
  340.  
  341. // for the setup() method
  342. import org.apache.cocoon.environment.SourceResolver;
  343. import java.util.Map;
  344. import org.apache.avalon.framework.parameters.Parameters;
  345. import org.apache.cocoon.ProcessingException;
  346. import java.io.IOException;
  347.  
  348. // used to deal with the request parameters.
  349. import org.apache.cocoon.environment.ObjectModelHelper;
  350. import org.apache.cocoon.environment.Request;
  351. import java.util.Enumeration;
  352.  
  353. import java.util.Date;
  354.  
  355.  
  356. public class RequestExampleGenerator extends AbstractGenerator 
  357. {
  358.  
  359.     // Will be initialized in the setup() method and used in generate()
  360.     Request request = null;
  361.     Enumeration paramNames = null;
  362.     String uri = null;
  363.  
  364.     // We will use attributes this time.
  365.     AttributesImpl myAttr = new AttributesImpl();
  366.     AttributesImpl emptyAttr = new AttributesImpl();
  367.     
  368.    
  369.     public void setup(SourceResolver resolver, Map objectModel, 
  370.              String src, Parameters par)  
  371.          throws ProcessingException, SAXException, IOException 
  372.     {
  373.        super.setup(resolver, objectModel, src, par);
  374.        request = ObjectModelHelper.getRequest(objectModel);
  375.        paramNames = request.getParameterNames();
  376.        uri = request.getRequestURI();
  377.     } 
  378.  
  379.     /**
  380.      * Implement the generate() method from AbstractGenerator.
  381.      */
  382.  
  383.     public void generate() throws SAXException
  384.     {
  385.  
  386.       contentHandler.startDocument();
  387.       
  388.       contentHandler.startElement("", "doc", "doc", emptyAttr);
  389.  
  390.       // <uri> and all following elements will be nested inside the doc element
  391.       contentHandler.startElement("", "uri", "uri", emptyAttr);
  392.  
  393.       contentHandler.characters(uri.toCharArray(),0,uri.length());
  394.  
  395.       contentHandler.endElement("", "uri", "uri");
  396.       
  397.       contentHandler.startElement("", "params", "params", emptyAttr);
  398.          
  399.       while (paramNames.hasMoreElements())
  400.       {
  401.           // Get the name of this request parameter.
  402.           String param = (String)paramNames.nextElement();
  403.           String paramValue = request.getParameter(param);
  404.       
  405.           // Since we've chosen to reuse one AttributesImpl instance, 
  406.           // we need to call its clear() method before each use.  We 
  407.           // use the request.getParameter() method to look up the value 
  408.           // associated with the current request parameter.
  409.           myAttr.clear();
  410.           myAttr.addAttribute("","value","value","",paramValue);
  411.  
  412.           // Each <param> will be nested inside the containing <params> element.
  413.           contentHandler.startElement("", "param", "param", myAttr);
  414.           contentHandler.characters(param.toCharArray(),0,param.length());
  415.           contentHandler.endElement("","param", "param");
  416.       }
  417.             
  418.       contentHandler.endElement("","params", "params");
  419.  
  420.       contentHandler.startElement("", "date", "date", emptyAttr);
  421.  
  422.       String dateString = (new Date()).toString();
  423.       contentHandler.characters(dateString.toCharArray(),0,dateString.length());
  424.  
  425.       contentHandler.endElement("", "date", "date");
  426.       contentHandler.endElement("","doc", "doc");
  427.       contentHandler.endDocument();
  428.    }
  429.  
  430.    public void recycle() {
  431.       super.recycle();
  432.       this.request = null;
  433.       this.paramNames = null;
  434.       this.parNames = null;
  435.       this.uri = null;
  436.    }
  437. }]]></source>
  438.             <s3 title="Compile and Test">
  439.             <p>Save this code as
  440.                          <code>RequestExampleGenerator.java</code>
  441.                          and compile as before.  You will need to add both
  442.                          <code>avalon-framework.jar</code> and
  443.                          <code>avalon-excalibur.jar</code> to your classpath
  444.                         this time.  Besides finding the exact name of the jar
  445.                         as described above, you may now also have to ensure
  446.                         that you have the version of excalibur targeted to your
  447.             jvm version - there is currently a version for JDK 1.4
  448.                         and one for 1.2/1.3</p>
  449.             <p>For your sitemap, you will need to add a definition
  450.                         for this generator like 
  451. <code><![CDATA[<map:generator name="requestExample" src="RequestExampleGenerator"/>]]></code>
  452.                         and you will need a sitemap pipeline like:</p>
  453. <source xml:space="preserve"><![CDATA[<map:match pattern="howYouDoin.xml">
  454.     <map:generate type="requestExample"/>
  455.     <map:serialize type="xml"/>
  456. </map:match>]]>
  457. </source>    
  458.             <p>At this point, you should be able to access the
  459.                          example at
  460. <code>http://localhost:8080/cocoon/howYouDoin.xml?anyParam=OK&more=better</code></p>
  461.             </s3>
  462.             <s3 title="New Concepts">
  463.              <s4 title="Lifecycle">
  464.                <p>First, notice that we now override the
  465.                 <code>setup(...)</code> and <code>recycle()</code> methods
  466.                 defined in <code>AbstractGenerator</code>.
  467.                 The <code>ComponentManager</code> that handles the lifecycle of
  468.                 all <code>component</code>s in Cocoon, calls
  469.                 <code>setup(..)</code> before each new call to
  470.                 <code>generate()</code> to give the Generator information
  471.                 about the current request and its environment, and calls
  472.                 recycle() when it is done to enable it to clean up resources
  473.                 as appropriate. Our example uses only the
  474.                 <code>objectModel</code> which abstracts the Request,
  475.                 Response, and Context. We get a reference to the Request
  476.                 wrapper, and obtain an <code>Enumeration</code> of all the
  477.                 GET/POST parameters available.</p>
  478.  
  479.                <p>The <code>src</code> and <code>SourceResolver</code> are
  480.                 provided to enable us to look up and use whatever source is
  481.                 specified in the pipeline setup. Had we specified 
  482. <code><![CDATA[<map:generate type="helloWorld" src="someSourceString"/>]]></code>
  483.                 we would have used the <code>SourceResolver</code> to work
  484.                 with "someSourceString", whether it be a file, or url, etc.</p>
  485.  
  486.                <p>We are also given a 
  487.                <code>Parameters</code> reference which we would use to obtain
  488.                 any parameter names and values which are children elements of
  489.                 our <code>map:generate</code> element in the pipeline.</p>
  490.  
  491. <note>It may be good practice to abstract the source of your parameters so
  492. that they do not have to come from the Request object. For instance, the
  493. following code would allow us to abstract the origin of two parameters, param1
  494. and param2:</note>
  495.  
  496. <source xml:space="preserve"><![CDATA[In RequestExampleGenerator.java, 
  497. ...
  498. String param1 = null;
  499. String param2 = null;
  500. ...
  501.    public void setup(SourceResolver resolver, Map objectModel, 
  502.                String src, Parameters par)  
  503.            throws ProcessingException, SAXException, IOException 
  504.    {
  505.         ... 
  506.         param1 = par.getParameter("param1");
  507.         param2 = par.getParameter("param2");
  508.    } 
  509.  
  510. and in sitemap.xmap, 
  511.  
  512. ...
  513. <map:match pattern="abstractedParameters.xml"/>
  514.     <map:act type="request">
  515.       <map:parameter name="parameters" value="true"/>
  516.       <map:generate type="requestExample">
  517.         <parameter name="param1" value="{visibleName1}"/>
  518.         <parameter name="param2" value="{visibleName2}"/>
  519.       </map:generate>
  520.     </map:act>
  521. </map:match>
  522. ...]]></source>
  523.             <p>As you can see, we have also hidden the internal
  524.                     name from the outside world who will use
  525.                     <code>?visibleName1=foo&visibleName2=bar</code>
  526.                     </p>
  527.           </s4>
  528.           <s4 title="Nested Elements">
  529.             <p>In this example, nested elements are created simply
  530.             by nesting complete
  531.             <code>startElement()</code>/<code>endElement</code> 
  532.             pairs within each other. If we had a logic failure in our code and 
  533.             sent non-wellformed xml events down the pipeline, nothing in our 
  534.             process would complain (try it!). Of course, any transformers later 
  535.             in the pipeline would behave in an unpredictable manner.</p>
  536.           </s4>
  537.           <s4 title="Attributes">
  538.             <p>Finally, we've introduced the use of attributes.
  539.             We chose to 
  540.             employ one <code>attributesImpl</code>, clearing it before each 
  541.             element.  Multiple attributes for an element would simply be added 
  542.             by repeated calls to <code>addAttribute</code>.</p>
  543.           </s4>
  544.             </s3>
  545.             <s3 title="A Lesson">
  546.              <p>Before moving on, it is worth noting that
  547.              after all this work, there is already a generator provided with 
  548.              Cocoon which does much of what we have accomplished here
  549.              - <code>org.apache.cocoon.generation.RequestGenerator</code>
  550.              which in the default configuration is probably available at
  551.              <code>http://localhost:8080/cocoon/request</code></p>
  552.              </s3>
  553.          </s2>
  554.  
  555.          <s2 title="Moving On">
  556.          <p>From here, we will move on to cover handling ugly pseudo-xml
  557.           (like real world html) with CDATA blocks, employing some of the
  558.           Avalon lifecycle method callbacks (Composable/Disposable), Database
  559.           access, and Caching.</p>
  560.          <s3 title="The Employee SQL Example Reworked">
  561.           <p>In the samples included with Cocoon, there is an example of a SQL
  562.            query using XSP and ESQL.  We will recreate part of that example
  563.            below using the same HSQL database, which should be automatically
  564.            configured and populated with data in the default build. If you
  565.            find that you do not have that database set up, see the ESQL XSP
  566.            sample for instructions on setting the datasource up.  Do note that
  567.            this specific task is handled in the ESQL XSP example in just a few
  568.            lines of code.  If your task is really this simple, there may be no
  569.            need to create your own generator.</p>
  570. <source xml:space="preserve"><![CDATA[import org.apache.cocoon.generation.ComposerGenerator;
  571. import org.apache.avalon.framework.component.ComponentManager;
  572. import org.apache.avalon.framework.component.ComponentException;
  573. import org.apache.avalon.framework.component.ComponentSelector;
  574. import org.apache.avalon.excalibur.datasource.DataSourceComponent;
  575. import org.apache.cocoon.environment.SourceResolver;
  576. import org.apache.avalon.framework.parameters.Parameters;
  577. import org.apache.cocoon.environment.ObjectModelHelper;
  578. import org.apache.cocoon.environment.Request;
  579. import org.apache.cocoon.caching.Cacheable;
  580. import org.apache.cocoon.caching.CacheValidity;
  581. import org.apache.cocoon.ProcessingException;
  582. import org.xml.sax.ContentHandler;
  583. import org.xml.sax.SAXException;
  584. import org.xml.sax.helpers.AttributesImpl;
  585.  
  586. import java.sql.*;
  587. import java.util.Map;
  588. import java.util.Date;
  589. import org.apache.avalon.framework.activity.Disposable;
  590.  
  591. public class EmployeeGeneratorExample extends ComposerGenerator
  592.    implements Cacheable, Disposable
  593. {
  594.  
  595.     public void dispose() {
  596.         super.dispose();
  597.         manager.release(datasource);
  598.         datasource = null;
  599.     }
  600.  
  601.     public void recycle() {
  602.         myAttr.clear();
  603.         super.recycle();
  604.     }
  605.  
  606.     public void setup(SourceResolver resolver, Map objectModel,
  607.                           String src, Parameters par) {
  608.         // Not neeed for this example, but you would get request
  609.             // and/or sitemap parameters here.
  610.     }
  611.  
  612.     
  613.     public void compose(ComponentManager manager) 
  614.     throws ComponentException{
  615.       super.compose(manager);
  616.       ComponentSelector selector = (ComponentSelector)
  617.             manager.lookup(DataSourceComponent.ROLE + "Selector");
  618.       this.datasource = (DataSourceComponent) selector.select("personnel");
  619.     }
  620.  
  621.     public void generate() 
  622.     throws SAXException, ProcessingException {
  623.         try {
  624.  
  625.             Connection conn = this.datasource.getConnection();
  626.             Statement stmt = conn.createStatement(); 
  627.             
  628.             ResultSet res = stmt.executeQuery(EMPLOYEE_QUERY);
  629.             
  630.                         //open the SAX event stream
  631.             contentHandler.startDocument();
  632.             myAttr.addAttribute("","date","date","",
  633.                             (new Date()).toString());
  634.                         //open root element
  635.             contentHandler.startElement("","content",
  636.                             "content",myAttr);
  637.  
  638.             
  639.             String currentDept = "";
  640.             boolean isFirstRow = true;
  641.             boolean moreRowsExist = res.next() ? true : false;
  642.             
  643.             while (moreRowsExist) {
  644.                 String thisDept = attrFromDB(res, "name");
  645.                 if (!thisDept.equals(currentDept)) {
  646.                 newDept(res,thisDept,isFirstRow);
  647.                 currentDept = thisDept;
  648.                 }
  649.                 addEmployee(res,attrFromDB(res,"id"),
  650.                               attrFromDB(res,"empName"));
  651.                 isFirstRow = false;
  652.                 
  653.                 if (!res.next()) {
  654.                 endDept();
  655.                 moreRowsExist = false;
  656.                 }
  657.             }
  658.             
  659.                         //close root element
  660.             contentHandler.endElement("","content","content");
  661.                         //close the SAX event stream
  662.             contentHandler.endDocument();
  663.             
  664.             res.close();
  665.             stmt.close();
  666.             conn.close();
  667.           } catch (SQLException e) { 
  668.               throw new ProcessingException(e); 
  669.           } 
  670.     }
  671.  
  672.     public long generateKey()
  673.     {
  674.         // Default non-caching behaviour. We will implement this later.
  675.         return 0;
  676.     }
  677.  
  678.     public CacheValidity generateValidity()
  679.     {
  680.         // Default non-caching behaviour. We will implement this later.
  681.         return null;
  682.     }
  683.  
  684.  
  685.   private DataSourceComponent datasource;
  686.   private AttributesImpl myAttr = new AttributesImpl();
  687.   
  688.   private String EMPLOYEE_QUERY = 
  689.   "SELECT department.name, employee.id, employee.name as empName " +
  690.   "FROM department, employee " + 
  691.   "WHERE department.id = employee.department_id  ORDER BY department.name";
  692.   
  693.   private void endDept() throws SAXException {
  694.       contentHandler.endElement("","dept","dept");
  695.   }
  696.   
  697.   private void newDept(ResultSet res, String dept, boolean isFirstRow)
  698.       throws SAXException {
  699.     if (!isFirstRow) {
  700.     endDept();
  701.     }
  702.     myAttr.clear();
  703.     myAttr.addAttribute("","name","name","",dept);
  704.     contentHandler.startElement("","dept","dept",myAttr);
  705.   }    
  706.   
  707.   private void addEmployee(ResultSet res, String id, String name)
  708.       throws SAXException {
  709.       myAttr.clear();
  710.       myAttr.addAttribute("","id","id","",id);
  711.       contentHandler.startElement("","employee","employee",myAttr);
  712.       contentHandler.characters(name.toCharArray(),0,name.length());
  713.       contentHandler.endElement("","employee","employee");
  714.   }
  715.   
  716.   private String attrFromDB(ResultSet res, String column)
  717.       throws SQLException {
  718.           String value = res.getString(column);
  719.         return (res.wasNull())?"":value;
  720.   }
  721.  
  722. }]]></source></s3>
  723.     <s3 title="Compile and Test">
  724.     <p>To compile this, you will now need the following on your classpath:
  725.          <code>avalon-excalibur.jar, avalon-framework.jar, cocoon.jar,
  726.          xml-apis.jar</code> (using whatever names they have in your
  727.          distribution).  When you compile this, you may receive some
  728.          deprecation warnings.  Do not worry about them - we will discuss 
  729.          that later.</p>
  730.     <p>To test it, copy it over to your <code>WEB-INF\classes\</code>
  731.          directory as before and add something like the following to your
  732.          <code>sitemap.xmap</code> ...</p>
  733.     <source xml:space="preserve"><![CDATA[...
  734. <map:generator name="employee" src="EmployeeGeneratorExample"/>
  735. ...
  736. <map:match pattern="employee.xml">
  737.     <map:generate type="employee"/>
  738.     <map:serialize type="xml"/>
  739. </map:match>
  740. ...]]></source>
  741.       </s3>
  742.  
  743.       <s3 title="New Concepts">
  744.     <s4 title="Composable and Disposable">
  745.     <p>We've implemented the Avalon lifecycle interfaces Composable and 
  746.     Disposable.  When Cocoon starts up (which happens when the servlet 
  747.     container starts up) the <code>ComponentManager</code> will call 
  748.     <code>compose(ComponentManager m)</code> for our component as it works 
  749.     its way through all the components declared in the sitemap.  The handle 
  750.     to <code>ComponentManager</code> is used to look up any other Avalon 
  751.     components that we need.  Lookups happen in an abstracted way using a 
  752.     ROLE which enables us to change out implementations of each component 
  753.     without affecting previously written code.  Our generator's ROLE by the 
  754.     way was defined in the <code>Generator</code> interface.  </p>
  755.     <p>Similarly, when this instance of our generator is disposed of by the 
  756.     container, it will call the <code>dispose()</code> method to allow us to 
  757.     clean up any resources we held on to between invocations.  Note that 
  758.     components can be pooled by the container.  If we thought that our employee 
  759.     generator was going to see a lot of traffic, we might change its definition 
  760.     at the top of sitemap.xmap to include attributes like <code>pool-grow="2" 
  761.     pool-max="16" pool-min="2"</code> so that multiple overlapping requests 
  762.     could be serviced without a log jam.</p>
  763.     </s4>
  764.     <s4 title="Datasource">
  765.     <p>We look up our HSQL database here by its name given in cocoon.xconf. 
  766.     If we had multiple datasources (say a backup development database and 
  767.     a live one), we could determine which one to use based on a simple 
  768.     configuration parameter in sitemap.xmap.  We could get at configuration 
  769.     parameters using the Avalon interface <code>Configurable</code>.</p>
  770.     <note>Notice that we wait until generate() to request our connection 
  771.     from the pool - as we should.  The problem is that we lose the benefit 
  772.     of using prepared statements since they would be destroyed when we 
  773.     returned the instance to the pool.  At present, the implementation of 
  774.     org.apache.avalon.excalibur.datasource.DataSourceComponent does not 
  775.     support the pooling of statements.</note> 
  776.     </s4>
  777.     <s4 title="Caching">
  778. <fixme author="open">Need more content here, or links to other docs.</fixme>
  779. <note>FIXME: This is still coming.</note>
  780.     <p>Introduce new code to implement Caching, discuss basic logic, and
  781.     deprecation/move to Avalon. I could use some help here from Carsten,
  782.     or someone who can quickly give an overview of the changes and plan.
  783.     </p>
  784.     </s4>
  785.     </s3>
  786.      </s2>
  787.       </s1>
  788.    </body>
  789. </document>
  790.  
  791.