001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.portal.coplet.adapter.impl;
018:
019: import java.util.Iterator;
020: import java.util.Map;
021:
022: import org.apache.avalon.framework.logger.AbstractLogEnabled;
023: import org.apache.avalon.framework.service.ServiceException;
024: import org.apache.avalon.framework.service.ServiceManager;
025: import org.apache.avalon.framework.service.Serviceable;
026: import org.apache.avalon.framework.thread.ThreadSafe;
027: import org.apache.cocoon.ProcessingException;
028: import org.apache.cocoon.components.thread.RunnableManager;
029: import org.apache.cocoon.environment.CocoonRunnable;
030: import org.apache.cocoon.portal.coplet.CopletData;
031: import org.apache.cocoon.portal.coplet.CopletInstanceData;
032: import org.apache.cocoon.portal.coplet.adapter.CopletAdapter;
033: import org.apache.cocoon.xml.SaxBuffer;
034: import org.apache.cocoon.xml.XMLUtils;
035: import org.xml.sax.ContentHandler;
036: import org.xml.sax.SAXException;
037: import EDU.oswego.cs.dl.util.concurrent.CountDown;
038:
039: /**
040: * This is the abstract base adapter to use pipelines as coplets
041: *
042: * <h2>Configuration</h2>
043: * <table><tbody>
044: * <tr>
045: * <th>buffer</th>
046: * <td>Shall the content of the coplet be buffered? If a coplet is
047: * buffered, errors local to the coplet are caught and a not
048: * availability notice is delivered instead. Buffering does not
049: * cache responses for subsequent requests.</td>
050: * <td></td>
051: * <td>boolean</td>
052: * <td><code>false</code></td>
053: * </tr>
054: * <tr>
055: * <th>timeout</th>
056: * <td>Max time in seconds content delivery may take. After a timeout,
057: * a not availability notice is delivered. Setting a timeout automatically
058: * turns on buffering.</td>
059: * <td></td>
060: * <td>int</td>
061: * <td><code>null</code></td>
062: * </tr>
063: * </tbody></table>
064: *
065: * @author <a href="mailto:cziegeler@s-und-n.de">Carsten Ziegeler</a>
066: * @author <a href="mailto:volker.schmitt@basf-it-services.com">Volker Schmitt</a>
067: *
068: * @version CVS $Id: AbstractCopletAdapter.java 433543 2006-08-22 06:22:54Z crossley $
069: */
070: public abstract class AbstractCopletAdapter extends AbstractLogEnabled
071: implements CopletAdapter, ThreadSafe, Serviceable {
072:
073: /** The service manager */
074: protected ServiceManager manager;
075:
076: /* (non-Javadoc)
077: * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
078: */
079: public void service(ServiceManager manager) throws ServiceException {
080: this .manager = manager;
081: }
082:
083: /**
084: * Get a configuration value
085: * First the coplet data is queried and if it doesn't provide an
086: * attribute with the given name, the coplet base data is used.
087: */
088: protected Object getConfiguration(CopletInstanceData coplet,
089: String key) {
090: CopletData copletData = coplet.getCopletData();
091: Object data = copletData.getAttribute(key);
092: if (data == null) {
093: data = copletData.getCopletBaseData().getCopletConfig()
094: .get(key);
095: }
096: return data;
097: }
098:
099: /**
100: * Get a configuration value
101: * First the coplet data is queried and if it doesn't provide an
102: * attribute with the given name, the coplet base data is used.
103: * If no value is found the default value is returned.
104: */
105: protected Object getConfiguration(CopletInstanceData coplet,
106: String key, Object defaultValue) {
107: Object data = this .getConfiguration(coplet, key);
108: if (data == null) {
109: data = defaultValue;
110: }
111: return data;
112: }
113:
114: /**
115: * Implement this and not toSAX()
116: */
117: public abstract void streamContent(CopletInstanceData coplet,
118: ContentHandler contentHandler) throws SAXException;
119:
120: /**
121: * This method streams the content of a coplet instance data.
122: * It handles buffering and timeout setting and calls
123: * {@link #streamContent(CopletInstanceData, ContentHandler)}
124: * for creating the content.
125: *
126: * @see org.apache.cocoon.portal.coplet.adapter.CopletAdapter#toSAX(org.apache.cocoon.portal.coplet.CopletInstanceData, org.xml.sax.ContentHandler)
127: */
128: public void toSAX(CopletInstanceData coplet,
129: ContentHandler contentHandler) throws SAXException {
130: final long startTime = System.currentTimeMillis();
131: Boolean bool = (Boolean) this
132: .getConfiguration(coplet, "buffer");
133: Integer timeout = (Integer) this .getConfiguration(coplet,
134: "timeout");
135: if (timeout != null) {
136: // if timeout is set we have to buffer!
137: bool = Boolean.TRUE;
138: }
139:
140: if (bool != null && bool.booleanValue()) {
141: boolean read = false;
142: SaxBuffer buffer = new SaxBuffer();
143: Exception error = null;
144: try {
145:
146: if (timeout != null) {
147: final int milli = timeout.intValue() * 1000;
148: LoaderThread loader = new LoaderThread(this ,
149: coplet, buffer);
150: final RunnableManager runnableManager = (RunnableManager) this .manager
151: .lookup(RunnableManager.ROLE);
152: try {
153: runnableManager.execute(new CocoonRunnable(
154: loader));
155: } finally {
156: this .manager.release(runnableManager);
157: }
158: try {
159: read = loader.join(milli);
160: } catch (InterruptedException ignore) {
161: // ignored
162: }
163: error = loader.exception;
164: if (error != null
165: && this .getLogger().isWarnEnabled()) {
166: this .getLogger().warn(
167: "Unable to get content of coplet: "
168: + coplet.getId(), error);
169: }
170: } else {
171: this .streamContent(coplet, buffer);
172: read = true;
173: }
174: } catch (Exception exception) {
175: error = exception;
176: this .getLogger().warn(
177: "Unable to get content of coplet: "
178: + coplet.getId(), exception);
179: } catch (Throwable t) {
180: error = new ProcessingException(
181: "Unable to get content of coplet: "
182: + coplet.getId(), t);
183: this .getLogger().warn(
184: "Unable to get content of coplet: "
185: + coplet.getId(), t);
186: }
187:
188: if (read) {
189: buffer.toSAX(contentHandler);
190: } else {
191: if (!this .renderErrorContent(coplet, contentHandler,
192: error)) {
193: // FIXME - get correct error message
194: contentHandler.startDocument();
195: XMLUtils.startElement(contentHandler, "p");
196: XMLUtils.data(contentHandler, "The coplet "
197: + coplet.getId()
198: + " is currently not available.");
199: XMLUtils.endElement(contentHandler, "p");
200: contentHandler.endDocument();
201: }
202: }
203: } else {
204: this .streamContent(coplet, contentHandler);
205: }
206: if (this .getLogger().isInfoEnabled()) {
207: final long msecs = System.currentTimeMillis() - startTime;
208: this .getLogger().info(
209: "Streamed coplet " + coplet.getCopletData().getId()
210: + " (instance " + coplet.getId() + ") in "
211: + msecs + "ms.");
212: }
213: }
214:
215: /* (non-Javadoc)
216: * @see org.apache.cocoon.portal.coplet.adapter.CopletAdapter#init(org.apache.cocoon.portal.coplet.CopletInstanceData)
217: */
218: public void init(CopletInstanceData coplet) {
219: // nothing to do here, can be overwritten in subclasses
220: }
221:
222: /* (non-Javadoc)
223: * @see org.apache.cocoon.portal.coplet.adapter.CopletAdapter#destroy(org.apache.cocoon.portal.coplet.CopletInstanceData)
224: */
225: public void destroy(CopletInstanceData coplet) {
226: // nothing to do here, can be overwritten in subclasses
227: }
228:
229: /* (non-Javadoc)
230: * @see org.apache.cocoon.portal.coplet.adapter.CopletAdapter#login(org.apache.cocoon.portal.coplet.CopletInstanceData)
231: */
232: public void login(CopletInstanceData coplet) {
233: // copy temporary attributes from the coplet data
234: Iterator iter = coplet.getCopletData().getAttributes()
235: .entrySet().iterator();
236: while (iter.hasNext()) {
237: Map.Entry entry = (Map.Entry) iter.next();
238: if (entry.getKey().toString().startsWith("temporary:")) {
239: coplet.setTemporaryAttribute(entry.getKey().toString()
240: .substring(10), entry.getValue());
241: }
242: }
243: }
244:
245: /* (non-Javadoc)
246: * @see org.apache.cocoon.portal.coplet.adapter.CopletAdapter#logout(org.apache.cocoon.portal.coplet.CopletInstanceData)
247: */
248: public void logout(CopletInstanceData coplet) {
249: // nothing to do here, can be overwritten in subclasses
250: }
251:
252: /**
253: * Render the error content for a coplet
254: * @param coplet The coplet instance data
255: * @param handler The content handler
256: * @param error The exception that occured
257: * @return True if the error content has been rendered, otherwise false
258: * @throws SAXException
259: */
260: protected boolean renderErrorContent(CopletInstanceData coplet,
261: ContentHandler handler, Exception error)
262: throws SAXException {
263: return false;
264: }
265: }
266:
267: final class LoaderThread implements Runnable {
268:
269: private final AbstractCopletAdapter adapter;
270: private final ContentHandler handler;
271: private final CopletInstanceData coplet;
272: private final CountDown finished;
273: Exception exception;
274:
275: public LoaderThread(AbstractCopletAdapter adapter,
276: CopletInstanceData coplet, ContentHandler handler) {
277: this .adapter = adapter;
278: this .coplet = coplet;
279: this .handler = handler;
280: this .finished = new CountDown(1);
281: }
282:
283: public void run() {
284: try {
285: adapter.streamContent(this .coplet, this .handler);
286: } catch (Exception local) {
287: this .exception = local;
288: } finally {
289: this .finished.release();
290: }
291: }
292:
293: boolean join(final long milis) throws InterruptedException {
294: return this.finished.attempt(milis);
295: }
296:
297: }
|