KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > acting > modular > DatabaseAction


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.cocoon.acting.modular;
18
19 import java.io.IOException JavaDoc;
20 import java.sql.Connection JavaDoc;
21 import java.sql.PreparedStatement JavaDoc;
22 import java.sql.SQLException JavaDoc;
23 import java.util.Map JavaDoc;
24
25 import org.apache.avalon.excalibur.datasource.DataSourceComponent;
26 import org.apache.avalon.framework.activity.Disposable;
27 import org.apache.avalon.framework.configuration.Configurable;
28 import org.apache.avalon.framework.configuration.Configuration;
29 import org.apache.avalon.framework.configuration.ConfigurationException;
30 import org.apache.avalon.framework.parameters.Parameters;
31 import org.apache.avalon.framework.service.ServiceException;
32 import org.apache.avalon.framework.service.ServiceManager;
33 import org.apache.avalon.framework.service.ServiceSelector;
34 import org.apache.avalon.framework.thread.ThreadSafe;
35
36 import org.apache.cocoon.Constants;
37 import org.apache.cocoon.ProcessingException;
38 import org.apache.cocoon.acting.AbstractComplementaryConfigurableAction;
39 import org.apache.cocoon.components.modules.database.AutoIncrementModule;
40 import org.apache.cocoon.components.modules.input.InputModule;
41 import org.apache.cocoon.components.modules.output.OutputModule;
42 import org.apache.cocoon.environment.Redirector;
43 import org.apache.cocoon.environment.SourceResolver;
44 import org.apache.cocoon.util.HashMap;
45 import org.apache.cocoon.util.JDBCTypeConversions;
46 import org.apache.commons.lang.BooleanUtils;
47
48 /**
49  * Abstract action for common function needed by database actions.
50  * The difference to the other Database*Actions is, that the actions
51  * in this package use additional components ("modules") for reading
52  * and writing parameters. In addition the descriptor format has
53  * changed to accomodate the new features.
54  *
55  * <p>This action is heavily based upon the original DatabaseAddActions.</p>
56  *
57  * <p>Modes have to be configured in cocoon.xconf. Mode names from
58  * descriptor.xml file are looked up in the component service. Default
59  * mode names can only be set during action setup. </p>
60  *
61  * <p>The number of affected rows is returned to the sitemap with the
62  * "row-count" parameter if at least one row was affected.</p>
63  *
64  * <p>All known column types can be found in
65  * {@link org.apache.cocoon.util.JDBCTypeConversions JDBCTypeConversions}.</p>
66  *
67  * <table>
68  * <tr><td colspan="2">Configuration options (setup):</td></tr>
69  * <tr><td>input </td><td>default mode name for reading values (request-param)</td></tr>
70  * <tr><td>autoincrement </td><td>default mode name for obtaining values from autoincrement columns (auto)</td></tr>
71  * <tr><td>append-row </td><td>append row number in square brackets to column name for output (yes)</td></tr>
72  * <tr><td>append-table-name</td><td>add table name to column name for both in- and output (yes)</td></tr>
73  * <tr><td>first-row </td><td>row index of first row (0)</td></tr>
74  * <tr><td>path-separator </td><td>string to separate table name from column name (.)</td></tr>
75  * </table>
76  *
77  * <table>
78  * <tr><td colspan="2">Configuration options (setup and per invocation):</td></tr>
79  * <tr><td>throw-exception </td><td>throw an exception when an error occurs (default: false)</td></tr>
80  * <tr><td>descriptor </td><td>file containing database description</td></tr>
81  * <tr><td>table-set </td><td>table-set name to work with </td></tr>
82  * <tr><td>output </td><td>mode name for writing values (request-attr)</td></tr>
83  * <tr><td>reloadable </td><td>dynamically reload descriptor file if change is detected</td></tr>
84  * <tr><td>use-transactions </td><td>defaults to yes</td></tr>
85  * <tr><td>connection </td><td>configured datasource connection to use (overrides value from descriptor file)</td></tr>
86  * <tr><td>fail-on-empty </td><td>(boolean) fail is statement affected zero rows (true)</td></tr>
87  * </table>
88  *
89  * @author <a HREF="mailto:haul@apache.org">Christian Haul</a>
90  * @version CVS $Id: DatabaseAction.java 124673 2005-01-08 20:17:19Z antonio $
91  * @see org.apache.cocoon.components.modules.input
92  * @see org.apache.cocoon.components.modules.output
93  * @see org.apache.cocoon.components.modules.database
94  * @see org.apache.cocoon.util.JDBCTypeConversions
95  */

96 public abstract class DatabaseAction extends AbstractComplementaryConfigurableAction implements Configurable, Disposable, ThreadSafe {
97
98     // ========================================================================
99
// constants
100
// ========================================================================
101

102     static final Integer JavaDoc MODE_AUTOINCR = new Integer JavaDoc(0);
103     static final Integer JavaDoc MODE_OTHERS = new Integer JavaDoc(1);
104     static final Integer JavaDoc MODE_OUTPUT = new Integer JavaDoc(2);
105
106     static final String JavaDoc ATTRIBUTE_KEY = "org.apache.cocoon.action.modular.DatabaseAction.outputModeName";
107
108     // These can be overidden from cocoon.xconf
109
static final String JavaDoc inputHint = "request-param"; // default to request parameters
110
static final String JavaDoc outputHint = "request-attr"; // default to request attributes
111
static final String JavaDoc databaseHint = "manual"; // default to manual auto increments
112

113     static final String JavaDoc INPUT_MODULE_SELECTOR = InputModule.ROLE + "Selector";
114     static final String JavaDoc OUTPUT_MODULE_SELECTOR = OutputModule.ROLE + "Selector";
115     static final String JavaDoc DATABASE_MODULE_SELECTOR = AutoIncrementModule.ROLE + "Selector";
116
117
118     // ========================================================================
119
// instance vars
120
// ========================================================================
121

122     protected ServiceSelector dbselector;
123     protected Map JavaDoc defaultModeNames = new HashMap( 3 );
124     protected final HashMap cachedQueryData = new HashMap();
125     protected String JavaDoc pathSeparator = ".";
126     protected int firstRow = 0;
127     protected boolean failOnEmpty = true;
128
129     // ========================================================================
130
// inner helper classes
131
// ========================================================================
132

133     /**
134      * Structure that takes all processed data for one column.
135      */

136     protected static class Column {
137         boolean isKey = false;
138         boolean isSet = false;
139         boolean isAutoIncrement = false;
140         String JavaDoc mode = null;
141         Configuration modeConf = null;
142         Configuration columnConf = null;
143     }
144
145     /**
146      * Structure that takes all processed data for a table depending
147      * on current default modes
148      */

149     protected static class CacheHelper {
150         /**
151          * Generated query string
152          */

153         public String JavaDoc queryString = null;
154         /**
155          * if a set is used, column number which is used to determine
156          * the number of rows to insert.
157          */

158         public int setMaster = -1;
159         public boolean isSet = false;
160         public int noOfKeys = 0;
161         public Column[] columns = null;
162
163         public CacheHelper( int cols ) {
164             this(0,cols);
165         }
166
167         public CacheHelper( int keys, int cols ) {
168             noOfKeys = keys;
169             columns = new Column[cols];
170             for ( int i=0; i<cols; i++ ) {
171                 columns[i] = new Column();
172             }
173         }
174     }
175
176     /**
177      * Structure that takes up both current mode types for database
178      * operations and table configuration data. Used to access parsed
179      * configuration data.
180      */

181     protected static class LookUpKey {
182         public Configuration tableConf = null;
183         public Map JavaDoc modeTypes = null;
184
185         public LookUpKey( Configuration tableConf, Map JavaDoc modeTypes ) {
186             this.tableConf = tableConf;
187             this.modeTypes = modeTypes;
188         }
189         
190         /* (non-Javadoc)
191          * @see java.lang.Object#equals(java.lang.Object)
192          */

193         public boolean equals(Object JavaDoc obj) {
194             boolean result = false;
195             if (obj != null && obj instanceof LookUpKey) {
196                 LookUpKey luk = (LookUpKey) obj;
197                 result = true;
198                 result = result && (luk.tableConf == null ?
199                             this.tableConf == null : luk.tableConf.equals(this.tableConf));
200                 result = result && (luk.modeTypes == null ?
201                             this.modeTypes == null : luk.modeTypes.equals(this.modeTypes));
202             }
203             
204             return result;
205         }
206         
207         /* (non-Javadoc)
208          * @see java.lang.Object#hashCode()
209          */

210         public int hashCode() {
211             return (this.tableConf != null ?
212                     this.tableConf.hashCode() :
213                         (this.modeTypes != null ? this.modeTypes.hashCode() : super.hashCode()));
214         }
215     }
216
217     // set up default modes
218
// <input/>
219
// <output/>
220
// <autoincrement/>
221
//
222
// all other modes need to be declared in cocoon.xconf
223
// no need to declare them per action (anymore!)
224
public void configure(Configuration conf) throws ConfigurationException {
225         super.configure(conf);
226         if (this.settings != null) {
227             this.defaultModeNames.put(MODE_OTHERS, this.settings.get("input", inputHint));
228             this.defaultModeNames.put(MODE_OUTPUT, this.settings.get("output", outputHint));
229             this.defaultModeNames.put(MODE_AUTOINCR, this.settings.get("autoincrement", databaseHint));
230             this.pathSeparator = (String JavaDoc)this.settings.get("path-separator", this.pathSeparator);
231             String JavaDoc tmp = (String JavaDoc)this.settings.get("first-row",null);
232             if (tmp != null) {
233                 try {
234                         this.firstRow = Integer.parseInt(tmp);
235                 } catch (NumberFormatException JavaDoc nfe) {
236                     if (getLogger().isWarnEnabled())
237                         getLogger().warn("problem parsing first row option "+tmp+" using default instead.");
238                 }
239             }
240             tmp = (String JavaDoc) this.settings.get("fail-on-empty",String.valueOf(this.failOnEmpty));
241             this.failOnEmpty = BooleanUtils.toBoolean(tmp);
242         }
243     }
244
245     // ========================================================================
246
// Avalon methods
247
// ========================================================================
248

249     /**
250      * Compose the Actions so that we can select our databases.
251      */

252     public void service(ServiceManager manager) throws ServiceException {
253         super.service(manager);
254         this.dbselector = (ServiceSelector) manager.lookup(DataSourceComponent.ROLE + "Selector");
255     }
256
257     /**
258      * dispose
259      */

260     public void dispose() {
261         this.manager.release(dbselector);
262     }
263
264     // ========================================================================
265
// protected utility methods
266
// ========================================================================
267

268     /**
269      * Get the Datasource we need.
270      */

271     protected DataSourceComponent getDataSource( Configuration conf, Parameters parameters )
272         throws ServiceException {
273
274         String JavaDoc sourceName = parameters.getParameter( "connection", (String JavaDoc) settings.get( "connection" ) );
275         if ( sourceName == null ) {
276             Configuration dsn = conf.getChild("connection");
277             return (DataSourceComponent) this.dbselector.select(dsn.getValue(""));
278         } else {
279             if (getLogger().isDebugEnabled())
280                 getLogger().debug("Using datasource: "+sourceName);
281             return (DataSourceComponent) this.dbselector.select(sourceName);
282         }
283     }
284
285     /**
286      * Return whether a type is a Large Object (BLOB/CLOB).
287      */

288     protected final boolean isLargeObject (String JavaDoc type) {
289         if ("ascii".equals(type)) return true;
290         if ("binary".equals(type)) return true;
291         if ("image".equals(type)) return true;
292         return false;
293     }
294
295     /**
296      * Store a key/value pair in the output attributes. We prefix the key
297      * with the name of this class to prevent potential name collisions.
298      */

299     protected void setOutputAttribute(Map JavaDoc objectModel, String JavaDoc outputMode, String JavaDoc key, Object JavaDoc value) {
300
301         ServiceSelector outputSelector = null;
302         OutputModule output = null;
303         try {
304             outputSelector = (ServiceSelector) this.manager.lookup(OUTPUT_MODULE_SELECTOR);
305             if (outputMode != null && outputSelector != null && outputSelector.isSelectable(outputMode)) {
306                 output = (OutputModule) outputSelector.select(outputMode);
307             }
308             if (output != null) {
309                 output.setAttribute(null, objectModel, key, value);
310             } else if (getLogger().isWarnEnabled()) {
311                 getLogger().warn("Could not select output mode " + outputMode);
312             }
313         } catch (Exception JavaDoc e) {
314                 if (getLogger().isWarnEnabled()) {
315                     getLogger().warn("Could not select output mode " + outputMode + ":" + e.getMessage());
316                 }
317         } finally {
318             if (outputSelector != null) {
319                 if (output != null)
320                     outputSelector.release(output);
321                 this.manager.release(outputSelector);
322             }
323          }
324     }
325
326     /**
327      * Inserts a row or a set of rows into the given table based on the
328      * request parameters
329      *
330      * @param table the table's configuration
331      * @param conn the database connection
332      * @param objectModel the objectModel
333      */

334     protected int processTable( Configuration table, Connection JavaDoc conn, Map JavaDoc objectModel,
335                                  Map JavaDoc results, Map JavaDoc modeTypes )
336         throws SQLException JavaDoc, ConfigurationException, Exception JavaDoc {
337
338         PreparedStatement JavaDoc statement = null;
339         int rows = 0;
340         try {
341             LookUpKey luk = new LookUpKey(table, modeTypes);
342             CacheHelper queryData = null;
343
344             if (getLogger().isDebugEnabled())
345                 getLogger().debug("modeTypes : "+ modeTypes);
346
347             // get cached data
348
// synchronize complete block since we don't want 100s of threads
349
// generating the same cached data set. In the long run all data
350
// is cached anyways so this won't cost much.
351
synchronized (this.cachedQueryData) {
352                 queryData = (CacheHelper) this.cachedQueryData.get(luk,null);
353                 if (queryData == null) {
354                     queryData = this.getQuery( table, modeTypes, defaultModeNames );
355                     this.cachedQueryData.put(luk,queryData);
356                 }
357             }
358
359             if (getLogger().isDebugEnabled())
360                 getLogger().debug("query: "+queryData.queryString);
361             statement = conn.prepareStatement(queryData.queryString);
362
363             Object JavaDoc[][] columnValues = this.getColumnValues( table, queryData, objectModel );
364
365             int setLength = 1;
366             if ( queryData.isSet ) {
367                 if ( columnValues[ queryData.setMaster ] != null ) {
368                     setLength = columnValues[ queryData.setMaster ].length;
369                 } else {
370                     setLength = 0;
371                 }
372             }
373
374             for ( int rowIndex = 0; rowIndex < setLength; rowIndex++ ) {
375                 if (getLogger().isDebugEnabled()) {
376                     getLogger().debug( "====> row no. " + rowIndex );
377                 }
378                 rows += processRow( objectModel, conn, statement, (String JavaDoc) modeTypes.get(MODE_OUTPUT), table, queryData, columnValues, rowIndex, results );
379             }
380         } finally {
381             try {
382                 if (statement != null) {
383                     statement.close();
384                 }
385             } catch (SQLException JavaDoc e) {}
386         }
387         return rows;
388     }
389
390     /**
391      * Choose a mode configuration based on its name.
392      * @param conf Configuration (i.e. a column's configuration) that might have
393      * several children configurations named "mode".
394      * @param type desired type (i.e. every mode has a type
395      * attribute), find the first mode that has a compatible type.
396      * Special mode "all" matches all queried types.
397      * @return configuration that has desired type or type "all" or null.
398      */

399     protected Configuration getMode( Configuration conf, String JavaDoc type )
400         throws ConfigurationException {
401
402         String JavaDoc modeAll = "all";
403         Configuration[] modes = conf.getChildren("mode");
404         Configuration modeConfig = null;
405
406         for ( int i=0; i<modes.length; i++ ) {
407             String JavaDoc modeType = modes[i].getAttribute("type", "others");
408             if ( modeType.equals(type) || modeType.equals(modeAll)) {
409                 if (getLogger().isDebugEnabled())
410                     getLogger().debug("requested mode was \""+type+"\" returning \""+modeType+"\"");
411                 modeConfig = modes[i];
412                 break;
413             }
414         }
415         return modeConfig;
416     }
417
418     /**
419      * compose name for output a long the lines of "table.column"
420      */

421     protected String JavaDoc getOutputName ( Configuration tableConf, Configuration columnConf ) {
422
423         return getOutputName( tableConf, columnConf, -1 );
424     }
425
426     /**
427      * compose name for output a long the lines of "table.column[row]" or
428      * "table.column" if rowIndex is -1.
429      * If the section of the sitemap corresponding to the action contains
430      * <append-table-name>false</append-table-name>
431      * the name for output is "column[row]"
432      * If the section of the sitemap corresponding to the action contains
433      * <append-row>false</append-row>
434      * the name for output is "column"
435      */

436     protected String JavaDoc getOutputName ( Configuration tableConf, Configuration columnConf, int rowIndex ) {
437
438         if ( rowIndex != -1 && this.settings.containsKey("append-row") &&
439              (this.settings.get("append-row").toString().equalsIgnoreCase("false") ||
440               this.settings.get("append-row").toString().equalsIgnoreCase("0")) ) {
441             rowIndex = -1;
442         } else {
443             rowIndex = rowIndex + this.firstRow;
444         }
445         if ( this.settings.containsKey("append-table-name") &&
446              (this.settings.get("append-table-name").toString().equalsIgnoreCase("false") ||
447               this.settings.get("append-table-name").toString().equalsIgnoreCase("0")) )
448             {
449                 return ( columnConf.getAttribute("name",null)
450                          + ( rowIndex == -1 ? "" : "[" + rowIndex + "]" ) );
451         } else {
452                 return ( tableConf.getAttribute("alias", tableConf.getAttribute("name", null) )
453                          + this.pathSeparator + columnConf.getAttribute("name",null)
454                          + ( rowIndex == -1 ? "" : "[" + rowIndex + "]" ) );
455             }
456     }
457
458     /*
459      * Read all values for a column from an InputModule
460      *
461      * If the given column is an autoincrement column, an empty array
462      * is returned, otherwise if it is part of a set, all available
463      * values are fetched, or only the first one if it is not part of
464      * a set.
465      *
466      */

467     protected Object JavaDoc[] getColumnValue(Configuration tableConf, Column column, Map JavaDoc objectModel)
468         throws ConfigurationException, ServiceException {
469
470         if (column.isAutoIncrement) {
471             return new Object JavaDoc[1];
472         } else {
473             Object JavaDoc[] values;
474             String JavaDoc cname = getOutputName( tableConf, column.columnConf );
475
476             // obtain input module and read values
477
ServiceSelector inputSelector = null;
478             InputModule input = null;
479             try {
480                 inputSelector = (ServiceSelector) this.manager.lookup(INPUT_MODULE_SELECTOR);
481                 if (column.mode != null && inputSelector != null && inputSelector.isSelectable(column.mode)){
482                     input = (InputModule) inputSelector.select(column.mode);
483                 }
484
485                 if (column.isSet) {
486                     if (getLogger().isDebugEnabled()) {
487                         getLogger().debug( "Trying to set column " + cname + " from " + column.mode + " using getAttributeValues method");
488                     }
489                     values = input.getAttributeValues( cname, column.modeConf, objectModel );
490                 } else {
491                     if (getLogger().isDebugEnabled()) {
492                         getLogger().debug( "Trying to set column " + cname + " from " + column.mode + " using getAttribute method");
493                     }
494                     values = new Object JavaDoc[1];
495                     values[0] = input.getAttribute( cname, column.modeConf, objectModel );
496                 }
497
498                 if (values != null) {
499                     for ( int i = 0; i < values.length; i++ ) {
500                         if (getLogger().isDebugEnabled()) {
501                             getLogger().debug( "Setting column " + cname + " [" + i + "] " + values[i] );
502                         }
503                     }
504                 }
505             } finally {
506                 if (inputSelector != null) {
507                     if (input != null) {
508                         inputSelector.release(input);
509                     }
510                     this.manager.release(inputSelector);
511                 }
512             }
513             return values;
514         }
515     }
516
517     /**
518      * Setup parsed attribute configuration object
519      */

520     protected void fillModes ( Configuration[] conf, boolean isKey, Map JavaDoc defaultModeNames,
521                                Map JavaDoc modeTypes, CacheHelper set )
522         throws ConfigurationException {
523
524         String JavaDoc setMode = null;
525         int offset = (isKey ? 0: set.noOfKeys);
526
527         for (int i = offset; i < conf.length + offset; i++) {
528             if (getLogger().isDebugEnabled()) {
529                 getLogger().debug("i=" + i);
530             }
531             set.columns[i].columnConf = conf[ i - offset ];
532             set.columns[i].isSet = false;
533             set.columns[i].isKey = isKey;
534             set.columns[i].isAutoIncrement = false;
535             if (isKey & this.honourAutoIncrement()) {
536                 set.columns[i].isAutoIncrement = set.columns[i].columnConf.getAttributeAsBoolean("autoincrement",false);
537             }
538             set.columns[i].modeConf = getMode(set.columns[i].columnConf,
539                                                selectMode(set.columns[i].isAutoIncrement, modeTypes));
540             set.columns[i].mode = (set.columns[i].modeConf != null ?
541                                    set.columns[i].modeConf.getAttribute("name", selectMode(isKey, defaultModeNames)) :
542                                    selectMode(isKey, defaultModeNames));
543             // Determine set mode for a whole column ...
544
setMode = set.columns[i].columnConf.getAttribute("set", null); // master vs slave vs null
545
if (setMode == null && set.columns[i].modeConf != null) {
546                 // ... or for each mode individually
547
setMode = set.columns[i].modeConf.getAttribute("set", null);
548             }
549             if (setMode != null) {
550                 set.columns[i].isSet = true;
551                 set.isSet = true;
552                 if (setMode.equals("master")) {
553                     set.setMaster = i;
554                 }
555             }
556         }
557     }
558
559     /**
560      * create a unique name using the getOutputName method and write
561      * the value to the output module and the results map if present.
562      *
563      */

564     protected void setOutput( Map JavaDoc objectModel, String JavaDoc outputMode, Map JavaDoc results,
565                          Configuration table, Configuration column, int rowIndex, Object JavaDoc value ) {
566
567         String JavaDoc param = this.getOutputName( table, column, rowIndex );
568         if (getLogger().isDebugEnabled()) {
569             getLogger().debug( "Setting column " + param + " to " + value );
570         }
571         this.setOutputAttribute(objectModel, outputMode, param, value);
572         if (results != null) {
573             results.put( param, String.valueOf( value ) );
574         }
575     }
576
577     /**
578      * set a column in a statement using the appropriate JDBC setXXX method.
579      *
580      */

581     protected void setColumn (PreparedStatement JavaDoc statement, int position, Configuration entry, Object JavaDoc value) throws Exception JavaDoc {
582         JDBCTypeConversions.setColumn(statement, position, value,
583                 (Integer JavaDoc)JDBCTypeConversions.typeConstants.get(entry.getAttribute("type")));
584     }
585
586     /**
587      * set a column in a statement using the appropriate JDBC setXXX
588      * method and propagate the value to the output module and results
589      * map if present. Effectively combines calls to setColumn and
590      * setOutput.
591      *
592      */

593     protected void setColumn (Map JavaDoc objectModel, String JavaDoc outputMode, Map JavaDoc results,
594                                Configuration table, Configuration column, int rowIndex,
595                                Object JavaDoc value, PreparedStatement JavaDoc statement, int position) throws Exception JavaDoc {
596
597         if (results != null) {
598             this.setOutput(objectModel, outputMode, results, table, column, rowIndex, value);
599         }
600         this.setColumn( statement, position, column, value );
601     }
602
603     // ========================================================================
604
// main method
605
// ========================================================================
606

607     /**
608      * Add a record to the database. This action assumes that
609      * the file referenced by the "descriptor" parameter conforms
610      * to the AbstractDatabaseAction specifications.
611      */

612     public Map JavaDoc act(Redirector redirector, SourceResolver resolver, Map JavaDoc objectModel,
613                     String JavaDoc source, Parameters param) throws Exception JavaDoc {
614
615         DataSourceComponent datasource = null;
616         Connection JavaDoc conn = null;
617         Map JavaDoc results = new HashMap();
618         int rows = 0;
619         boolean failed = false;
620
621         // read global parameter settings
622
boolean reloadable = Constants.DESCRIPTOR_RELOADABLE_DEFAULT;
623
624         // call specific default modes apart from output mode are not supported
625
// set request attribute
626
String JavaDoc outputMode = param.getParameter("output", (String JavaDoc) defaultModeNames.get(MODE_OUTPUT));
627
628         if (this.settings.containsKey("reloadable")) {
629             reloadable = Boolean.valueOf((String JavaDoc) this.settings.get("reloadable")).booleanValue();
630         }
631         // read local parameter settings
632
try {
633             Configuration conf =
634                 this.getConfiguration(param.getParameter("descriptor", (String JavaDoc) this.settings.get("descriptor")),
635                                       resolver,
636                                       param.getParameterAsBoolean("reloadable",reloadable));
637
638             // get database connection and try to turn off autocommit
639
datasource = this.getDataSource(conf, param);
640             conn = datasource.getConnection();
641             if (conn.getAutoCommit() == true) {
642                 try {
643                     conn.setAutoCommit(false);
644                 } catch (Exception JavaDoc ex) {
645                     String JavaDoc tmp = param.getParameter("use-transactions",(String JavaDoc) this.settings.get("use-transactions",null));
646                     if (tmp != null && (tmp.equalsIgnoreCase("no") || tmp.equalsIgnoreCase("false") || tmp.equalsIgnoreCase("0"))) {
647                         if (getLogger().isErrorEnabled())
648                             getLogger().error("This DB connection does not support transactions. If you want to risk your data's integrity by continuing nonetheless set parameter \"use-transactions\" to \"no\".");
649                         throw ex;
650                     }
651                 }
652             }
653
654             // find tables to work with
655
Configuration[] tables = conf.getChildren("table");
656             String JavaDoc tablesetname = param.getParameter("table-set", (String JavaDoc) this.settings.get("table-set"));
657
658             Map JavaDoc modeTypes = null;
659
660             if (tablesetname == null) {
661                 modeTypes = new HashMap(6);
662                 modeTypes.put( MODE_AUTOINCR, "autoincr" );
663                 modeTypes.put( MODE_OTHERS, "others" );
664                 modeTypes.put( MODE_OUTPUT, outputMode );
665                 for (int i = 0; i < tables.length; i++) {
666                     rows += processTable(tables[i], conn, objectModel, results, modeTypes);
667                 }
668             } else {
669                 // new set based behaviour
670

671                 // create index for table names / aliases
672
Map JavaDoc tableIndex = new HashMap(2*tables.length);
673                 String JavaDoc tableName = null;
674                 Object JavaDoc result = null;
675                 for (int i=0; i<tables.length; i++) {
676                     tableName = tables[i].getAttribute("alias",tables[i].getAttribute("name",""));
677                     result = tableIndex.put(tableName,new Integer JavaDoc(i));
678                     if (result != null) {
679                         throw new IOException JavaDoc("Duplicate table entry for "+tableName+" at positions "+result+" and "+i);
680                     }
681                 }
682
683                 Configuration[] tablesets = conf.getChildren("table-set");
684                 String JavaDoc setname = null;
685                 boolean found = false;
686
687                 // find tables contained in tableset
688
int j = 0;
689                 for (j = 0; j < tablesets.length; j++) {
690                     setname = tablesets[j].getAttribute ("name", "");
691                     if (tablesetname.trim().equals (setname.trim ())) {
692                         found = true;
693                         break;
694                     }
695                 }
696                 if (!found) {
697                     throw new IOException JavaDoc(" given set " + tablesetname + " does not exists in a description file.");
698                 }
699
700                 Configuration[] set = tablesets[j].getChildren("table");
701
702                 for (int i = 0; i < set.length; i++) {
703                     // look for alternative mode types
704
modeTypes = new HashMap(6);
705                     modeTypes.put( MODE_AUTOINCR, set[i].getAttribute( "autoincr-mode", "autoincr" ) );
706                     modeTypes.put( MODE_OTHERS, set[i].getAttribute( "others-mode", "others" ) );
707                     modeTypes.put( MODE_OUTPUT, outputMode );
708                     tableName=set[i].getAttribute("name","");
709                     if (tableIndex.containsKey(tableName)) {
710                         j = ((Integer JavaDoc)tableIndex.get(tableName)).intValue();
711                         rows += processTable( tables[j], conn, objectModel, results, modeTypes );
712                     } else {
713                         throw new IOException JavaDoc(" given table " + tableName + " does not exists in a description file.");
714                     }
715                 }
716             }
717
718             if (conn.getAutoCommit() == false) {
719                 conn.commit();
720             }
721
722             // obtain output mode module and rollback output
723
ServiceSelector outputSelector = null;
724             OutputModule output = null;
725             try {
726                 outputSelector = (ServiceSelector) this.manager.lookup(OUTPUT_MODULE_SELECTOR);
727                 if (outputMode != null && outputSelector != null && outputSelector.isSelectable(outputMode)){
728                     output = (OutputModule) outputSelector.select(outputMode);
729                 }
730                 if (output != null) {
731                     output.commit(null, objectModel);
732                 } else if (getLogger().isWarnEnabled()) {
733                     getLogger().warn("Could not select output mode " + outputMode);
734                 }
735             } catch (ServiceException e) {
736                 if (getLogger().isWarnEnabled()) {
737                     getLogger().warn("Could not select output mode " + outputMode + ":" + e.getMessage());
738                 }
739             } finally {
740                 if (outputSelector != null) {
741                     if (output != null) {
742                         outputSelector.release(output);
743                     }
744                     this.manager.release(outputSelector);
745                 }
746             }
747         } catch (Exception JavaDoc e) {
748             failed = true;
749             if ( conn != null ) {
750                 try {
751                     if (getLogger().isDebugEnabled()) {
752                         getLogger().debug( "Rolling back transaction. Caused by " + e.getMessage() );
753                         e.printStackTrace();
754                     }
755                     conn.rollback();
756                     results = null;
757
758                     // obtain output mode module and commit output
759
ServiceSelector outputSelector = null;
760                     OutputModule output = null;
761                     try {
762                         outputSelector = (ServiceSelector) this.manager.lookup(OUTPUT_MODULE_SELECTOR);
763                         if (outputMode != null && outputSelector != null && outputSelector.isSelectable(outputMode)){
764                             output = (OutputModule) outputSelector.select(outputMode);
765                         }
766                         if (output != null) {
767                             output.rollback( null, objectModel, e);
768                         } else if (getLogger().isWarnEnabled()) {
769                             getLogger().warn("Could not select output mode " + outputMode);
770                         }
771                     } catch (ServiceException e2) {
772                         if (getLogger().isWarnEnabled()) {
773                             getLogger().warn("Could not select output mode " + outputMode + ":" + e2.getMessage());
774                         }
775                     } finally {
776                         if (outputSelector != null) {
777                             if (output != null) {
778                                 outputSelector.release(output);
779                             }
780                             this.manager.release(outputSelector);
781                         }
782                     }
783                 } catch (SQLException JavaDoc se) {
784                     if (getLogger().isDebugEnabled())
785                         getLogger().debug("There was an error rolling back the transaction", se);
786                 }
787             }
788
789             //throw new ProcessingException("Could not add record :position = " + currentIndex, e);
790

791             // don't throw an exception, an error has been signalled, that should suffice
792

793             String JavaDoc throwException = (String JavaDoc) this.settings.get( "throw-exception",
794                                                                 param.getParameter( "throw-exception", null ) );
795             if ( throwException != null && BooleanUtils.toBoolean(throwException)) {
796                 throw new ProcessingException("Cannot process the requested SQL statement ",e);
797             }
798         } finally {
799             if (conn != null) {
800                 try {
801                     conn.close();
802                 } catch (SQLException JavaDoc sqe) {
803                     getLogger().warn("There was an error closing the datasource", sqe);
804                 }
805             }
806
807             if (datasource != null)
808                 this.dbselector.release(datasource);
809         }
810         if (results != null) {
811             if (rows>0 || (!failed && !this.failOnEmpty)) {
812                     results.put("row-count",new Integer JavaDoc(rows));
813             } else {
814                 results = null;
815             }
816         } else {
817             if (rows>0) {
818                 results = new HashMap(1);
819                 results.put("row-count",new Integer JavaDoc(rows));
820             }
821         }
822
823         return results; // (results == null? results : Collections.unmodifiableMap(results));
824
}
825
826     // ========================================================================
827
// abstract methods
828
// ========================================================================
829

830     /**
831      * set all necessary ?s and execute the query
832      * return number of rows processed
833      *
834      * This method is intended to be overridden by classes that
835      * implement other operations e.g. delete
836      */

837     protected abstract int processRow( Map JavaDoc objectModel, Connection JavaDoc conn, PreparedStatement JavaDoc statement, String JavaDoc outputMode,
838                                        Configuration table, CacheHelper queryData, Object JavaDoc[][] columnValues,
839                                        int rowIndex, Map JavaDoc results )
840         throws SQLException JavaDoc, ConfigurationException, Exception JavaDoc;
841
842     /**
843      * determine which mode to use as default mode
844      *
845      * This method is intended to be overridden by classes that
846      * implement other operations e.g. delete
847      */

848     protected abstract String JavaDoc selectMode( boolean isAutoIncrement, Map JavaDoc modes );
849
850     /**
851      * determine whether autoincrement columns should be honoured by
852      * this operation. This is usually snsible only for INSERTs.
853      *
854      * This method is intended to be overridden by classes that
855      * implement other operations e.g. delete
856      */

857     protected abstract boolean honourAutoIncrement();
858
859     /**
860      * Fetch all values for all columns that are needed to do the
861      * database operation.
862      *
863      * This method is intended to be overridden by classes that
864      * implement other operations e.g. delete
865      */

866     abstract Object JavaDoc[][] getColumnValues( Configuration tableConf, CacheHelper queryData, Map JavaDoc objectModel )
867         throws ConfigurationException, ServiceException;
868
869     /**
870      * Get the String representation of the PreparedStatement. This is
871      * mapped to the Configuration object itself, so if it doesn't exist,
872      * it will be created.
873      *
874      * This method is intended to be overridden by classes that
875      * implement other operations e.g. delete
876      *
877      * @param table the table's configuration object
878      * @return the insert query as a string
879      */

880     protected abstract CacheHelper getQuery( Configuration table, Map JavaDoc modeTypes, Map JavaDoc defaultModeNames )
881         throws ConfigurationException, ServiceException;
882
883 }
884
Popular Tags