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