/*
* 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.components.treeprocessor;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.ComponentSelector;
import org.apache.avalon.framework.component.Recomposable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.cocoon.components.CocoonComponentManager;
import org.apache.cocoon.components.pipeline.ProcessingPipeline;
import org.apache.cocoon.components.treeprocessor.variables.VariableResolver;
import org.apache.cocoon.environment.Redirector;
import org.apache.cocoon.sitemap.SitemapErrorHandler;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* The invocation context of <code>ProcessingNode</code>s.
*
* <p>This class serves two purposes:
* <ul>
* <li>Avoid explicit enumeration of all needed parameters in
* {@link ProcessingNode#invoke(org.apache.cocoon.environment.Environment, InvokeContext)},
* thus allowing easier addition of new parameters,</li>
* <li>Hold pipelines, and provide "just in time" lookup for them.</li>
* </ul>
*
* @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
* @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
* @author <a href="mailto:tcurdt@apache.org">Torsten Curdt</a>
* @version $Id$
*/
public class InvokeContext extends AbstractLogEnabled
implements Recomposable, Disposable {
private List mapStack = new ArrayList();
private HashMap nameToMap = new HashMap();
private HashMap mapToName = new HashMap();
/** True if building pipeline only, not processing it. */
private boolean isBuildingPipelineOnly;
/** The redirector */
protected Redirector redirector;
/** The current component manager, as set by the last call to compose() or recompose() */
private ComponentManager currentManager;
/** The component manager that was used to get the pipelines */
private ComponentManager pipelinesManager;
/** The name of the processing pipeline component */
protected String processingPipelineName;
/** The parameters for the processing pipeline */
protected Map processingPipelineParameters;
/** The object model used to resolve processingPipelineParameters */
protected Map processingPipelineObjectModel;
/** The Selector for the processing pipeline */
protected ComponentSelector pipelineSelector;
/** The ProcessingPipeline used */
protected ProcessingPipeline processingPipeline;
/** The error handler for the pipeline. */
protected SitemapErrorHandler errorHandler;
/**
* Create an <code>InvokeContext</code> without existing pipelines. This also means
* the current request is external.
*/
public InvokeContext() {
this.isBuildingPipelineOnly = false;
}
/**
* Determines if the Pipeline been set for this context
*/
public boolean pipelineIsSet() {
return (this.processingPipeline != null);
}
/**
* Create an <code>InvokeContext</code>
*/
public InvokeContext(boolean isBuildingPipelineOnly) {
this.isBuildingPipelineOnly = isBuildingPipelineOnly;
}
/**
* Composable Interface
*/
public void compose(ComponentManager manager) throws ComponentException {
this.currentManager = manager;
}
/**
* Recomposable interface
*/
public void recompose(ComponentManager manager) throws ComponentException {
this.currentManager = manager;
if (this.processingPipeline != null) {
this.processingPipeline.recompose(manager);
}
}
/**
* Informs the context about a new pipeline section
*/
public void inform(String pipelineName,
Map parameters,
Map objectModel) {
this.processingPipelineName = pipelineName;
this.processingPipelineParameters = parameters;
this.processingPipelineObjectModel = objectModel;
}
/**
* Get the current <code>ProcessingPipeline</code>
*/
public ProcessingPipeline getProcessingPipeline()
throws Exception {
if (this.processingPipeline == null) {
// Keep current manager for proper release
this.pipelinesManager = this.currentManager;
this.pipelineSelector = (ComponentSelector)this.pipelinesManager.lookup(ProcessingPipeline.ROLE+"Selector");
this.processingPipeline = (ProcessingPipeline)this.pipelineSelector.select(this.processingPipelineName);
this.processingPipeline.recompose( this.pipelinesManager );
this.processingPipeline.setup(
VariableResolver.buildParameters(this.processingPipelineParameters,
this,
this.processingPipelineObjectModel)
);
if (this.isBuildingPipelineOnly) {
CocoonComponentManager.addComponentForAutomaticRelease(this.pipelineSelector,
this.processingPipeline,
this.pipelinesManager);
}
this.processingPipeline.setErrorHandler(this.errorHandler);
}
return this.processingPipeline;
}
/**
* Set the processing pipeline for sub-sitemaps
*/
public void setProcessingPipeline(ProcessingPipeline pipeline) {
this.processingPipeline = pipeline;
}
/**
* Are we building a pipeline (and not executing it) ?
*/
public final boolean isBuildingPipelineOnly() {
return this.isBuildingPipelineOnly;
}
/**
* Get the current Map stack used to resolve expressions.
*/
public final List getMapStack() {
return this.mapStack;
}
/**
* Get the result Map by anchor name
*/
public final Map getMapByAnchor(String anchor) {
return (Map) this.nameToMap.get(anchor);
}
/**
* Push a Map on top of the current Map stack.
*/
public final void pushMap(String anchorName, Map map) {
this.mapStack.add(map);
if (getLogger().isDebugEnabled()) {
dumpParameters();
}
if (anchorName != null) {
if (!this.nameToMap.containsKey(anchorName)) {
this.nameToMap.put(anchorName,map);
this.mapToName.put(map,anchorName);
} else {
if (getLogger().isErrorEnabled()) {
getLogger().error("name [" + anchorName + "] clashes");
}
}
}
}
/**
* Dumps all sitemap parameters to log
*/
protected void dumpParameters() {
if (!this.mapStack.isEmpty()) {
StringBuffer sb = new StringBuffer();
sb.append("\nCurrent Sitemap Parameters:\n");
String path = "";
for (int i = this.mapStack.size() - 1; i >= 0; i--) {
Map map = (Map) this.mapStack.get(i);
sb.append("LEVEL ").append(i+1);
if (this.mapToName.containsKey(map)) {
sb.append(" is named '").append(String.valueOf(this.mapToName.get(map))).append("'");
}
sb.append("\n");
for (Iterator iter = map.entrySet().iterator(); iter.hasNext(); ) {
final Map.Entry me = (Map.Entry)iter.next();
final Object key = me.getKey();
sb.append("PARAM: '").append(path).append(key).append("' ");
sb.append("VALUE: '").append(me.getValue()).append("'\n");
}
path = "../" + path;
}
getLogger().debug(sb.toString());
}
}
/**
* Pop the topmost element of the current Map stack.
*/
public final void popMap() {
Object map = this.mapStack.remove(this.mapStack.size() - 1);
Object name = this.mapToName.get(map);
this.mapToName.remove(map);
this.nameToMap.remove(name);
}
/**
* Set the redirector to be used by nodes that need it.
*
* @param redirector the redirector
*/
public void setRedirector(Redirector redirector) {
this.redirector = redirector;
}
/**
* Get the redirector to be used by nodes that need it.
*
* @return the redirector
*/
public Redirector getRedirector() {
return this.redirector;
}
/**
* Release the pipelines, if any, if they were looked up by this context.
*/
public void dispose() {
if (!this.isBuildingPipelineOnly && this.pipelinesManager != null) {
if (this.pipelineSelector != null) {
this.pipelineSelector.release(this.processingPipeline);
this.processingPipeline = null;
this.pipelinesManager.release(this.pipelineSelector);
this.pipelineSelector = null;
}
this.pipelinesManager = null;
this.processingPipelineName = null;
this.processingPipelineParameters = null;
this.processingPipelineObjectModel = null;
this.errorHandler = null;
}
}
/**
* Set the error handler for the pipeline.
*/
public void setErrorHandler(SitemapErrorHandler handler) {
this.errorHandler = handler;
}
}