001: /*
002:
003: Derby - Class org.apache.derby.jdbc.EmbeddedDataSource
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to You under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.jdbc;
023:
024: import org.apache.derby.iapi.reference.Attribute;
025:
026: import java.sql.Connection;
027: import java.sql.DriverManager;
028: import java.sql.SQLException;
029:
030: import java.io.PrintWriter;
031: import java.util.Properties;
032:
033: /* -- New jdbc 20 extension types --- */
034: import javax.sql.DataSource;
035:
036: import org.apache.derby.iapi.reference.Attribute;
037: import org.apache.derby.iapi.reference.MessageId;
038: import org.apache.derby.iapi.reference.SQLState;
039: import org.apache.derby.iapi.error.ExceptionSeverity;
040: import org.apache.derby.iapi.services.i18n.MessageService;
041: import org.apache.derby.impl.jdbc.Util;
042:
043: /**
044:
045:
046: EmbeddedDataSource is Derby's DataSource implementation for JDBC3.0 and JDBC2.0.
047:
048:
049: <P>A DataSource is a factory for Connection objects. An object that
050: implements the DataSource interface will typically be registered with a
051: JNDI service provider.
052: <P>
053: EmbeddedDataSource automatically supports the correct JDBC specification version
054: for the Java Virtual Machine's environment.
055: <UL>
056: <LI> JDBC 3.0 - Java 2 - JDK 1.4, J2SE 5.0
057: <LI> JDBC 2.0 - Java 2 - JDK 1.2,1.3
058: </UL>
059:
060: <P>The following is a list of properties that can be set on a Derby
061: DataSource object:
062: <P><B>Standard DataSource properties</B> (from JDBC 3.0 specification).
063:
064: <UL><LI><B><code>databaseName</code></B> (String): <I>Mandatory</I>
065: <BR>This property must be set and it
066: identifies which database to access. If a database named wombat located at
067: g:/db/wombat is to be accessed, then one should call
068: <code>setDatabaseName("g:/db/wombat")</code> on the data source object.</LI>
069:
070: <LI><B><code>dataSourceName</code></B> (String): <I>Optional</I>
071: <BR> Name for DataSource. Not used by the data source object. Used for
072: informational purpose only.</LI>
073:
074: <LI><B><code>description</code></B> (String): <I>Optional</I>
075: <BR>Description of the data source. Not
076: used by the data source object. Used for informational purpose only.</LI>
077:
078: <LI><B><code>password</code></B> (String): <I>Optional</I>
079: <BR>Database password for the no argument <code>DataSource.getConnection()</code>,
080: <code>ConnectionPoolDataSource.getPooledConnection()</code>
081: and <code>XADataSource.getXAConnection()</code> methods.
082:
083: <LI><B><code>user</code></B> (String): <I>Optional</I>
084: <BR>Database user for the no argument <code>DataSource.getConnection()</code>,
085: <code>ConnectionPoolDataSource.getPooledConnection()</code>
086: and <code>XADataSource.getXAConnection()</code> methods.
087: </UL>
088:
089: <BR><B>Derby specific DataSource properties.</B>
090:
091: <UL>
092:
093: <LI><B><code>attributesAsPassword</code></B> (Boolean): <I>Optional</I>
094: <BR>If true, treat the password value in a
095: <code>DataSource.getConnection(String user, String password)</code>,
096: <code>ConnectionPoolDataSource.getPooledConnection(String user, String password)</code>
097: or <code>XADataSource.getXAConnection(String user, String password)</code> as a set
098: of connection attributes. The format of the attributes is the same as the format
099: of the attributes in the property connectionAttributes. If false the password value
100: is treated normally as the password for the given user.
101: Setting this property to true allows a connection request from an application to
102: provide more authentication information that just a password, for example the request
103: can include the user's password and an encrypted database's boot password.</LI>
104:
105: <LI><B><code>connectionAttributes</code></B> (String): <I>Optional</I>
106: <BR>Defines a set of Derby connection attributes for use in all connection requests.
107: The format of the String matches the format of the connection attributes in a Derby JDBC URL.
108: That is a list of attributes in the form <code><I>attribute</I>=<I>value</I></code>, each separated by semi-colon (';').
109: E.g. <code>setConnectionAttributes("bootPassword=erd3234dggd3kazkj3000");</code>.
110: <BR>The database name must be set by the DataSource property <code>databaseName</code> and not by setting the <code>databaseName</code>
111: connection attribute in the <code>connectionAttributes</code> property.
112: <BR>
113: Any attributes that can be set using a property of this DataSource implementation
114: (e.g user, password) should not be set in connectionAttributes. Conflicting
115: settings in connectionAttributes and properties of the DataSource will lead to
116: unexpected behaviour.
117: <BR>Please see the Derby documentation for a complete list of connection attributes. </LI>
118:
119: <LI><B><code>createDatabase</code></B> (String): <I>Optional</I>
120: <BR>If set to the string "create", this will
121: cause a new database of <code>databaseName</code> if that database does not already
122: exist. The database is created when a connection object is obtained from
123: the data source. </LI>
124:
125: <LI><B><code>shutdownDatabase</code></B> (String): <I>Optional</I>
126: <BR>If set to the string "shutdown",
127: this will cause the database to shutdown when a java.sql.Connection object
128: is obtained from the data source. E.g., If the data source is an
129: XADataSource, a getXAConnection().getConnection() is necessary to cause the
130: database to shutdown.
131:
132: </UL>
133:
134: <P><B>Examples.</B>
135:
136: <P>This is an example of setting a property directly using Derby's
137: EmbeddedDataSource object. This code is typically written by a system integrator :
138: <PRE>
139: *
140: * import org.apache.derby.jdbc.*;
141: *
142: * // dbname is the database name
143: * // if create is true, create the database if necessary
144: * javax.sql.DataSource makeDataSource (String dbname, boolean create)
145: * throws Throwable
146: * {
147: * EmbeddedDataSource ds = new EmbeddedDataSource();
148: * ds.setDatabaseName(dbname);
149: *
150: * if (create)
151: * ds.setCreateDatabase("create");
152: *
153: * return ds;
154: * }
155: </PRE>
156:
157: <P>Example of setting properties thru reflection. This code is typically
158: generated by tools or written by a system integrator: <PRE>
159: *
160: * javax.sql.DataSource makeDataSource(String dbname)
161: * throws Throwable
162: * {
163: * Class[] parameter = new Class[1];
164: * parameter[0] = dbname.getClass();
165: * DataSource ds = new EmbeddedDataSource();
166: * Class cl = ds.getClass();
167: *
168: * Method setName = cl.getMethod("setDatabaseName", parameter);
169: * Object[] arg = new Object[1];
170: * arg[0] = dbname;
171: * setName.invoke(ds, arg);
172: *
173: * return ds;
174: * }
175: </PRE>
176:
177: <P>Example on how to register a data source object with a JNDI naming
178: service.
179: <PRE>
180: * DataSource ds = makeDataSource("mydb");
181: * Context ctx = new InitialContext();
182: * ctx.bind("jdbc/MyDB", ds);
183: </PRE>
184:
185: <P>Example on how to retrieve a data source object from a JNDI naming
186: service.
187: <PRE>
188: * Context ctx = new InitialContext();
189: * DataSource ds = (DataSource)ctx.lookup("jdbc/MyDB");
190: </PRE>
191:
192: */
193: public class EmbeddedDataSource extends ReferenceableDataSource
194: implements javax.sql.DataSource {
195:
196: private static final long serialVersionUID = -4945135214995641181L;
197:
198: /** instance variables that will be serialized */
199:
200: /**
201: * The database name.
202: * @serial
203: */
204: private String databaseName;
205:
206: /**
207: * The data source name.
208: * @serial
209: */
210: private String dataSourceName;
211:
212: /**
213: * Description of the database.
214: * @serial
215: */
216: private String description;
217:
218: /**
219: * Set to "create" if the database should be created.
220: * @serial
221: */
222: private String createDatabase;
223:
224: /**
225: * Set to "shutdown" if the database should be shutdown.
226: * @serial
227: */
228: private String shutdownDatabase;
229:
230: /**
231: * Derby specific connection attributes.
232: * @serial
233: */
234: private String connectionAttributes;
235:
236: /**
237: Set password to be a set of connection attributes.
238: */
239: private boolean attributesAsPassword;
240:
241: /** instance variables that will not be serialized */
242: transient private PrintWriter printer;
243: transient private int loginTimeout;
244:
245: // Unlike a DataSource, LocalDriver is shared by all
246: // Derby databases in the same jvm.
247: transient InternalDriver driver;
248:
249: transient private String jdbcurl;
250:
251: /**
252: No-arg constructor.
253: */
254: public EmbeddedDataSource() {
255: // needed by Object Factory
256:
257: // don't put anything in here or in any of the set method because this
258: // object may be materialized in a remote machine and then sent thru
259: // the net to the machine where it will be used.
260: }
261:
262: //Most of our customers would be using jndi to get the data
263: //sources. Since we don't have a jndi to test this, we are
264: //adding this method to fake it. This is getting used in
265: //xaJNDI test so we can compare the 2 data sources.
266: public boolean equals(Object p0) {
267: if (p0 instanceof EmbeddedDataSource) {
268: EmbeddedDataSource ds = (EmbeddedDataSource) p0;
269:
270: boolean match = true;
271:
272: if (databaseName != null) {
273: if (!(databaseName.equals(ds.databaseName)))
274: match = false;
275: } else if (ds.databaseName != null)
276: match = false;
277:
278: if (dataSourceName != null) {
279: if (!(dataSourceName.equals(ds.dataSourceName)))
280: match = false;
281: } else if (ds.dataSourceName != null)
282: match = false;
283:
284: if (description != null) {
285: if (!(description.equals(ds.description)))
286: match = false;
287: } else if (ds.description != null)
288: match = false;
289:
290: if (createDatabase != null) {
291: if (!(createDatabase.equals(ds.createDatabase)))
292: match = false;
293: } else if (ds.createDatabase != null)
294: match = false;
295:
296: if (shutdownDatabase != null) {
297: if (!(shutdownDatabase.equals(ds.shutdownDatabase)))
298: match = false;
299: } else if (ds.shutdownDatabase != null)
300: match = false;
301:
302: if (connectionAttributes != null) {
303: if (!(connectionAttributes
304: .equals(ds.connectionAttributes)))
305: match = false;
306: } else if (ds.connectionAttributes != null)
307: match = false;
308:
309: if (loginTimeout != ds.loginTimeout)
310: match = false;
311:
312: return match;
313:
314: }
315:
316: return false;
317: }
318:
319: /*
320: * Properties to be seen by Bean - access thru reflection.
321: */
322:
323: /**
324: Set this property to create a new database. If this
325: property is not set, the database (identified by databaseName) is
326: assumed to be already existing.
327:
328: @param create if set to the string "create", this data source will try
329: to create a new database of databaseName, or boot the database if one
330: by that name already exists.
331: */
332: public final void setCreateDatabase(String create) {
333: if (create != null
334: && create.toLowerCase(java.util.Locale.ENGLISH).equals(
335: "create"))
336: createDatabase = create;
337: else
338: createDatabase = null;
339: }
340:
341: /** @return "create" if create is set, or null if not */
342: public final String getCreateDatabase() {
343: return createDatabase;
344: }
345:
346: /**
347: Set this property if one wishes to shutdown the database identified by
348: databaseName.
349:
350: @param shutdown if set to the string "shutdown", this data source will
351: shutdown the database if it is running.
352: */
353: public final void setShutdownDatabase(String shutdown) {
354: if (shutdown != null && shutdown.equalsIgnoreCase("shutdown"))
355: shutdownDatabase = shutdown;
356: else
357: shutdownDatabase = null;
358: }
359:
360: /** @return "shutdown" if shutdown is set, or null if not */
361: public final String getShutdownDatabase() {
362: return shutdownDatabase;
363: }
364:
365: /**
366: Set this property to pass in more Derby specific
367: connection URL attributes.
368: <BR>
369: Any attributes that can be set using a property of this DataSource implementation
370: (e.g user, password) should not be set in connectionAttributes. Conflicting
371: settings in connectionAttributes and properties of the DataSource will lead to
372: unexpected behaviour.
373:
374: @param prop set to the list of Derby connection
375: attributes separated by semi-colons. E.g., to specify an encryption
376: bootPassword of "x8hhk2adf", and set upgrade to true, do the following:
377: <PRE>
378: ds.setConnectionAttributes("bootPassword=x8hhk2adf;upgrade=true");
379: </PRE>
380: See the Derby documentation for complete list.
381: */
382: public final void setConnectionAttributes(String prop) {
383: connectionAttributes = prop;
384: update();
385: }
386:
387: /** @return Derby specific connection URL attributes */
388: public final String getConnectionAttributes() {
389: return connectionAttributes;
390: }
391:
392: /**
393: Set attributeAsPassword property to enable passing connection request attributes in the password argument of getConnection.
394: If the property is set to true then the password argument of the DataSource.getConnection(String user, String password)
395: method call is taken to be a list of connection attributes with the same format as the connectionAttributes property.
396:
397: @param attributesAsPassword true to encode password argument as a set of connection attributes in a connection request.
398: */
399: public final void setAttributesAsPassword(
400: boolean attributesAsPassword) {
401: this .attributesAsPassword = attributesAsPassword;
402: update();
403: }
404:
405: /**
406: Return the value of the attributesAsPassword property.
407: */
408: public final boolean getAttributesAsPassword() {
409: return attributesAsPassword;
410: }
411:
412: /*
413: * DataSource methods
414: */
415:
416: /**
417: * Attempt to establish a database connection.
418: *
419: * @return a Connection to the database
420: * @exception SQLException if a database-access error occurs.
421: */
422: public final Connection getConnection() throws SQLException {
423: return this .getConnection(getUser(), getPassword(), false);
424: }
425:
426: /**
427: * Attempt to establish a database connection with the given username and password.
428: If the attributeAsPassword property is set to true then the password argument is taken to be a list of
429: connection attributes with the same format as the connectionAttributes property.
430:
431: *
432: * @param username the database user on whose behalf the Connection is
433: * being made
434: * @param password the user's password
435: * @return a Connection to the database
436: * @exception SQLException if a database-access error occurs.
437: */
438: public final Connection getConnection(String username,
439: String password) throws SQLException {
440: return this .getConnection(username, password, true);
441: }
442:
443: /**
444: @param requestPassword true if the password came from the getConnection() call.
445: */
446: final Connection getConnection(String username, String password,
447: boolean requestPassword) throws SQLException {
448:
449: Properties info = new Properties();
450: if (username != null)
451: info.put(Attribute.USERNAME_ATTR, username);
452:
453: if (!requestPassword || !attributesAsPassword) {
454: if (password != null)
455: info.put(Attribute.PASSWORD_ATTR, password);
456: }
457:
458: if (createDatabase != null)
459: info.put(Attribute.CREATE_ATTR, "true");
460: if (shutdownDatabase != null)
461: info.put(Attribute.SHUTDOWN_ATTR, "true");
462:
463: String url = jdbcurl;
464:
465: if (attributesAsPassword && requestPassword && password != null) {
466:
467: StringBuffer sb = new StringBuffer(url.length()
468: + password.length() + 1);
469:
470: sb.append(url);
471: sb.append(';');
472: sb.append(password); // these are now request attributes on the URL
473:
474: url = sb.toString();
475:
476: }
477: Connection conn = findDriver().connect(url, info);
478:
479: // JDBC driver's getConnection method returns null if
480: // the driver does not handle the request's URL.
481: if (conn == null)
482: throw Util.generateCsSQLException(
483: SQLState.PROPERTY_INVALID_VALUE,
484: Attribute.DBNAME_ATTR, getDatabaseName());
485:
486: return conn;
487: }
488:
489: InternalDriver findDriver() throws SQLException {
490: String url = jdbcurl;
491:
492: if (driver == null || !driver.acceptsURL(url)) {
493: synchronized (this ) {
494: // The driver has either never been booted, or it has been
495: // shutdown by a 'jdbc:derby:;shutdown=true'
496: if (driver == null || !driver.acceptsURL(url)) {
497:
498: new org.apache.derby.jdbc.EmbeddedDriver();
499:
500: // If we know the driver, we loaded it. Otherwise only
501: // work if DriverManager has already loaded it.
502:
503: AutoloadedDriver autoloadedDriver = (AutoloadedDriver) DriverManager
504: .getDriver(url);
505: driver = (InternalDriver) autoloadedDriver
506: .getDriverModule();
507: // DriverManager will throw an exception if it cannot find the driver
508: }
509: }
510: }
511: return driver;
512: // else driver != null and driver can accept url
513: }
514:
515: void update() {
516: StringBuffer sb = new StringBuffer(64);
517:
518: sb.append(Attribute.PROTOCOL);
519:
520: // Set the database name from the databaseName property
521: String dbName = getDatabaseName();
522:
523: if (dbName != null) {
524: dbName = dbName.trim();
525: }
526:
527: if (dbName == null || dbName.length() == 0) {
528: // need to put something in so that we do not allow the
529: // database name to be set from the request or from the
530: // connection attributes.
531:
532: // this space will selected as the database name (and trimmed to an empty string)
533: // See the getDatabaseName() code in InternalDriver. Since this is a non-null
534: // value, it will be selected over any databaseName connection attribute.
535: dbName = " ";
536: }
537:
538: sb.append(dbName);
539:
540: String connAttrs = getConnectionAttributes();
541: if (connAttrs != null) {
542: connAttrs = connAttrs.trim();
543: if (connAttrs.length() != 0) {
544: sb.append(';');
545: sb.append(connectionAttributes);
546: }
547: }
548:
549: jdbcurl = sb.toString();
550: }
551: }
|