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.components.flow;
018:
019: import java.io.OutputStream;
020: import java.util.ArrayList;
021: import java.util.Map;
022:
023: import org.apache.avalon.framework.activity.Disposable;
024: import org.apache.avalon.framework.component.Component;
025: import org.apache.avalon.framework.configuration.Configurable;
026: import org.apache.avalon.framework.configuration.Configuration;
027: import org.apache.avalon.framework.configuration.ConfigurationException;
028: import org.apache.avalon.framework.context.ContextException;
029: import org.apache.avalon.framework.context.Contextualizable;
030: import org.apache.avalon.framework.logger.AbstractLogEnabled;
031: import org.apache.avalon.framework.service.ServiceException;
032: import org.apache.avalon.framework.service.ServiceManager;
033: import org.apache.avalon.framework.service.Serviceable;
034: import org.apache.avalon.framework.thread.SingleThreaded;
035: import org.apache.cocoon.Constants;
036: import org.apache.cocoon.components.ContextHelper;
037: import org.apache.cocoon.components.flow.util.PipelineUtil;
038: import org.apache.cocoon.environment.Context;
039: import org.apache.cocoon.environment.Redirector;
040: import org.apache.excalibur.source.SourceUtil;
041:
042: /**
043: * Abstract superclass for various scripting languages used by Cocoon
044: * for flow control. Defines some useful behavior like the ability to
045: * reload script files if they get modified (useful when doing
046: * development), and passing the control to Cocoon's sitemap for
047: * result page generation.
048: * <p>
049: * Flow intrepreters belonging to different sitemaps should be isolated. To achieve this,
050: * class implements the {@link org.apache.avalon.framework.thread.SingleThreaded}. Since
051: * the sitemap engine looks up the flow intepreter once at sitemap build time, this ensures
052: * that each sitemap will use a different instance of this class. But that instance will
053: * handle all flow calls for a given sitemap, and must therefore be thread safe.
054: *
055: * @author <a href="mailto:ovidiu@cup.hp.com">Ovidiu Predescu</a>
056: * @since March 15, 2002
057: * @version CVS $Id: AbstractInterpreter.java 433543 2006-08-22 06:22:54Z crossley $
058: */
059: public abstract class AbstractInterpreter extends AbstractLogEnabled
060: implements Component, Serviceable, Contextualizable,
061: Interpreter, SingleThreaded, Configurable, Disposable {
062:
063: // The instance ID of this interpreter, used to identify user scopes
064: private String instanceID;
065:
066: protected org.apache.avalon.framework.context.Context avalonContext;
067:
068: /**
069: * List of source locations that need to be resolved.
070: */
071: protected ArrayList needResolve = new ArrayList();
072:
073: protected org.apache.cocoon.environment.Context context;
074: protected ServiceManager manager;
075: protected ContinuationsManager continuationsMgr;
076:
077: /**
078: * Whether reloading of scripts should be done. Specified through
079: * the "reload-scripts" attribute in <code>flow.xmap</code>.
080: */
081: protected boolean reloadScripts;
082:
083: /**
084: * Interval between two checks for modified script files. Specified
085: * through the "check-time" XML attribute in <code>flow.xmap</code>.
086: */
087: protected long checkTime;
088:
089: public AbstractInterpreter() {
090: }
091:
092: /**
093: * Set the unique ID for this interpreter, which can be used to distinguish user value scopes
094: * attached to the session.
095: */
096: public void setInterpreterID(String interpreterID) {
097: this .instanceID = interpreterID;
098: }
099:
100: /**
101: * Get the unique ID for this interpreter, which can be used to distinguish user value scopes
102: * attached to the session.
103: *
104: * @return a unique ID for this interpreter
105: */
106: public String getInterpreterID() {
107: return this .instanceID;
108: }
109:
110: public void configure(Configuration config)
111: throws ConfigurationException {
112: reloadScripts = config.getChild("reload-scripts")
113: .getValueAsBoolean(false);
114: checkTime = config.getChild("check-time").getValueAsLong(1000L);
115: }
116:
117: /**
118: * Serviceable
119: */
120: public void service(ServiceManager sm) throws ServiceException {
121: this .manager = sm;
122: this .continuationsMgr = (ContinuationsManager) sm
123: .lookup(ContinuationsManager.ROLE);
124: }
125:
126: public void contextualize(
127: org.apache.avalon.framework.context.Context context)
128: throws ContextException {
129: this .avalonContext = context;
130: this .context = (Context) context
131: .get(Constants.CONTEXT_ENVIRONMENT_CONTEXT);
132: }
133:
134: /* (non-Javadoc)
135: * @see org.apache.avalon.framework.activity.Disposable#dispose()
136: */
137: public void dispose() {
138: if (this .manager != null) {
139: this .manager.release(this .continuationsMgr);
140: this .continuationsMgr = null;
141: this .manager = null;
142: }
143: }
144:
145: /**
146: * Registers a source file with the interpreter. Using this method
147: * an implementation keeps track of all the script files which are
148: * compiled. This allows them to reload the script files which get
149: * modified on the file system.
150: *
151: * <p>The parsing/compilation of a script file by an interpreter
152: * happens in two phases. In the first phase the file's location is
153: * registered in the <code>needResolve</code> array.
154: *
155: * <p>The second is possible only when a Cocoon
156: * <code>Environment</code> is passed to the Interpreter. This
157: * allows the file location to be resolved using Cocoon's
158: * <code>SourceFactory</code> class.
159: *
160: * <p>Once a file's location can be resolved, it is removed from the
161: * <code>needResolve</code> array and placed in the
162: * <code>scripts</code> hash table. The key in this hash table is
163: * the file location string, and the value is a
164: * DelayedRefreshSourceWrapper instance which keeps track of when
165: * the file needs to re-read.
166: *
167: * @param source the location of the script
168: *
169: * @see org.apache.cocoon.environment.Environment
170: * @see org.apache.cocoon.components.source.impl.DelayedRefreshSourceWrapper
171: */
172: public void register(String source) {
173: synchronized (this ) {
174: needResolve.add(source);
175: }
176: }
177:
178: /**
179: * Call the Cocoon sitemap for the given URI, sending the output of the
180: * eventually matched pipeline to the specified outputstream.
181: *
182: * @param uri The URI for which the request should be generated.
183: * @param biz Extra data associated with the subrequest.
184: * @param out An OutputStream where the output should be written to.
185: * @exception Exception If an error occurs.
186: */
187: public void process(String uri, Object biz, OutputStream out)
188: throws Exception {
189: // FIXME (SW): should we deprecate this method in favor of PipelineUtil?
190: PipelineUtil pipeUtil = new PipelineUtil();
191: try {
192: pipeUtil.contextualize(this .avalonContext);
193: pipeUtil.service(this .manager);
194: pipeUtil.processToStream(uri, biz, out);
195: } finally {
196: pipeUtil.dispose();
197: }
198: }
199:
200: public void forwardTo(String uri, Object bizData,
201: WebContinuation continuation, Redirector redirector)
202: throws Exception {
203: if (SourceUtil.indexOfSchemeColon(uri) == -1) {
204: uri = "cocoon:/" + uri;
205: Map objectModel = ContextHelper
206: .getObjectModel(this .avalonContext);
207: FlowHelper.setWebContinuation(objectModel, continuation);
208: FlowHelper.setContextObject(objectModel, bizData);
209: if (redirector.hasRedirected()) {
210: throw new IllegalStateException(
211: "Pipeline has already been processed for this request");
212: }
213: // this is a hint for the redirector
214: objectModel.put("cocoon:forward", "true");
215: redirector.redirect(false, uri);
216: } else {
217: throw new Exception(
218: "uri is not allowed to contain a scheme (cocoon:/ is always automatically used)");
219: }
220: }
221: }
|