You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by st...@locus.apache.org on 2000/08/31 00:28:47 UTC

cvs commit: xml-cocoon/xdocs how-it-works.xml docs-book.xml installing.xml livesites.xml site-book.xml

stefano     00/08/30 15:28:46

  Modified:    xdocs    docs-book.xml installing.xml livesites.xml
                        site-book.xml
  Added:       xdocs    how-it-works.xml
  Log:
  docs updates
  
  Revision  Changes    Path
  1.12      +1 -0      xml-cocoon/xdocs/docs-book.xml
  
  Index: docs-book.xml
  ===================================================================
  RCS file: /home/cvs/xml-cocoon/xdocs/docs-book.xml,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- docs-book.xml	2000/08/16 15:30:28	1.11
  +++ docs-book.xml	2000/08/30 22:28:41	1.12
  @@ -12,6 +12,7 @@
     <page id="infrastructure" label="Infrastructure" source="infrastructure.xml"/>
     <page id="guide" label="User Guide" source="guide.xml"/>
     <page id="dynamic" label="Dynamic Content" source="dynamic.xml"/>
  +  <page id="how-it-works" label="How it works" source="how-it-works.xml"/>
   <separator/>
     <page id="xsp" label="XSP Processor" source="xspprocessor.xml"/>
     <page id="sqltaglib" label="SQL XSP Taglib" source="sqltaglib.xml"/>
  
  
  
  1.22      +109 -115  xml-cocoon/xdocs/installing.xml
  
  Index: installing.xml
  ===================================================================
  RCS file: /home/cvs/xml-cocoon/xdocs/installing.xml,v
  retrieving revision 1.21
  retrieving revision 1.22
  diff -u -r1.21 -r1.22
  --- installing.xml	2000/08/23 22:52:45	1.21
  +++ installing.xml	2000/08/30 22:28:42	1.22
  @@ -193,121 +193,115 @@
     </s2>
   
     <s2 title="Installing Cocoon on Apache Tomcat">
  -    <p>
  -     Tomcat has two basic methods of locating Java classes for the runtime environment.
  -     The first is the overall classpath that Tomcat uses to run, and this is the
  -     classpath provided to Java classes that use constructs such as
  -     <code>Class.forName().newInstance()</code>.  The second classpath is
  -     associated with a specific context, and is somewhat analagous to the
  -     servlet zones used in Apache JServ (see section above).
  -    </p>
  -
  -    <p>
  -     Because the Cocoon framework utilizes <code>Class.forName()</code> and other
  -     dynamic instance handling techniques, the Cocoon classes need to have its
  -     classpath aware of the component classes used within the framework.  To do
  -     this, take all the required components (see above) and put them in your
  -     <em>&lt;Tomcat-Root&gt;/lib</em> directory.  This is the standard location
  -     for Tomcat core libraries. To ensure that Tomcat will use these, you need
  -     to edit the Tomcat startup file.
  -    </p>
  -
  -    <p>
  -     On Windows, this is <em>&lt;Tomcat-Root&gt;/tomcat.bat</em> and on
  -     Unix, this is <em>&lt;Tomcat-Root&gt;/tomcat.sh</em>.  In this
  -     file you must add all the component jar files to Tomcat's classpath.
  -    </p>
  -
  -    <note>
  -     the <code>cocoon.jar</code>
  -     package should be added to the servlet engine classpath as any other
  -     required package (as shown above).
  -    </note>
  -
  -    <p>
  -     Next you need to tell your context where Cocoon can find it's properties
  -     file, as well as to map Cocoon to XML document requests.
  -     Make sure you have a <em>web.xml</em> file in your context's
  -     <em>WEB-INF</em> directory (look in <code>src/WEB-INF/</code> to find a
  -     template <em>web.xml</em>).  This file specifies servlet mappings
  -     and initial arguments to servlets and looks like this:
  -    </p>
  -
  -<source><![CDATA[
  -<servlet>
  - <servlet-name>org.apache.cocoon.Cocoon</servlet-name>
  - <servlet-class>org.apache.cocoon.Cocoon</servlet-class>
  - <init-param>
  -  <param-name>properties</param-name>
  -  <param-value>
  -    [path-to-cocoon.properties]/cocoon.properties
  -  </param-value>
  - </init-param>
  -</servlet>
  -
  -<servlet-mapping>
  - <servlet-name>org.apache.cocoon.Cocoon</servlet-name>
  - <url-pattern>*.xml</url-pattern>
  -</servlet-mapping>
  -]]></source>
  -
  -    <p>
  -     Make sure you replaced the path to the Cocoon.properties file with the
  -     location of that file on your system, 
  -     <strong>relative to the context root</strong>. Even if you start the
  -     filename with a <code>/</code> it will still be relative to the context root.
  -    </p>
  -
  -    <note>
  -      Because Cocoon now (since version 1.7.3) uses getResource() in the
  -      Servlet API to read cocoon.properties, and has different instances for
  -      each servlet context, you need to put a copy of cocoon.properties in all
  -      of your servlet contexts that use Cocoon. (It is recommended to put it
  -      in the context's WEB-INF directory, or a password-protected directory, 
  -      to prevent anyone from being able to read it over the web.)
  -      However, you <strong>cannot</strong> use symbolic links on Unix to point 
  -      to a file outside of the current context, because Tomcat does not allow 
  -      it for security reasons.
  -    </note>
  -
  -    <note>
  -      the <code>cocoon.properties</code> file <strong>must</strong> be referenced 
  -      with relative paths in <code>WEB-INF/web.xml</code>, otherwise, Cocoon
  -      won't be able to locate it's properties and won't be able to start.
  -    </note>
  -    
  -    <p>
  -     Note that you should not need to change anything from the template
  -     properties file found in the distribution, but you must edit it for
  -     more complex operation. Please refer directly to the file that contains
  -     brief indications and comments on the different configurations, but you
  -     don't need to care about that at this point.
  -    </p>
  -
  -   <p>
  -    At this point, you should check if your system matches the global
  -    considerations about Cocoon properties. Usually, you might want to give
  -    the installation a try as it is and then read again that section if
  -    something goes wrong. Most installations don't need any changes to
  -    be operational.
  -   </p>
  -
  -   <p>
  -    If you have upgraded Cocoon from an older version and Cocoon won't
  -    initialize, either ensure that
  -    you are using the latest cocoon.properties - or, if you are have some
  -    non-standard properties in cocoon.properties which you need to keep,
  -    refer to the latest cocoon.properties to find out what changes need to
  -    be made, if any.
  -   </p>
  -
  -   <p>
  -    Everything should now be configured fine. Restart both Apache and Tomcat
  -    and try accessing the samples contained in the distribution to see
  -    Cocoon in action or the <code>/Cocoon.xml</code> page for Cocoon internal
  -    status.
  -   </p>
  -
  +      <p>
  +      To make Cocoon work with Tomcat, you must add a context to Tomcat that describes
  +      to Tomcat how to load Cocoon files. Then you must tell Apache to send 
  +      certain requests to Tomcat (and consequently Cocoon). Finally you must 
  +      provide the .xml files to be served by Cocoon. These steps are outlined below.
  +      </p>
  +
  +      <p>
  +      A context in Tomcat describes to Tomcat how and when to load a particular servlet
  +      and Cocoon is one such servlet. First we need to make sure that Tomcat knows how to
  +      load the Cocoon .jar files. To begin with, you must copy any .jar files from
  +      <code>$COCOON_HOME/lib</code> to <code>$TOMCAT_HOME/lib</code> that are necessary for Cocoon to run.
  +      In addition, you must copy <code>$COCOON_HOME/bin/cocoon.jar</code> to <code>$TOMCAT_HOME/lib</code>.
  +      </p>
  + 
  +     <p>
  +      In recent version of Tomcat under Unix, Tomcat will automatically detect any <code>.jar</code> files
  +      in the <code>$TOMCAT_HOME/lib</code> directory. But under Windows, you must explicitly add the new
  +      <code>.jar</code> files in the appropriate place in the file <code>$TOMCAT_ROOT/bin/tomcat.bat</code>.
  +     </p>
  + 
  +     <p>
  +      Next you must tell Tomcat about the new context which will run Cocoon requests. To do
  +      this edit the file <code>$TOMCAT_HOME/conf/server.xml</code> and add the following line:
  +     </p>
  + 
  + <source><![CDATA[
  + <Context path="/cocoon" docBase="webapps/cocoon" debug="0" reloadable="true" >
  + </Context>
  + ]]></source>
  + 
  +      <p>
  +      This tells Tomcat that requests that come in under that partial path "/cocoon" should
  +      be mapped to the context defined in the directory "webapps/cocoon". We will set that up
  +      shortly.
  +      </p>
  + 
  +     <p>
  +      Next we need to tell Apache to forward the same partial pathnames to Tomcat. This is done
  +      by editing the tomcat <code>.conf</code> file (it's called <code>tomcat-apache.conf</code> if you're using Tomcat 3.1 and
  +      Apache 1.3.12) in your Apache setup. Add the following lines:
  +     </p>
  + 
  + <source><![CDATA[
  + Alias /cocoon $TOMCAT_HOME/webapps/cocoon
  + <Directory "$TOMCAT_HOME/webapps/cocoon">
  +     Options Indexes FollowSymLinks
  + </Directory>
  + ApJServMount /cocoon /cocoon
  + <Location /cocoon/WEB-INF/ >
  +     AllowOverride None
  +     deny from all
  + </Location>
  + ]]></source>
  + 
  +      <p>
  +      This tells Apache to direct requests that come in under that partial path "/cocoon" to
  +      the directory under Tomcat (<code>$TOMCAT_HOME/webapps/cocoon</code>).
  +      </p>
  + 
  +     <p>
  +      Finally, we need to set up the actual context that we have defined and pointed requests
  +      to above. To do this, we need to create a new directory in webapps called cocoon. Then
  +      we need to make a sub-directory that describes to Tomcat how to map particular files to
  +      Cocoon, then we need to fill the sub-directory with our Cocoon source files (<code>.xml</code> files).
  +     </p>
  + 
  +     <p>
  +      First make a directory and its subdirectory:
  +     </p>
  + 
  + <source><![CDATA[
  + mkdir $TOMCAT_HOME/webapps/cocoon
  + mkdir $TOMCAT_HOME/webapps/cocoon/WEB-INF
  + ]]></source>
  + 
  +     <p>
  +      Next copy the template files from the Cocoon distribution:
  +     </p>
  + 
  + <source><![CDATA[
  + cp $COCOON_HOME/src/WEB-INF/web.xml $TOMCAT_HOME/webapps/cocoon/WEB-INF
  + cp $COCOON_HOME/conf/cocoon.properties $TOMCAT_HOME/webapps/cocoon/WEB-INF
  + ]]></source>
  + 
  +     <p>
  +      Next you need to edit the <code>$TOMCAT_HOME/webapps/WEB-INF/web.xml</code> file to point to the
  +      Cocoon properties file in the same directory. Do this by changing the text
  +      <code>conf/cocoon.properties</code> to <code>WEB-INF/cocoon-properties</code>. Note that this path is a relative
  +      path must be so. Don't try to use an absolute path here. It won't work. Also note that
  +      the web.xml file describes how to map .xml requests to the Cocoon servlet.
  +     </p>
  + 
  +     <p>
  +      Next you need to populate the cocoon context with source <code>.xml</code> files. For testing purposes
  +      you can just use the samples that come along with Cocoon.
  +     </p>
  + 
  +  <source><![CDATA[
  + cp -R $COCOON_HOME/samples $TOMCAT_HOME/webapps/cocoon/servlets
  +  ]]></source>
  + 
  +      <p>
  +      Finally, you need to stop Tomcat, stop Apache, then restart the two in order to make
  +      all of the new settings load. You should be able to access pages like 
  +      http://localhost/cocoon/Cocoon.xml and http://localhost/cocoon/servlets/index.xml
  +     </p>
  + 
  + 
      <note>
       Make sure that <code>xerces.jar</code> is located <strong>before</strong> 
       <code>xml.jar</code> otherwise Cocoon won't run. To do this, rename 
  
  
  
  1.9       +1 -0      xml-cocoon/xdocs/livesites.xml
  
  Index: livesites.xml
  ===================================================================
  RCS file: /home/cvs/xml-cocoon/xdocs/livesites.xml,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- livesites.xml	2000/08/18 12:16:16	1.8
  +++ livesites.xml	2000/08/30 22:28:43	1.9
  @@ -30,6 +30,7 @@
       <li><link href="http://www.myerealtor.com/">http://www.myerealtor.com</link></li>
       <li><link href="http://politics.smallworld.com/">http://politics.smallworld.com</link></li>
       <li><link href="http://www.methodsystems.com/">http://www.methodsystems.com</link></li>
  +    <li><link href="http://www.intelinet.com.br/">http://www.intelinet.com.br</link></li>
   <!--<li><link href=""></link></li>-->
      </ul>
   
  
  
  
  1.13      +1 -0      xml-cocoon/xdocs/site-book.xml
  
  Index: site-book.xml
  ===================================================================
  RCS file: /home/cvs/xml-cocoon/xdocs/site-book.xml,v
  retrieving revision 1.12
  retrieving revision 1.13
  diff -u -r1.12 -r1.13
  --- site-book.xml	2000/08/16 15:30:29	1.12
  +++ site-book.xml	2000/08/30 22:28:43	1.13
  @@ -16,6 +16,7 @@
     <page id="infrastructure" label="Infrastructure" source="infrastructure.xml"/>
     <page id="guide" label="User Guide" source="guide.xml"/>
     <page id="dynamic" label="Dynamic Content" source="dynamic.xml"/>
  +  <page id="how-it-works" label="How it works" source="how-it-works.xml"/>
   <separator/>
     <page id="xsp" label="XSP Processor" source="xspprocessor.xml"/>
     <page id="sqltaglib" label="SQL XSP Taglib" source="sqltaglib.xml"/>
  
  
  
  1.1                  xml-cocoon/xdocs/how-it-works.xml
  
  Index: how-it-works.xml
  ===================================================================
  <?xml version="1.0"?>
  <!DOCTYPE document SYSTEM "./dtd/document-v10.dtd">
  <document>
   <header>
    <title>How Cocoon engine works</title>
    <version>Thu Aug 24 16:38:14</version>
    <authors>
     <person name="Luca Ida Giovanni Toldo" email="lucatoldo@aol.com"/>
    </authors>
   </header>
  <body>
   <s1 title="How Cocoon 1.7.4. works">
    <p>
    From hereafter and unless otherwise specified, for sake of brevity any class name
  is assumed to be prepended the <source>org.apache.cocoon</source> prefix. The description hereafter tries to follow the operations of Cocoon from a &quot; document point of view &quot; while the <source>javadoc</source> documentation describes it from a &quot; procedural point of view&quot;. Therefore, this documentation tries to be complementary to the <source>javadoc</source> one and not to repeat parts therein. Furthermore, since the ultimate documentation is the <source>code</source> itself, this document tries not to go too deep but eventually integrates the comments in the code.
    </p>
    </s1>
    <s1 title="Cocoon">
     <p>
     This is the &quot;main&quot; class either at commandline use or from the
     servlet environment. Clearly, it contains the methods <source>init</source>
     for the first case as well as <source>main</source> for the latter case.
     </p>
     <p>
     Hereafter are described the operations in the two common cases of commandline (
  typical for initial debugging and software development) and in servlet environment.
     </p>
      <s2 title="from the commandline">
      <p>
         When <source>Cocoon</source> is invoked from the commandline, it requires the information for the location of the <source>cocoon.properties</source> and then the name of the file containing the information to be processed. Upon loading of the properties,
  it creates a new <source>EngineWrapper</source> initialized with the above mentioned properties and then handles to it <source>output</source> as well as <source>input</source> streams.
      </p>
       <s3 title="EngineWrapper">
        <p>
         This is a &quot;dirty hack&quot; implemented in order to wrap methods that allow the engine to be called from commandline.The code &quot;is a hack&quot; since it tries to work around the problem that servlet API <cite>are not that easy to deal when you enter other modes of operation</cite>. Likely, when the code will be adopt <code>Stylebook</code>, this class will as well be cleaned up.
        </p>
        <p>
         Basically, this class instantiates a <code>Engine</code> class and handles to it the <source>output</source> as well as <source>input</source> streams.
        </p>
       </s3>
      </s2>
      <s2 title="from the servlet">
       <s3 title="startup phase">
        <p>
         As any well behaving servlet, upon startup the <source>init</source> method is invocated, which upon successful load of configuration information creates the <source>Engine</source>
        </p>
       </s3>
       <s3 title="production phase">
       <p>
        According to the servlet specification, a <source>service</source> method is as-well provided by this <source>Class</source>. This method is the one which is called by the servlet (e.g. Tomcat) and gets both the <source>HttpServletRequest</source> as wellas will return (to the servlet, e.g. to Tomcat) the <source>HttpServletResponse</source>. Basically, what happens is that the
       </p>
       </s3>
      </s2>
    </s1>
    <s2 title="Engine" >
     <cite>This class implements the engine that does all the document processing.</cite>
     <p>
     What a better definition of the function of this class than the words of its Author (Stfano Mazzocchi) ? From this otherwise lapidary definition, one should realize the importance of this Class in the context of the Cocoon operations and thus carefuly read should be performed and thorough understanding of it be assured.
     </p>
     <s3 title="startup phase">
      <p>
      Either from commandline or from the servlet, upon startup of the cocoon servlet the <code>Engine</code> is instantiated by the <code>private Engine</code> method. For the sake of understanding the Cocoon operations, it is important to know that at this point in time (and only this time in the whole lifespan of the Cocoon servlet) the objects performing the initialization of the various components 
      </p>
       <ul>
        <li><code>Parser</code></li>
     
       </ul> 
      <p>are instantiated with the parameters contained by the Configuration object. This is the reason why if changes are applied to the cocoon.properties file, these cannot be exploited by the Cocoon engine unless the engine is stopped and restarted.</p>
     </s3>
     <s3 title="production phase">
      <p>
      The <code>handle</code> method has been already mentioned previously and is indeed the main actor of the runtime operations of Cocoon. It gets invocated with two handles, one for the input <code>HttpServletRequest</code> and one for the output <code>HttpServletResponse</code>.
      </p>
      <p>Until the whole page is done, it</p>
       <ol>
        <li>Create the <code>Page</code> wrapper</li>
        <li>Gets the document <code>Producer</code> from the <code>ProducerFactory</code> matching the request</li>
        <li>Generate a <code>org.w3c.dom.Document</code> from the input stream</li>
        <li>Setup the hash table environment to pass the parameters to the processor pipeline</li>
        <li>Process the document through the document <code>Processor</code>s, for each processor invoked in the <code>Document</code> and matched in the <code>ProcessorFactory</code></li>
        <li>Get the <code>Formatter</code> requested by the <code>Document</code> and matching the <code>FormatterFactory</code></li>
        <li>Format the page</li>
        <li>Fill the page bean with content</li>
        <li>Set the content type and the encoding</li>
       </ol>
       <p>Finally,</p>
       <ul>
         <li>Set the content type of the response</li>
         <li>Setup the output writer</li>
         <li>Print the page to the PrintWriter object</li>
         <li>Append processsing information sas XML comment, if the content type allows</li>
         <li>Flush the PrinterWriter to the user</li>
         <li>Cache the page</li>
       </ul>
      <p>
       Now I suggest you to take a deep breath and read again the above steps since
  this is so beautiful the simplicity of the algorithm exploited that it make sense to appreciate i tin depth and breath.
      </p>
     </s3>
     <p>
      At this point the key elements are therefore the processors and the formatters, which directly operate upon the content of the Document. We are going to investigate them in detail. Should be already clear that indeed one can have more than one <code>Processor</code> in one <code>Document</code> and that these are going to be applied sequentially one on top of the previous. Namely, this is how is implemented the &quot;chaining&quot; of various <code>Processors</code>: in five lines of code (including debugging information). Again, simplicity and good coding style are assets of this implementation. Let us have a look then at what <code>Processors</code> and <code>Formatters</code> are, since these could be leveraged further and indeed these are going to be likely extended with new components for specific needs .
     </p>
    </s2>
    <s2 title="ProducerFactory">
    <p>
    For each source there must be an appropriate Producer implemented. Currently (version 1.7.4) the following ones are implemented:
    </p>
     <ul>
      <li>from File</li>
      <li>from HttpServletRequest</li>
     </ul>
    Certainly one could think of more producer types, such as CORBA, RMI, SMTP, etc..
    </s2>
  
    <s2 title="ProcessorFactory">
     <p>
     For each processing instruction type  there must be an appropriate Processor implemented. Currently (version 1.7.4) the following ones are implemented:
     </p>
      <ul>
       <li>Light weight Directory Access Protocol</li>
       <li>SQL</li>
       <li>eXtendible Server Pages (superseeds Dynamic Content Processor)</li> 
       <li>Dynamic Content Processor (deprecated, useXSP  instead)</li>
       <li>xinclude</li>
       <li>xslt</li>
      </ul>
    </s2>
  
    <s2 title="FormatterFactory">
     <p>
     For each format in which the output should be delivered (e.g. PDF, TEXT, HTML, XML, XHTML ), there must be an appropriate Formatter implemented. Currently (version 1.7.4 ) the following ones are distributed:
     </p>
     <ul>
      <li>FO2PDF</li>
      <li>HTML</li>
      <li>Text</li>
      <li>XHTML</li>
      <li>XML</li>
     </ul>
      <p>
      Clearly, one might imagine of many more formats such as
     </p>
     <ul>
       <li>FO2RTF Microsoft Rich Text Format</li>
       <li>FO2MIF FrameMaker Interchange Format</li>
       <li>BRAILLE</li>
     </ul>
     <p>
       Could be noticed that the "FO" processing required both for FO2RTF as well as FO2MIF might be skipped
       would one express the content in a format which does not implies formatting in the same page layout
       semantic as for the printed medium. Infact, VoiceXML is supported by Cocoon by simply returning XML
       and indeed in that case the processing instruction cocoon format is <code>text/xml</code> ! In the
       case of VRML, the cocoon format is <code>model/vrml</code> which in the <code>cocoon.properties</code>
       configuration file is &quot;mapped&quot; to <code>TextFormatter</code>
     </p>
     <p>
      Recent discussions in the the Cocoon mailing list have raised further interest on the latter topic which seems as well to be well designed for being implemented in decentralized way. Therefore hereafter is further analyzed how the <code>FormatterFactory</code> works and what needs to be done to implement new <code>Formatter</code> classes in a way that can be easily integrated in Cocoon 1.7.4
     </p>
     <p>
      Due to the empyrical nature of the operations, we will follow a couple of examples of growing complexity in order to identify common programming paradigms and coding patterns which could then be used as guidelines for further formatters.
     </p>
    <s3 title="XMLFormatter">
     <p>
      Likely the easiest of the tasks, since is mostly a &quot;pass-through&quot; method. Its tasks are:
     </p>
     <ul>
      <li>set the MIME type= &quot;text/xml/quot;</li>
      <li>pass the XML through, leaving the XML declarations an dusing the XML method</li>
     </ul>
     <p>
    The <code>format</code> method is implemented by simply making use of 
  <code>org.apache.xml.serialize.SerializerFactory</code> 
     </p>   
    </s3>
    <s3 title="TextFormatter">
     <p>
      Another simple implementation of a Formatter, as stated in the code notes from the Author (Stefano Mazzocchi, surprised?) <cite>This formatter is used to serialize non-marked-up results such as plain text outputs, VRML, CSS etc.</cite>. Its biggest tasks are:
     </p>
     <ul>
      <li>set the MIMEtype= &quot;text/plain&quot;</li>
      <li>convert the XML to text, removing the XML declarations and using TEXT method</li>
     </ul>
    </s3>
  
     <s3 title="HTMLFormatter">
      <p>
      Here the difficult task of mapping between XML and HTML tags is exploited as above by the SerializerFactory methods and thus is simply passed through to that. Very little coding as well. Mostly focused on:
      </p>
      <ul>
       <li>set the MIMEtype=&quot;text/html&quot;</li>
       <li>convert XML to HTML, removing the XML declaration and using HTML method</li>
      </ul>
     </s3>
  
     <s3 title="FO2PDFFormatter">
      <p>
      Certainly the most complex of the Formatters so far implemented. It does not make use of the
  SerializerFactory but instead of PDFRenderer from the FOP engine. 
      </p>
      <ul>
       <li>set the MIME type=&quot;application/pdf&quot;</li>
       <li>instantiate a new org.apache.fop.apps.Driver class</li>
       <li>follow the example use of such class by setting the Writer to the cocoon's writer and then feeding the buildFooTree with the DOM</li>
      </ul>
     </s3>
  
    </s2>
  
  </body>
  </document>