org.milyn.io.AbstractOutputStreamResource Maven / Gradle / Ivy
/*
* Milyn - Copyright (C) 2006 - 2010
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (version 2.1) as published
* by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE.
*
* See the GNU Lesser General Public License for more details:
* http://www.gnu.org/licenses/lgpl.txt
*/
package org.milyn.io;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.milyn.SmooksException;
import org.milyn.assertion.AssertArgument;
import org.milyn.cdr.annotation.ConfigParam;
import org.milyn.container.ExecutionContext;
import org.milyn.delivery.ExecutionLifecycleCleanable;
import org.milyn.delivery.Fragment;
import org.milyn.delivery.VisitLifecycleCleanable;
import org.milyn.delivery.ordering.Consumer;
import org.milyn.delivery.dom.DOMVisitBefore;
import org.milyn.delivery.sax.SAXElement;
import org.milyn.delivery.sax.SAXVisitBefore;
import org.w3c.dom.Element;
import java.io.*;
import java.nio.charset.Charset;
/**
* AbstractOuputStreamResource is the base class for handling output stream
* resources in Smooks.
*
* Note that a {@link Writer} can also be opened on a stream resource. If a {@link Writer}
* has been opened on a resource, an {@link OutputStream} cannot also be opened (and visa versa).
*
* Example configuration:
*
* <resource-config selector="#document">
* <resource>org.milyn.io.ConcreateImpl</resource>
* <param name="resourceName">resourceName</param>
* <param name="writerEncoding">UTF-8</param> <!-- Optional -->
* </resource-config>
*
*
* Description of configuration properties:
*
* resource
: should be a concreate implementation of this class
* resourceName
: the name of this resouce. Will be used to identify this resource
* writerEncoding
: (Optional) the encoding to be used by any writers opened on this resource (Default is "UTF-8")
*
*
* @author Daniel Bevenius
*
*/
public abstract class AbstractOutputStreamResource implements SAXVisitBefore, DOMVisitBefore, Consumer, VisitLifecycleCleanable, ExecutionLifecycleCleanable
{
Log log = LogFactory.getLog( AbstractOutputStreamResource.class );
protected static final String RESOURCE_CONTEXT_KEY_PREFIX = AbstractOutputStreamResource.class.getName() + "#outputresource:";
private static final String OUTPUTSTREAM_CONTEXT_KEY_PREFIX = AbstractOutputStreamResource.class.getName() + "#outputstream:";
@ConfigParam
private String resourceName;
@ConfigParam(defaultVal = "UTF-8")
private Charset writerEncoding = Charset.forName("UTF-8");
// public
/**
* Retrieve/create an output stream that is appropriate for the concreate implementation
*
* @param executionContext Execution Context.
* @return OutputStream specific to the concreate implementation
*/
public abstract OutputStream getOutputStream( final ExecutionContext executionContext ) throws IOException;
/**
* Get the name of this resource
*
* @return The name of the resource
*/
public String getResourceName() {
return resourceName;
}
public boolean consumes(Object object) {
if(object.equals(resourceName)) {
return true;
}
return false;
}
/**
* Set the name of this resource
*
* @param resourceName The name of the resource
*/
public AbstractOutputStreamResource setResourceName(String resourceName) {
AssertArgument.isNotNullAndNotEmpty(resourceName, "resourceName");
this.resourceName = resourceName;
return this;
}
public AbstractOutputStreamResource setWriterEncoding(Charset writerEncoding) {
AssertArgument.isNotNull(writerEncoding, "writerEncoding");
this.writerEncoding = writerEncoding;
return this;
}
public Charset getWriterEncoding() {
return writerEncoding;
}
public void visitBefore( final SAXElement element, final ExecutionContext executionContext ) throws SmooksException, IOException
{
bind ( executionContext );
}
public void visitBefore( final Element element, final ExecutionContext executionContext ) throws SmooksException
{
bind ( executionContext );
}
public void executeVisitLifecycleCleanup(Fragment fragment, ExecutionContext executionContext) {
if(closeCondition( executionContext )) {
closeResource( executionContext );
}
}
public void executeExecutionLifecycleCleanup( ExecutionContext executionContext )
{
closeResource( executionContext );
}
protected boolean closeCondition(ExecutionContext executionContext) {
return true;
}
/**
* Get an {@link OutputStream} to the named Resource.
*
* @param resourceName The resource name.
* @param executionContext The current ExececutionContext.
* @return An {@link OutputStream} to the named Resource.
* @throws SmooksException Unable to access OutputStream.
*/
public static OutputStream getOutputStream(
final String resourceName,
final ExecutionContext executionContext) throws SmooksException
{
String resourceKey = OUTPUTSTREAM_CONTEXT_KEY_PREFIX + resourceName;
Object resourceIOObj = executionContext.getAttribute( resourceKey );
if( resourceIOObj == null )
{
AbstractOutputStreamResource resource = (AbstractOutputStreamResource) executionContext.getAttribute( RESOURCE_CONTEXT_KEY_PREFIX + resourceName );
OutputStream outputStream = openOutputStream(resource, resourceName, executionContext);
executionContext.setAttribute( resourceKey, outputStream );
return outputStream;
} else {
if(resourceIOObj instanceof OutputStream) {
return (OutputStream) resourceIOObj;
} else if(resourceIOObj instanceof Writer) {
throw new SmooksException("An Writer to the '" + resourceName + "' resource is already open. Cannot open an OutputStream to this resource now!");
} else {
throw new RuntimeException("Invalid runtime ExecutionContext state. Value stored under context key '" + resourceKey + "' must be either and OutputStream or Writer. Is '" + resourceIOObj.getClass().getName() + "'.");
}
}
}
/**
* Get a {@link Writer} to the named {@link OutputStream} Resource.
*
* Wraps the {@link OutputStream} in a {@link Writer}. Uses the "writerEncoding"
* param to set the encoding on the {@link Writer}.
*
* @param resourceName The resource name.
* @param executionContext The current ExececutionContext.
* @return A {@link Writer} to the named {@link OutputStream} Resource.
* @throws SmooksException Unable to access OutputStream.
*/
public static Writer getOutputWriter(final String resourceName, final ExecutionContext executionContext) throws SmooksException {
String resourceKey = OUTPUTSTREAM_CONTEXT_KEY_PREFIX + resourceName;
Object resourceIOObj = executionContext.getAttribute( resourceKey );
if( resourceIOObj == null ) {
AbstractOutputStreamResource resource = (AbstractOutputStreamResource) executionContext.getAttribute( RESOURCE_CONTEXT_KEY_PREFIX + resourceName );
OutputStream outputStream = openOutputStream(resource, resourceName, executionContext);
Writer outputStreamWriter = new OutputStreamWriter(outputStream, resource.getWriterEncoding());
executionContext.setAttribute( resourceKey, outputStreamWriter );
return outputStreamWriter;
} else {
if(resourceIOObj instanceof Writer) {
return (Writer) resourceIOObj;
} else if(resourceIOObj instanceof OutputStream) {
throw new SmooksException("An OutputStream to the '" + resourceName + "' resource is already open. Cannot open a Writer to this resource now!");
} else {
throw new RuntimeException("Invalid runtime ExecutionContext state. Value stored under context key '" + resourceKey + "' must be either and OutputStream or Writer. Is '" + resourceIOObj.getClass().getName() + "'.");
}
}
}
private static OutputStream openOutputStream(AbstractOutputStreamResource resource, String resourceName, ExecutionContext executionContext) {
if( resource == null )
{
throw new SmooksException( "OutputResource '" + resourceName + "' not bound to context. Configure an '" + AbstractOutputStreamResource.class.getName() + "' implementation, or change resource ordering." );
}
try
{
return resource.getOutputStream( executionContext );
}
catch ( IOException e )
{
throw new SmooksException( "Unable to set outputstream for '" + resource.getResourceName() + "'.", e );
}
}
/**
* Close the resource output stream.
*
* Classes overriding this method must call super on this method. This will
* probably need to be done before performing any aditional cleanup.
*
* @param executionContext Smooks ExecutionContext
*/
protected void closeResource( final ExecutionContext executionContext )
{
try
{
Closeable output = (Closeable) executionContext.getAttribute( OUTPUTSTREAM_CONTEXT_KEY_PREFIX + getResourceName() );
close( output );
}
finally
{
executionContext.removeAttribute( OUTPUTSTREAM_CONTEXT_KEY_PREFIX + getResourceName() );
executionContext.removeAttribute( RESOURCE_CONTEXT_KEY_PREFIX + getResourceName() );
}
}
private void bind( final ExecutionContext executionContext )
{
executionContext.setAttribute( RESOURCE_CONTEXT_KEY_PREFIX + getResourceName(), this );
}
private void close( final Closeable closeable)
{
if ( closeable == null )
{
return;
}
if(closeable instanceof Flushable) {
try
{
((Flushable) closeable).flush();
}
catch (IOException e)
{
log.debug( "IOException while trying to flush output resource '" + resourceName + "': ", e );
}
}
try
{
closeable.close();
}
catch (IOException e)
{
log.debug( "IOException while trying to close output resource '" + resourceName + "': ", e );
}
}
}