001: /*
002:
003: Derby - Class org.apache.derby.jdbc.EmbeddedSimpleDataSource
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.jdbc.JDBCBoot;
025: import org.apache.derby.iapi.reference.Attribute;
026: import org.apache.derby.iapi.reference.MessageId;
027:
028: import java.sql.Connection;
029: import java.sql.SQLException;
030:
031: import java.io.PrintWriter;
032: import java.util.Properties;
033:
034: /* -- New jdbc 20 extension types --- */
035: import javax.sql.DataSource;
036:
037: import org.apache.derby.iapi.reference.SQLState;
038: import org.apache.derby.iapi.services.i18n.MessageService;
039: import org.apache.derby.impl.jdbc.Util;
040:
041: /**
042: *
043: *
044: * EmbeddedSimpleDataSource is Derby's DataSource implementation
045: * for J2ME/CDC/Foundation. It is also supports J2SE platforms.
046: *
047: *
048: * Supports the same properties as EmbeddedDataSource, see that class for details.
049: * <P>
050: EmbeddedSimpleDataSource automatically supports the correct JDBC specification version
051: for the Java Virtual Machine's environment.
052: <UL>
053: <LI> JDBC Optional Package for CDC/Foundation Profile(JSR-169) - J2ME - CDC/Foundation
054: <LI> JDBC 3.0 - Java 2 - JDK 1.4, J2SE 5.0
055: <LI> JDBC 2.0 - Java 2 - JDK 1.2,1.3
056: </UL>
057: * @see EmbeddedDataSource
058: *
059: */
060: public final class EmbeddedSimpleDataSource implements DataSource {
061:
062: private String password;
063:
064: private String user;
065:
066: /**
067: * The database name.
068: *
069: * @serial
070: */
071: private String databaseName;
072:
073: /**
074: * The data source name.
075: *
076: * @serial
077: */
078: private String dataSourceName;
079:
080: /**
081: * Description of the database.
082: *
083: * @serial
084: */
085: private String description;
086:
087: /**
088: * Set to "create" if the database should be created.
089: *
090: * @serial
091: */
092: private String createDatabase;
093:
094: /**
095: * Set to "shutdown" if the database should be shutdown.
096: *
097: * @serial
098: */
099: private String shutdownDatabase;
100:
101: /**
102: * Cloudscape specific connection attributes.
103: *
104: * @serial
105: */
106: private String connectionAttributes;
107:
108: /** instance variables that will not be serialized */
109: transient private PrintWriter printer;
110:
111: transient private int loginTimeout;
112:
113: // Unlike a DataSource, LocalDriver is shared by all
114: // Cloudscape databases in the same jvm.
115: transient private InternalDriver driver;
116:
117: transient private String jdbcurl;
118:
119: /**
120: * No-arg constructor.
121: */
122: public EmbeddedSimpleDataSource() {
123: }
124:
125: /*
126: * DataSource methods
127: */
128:
129: /**
130: * Gets the maximum time in seconds that this data source can wait while
131: * attempting to connect to a database. A value of zero means that the
132: * timeout is the default system timeout if there is one; otherwise it means
133: * that there is no timeout. When a data source object is created, the login
134: * timeout is initially zero.
135: *
136: * @return the data source login time limit
137: * @exception SQLException
138: * if a database access error occurs.
139: */
140: public int getLoginTimeout() throws SQLException {
141: return loginTimeout;
142: }
143:
144: /**
145: * Sets the maximum time in seconds that this data source will wait while
146: * attempting to connect to a database. A value of zero specifies that the
147: * timeout is the default system timeout if there is one; otherwise it
148: * specifies that there is no timeout. When a data source object is created,
149: * the login timeout is initially zero.
150: * <P>
151: * Cloudscape ignores this property.
152: *
153: * @param seconds
154: * the data source login time limit
155: * @exception SQLException
156: * if a database access error occurs.
157: */
158: public void setLoginTimeout(int seconds) throws SQLException {
159: loginTimeout = seconds;
160: }
161:
162: /**
163: * Get the log writer for this data source.
164: *
165: * <p>
166: * The log writer is a character output stream to which all logging and
167: * tracing messages for this data source object instance will be printed.
168: * This includes messages printed by the methods of this object, messages
169: * printed by methods of other objects manufactured by this object, and so
170: * on. Messages printed to a data source specific log writer are not printed
171: * to the log writer associated with the java.sql.Drivermanager class. When
172: * a data source object is created the log writer is initially null, in
173: * other words, logging is disabled.
174: *
175: * @return the log writer for this data source, null if disabled
176: * @exception SQLException
177: * if a database-access error occurs.
178: */
179: public PrintWriter getLogWriter() throws SQLException {
180: return printer;
181: }
182:
183: /**
184: * Set the log writer for this data source.
185: *
186: * <p>
187: * The log writer is a character output stream to which all logging and
188: * tracing messages for this data source object instance will be printed.
189: * This includes messages printed by the methods of this object, messages
190: * printed by methods of other objects manufactured by this object, and so
191: * on. Messages printed to a data source specific log writer are not printed
192: * to the log writer associated with the java.sql.Drivermanager class. When
193: * a data source object is created the log writer is initially null, in
194: * other words, logging is disabled.
195: *
196: * @param out
197: * the new log writer; to disable, set to null
198: * @exception SQLException
199: * if a database-access error occurs.
200: */
201: public void setLogWriter(PrintWriter out) throws SQLException {
202: printer = out;
203: }
204:
205: /*
206: * Properties to be seen by Bean - access thru reflection.
207: */
208: /**
209: * Set the database name. Setting this property is mandatory. If a database
210: * named wombat at g:/db needs to be accessed, database name should be set
211: * to "g:/db/wombat". The database will be booted if it is not already
212: * running in the system.
213: *
214: * @param databaseName
215: * the name of the database
216: */
217: public final synchronized void setDatabaseName(String databaseName) {
218: this .databaseName = databaseName;
219: update();
220: }
221:
222: public String getDatabaseName() {
223: return databaseName;
224: }
225:
226: /**
227: * Set the data source name. The property is not mandatory. It is used for
228: * informational purposes only.
229: *
230: * @param dsn
231: * the name of the data source
232: */
233: public final void setDataSourceName(String dsn) {
234: dataSourceName = dsn;
235: }
236:
237: /** @return data source name */
238: public final String getDataSourceName() {
239: return dataSourceName;
240: }
241:
242: /**
243: * Set the data source descripton. This property is not mandatory. It is
244: * used for informational purposes only.
245: *
246: * @param desc
247: * the description of the data source
248: */
249: public final void setDescription(String desc) {
250: description = desc;
251: }
252:
253: /** @return description */
254: public final String getDescription() {
255: return description;
256: }
257:
258: /**
259: * Set the <code>user</code> property for the data source. This is user
260: * name for any data source getConnection() call that takes no arguments.
261: */
262: public final void setUser(String user) {
263: this .user = user;
264: }
265:
266: /** @return user */
267: public final String getUser() {
268: return user;
269: }
270:
271: /**
272: * Set the <code>password</code> property for the data source. This is
273: * user's password for any data source getConnection() call that takes no
274: * arguments.
275: */
276: public final void setPassword(String password) {
277: this .password = password;
278: }
279:
280: /** @return password */
281: public final String getPassword() {
282: return password;
283: }
284:
285: /**
286: * Set this property to create a new database. If this property is not set,
287: * the database (identified by databaseName) is assumed to be already
288: * existing.
289: *
290: * @param create
291: * if set to the string "create", this data source will try to
292: * create a new database of databaseName, or boot the database if
293: * one by that name already exists.
294: */
295: public final void setCreateDatabase(String create) {
296: if (create != null
297: && create.toLowerCase(java.util.Locale.ENGLISH).equals(
298: "create"))
299: createDatabase = create;
300: else
301: createDatabase = null;
302: }
303:
304: /** @return "create" if create is set, or null if not */
305: public final String getCreateDatabase() {
306: return createDatabase;
307: }
308:
309: /**
310: * Set this property if one wishes to shutdown the database identified by
311: * databaseName.
312: *
313: * @param shutdown
314: * if set to the string "shutdown", this data source will
315: * shutdown the database if it is running.
316: */
317: public final void setShutdownDatabase(String shutdown) {
318: if (shutdown != null && shutdown.equalsIgnoreCase("shutdown"))
319: shutdownDatabase = shutdown;
320: else
321: shutdownDatabase = null;
322: }
323:
324: /** @return "shutdown" if shutdown is set, or null if not */
325: public final String getShutdownDatabase() {
326: return shutdownDatabase;
327: }
328:
329: /**
330: * Set this property to pass in more Cloudscape specific connection URL
331: * attributes.
332: <BR>
333: Any attributes that can be set using a property of this DataSource implementation
334: (e.g user, password) should not be set in connectionAttributes. Conflicting
335: settings in connectionAttributes and properties of the DataSource will lead to
336: unexpected behaviour.
337: *
338: * @param prop
339: * set to the list of Cloudscape connection attributes separated
340: * by semi-colons. E.g., to specify an encryption bootPassword of
341: * "x8hhk2adf", and set upgrade to true, do the following:
342: *
343: * <PRE>
344: *
345: * ds.setConnectionAttributes("bootPassword=x8hhk2adf;upgrade=true");
346: *
347: * </PRE>
348: *
349: * See Cloudscape's documentation for complete list.
350: */
351: public final void setConnectionAttributes(String prop) {
352: connectionAttributes = prop;
353: update();
354: }
355:
356: /** @return Cloudscape specific connection URL attributes */
357: public final String getConnectionAttributes() {
358: return connectionAttributes;
359: }
360:
361: /*
362: * DataSource methods
363: */
364:
365: /**
366: * Attempt to establish a database connection.
367: *
368: * @return a Connection to the database
369: * @exception SQLException
370: * if a database-access error occurs.
371: */
372: public final Connection getConnection() throws SQLException {
373: return this .getConnection(getUser(), getPassword());
374: }
375:
376: /**
377: * Attempt to establish a database connection with the given username and
378: * password. If the attributeAsPassword property is set to true then the
379: * password argument is taken to be a list of connection attributes with the
380: * same format as the connectionAttributes property.
381: *
382: *
383: * @param username
384: * the database user on whose behalf the Connection is being made
385: * @param password
386: * the user's password
387: * @return a Connection to the database
388: * @exception SQLException
389: * if a database-access error occurs.
390: */
391: public final Connection getConnection(String username,
392: String password) throws SQLException {
393:
394: Properties info = new Properties();
395: if (username != null)
396: info.put(Attribute.USERNAME_ATTR, username);
397:
398: if (password != null)
399: info.put(Attribute.PASSWORD_ATTR, password);
400:
401: if (createDatabase != null)
402: info.put(Attribute.CREATE_ATTR, "true");
403: if (shutdownDatabase != null)
404: info.put(Attribute.SHUTDOWN_ATTR, "true");
405:
406: Connection conn = findDriver().connect(jdbcurl, info);
407:
408: // JDBC driver's getConnection method returns null if
409: // the driver does not handle the request's URL.
410: if (conn == null)
411: throw Util.generateCsSQLException(
412: SQLState.PROPERTY_INVALID_VALUE,
413: Attribute.DBNAME_ATTR, getDatabaseName());
414:
415: return conn;
416: }
417:
418: private InternalDriver findDriver() throws SQLException {
419: String url = jdbcurl;
420:
421: if (driver == null || !driver.acceptsURL(url)) {
422: synchronized (this ) {
423: // The driver has either never been booted, or it has been
424: // shutdown by a 'jdbc:derby:;shutdown=true'
425: if (driver == null || !driver.acceptsURL(url)) {
426:
427: new JDBCBoot().boot(Attribute.PROTOCOL, System.err);
428:
429: // If we know the driver, we loaded it. Otherwise only
430: // work if DriverManager has already loaded it.
431:
432: driver = InternalDriver.activeDriver();
433:
434: if (driver == null)
435: throw new SQLException(
436: MessageService
437: .getTextMessage(MessageId.CORE_JDBC_DRIVER_UNREGISTERED));
438: }
439: }
440: }
441: return driver;
442: // else driver != null and driver can accept url
443: }
444:
445: private void update() {
446: StringBuffer sb = new StringBuffer(64);
447:
448: sb.append(Attribute.PROTOCOL);
449:
450: // Set the database name from the databaseName property
451: String dbName = getDatabaseName();
452:
453: if (dbName != null) {
454: dbName = dbName.trim();
455: }
456:
457: if (dbName == null || dbName.length() == 0) {
458: // need to put something in so that we do not allow the
459: // database name to be set from the request or from the
460: // connection attributes.
461:
462: // this space will selected as the database name (and trimmed to an
463: // empty string)
464: // See the getDatabaseName() code in InternalDriver. Since this is a
465: // non-null
466: // value, it will be selected over any databaseName connection
467: // attribute.
468: dbName = " ";
469: }
470:
471: sb.append(dbName);
472:
473: String connAttrs = getConnectionAttributes();
474: if (connAttrs != null) {
475: connAttrs = connAttrs.trim();
476: if (connAttrs.length() != 0) {
477: sb.append(';');
478: sb.append(connectionAttributes);
479: }
480: }
481:
482: jdbcurl = sb.toString();
483: }
484: }
|