1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.jetspeed.serializer;
18
19 import java.io.StringReader;
20 import java.io.StringWriter;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24
25 import javax.sql.DataSource;
26
27 import org.apache.commons.beanutils.BeanUtils;
28 import org.apache.commons.beanutils.DynaBean;
29 import org.apache.commons.beanutils.DynaProperty;
30 import org.apache.commons.dbcp.BasicDataSource;
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.apache.ddlutils.DatabaseOperationException;
34 import org.apache.ddlutils.Platform;
35 import org.apache.ddlutils.PlatformFactory;
36 import org.apache.ddlutils.PlatformUtils;
37 import org.apache.ddlutils.io.DataReader;
38 import org.apache.ddlutils.io.DataToDatabaseSink;
39 import org.apache.ddlutils.io.DatabaseIO;
40 import org.apache.ddlutils.model.Column;
41 import org.apache.ddlutils.model.Database;
42 import org.apache.ddlutils.model.JdbcTypeCategoryEnum;
43 import org.apache.ddlutils.model.Table;
44
45 /***
46 * Jetspeed DDLUtil
47 * <p>
48 * The Jetspeed DDL Utility is capabale of extracting existing schema
49 * information as well as recreating databases.
50 *
51 * @author <a href="mailto:hajo@bluesunrise.com">Hajo Birthelmer</a>
52 * @version $Id: $
53 */
54 public class JetspeedDDLUtil
55 {
56 public static final String DATASOURCE_DATABASENAME = "DATABASENAME".intern();
57 public static final String DATASOURCE_CLASS = "DATASOURCE_CLASS".intern();
58 public static final String DATASOURCE_DRIVER = "driverClassName".intern();
59 public static final String DATASOURCE_URL = "url".intern();
60 public static final String DATASOURCE_USERNAME = "username".intern();
61 public static final String DATASOURCE_PASSWORD = "password".intern();
62
63
64 /*** Logger */
65 private static final Log log = LogFactory.getLog(JetspeedDDLUtil.class);
66
67 JdbcTypeCategoryEnum temEnum = null;
68
69 Map parameters;
70
71 PlatformUtils utils;
72
73 StringWriter writer;
74
75 private Platform platform;
76
77 /*** The data source to test against. */
78 private DataSource dataSource;
79 /*** The database name. */
80 private String _databaseName;
81 /*** The database model. */
82 private Database model;
83
84 private boolean connected = false;
85
86 public JetspeedDDLUtil()
87 {
88
89 }
90
91 public void startUp()
92 {
93
94 }
95
96 public void tearDown()
97 {
98 if (connected)
99 {
100 platform = null;
101
102 }
103 }
104
105 /***
106 * Tries to determine whether a the jdbc driver and connection url re
107 * supported.
108 *
109 * @param driverName
110 * The fully qualified name of the JDBC driver
111 * @param jdbcConnectionUrl
112 * The connection url
113 * @return True if this driver/url is supported
114 */
115 public boolean isDatabaseSupported(String driverName,
116 String jdbcConnectionUrl)
117 {
118 if (utils.determineDatabaseType(driverName, jdbcConnectionUrl) != null)
119 return true;
120 else
121 return false;
122
123 }
124
125
126
127 /***
128 * Parses the database defined in the given XML file and creates a database
129 * schema (model) object
130 *
131 * @param fileName
132 */
133 public void writeDatabaseSchematoFile(String fileName)
134 {
135 new DatabaseIO().write(model, fileName);
136 }
137
138
139 /***
140 * Parses the database defined in the given XML file and creates a database
141 * schema (model) object
142 *
143 * @param dbDef
144 * The database XML definition
145 * @return The database model
146 */
147 protected Database createDatabaseSchemaFromXML(String fileName)
148 {
149 DatabaseIO io = new DatabaseIO();
150 io.setValidateXml(false);
151 return io.read(fileName);
152 }
153
154 /***
155 * Parses the database defined in the given XML definition String and
156 * creates a database schema (model) object
157 *
158 * @param dbDef
159 * The database XML definition
160 * @return The database model
161 */
162 protected Database createDatabaseSchemaFromString(String dbDef)
163 {
164 DatabaseIO dbIO = new DatabaseIO();
165
166 dbIO.setUseInternalDtd(true);
167 dbIO.setValidateXml(false);
168 return dbIO.read(new StringReader(dbDef));
169 }
170
171 /***
172 * <p>
173 * Create a database connection (platform instance) from a data source
174 * </p>
175 *
176 * @param dataSource
177 */
178 protected Platform connectToDatabase(DataSource dataSource)
179 {
180 return PlatformFactory.createNewPlatformInstance(dataSource);
181 }
182
183 /***
184 * <p>
185 * Create a database connection (platform instance) from a (case
186 * insensitive) database type (like MySQL)
187 * </p>
188 *
189 * @param dataSource
190 */
191 protected Platform connectToDatabase(String databaseType)
192 {
193 return PlatformFactory.createNewPlatformInstance(databaseType);
194 }
195 /***
196 * <p>
197 * Update a given database schema to match the schema of targetModel If
198 * alterDB is true, the routine attempts to modify the existing database
199 * shcema while preserving the data (as much as possible). If not, the
200 * existing tables are dropped prior to recreate
201 *
202 * @param targetModel
203 * The new database model
204 * @param alterDb
205 * if true, try to use alter database and preserve data
206 */
207 protected void updateDatabaseSchema(Database targetModel, boolean alterDb)
208 throws SerializerException
209 {
210 try
211 {
212 platform.setSqlCommentsOn(false);
213 try
214 {
215 targetModel.resetDynaClassCache();
216 } catch (Exception internalEx)
217 {
218 internalEx.printStackTrace();
219 }
220 if (alterDb)
221 {
222 model.mergeWith(targetModel);
223 try
224 {
225 platform.alterTables(model, true);
226 }
227 catch (Exception aEX)
228 {
229 System.out.println("Error in ALTER DATABASE");
230 aEX.printStackTrace();
231 log.error(aEX);
232 }
233 } else
234 {
235 try
236 {
237
238
239
240
241
242
243 if (model == null)
244 {
245 model = targetModel;
246 }
247 platform.dropTables(model, true);
248 }
249 catch (Exception aEX)
250 {
251 log.error(aEX);
252 }
253 try
254 {
255 platform.createTables(model, false, true);
256 if (this._databaseName.startsWith("oracle"))
257 {
258 model = this.readModelFromDatabase(null);
259 modifyVarBinaryColumn(model, "PA_METADATA_FIELDS", "COLUMN_VALUE");
260 modifyVarBinaryColumn(model, "PD_METADATA_FIELDS", "COLUMN_VALUE");
261 modifyVarBinaryColumn(model, "LANGUAGE", "KEYWORDS");
262 modifyVarBinaryColumn(model, "PORTLET_CONTENT_TYPE", "MODES");
263 modifyVarBinaryColumn(model, "PARAMETER", "PARAMETER_VALUE");
264 modifyVarBinaryColumn(model, "LOCALIZED_DESCRIPTION", "DESCRIPTION");
265 modifyVarBinaryColumn(model, "LOCALIZED_DISPLAY_NAME", "DISPLAY_NAME");
266 modifyVarBinaryColumn(model, "CUSTOM_PORTLET_MODE", "DESCRIPTION");
267 modifyVarBinaryColumn(model, "CUSTOM_WINDOW_STATE", "DESCRIPTION");
268 modifyVarBinaryColumn(model, "MEDIA_TYPE", "DESCRIPTION");
269 platform.alterTables(model, true);
270 }
271 }
272 catch (Exception aEX)
273 {
274 aEX.printStackTrace();
275 log.error(aEX);
276 }
277 }
278
279 } catch (Exception ex)
280 {
281 ex.printStackTrace();
282 throw new SerializerException(
283
284 SerializerException.CREATE_OBJECT_FAILED.create(ex.getLocalizedMessage()));
285 }
286 }
287
288 private void modifyVarBinaryColumn(Database targetModel, String tableName, String columnName)
289 {
290 Table table = targetModel.findTable(tableName);
291 Column c = table.findColumn(columnName);
292 c.setType("VARCHAR");
293 c.setSize("2000");
294 System.out.println("updating column " + c.getName() + " for table " + table.getName());
295 }
296 /***
297 * Alter an existing database from the given model. Data is preserved as
298 * much as possible
299 *
300 * @param model
301 * The new database model
302 */
303 public void alterDatabase(Database model) throws SerializerException
304 {
305 updateDatabaseSchema(model, true);
306 }
307
308 /***
309 * Creates a new database from the given model. Note that all data is LOST
310 *
311 * @param model
312 * The new database model
313 */
314 public void createDatabase(Database model) throws SerializerException
315 {
316 updateDatabaseSchema(model, false);
317 }
318
319 /***
320 * <p>
321 * Inserts data into the database. Data is expected to be in the format
322 * </p>
323 * <p>
324 * <?xml version='1.0' encoding='ISO-8859-1'?> <data> <TABLENAME
325 * FIELD1='TEXTVALUE' FIELD2=INTVALUE .... /> <TABLENAME FIELD1='TEXTVALUE'
326 * FIELD2=INTVALUE .... /> </data>
327 * </p>
328 *
329 * @param model
330 * The database model
331 * @param dataXml
332 * The data xml
333 * @return The database
334 */
335 protected Database insertData(Database model, String dataXml)
336 throws DatabaseOperationException
337 {
338 try
339 {
340 DataReader dataReader = new DataReader();
341
342 dataReader.setModel(model);
343 dataReader.setSink(new DataToDatabaseSink(platform, model));
344 dataReader.parse(new StringReader(dataXml));
345 return model;
346 } catch (Exception ex)
347 {
348 throw new DatabaseOperationException(ex);
349 }
350 }
351
352 /***
353 * Drops the tables defined in the database model on this connection.
354 *
355 * @param model
356 * The database model
357 *
358 */
359 protected void dropDatabaseTables(Database model)
360 throws DatabaseOperationException
361 {
362 platform.dropTables(model, true);
363 }
364
365 /***
366 * Reads the database model from a live database.
367 *
368 * @param platform
369 * The physical database connection
370 * @param databaseName
371 * The name of the resulting database
372 * @return The model
373 */
374 public Database readModelFromDatabase(String databaseName)
375 {
376 return platform.readModelFromDatabase(databaseName);
377 }
378
379 /***
380 * datasource.class=org.apache.commons.dbcp.BasicDataSource
381 * datasource.driverClassName=com.mysql.jdbc.Driver
382 * datasource.url=jdbc:mysql://localhost/ddlutils datasource.username=root
383 * datasource.password=root123
384 *
385 */
386
387 /***
388 * Initializes the datasource and the connection (platform)
389 */
390 public void init(Map parameters)
391 {
392 if (connected)
393 tearDown();
394
395 try
396 {
397 String dataSourceClass = (String) parameters.get(DATASOURCE_CLASS);
398 if (dataSourceClass == null)
399 dataSourceClass = BasicDataSource.class.getName();
400
401 dataSource = (DataSource) Class.forName(dataSourceClass)
402 .newInstance();
403
404 for (Iterator it = parameters.entrySet().iterator(); it.hasNext();)
405 {
406 Map.Entry entry = (Map.Entry) it.next();
407 String propName = (String) entry.getKey();
408
409 if (!(propName.equals(DATASOURCE_CLASS)))
410 {
411 BeanUtils.setProperty(dataSource, propName, entry
412 .getValue());
413 }
414 }
415 } catch (Exception ex)
416 {
417 throw new DatabaseOperationException(ex);
418 }
419 String databaseName = null;
420 _databaseName = null;
421 try
422 {
423 databaseName = (String) parameters.get(DATASOURCE_DATABASENAME);
424 if (databaseName != null)
425 {
426 platform = PlatformFactory.createNewPlatformInstance(databaseName);
427 if (platform != null)
428 _databaseName = databaseName;
429 }
430 } catch (Exception ex)
431 {
432 log.warn("Exception in trying to establish connection to " + databaseName + " : " + ex.getLocalizedMessage());
433 log.warn(ex);
434 }
435 if (_databaseName == null)
436 {
437 _databaseName = new PlatformUtils().determineDatabaseType(dataSource);
438 if (_databaseName == null)
439 {
440 throw new DatabaseOperationException(
441 "Could not determine platform from datasource, please specify it in the jdbc.properties via the ddlutils.platform property");
442 }
443 else
444 {
445 try
446 {
447 platform = PlatformFactory.createNewPlatformInstance(_databaseName);
448 } catch (Exception ex)
449 {
450 throw new DatabaseOperationException(
451 "Could not establish connection to " + _databaseName + " : " + ex.getLocalizedMessage(),ex);
452 }
453 }
454 }
455
456
457 writer = new StringWriter();
458 platform.getSqlBuilder().setWriter(writer);
459
460
461
462
463
464
465 platform.setDataSource(dataSource);
466 System.out.println("reading model...");
467 model = this.readModelFromDatabase(null);
468 System.out.println("done reading model...");
469 /***
470 JdbcModelReader reader = platform.getModelReader();
471 try
472 {
473 model = reader.getDatabase(platform.borrowConnection(), null);
474 } catch (Exception ex)
475 {
476 throw new DatabaseOperationException(ex);
477 }
478 */
479
480 connected = true;
481 }
482
483 /***
484 * Returns the database model.
485 *
486 * @return The model
487 */
488 protected Database getModel()
489 {
490 return model;
491 }
492
493 /***
494 * Inserts data into the database.
495 *
496 * @param dataXml
497 * The data xml
498 * @return The database
499 */
500 protected Database insertData(String dataXml)
501 throws DatabaseOperationException
502 {
503 try
504 {
505 DataReader dataReader = new DataReader();
506
507 dataReader.setModel(model);
508 dataReader.setSink(new DataToDatabaseSink(platform, model));
509 dataReader.parse(new StringReader(dataXml));
510 return model;
511 } catch (Exception ex)
512 {
513 throw new DatabaseOperationException(ex);
514 }
515 }
516
517 /***
518 * Drops the tables defined in the database model.
519 */
520 protected void dropDatabase() throws DatabaseOperationException
521 {
522 platform.dropTables(model, true);
523 }
524
525 /***
526 * Determines the value of the bean's property that has the given name.
527 * Depending on the case-setting of the current builder, the case of teh
528 * name is considered or not.
529 *
530 * @param bean
531 * The bean
532 * @param propName
533 * The name of the property
534 * @return The value
535 */
536 protected Object getPropertyValue(DynaBean bean, String propName)
537 {
538 if (platform.isDelimitedIdentifierModeOn())
539 {
540 return bean.get(propName);
541 } else
542 {
543 DynaProperty[] props = bean.getDynaClass().getDynaProperties();
544
545 for (int idx = 0; idx < props.length; idx++)
546 {
547 if (propName.equalsIgnoreCase(props[idx].getName()))
548 {
549 return bean.get(props[idx].getName());
550 }
551 }
552 throw new IllegalArgumentException(
553 "The bean has no property with the name " + propName);
554 }
555 }
556
557 public DataSource getDataSource()
558 {
559 return dataSource;
560 }
561
562 public Platform getPlatform()
563 {
564 return platform;
565 }
566
567
568
569 public List getRows(String tableName)
570 {
571 Table table = getModel().findTable(tableName, getPlatform().isDelimitedIdentifierModeOn());
572
573 return getPlatform().fetch(getModel(), getSelectQueryForAllString( table), new Table[] { table });
574 }
575
576
577 public String getSelectQueryForAllString( Table table)
578 {
579
580 StringBuffer query = new StringBuffer();
581
582 query.append("SELECT * FROM ");
583 if (getPlatform().isDelimitedIdentifierModeOn())
584 {
585 query.append(getPlatform().getPlatformInfo().getDelimiterToken());
586 }
587 query.append(table.getName());
588 if (getPlatform().isDelimitedIdentifierModeOn())
589 {
590 query.append(getPlatform().getPlatformInfo().getDelimiterToken());
591 }
592 return query.toString();
593 }
594
595
596
597
598 }