blob: 216a09b54d89bffcfc81ede90ea371f70ab0d970 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cocoon.portal.coplet.adapter.impl;
import java.util.Iterator;
import java.util.Map;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.cocoon.ProcessingException;
import org.apache.cocoon.components.thread.RunnableManager;
import org.apache.cocoon.environment.CocoonRunnable;
import org.apache.cocoon.portal.coplet.CopletData;
import org.apache.cocoon.portal.coplet.CopletInstanceData;
import org.apache.cocoon.portal.coplet.adapter.CopletAdapter;
import org.apache.cocoon.xml.SaxBuffer;
import org.apache.cocoon.xml.XMLUtils;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import EDU.oswego.cs.dl.util.concurrent.CountDown;
/**
* This is the abstract base adapter to use pipelines as coplets
*
* <h2>Configuration</h2>
* <table><tbody>
* <tr>
* <th>buffer</th>
* <td>Shall the content of the coplet be buffered? If a coplet is
* buffered, errors local to the coplet are caught and a not
* availability notice is delivered instead. Buffering does not
* cache responses for subsequent requests.</td>
* <td></td>
* <td>boolean</td>
* <td><code>false</code></td>
* </tr>
* <tr>
* <th>timeout</th>
* <td>Max time in seconds content delivery may take. After a timeout,
* a not availability notice is delivered. Setting a timeout automatically
* turns on buffering.</td>
* <td></td>
* <td>int</td>
* <td><code>null</code></td>
* </tr>
* </tbody></table>
*
* @author <a href="mailto:cziegeler@s-und-n.de">Carsten Ziegeler</a>
* @author <a href="mailto:volker.schmitt@basf-it-services.com">Volker Schmitt</a>
*
* @version CVS $Id$
*/
public abstract class AbstractCopletAdapter
extends AbstractLogEnabled
implements CopletAdapter, ThreadSafe, Serviceable {
/** The service manager */
protected ServiceManager manager;
/* (non-Javadoc)
* @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
*/
public void service(ServiceManager manager) throws ServiceException {
this.manager = manager;
}
/**
* Get a configuration value
* First the coplet data is queried and if it doesn't provide an
* attribute with the given name, the coplet base data is used.
*/
protected Object getConfiguration(CopletInstanceData coplet, String key) {
CopletData copletData = coplet.getCopletData();
Object data = copletData.getAttribute( key );
if ( data == null) {
data = copletData.getCopletBaseData().getCopletConfig().get( key );
}
return data;
}
/**
* Get a configuration value
* First the coplet data is queried and if it doesn't provide an
* attribute with the given name, the coplet base data is used.
* If no value is found the default value is returned.
*/
protected Object getConfiguration(CopletInstanceData coplet,
String key,
Object defaultValue) {
Object data = this.getConfiguration(coplet, key);
if ( data == null ) {
data = defaultValue;
}
return data;
}
/**
* Implement this and not toSAX()
*/
public abstract void streamContent(CopletInstanceData coplet,
ContentHandler contentHandler)
throws SAXException;
/**
* This method streams the content of a coplet instance data.
* It handles buffering and timeout setting and calls
* {@link #streamContent(CopletInstanceData, ContentHandler)}
* for creating the content.
*
* @see org.apache.cocoon.portal.coplet.adapter.CopletAdapter#toSAX(org.apache.cocoon.portal.coplet.CopletInstanceData, org.xml.sax.ContentHandler)
*/
public void toSAX(CopletInstanceData coplet, ContentHandler contentHandler)
throws SAXException {
final long startTime = System.currentTimeMillis();
Boolean bool = (Boolean) this.getConfiguration( coplet, "buffer" );
Integer timeout = (Integer) this.getConfiguration( coplet, "timeout");
if ( timeout != null ) {
// if timeout is set we have to buffer!
bool = Boolean.TRUE;
}
if ( bool != null && bool.booleanValue() ) {
boolean read = false;
SaxBuffer buffer = new SaxBuffer();
Exception error = null;
try {
if ( timeout != null ) {
final int milli = timeout.intValue() * 1000;
LoaderThread loader = new LoaderThread(this, coplet, buffer);
final RunnableManager runnableManager = (RunnableManager)this.manager.lookup( RunnableManager.ROLE );
try {
runnableManager.execute( new CocoonRunnable(loader) );
} finally {
this.manager.release( runnableManager );
}
try {
read = loader.join( milli );
} catch (InterruptedException ignore) {
// ignored
}
error = loader.exception;
if ( error != null && this.getLogger().isWarnEnabled() ) {
this.getLogger().warn("Unable to get content of coplet: " + coplet.getId(), error);
}
} else {
this.streamContent( coplet, buffer );
read = true;
}
} catch (Exception exception ) {
error = exception;
this.getLogger().warn("Unable to get content of coplet: " + coplet.getId(), exception);
} catch (Throwable t ) {
error = new ProcessingException("Unable to get content of coplet: " + coplet.getId(), t);
this.getLogger().warn("Unable to get content of coplet: " + coplet.getId(), t);
}
if ( read ) {
buffer.toSAX( contentHandler );
} else {
if ( !this.renderErrorContent(coplet, contentHandler, error)) {
// FIXME - get correct error message
contentHandler.startDocument();
XMLUtils.startElement( contentHandler, "p");
XMLUtils.data( contentHandler, "The coplet " + coplet.getId() + " is currently not available.");
XMLUtils.endElement(contentHandler, "p");
contentHandler.endDocument();
}
}
} else {
this.streamContent( coplet, contentHandler );
}
if ( this.getLogger().isInfoEnabled() ) {
final long msecs = System.currentTimeMillis() - startTime;
this.getLogger().info("Streamed coplet " + coplet.getCopletData().getId() +
" (instance " + coplet.getId() + ") in " + msecs + "ms.");
}
}
/* (non-Javadoc)
* @see org.apache.cocoon.portal.coplet.adapter.CopletAdapter#init(org.apache.cocoon.portal.coplet.CopletInstanceData)
*/
public void init(CopletInstanceData coplet) {
// nothing to do here, can be overwritten in subclasses
}
/* (non-Javadoc)
* @see org.apache.cocoon.portal.coplet.adapter.CopletAdapter#destroy(org.apache.cocoon.portal.coplet.CopletInstanceData)
*/
public void destroy(CopletInstanceData coplet) {
// nothing to do here, can be overwritten in subclasses
}
/* (non-Javadoc)
* @see org.apache.cocoon.portal.coplet.adapter.CopletAdapter#login(org.apache.cocoon.portal.coplet.CopletInstanceData)
*/
public void login(CopletInstanceData coplet) {
// copy temporary attributes from the coplet data
Iterator iter = coplet.getCopletData().getAttributes().entrySet().iterator();
while ( iter.hasNext() ) {
Map.Entry entry = (Map.Entry)iter.next();
if ( entry.getKey().toString().startsWith("temporary:") ) {
coplet.setTemporaryAttribute(entry.getKey().toString().substring(10),
entry.getValue());
}
}
}
/* (non-Javadoc)
* @see org.apache.cocoon.portal.coplet.adapter.CopletAdapter#logout(org.apache.cocoon.portal.coplet.CopletInstanceData)
*/
public void logout(CopletInstanceData coplet) {
// nothing to do here, can be overwritten in subclasses
}
/**
* Render the error content for a coplet
* @param coplet The coplet instance data
* @param handler The content handler
* @param error The exception that occured
* @return True if the error content has been rendered, otherwise false
* @throws SAXException
*/
protected boolean renderErrorContent(CopletInstanceData coplet,
ContentHandler handler,
Exception error)
throws SAXException {
return false;
}
}
final class LoaderThread implements Runnable {
private final AbstractCopletAdapter adapter;
private final ContentHandler handler;
private final CopletInstanceData coplet;
private final CountDown finished;
Exception exception;
public LoaderThread(AbstractCopletAdapter adapter,
CopletInstanceData coplet,
ContentHandler handler) {
this.adapter = adapter;
this.coplet = coplet;
this.handler = handler;
this.finished = new CountDown( 1 );
}
public void run() {
try {
adapter.streamContent( this.coplet, this.handler );
} catch (Exception local) {
this.exception = local;
} finally {
this.finished.release();
}
}
boolean join( final long milis )
throws InterruptedException {
return this.finished.attempt( milis );
}
}