blob: 14530b84c8c9e2f457d623bfa930ec376796bb8a [file] [log] [blame]
/*
* Copyright 1999-2005 The Apache Software Foundation.
*
* Licensed 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 java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.ServiceSelector;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.cocoon.Processor;
import org.apache.cocoon.components.pipeline.ProcessingPipeline;
import org.apache.cocoon.environment.Environment;
import org.apache.cocoon.environment.Redirector;
import org.apache.cocoon.sitemap.SitemapErrorHandler;
/**
* 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>
*
* @version $Id$
*/
public class InvokeContext extends AbstractLogEnabled
implements Serviceable, 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 ServiceManager currentManager;
/** The component manager that was used to get the pipelines */
private ServiceManager pipelinesManager;
/** The name of the processing pipeline component */
protected String processingPipelineName;
/** The parameters for the processing pipeline */
protected Parameters processingPipelineParameters;
/** The object model used to resolve processingPipelineParameters */
protected Map processingPipelineObjectModel;
/** The Selector for the processing pipeline */
protected ServiceSelector pipelineSelector;
/** The ProcessingPipeline used */
protected ProcessingPipeline processingPipeline;
/** The internal pipeline description */
protected Processor.InternalPipelineDescription internalPipelineDescription;
/** The error handler for the pipeline. */
protected SitemapErrorHandler errorHandler;
/** The last processor */
protected Processor lastProcessor;
/**
* 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;
}
/**
* Serviceable interface
*/
public void service(ServiceManager manager) throws ServiceException {
this.currentManager = manager;
if (this.processingPipeline != null) {
this.processingPipeline.setProcessorManager(manager);
}
}
/**
* Informs the context about a new pipeline section
*/
public void inform(String pipelineName,
Parameters parameters,
Map objectModel) {
this.processingPipelineName = pipelineName;
this.processingPipelineParameters = parameters;
this.processingPipelineObjectModel = objectModel;
}
public Parameters getPipelineParameters() {
return this.processingPipelineParameters;
}
public String getPipelineType() {
return this.processingPipelineName;
}
/**
* 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 = (ServiceSelector)this.pipelinesManager.lookup(ProcessingPipeline.ROLE+"Selector");
this.processingPipeline = (ProcessingPipeline)this.pipelineSelector.select(this.processingPipelineName);
this.processingPipeline.setProcessorManager(this.currentManager);
this.processingPipeline.setup(this.processingPipelineParameters);
this.processingPipeline.setErrorHandler(this.errorHandler);
}
return this.processingPipeline;
}
/**
* Set the processing pipeline for sub-sitemaps
*/
public void setInternalPipelineDescription(Processor.InternalPipelineDescription desc) {
this.processingPipeline = desc.processingPipeline;
this.pipelinesManager = desc.pipelineManager;
this.lastProcessor = desc.lastProcessor;
this.pipelineSelector = desc.pipelineSelector;
this.internalPipelineDescription = new Processor.InternalPipelineDescription(
this.processingPipeline, this.pipelineSelector, this.pipelinesManager);
this.internalPipelineDescription.lastProcessor = this.lastProcessor;
this.internalPipelineDescription.prefix = desc.prefix;
this.internalPipelineDescription.uri = desc.uri;
}
/**
* Get the pipeline description
*/
public Processor.InternalPipelineDescription getInternalPipelineDescription(Environment env) {
if ( this.internalPipelineDescription == null ) {
this.internalPipelineDescription = new Processor.InternalPipelineDescription(
this.processingPipeline, this.pipelineSelector, this.pipelinesManager);
this.internalPipelineDescription.lastProcessor = this.lastProcessor;
this.internalPipelineDescription.prefix = env.getURIPrefix();
this.internalPipelineDescription.uri = env.getURI();
}
return this.internalPipelineDescription;
}
/**
* Set the last processor
*/
public void setLastProcessor(Processor p) {
this.lastProcessor = p;
}
/**
* 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(map.get(key)).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.internalPipelineDescription == null && 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;
}
}
/**
* Set the error handler for the pipeline.
*/
public void setErrorHandler(SitemapErrorHandler handler) {
this.errorHandler = handler;
}
}