KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > acting > DatabaseAddAction


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 package org.apache.cocoon.acting;
17
18 import java.sql.Connection JavaDoc;
19 import java.sql.PreparedStatement JavaDoc;
20 import java.sql.ResultSet JavaDoc;
21 import java.sql.SQLException JavaDoc;
22 import java.util.Collections JavaDoc;
23 import java.util.Enumeration JavaDoc;
24 import java.util.HashMap JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.Map JavaDoc;
27 import java.util.SortedSet JavaDoc;
28 import java.util.TreeSet JavaDoc;
29
30 import org.apache.avalon.excalibur.datasource.DataSourceComponent;
31 import org.apache.avalon.framework.configuration.Configuration;
32 import org.apache.avalon.framework.configuration.ConfigurationException;
33 import org.apache.avalon.framework.parameters.Parameters;
34 import org.apache.avalon.framework.thread.ThreadSafe;
35 import org.apache.cocoon.Constants;
36 import org.apache.cocoon.ProcessingException;
37 import org.apache.cocoon.environment.ObjectModelHelper;
38 import org.apache.cocoon.environment.Redirector;
39 import org.apache.cocoon.environment.Request;
40 import org.apache.cocoon.environment.SourceResolver;
41 import org.apache.commons.lang.StringUtils;
42
43 /**
44  * Adds record in a database. The action can update one or more tables,
45  * and can add more than one row to a table at a time. The form descriptor
46  * semantics for this are still in a bit of a state of flux. Note
47  * that if a secondary table relies on the value of a new primary key in a
48  * primary table, the primary key must be created using manual mode.
49  *
50  * @author <a HREF="mailto:bloritsch@apache.org">Berin Loritsch</a>
51  * @author <a HREF="mailto:balld@apache.org">Donald Ball</a>
52  * @version CVS $Id: DatabaseAddAction.java 30932 2004-07-29 17:35:38Z vgritsenko $
53  */

54 public class DatabaseAddAction extends AbstractDatabaseAction implements ThreadSafe {
55     protected static final Map JavaDoc addStatements = new HashMap JavaDoc();
56     private static final Map JavaDoc selectStatements = new HashMap JavaDoc();
57
58     /**
59      * Add a record to the database. This action assumes that
60      * the file referenced by the "descriptor" parameter conforms
61      * to the AbstractDatabaseAction specifications.
62      */

63     public Map JavaDoc act(Redirector redirector, SourceResolver resolver, Map JavaDoc objectModel, String JavaDoc source, Parameters param) throws Exception JavaDoc {
64         DataSourceComponent datasource = null;
65         Connection JavaDoc conn = null;
66         Map JavaDoc results = new HashMap JavaDoc();
67
68         // read global parameter settings
69
boolean reloadable = Constants.DESCRIPTOR_RELOADABLE_DEFAULT;
70         if (this.settings.containsKey("reloadable"))
71             reloadable = Boolean.valueOf((String JavaDoc) this.settings.get("reloadable")).booleanValue();
72         // read local parameter settings
73
try {
74             Configuration conf =
75                 this.getConfiguration(param.getParameter("descriptor", (String JavaDoc) this.settings.get("descriptor")), resolver,
76                                       param.getParameterAsBoolean("reloadable",reloadable));
77
78             datasource = this.getDataSource(conf);
79             conn = datasource.getConnection();
80             Request request = ObjectModelHelper.getRequest(objectModel);
81
82             if (conn.getAutoCommit()) {
83                 conn.setAutoCommit(false);
84             }
85
86             Configuration[] tables = conf.getChildren("table");
87             for (int i=0; i<tables.length; i++) {
88               Configuration table = tables[i];
89               processTable(table,conn,request,results);
90             }
91             conn.commit();
92         } catch (Exception JavaDoc e) {
93             if (conn != null) {
94                 try {
95                     conn.rollback();
96                 } catch (SQLException JavaDoc se) {
97                     getLogger().debug("There was an error rolling back the transaction", se);
98                 }
99             }
100
101             //throw new ProcessingException("Could not add record :position = " + currentIndex, e);
102
throw new ProcessingException("Could not add record",e);
103         } finally {
104             if (conn != null) {
105                 try {
106                     conn.close();
107                 } catch (SQLException JavaDoc sqe) {
108                     getLogger().warn("There was an error closing the datasource", sqe);
109                 }
110             }
111
112             if (datasource != null) this.dbselector.release(datasource);
113         }
114
115         return Collections.unmodifiableMap(results);
116     }
117
118     /**
119      * Inserts a row or a set of rows into the given table based on the
120      * request parameters
121      *
122      * @param table the table's configuration
123      * @param conn the database connection
124      * @param request the request
125      */

126     void processTable(Configuration table, Connection JavaDoc conn, Request request, Map JavaDoc results) throws SQLException JavaDoc,ConfigurationException,Exception JavaDoc {
127       PreparedStatement JavaDoc statement = null;
128       try {
129         String JavaDoc query = this.getAddQuery(table);
130         getLogger().debug("Add query: "+query);
131         statement = conn.prepareStatement(query);
132         Configuration[] keys = table.getChild("keys").getChildren("key");
133         Configuration[] values = table.getChild("values").getChildren("value");
134         int currentIndex = 1;
135         boolean manyrows = false;
136         int wildcardIndex = -1;
137         String JavaDoc wildcardParam = null;
138         for (int i=0; i<keys.length; i++) {
139           wildcardParam = keys[i].getAttribute("param");
140           if ((wildcardIndex = wildcardParam.indexOf('*')) != -1) {
141             manyrows = true;
142             break;
143           }
144         }
145         if (manyrows) {
146           /**
147            * This table has a column with a wildcard, so we're going
148            * to be inserting n rows, where 0 <= n
149            */

150           String JavaDoc prefix = wildcardParam.substring(0,wildcardIndex);
151           String JavaDoc suffix = StringUtils.substring(wildcardParam, wildcardIndex + 1);
152           Enumeration JavaDoc names = request.getParameterNames();
153           SortedSet JavaDoc matchset = new TreeSet JavaDoc();
154           int prefixLength = prefix.length();
155           int length = prefixLength + suffix.length();
156           while (names.hasMoreElements()) {
157             String JavaDoc name = (String JavaDoc)names.nextElement();
158             if (name.startsWith(prefix) && name.endsWith(suffix)) {
159               String JavaDoc wildcard = StringUtils.mid(name, prefixLength, name.length() - length);
160               matchset.add(wildcard);
161             }
162           }
163           int rowIndex = 1;
164           Iterator JavaDoc iterator = matchset.iterator();
165           while (iterator.hasNext()) {
166             String JavaDoc wildcard = (String JavaDoc)iterator.next();
167             currentIndex = 1;
168             for (int j=0; j<keys.length; j++) {
169               String JavaDoc myparam = getActualParam(keys[j].getAttribute("param"),wildcard);
170               currentIndex += setKey(table,keys[j],conn,statement,currentIndex,request,myparam,results);
171             }
172             for (int j=0; j<values.length; j++) {
173               String JavaDoc myparam = getActualParam(values[j].getAttribute("param"),wildcard);
174               this.setColumn(statement,currentIndex,request,values[j],myparam,request.getParameter(myparam),rowIndex);
175               currentIndex++;
176             }
177             statement.execute();
178             rowIndex++;
179           }
180         } else {
181           /**
182            * This table has no wildcard columns, so we're going to
183            * be inserting 1 row.
184            */

185           for (int i = 0; i < keys.length; i++) {
186             currentIndex += setKey(table,keys[i],conn,statement,currentIndex,request,keys[i].getAttribute("param",""),results);
187           }
188           for (int i = 0; i < values.length; i++, currentIndex++) {
189             this.setColumn(statement, currentIndex, request, values[i]);
190           }
191           statement.execute();
192           /** Done processing table **/
193         }
194       } finally {
195         try {
196           if (statement != null) {
197             statement.close();
198           }
199         } catch (SQLException JavaDoc e) {}
200       }
201     }
202
203     /**
204      * Sets the key value on the prepared statement. There are four modes:
205      *
206      * <dl>
207      * <dt>automatic (default)</dt>
208      * <dd>let the database automatically create the key. note this
209      * prohibits the action from storing the key value anywhere.</dd>
210      * <dt>manual</dt>
211      * <dd>create the key value using SELECT(dbcol)+1 from TABLE</dd>
212      * <dt>form</dt>
213      * <dd>look for the key value in the request parameters</dd>
214      * <dt>request-attribute</dt>
215      * <dd>look for the key value in the request attributes</dd>
216      * </dl>
217      *
218      * This method has a couple of side effects. If the mode is manual,
219      * the key value is stored in the request object's attributes for use
220      * by other inserts. The key is the string "key:TABLENAME:DBCOL".
221      * This method also puts the value of manually created keys in the results
222      * map. That key is simply the value of the dbcol attribute. Note this
223      * stuff is definitely up in the air.
224      *
225      * @param table the table's configuration object
226      * @param key the key's configuration object
227      * @param conn the database connection
228      * @param statement the insert statement
229      * @param currentIndex the position of the key column
230      * @param request the request object
231      * @param param the actual name of the request parameter
232      * @return the number of columns by which to increment the currentIndex
233      */

234     int setKey(Configuration table, Configuration key, Connection JavaDoc conn, PreparedStatement JavaDoc statement, int currentIndex, Request request, String JavaDoc param, Map JavaDoc results) throws ConfigurationException, SQLException JavaDoc,Exception JavaDoc {
235       String JavaDoc mode = key.getAttribute("mode","automatic");
236       String JavaDoc keyname = new StringBuffer JavaDoc("key:").append(table.getAttribute("name"))
237                            .append(':').append(key.getAttribute("dbcol")).toString();
238       if ("manual".equals(mode)) {
239         // Set the key value using SELECT MAX(keyname)+1
240
String JavaDoc selectQuery = this.getSelectQuery(key);
241         PreparedStatement JavaDoc select_statement = conn.prepareStatement(selectQuery);
242         ResultSet JavaDoc set = select_statement.executeQuery();
243         set.next();
244         int value = set.getInt("maxid") + 1;
245         statement.setInt(currentIndex, value);
246         getLogger().debug("Manually setting key to " + value);
247         setRequestAttribute(request,keyname,new Integer JavaDoc(value));
248         results.put(key.getAttribute("dbcol"),String.valueOf(value));
249         set.close();
250         select_statement.close();
251       } else if ("form".equals(mode)) {
252         // Set the key value from the request
253
getLogger().debug("Setting key from form");
254         this.setColumn(statement, currentIndex, request, key, param);
255       } else if ("request-attribute".equals(mode)) {
256         Integer JavaDoc value = (Integer JavaDoc)getRequestAttribute(request,key.getAttribute("request-attribute-name"));
257         getLogger().debug("Setting key from request attribute "+value);
258         statement.setInt(currentIndex,value.intValue());
259       } else {
260         getLogger().debug("Automatically setting key");
261         // The database automatically creates a key value
262
return 0;
263       }
264       return 1;
265     }
266
267     /**
268      * Returns the actual name of the parameter. If the name contains
269      * no wildcard, the param is returned untouched, otherwise the
270      * wildcard value is substituted for the * character. This probably
271      * doesn't deserve a method unto itself, but I can imagine wanting
272      * to use a more sophisticated matching and substitution algorithm.
273      *
274      * @param param the name of the parameter, possibly with a wildcard char
275      * @param wildcard the wildcard value
276      * @return the actual name of the parameter
277      */

278     String JavaDoc getActualParam(String JavaDoc param, String JavaDoc wildcard) {
279       int index;
280       if ((index = param.indexOf('*')) != -1) {
281         return param.substring(0,index)+wildcard+param.substring(index+1);
282       } else {
283         return param;
284       }
285     }
286
287     /**
288      * Get the String representation of the PreparedStatement. This is
289      * mapped to the Configuration object itself, so if it doesn't exist,
290      * it will be created.
291      *
292      * @param table the table's configuration object
293      * @return the insert query as a string
294      */

295     protected String JavaDoc getAddQuery(Configuration table) throws ConfigurationException {
296         String JavaDoc query = null;
297         synchronized (DatabaseAddAction.addStatements) {
298             query = (String JavaDoc) DatabaseAddAction.addStatements.get(table);
299             if (query == null) {
300                 Configuration[] values = table.getChild("values").getChildren("value");
301                 Configuration[] keys = table.getChild("keys").getChildren("key");
302
303                 StringBuffer JavaDoc queryBuffer = new StringBuffer JavaDoc("INSERT INTO ");
304                 queryBuffer.append(table.getAttribute("name"));
305                 queryBuffer.append(" (");
306
307                 int numParams = 0;
308
309                 for (int i = 0; i < keys.length; i++) {
310                     String JavaDoc mode = keys[i].getAttribute("mode", "automatic");
311                     if ("manual".equals(mode) || "form".equals(mode) || "request-attribute".equals(mode)) {
312                         if (numParams > 0) {
313                             queryBuffer.append(", ");
314                         }
315                         queryBuffer.append(keys[i].getAttribute("dbcol"));
316                         this.setSelectQuery(table.getAttribute("name"), keys[i]);
317                         numParams++;
318                     }
319                 }
320                 queryBuffer.append(buildList(values, numParams));
321                 numParams += values.length;
322                 queryBuffer.append(") VALUES (");
323                 if (numParams > 0) {
324                     queryBuffer.append("?");
325                     queryBuffer.append(StringUtils.repeat(", ?", numParams - 1));
326                 }
327                 queryBuffer.append(")");
328                 query = queryBuffer.toString();
329
330                 DatabaseAddAction.addStatements.put(table, query);
331             }
332         }
333         return query;
334     }
335
336     /**
337      * Set the String representation of the MaxID lookup statement. This is
338      * mapped to the Configuration object itself, so if it doesn't exist,
339      * it will be created.
340      */

341     protected final synchronized void setSelectQuery(String JavaDoc tableName, Configuration entry) throws ConfigurationException {
342         StringBuffer JavaDoc queryBuffer = new StringBuffer JavaDoc("SELECT max(");
343         queryBuffer.append(entry.getAttribute("dbcol"));
344         queryBuffer.append(") AS maxid FROM ");
345         queryBuffer.append(tableName);
346
347         DatabaseAddAction.selectStatements.put(entry, queryBuffer.toString());
348     }
349
350     protected final synchronized String JavaDoc getSelectQuery(Configuration entry) throws ConfigurationException {
351         return (String JavaDoc) DatabaseAddAction.selectStatements.get(entry);
352     }
353 }
354
Popular Tags