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.sitemap;
18
19 import java.util.HashMap;
20 import java.util.Iterator;
21 import java.util.Map;
22
23 import org.apache.avalon.framework.activity.Disposable;
24 import org.apache.avalon.framework.component.ComponentException;
25 import org.apache.avalon.framework.component.ComponentManager;
26 import org.apache.avalon.framework.component.Composable;
27 import org.apache.cocoon.ProcessingException;
28 import org.apache.cocoon.components.pipeline.ProcessingPipeline;
29 import org.apache.cocoon.components.treeprocessor.AbstractProcessingNode;
30 import org.apache.cocoon.components.treeprocessor.InvokeContext;
31 import org.apache.cocoon.components.treeprocessor.TreeProcessor;
32 import org.apache.cocoon.components.treeprocessor.variables.VariableResolver;
33 import org.apache.cocoon.environment.Environment;
34 import org.apache.commons.lang.BooleanUtils;
35
36 /**
37 *
38 * @author <a href="mailto:bluetkemeier@s-und-n.de">Björn Lütkemeier</a>
39 * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
40 * @version CVS $Id: MountNode.java 433543 2006-08-22 06:22:54Z crossley $
41 */
42 public class MountNode extends AbstractProcessingNode
43 implements Composable, Disposable {
44
45 /** The key to get the pass_through value from the Environment */
46 public final static String COCOON_PASS_THROUGH = "COCOON_PASS_THROUGH";
47
48 /** The 'uri-prefix' attribute */
49 private final VariableResolver prefix;
50
51 /** The 'src' attribute */
52 private final VariableResolver source;
53
54 /** Processors for sources */
55 private Map processors = new HashMap();
56
57 /** The processor for this node */
58 private final TreeProcessor parentProcessor;
59
60 /** The value of the 'check-reload' attribute */
61 private final boolean checkReload;
62
63 /** The component manager to be used by the mounted processor */
64 private ComponentManager manager;
65
66 /** The value of the 'pass-through' attribute */
67 private final Boolean passThrough;
68
69 public MountNode(VariableResolver prefix,
70 VariableResolver source,
71 TreeProcessor parentProcessor,
72 boolean checkReload,
73 boolean passThrough) {
74 this.prefix = prefix;
75 this.source = source;
76 this.parentProcessor = parentProcessor;
77 this.checkReload = checkReload;
78 this.passThrough = BooleanUtils.toBooleanObject(passThrough);
79 }
80
81 public void compose(ComponentManager manager) throws ComponentException {
82 this.manager = manager;
83 }
84
85 public final boolean invoke(Environment env, InvokeContext context)
86 throws Exception {
87 final Map objectModel = env.getObjectModel();
88
89 String resolvedSource = this.source.resolve(context, objectModel);
90 String resolvedPrefix = this.prefix.resolve(context, objectModel);
91
92 if (resolvedSource.length() == 0) {
93 throw new ProcessingException("Source of mount statement is empty");
94 }
95 TreeProcessor processor = getProcessor(resolvedSource);
96
97 // Save context
98 String oldPrefix = env.getURIPrefix();
99 String oldURI = env.getURI();
100 String oldContext = env.getContext();
101 Object oldPassThrough = env.getAttribute(COCOON_PASS_THROUGH);
102 env.setAttribute(COCOON_PASS_THROUGH, this.passThrough);
103
104 boolean pipelineWasBuilt = false;
105
106 try {
107 env.changeContext(resolvedPrefix, resolvedSource);
108
109 if (context.isBuildingPipelineOnly()) {
110 // Propagate pipelines
111 ProcessingPipeline pp = processor.buildPipeline(env);
112 if (pp != null) {
113 context.setProcessingPipeline( pp );
114 pipelineWasBuilt = true;
115 }
116 } else {
117 // Processor will create its own pipelines
118 pipelineWasBuilt = processor.process(env);
119 }
120 } catch(Exception e) {
121 // Wrap with our location
122 throw ProcessingException.throwLocated("Sitemap: error when calling sub-sitemap", e, getLocation());
123
124 } finally {
125 // We restore the context only if no pipeline was built. This allows the pipeline
126 // environment to be left unchanged when we go back to ancestor processors.
127 // If no pipeline was built, we restore the context, so that the current processor
128 // continues executing the sitemap in the correct environment.
129 if (!pipelineWasBuilt) {
130 env.setContext(oldPrefix, oldURI, oldContext);
131 }
132
133 if (oldPassThrough != null) {
134 env.setAttribute(COCOON_PASS_THROUGH, oldPassThrough);
135 } else {
136 env.removeAttribute(COCOON_PASS_THROUGH);
137 }
138
139 // Turning recomposing as a test, according to:
140 // http://marc.theaimsgroup.com/?t=106802211400005&r=1&w=2
141 // Recompose pipelines which may have been recomposed by subsitemap
142 // context.recompose(this.manager);
143 }
144
145 return pipelineWasBuilt;
146 }
147
148 private synchronized TreeProcessor getProcessor(String source)
149 throws Exception {
150
151 TreeProcessor processor = (TreeProcessor) processors.get(source);
152 if (processor == null) {
153 // Handle directory mounts
154 String actualSource;
155 if (source.charAt(source.length() - 1) == '/') {
156 actualSource = source + "sitemap.xmap";
157 } else {
158 actualSource = source;
159 }
160
161 processor = this.parentProcessor.createChildProcessor(this.manager, actualSource, this.checkReload);
162
163 // Associate to the original source
164 processors.put(source, processor);
165 }
166
167 return processor;
168 }
169
170 /**
171 *
172 */
173 public void dispose() {
174 Iterator iter = this.processors.values().iterator();
175 while(iter.hasNext()) {
176 ((TreeProcessor)iter.next()).dispose();
177 }
178 }
179 }