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.acting;
018:
019: import org.apache.avalon.framework.configuration.Configurable;
020: import org.apache.avalon.framework.configuration.Configuration;
021: import org.apache.avalon.framework.configuration.ConfigurationException;
022: import org.apache.avalon.framework.parameters.Parameters;
023: import org.apache.avalon.framework.service.ServiceSelector;
024: import org.apache.avalon.framework.thread.ThreadSafe;
025:
026: import org.apache.cocoon.components.modules.output.OutputModule;
027: import org.apache.cocoon.environment.Redirector;
028: import org.apache.cocoon.environment.SourceResolver;
029:
030: import java.util.ArrayList;
031: import java.util.Collections;
032: import java.util.HashMap;
033: import java.util.Iterator;
034: import java.util.List;
035: import java.util.Map;
036:
037: /**
038: * This is the action used to propagate parameters into a store using an
039: * {@link org.apache.cocoon.components.modules.output.OutputModule}. It
040: * simply propagates given expression. Additionaly it will make all propagated values
041: * available via returned Map.
042: *
043: * <p>Example configuration:</p>
044: * <pre>
045: * <map:action type="...." name="...." logger="...">
046: * <output-module name="session-attr">
047: * <!-- optional configuration for output module -->
048: * </output-module>
049: * <store-empty-parameters>true</store-empty-parameters>
050: * <defaults>
051: * <default name="..." value="...."/>
052: * <default name="..." value="..."/>
053: * </defaults>
054: * </map:action>
055: * </pre>
056: *
057: * <p>Example use:</p>
058: * <pre>
059: * <map:act type="session-propagator">
060: * <paramater name="example" value="{example}"/>
061: * <paramater name="example1" value="xxx"/>
062: * <parameter name="PropagatorAction:store-empty-parameters" value="true"/>
063: * <parameter name="PropagatorAction:output-module" value="session-attr"/>
064: * </map:act>
065: * </pre>
066: *
067: * <h3>Configuration</h3>
068: * <table><tbody>
069: * <tr>
070: * <th>output-module</th>
071: * <td>Nested element configuring output to use. Name attribute holds
072: * output module hint.</td>
073: * <td></td><td>XML</td><td><code>request-attr</code></td>
074: * </tr>
075: * <tr>
076: * <th>store-empty-parameters</th>
077: * <td>Propagate parameters with empty values.</td>
078: * <td></td><td>boolean</td><td><code>true</code></td>
079: * </tr>
080: * <tr>
081: * <th>defaults</th>
082: * <td>Parent for default parameters to propagate.</td>
083: * <td></td><td>XML</td><td></td>
084: * </tr>
085: * <tr>
086: * <th>defaults/default</th>
087: * <td>Name attribute holds parameter name, value attribute holds
088: * parameter value. Will be used when not set on use.</td>
089: * <td></td><td>parameter</td><td></td>
090: * </tr>
091: * </tbody></table>
092: *
093: *<h3>Parameters</h3>
094: * <table><tbody>
095: * <tr>
096: * <th>PropagatorAction:output-module</th>
097: * <td>Alternative output module hint to use. A <code>null</code> configuration
098: * will be passed to a module selected this way.</td>
099: * <td></td><td>String</td><td>as determined by configuration</td>
100: * </tr>
101: * <tr>
102: * <th>PropagatorAction:store-empty-parameters</th>
103: * <td>Propagate parameters with empty values.</td>
104: * <td></td><td>boolean</td><td>as determined by configuration</td>
105: * </tr>
106: * <tr>
107: * <th>any other</th>
108: * <td>Any other parameter will be propagated.</td>
109: * <td></td><td>String</td><td></td>
110: * </tr>
111: * </tbody></table>
112: *
113: * @author <a href="mailto:haul@apache.org">Christian Haul</a>
114: * @author <a href="mailto:Martin.Man@seznam.cz">Martin Man</a>
115: * @version CVS $Id: PropagatorAction.java 433543 2006-08-22 06:22:54Z crossley $
116: */
117: public class PropagatorAction extends ServiceableAction implements
118: Configurable, ThreadSafe {
119:
120: /** Prefix for sitemap parameters targeted at this action. */
121: private static final String ACTION_PREFIX = "PropagatorAction:";
122:
123: /** Configuration parameter name. */
124: private static final String CONFIG_STORE_EMPTY = "store-empty-parameters";
125:
126: /** Configuration parameter name. */
127: private static final String CONFIG_OUTPUT_MODULE = "output-module";
128:
129: /** Default output module name. */
130: private static final String OUTPUT_HINT = "request-attr"; // defaults to request attributes
131:
132: /** Should empty parameter values be propagated? */
133: private boolean storeEmpty = true;
134:
135: /** Configuration object for output module. */
136: private Configuration outputConf;
137:
138: /** Name of output module to use. */
139: private String outputName;
140:
141: /** List of {@link Entry}s holding default values. */
142: private List defaults;
143:
144: /**
145: * A private helper holding default parameter entries.
146: *
147: */
148: private static class Entry {
149: public String key;
150: public String value;
151:
152: public Entry(String key, String value) {
153: this .key = key;
154: this .value = value;
155: }
156: }
157:
158: /* (non-Javadoc)
159: * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
160: */
161: public void configure(Configuration config)
162: throws ConfigurationException {
163: this .outputConf = config.getChild(CONFIG_OUTPUT_MODULE);
164: this .outputName = this .outputConf.getAttribute("name",
165: OUTPUT_HINT);
166: this .storeEmpty = config.getChild(CONFIG_STORE_EMPTY)
167: .getValueAsBoolean(this .storeEmpty);
168:
169: Configuration[] dflts = config.getChild("defaults")
170: .getChildren("default");
171: if (dflts != null) {
172: this .defaults = new ArrayList(dflts.length);
173: for (int i = 0; i < dflts.length; i++) {
174: this .defaults.add(new Entry(dflts[i]
175: .getAttribute("name"), dflts[i]
176: .getAttribute("value")));
177: }
178: } else {
179: this .defaults = new ArrayList(0);
180: }
181: }
182:
183: /* (non-Javadoc)
184: * @see org.apache.cocoon.acting.Action#act(Redirector, SourceResolver, Map, String, Parameters)
185: */
186: public Map act(Redirector redirector, SourceResolver resolver,
187: Map objectModel, String source, Parameters parameters)
188: throws Exception {
189: // Read action parameters
190: String outputName = parameters.getParameter(ACTION_PREFIX
191: + CONFIG_OUTPUT_MODULE, null);
192: boolean storeEmpty = parameters.getParameterAsBoolean(
193: ACTION_PREFIX + CONFIG_STORE_EMPTY, this .storeEmpty);
194: parameters
195: .removeParameter(ACTION_PREFIX + CONFIG_OUTPUT_MODULE);
196: parameters.removeParameter(ACTION_PREFIX + CONFIG_STORE_EMPTY);
197:
198: Configuration outputConf = null;
199: if (outputName == null) {
200: outputName = this .outputName;
201: outputConf = this .outputConf;
202: }
203:
204: // Action results map
205: final Map results = new HashMap();
206:
207: OutputModule output = null;
208: ServiceSelector selector = null;
209: try {
210: selector = (ServiceSelector) this .manager
211: .lookup(OutputModule.ROLE + "Selector");
212: if (outputName != null && selector != null
213: && selector.isSelectable(outputName)) {
214:
215: output = (OutputModule) selector.select(outputName);
216:
217: String[] names = parameters.getNames();
218: for (int i = 0; i < names.length; i++) {
219: String name = names[i];
220: String value = parameters.getParameter(name);
221: if (storeEmpty
222: || (value != null && !value.equals(""))) {
223: if (getLogger().isDebugEnabled()) {
224: getLogger()
225: .debug(
226: "Propagating <" + name
227: + "> value <"
228: + value + ">");
229: }
230: output.setAttribute(outputConf, objectModel,
231: name, value);
232: results.put(name, value);
233: }
234: }
235:
236: // Defaults, that are not overridden
237: for (Iterator i = defaults.iterator(); i.hasNext();) {
238: Entry entry = (Entry) i.next();
239: if (!results.containsKey(entry.key)) {
240: if (getLogger().isDebugEnabled()) {
241: getLogger().debug(
242: "Propagating default <" + entry.key
243: + "> value <" + entry.value
244: + ">");
245: }
246: output.setAttribute(outputConf, objectModel,
247: entry.key, entry.value);
248: results.put(entry.key, entry.value);
249: }
250: }
251:
252: output.commit(outputConf, objectModel);
253: }
254: } catch (Exception e) {
255: if (output != null) {
256: output.rollback(outputConf, objectModel, e);
257: }
258: throw e;
259: } finally {
260: if (selector != null) {
261: if (output != null) {
262: selector.release(output);
263: }
264: this.manager.release(selector);
265: }
266: }
267:
268: return Collections.unmodifiableMap(results);
269: }
270: }
|