001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.storage.implementation.database;
011:
012: import java.sql.*;
013:
014: import javax.sql.DataSource;
015: import java.lang.reflect.*;
016:
017: import org.mmbase.module.Module;
018: import org.mmbase.module.core.MMBase;
019: import org.mmbase.module.database.JDBC;
020: import org.mmbase.module.database.MultiConnection;
021: import org.mmbase.storage.StorageInaccessibleException;
022: import org.mmbase.util.logging.*;
023:
024: /**
025: * This class functions as a Datasource wrapper around the JDBC Module.
026: * It is intended for use when MMBase runs outside an applicationserver, or when the server does not (or poorly)
027: * support pooled datasources.
028: * I can also be used by older systems that use the JDBC Module and do not want to change their configuration.
029: * Note that the JDBC Module will likely be fased out as a module at some point in the future,
030: * with code and supporting classes to be moved to this class instead.
031: *
032: * @author Pierre van Rooden
033: * @author Michiel Meeuwissen
034: * @since MMBase-1.7
035: * @version $Id: GenericDataSource.java,v 1.16 2008/02/22 12:27:48 michiel Exp $
036: */
037: public final class GenericDataSource implements DataSource {
038: private static final Logger log = Logging
039: .getLoggerInstance(GenericDataSource.class);
040:
041: // Reference to the JDBC Module
042: final private JDBC jdbc;
043:
044: private java.io.PrintWriter printWriter = null;
045:
046: final private String dataDir;
047: final private boolean meta;
048:
049: private boolean basePathOk = false;
050:
051: /**
052: * Constructs a datasource for accessing the database belonging to the given MMBase module.
053: * The MMBase parameter is not currently used, but is included for future expansion
054: * @param mmbase the MMBase instance
055: * @param A Datadir (as a string ending in a /) which may be used in some URL's (most noticably those of HSQLDB). Can be <code>null</code> if not used.
056: * @throws StorageInaccessibleException if the JDBC module used in creating the datasource is inaccessible
057: */
058: GenericDataSource(MMBase mmbase, String dataDir)
059: throws StorageInaccessibleException {
060: jdbc = Module.getModule(JDBC.class);
061: if (jdbc == null) {
062: throw new StorageInaccessibleException(
063: "Cannot load Datasource or JDBC Module");
064: }
065: jdbc.startModule();
066: this .dataDir = dataDir == null ? "" : dataDir;
067: meta = false;
068: }
069:
070: /**
071: */
072: GenericDataSource(MMBase mmbase)
073: throws StorageInaccessibleException {
074: jdbc = Module.getModule(JDBC.class);
075: if (jdbc == null) {
076: throw new StorageInaccessibleException(
077: "Cannot load Datasource or JDBC Module");
078: }
079: dataDir = null;
080: meta = true;
081: }
082:
083: /**
084: * Interesting trick to make things compile in both java 1.5 and 1.6
085: */
086: public static class ConnectionProxy implements
087: java.lang.reflect.InvocationHandler {
088:
089: private final MultiConnection multiCon;
090:
091: public static Connection newInstance(
092: MultiConnection multiConnection) {
093: return (Connection) java.lang.reflect.Proxy
094: .newProxyInstance(multiConnection.getClass()
095: .getClassLoader(), new Class[] {
096: Connection.class, MultiConnection.class },
097: new ConnectionProxy(multiConnection));
098: }
099:
100: private ConnectionProxy(MultiConnection m) {
101: this .multiCon = m;
102: }
103:
104: public Object invoke(Object proxy, Method m, Object[] args)
105: throws Throwable {
106: Object result;
107: try {
108:
109: Method multiMethod = multiCon.getClass().getMethod(
110: m.getName(), m.getParameterTypes());
111: result = multiMethod.invoke(multiCon, args);
112: } catch (InvocationTargetException e) {
113: throw e.getTargetException();
114: } catch (IllegalArgumentException iae) {
115: log.service("Probably called unimplemented method " + m
116: + ", falling back to wrapped object. "
117: + iae.getMessage(), iae);
118: try {
119: result = m.invoke(
120: multiCon.unwrap(Connection.class), args);
121: } catch (Exception e) {
122: throw new RuntimeException(
123: "unexpected invocation exception: "
124: + e.getMessage());
125: }
126: } catch (Exception e) {
127: throw new RuntimeException(
128: "unexpected invocation exception: "
129: + e.getMessage());
130: } finally {
131:
132: }
133: return result;
134: }
135: }
136:
137: // see javax.sql.DataSource
138: public Connection getConnection() throws SQLException {
139: String url = makeUrl();
140: if (log.isTraceEnabled()) {
141: log.trace("Getting " + (meta ? "META " : "")
142: + "connection for " + url);
143: }
144: if (meta) {
145: String name = jdbc.getInitParameter("user");
146: String password = jdbc.getInitParameter("password");
147: if (name.equals("url") && password.equals("url")) {
148: return DriverManager.getConnection(url);
149: } else {
150: return DriverManager.getConnection(url, name, password);
151: }
152: } else {
153: // why is this
154: return ConnectionProxy.newInstance(jdbc.getConnection(url));
155:
156: }
157: }
158:
159: /**
160: * @since MMBase-1.8
161: */
162: public Connection getDirectConnection() throws SQLException {
163: String url = makeUrl();
164: return jdbc.getDirectConnection(url);
165: }
166:
167: // see javax.sql.DataSource
168: public Connection getConnection(String userName, String password)
169: throws SQLException {
170: String url = makeUrl();
171: if (log.isDebugEnabled()) {
172: log.trace("Getting " + (meta ? "META " : "")
173: + "connection for " + url);
174: }
175: if (meta) {
176: return DriverManager.getConnection(url, userName, password);
177: } else {
178: return ConnectionProxy.newInstance(jdbc.getConnection(url,
179: userName, password));
180: }
181: }
182:
183: // see javax.sql.DataSource
184: public int getLoginTimeout() {
185: return 0;
186: }
187:
188: // see javax.sql.DataSource
189: public java.io.PrintWriter getLogWriter() {
190: return printWriter;
191: }
192:
193: /**
194: * {@inheritDoc}
195: * Note: currently this code does not actually change the timeout. Login timeout is not implemented by JDBC module
196: */
197: public void setLoginTimeout(int seconds) {
198: // loginTimeout = seconds;
199: }
200:
201: // see javax.sql.DataSource
202: public void setLogWriter(java.io.PrintWriter out) {
203: printWriter = out;
204: }
205:
206: /**
207: * Makes URL which can be used to produce a Conncetion. If this is a 'meta' datasource, then
208: * 'lookup.xml' will be tried, for a 'meta url'.
209: * @since MMBase-1.8
210: */
211: protected String makeUrl() {
212: if (meta) {
213: DatabaseStorageLookup lookup = new DatabaseStorageLookup();
214: try {
215: String metaUrl = lookup.getMetaURL(Class.forName(jdbc
216: .getInitParameter("driver")));
217: if (metaUrl != null) {
218: String database = jdbc.getInitParameter("database");
219: if (database != null) {
220: metaUrl = metaUrl
221: .replaceAll("\\$DBM", database);
222: }
223: String host = jdbc.getInitParameter("host");
224: if (host != null) {
225: metaUrl = metaUrl.replaceAll("\\$HOST", host);
226: }
227: String port = jdbc.getInitParameter("port");
228: if (port != null) {
229: metaUrl = metaUrl.replaceAll("\\$PORT", port);
230: }
231: return metaUrl;
232: }
233: } catch (ClassNotFoundException cnfe) {
234: log.error(cnfe);
235: }
236: }
237: String url = jdbc.makeUrl();
238: String newUrl = url.replaceAll("\\$DATADIR", dataDir);
239: if ((!basePathOk) && (!newUrl.equals(url))) {
240: basePathOk = DatabaseStorageManagerFactory
241: .checkBinaryFileBasePath(dataDir);
242: }
243: return newUrl;
244: }
245:
246: //untested
247: public boolean isWrapperFor(Class<?> iface) {
248: return iface.isAssignableFrom(JDBC.class);
249: }
250:
251: //untested
252: public <T> T unwrap(Class<T> iface) {
253: return (T) jdbc;
254: }
255:
256: }
|