KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jcorporate > expresso > core > db > TableCreator


1 /* ====================================================================
2  * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
3  *
4  * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  *
18  * 3. The end-user documentation included with the redistribution,
19  * if any, must include the following acknowledgment:
20  * "This product includes software developed by Jcorporate Ltd.
21  * (http://www.jcorporate.com/)."
22  * Alternately, this acknowledgment may appear in the software itself,
23  * if and wherever such third-party acknowledgments normally appear.
24  *
25  * 4. "Jcorporate" and product names such as "Expresso" must
26  * not be used to endorse or promote products derived from this
27  * software without prior written permission. For written permission,
28  * please contact info@jcorporate.com.
29  *
30  * 5. Products derived from this software may not be called "Expresso",
31  * or other Jcorporate product names; nor may "Expresso" or other
32  * Jcorporate product names appear in their name, without prior
33  * written permission of Jcorporate Ltd.
34  *
35  * 6. No product derived from this software may compete in the same
36  * market space, i.e. framework, without prior written permission
37  * of Jcorporate Ltd. For written permission, please contact
38  * partners@jcorporate.com.
39  *
40  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
41  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
42  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43  * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
44  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
46  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
47  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
48  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
49  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
50  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  * ====================================================================
53  *
54  * This software consists of voluntary contributions made by many
55  * individuals on behalf of the Jcorporate Ltd. Contributions back
56  * to the project(s) are encouraged when you make modifications.
57  * Please send them to support@jcorporate.com. For more information
58  * on Jcorporate Ltd. and its products, please see
59  * <http://www.jcorporate.com/>.
60  *
61  * Portions of this software are based upon other open source
62  * products and are subject to their respective licenses.
63  */

64
65 package com.jcorporate.expresso.core.db;
66
67 import com.jcorporate.expresso.core.dataobjects.Securable;
68 import com.jcorporate.expresso.core.dataobjects.jdbc.JDBCObjectMetaData;
69 import com.jcorporate.expresso.core.db.config.JDBCConfig;
70 import com.jcorporate.expresso.core.dbobj.DBIndex;
71 import com.jcorporate.expresso.core.dbobj.DBObject;
72 import com.jcorporate.expresso.core.dbobj.DBObjectDef;
73 import com.jcorporate.expresso.core.dbobj.Schema;
74 import com.jcorporate.expresso.core.misc.ConfigManager;
75 import com.jcorporate.expresso.core.misc.StringUtil;
76 import com.jcorporate.expresso.kernel.management.ExpressoRuntimeMap;
77 import com.jcorporate.expresso.kernel.util.FastStringBuffer;
78 import com.jcorporate.expresso.kernel.util.LocatorUtils;
79 import com.jcorporate.expresso.services.dbobj.DBOtherMap;
80 import com.jcorporate.expresso.services.dbobj.Setup;
81 import org.apache.log4j.Logger;
82
83 import java.lang.ref.WeakReference JavaDoc;
84 import java.util.Enumeration JavaDoc;
85 import java.util.Iterator JavaDoc;
86 import java.util.Vector JavaDoc;
87
88 /**
89  * A singleton utility class that helps create tables. It is stored as a weak reference
90  * since it will be seldom used and when done should be discarded.
91  *
92  * @author Michael Rimov
93  * <p/>
94  * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
95  * @since $DatabaseSchema $Date: 2004/11/18 02:03:27 $
96  */

97
98 public class TableCreator {
99
100     /**
101      * The log4j Logger. This one is not static since the whole
102      * reference is a weak reference, this way, the System can gc the logger
103      * object as well.
104      */

105     private Logger log = Logger.getLogger(TableCreator.class);
106
107     /**
108      * The one and only instance
109      */

110     static WeakReference JavaDoc theInstance = null;
111     /**
112      * setup name for switch about using HSQL option for 'cached' tables (special case for hsql driver)
113      */

114     public static final String JavaDoc USE_CACHED_HSQL = "UseCachedHSQL";
115
116     protected TableCreator() {
117     }
118
119     /**
120      * The way to a Table Creator's Heart; or more importantly the way to
121      * get a reference to a TableCreator object. The implementation doesn't
122      * care how hard it is to create a TableCreator instance since it is
123      * seldom used.
124      *
125      * @return a TableCreator instance.
126      */

127     public static synchronized TableCreator getInstance() {
128         synchronized (TableCreator.class) {
129             if (theInstance == null) {
130                 TableCreator tc = new TableCreator();
131                 theInstance = new WeakReference JavaDoc(tc);
132                 return tc;
133             } else {
134                 TableCreator returnValue = (TableCreator) theInstance.get();
135                 if (returnValue == null) {
136                     returnValue = new TableCreator();
137                     theInstance = new WeakReference JavaDoc(returnValue);
138                 }
139
140                 return returnValue;
141             }
142         }
143     }
144
145     /**
146      * Create the table needed by this DB Object in the database. Assumes it is
147      * not there already. This method is used by the Schema object when called
148      * from the DBCreate servlet to initialize a database to match the defined
149      * DB objects in a schema.
150      *
151      * @param dbObj The Instantiated Database Object to check against.
152      * @throws DBException If there's an error creating the table with the
153      * database
154      * @todo Refactor this method into smaller chunks... ie creating the table.
155      * Creating
156      * <p/>
157      * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
158      * @since $DatabaseSchema $Date: 2004/11/18 02:03:27 $
159      */

160     public synchronized void createTable(DBObject dbObj) throws DBException {
161         FastStringBuffer sqlStatement = FastStringBuffer.getInstance();
162         //Since we're only borrowing an instance, let's just go ahead and
163
//create it to make out logic easier.
164
FastStringBuffer pkStatement = FastStringBuffer.getInstance();
165
166         JDBCObjectMetaData metadata = dbObj.getJDBCMetaData();
167
168         try {
169
170             DBConnectionPool myPool = null;
171             DBConnection myConnection = null;
172             boolean addComma = false;
173
174             try {
175
176                 sqlStatement = createTableSQLDefinition(dbObj
177                         , sqlStatement);
178
179                 myPool = DBConnectionPool.getInstance(dbObj.getDataContext());
180                 myConnection = myPool.getConnection("Table Creator");
181
182                 String JavaDoc myDBDriver = myConnection.getDBDriver();
183                 Iterator JavaDoc e2 = metadata.getKeyFieldListArray().iterator();
184
185                 // Determine whether primary key fields have been defined.
186
if (e2.hasNext()) {
187
188                     /* If we're using a connection to PostgreSQL, we have a different */
189                     /* syntax for primary key */
190                     // Note: The driver name for the PostgreSQL jdbc driver has changed */
191
// for version 7.0. We now
192
// check for both driver names.
193
if (myDBDriver.equals("postgresql.Driver") ||
194                             myDBDriver.equals("org.postgresql.Driver")) {
195                         sqlStatement.append(", CONSTRAINT pk");
196                         sqlStatement.append(metadata.getTargetSQLTable(dbObj.getDataContext()));
197                         sqlStatement.append(" PRIMARY KEY (");
198                         addComma = false;
199
200                         while (e2.hasNext()) {
201                             String JavaDoc OneKey = (String JavaDoc) e2.next();
202
203                             if (addComma) {
204                                 sqlStatement.append(", ");
205                             }
206
207                             sqlStatement.append(OneKey);
208                             addComma = true;
209                         }
210
211                         sqlStatement.append(")");
212                     } else if ((myDBDriver.equals("org.hsql.jdbcDriver")) ||
213                             (myDBDriver.equals("org.hsqldb.jdbcDriver"))) { /* if Hypersonic */
214                         sqlStatement.append(", PRIMARY KEY (");
215                         addComma = false;
216
217                         while (e2.hasNext()) {
218                             String JavaDoc OneKey = (String JavaDoc) e2.next();
219
220                             if (addComma) {
221                                 sqlStatement.append(", ");
222                             }
223
224                             sqlStatement.append(OneKey);
225                             addComma = true;
226                         }
227
228                         sqlStatement.append(")");
229                     } else {
230                         pkStatement.append("alter table ");
231                         pkStatement.append(metadata.getTargetSQLTable(dbObj.getDataContext()));
232                         pkStatement.append(" add primary key(");
233                         addComma = false;
234
235                         while (e2.hasNext()) {
236                             String JavaDoc OneKey = (String JavaDoc) e2.next();
237
238                             if (addComma) {
239                                 pkStatement.append(", ");
240                             }
241
242                             pkStatement.append(OneKey);
243                             addComma = true;
244                         }
245
246                         pkStatement.append(")");
247                     } /* if not PSQL */
248
249                 } /* If primary key fields defined */
250
251
252                 sqlStatement.append(")");
253
254                 // Send SQL to database
255
if (log.isInfoEnabled()) {
256                     log.info("Executing:" + sqlStatement.toString());
257                 }
258                 myConnection.executeUpdate(sqlStatement.toString());
259
260                 // If separate statement is needed, create primary key
261
String JavaDoc thepkSQL = pkStatement.toString();
262                 if (thepkSQL != null && thepkSQL.length() > 0) {
263                     if (log.isInfoEnabled()) {
264                         log.info("Executing:" + thepkSQL);
265                     }
266
267                     myConnection.executeUpdate(thepkSQL);
268                 }
269                 //
270
//Construct one or more indicies. for the dbObject
271
//
272
createIndices(dbObj, myConnection, true);
273             } catch (Throwable JavaDoc t) {
274                 t.printStackTrace();
275                 throw new DBException(t);
276             } finally {
277                 if (myPool != null) {
278                     myPool.release(myConnection);
279                 }
280             }
281         } finally {
282             pkStatement.release();
283             sqlStatement.release();
284         }
285
286     }
287
288     /**
289      * Create the SQL statement to create the table.
290      *
291      * @param dbObj the dbobject where we get the parameters from.
292      * @param sqlStatement the initialized FastStringBuffer that we're going to
293      * append to. The return value is the same we pass in. Allocation and
294      * deallocation is up to the parent calling this function.
295      * @return FastStringBuffer with the create statements added
296      * @throws DBException upon creation error
297      * <p/>
298      * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
299      * @since $DatabaseSchema $Date: 2004/11/18 02:03:27 $
300      */

301     protected FastStringBuffer createTableSQLDefinition(DBObject dbObj,
302                                                         FastStringBuffer sqlStatement) throws DBException {
303         boolean addComma = false;
304
305         //We have to use the mapped data context here because
306
String JavaDoc dataContext = dbObj.getMappedDataContext();
307         TypeMapper typeMapper = TypeMapper.getInstance(dataContext);
308         JDBCConfig myConfig = null;
309         JDBCObjectMetaData metadata = dbObj.getJDBCMetaData();
310
311         try {
312             myConfig = ConfigManager.getJdbcRequired(dbObj.getDataContext());
313         } catch (com.jcorporate.expresso.core.misc.ConfigurationException ce) {
314             throw new DBException(ce);
315         }
316         sqlStatement.append("CREATE");
317         if ("org.hsqldb.jdbcDriver".equals(myConfig.getDriver())) {
318             // add development option where we want all data NOT in
319
// editable text format, but rather cached for efficiency in a .data table
320
String JavaDoc useCachedHSQL = Setup.getValueUnrequired(dbObj.getDataContext(), USE_CACHED_HSQL);
321             if (StringUtil.toBoolean(useCachedHSQL)) {
322                 sqlStatement.append(" CACHED ");
323             }
324         }
325         sqlStatement.append(" TABLE ");
326         sqlStatement.append(metadata.getTargetSQLTable(dbObj.getDataContext()));
327         sqlStatement.append("(");
328
329         for (Iterator JavaDoc lf = metadata.getFieldListArray().iterator(); lf.hasNext();) {
330             String JavaDoc fieldName = (String JavaDoc) lf.next();
331
332             if (!dbObj.getJDBCMetaData().isVirtual(fieldName)) {
333                 FastStringBuffer oneType = FastStringBuffer.getInstance();
334                 try {
335                     if (addComma) {
336                         sqlStatement.append(", ");
337                     }
338
339                     oneType.append(typeMapper.getTypeForDB(metadata.getType(fieldName)));
340
341                     if (!metadata.getLength(fieldName).equals("0")) {
342                         oneType.append("(" + dbObj.getLength(fieldName));
343
344                         if (metadata.getPrecision(fieldName) > 0) {
345                             oneType.append(", " +
346                                     metadata.getPrecision(fieldName));
347                         }
348
349                         oneType.append(")");
350                     }
351
352                     sqlStatement.append(fieldName);
353                     sqlStatement.append(" ");
354                     sqlStatement.append(oneType.toString());
355
356                     if (!dbObj.getFieldMetaData(fieldName).allowsNull()) {
357                         sqlStatement.append(" not null");
358                     } else {
359                         if (myConfig.useNullOnCreate()) {
360                             sqlStatement.append(" null"); //Added for compatability w/Sybase...sql-92 standard?
361
}
362                     }
363
364                     addComma = true;
365                 } finally {
366                     oneType.release();
367                 }
368             } /* if field is not virtual */
369         } /* for each field */
370         return sqlStatement;
371     }
372
373     /**
374      * Try to do a search on each DBObject member - if we fail
375      * we know the table does not exist. Create it
376      *
377      * @param oneSchema The schema to create tables for.
378      * @param dataContext The database context to create the tables.
379      * @return A Vector containing the created dbObjects
380      * <p/>
381      * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
382      * @throws DBException If the create process fails
383      * @since $DatabaseSchema $Date: 2004/11/18 02:03:27 $
384      */

385     public synchronized Vector JavaDoc createAsNeeded(Schema oneSchema, String JavaDoc dataContext)
386             throws DBException {
387         Vector JavaDoc newObjs = new Vector JavaDoc(1);
388         DBObject oneMember = null;
389         String JavaDoc oneOtherDbName = null;
390
391         JDBCObjectMetaData metadata;
392         for (Enumeration JavaDoc e = oneSchema.getMembers(); e.hasMoreElements();) {
393             oneMember = (DBObject) e.nextElement();
394             if (oneMember instanceof Securable) {
395                 ((Securable) oneMember).setRequestingUid(Securable.SYSTEM_ACCOUNT);
396             }
397             metadata = oneMember.getJDBCMetaData();
398
399             // Changed by Adam to respect hardcoded
400
// DBName values. Now, each dbobject is inspected. If there is a
401
// dbName, it is used. If not, the default dbName is used.
402
oneOtherDbName = (String JavaDoc) oneSchema.getDBObjMap().get(oneMember.getClass().getName());
403
404             //
405
//While under most situations we wouldn't set the dbname to the mapped
406
//context, here we do it because there is most likely no mapping
407
//table set up.
408
//
409
if (oneOtherDbName != null) {
410                 oneMember.setDataContext(oneOtherDbName);
411             } else {
412                 oneMember.setDataContext(dataContext);
413             }
414
415             if (log.isInfoEnabled()) {
416                 log.info("Verifying table " + metadata.getTargetSQLTable(oneMember.getDataContext()) + " exists");
417             }
418
419             try {
420                 oneMember.count();
421
422                 // check indices on established objects
423
createIndices(oneMember, null, false);
424
425                 if (log.isInfoEnabled()) {
426                     log.info("Table " + metadata.getTargetSQLTable(oneMember.getDataContext()) + " is OK");
427                 }
428             } catch (DBException de) {
429                 log.debug(de);
430                 newObjs.addElement(metadata.getName());
431                 if (log.isInfoEnabled()) {
432                     log.info("Table for object " + metadata.getName() +
433                             " did not exist in db '" + oneMember.getDataContext() +
434                             "' - creating");
435                 }
436                 createTable(oneMember);
437
438                 /* Now re-verify */
439                 try {
440                     oneMember.count();
441                 } catch (DBException de2) {
442                     throw new DBException("Table '" +
443                             metadata.getTargetSQLTable(oneMember.getDataContext()) +
444                             "' was not created successfully in db '" +
445                             oneMember.getDataContext() + "'", de2);
446                 }
447             }
448             //If this object resides in another database, write an entry to
449
//the DBOtherMap table
450
if (oneOtherDbName != null) {
451                 try {
452                     DBOtherMap otherdb = new DBOtherMap();
453                     otherdb.setDataContext(dataContext);
454                     otherdb.setField("DBObjName",
455                             oneMember.getClass().getName());
456
457                     if (!otherdb.find()) {
458                         otherdb.setField("DBConnName", oneOtherDbName);
459                         otherdb.setField("Descrip",
460                                 metadata.getDescription());
461                         otherdb.add();
462                     }
463                 } catch (DBException dbo) {
464                     log.error(dbo);
465                     log.error("Could not create an entry in the DBOtherMap " +
466                             "table for object ' " +
467                             oneMember.getClass().getName() + "'");
468                 }
469             }
470         }
471
472         return newObjs;
473     }
474
475     /**
476      * Protected method to get JDBC Configurations whether running Expresso Runtime
477      * or not.
478      *
479      * @param dataContext the Data Context to get the configuration for.
480      * @return JDBCConfig for the specified data context.
481      * @throws DBException if there is an error retrieving the JDBC configuration data.
482      */

483     protected JDBCConfig getJDBCConfig(String JavaDoc dataContext) throws DBException {
484         if (ExpressoRuntimeMap.getDefaultRuntime() == null) {
485             try {
486                 return ConfigManager.getContext(dataContext).getJdbc();
487             } catch (com.jcorporate.expresso.core.misc.ConfigurationException ex) {
488                 log.error("Error getting configuration for context: " + dataContext);
489                 throw new DBException("Error getting configuration for context", ex);
490             }
491         } else {
492             LocatorUtils lc = new LocatorUtils(ExpressoRuntimeMap.getDefaultRuntime());
493             DBConfig config = (DBConfig) lc.locateComponent(dataContext + ".PersistenceManager.DBConfig");
494             if (config == null) {
495                 throw new DBException("Unable to find DB Config in the default runtime.");
496             }
497
498             return config.getCurrentConfig();
499         }
500     }
501
502     /**
503      * create indices, using provided connection if any, and complaining upon failure if specified
504      *
505      * @param dbObj the object whose indices will be created
506      * @param providedConn any db connection we should use. can be null, in which case we get/dispose of one internally
507      * @param shouldComplain whether we should log a warning if index cannot be created. use 'false'
508      * if you are checking established dbobjects, and should not complain if all of the indices already exist
509      * @throws DBException upon error
510      */

511     protected void createIndices(DBObject dbObj,
512                                  DBConnection providedConn,
513                                  boolean shouldComplain) throws DBException {
514
515         if (dbObj.getJDBCMetaData().hasIndex()) {
516             JDBCConfig myConfig = null;
517
518             try {
519                 myConfig = ConfigManager.getJdbcRequired(dbObj.getDataContext());
520             } catch (com.jcorporate.expresso.core.misc.ConfigurationException ce) {
521                 throw new DBException(ce);
522             }
523             if (myConfig.createTableIndicies()) {
524                 Object JavaDoc[] indexArray = ((DBObjectDef) dbObj.getMetaData()).getIndexArray();
525
526                 if (indexArray != null) {
527
528                     DBConnection usedConn = providedConn;
529                     DBConnection localConn = null;
530                     DBConnectionPool myPool = null;
531                     try {
532                         if (usedConn == null) {
533                             // must provide local connection
534
myPool = DBConnectionPool.getInstance(dbObj.getDataContext());
535                             localConn = myPool.getConnection("Table Creator");
536                             usedConn = localConn;
537                         }
538                         for (int i = 0; i < indexArray.length; i++) {
539                             String JavaDoc createIndexString = ((DBIndex) indexArray[i]).constructSQL();
540
541                             if (log.isDebugEnabled() == true) {
542                                 log.debug("Executing " + createIndexString);
543                             }
544
545                             try {
546                                 usedConn.executeUpdate(createIndexString);
547                             } catch (DBException ex) {
548
549                                 // we only complain if we expect success always.
550
// for example, if we are just checking that all indices should exist already,
551
// we will get errors trying to recreate them,
552
// and we should not complain
553
if (shouldComplain) {
554                                     log.warn("Unable to create index. ", ex);
555                                 }
556                             }
557                         }
558                     } finally {
559                         if (localConn != null) {
560                             myPool.release(localConn);
561                         }
562                     }
563                 }
564             }
565         }
566
567     }
568
569 }
570
Popular Tags