giacomo 01/06/12 07:51:18 Modified: webapp sitemap.xmap Added: src/org/apache/cocoon/generation StreamGenerator.java src/org/apache/cocoon/util PostInputStream.java webapp/docs/samples/stream OrderPage.xml ReadMeAdd.txt telnet.txt Log: Added StreamGenerator and samples Submitted by: kingadziembowska <[EMAIL PROTECTED]> Revision Changes Path 1.12 +15 -25 xml-cocoon2/webapp/sitemap.xmap Index: sitemap.xmap =================================================================== RCS file: /home/cvs/xml-cocoon2/webapp/sitemap.xmap,v retrieving revision 1.11 retrieving revision 1.12 diff -u -u -r1.11 -r1.12 --- sitemap.xmap 2001/05/29 20:11:38 1.11 +++ sitemap.xmap 2001/06/12 14:51:16 1.12 @@ -17,6 +17,7 @@ <map:generator name="velocity" src="org.apache.cocoon.generation.VelocityGenerator"/> <map:generator name="html" src="org.apache.cocoon.generation.HTMLGenerator" label="content"/> <map:generator name="jsp" src="org.apache.cocoon.generation.JspGenerator"/> + <map:generator name="stream" src="org.apache.cocoon.generation.StreamGenerator"/> </map:generators> <map:transformers default="xslt"> @@ -346,31 +347,6 @@ <map:read src="docs/samples/{1}.png" mime-type="image/png"/> </map:match> - <!-- - This pipeline is responsable for generating the images for the navigation - bar. It uses the navimage.xsp to generate an SVG document which contains - the text for the image from the request URI passed as a parameter. The - requested language is evaluated by the LangSelect action. The text - represents a key into the i18n dictionary for all navigation items. At - the end the generated SVG is serialized as a PNG image. - This pipeline is fully cachable and thus an image depends only on - the text id and the laguage used and is generated once for each combination. - --> - <map:match type="regexp" pattern="i18n/images/(.*)_(.*)_(.*).png"> - <map:generate type="serverpages" src="docs/samples/i18n/navimages.xsp"> - <map:parameter name="text" value="{1}"/> - <map:parameter name="lang" value="{2}"/> - <map:parameter name="kind" value="{3}"/> - </map:generate> - <map:transform type="i18n" src="docs/samples/i18n/nav_trans.xml"> - <map:parameter name="lang" value="{2}"/> - <map:parameter name="default_lang" value="en"/> - <map:parameter name="available_lang_1" value="en"/> - <map:parameter name="available_lang_2" value="de"/> - </map:transform> - <map:serialize type="svg2png"/> - </map:match> - <!-- =========================== Dynamic ================================ --> <map:match pattern="xsp/*"> <map:generate type="serverpages" src="docs/samples/xsp/{1}.xsp"/> @@ -425,6 +401,20 @@ <map:parameter name="view-source" value="docs/samples/session/{1}.xsp"/> </map:transform> <map:serialize/> + </map:match> + + <!-- ========================== Stream ================================= --> + <map:match pattern="request1"> + <map:generate type="stream"> + <map:parameter name="form-name" value="Foo"/> + </map:generate> + <map:serialize type="xml"/> + </map:match> + + <map:match pattern="Order"> + <map:generate src="docs/samples/stream/OrderPage.xml"/> + <map:transform src="stylesheets/dynamic-page2html.xsl"/> + <map:serialize type="html"/> </map:match> <!-- ========================== XSP Sources ============================== --> 1.1 xml-cocoon2/src/org/apache/cocoon/generation/StreamGenerator.java Index: StreamGenerator.java =================================================================== /***************************************************************************** * Copyright (C) The Apache Software Foundation. All rights reserved. * * ------------------------------------------------------------------------- * * This software is published under the terms of the Apache Software License * * version 1.1, a copy of which has been included with this distribution in * * the LICENSE file. * *****************************************************************************/ package org.apache.cocoon.generation; import java.io.StringReader; import java.io.IOException; import java.util.Map; import org.apache.cocoon.Constants; import org.apache.cocoon.ProcessingException; import org.apache.cocoon.ResourceNotFoundException; import org.apache.cocoon.Roles; import org.apache.cocoon.components.parser.Parser; import org.apache.cocoon.environment.http.HttpRequest; import org.apache.cocoon.environment.Source; import org.apache.cocoon.environment.SourceResolver; import org.apache.cocoon.util.PostInputStream; import org.apache.avalon.excalibur.pool.Poolable; import org.apache.avalon.framework.component.Component; import org.apache.avalon.framework.component.ComponentException; import org.apache.avalon.framework.component.ComponentManager; import org.apache.avalon.framework.parameters.Parameters; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * * The <code>StreamGenerator</code> is a class that reads XML from a request InputStream * and generates SAX Events. * For the POST requests with mimetype of application/x-www-form-urlencoded the xml data is * expected to be associated with the name specified in the sitemap parameter. * For the POST requests with mimetypes: text/plain, text/xml, application/xml the xml data is in the body of teh POST * request and its length is specified by the value returned by getContentLength() method. * The StreamGenerator uses helper org.apache.cocoon.util.PostInputStream class for InputStream reading operations. * At the time that Parser is reading the data out of InputStream - Parser has no knowledge about the length of data to be read. * The only way to signal to the Parser that all data was read from the InputStream is to control reading operation - PostInputStream- and to return to * the requestor -1 when the number of bytes read is equal to the getContentLength() value. * * @author <a href="mailto:[EMAIL PROTECTED]">Kinga Dziembowski</a> * @version $Revision: 1.1 $ $Date: 2001/06/12 14:51:17 $ */ public class StreamGenerator extends ComposerGenerator { public static final String CLASS = StreamGenerator.class.getName(); /** The parameter holding the name associated with the xml data **/ public static final String FORM_NAME = "form-name"; /** The input source */ private InputSource inputSource; /** The system ID of the input source */ private String systemID; /** * Set the current <code>ComponentManager</code> instance used by this * <code>Composable</code>. */ public void compose(ComponentManager manager) { super.compose(manager); } /** * Recycle this component. * All instance variables are set to <code>null</code>. */ public void recycle() { super.recycle(); this.inputSource = null; this.systemID = null; } /** * Setup the stream generator. */ public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par) throws ProcessingException, SAXException, IOException { super.setup(resolver, objectModel, src, par); } /** * Generate XML data out of request InputStream. */ public void generate() throws IOException, SAXException, ProcessingException { Parser parser = null; String parameter = parameters.getParameter(StreamGenerator.FORM_NAME, null); int len = 0; try { HttpRequest request = (HttpRequest) objectModel.get(Constants.REQUEST_OBJECT); if (request.getContentType().equals("application/x-www-form-urlencoded")) { String sXml = request.getParameter(parameter); inputSource = new InputSource(new StringReader(sXml)); } else if (request.getContentType().equals("text/plain") || request.getContentType().equals("text/xml") || request.getContentType().equals("application/xml")) { len = request.getContentLength(); if (len > 0) { PostInputStream anStream = new PostInputStream(request.getInputStream(), len); inputSource = new InputSource(anStream); } else { throw new IOException("getContentLen() == 0"); } } else { throw new IOException("Unexpected getContentType(): " + request.getContentType()); } } getLogger().debug("processing stream ContentType= " + request.getContentType() + "ContentLen= " + len); parser = (Parser)this.manager.lookup(Roles.PARSER); parser.setContentHandler(super.contentHandler); parser.setLexicalHandler(super.lexicalHandler); parser.parse(this.inputSource); } catch (IOException e) { getLogger().error("StreamGenerator.generate()", e); throw new ResourceNotFoundException("StreamGenerator could not find resource", e); } catch (SAXException e) { getLogger().error("StreamGenerator.generate()", e); throw(e); } catch (Exception e){ getLogger().error("Could not get parser", e); throw new ProcessingException("Exception in StreamGenerator.generate()",e); } finally { if (parser != null) this.manager.release((Component) parser); } } } 1.1 xml-cocoon2/src/org/apache/cocoon/util/PostInputStream.java Index: PostInputStream.java =================================================================== /***************************************************************************** * Copyright (C) The Apache Software Foundation. All rights reserved. * * ------------------------------------------------------------------------- * * This software is published under the terms of the Apache Software License * * version 1.1, a copy of which has been included with this distribution in * * the LICENSE file. * *****************************************************************************/ package org.apache.cocoon.util; import java.io.InputStream; import java.io.IOException; /** * The class PostInputStream is a wrapper for InputStream associated with POST message. * It allows to control read operation, restricting the number of bytes read to the value returned by getContentLen() method. * * @author <a href="mailto:[EMAIL PROTECTED]">Kinga Dziembowski</a> * @version $Id: PostInputStream.java,v 1.1 2001/06/12 14:51:17 giacomo Exp $ */ public class PostInputStream extends InputStream { /** * Class name */ public static final String CLASS = PostInputStream.class.getName(); /** The real InputStream object */ private InputStream m_inputStream = null; /** The length of InputStream */ private int m_contentLen = 0; /** The number of bytes read */ protected int m_bytesRead = 0; /** * Creates a PostInputStream */ public PostInputStream() { super(); } /** * Creates a <code>PostInputStream</code> based on a real InputStream object with the specified * post message body length. Saves its argument, the input stream * <code>m_inputStream</code>, for later use. * * @param input the underlying input stream. * @param len the post message body length. * @exception IllegalArgumentException len <= 0. */ public PostInputStream(final InputStream input, final int len) throws IllegalArgumentException { super(); init(input, len ); } /** * Sets the underlying input stream and contentLen value . * * @param inputStream the input stream; can not be null. * @param len the post message body length. * * @throws IllegalArgumentException */ protected void init(final InputStream input, final int len) throws IllegalArgumentException { if (len <= 0) { throw new IllegalArgumentException("contentLen <= 0 "); } this.m_inputStream = input; this.m_contentLen = len; } /** * Sets the underlying input stream and contentLen value . * * @param inputStream the input stream; can not be null. * @param len the post message body length. * * @throws IOException */ public synchronized void setInputStream(final InputStream input, final int len) throws IOException { if (m_inputStream != null) { close(); } init(input, len); } /** * Returns the underlying input stream. * * @return inputStream the underlying InputStream. */ public InputStream getInputStream() { return( m_inputStream ); } /** * Returns the post message body length. * * @return m_contentLen; */ public int getContentLen() { return( m_contentLen ); } /** * Reads the next byte from the input stream. If the end of the stream has been reached, this method returns -1. * * @return the next byte or -1 if at the end of the stream. * * @throws IOException */ public synchronized int read() throws IOException { checkOpen(); if (m_bytesRead == m_contentLen) { return -1; } int byt = m_inputStream.read(); if (byt != -1) { m_bytesRead++; } return byt; } /** * Reads bytes from this byte-input stream into the specified byte array, * starting at the given offset. * * <p> This method implements the general contract of the corresponding * <code>{@link InputStream#read(byte[], int, int) read}</code> method of * the <code>{@link InputStream}</code> class. * This method delegetes tre read operation to the underlying InputStream implementation class but it * controlls the number of bytes read from the stream.In the remote situation the underlying InputStream has no knowledge of * the length of the stream and the notion of the "end" is undefined. This wrapper class has a knowledge of the * length of data send by the requestor by the means of contentLength. This method returns the number of bytes read and * accumulates the total number of bytes read in m_bytesRead. When the m_bytesRead is equal to the specified contentLength * value the method returns returns -1 to signal the end of data. * * @param buffer the byte array to read into; can not be null. * @param offset the starting offset in the byte array. * @param len the maximum number of bytes to read. * * @return the number of bytes read, or <code>-1</code> if the end of * the stream has been reached. * @exception IOException if an I/O error occurs. */ public synchronized int read(byte[] buffer, int offset, int len) throws IOException { checkOpen(); if (m_bytesRead == m_contentLen) { return -1; } int num = m_inputStream.read(buffer, offset, len); if (num > 0) { m_bytesRead += num; } return num; } public synchronized int read(byte[] buffer) throws IOException { return read( buffer, 0, buffer.length); } /** * Checks to see if this stream is closed; if it is, an IOException is thrown. * * @throws IOException */ protected void checkOpen() throws IOException { if (m_inputStream == null) { throw new IOException("InputStream closed"); } } /** * See the general contract of the <code>skip</code> * method of <code>InputStream</code>. * Delegates execution to the underlying InputStream implementation class. * Checks to see if this stream is closed; if it is, an IOException is thrown. * @param n the number of bytes to be skipped. * @return the actual number of bytes skipped. * @exception IOException if an I/O error occurs. */ public synchronized long skip(long n) throws IOException { checkOpen(); return m_inputStream.skip(n); } /** * Returns the number of bytes available from this input stream that can be read without the stream blocking. * Delegates execution to the underlying InputStream implementation class. * @return available the number of available bytes. * * @throws IOException */ public synchronized int available() throws IOException { checkOpen(); return m_inputStream.available(); } /** * Tests if this input stream supports the <code>mark</code> * and <code>reset</code> methods. The <code>markSupported</code> * method of <code>BufferedInputStream</code> returns * <code>false</code>. * * @return a <code>boolean</code> indicating if this stream type supports * the <code>mark</code> and <code>reset</code> methods. * @see java.io.InputStream#mark(int) * @see java.io.InputStream#reset() */ public boolean markSupported() { return false; } /** * Closes this input stream by closing the underlying stream and marking this one as closed. * * @throws IOException */ public synchronized void close() throws IOException { if (m_inputStream == null) { return; } m_inputStream.close(); m_inputStream = null; m_contentLen = 0; m_bytesRead = 0; } /** * Returns a String representation of this. * * @return string the String representation of this. */ public String toString() { return getClass().getName() + "[inputStream=" + m_inputStream + ", contentLen=" + m_contentLen + "bytesRead=" + m_bytesRead + "]"; } } 1.1 xml-cocoon2/webapp/docs/samples/stream/OrderPage.xml Index: OrderPage.xml =================================================================== <page> <title>B2B processing</title> <content> <FORM action="http://localhost:8080/cocoon/request1" id="FORM1" method="post" name="FORM1"> <para>Input your XML documet here:</para> <TEXTAREA cols="80" id="TEXTAREA1" name="Foo" rows="60"><?xml version="1.0"?> <Orders> <OrderID>20259</OrderID> <CustomerID>WWWWWWW</CustomerID> <EmployeeID>6</EmployeeID> <OrderDate>2001-05-05 00:00:00</OrderDate> <RequiredDate>2001-06-05 00:00:00</RequiredDate> <ShippedDate>2001-06-01 00:00:00</ShippedDate> <ShipVia>1</ShipVia> <Freight>11.6100</Freight> <ShipName>Thoms White</ShipName> <ShipAddress>Somestr. 48</ShipAddress> <ShipCity>Munster</ShipCity> <ShipRegion>West</ShipRegion> <ShipPostalCode>00000</ShipPostalCode> <ShipCountry>Germany</ShipCountry> <OrderDetails> <OrderID>20259</OrderID> <ProductID>51</ProductID> <UnitPrice>42.4000</UnitPrice> <Quantity>40</Quantity> <Discount>0.0</Discount> </OrderDetails> <OrderDetails> <OrderID>20259</OrderID> <ProductID>14</ProductID> <UnitPrice>18.6000</UnitPrice> <Quantity>9</Quantity> <Discount>0.0</Discount> </OrderDetails> <OrderDetails> <OrderID>20259</OrderID> <ProductID>7</ProductID> <UnitPrice>12.4000</UnitPrice> <Quantity>30</Quantity> <Discount>0.0</Discount> </OrderDetails> <Customers> <CustomerID>WWWWWWW</CustomerID> <CompanyName>Thomas White</CompanyName> <ContactName>Karin Black</ContactName> <ContactTitle>Marketing Manager</ContactTitle> <Address>Somestr. 48</Address> <City>Munster</City> <Region>West</Region> <PostalCode>00000</PostalCode> <Country>Germany</Country> <Phone>xxxx-yyyyyy</Phone> <Fax>xxxx-yyyyyy</Fax> </Customers> </Orders> </TEXTAREA> <INPUT id="submit1" name="submit1" type="submit" value="Submit"/> </FORM> </content> </page> 1.1 xml-cocoon2/webapp/docs/samples/stream/ReadMeAdd.txt Index: ReadMeAdd.txt =================================================================== Hewlett-Packard Bluestone Cocoon Project @version@ What is being added? -------------------- · StreamGenerator · PostInputStream StreamGenerator The StreamGenerator is a class that reads XML from an HttpRequest InputStream and generates SAX Events. StreamGenerator expects XML data coming as POST message. For POST requests with mimetype of application/x-www-form-urlencoded, the xml data expects to be associated with the name specified in the sitemap parameter. For POST requests with mimetypes: text/plain, text/xml, application/xml the xml data is in the body of the POST request and its length is specified by the value returned by getContentLength() method. PostInputStream The StreamGenerator uses helper class org.apache.cocoon.util.PostInputStream for InputStream reading operations. At the time that Parser reads the data out of InputStream - Parser has no knowledge about the length of data to be read. The only way to signal to the Parser that all data was read from the InputStream is to control reading operation - PostInputStream- and to return to the requestor -1 when the number of bytes read is equal to the getContentLength() value. Installation Instructions ---------------------------- TO VIEW ABOVE COCOON ELEMENTS "IN ACTION": Unzip the attached file. The necessary elements will "fall into place" within the Cocoon code. Compile it using instruction in Cocoon install file. Since the generator is a generic object, i.e. it can process any stream out of the POST message there are two ways to see StreamGenerator in action: 1. To invoke URL http://localhost:8080/cocoon/Order 2. To use telnet program to generate POST request The first option is not a "pure" stream invocation, but it is quick way to observe desired effects. The result of this invocation is a form containing the XML document embedded in the textarea of the form. Submission of this form will invoke StreamGenerator. The testarea name/value par is specified as a parameter in the sitemap definition for the StreamGenerator. The expected result is the submitted xml document send back to the browser. The second or "pure" option of testing StreamGenerator "in action," requires the use of Telnet program or any other process able to generate correct POST message. The procedure is: · To invoke telnet, connect to localhost 8080 and to use content of telnet.txt file as a post message. · Here, the Copy-Paste method should be used. · Remember to hit the enter button twice enter after the contents of the post are set in telnet. It is important because Content-len is calculated assuming two "enter" in the end of http message. Once again, the performed task results in the mirror of the original document being sent back to the requestor. The "pure" stream generation can be observed using the telnet utility where you can invoke a message targeting my processing. Any other method is good (URL object connection) as long the message is well formed. Compatibility Issues ------------------------ The attached code was tested and is compatible with the Cocoon codebase ver.2.0a7 taken from CVS Repository 5/26/2001. The code was tested on Windows 2000 and NT 4.0 using Tomcatt 4.0-b5 and newest Bluestone HP application server. 1.1 xml-cocoon2/webapp/docs/samples/stream/telnet.txt Index: telnet.txt =================================================================== POST /cocoon/request1 HTTP/1.1 Content-Type: text/plain Content-Length:1513 <?xml version="1.0"?> <Orders> <OrderID>20259</OrderID> <CustomerID>WWWWWWW</CustomerID> <EmployeeID>6</EmployeeID> <OrderDate>2001-05-05 00:00:00</OrderDate> <RequiredDate>2001-06-05 00:00:00</RequiredDate> <ShippedDate>2001-06-01 00:00:00</ShippedDate> <ShipVia>1</ShipVia> <Freight>11.6100</Freight> <ShipName>Thoms White</ShipName> <ShipAddress>Somestr. 48</ShipAddress> <ShipCity>Munster</ShipCity> <ShipRegion>West</ShipRegion> <ShipPostalCode>00000</ShipPostalCode> <ShipCountry>Germany</ShipCountry> <OrderDetails> <OrderID>20259</OrderID> <ProductID>51</ProductID> <UnitPrice>42.4000</UnitPrice> <Quantity>40</Quantity> <Discount>0.0</Discount> </OrderDetails> <OrderDetails> <OrderID>20259</OrderID> <ProductID>14</ProductID> <UnitPrice>18.6000</UnitPrice> <Quantity>9</Quantity> <Discount>0.0</Discount> </OrderDetails> <OrderDetails> <OrderID>20259</OrderID> <ProductID>7</ProductID> <UnitPrice>12.4000</UnitPrice> <Quantity>30</Quantity> <Discount>0.0</Discount> </OrderDetails> <Customers> <CustomerID>WWWWWWW</CustomerID> <CompanyName>Thomas White</CompanyName> <ContactName>Karin Black</ContactName> <ContactTitle>Marketing Manager</ContactTitle> <Address>Somestr. 48</Address> <City>Munster</City> <Region>West</Region> <PostalCode>00000</PostalCode> <Country>Germany</Country> <Phone>xxxx-yyyyyy</Phone> <Fax>xxxx-yyyyyy</Fax> </Customers> </Orders> ---------------------------------------------------------------------- In case of troubles, e-mail: [EMAIL PROTECTED] To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]