1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.cocoon.components.treeprocessor;
18
19 import org.apache.avalon.framework.activity.Disposable;
20 import org.apache.avalon.framework.component.ComponentException;
21 import org.apache.avalon.framework.component.ComponentManager;
22 import org.apache.avalon.framework.component.ComponentSelector;
23 import org.apache.avalon.framework.component.Recomposable;
24 import org.apache.avalon.framework.logger.AbstractLogEnabled;
25 import org.apache.cocoon.components.CocoonComponentManager;
26 import org.apache.cocoon.components.pipeline.ProcessingPipeline;
27 import org.apache.cocoon.components.treeprocessor.variables.VariableResolver;
28 import org.apache.cocoon.environment.Redirector;
29 import org.apache.cocoon.sitemap.SitemapErrorHandler;
30
31 import java.util.ArrayList;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.Map;
36
37 /**
38 * The invocation context of <code>ProcessingNode</code>s.
39 *
40 * <p>This class serves two purposes:
41 * <ul>
42 * <li>Avoid explicit enumeration of all needed parameters in
43 * {@link ProcessingNode#invoke(org.apache.cocoon.environment.Environment, InvokeContext)},
44 * thus allowing easier addition of new parameters,</li>
45 * <li>Hold pipelines, and provide "just in time" lookup for them.</li>
46 * </ul>
47 *
48 * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
49 * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
50 * @author <a href="mailto:tcurdt@apache.org">Torsten Curdt</a>
51 * @version $Id: InvokeContext.java 433543 2006-08-22 06:22:54Z crossley $
52 */
53 public class InvokeContext extends AbstractLogEnabled
54 implements Recomposable, Disposable {
55
56 private List mapStack = new ArrayList();
57 private HashMap nameToMap = new HashMap();
58 private HashMap mapToName = new HashMap();
59
60 /** True if building pipeline only, not processing it. */
61 private boolean isBuildingPipelineOnly;
62
63 /** The redirector */
64 protected Redirector redirector;
65
66
67 /** The current component manager, as set by the last call to compose() or recompose() */
68 private ComponentManager currentManager;
69
70 /** The component manager that was used to get the pipelines */
71 private ComponentManager pipelinesManager;
72
73 /** The name of the processing pipeline component */
74 protected String processingPipelineName;
75
76 /** The parameters for the processing pipeline */
77 protected Map processingPipelineParameters;
78
79 /** The object model used to resolve processingPipelineParameters */
80 protected Map processingPipelineObjectModel;
81
82 /** The Selector for the processing pipeline */
83 protected ComponentSelector pipelineSelector;
84
85 /** The ProcessingPipeline used */
86 protected ProcessingPipeline processingPipeline;
87
88 /** The error handler for the pipeline. */
89 protected SitemapErrorHandler errorHandler;
90
91 /**
92 * Create an <code>InvokeContext</code> without existing pipelines. This also means
93 * the current request is external.
94 */
95 public InvokeContext() {
96 this.isBuildingPipelineOnly = false;
97 }
98
99 /**
100 * Determines if the Pipeline been set for this context
101 */
102 public boolean pipelineIsSet() {
103 return (this.processingPipeline != null);
104 }
105
106 /**
107 * Create an <code>InvokeContext</code>
108 */
109 public InvokeContext(boolean isBuildingPipelineOnly) {
110 this.isBuildingPipelineOnly = isBuildingPipelineOnly;
111 }
112
113 /**
114 * Composable Interface
115 */
116 public void compose(ComponentManager manager) throws ComponentException {
117 this.currentManager = manager;
118 }
119
120 /**
121 * Recomposable interface
122 */
123 public void recompose(ComponentManager manager) throws ComponentException {
124 this.currentManager = manager;
125 if (this.processingPipeline != null) {
126 this.processingPipeline.recompose(manager);
127 }
128 }
129
130 /**
131 * Informs the context about a new pipeline section
132 */
133 public void inform(String pipelineName,
134 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()
145 throws Exception {
146 if (this.processingPipeline == null) {
147 // Keep current manager for proper release
148 this.pipelinesManager = this.currentManager;
149
150 this.pipelineSelector = (ComponentSelector)this.pipelinesManager.lookup(ProcessingPipeline.ROLE+"Selector");
151 this.processingPipeline = (ProcessingPipeline)this.pipelineSelector.select(this.processingPipelineName);
152 this.processingPipeline.recompose( this.pipelinesManager );
153 this.processingPipeline.setup(
154 VariableResolver.buildParameters(this.processingPipelineParameters,
155 this,
156 this.processingPipelineObjectModel)
157 );
158 if (this.isBuildingPipelineOnly) {
159 CocoonComponentManager.addComponentForAutomaticRelease(this.pipelineSelector,
160 this.processingPipeline,
161 this.pipelinesManager);
162 }
163 this.processingPipeline.setErrorHandler(this.errorHandler);
164 }
165 return this.processingPipeline;
166 }
167
168 /**
169 * Set the processing pipeline for sub-sitemaps
170 */
171 public void setProcessingPipeline(ProcessingPipeline pipeline) {
172 this.processingPipeline = pipeline;
173 }
174
175 /**
176 * Are we building a pipeline (and not executing it) ?
177 */
178 public final boolean isBuildingPipelineOnly() {
179 return this.isBuildingPipelineOnly;
180 }
181
182 /**
183 * Get the current Map stack used to resolve expressions.
184 */
185 public final List getMapStack() {
186 return this.mapStack;
187 }
188
189 /**
190 * Get the result Map by anchor name
191 */
192 public final Map getMapByAnchor(String anchor) {
193 return (Map) this.nameToMap.get(anchor);
194 }
195
196 /**
197 * Push a Map on top of the current Map stack.
198 */
199 public final void pushMap(String anchorName, Map map) {
200 this.mapStack.add(map);
201
202 if (getLogger().isDebugEnabled()) {
203 dumpParameters();
204 }
205
206 if (anchorName != null) {
207 if (!this.nameToMap.containsKey(anchorName)) {
208 this.nameToMap.put(anchorName,map);
209 this.mapToName.put(map,anchorName);
210 } else {
211 if (getLogger().isErrorEnabled()) {
212 getLogger().error("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(String.valueOf(this.mapToName.get(map))).append("'");
233 }
234 sb.append("\n");
235
236 for (Iterator iter = map.entrySet().iterator(); iter.hasNext(); ) {
237 final Map.Entry me = (Map.Entry)iter.next();
238 final Object key = me.getKey();
239 sb.append("PARAM: '").append(path).append(key).append("' ");
240 sb.append("VALUE: '").append(me.getValue()).append("'\n");
241 }
242 path = "../" + path;
243 }
244
245 getLogger().debug(sb.toString());
246 }
247 }
248
249 /**
250 * Pop the topmost element of the current Map stack.
251 */
252 public final void popMap() {
253 Object map = this.mapStack.remove(this.mapStack.size() - 1);
254 Object name = this.mapToName.get(map);
255 this.mapToName.remove(map);
256 this.nameToMap.remove(name);
257 }
258
259 /**
260 * Set the redirector to be used by nodes that need it.
261 *
262 * @param redirector the redirector
263 */
264 public void setRedirector(Redirector redirector) {
265 this.redirector = redirector;
266 }
267
268 /**
269 * Get the redirector to be used by nodes that need it.
270 *
271 * @return the redirector
272 */
273 public Redirector getRedirector() {
274 return this.redirector;
275 }
276
277 /**
278 * Release the pipelines, if any, if they were looked up by this context.
279 */
280 public void dispose() {
281 if (!this.isBuildingPipelineOnly && this.pipelinesManager != null) {
282 if (this.pipelineSelector != null) {
283 this.pipelineSelector.release(this.processingPipeline);
284 this.processingPipeline = null;
285 this.pipelinesManager.release(this.pipelineSelector);
286 this.pipelineSelector = null;
287 }
288 this.pipelinesManager = null;
289
290 this.processingPipelineName = null;
291 this.processingPipelineParameters = null;
292 this.processingPipelineObjectModel = null;
293
294 this.errorHandler = null;
295 }
296 }
297
298 /**
299 * Set the error handler for the pipeline.
300 */
301 public void setErrorHandler(SitemapErrorHandler handler) {
302 this.errorHandler = handler;
303 }
304 }