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.sitemap;
018:
019: import java.util.HashMap;
020: import java.util.Iterator;
021: import java.util.Map;
022:
023: import org.apache.avalon.framework.activity.Disposable;
024: import org.apache.avalon.framework.component.ComponentException;
025: import org.apache.avalon.framework.component.ComponentManager;
026: import org.apache.avalon.framework.component.Composable;
027: import org.apache.cocoon.ProcessingException;
028: import org.apache.cocoon.components.pipeline.ProcessingPipeline;
029: import org.apache.cocoon.components.treeprocessor.AbstractProcessingNode;
030: import org.apache.cocoon.components.treeprocessor.InvokeContext;
031: import org.apache.cocoon.components.treeprocessor.TreeProcessor;
032: import org.apache.cocoon.components.treeprocessor.variables.VariableResolver;
033: import org.apache.cocoon.environment.Environment;
034: import org.apache.commons.lang.BooleanUtils;
035:
036: /**
037: *
038: * @author <a href="mailto:bluetkemeier@s-und-n.de">Björn Lütkemeier</a>
039: * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
040: * @version CVS $Id: MountNode.java 433543 2006-08-22 06:22:54Z crossley $
041: */
042: public class MountNode extends AbstractProcessingNode implements
043: Composable, Disposable {
044:
045: /** The key to get the pass_through value from the Environment */
046: public final static String COCOON_PASS_THROUGH = "COCOON_PASS_THROUGH";
047:
048: /** The 'uri-prefix' attribute */
049: private final VariableResolver prefix;
050:
051: /** The 'src' attribute */
052: private final VariableResolver source;
053:
054: /** Processors for sources */
055: private Map processors = new HashMap();
056:
057: /** The processor for this node */
058: private final TreeProcessor parentProcessor;
059:
060: /** The value of the 'check-reload' attribute */
061: private final boolean checkReload;
062:
063: /** The component manager to be used by the mounted processor */
064: private ComponentManager manager;
065:
066: /** The value of the 'pass-through' attribute */
067: private final Boolean passThrough;
068:
069: public MountNode(VariableResolver prefix, VariableResolver source,
070: TreeProcessor parentProcessor, boolean checkReload,
071: boolean passThrough) {
072: this .prefix = prefix;
073: this .source = source;
074: this .parentProcessor = parentProcessor;
075: this .checkReload = checkReload;
076: this .passThrough = BooleanUtils.toBooleanObject(passThrough);
077: }
078:
079: public void compose(ComponentManager manager)
080: throws ComponentException {
081: this .manager = manager;
082: }
083:
084: public final boolean invoke(Environment env, InvokeContext context)
085: throws Exception {
086: final Map objectModel = env.getObjectModel();
087:
088: String resolvedSource = this .source.resolve(context,
089: objectModel);
090: String resolvedPrefix = this .prefix.resolve(context,
091: objectModel);
092:
093: if (resolvedSource.length() == 0) {
094: throw new ProcessingException(
095: "Source of mount statement is empty");
096: }
097: TreeProcessor processor = getProcessor(resolvedSource);
098:
099: // Save context
100: String oldPrefix = env.getURIPrefix();
101: String oldURI = env.getURI();
102: String oldContext = env.getContext();
103: Object oldPassThrough = env.getAttribute(COCOON_PASS_THROUGH);
104: env.setAttribute(COCOON_PASS_THROUGH, this .passThrough);
105:
106: boolean pipelineWasBuilt = false;
107:
108: try {
109: env.changeContext(resolvedPrefix, resolvedSource);
110:
111: if (context.isBuildingPipelineOnly()) {
112: // Propagate pipelines
113: ProcessingPipeline pp = processor.buildPipeline(env);
114: if (pp != null) {
115: context.setProcessingPipeline(pp);
116: pipelineWasBuilt = true;
117: }
118: } else {
119: // Processor will create its own pipelines
120: pipelineWasBuilt = processor.process(env);
121: }
122: } catch (Exception e) {
123: // Wrap with our location
124: throw ProcessingException.throwLocated(
125: "Sitemap: error when calling sub-sitemap", e,
126: getLocation());
127:
128: } finally {
129: // We restore the context only if no pipeline was built. This allows the pipeline
130: // environment to be left unchanged when we go back to ancestor processors.
131: // If no pipeline was built, we restore the context, so that the current processor
132: // continues executing the sitemap in the correct environment.
133: if (!pipelineWasBuilt) {
134: env.setContext(oldPrefix, oldURI, oldContext);
135: }
136:
137: if (oldPassThrough != null) {
138: env.setAttribute(COCOON_PASS_THROUGH, oldPassThrough);
139: } else {
140: env.removeAttribute(COCOON_PASS_THROUGH);
141: }
142:
143: // Turning recomposing as a test, according to:
144: // http://marc.theaimsgroup.com/?t=106802211400005&r=1&w=2
145: // Recompose pipelines which may have been recomposed by subsitemap
146: // context.recompose(this.manager);
147: }
148:
149: return pipelineWasBuilt;
150: }
151:
152: private synchronized TreeProcessor getProcessor(String source)
153: throws Exception {
154:
155: TreeProcessor processor = (TreeProcessor) processors
156: .get(source);
157: if (processor == null) {
158: // Handle directory mounts
159: String actualSource;
160: if (source.charAt(source.length() - 1) == '/') {
161: actualSource = source + "sitemap.xmap";
162: } else {
163: actualSource = source;
164: }
165:
166: processor = this .parentProcessor.createChildProcessor(
167: this .manager, actualSource, this .checkReload);
168:
169: // Associate to the original source
170: processors.put(source, processor);
171: }
172:
173: return processor;
174: }
175:
176: /**
177: *
178: */
179: public void dispose() {
180: Iterator iter = this .processors.values().iterator();
181: while (iter.hasNext()) {
182: ((TreeProcessor) iter.next()).dispose();
183: }
184: }
185: }
|