001: /*
002: * (C) Copyright 2005 Nabh Information Systems, Inc.
003: *
004: * All copyright notices regarding Nabh's products MUST remain
005: * intact in the scripts and in the outputted HTML.
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public License
008: * as published by the Free Software Foundation; either version 2.1
009: * of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: *
020: */
021: package com.nabhinc.util.db;
022:
023: import java.io.ByteArrayOutputStream;
024: import java.io.File;
025: import java.io.FileInputStream;
026: import java.io.FileNotFoundException;
027: import java.io.FileWriter;
028: import java.io.IOException;
029: import java.io.InputStream;
030: import java.sql.Types;
031: import java.util.ArrayList;
032: import java.util.HashMap;
033: import java.util.List;
034:
035: import javax.servlet.ServletException;
036: import javax.sql.DataSource;
037: import javax.xml.bind.JAXBContext;
038: import javax.xml.bind.JAXBException;
039: import javax.xml.bind.Marshaller;
040: import javax.xml.bind.Unmarshaller;
041: import javax.xml.bind.annotation.XmlAttribute;
042: import javax.xml.bind.annotation.XmlElement;
043: import javax.xml.bind.annotation.XmlElementWrapper;
044: import javax.xml.bind.annotation.XmlRootElement;
045:
046: import org.apache.commons.dbcp.BasicDataSource;
047: import org.apache.commons.logging.Log;
048: import org.apache.commons.logging.LogFactory;
049:
050: import com.nabhinc.core.Defaults;
051: import com.nabhinc.util.ReflectionUtil;
052: import com.nabhinc.util.StringUtil;
053:
054: /**
055: * Database configuration utility class.
056: *
057: * @author Padmanabh Dabke
058: * (c) 2005 Nabh Information Systems, Inc. All Rights Reserved.
059: */
060: @XmlRootElement(name="db-config")
061: public class DBConfigUtil {
062:
063: public static final String DB_CONFIG_ROOT_TAG = "db-config";
064: public static final String DS_LOCAL_DATASOURCES_TAG = "local-data-sources";
065: public static final String DS_LOCAL_DATASOURCE_TAG = "data-source";
066: public static final String DS_NAME_ATTRIBUTE = "name";
067: public static final String DS_URL_ATTRIBUTE = "url";
068: public static final String DS_USER_NAME_ATTRIBUTE = "user-name";
069: public static final String DS_PASSWORD_ATTRIBUTE = "password";
070: public static final String DS_DRIVER_CLASS_NAME_ATTRIBUTE = "driver-class-name";
071: public static final String DEFAULT_DATA_SOURCE_TAG = "default-data-source";
072:
073: public static final String DATA_SOURCES_TAG = "data-sources";
074: public static final String DATA_SOURCE_TAG = "data-source";
075: public static final String DATA_SOURCE_NAME_ATTRIB = "name";
076: public static final String DATA_SOURCE_LABEL_ATTRIB = "label";
077: public static final String DATA_SOURCE_FLAVOR_ATTRIB = "flavor";
078:
079: public static final String ONLINE_USER_DATASOURCE_TAG = "online-user-data-source";
080: public static final String SHUTDOWN_DEFAULT_DATASOURCE_TAG = "shutdown-default-data-source";
081: public static final String ONLINE_USER_TABLE_NAME_TAG = "online-user-table-name";
082:
083: public static final String DEFAULT_ONLINE_USER_TABLE_NAME = "IM_ACTIVE_SESSIONS";
084: public static final String DEFAULT_IN_MEMORY_DATASOURCE = "java:comp/env/jdbc/inmemorydb";
085:
086: private static boolean dbcuInitialized = false;
087: private static HashMap<String, String> dbcuDataSourceMap = new HashMap<String, String>();
088:
089: private static DBConfigUtil dbcuSelf = null;
090: // private static String dbcuConfigFile = null;
091: private static String dbcuRealPath = "";
092: /**
093: * Name of the data source that maintains transient
094: * information such as online user stats.
095: */
096: private static String dbcuInMemoryDataSource = DEFAULT_IN_MEMORY_DATASOURCE;
097:
098: /*
099: * Name of the table for storing information about online users.
100: */
101: private static String dbcuOnlineUserTableName = DEFAULT_ONLINE_USER_TABLE_NAME;
102:
103: private static boolean dbcuIsShutdownDefaultDataSource = false;
104:
105: private static String dbcuDataSourceClass = null;
106:
107: private static Log dbcuLogger = LogFactory
108: .getLog(DBConfigUtil.class);
109:
110: @XmlRootElement(name="data-source")
111: public static class DataSourceInfo {
112: @XmlAttribute(name="name")
113: public String name = null;
114: @XmlAttribute(name="label")
115: public String label = null;
116: @XmlAttribute(name="flavor")
117: public String flavor = null;
118: }
119:
120: private static List<DataSourceInfo> dbcuDataSources = new ArrayList<DataSourceInfo>();
121:
122: @XmlRootElement(name="property")
123: public static class PropertyInfo {
124: @XmlAttribute(name="name")
125: public String name = null;
126:
127: @XmlAttribute(name="value")
128: public String value = null;
129: }
130:
131: @XmlRootElement(name="local-data-source")
132: public static class LocalDataSourceInfo {
133: @XmlAttribute(name="driver-class-name")
134: public String driverClassName = null;
135:
136: @XmlAttribute(name="url")
137: public String url = null;
138:
139: @XmlAttribute(name="user-name")
140: public String userName = null;
141:
142: @XmlAttribute(name="password")
143: public String password = null;
144:
145: @XmlAttribute(name="name")
146: public String name = null;
147:
148: @XmlElement(name="property")
149: public List<PropertyInfo> properties = new ArrayList<PropertyInfo>();
150: }
151:
152: private static List<LocalDataSourceInfo> dbcuLocalDataSources = new ArrayList<LocalDataSourceInfo>();
153:
154: @XmlElementWrapper(name="data-sources")
155: @XmlElement(name="data-source")
156: public List<DataSourceInfo> getDataSources() {
157: return DBConfigUtil.dbcuDataSources;
158: }
159:
160: public void setDataSources(List<DataSourceInfo> ds) {
161: DBConfigUtil.dbcuDataSources = ds;
162: }
163:
164: @XmlElementWrapper(name="local-data-sources")
165: @XmlElement(name="data-source")
166: public List<LocalDataSourceInfo> getLocalDataSources() {
167: return DBConfigUtil.dbcuLocalDataSources;
168: }
169:
170: public void setLocalDataSources(List<LocalDataSourceInfo> ds) {
171: DBConfigUtil.dbcuLocalDataSources = ds;
172: }
173:
174: @XmlElement(name="default-data-source")
175: public String getDefaultDataSourceName() {
176: return Defaults.getDataSourceName();
177: }
178:
179: public void setDefaultDataSourceName(String dsName) {
180: Defaults.setDataSourceName(dsName);
181: dbcuLogger.info("Set default data source name to " + dsName);
182: }
183:
184: public String getDataSourceClass() {
185: return DBConfigUtil.dbcuDataSourceClass;
186: }
187:
188: @XmlElement(name="data-source-class")
189: public void setDataSourceClass(String dsClass) {
190: DBConfigUtil.dbcuDataSourceClass = dsClass;
191: }
192:
193: /**
194: * Return the online user data source name. Default is <code>java:comp/env/jdbc/inmemorydb</code>.
195: * The data source name is application server's dependant and need to be change accordingly
196: * @return String represents online users data source name.
197: */
198: @XmlElement(name="online-user-data-source")
199: public String getOnlineUserDataSourceName() {
200: return dbcuInMemoryDataSource;
201: }
202:
203: /**
204: * Sets the online user data source name.
205: * @param dataSourceName
206: */
207: public void setOnlineUserDataSourceName(String dataSourceName) {
208: if (dataSourceName != null && !"".equals(dataSourceName.trim())) {
209: dbcuInMemoryDataSource = dataSourceName;
210: dbcuLogger.info("Online user data source is set to "
211: + dbcuInMemoryDataSource);
212: }
213: }
214:
215: /**
216: * Indicates whether or not the online user data source is shutdown upon stopping/restarting.
217: * Default is true (shutdown).
218: * @return true if the data source need to be shutdown.
219: */
220: @XmlElement(name="shutdown-default-data-source")
221: public boolean isShutdownDefaultDataSource() {
222: return dbcuIsShutdownDefaultDataSource;
223: }
224:
225: public void setShutdownDefaultDataSource(boolean isShutdown) {
226: dbcuIsShutdownDefaultDataSource = isShutdown;
227: }
228:
229: @XmlElement(name="online-user-table-name")
230: public String getOnlineUserTableName() {
231: return dbcuOnlineUserTableName;
232: }
233:
234: public void setOnlineUserTableName(String tableName) {
235: if (tableName != null && !"".equals(tableName.trim())) {
236: dbcuOnlineUserTableName = tableName;
237: dbcuLogger.info("Online user table name is set to "
238: + dbcuOnlineUserTableName);
239:
240: }
241: }
242:
243: /**
244: * Return constant represents the java.sql.Types type.
245: *
246: * @param str String representing SQL type, i.e. VARCHAR, INTEGER, etc.
247: * @return java.sql.Types contants
248: * @throws Exception
249: */
250: public static int getSQLType(String str) {
251: if (str == null)
252: return java.sql.Types.VARCHAR;
253: str = str.toUpperCase();
254: if (str.equals("VARCHAR"))
255: return java.sql.Types.VARCHAR;
256: else if (str.equals("INTEGER"))
257: return java.sql.Types.INTEGER;
258: else if (str.equals("DECIMAL"))
259: return java.sql.Types.DECIMAL;
260: else if (str.equals("NUMERIC"))
261: return java.sql.Types.NUMERIC;
262: else if (str.equals("BOOLEAN"))
263: return java.sql.Types.BOOLEAN;
264: else if (str.equals("SMALLINT"))
265: return java.sql.Types.SMALLINT;
266: else if (str.equals("DATE"))
267: return java.sql.Types.DATE;
268: else if (str.equals("TIME"))
269: return java.sql.Types.TIME;
270: else if (str.equals("TIMESTAMP"))
271: return java.sql.Types.TIMESTAMP;
272: else if (str.equals("FLOAT"))
273: return java.sql.Types.FLOAT;
274: else if (str.equals("DOUBLE"))
275: return java.sql.Types.DOUBLE;
276: else if (str.equals("ARRAY"))
277: return java.sql.Types.ARRAY;
278: else if (str.equals("BIGINT"))
279: return java.sql.Types.BIGINT;
280: else if (str.equals("BINARY"))
281: return java.sql.Types.BINARY;
282: else if (str.equals("BIT"))
283: return java.sql.Types.BIT;
284: else if (str.equals("BLOB"))
285: return java.sql.Types.BLOB;
286: else if (str.equals("CHAR"))
287: return java.sql.Types.CHAR;
288: else if (str.equals("CLOB"))
289: return java.sql.Types.CLOB;
290: else if (str.equals("LONGVARBINARY"))
291: return java.sql.Types.LONGVARBINARY;
292: else if (str.equals("LONGVARCHAR"))
293: return java.sql.Types.LONGVARCHAR;
294: else if (str.equals("JAVA_OBJECT"))
295: return java.sql.Types.JAVA_OBJECT;
296: else
297: throw new IllegalArgumentException("Unrecognized SQL type.");
298: }
299:
300: /**
301: * Returns String represents the java.sql.Types (in upper case).
302: * @param code java.sql.Types constant
303: * @return String (in upper case) representing the java.sql.Types constants.
304: */
305: public static String getSQLTypeString(int code) {
306:
307: switch (code) {
308: case Types.VARCHAR:
309: return "VARCHAR";
310: case Types.INTEGER:
311: return "INTEGER";
312: case Types.DECIMAL:
313: return "DECIMAL";
314: case Types.NUMERIC:
315: return "NUMERIC";
316: case Types.BOOLEAN:
317: return "BOOLEAN";
318: case Types.SMALLINT:
319: return "SMALLINT";
320: case Types.DATE:
321: return "DATE";
322: case Types.TIME:
323: return "TIME";
324: case Types.TIMESTAMP:
325: return "TIMESTAMP";
326: case Types.FLOAT:
327: return "FLOAT";
328: case Types.DOUBLE:
329: return "DOUBLE";
330: case Types.ARRAY:
331: return "ARRAY";
332: case Types.BIGINT:
333: return "BIGINT";
334: case Types.BINARY:
335: return "BINARY";
336: case Types.BIT:
337: return "BIT";
338: case Types.BLOB:
339: return "BLOB";
340: case Types.CHAR:
341: return "CHAR";
342: case Types.CLOB:
343: return "CLOB";
344: case Types.LONGVARBINARY:
345: return "LONGVARBINARY";
346: case Types.LONGVARCHAR:
347: return "LONGVARCHAR";
348: case Types.JAVA_OBJECT:
349: return "JAVA_OBJECT";
350: default:
351: return "VARCHAR";
352: }
353: }
354:
355: public static synchronized void init(String dbConfigFile,
356: String webAppRoot) throws JAXBException, ServletException,
357: IOException {
358: if (dbcuInitialized)
359: return;
360:
361: InputStream fis = null;
362: // dbcuConfigFile = dbConfigFile;
363: dbcuRealPath = webAppRoot;
364:
365: if (!new File(dbConfigFile).exists()) {
366: dbcuInitialized = true;
367: dbcuLogger
368: .warn("Database properties not initialized. The configuration file, dbconfig.xml, could not be found.");
369:
370: } else {
371: try {
372: fis = new FileInputStream(dbConfigFile);
373: init(fis, webAppRoot);
374: } catch (FileNotFoundException fex) {
375:
376: } finally {
377: try {
378: fis.close();
379: } catch (Exception ex) {
380: }
381: }
382: }
383: }
384:
385: public synchronized static void init(InputStream fis,
386: String webAppRoot) throws JAXBException, IOException,
387: ServletException {
388: if (dbcuInitialized)
389: return;
390:
391: if (fis == null) {
392: dbcuInitialized = true;
393: dbcuLogger
394: .warn("Database properties not initialized. The configuration file, dbconfig.xml, could not be found.");
395: return;
396: }
397:
398: JAXBContext jc = JAXBContext.newInstance(DBConfigUtil.class);
399: Unmarshaller um = jc.createUnmarshaller();
400: dbcuSelf = (DBConfigUtil) um.unmarshal(fis);
401: dbcuInitialized = true;
402: dbcuLogger
403: .info("Successfully parsed database configuration file.");
404:
405: //set real path
406: dbcuRealPath = webAppRoot;
407:
408: // Check if you are using local datasource. In this case,
409: // DBUtil.getConnection method will not look up datasources
410: // in JNDI. Instead a static hashmap will be used. This
411: // is done to make Stringbeans webapp completely self-contained.
412:
413: if (DBConfigUtil.dbcuLocalDataSources.size() > 0) {
414: DBUtil.dbuUseJNDI = false;
415: try {
416: for (int i = 0; i < DBConfigUtil.dbcuLocalDataSources
417: .size(); i++) {
418: setDataSource(webAppRoot,
419: DBConfigUtil.dbcuLocalDataSources.get(i));
420: }
421: } catch (Exception e) {
422: throw new ServletException(
423: "Failed to create local data sources.", e);
424: }
425: }
426:
427: if (Defaults.getDataSourceName() == null) {
428: dbcuLogger
429: .warn("Could not find default data source name in configuration.");
430: }
431:
432: boolean isDefaultDataSourceListed = false;
433: boolean isOnlineUserDataSourceListed = false;
434: if (DBConfigUtil.dbcuDataSources.size() > 0) {
435: String defaultDataSourceName = Defaults.getDataSourceName();
436:
437: for (int i = 0; i < DBConfigUtil.dbcuDataSources.size(); i++) {
438: DataSourceInfo dsInfo = DBConfigUtil.dbcuDataSources
439: .get(i);
440: dbcuDataSourceMap.put(dsInfo.label, dsInfo.name);
441:
442: if (StringUtil.isNotNullOrEmpty(dsInfo.flavor)) {
443: DBUtil.dbuDSFlavorsTable.put(dsInfo.name,
444: dsInfo.flavor);
445: }
446:
447: if (defaultDataSourceName != null
448: && defaultDataSourceName.equals(dsInfo.name))
449: isDefaultDataSourceListed = true;
450:
451: if (dbcuInMemoryDataSource.equals(dsInfo.name))
452: isOnlineUserDataSourceListed = true;
453: }
454: } else {
455: dbcuLogger
456: .warn("List of data sources in used is not defined.");
457:
458: }
459:
460: if (!isDefaultDataSourceListed) {
461: dbcuLogger
462: .warn("Default data source is not specified in data sources list: "
463: + Defaults.getDataSourceName());
464: }
465:
466: if (!isOnlineUserDataSourceListed) {
467: dbcuLogger
468: .warn("Online user data source is not specified in data sources list: "
469: + dbcuInMemoryDataSource);
470: }
471:
472: dbcuInitialized = true;
473:
474: }
475:
476: public static void setDataSource(String webappRoot,
477: LocalDataSourceInfo dsInfo) throws InstantiationException,
478: IllegalAccessException, ClassNotFoundException {
479:
480: String dataSourceName = dsInfo.name;
481: String url = dsInfo.url;
482: String username = dsInfo.userName;
483: String password = dsInfo.password;
484: String driver = dsInfo.driverClassName;
485: int webRootIndex = url.indexOf("$");
486: if (webRootIndex != -1) {
487: if (url.substring(webRootIndex).startsWith("${webappRoot}")) {
488: url = url.substring(0, webRootIndex)
489: + webappRoot.replaceAll("\\\\", "/")
490: + url.substring(webRootIndex + 13);
491: }
492: }
493:
494: DataSource ds = null;
495:
496: if (DBConfigUtil.dbcuDataSourceClass == null) {
497: BasicDataSource lds = new BasicDataSource();
498: lds.setUrl(url);
499: lds.setUsername(username);
500: lds.setPassword(password);
501: lds.setDriverClassName(driver);
502: ds = lds;
503: } else {
504: ds = (DataSource) Class.forName(
505: DBConfigUtil.dbcuDataSourceClass).newInstance();
506: }
507: for (int j = 0; j < dsInfo.properties.size(); j++) {
508: try {
509: PropertyInfo propInfo = dsInfo.properties.get(j);
510: ReflectionUtil.setProperty(ds, propInfo.name,
511: propInfo.value, true);
512: } catch (Exception e) {
513: dbcuLogger.warn(
514: "Error in setting data source property.", e);
515: }
516: }
517:
518: DBUtil.dbuLocalDSTable.put(dataSourceName, ds);
519: }
520:
521: public static HashMap getDataSourceMap() {
522: return dbcuDataSourceMap;
523: }
524:
525: public static DBConfigUtil getInstance() {
526: return dbcuSelf;
527: }
528:
529: public static void save() throws IOException, JAXBException {
530: String dbFilePath = dbcuRealPath + File.separator + "WEB-INF"
531: + File.separator + "dbconfig.xml";
532: File backupDbFile = new File(dbFilePath + ".backup");
533: File tempDbFile = new File(dbFilePath + ".tmp");
534: tempDbFile.delete();
535: JAXBContext jc = JAXBContext.newInstance(DBConfigUtil.class);
536: Marshaller m = jc.createMarshaller();
537: m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
538: m.marshal(dbcuSelf, new FileWriter(tempDbFile));
539: if (backupDbFile.exists())
540: backupDbFile.delete();
541: File portalFile = new File(dbFilePath);
542: portalFile.renameTo(backupDbFile);
543: boolean success = tempDbFile.renameTo(portalFile);
544: if (!success) {
545: dbcuLogger.warn("Failed to rename dbconfig.xml.");
546:
547: throw new IOException("Failed to rename dbconfig.xml.");
548: }
549: }
550:
551: public static void main(String[] args) {
552: try {
553: Class[] cls = new Class[] { DBConfigUtil.class };
554:
555: /*
556: DBConfigUtil db = new DBConfigUtil();
557: db.setDefaultDataSourceName("java:comp/env/jdbc/stringbeansdb");
558: db.setOnlineUserDataSourceName("java:comp/env/jdbc/inmemorydb");
559: db.setOnlineUserTableName("IM_ACTIVE_SESSIONS");
560: db.setShutdownDefaultDataSource(true);
561:
562: DataSourceInfo dsInfo1 = new DataSourceInfo();
563: // <data-source name= label="Stringbeans data source" flavor="" />
564:
565: dsInfo1.name = "java:comp/env/jdbc/stringbeansdb";
566: dsInfo1.label = "Stringbeans data source";
567: dsInfo1.flavor = "org.hibernate.dialect.SAPDBDialect";
568: db.getDataSources().add(dsInfo1);
569:
570: DataSourceInfo dsInfo2 = new DataSourceInfo();
571: dsInfo2.name = "java:comp/env/jdbc/anotherdb";
572: dsInfo2.label = "Stringbeans data source";
573: dsInfo2.flavor = "org.hibernate.dialect.HSQLDilect";
574: db.getDataSources().add(dsInfo2);
575:
576: LocalDataSourceInfo ldsInfo1 = new LocalDataSourceInfo();
577: ldsInfo1.driverClassName = "driver class";
578: ldsInfo1.url = "http://www.nabh.com";
579: ldsInfo1.password = "";
580: ldsInfo1.userName = "sa";
581: db.getLocalDataSources().add(ldsInfo1);
582: */
583: JAXBContext jc = JAXBContext.newInstance(cls);
584: Unmarshaller um = jc.createUnmarshaller();
585: DBConfigUtil db = (DBConfigUtil) um
586: .unmarshal(new File(
587: "C:\\AllData\\Stringbeans\\3.x\\webapp\\stringbeans\\WEB-INF\\dbconfig.xml"));
588: Marshaller m = jc.createMarshaller();
589: m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
590: PropertyInfo propInfo = new PropertyInfo();
591: propInfo.name = "size";
592: propInfo.value = "20";
593: db.getLocalDataSources().get(0).properties.add(propInfo);
594: ByteArrayOutputStream sos = new ByteArrayOutputStream();
595: m.marshal(db, sos);
596: String xml = sos.toString();
597: System.out.println(xml);
598: } catch (Exception e) {
599: e.printStackTrace();
600: }
601:
602: }
603:
604: }
|