sylvain 02/02/28 10:00:50 Modified: src/java/org/apache/cocoon/components/source AbstractStreamWriteableSource.java FileSource.java src/java/org/apache/cocoon/environment WriteableSource.java Log: Add cancel methods on WriteableSource and FileSource Revision Changes Path 1.2 +135 -26 xml-cocoon2/src/java/org/apache/cocoon/components/source/AbstractStreamWriteableSource.java Index: AbstractStreamWriteableSource.java =================================================================== RCS file: /home/cvs/xml-cocoon2/src/java/org/apache/cocoon/components/source/AbstractStreamWriteableSource.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- AbstractStreamWriteableSource.java 22 Feb 2002 23:25:18 -0000 1.1 +++ AbstractStreamWriteableSource.java 28 Feb 2002 18:00:50 -0000 1.2 @@ -85,7 +85,7 @@ * <code>getOutputStream()</code>. * * @author <a href="mailto:[EMAIL PROTECTED]">Sylvain Wallez</a> - * @version $Id: AbstractStreamWriteableSource.java,v 1.1 2002/02/22 23:25:18 sylvain Exp $ + * @version $Id: AbstractStreamWriteableSource.java,v 1.2 2002/02/28 18:00:50 sylvain Exp $ */ public abstract class AbstractStreamWriteableSource extends AbstractStreamSource implements WriteableSource { @@ -93,52 +93,161 @@ protected AbstractStreamWriteableSource(ComponentManager manager) { super(manager); } + + /** + * Checks if the <code>OutputStream</code> under <code>handler</code> can be cancelled. + * + * @see #canCancel(OutputStream) + */ + public boolean canCancel(ContentHandler handler) { + if (handler instanceof WritingPipe) { + WritingPipe pipe = (WritingPipe)handler; + if (pipe.getSource() == this) { + return pipe.canCancel(); + } + } + + // Not a valid handler for this source + throw new IllegalArgumentException("The handler is not associated to this source"); + } + + /** + * Always return <code>false</code>. To be redefined by implementations that support + * <code>cancel()</code>. + */ + public boolean canCancel(OutputStream stream) { + return false; + } + + /** + * Cancels the <code>OutputStream</code> under <code>handler</code>. + * + * @see #cancel(OutputStream) + */ + public void cancel(ContentHandler handler) throws Exception { + if (handler instanceof WritingPipe) { + WritingPipe pipe = (WritingPipe)handler; + if (pipe.getSource() == this) { + pipe.cancel(); + return; + } + } + + // Not a valid handler for this source + throw new IllegalArgumentException("The handler is not associated to this source"); + } + + /** + * Always throw <code>UnsupportedOperationException</code>. To be redefined by + * implementations that support <code>cancel()</code>. + */ + public void cancel(OutputStream stream) throws Exception { + throw new UnsupportedOperationException("Cancel is not implemented on " + + this.getClass().getName()); + } /** * Get a <code>ContentHandler</code> to write a SAX stream to this source. It - * uses the 'xml' serializer to serialize events, and thus this serializer - * must exist in this source's component manager. + * uses either the 'xml' or 'html' serializer depending on the result of + * {@link #isHTMLContent()} to serialize events, and thus these serializers must + * exist in this source's component manager. */ public ContentHandler getContentHandler() throws SAXException, ProcessingException { Serializer serializer; + ComponentSelector selector; + + String serializerName = this.isHTMLContent() ? "html" : "xml"; // Get the serializer try { - ComponentSelector selector = + selector = (ComponentSelector)this.manager.lookup(Serializer.ROLE + "Selector"); - serializer = (Serializer)selector.select("xml"); + serializer = (Serializer)selector.select(serializerName); } catch(ComponentException ce) { - throw new ProcessingException("Cannot get 'xml' serializer"); + throw new ProcessingException("Cannot get '" + serializerName + "' serializer"); } - // Connect the output stream to the serializer - final OutputStream output; try { - output = getOutputStream(); - serializer.setOutputStream(getOutputStream()); + return new WritingPipe(getOutputStream(), selector, serializer); } catch(IOException ioe) { + selector.release(serializer); throw new ProcessingException("Cannot open stream for " + this.getSystemId(), ioe); - } + } + } + + /** + * A pipe that closes the outputstream at the end of the document and handles cancel(). + */ + private class WritingPipe extends AbstractXMLPipe { + + // The output stream + private OutputStream output; + + // Serialier and its selector for proper release + private Serializer serializer; + private ComponentSelector selector; + + public WritingPipe(OutputStream output, ComponentSelector selector, Serializer serializer) + throws IOException { + this.output = output; + this.selector = selector; + this.serializer = serializer; + + // Connect this pipe, the serializer and the output stream + this.setConsumer(this.serializer); + this.serializer.setOutputStream(this.output); + } + + public WriteableSource getSource() { + return AbstractStreamWriteableSource.this; + } - // Wrap the serializer in a pipe that will close the output stream - // at the end of the document - AbstractXMLPipe result = new AbstractXMLPipe() { - - public void endDocument() throws SAXException { - super.endDocument(); - try { - output.close(); - } - catch(Exception e) { - throw new SAXException("Error while closing output stream", e); - } + /** + * Close the underlying stream + */ + public void endDocument() throws SAXException { + super.endDocument(); + try { + close(); } - }; + catch(Exception e) { + throw new SAXException("Error while closing output stream", e); + } + } + + public boolean canCancel() { + return this.output != null; + } + + /** + * Cancel the wrapped output stream + */ + public void cancel() throws Exception { + AbstractStreamWriteableSource.this.cancel(output); + close(); + } - result.setConsumer(serializer); + private void close() throws IOException { + if (this.serializer != null) { + // Disconnect serializer; + this.recycle(); + // and release it + this.selector.release(this.serializer); + this.serializer = null; + } + + if (this.output != null) { + this.output.close(); + this.output = null; + } + } - return result; + // Ensure all is closed properly + protected void finalize() throws Throwable { + close(); + super.finalize(); + } } } 1.2 +94 -27 xml-cocoon2/src/java/org/apache/cocoon/components/source/FileSource.java Index: FileSource.java =================================================================== RCS file: /home/cvs/xml-cocoon2/src/java/org/apache/cocoon/components/source/FileSource.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- FileSource.java 22 Feb 2002 23:25:18 -0000 1.1 +++ FileSource.java 28 Feb 2002 18:00:50 -0000 1.2 @@ -75,7 +75,7 @@ * A <code>WriteableSource</code> for 'file:/' system IDs. * * @author <a href="mailto:[EMAIL PROTECTED]">Sylvain Wallez</a> - * @version $Id: FileSource.java,v 1.1 2002/02/22 23:25:18 sylvain Exp $ + * @version $Id: FileSource.java,v 1.2 2002/02/28 18:00:50 sylvain Exp $ */ public class FileSource extends AbstractStreamWriteableSource implements WriteableSource { @@ -107,6 +107,10 @@ this.file = new File(url.substring(5)); // 5 == "file:".length() } + public boolean exists() { + return this.file.exists(); + } + /** * Returns <code>true</code> if the file name ends with ".htm" or ".html". */ @@ -157,7 +161,7 @@ // Create a temp file. It will replace the right one when writing terminates, // and serve as a lock to prevent concurrent writes. - final File tmpFile = new File(this.file.getPath() + ".tmp"); + File tmpFile = new File(this.file.getPath() + ".tmp"); // Ensure the directory exists tmpFile.getParentFile().mkdirs(); @@ -174,37 +178,100 @@ } // Return a stream that will rename the temp file on close. - return new FilterOutputStream(new FileOutputStream(tmpFile)) { - boolean closed = false; + return new FileSourceOutputStream(tmpFile); + } + + /** + * Always return <code>false</code>. To be redefined by implementations that support + * <code>cancel()</code>. + */ + public boolean canCancel(OutputStream stream) { + if (stream instanceof FileSourceOutputStream) { + FileSourceOutputStream fsos = (FileSourceOutputStream)stream; + if (fsos.getSource() == this) { + return fsos.canCancel(); + } + } - public void close() throws IOException { - super.close(); + // Not a valid stream for this source + throw new IllegalArgumentException("The stream is not associated to this source"); + } - try { - // Delete destination file - if (FileSource.this.file.exists()) { - FileSource.this.file.delete(); - } - // Rename temp file to destination file - tmpFile.renameTo(FileSource.this.file); - - } finally { - // Ensure temp file is deleted, ie lock is released. - // If there was a failure above, written data is lost. - if (tmpFile.exists()) { - tmpFile.delete(); - } - this.closed = true; - } + /** + * Cancels the output stream. + */ + public void cancel(OutputStream stream) throws Exception { + if (stream instanceof FileSourceOutputStream) { + FileSourceOutputStream fsos = (FileSourceOutputStream)stream; + if (fsos.getSource() == this) { + fsos.cancel(); + return; } - - public void finalize() { - if (!this.closed && tmpFile.exists()) { - // Something wrong happened while writing : delete temp file + } + + // Not a valid stream for this source + throw new IllegalArgumentException("The stream is not associated to this source"); + } + + /** + * A file outputStream that will rename the temp file to the destination file upon close() + * and discard the temp file upon cancel(). + */ + private class FileSourceOutputStream extends FileOutputStream { + + private File tmpFile; + private boolean isClosed = false; + + public FileSourceOutputStream(File tmpFile) throws IOException { + super(tmpFile); + this.tmpFile = tmpFile; + } + + public FileSource getSource() { + return FileSource.this; + } + + public void close() throws IOException { + super.close(); + + try { + // Delete destination file + if (FileSource.this.file.exists()) { + FileSource.this.file.delete(); + } + // Rename temp file to destination file + tmpFile.renameTo(FileSource.this.file); + + } finally { + // Ensure temp file is deleted, ie lock is released. + // If there was a failure above, written data is lost. + if (tmpFile.exists()) { tmpFile.delete(); } + this.isClosed = true; } - }; + } + + public boolean canCancel() { + return !this.isClosed; + } + + public void cancel() throws Exception { + if (this.isClosed) { + throw new IllegalStateException("Cannot cancel : outputstrem is already closed"); + } + + this.isClosed = true; + super.close(); + this.tmpFile.delete(); + } + + public void finalize() { + if (!this.isClosed && tmpFile.exists()) { + // Something wrong happened while writing : delete temp file + tmpFile.delete(); + } + } } } 1.2 +41 -3 xml-cocoon2/src/java/org/apache/cocoon/environment/WriteableSource.java Index: WriteableSource.java =================================================================== RCS file: /home/cvs/xml-cocoon2/src/java/org/apache/cocoon/environment/WriteableSource.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- WriteableSource.java 22 Feb 2002 23:25:19 -0000 1.1 +++ WriteableSource.java 28 Feb 2002 18:00:50 -0000 1.2 @@ -75,7 +75,7 @@ * SAX events to a byte stream. * * @author <a href="[EMAIL PROTECTED]">Sylvain Wallez</a> - * @version CVS $Id: WriteableSource.java,v 1.1 2002/02/22 23:25:19 sylvain Exp $ + * @version CVS $Id: WriteableSource.java,v 1.2 2002/02/28 18:00:50 sylvain Exp $ */ public interface WriteableSource extends ModifiableSource { @@ -83,6 +83,13 @@ // such as user/password, headers, etc ? /** + * Does this source actually exist ? + * + * @return true if the resource exists. + */ + boolean exists(); + + /** * Get a <code>ContentHandler</code> where an XML document can * be written using SAX events. * <p> @@ -93,7 +100,7 @@ * @return a handler for SAX events */ ContentHandler getContentHandler() throws SAXException, ProcessingException; - + /** * Get an <code>InputStream</code> where raw bytes can be written to. * The signification of these bytes is implementation-dependent and @@ -102,5 +109,36 @@ * @return a stream to write to */ OutputStream getOutputStream() throws IOException, ProcessingException; - + + /** + * Can the data sent to a <code>ContentHandler</code> returned by + * {@link #getContentHandler()} be cancelled ? + * + * @return true if the handler can be cancelled + */ + boolean canCancel(ContentHandler handler); + + /** + * Can the data sent to an <code>OutputStream</code> returned by + * {@link #getOutputStream()} be cancelled ? + * + * @return true if the stream can be cancelled + */ + boolean canCancel(OutputStream stream); + + /** + * Cancel the data sent to a <code>ContentHandler</code> returned by + * {@link #getContentHandler()}. + * <p> + * After cancel, the handler should no more be used. + */ + void cancel(ContentHandler handler) throws Exception; + + /** + * Cancel the data sent to an <code>OutputStream</code> returned by + * {@link #getOutputStream()}. + * <p> + * After cancel, the stream should no more be used. + */ + void cancel(OutputStream stream) throws Exception; }
---------------------------------------------------------------------- In case of troubles, e-mail: [EMAIL PROTECTED] To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]