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.acting.modular;
019:
020: import java.sql.Connection;
021: import java.sql.PreparedStatement;
022: import java.sql.SQLException;
023: import java.sql.Statement;
024: import java.util.Map;
025:
026: import org.apache.avalon.framework.configuration.Configuration;
027: import org.apache.avalon.framework.configuration.ConfigurationException;
028: import org.apache.avalon.framework.service.ServiceException;
029: import org.apache.avalon.framework.service.ServiceSelector;
030:
031: import org.apache.cocoon.components.modules.database.AutoIncrementModule;
032:
033: /**
034: * Adds record in a database. The action can update one or more
035: * tables, and can add more than one row to a table at a time. See
036: * {@link DatabaseAction} for details.
037: *
038: * @author <a href="mailto:haul@apache.org">Christian Haul</a>
039: * @version CVS $Id: DatabaseAddAction.java 433543 2006-08-22 06:22:54Z crossley $
040: */
041: public class DatabaseAddAction extends DatabaseAction {
042:
043: /**
044: * set all necessary ?s and execute the query
045: */
046: protected int processRow(Map objectModel, Connection conn,
047: PreparedStatement statement, String outputMode,
048: Configuration table, CacheHelper queryData,
049: Object[][] columnValues, int rowIndex, Map results)
050: throws SQLException, ConfigurationException, Exception {
051:
052: int currentIndex = 1;
053: for (int i = 0; i < queryData.columns.length; i++) {
054: Column col = queryData.columns[i];
055: if (col.isAutoIncrement && col.isKey) {
056: currentIndex += setKeyAuto(table, col, currentIndex,
057: rowIndex, conn, statement, objectModel,
058: outputMode, results);
059: } else {
060: this .setOutput(objectModel, outputMode, results, table,
061: col.columnConf, rowIndex,
062: columnValues[i][(col.isSet ? rowIndex : 0)]);
063: this .setColumn(statement, currentIndex, col.columnConf,
064: columnValues[i][(col.isSet ? rowIndex : 0)]);
065: currentIndex++;
066: }
067: }
068: int rowCount = statement.executeUpdate();
069: // get resulting ids for autoincrement columns
070: for (int i = 0; i < queryData.columns.length; i++) {
071: if (queryData.columns[i].isAutoIncrement
072: && queryData.columns[i].isKey) {
073: storeKeyValue(table, queryData.columns[i], rowIndex,
074: conn, statement, objectModel, outputMode,
075: results);
076: }
077: }
078: return rowCount;
079: }
080:
081: /**
082: * Sets the key value on the prepared statement for an autoincrement type.
083: *
084: * @param table the table's configuration object
085: * @param column the key's configuration object
086: * @param currentIndex the position of the key column
087: * @param rowIndex the position in the current row set
088: * @param conn the database connection
089: * @param statement the insert statement
090: * @param objectModel the objectModel object
091: * @param outputMode name of the requested output module
092: * @param results sitemap result object
093: * @return the number of columns by which to increment the currentIndex
094: */
095: protected int setKeyAuto(Configuration table, Column column,
096: int currentIndex, int rowIndex, Connection conn,
097: PreparedStatement statement, Map objectModel,
098: String outputMode, Map results)
099: throws ConfigurationException, SQLException, Exception {
100:
101: int columnCount = 0;
102: ServiceSelector autoincrSelector = null;
103: AutoIncrementModule autoincr = null;
104: try {
105: autoincrSelector = (ServiceSelector) this .manager
106: .lookup(DATABASE_MODULE_SELECTOR);
107: if (column.mode != null && autoincrSelector != null
108: && autoincrSelector.isSelectable(column.mode)) {
109: autoincr = (AutoIncrementModule) autoincrSelector
110: .select(column.mode);
111: }
112: if (autoincr != null) {
113: if (autoincr.includeInQuery()) {
114: if (autoincr.includeAsValue()) {
115: Object value = autoincr.getPreValue(table,
116: column.columnConf, column.modeConf,
117: conn, objectModel);
118: this .setColumn(objectModel, outputMode,
119: results, table, column.columnConf,
120: rowIndex, value, statement,
121: currentIndex);
122: columnCount = 1;
123: }
124: } else {
125: if (getLogger().isDebugEnabled()) {
126: getLogger().debug("Automatically setting key");
127: }
128: }
129: } else if (getLogger().isWarnEnabled()) {
130: getLogger()
131: .warn(
132: "Could not select autoincrement module"
133: + outputMode
134: + ". Defaulting to automatically setting key.");
135: }
136: } finally {
137: if (autoincrSelector != null) {
138: if (autoincr != null) {
139: autoincrSelector.release(autoincr);
140: }
141: this .manager.release(autoincrSelector);
142: }
143: }
144: return columnCount;
145: }
146:
147: /**
148: * Put key values into request attributes. Checks whether the
149: * value needs to be retrieved from the database module first.
150: *
151: */
152: protected void storeKeyValue(Configuration tableConf, Column key,
153: int rowIndex, Connection conn, Statement statement,
154: Map objectModel, String outputMode, Map results)
155: throws SQLException, ConfigurationException,
156: ServiceException {
157:
158: ServiceSelector autoincrSelector = null;
159: AutoIncrementModule autoincr = null;
160: try {
161: autoincrSelector = (ServiceSelector) this .manager
162: .lookup(DATABASE_MODULE_SELECTOR);
163: if (key.mode != null && autoincrSelector != null
164: && autoincrSelector.isSelectable(key.mode)) {
165: autoincr = (AutoIncrementModule) autoincrSelector
166: .select(key.mode);
167: }
168: if (autoincr != null) {
169: if (!autoincr.includeAsValue()) {
170: Object value = autoincr.getPostValue(tableConf,
171: key.columnConf, key.modeConf, conn,
172: statement, objectModel);
173: this .setOutput(objectModel, outputMode, results,
174: tableConf, key.columnConf, rowIndex, value);
175: }
176: } else {
177: throw new ConfigurationException(
178: "Could not obtain key value");
179: }
180: } finally {
181: if (autoincrSelector != null) {
182: if (autoincr != null) {
183: autoincrSelector.release(autoincr);
184: }
185: this .manager.release(autoincrSelector);
186: }
187: }
188: }
189:
190: /**
191: * determine which mode to use as default mode
192: * here: INSERT
193: * highly specific to operation INSERT / UPDATE / DELETE / SELECT
194: */
195: protected String selectMode(boolean isAutoIncrement, Map modes) {
196:
197: if (isAutoIncrement)
198: return (String) modes.get(MODE_AUTOINCR);
199: else
200: return (String) modes.get(MODE_OTHERS);
201: }
202:
203: /**
204: * determine whether autoincrement columns should be honoured by
205: * this operation. This is usually snsible only for INSERTs.
206: */
207: protected boolean honourAutoIncrement() {
208: return true;
209: }
210:
211: /**
212: * Fetch all values for all columns that are needed to do the
213: * database operation.
214: */
215: protected Object[][] getColumnValues(Configuration tableConf,
216: CacheHelper queryData, Map objectModel)
217: throws ConfigurationException, ServiceException {
218:
219: Object[][] columnValues = new Object[queryData.columns.length][];
220: for (int i = 0; i < queryData.columns.length; i++) {
221: columnValues[i] = this .getColumnValue(tableConf,
222: queryData.columns[i], objectModel);
223: }
224: return columnValues;
225: }
226:
227: /**
228: * Get the String representation of the PreparedStatement. This is
229: * mapped to the Configuration object itself, so if it doesn't exist,
230: * it will be created.
231: * @param table the table's configuration object
232: * @return the insert query as a string
233: */
234: protected CacheHelper getQuery(Configuration table, Map modeTypes,
235: Map defaultModeNames) throws ConfigurationException,
236: ServiceException {
237:
238: LookUpKey lookUpKey = new LookUpKey(table, modeTypes);
239: CacheHelper queryData = null;
240: synchronized (this .cachedQueryData) {
241: queryData = (CacheHelper) this .cachedQueryData
242: .get(lookUpKey);
243:
244: if (queryData == null) {
245: Configuration[] values = table.getChild("values")
246: .getChildren("value");
247: Configuration[] keys = table.getChild("keys")
248: .getChildren("key");
249:
250: queryData = new CacheHelper(keys.length, keys.length
251: + values.length);
252: fillModes(keys, true, defaultModeNames, modeTypes,
253: queryData);
254: fillModes(values, false, defaultModeNames, modeTypes,
255: queryData);
256:
257: StringBuffer queryBuffer = new StringBuffer(
258: "INSERT INTO ");
259: StringBuffer valueBuffer = new StringBuffer(
260: ") VALUES (");
261:
262: queryBuffer.append(table.getAttribute("name"));
263: queryBuffer.append(" (");
264: int actualColumns = 0;
265:
266: for (int i = 0; i < queryData.columns.length; i++) {
267: if (actualColumns > 0) {
268: queryBuffer.append(", ");
269: valueBuffer.append(", ");
270: }
271: if (queryData.columns[i].isKey
272: && queryData.columns[i].isAutoIncrement) {
273:
274: ServiceSelector autoincrSelector = null;
275: AutoIncrementModule autoincr = null;
276: try {
277: autoincrSelector = (ServiceSelector) this .manager
278: .lookup(DATABASE_MODULE_SELECTOR);
279: if (queryData.columns[i].mode != null
280: && autoincrSelector != null
281: && autoincrSelector
282: .isSelectable(queryData.columns[i].mode)) {
283: autoincr = (AutoIncrementModule) autoincrSelector
284: .select(queryData.columns[i].mode);
285:
286: if (autoincr.includeInQuery()) {
287: actualColumns++;
288: queryBuffer
289: .append(queryData.columns[i].columnConf
290: .getAttribute("name"));
291: if (autoincr.includeAsValue()) {
292: valueBuffer.append("?");
293: } else {
294: valueBuffer
295: .append(autoincr
296: .getSubquery(
297: table,
298: queryData.columns[i].columnConf,
299: queryData.columns[i].modeConf));
300: }
301: }
302: } else {
303: if (getLogger().isErrorEnabled())
304: getLogger()
305: .error(
306: "Could not find mode description "
307: + queryData.columns[i].mode
308: + " for column #"
309: + i);
310: if (getLogger().isDebugEnabled()) {
311: getLogger()
312: .debug(
313: "Column data "
314: + queryData.columns[i]);
315: }
316: throw new ConfigurationException(
317: "Could not find mode description "
318: + queryData.columns[i].mode
319: + " for column " + i);
320: }
321: } finally {
322: if (autoincrSelector != null) {
323: if (autoincr != null)
324: autoincrSelector.release(autoincr);
325: this .manager.release(autoincrSelector);
326: }
327: }
328: } else {
329: actualColumns++;
330: queryBuffer
331: .append(queryData.columns[i].columnConf
332: .getAttribute("name"));
333: valueBuffer.append("?");
334: }
335: }
336: valueBuffer.append(")");
337: queryBuffer.append(valueBuffer);
338:
339: queryData.queryString = queryBuffer.toString();
340:
341: this.cachedQueryData.put(lookUpKey, queryData);
342: }
343: }
344: return queryData;
345: }
346: }
|