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:
018: package org.apache.cocoon.components.modules.output;
019:
020: import java.util.Iterator;
021: import java.util.Map;
022:
023: import org.apache.avalon.framework.configuration.Configuration;
024: import org.apache.avalon.framework.logger.Logger;
025: import org.apache.cocoon.environment.ObjectModelHelper;
026: import org.apache.cocoon.environment.Session;
027:
028: /**
029: * Abstraction layer to encapsulate different output
030: * destinations. Configuration option <key-prefix> defaults to
031: * "org.apache.cocoon.components.modules.output.OutputModule"+":"
032: *
033: * Can be used with different isolation-level: default is "0" being
034: * no isolation at all, values are immediately visible but are removed
035: * on a rollback; "1" keeps the values at a save place until either
036: * rollback or commit is called. Then values are either discarded or
037: * copied to the final destination.
038: *
039: * @author <a href="mailto:haul@apache.org">Christian Haul</a>
040: * @version CVS $Id: SessionAttributeOutputModule.java 433543 2006-08-22 06:22:54Z crossley $
041: */
042: public class SessionAttributeOutputModule extends AbstractOutputModule {
043:
044: public final String PREFIX = "org.apache.cocoon.components.modules.output.OutputModule";
045: public final String TRANS_PREFIX = "org.apache.cocoon.components.modules.output.OutputModule.SessionAttributeOutputModule.transient";
046: public final String ROLLBACK_LIST = "org.apache.cocoon.components.modules.output.OutputModule.SessionAttributeOutputModule.rollback";
047:
048: /**
049: * communicate an attribute value to further processing logic.
050: * @param modeConf column's mode configuration from resource
051: * description. This argument is optional.
052: * @param objectModel The objectModel
053: * @param name The attribute's label, consisting of "table.column"
054: * or "table.column[index]" in case of multiple attributes of the
055: * same spec.
056: * @param value The attriute's value.
057: * */
058: public void setAttribute(Configuration modeConf, Map objectModel,
059: String name, Object value) {
060: if (this .settings.get("isolation-level", "0").equals("1")) {
061: if (getLogger().isDebugEnabled())
062: getLogger().debug(
063: "setting transient ['" + name + "'] to ['"
064: + value + "']");
065: this .transientSetAttribute(objectModel, TRANS_PREFIX, name,
066: value);
067: } else {
068: // use read uncommitted isolation level
069:
070: Session session = ObjectModelHelper.getRequest(objectModel)
071: .getSession();
072:
073: name = getName(name);
074:
075: if (!this .attributeExists(objectModel, ROLLBACK_LIST, name)) {
076: Object tmp = session.getAttribute(name);
077: this .transientSetAttribute(objectModel, ROLLBACK_LIST,
078: name, tmp);
079: }
080:
081: if (getLogger().isDebugEnabled())
082: getLogger()
083: .debug(
084: "setting ['" + name + "'] to ['"
085: + value + "']");
086: session.setAttribute(name, value);
087: }
088:
089: }
090:
091: /**
092: * If a database transaction needs to rollback, this is called to
093: * inform the further processing logic about this fact. All
094: * already set attribute values are invalidated. <em>This is difficult
095: * because only the request object can be used to synchronize this
096: * and build some kind of transaction object. Beaware that sending
097: * your data straight to some beans or other entities could result
098: * in data corruption!</em>
099: * */
100: public void rollback(Configuration modeConf, Map objectModel,
101: Exception e) {
102: if (this .settings.get("isolation-level", "0").equals("1")) {
103: if (getLogger().isDebugEnabled()) {
104: getLogger().debug("rolling back");
105: }
106: this .rollback(objectModel, TRANS_PREFIX);
107: } else {
108: if (getLogger().isDebugEnabled()) {
109: getLogger().debug("start rolling back");
110: }
111: Session session = ObjectModelHelper.getRequest(objectModel)
112: .getSession();
113: Map rollbackList = this .prepareCommit(objectModel,
114: ROLLBACK_LIST);
115: if (rollbackList != null) {
116: for (Iterator i = rollbackList.entrySet().iterator(); i
117: .hasNext();) {
118: Map.Entry me = (Map.Entry) i.next();
119: String key = (String) me.getKey();
120: Object val = me.getValue();
121: if (val != null) {
122: if (getLogger().isDebugEnabled()) {
123: getLogger().debug(
124: "rolling back ['" + key
125: + "'] to ['" + val + "']");
126: }
127: session.setAttribute(key, val);
128: } else {
129: if (getLogger().isDebugEnabled()) {
130: getLogger().debug(
131: "rolling back ['" + key + "']");
132: }
133: session.removeAttribute(key);
134: }
135: }
136: }
137: }
138: if (getLogger().isDebugEnabled()) {
139: getLogger().debug("done rolling back");
140: }
141: String prefix = (String) this .settings
142: .get("key-prefix", PREFIX);
143: if (!(prefix.equals(""))) {
144: ObjectModelHelper.getRequest(objectModel).getSession()
145: .setAttribute(prefix + ":", e.getMessage());
146: } else {
147: ObjectModelHelper.getRequest(objectModel).getSession()
148: .setAttribute("errorMessage", e.getMessage());
149: }
150: }
151:
152: /**
153: * Signal that the database transaction completed
154: * successfully. See notes on @link{rollback}.
155: * */
156: public void commit(Configuration modeConf, Map objectModel) {
157: if (this .settings.get("isolation-level", "0").equals("1")) {
158:
159: Logger logger = getLogger();
160: if (logger.isDebugEnabled()) {
161: logger.debug("prepare commit");
162: }
163: Map aMap = this .prepareCommit(objectModel, TRANS_PREFIX);
164: if (aMap == null || aMap.isEmpty()) {
165: return;
166: }
167: String prefix = (String) this .settings.get("key-prefix",
168: PREFIX);
169: if (prefix.length() > 0) {
170: prefix = prefix + ":";
171: } else {
172: prefix = null;
173: }
174: Session session = ObjectModelHelper.getRequest(objectModel)
175: .getSession();
176: for (Iterator i = aMap.entrySet().iterator(); i.hasNext();) {
177: Map.Entry me = (Map.Entry) i.next();
178: String key = (String) me.getKey();
179: Object value = me.getValue();
180: if (prefix != null) {
181: key = prefix + key;
182: }
183: if (logger.isDebugEnabled()) {
184: logger.debug("committing ['" + key + "'] to ['"
185: + value + "']");
186: }
187: session.setAttribute(key, value);
188: }
189: if (logger.isDebugEnabled()) {
190: logger.debug("done commit");
191: }
192: } else {
193: if (getLogger().isDebugEnabled())
194: getLogger().debug("commit");
195: this .prepareCommit(objectModel, ROLLBACK_LIST);
196: }
197: }
198:
199: protected String getName(String name) {
200: String prefix = (String) this .settings
201: .get("key-prefix", PREFIX);
202: return (prefix.length() == 0 ? name : prefix + ":" + name);
203: }
204:
205: }
|