001: /* Copyright 2004 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal.tools.dbloader;
007:
008: import java.io.BufferedWriter;
009: import java.io.File;
010: import java.io.FileWriter;
011: import java.io.IOException;
012: import java.io.PrintWriter;
013: import java.net.MalformedURLException;
014: import java.net.URL;
015: import java.sql.SQLException;
016:
017: import javax.xml.parsers.DocumentBuilder;
018: import javax.xml.parsers.DocumentBuilderFactory;
019: import javax.xml.parsers.ParserConfigurationException;
020: import javax.xml.parsers.SAXParserFactory;
021:
022: import org.jasig.portal.PortalException;
023: import org.jasig.portal.RDBMServices;
024: import org.jasig.portal.utils.DTDResolver;
025: import org.jasig.portal.utils.XSLT;
026: import org.springframework.dao.DataAccessException;
027: import org.w3c.dom.Document;
028: import org.xml.sax.InputSource;
029: import org.xml.sax.SAXException;
030: import org.xml.sax.XMLReader;
031: import org.xml.sax.helpers.DefaultHandler;
032:
033: /**
034: * <p>A tool to set up a uPortal database. This tool was created so that uPortal
035: * developers would only have to maintain a single set of xml documents to define
036: * the uPortal database schema and data. Previously it was necessary to maintain
037: * different scripts for each database we wanted to support.</p>
038: *
039: * <p>DbLoader reads the generic types that are specified in tables.xml and
040: * tries to map them to local types by querying the database metadata via methods
041: * implemented by the JDBC driver. Fallback mappings can be supplied in
042: * dbloader.xml for cases where the JDBC driver is not able to determine the
043: * appropriate mapping. Such cases will be reported to standard out.</p>
044: *
045: * <p>An xsl transformation is used to produce the DROP TABLE and CREATE TABLE
046: * SQL statements. These statements can be altered by modifying tables.xsl</p>
047: *
048: * <p>Generic data types (as defined in java.sql.Types) which may be specified
049: * in tables.xml include:
050: * <code>BIT, TINYINT, SMALLINT, INTEGER, BIGINT, FLOAT, REAL, DOUBLE,
051: * NUMERIC, DECIMAL, CHAR, VARCHAR, LONGVARCHAR, DATE, TIME, TIMESTAMP,
052: * BINARY, VARBINARY, LONGVARBINARY, NULL, OTHER, JAVA_OBJECT, DISTINCT,
053: * STRUCT, ARRAY, BLOB, CLOB, REF, DATALINK, BOOLEAN</code>
054: *
055: * <p><strong>WARNING: YOU MAY WANT TO MAKE A BACKUP OF YOUR DATABASE BEFORE RUNNING DbLoader</strong></p>
056: *
057: * <p>DbLoader will perform the following steps:
058: * <ol>
059: * <li>Read configurable properties from dbloader.xml</li>
060: * <li>Get database connection from RDBMServices
061: * (reads JDBC database settings from rdbm.properties).</li>
062: * <li>Read tables.xml and issue corresponding DROP TABLE and CREATE TABLE SQL statements.</li>
063: * <li>Read data.xml and issue corresponding INSERT/UPDATE/DELETE SQL statements.</li>
064: * </ol>
065: * </p>
066: *
067: * @author Ken Weiner, kweiner@unicon.net
068: * @author Mark Boyd, mboyd@sungardsct.com
069: * @version $LastChangedRevision: 36814 $
070: * @see java.sql.Types
071: * @since uPortal 2.0
072: */
073: public class DbLoader {
074: private Configuration config = null;
075:
076: public DbLoader(Configuration c) {
077: this .config = c;
078: }
079:
080: /**
081: * Creates a default DbLoader with no configuration object installed. Before
082: * DbLoader can work it must have a configuration object set.
083: *
084: */
085: public DbLoader() {
086: }
087:
088: /**
089: * Set the configuration object to govern DbLoader's behavior.
090: * @param c
091: */
092: public void setConfig(Configuration c) {
093: this .config = c;
094: }
095:
096: public static void main(String[] args) {
097: RDBMServices.setGetDatasourceFromJndi(false); /*don't try jndi when not in web app */
098: Configuration config = new Configuration();
099:
100: try {
101: // read dbloader.xml properties
102: loadConfiguration(config);
103: // read command line arguements to override properties in dbloader.xml
104: readOverrides(config, args);
105:
106: // create the script file if indicated
107: if (config.getCreateScript())
108: initScript(config);
109:
110: // instantiate loader and run
111: DbLoader loader = new DbLoader(config);
112: loader.process();
113: } catch (Exception e) {
114: e.printStackTrace(config.getLog());
115: } finally
116: // call local exit method to clean up. This does not actually
117: // do a system.exit() allowing a stack trace to the console in
118: // the case of a run time error.
119: {
120: exit(config);
121: }
122: config.getLog().flush();
123:
124: if (config.getScriptWriter() != null)
125: config.getScriptWriter().flush();
126: }
127:
128: public void process() throws SQLException, PortalException,
129: IOException, SAXException, ParserConfigurationException {
130: try {
131: config.setConnection(RDBMServices.getConnection());
132:
133: long startTime = System.currentTimeMillis();
134:
135: DbUtils.logDbInfo(config);
136:
137: if (config.getDataURL() == null)
138: config.setDataURL(DbLoader.class.getResource(config
139: .getDataUri()));
140:
141: // okay, start processing
142: // get tablesURL and dataURL here
143: if (config.getTablesURL() == null)
144: config.setTablesURL(DbLoader.class.getResource(config
145: .getTablesUri()));
146:
147: config.getLog().println(
148: "Getting tables from: " + config.getTablesURL());
149: config.getLog().println(
150: "Getting data from: " + config.getDataURL());
151:
152: DocumentBuilder domParser = null;
153:
154: // get a dom parser for handling tables.xml and/or indexes.xml
155: try {
156: // Read tables.xml
157: DocumentBuilderFactory dbf = null;
158: dbf = DocumentBuilderFactory.newInstance();
159: domParser = dbf.newDocumentBuilder();
160: } catch (ParserConfigurationException pce) {
161: config.getLog().println(
162: "Unable to instantiate DOM parser. Pease check your JAXP "
163: + "configuration.");
164: pce.printStackTrace(config.getLog());
165: return;
166: }
167:
168: // load tables.xml doc if we are creating or dropping tables or
169: // we are populating tables otherwise skip.
170: try {
171: // Eventually, write and validate against a DTD
172: //domParser.setFeature ("http://xml.org/sax/features/validation", true);
173: domParser.setEntityResolver(new DTDResolver(
174: "tables.dtd"));
175:
176: //tablesURL = DbLoader.class.getResource(Configuration.properties.getTablesUri());
177: if (config.getCreateTables() || config.getDropTables()
178: || config.getPopulateTables()
179: || config.getCreateScript())
180: config.setTablesDoc(domParser
181: .parse(new InputSource(config
182: .getTablesURL().openStream())));
183: } catch (Exception e) {
184: config.getLog().println(
185: "Could not process tablesURL '"
186: + config.getTablesURL() + "'");
187: e.printStackTrace(config.getLog());
188:
189: return;
190: }
191:
192: // Hold on to tables xml with generic types for populating tables
193: if (config.getPopulateTables() || config.getCreateScript())
194: config.setGenericTablesDoc((Document) config
195: .getTablesDoc().cloneNode(true));
196:
197: // drop and create tables if indicated
198: if (config.getCreateTables() || config.getDropTables()
199: || config.getCreateScript()) {
200: // Replace all generic data types with local data types
201: DomUtils
202: .replaceDataTypes(config, config.getTablesDoc());
203:
204: // tables.xml + tables.xsl --> DROP TABLE and CREATE TABLE sql statements
205: XSLT xslt = new XSLT(this );
206: xslt.setXML(config.getTablesDoc());
207: xslt.setXSL(config.getTablesXslUri());
208: xslt.setTarget(new TableHandler(config));
209:
210: if (config.getUpgradeVersion() != null) {
211: xslt.setStylesheetParameter("upgradeMajor", Integer
212: .toString(config.getUpgradeMajor()));
213: xslt.setStylesheetParameter("upgradeMinor", Integer
214: .toString(config.getUpgradeMinor()));
215: }
216:
217: xslt.transform();
218: } else {
219: config.getLog().println();
220: config
221: .getLog()
222: .println(
223: "Dropping tables and Creating tables...Disabled");
224: config.getLog().println();
225: }
226:
227: // populate tables if indiicated
228: // data.xml --> INSERT sql statements
229:
230: if (config.getPopulateTables()) {
231: config.getLog().println("Populating tables...");
232: XMLReader parser = getXMLReader();
233: DefaultHandler dataHandler = DataHandlerFactory
234: .instance().getHandler(config);
235: parser.setContentHandler(dataHandler);
236: parser.setErrorHandler(dataHandler);
237: parser.setEntityResolver(new DTDResolver("data.dtd"));
238: parser.parse(new InputSource(config.getDataURL()
239: .openStream()));
240: } else if (config.getCreateScript()) {
241: config.getLog().println(
242: "Populating tables in the script...");
243: XMLReader parser = getXMLReader();
244: DefaultHandler dataHandler = DataHandlerFactory
245: .instance().getHandler(config);
246: parser.setContentHandler(dataHandler);
247: parser.setErrorHandler(dataHandler);
248: parser.setEntityResolver(new DTDResolver("data.dtd"));
249: parser.parse(new InputSource(config.getDataURL()
250: .openStream()));
251: } else
252: config.getLog()
253: .println("Populating tables...disabled.");
254:
255: // cleanup and exit
256: config.getLog().println("Done!");
257: long endTime = System.currentTimeMillis();
258: config.getLog().println(
259: "Elapsed time: " + ((endTime - startTime) / 1000f)
260: + " seconds");
261: } catch (DataAccessException dae) {
262: // we know this will be thrown by RDBMServices when getConnection() fails.
263: config.getLog().println(
264: "DbLoader couldn't obtain a database connection. "
265: + "See the portal log for details.");
266: return;
267: } finally {
268: RDBMServices.releaseConnection(config.getConnection());
269: }
270: }
271:
272: public static void loadConfiguration(Configuration config)
273: throws ParserConfigurationException, SAXException,
274: IOException {
275: PropertiesHandler handler = new PropertiesHandler(config);
276: config.setPropertiesURL(DbLoader.class
277: .getResource("/properties/db/dbloader.xml"));
278: // Read in the dbloader properties
279: XMLReader parser = getXMLReader();
280: parser.setContentHandler(handler);
281: parser.setErrorHandler(handler);
282: handler.properties.getLog().print(
283: "Parsing " + handler.properties.getPropertiesURL()
284: + "...");
285: parser.parse(new InputSource(handler.properties
286: .getPropertiesURL().openStream()));
287: }
288:
289: /**
290: * @param config
291: */
292: private static void readOverrides(Configuration config,
293: String[] args) throws MalformedURLException {
294: boolean usetable = false;
295: boolean useDataUri = false;
296: boolean useDataFile = false;
297: boolean useLocale = false;
298: boolean upgrade = false;
299:
300: String adminLocale = null;
301: String upgradeVersion = null;
302:
303: for (int i = 0; i < args.length; i++) {
304: if (!args[i].startsWith("-")) {
305: if (usetable) {
306: config.setTablesUri(args[i]);
307: usetable = false;
308: } else if (useDataUri) {
309: config.setDataUri(args[i]);
310: config.setDataURL(DbLoader.class.getResource(config
311: .getDataUri()));
312: useDataUri = false;
313: } else if (useDataFile) {
314: URL url = getDataFileUri(args[i]);
315: config.setDataUri(url.toString());
316: config.setDataURL(url);
317: useDataFile = false;
318: } else if (useLocale) {
319: adminLocale = args[i];
320: config.setAdminLocale(adminLocale);
321: useLocale = false;
322: } else if (upgrade) {
323: upgradeVersion = args[i];
324: config.setUpgradeVersion(upgradeVersion);
325: int index = upgradeVersion.indexOf('.');
326: config.setUpgradeMajor(Integer
327: .parseInt(upgradeVersion
328: .substring(0, index)));
329: if (upgradeVersion.indexOf('.', index + 1) != -1) {
330: config.setUpgradeMinor(Integer
331: .parseInt(upgradeVersion.substring(
332: index + 1,
333: upgradeVersion.indexOf('.',
334: index + 1))));
335: } else {
336: config.setUpgradeMinor(Integer
337: .parseInt(upgradeVersion
338: .substring(index + 1)));
339: }
340: upgrade = false;
341: }
342: } else if (args[i].equals("-u")) {
343: upgrade = true;
344: } else if (args[i].equals("-t")) {
345: usetable = true;
346: } else if (args[i].equals("-d")) {
347: useDataUri = true;
348: } else if (args[i].equals("-df")) {
349: useDataFile = true;
350: } else if (args[i].equals("-c")) {
351: config.setCreateScript(true);
352: } else if (args[i].equals("-nc")) {
353: config.setCreateScript(false);
354: } else if (args[i].equals("-D")) {
355: config.setDropTables(true);
356: } else if (args[i].equals("-nD")) {
357: config.setDropTables(false);
358: } else if (args[i].equals("-C")) {
359: config.setCreateTables(true);
360: } else if (args[i].equals("-nC")) {
361: config.setCreateTables(false);
362: } else if (args[i].equals("-P")) {
363: config.setPopulateTables(true);
364: } else if (args[i].equals("-nP")) {
365: config.setPopulateTables(false);
366: } else if (args[i].equals("-l")) {
367: config.setLocaleAware(true);
368: useLocale = true;
369: }
370: }
371: }
372:
373: /**
374: * @param file
375: * @return
376: */
377: private static URL getDataFileUri(String file)
378: throws IllegalArgumentException {
379: File f = new File(file);
380: if (!f.exists()) {
381: throw new IllegalArgumentException("File specified '"
382: + file + "' not found.");
383: }
384: URL url = null;
385: try {
386: url = f.toURL();
387: } catch (MalformedURLException mue) {
388: throw new IllegalArgumentException("File specified '"
389: + file
390: + "' can not be converted to a URL for loading.");
391: }
392: return url;
393: }
394:
395: private static XMLReader getXMLReader() throws SAXException,
396: ParserConfigurationException {
397: SAXParserFactory spf = SAXParserFactory.newInstance();
398: return spf.newSAXParser().getXMLReader();
399: }
400:
401: private static void initScript(Configuration config)
402: throws java.io.IOException {
403: String scriptFileName = config.getScriptFileName();
404: File scriptFile = new File(scriptFileName);
405: if (scriptFile.exists())
406: scriptFile.delete();
407: scriptFile.createNewFile();
408: config.getLog().println(
409: "Generating script file "
410: + scriptFile.getAbsolutePath());
411: config.setScriptWriter(new PrintWriter(new BufferedWriter(
412: new FileWriter(scriptFileName, true))));
413: }
414:
415: static void exit(Configuration config) {
416: if (config.getScriptWriter() != null)
417: config.getScriptWriter().close();
418: }
419: }
|