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.treeprocessor;
018:
019: import org.apache.avalon.framework.activity.Disposable;
020: import org.apache.avalon.framework.component.ComponentException;
021: import org.apache.avalon.framework.component.ComponentManager;
022: import org.apache.avalon.framework.component.ComponentSelector;
023: import org.apache.avalon.framework.component.Recomposable;
024: import org.apache.avalon.framework.logger.AbstractLogEnabled;
025: import org.apache.cocoon.components.CocoonComponentManager;
026: import org.apache.cocoon.components.pipeline.ProcessingPipeline;
027: import org.apache.cocoon.components.treeprocessor.variables.VariableResolver;
028: import org.apache.cocoon.environment.Redirector;
029: import org.apache.cocoon.sitemap.SitemapErrorHandler;
030:
031: import java.util.ArrayList;
032: import java.util.HashMap;
033: import java.util.Iterator;
034: import java.util.List;
035: import java.util.Map;
036:
037: /**
038: * The invocation context of <code>ProcessingNode</code>s.
039: *
040: * <p>This class serves two purposes:
041: * <ul>
042: * <li>Avoid explicit enumeration of all needed parameters in
043: * {@link ProcessingNode#invoke(org.apache.cocoon.environment.Environment, InvokeContext)},
044: * thus allowing easier addition of new parameters,</li>
045: * <li>Hold pipelines, and provide "just in time" lookup for them.</li>
046: * </ul>
047: *
048: * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
049: * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
050: * @author <a href="mailto:tcurdt@apache.org">Torsten Curdt</a>
051: * @version $Id: InvokeContext.java 433543 2006-08-22 06:22:54Z crossley $
052: */
053: public class InvokeContext extends AbstractLogEnabled implements
054: Recomposable, Disposable {
055:
056: private List mapStack = new ArrayList();
057: private HashMap nameToMap = new HashMap();
058: private HashMap mapToName = new HashMap();
059:
060: /** True if building pipeline only, not processing it. */
061: private boolean isBuildingPipelineOnly;
062:
063: /** The redirector */
064: protected Redirector redirector;
065:
066: /** The current component manager, as set by the last call to compose() or recompose() */
067: private ComponentManager currentManager;
068:
069: /** The component manager that was used to get the pipelines */
070: private ComponentManager pipelinesManager;
071:
072: /** The name of the processing pipeline component */
073: protected String processingPipelineName;
074:
075: /** The parameters for the processing pipeline */
076: protected Map processingPipelineParameters;
077:
078: /** The object model used to resolve processingPipelineParameters */
079: protected Map processingPipelineObjectModel;
080:
081: /** The Selector for the processing pipeline */
082: protected ComponentSelector pipelineSelector;
083:
084: /** The ProcessingPipeline used */
085: protected ProcessingPipeline processingPipeline;
086:
087: /** The error handler for the pipeline. */
088: protected SitemapErrorHandler errorHandler;
089:
090: /**
091: * Create an <code>InvokeContext</code> without existing pipelines. This also means
092: * the current request is external.
093: */
094: public InvokeContext() {
095: this .isBuildingPipelineOnly = false;
096: }
097:
098: /**
099: * Determines if the Pipeline been set for this context
100: */
101: public boolean pipelineIsSet() {
102: return (this .processingPipeline != null);
103: }
104:
105: /**
106: * Create an <code>InvokeContext</code>
107: */
108: public InvokeContext(boolean isBuildingPipelineOnly) {
109: this .isBuildingPipelineOnly = isBuildingPipelineOnly;
110: }
111:
112: /**
113: * Composable Interface
114: */
115: public void compose(ComponentManager manager)
116: throws ComponentException {
117: this .currentManager = manager;
118: }
119:
120: /**
121: * Recomposable interface
122: */
123: public void recompose(ComponentManager manager)
124: throws ComponentException {
125: this .currentManager = manager;
126: if (this .processingPipeline != null) {
127: this .processingPipeline.recompose(manager);
128: }
129: }
130:
131: /**
132: * Informs the context about a new pipeline section
133: */
134: public void inform(String pipelineName, Map parameters,
135: Map objectModel) {
136: this .processingPipelineName = pipelineName;
137: this .processingPipelineParameters = parameters;
138: this .processingPipelineObjectModel = objectModel;
139: }
140:
141: /**
142: * Get the current <code>ProcessingPipeline</code>
143: */
144: public ProcessingPipeline getProcessingPipeline() throws Exception {
145: if (this .processingPipeline == null) {
146: // Keep current manager for proper release
147: this .pipelinesManager = this .currentManager;
148:
149: this .pipelineSelector = (ComponentSelector) this .pipelinesManager
150: .lookup(ProcessingPipeline.ROLE + "Selector");
151: this .processingPipeline = (ProcessingPipeline) this .pipelineSelector
152: .select(this .processingPipelineName);
153: this .processingPipeline.recompose(this .pipelinesManager);
154: this .processingPipeline.setup(VariableResolver
155: .buildParameters(this .processingPipelineParameters,
156: this , this .processingPipelineObjectModel));
157: if (this .isBuildingPipelineOnly) {
158: CocoonComponentManager.addComponentForAutomaticRelease(
159: this .pipelineSelector, this .processingPipeline,
160: this .pipelinesManager);
161: }
162: this .processingPipeline.setErrorHandler(this .errorHandler);
163: }
164: return this .processingPipeline;
165: }
166:
167: /**
168: * Set the processing pipeline for sub-sitemaps
169: */
170: public void setProcessingPipeline(ProcessingPipeline pipeline) {
171: this .processingPipeline = pipeline;
172: }
173:
174: /**
175: * Are we building a pipeline (and not executing it) ?
176: */
177: public final boolean isBuildingPipelineOnly() {
178: return this .isBuildingPipelineOnly;
179: }
180:
181: /**
182: * Get the current Map stack used to resolve expressions.
183: */
184: public final List getMapStack() {
185: return this .mapStack;
186: }
187:
188: /**
189: * Get the result Map by anchor name
190: */
191: public final Map getMapByAnchor(String anchor) {
192: return (Map) this .nameToMap.get(anchor);
193: }
194:
195: /**
196: * Push a Map on top of the current Map stack.
197: */
198: public final void pushMap(String anchorName, Map map) {
199: this .mapStack.add(map);
200:
201: if (getLogger().isDebugEnabled()) {
202: dumpParameters();
203: }
204:
205: if (anchorName != null) {
206: if (!this .nameToMap.containsKey(anchorName)) {
207: this .nameToMap.put(anchorName, map);
208: this .mapToName.put(map, anchorName);
209: } else {
210: if (getLogger().isErrorEnabled()) {
211: getLogger().error(
212: "name [" + anchorName + "] clashes");
213: }
214: }
215: }
216: }
217:
218: /**
219: * Dumps all sitemap parameters to log
220: */
221: protected void dumpParameters() {
222: if (!this .mapStack.isEmpty()) {
223: StringBuffer sb = new StringBuffer();
224:
225: sb.append("\nCurrent Sitemap Parameters:\n");
226: String path = "";
227:
228: for (int i = this .mapStack.size() - 1; i >= 0; i--) {
229: Map map = (Map) this .mapStack.get(i);
230: sb.append("LEVEL ").append(i + 1);
231: if (this .mapToName.containsKey(map)) {
232: sb.append(" is named '").append(
233: String.valueOf(this .mapToName.get(map)))
234: .append("'");
235: }
236: sb.append("\n");
237:
238: for (Iterator iter = map.entrySet().iterator(); iter
239: .hasNext();) {
240: final Map.Entry me = (Map.Entry) iter.next();
241: final Object key = me.getKey();
242: sb.append("PARAM: '").append(path).append(key)
243: .append("' ");
244: sb.append("VALUE: '").append(me.getValue()).append(
245: "'\n");
246: }
247: path = "../" + path;
248: }
249:
250: getLogger().debug(sb.toString());
251: }
252: }
253:
254: /**
255: * Pop the topmost element of the current Map stack.
256: */
257: public final void popMap() {
258: Object map = this .mapStack.remove(this .mapStack.size() - 1);
259: Object name = this .mapToName.get(map);
260: this .mapToName.remove(map);
261: this .nameToMap.remove(name);
262: }
263:
264: /**
265: * Set the redirector to be used by nodes that need it.
266: *
267: * @param redirector the redirector
268: */
269: public void setRedirector(Redirector redirector) {
270: this .redirector = redirector;
271: }
272:
273: /**
274: * Get the redirector to be used by nodes that need it.
275: *
276: * @return the redirector
277: */
278: public Redirector getRedirector() {
279: return this .redirector;
280: }
281:
282: /**
283: * Release the pipelines, if any, if they were looked up by this context.
284: */
285: public void dispose() {
286: if (!this .isBuildingPipelineOnly
287: && this .pipelinesManager != null) {
288: if (this .pipelineSelector != null) {
289: this .pipelineSelector.release(this .processingPipeline);
290: this .processingPipeline = null;
291: this .pipelinesManager.release(this .pipelineSelector);
292: this .pipelineSelector = null;
293: }
294: this .pipelinesManager = null;
295:
296: this .processingPipelineName = null;
297: this .processingPipelineParameters = null;
298: this .processingPipelineObjectModel = null;
299:
300: this .errorHandler = null;
301: }
302: }
303:
304: /**
305: * Set the error handler for the pipeline.
306: */
307: public void setErrorHandler(SitemapErrorHandler handler) {
308: this.errorHandler = handler;
309: }
310: }
|