--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.jdbc.EmbeddedDataSource\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.jdbc;\r
+\r
+import org.apache.derby.iapi.reference.Attribute;\r
+\r
+import java.sql.Connection;\r
+import java.sql.DriverManager;\r
+import java.sql.SQLException;\r
+import java.sql.SQLFeatureNotSupportedException;\r
+import java.io.PrintWriter;\r
+import java.util.Hashtable;\r
+import java.util.Properties;\r
+\r
+import java.util.logging.Logger;\r
+\r
+/* -- New jdbc 20 extension types --- */\r
+import javax.naming.Context;\r
+import javax.naming.Name;\r
+import javax.sql.DataSource;\r
+\r
+\r
+\r
+import org.apache.derby.iapi.reference.Attribute;\r
+import org.apache.derby.iapi.reference.MessageId;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.error.ExceptionSeverity;\r
+import org.apache.derby.iapi.services.i18n.MessageService;\r
+import org.apache.derby.impl.jdbc.Util;\r
+\r
+/** \r
+ \r
+\r
+ EmbeddedDataSource is Derby's DataSource implementation for JDBC3.0.\r
+ \r
+\r
+ <P>A DataSource is a factory for Connection objects. An object that\r
+ implements the DataSource interface will typically be registered with a\r
+ JNDI service provider.\r
+ <P>\r
+ EmbeddedDataSource automatically supports the correct JDBC specification version\r
+ for the Java Virtual Machine's environment.\r
+ <UL>\r
+ <LI> JDBC 3.0 - Java 2 - JDK 1.4, J2SE 5.0\r
+ </UL>\r
+\r
+ <P>The following is a list of properties that can be set on a Derby\r
+ DataSource object:\r
+ <P><B>Standard DataSource properties</B> (from JDBC 3.0 specification).\r
+\r
+ <UL><LI><B><code>databaseName</code></B> (String): <I>Mandatory</I>\r
+ <BR>This property must be set and it\r
+ identifies which database to access. If a database named wombat located at\r
+ g:/db/wombat is to be accessed, then one should call\r
+ <code>setDatabaseName("g:/db/wombat")</code> on the data source object.</LI>\r
+\r
+ <LI><B><code>dataSourceName</code></B> (String): <I>Optional</I>\r
+ <BR> Name for DataSource. Not used by the data source object. Used for\r
+ informational purpose only.</LI>\r
+\r
+ <LI><B><code>description</code></B> (String): <I>Optional</I>\r
+ <BR>Description of the data source. Not\r
+ used by the data source object. Used for informational purpose only.</LI> \r
+\r
+ <LI><B><code>password</code></B> (String): <I>Optional</I>\r
+ <BR>Database password for the no argument <code>DataSource.getConnection()</code>,\r
+ <code>ConnectionPoolDataSource.getPooledConnection()</code>\r
+ and <code>XADataSource.getXAConnection()</code> methods.\r
+\r
+ <LI><B><code>user</code></B> (String): <I>Optional</I>\r
+ <BR>Database user for the no argument <code>DataSource.getConnection()</code>,\r
+ <code>ConnectionPoolDataSource.getPooledConnection()</code>\r
+ and <code>XADataSource.getXAConnection()</code> methods.\r
+ </UL>\r
+\r
+ <BR><B>Derby specific DataSource properties.</B>\r
+\r
+ <UL>\r
+\r
+ <LI><B><code>attributesAsPassword</code></B> (Boolean): <I>Optional</I>\r
+ <BR>If true, treat the password value in a\r
+ <code>DataSource.getConnection(String user, String password)</code>,\r
+ <code>ConnectionPoolDataSource.getPooledConnection(String user, String password)</code>\r
+ or <code>XADataSource.getXAConnection(String user, String password)</code> as a set\r
+ of connection attributes. The format of the attributes is the same as the format\r
+ of the attributes in the property connectionAttributes. If false the password value\r
+ is treated normally as the password for the given user.\r
+ Setting this property to true allows a connection request from an application to\r
+ provide more authentication information that just a password, for example the request\r
+ can include the user's password and an encrypted database's boot password.</LI>\r
+\r
+ <LI><B><code>connectionAttributes</code></B> (String): <I>Optional</I>\r
+ <BR>Defines a set of Derby connection attributes for use in all connection requests.\r
+ The format of the String matches the format of the connection attributes in a Derby JDBC URL.\r
+ That is a list of attributes in the form <code><I>attribute</I>=<I>value</I></code>, each separated by semi-colon (';').\r
+ E.g. <code>setConnectionAttributes("bootPassword=erd3234dggd3kazkj3000");</code>.\r
+ <BR>The database name must be set by the DataSource property <code>databaseName</code> and not by setting the <code>databaseName</code>\r
+ connection attribute in the <code>connectionAttributes</code> property.\r
+ <BR>\r
+ Any attributes that can be set using a property of this DataSource implementation\r
+ (e.g user, password) should not be set in connectionAttributes. Conflicting\r
+ settings in connectionAttributes and properties of the DataSource will lead to\r
+ unexpected behaviour. \r
+ <BR>Please see the Derby documentation for a complete list of connection attributes. </LI>\r
+\r
+ <LI><B><code>createDatabase</code></B> (String): <I>Optional</I>\r
+ <BR>If set to the string "create", this will\r
+ cause a new database of <code>databaseName</code> if that database does not already\r
+ exist. The database is created when a connection object is obtained from\r
+ the data source. </LI> \r
+\r
+ <LI><B><code>shutdownDatabase</code></B> (String): <I>Optional</I>\r
+ <BR>If set to the string "shutdown",\r
+ this will cause the database to shutdown when a java.sql.Connection object\r
+ is obtained from the data source. E.g., If the data source is an\r
+ XADataSource, a getXAConnection().getConnection() is necessary to cause the\r
+ database to shutdown.\r
+\r
+ </UL>\r
+\r
+ <P><B>Examples.</B>\r
+\r
+ <P>This is an example of setting a property directly using Derby's\r
+ EmbeddedDataSource object. This code is typically written by a system integrator :\r
+ <PRE> \r
+ *\r
+ * import org.apache.derby.jdbc.*;\r
+ *\r
+ * // dbname is the database name\r
+ * // if create is true, create the database if necessary\r
+ * javax.sql.DataSource makeDataSource (String dbname, boolean create)\r
+ * throws Throwable \r
+ * { \r
+ * EmbeddedDataSource ds = new EmbeddedDataSource(); \r
+ * ds.setDatabaseName(dbname);\r
+ *\r
+ * if (create)\r
+ * ds.setCreateDatabase("create");\r
+ * \r
+ * return ds;\r
+ * }\r
+ </PRE>\r
+\r
+ <P>Example of setting properties thru reflection. This code is typically\r
+ generated by tools or written by a system integrator: <PRE>\r
+ * \r
+ * javax.sql.DataSource makeDataSource(String dbname) \r
+ * throws Throwable \r
+ * {\r
+ * Class[] parameter = new Class[1];\r
+ * parameter[0] = dbname.getClass();\r
+ * DataSource ds = new EmbeddedDataSource();\r
+ * Class cl = ds.getClass();\r
+ *\r
+ * Method setName = cl.getMethod("setDatabaseName", parameter);\r
+ * Object[] arg = new Object[1];\r
+ * arg[0] = dbname;\r
+ * setName.invoke(ds, arg);\r
+ *\r
+ * return ds;\r
+ * }\r
+ </PRE>\r
+\r
+ <P>Example on how to register a data source object with a JNDI naming\r
+ service.\r
+ <PRE>\r
+ * DataSource ds = makeDataSource("mydb");\r
+ * Context ctx = new InitialContext();\r
+ * ctx.bind("jdbc/MyDB", ds);\r
+ </PRE>\r
+\r
+ <P>Example on how to retrieve a data source object from a JNDI naming\r
+ service. \r
+ <PRE>\r
+ * Context ctx = new InitialContext();\r
+ * DataSource ds = (DataSource)ctx.lookup("jdbc/MyDB");\r
+ </PRE>\r
+\r
+*/\r
+public class EmbeddedDataSource extends ReferenceableDataSource implements\r
+ javax.sql.DataSource\r
+{\r
+\r
+ private static final long serialVersionUID = -4945135214995641181L;\r
+\r
+ /** instance variables that will be serialized */\r
+\r
+ /**\r
+ * The database name.\r
+ * @serial\r
+ */\r
+ private String databaseName;\r
+\r
+ /**\r
+ * The data source name.\r
+ * @serial\r
+ */\r
+ private String dataSourceName;\r
+\r
+ /**\r
+ * Description of the database.\r
+ * @serial\r
+ */\r
+ private String description;\r
+\r
+ /**\r
+ * Set to "create" if the database should be created.\r
+ * @serial\r
+ */\r
+ private String createDatabase;\r
+\r
+ /**\r
+ * Set to "shutdown" if the database should be shutdown.\r
+ * @serial\r
+ */\r
+ private String shutdownDatabase;\r
+\r
+ /**\r
+ * Derby specific connection attributes.\r
+ * @serial\r
+ */\r
+ private String connectionAttributes;\r
+\r
+ /**\r
+ Set password to be a set of connection attributes.\r
+ */\r
+ private boolean attributesAsPassword;\r
+\r
+ /** instance variables that will not be serialized */\r
+ transient private PrintWriter printer;\r
+ transient private int loginTimeout;\r
+\r
+ // Unlike a DataSource, LocalDriver is shared by all\r
+ // Derby databases in the same jvm.\r
+ transient InternalDriver driver;\r
+\r
+ transient private String jdbcurl;\r
+\r
+ /**\r
+ No-arg constructor.\r
+ */\r
+ public EmbeddedDataSource() {\r
+ // needed by Object Factory\r
+\r
+ // don't put anything in here or in any of the set method because this\r
+ // object may be materialized in a remote machine and then sent thru\r
+ // the net to the machine where it will be used.\r
+ }\r
+\r
+\r
+ //Most of our customers would be using jndi to get the data\r
+ //sources. Since we don't have a jndi to test this, we are\r
+ //adding this method to fake it. This is getting used in\r
+ //xaJNDI test so we can compare the 2 data sources.\r
+ public boolean equals(Object p0) {\r
+ if (p0 instanceof EmbeddedDataSource) {\r
+ EmbeddedDataSource ds = (EmbeddedDataSource)p0;\r
+\r
+ boolean match = true;\r
+ \r
+ if (databaseName != null) {\r
+ if (!(databaseName.equals(ds.databaseName)))\r
+ match = false;\r
+ } else if (ds.databaseName != null)\r
+ match = false;\r
+\r
+ if (dataSourceName != null) {\r
+ if (!(dataSourceName.equals(ds.dataSourceName)))\r
+ match = false;\r
+ } else if (ds.dataSourceName != null)\r
+ match = false;\r
+\r
+ if (description != null) {\r
+ if (!(description.equals(ds.description)))\r
+ match = false;\r
+ } else if (ds.description != null)\r
+ match = false;\r
+\r
+ if (createDatabase != null) {\r
+ if (!(createDatabase.equals(ds.createDatabase)))\r
+ match = false;\r
+ } else if (ds.createDatabase != null)\r
+ match = false;\r
+\r
+ if (shutdownDatabase != null) {\r
+ if (!(shutdownDatabase.equals(ds.shutdownDatabase)))\r
+ match = false;\r
+ } else if (ds.shutdownDatabase != null)\r
+ match = false;\r
+\r
+ if (connectionAttributes != null) {\r
+ if (!(connectionAttributes.equals(ds.connectionAttributes)))\r
+ match = false;\r
+ } else if (ds.connectionAttributes != null)\r
+ match = false;\r
+\r
+ if (loginTimeout != ds.loginTimeout)\r
+ match = false;\r
+\r
+ return match;\r
+\r
+ }\r
+\r
+ return false;\r
+ }\r
+\r
+ /*\r
+ * Properties to be seen by Bean - access thru reflection.\r
+ */\r
+\r
+ /**\r
+ Set this property to create a new database. If this\r
+ property is not set, the database (identified by databaseName) is\r
+ assumed to be already existing.\r
+\r
+ @param create if set to the string "create", this data source will try\r
+ to create a new database of databaseName, or boot the database if one\r
+ by that name already exists.\r
+ */\r
+ public final void setCreateDatabase(String create) {\r
+ if (create != null && create.toLowerCase(java.util.Locale.ENGLISH).equals("create"))\r
+ createDatabase = create;\r
+ else\r
+ createDatabase = null;\r
+ }\r
+ /** @return "create" if create is set, or null if not */\r
+ public final String getCreateDatabase() {\r
+ return createDatabase;\r
+ }\r
+\r
+\r
+ /**\r
+ Set this property if one wishes to shutdown the database identified by\r
+ databaseName. \r
+\r
+ @param shutdown if set to the string "shutdown", this data source will \r
+ shutdown the database if it is running.\r
+ */\r
+ public final void setShutdownDatabase(String shutdown) {\r
+ if (shutdown != null && shutdown.equalsIgnoreCase("shutdown"))\r
+ shutdownDatabase = shutdown;\r
+ else\r
+ shutdownDatabase = null;\r
+ }\r
+ /** @return "shutdown" if shutdown is set, or null if not */\r
+ public final String getShutdownDatabase() {\r
+ return shutdownDatabase;\r
+ }\r
+\r
+ /**\r
+ Set this property to pass in more Derby specific\r
+ connection URL attributes.\r
+ <BR>\r
+ Any attributes that can be set using a property of this DataSource implementation\r
+ (e.g user, password) should not be set in connectionAttributes. Conflicting\r
+ settings in connectionAttributes and properties of the DataSource will lead to\r
+ unexpected behaviour. \r
+\r
+ @param prop set to the list of Derby connection\r
+ attributes separated by semi-colons. E.g., to specify an encryption\r
+ bootPassword of "x8hhk2adf", and set upgrade to true, do the following: \r
+ <PRE>\r
+ ds.setConnectionAttributes("bootPassword=x8hhk2adf;upgrade=true");\r
+ </PRE>\r
+ See the Derby documentation for complete list.\r
+ */\r
+ public final void setConnectionAttributes(String prop) {\r
+ connectionAttributes = prop;\r
+ update();\r
+ }\r
+ /** @return Derby specific connection URL attributes */\r
+ public final String getConnectionAttributes() {\r
+ return connectionAttributes;\r
+ }\r
+\r
+\r
+ /**\r
+ Set attributeAsPassword property to enable passing connection request attributes in the password argument of getConnection.\r
+ If the property is set to true then the password argument of the DataSource.getConnection(String user, String password)\r
+ method call is taken to be a list of connection attributes with the same format as the connectionAttributes property.\r
+\r
+ @param attributesAsPassword true to encode password argument as a set of connection attributes in a connection request.\r
+ */\r
+ public final void setAttributesAsPassword(boolean attributesAsPassword) {\r
+ this.attributesAsPassword = attributesAsPassword;\r
+ update();\r
+ }\r
+\r
+ /**\r
+ Return the value of the attributesAsPassword property.\r
+ */\r
+ public final boolean getAttributesAsPassword() {\r
+ return attributesAsPassword;\r
+ }\r
+\r
+ /*\r
+ * DataSource methods \r
+ */\r
+\r
+\r
+ /**\r
+ * Attempt to establish a database connection.\r
+ *\r
+ * @return a Connection to the database\r
+ * @exception SQLException if a database-access error occurs.\r
+ */\r
+ public final Connection getConnection() throws SQLException\r
+ {\r
+ return this.getConnection(getUser(), getPassword(), false);\r
+ }\r
+\r
+ /**\r
+ * Attempt to establish a database connection with the given username and password.\r
+ If the attributeAsPassword property is set to true then the password argument is taken to be a list of\r
+ connection attributes with the same format as the connectionAttributes property.\r
+\r
+ *\r
+ * @param username the database user on whose behalf the Connection is \r
+ * being made\r
+ * @param password the user's password\r
+ * @return a Connection to the database\r
+ * @exception SQLException if a database-access error occurs.\r
+ */\r
+ public final Connection getConnection(String username, String password) \r
+ throws SQLException\r
+ {\r
+ return this.getConnection(username, password, true);\r
+ }\r
+\r
+ /**\r
+ @param requestPassword true if the password came from the getConnection() call.\r
+ */\r
+ final Connection getConnection(String username, String password, boolean requestPassword)\r
+ throws SQLException {\r
+\r
+ Properties info = new Properties();\r
+ if (username != null)\r
+ info.put(Attribute.USERNAME_ATTR, username);\r
+\r
+ if (!requestPassword || !attributesAsPassword)\r
+ {\r
+ if (password != null)\r
+ info.put(Attribute.PASSWORD_ATTR, password);\r
+ }\r
+\r
+ if (createDatabase != null)\r
+ info.put(Attribute.CREATE_ATTR, "true");\r
+ if (shutdownDatabase != null)\r
+ info.put(Attribute.SHUTDOWN_ATTR, "true");\r
+\r
+ String url = jdbcurl;\r
+\r
+ if (attributesAsPassword && requestPassword && password != null) {\r
+\r
+\r
+ StringBuffer sb = new StringBuffer(url.length() + password.length() + 1);\r
+\r
+ sb.append(url);\r
+ sb.append(';');\r
+ sb.append(password); // these are now request attributes on the URL\r
+\r
+ url = sb.toString();\r
+\r
+ }\r
+ Connection conn = findDriver().connect(url, info);\r
+\r
+ // JDBC driver's getConnection method returns null if\r
+ // the driver does not handle the request's URL.\r
+ if (conn == null)\r
+ throw Util.generateCsSQLException(SQLState.PROPERTY_INVALID_VALUE,Attribute.DBNAME_ATTR,getDatabaseName());\r
+\r
+ return conn;\r
+ }\r
+ \r
+ InternalDriver findDriver() throws SQLException\r
+ {\r
+ String url = jdbcurl;\r
+\r
+ if (driver == null || !driver.acceptsURL(url))\r
+ {\r
+ synchronized(this)\r
+ {\r
+ // The driver has either never been booted, or it has been\r
+ // shutdown by a 'jdbc:derby:;shutdown=true'\r
+ if (driver == null || !driver.acceptsURL(url))\r
+ {\r
+\r
+ new org.apache.derby.jdbc.EmbeddedDriver();\r
+\r
+ // If we know the driver, we loaded it. Otherwise only\r
+ // work if DriverManager has already loaded it.\r
+\r
+ AutoloadedDriver autoloadedDriver =\r
+ (AutoloadedDriver) DriverManager.getDriver(url);\r
+ driver = (InternalDriver) autoloadedDriver.getDriverModule();\r
+ // DriverManager will throw an exception if it cannot find the driver\r
+ }\r
+ }\r
+ }\r
+ return driver;\r
+ // else driver != null and driver can accept url\r
+ }\r
+\r
+ void update()\r
+ {\r
+ StringBuffer sb = new StringBuffer(64);\r
+\r
+ sb.append(Attribute.PROTOCOL);\r
+\r
+\r
+ // Set the database name from the databaseName property\r
+ String dbName = getDatabaseName();\r
+\r
+ if (dbName != null) {\r
+ dbName = dbName.trim();\r
+ }\r
+\r
+ if (dbName == null || dbName.length() == 0) {\r
+ // need to put something in so that we do not allow the\r
+ // database name to be set from the request or from the\r
+ // connection attributes.\r
+\r
+ // this space will selected as the database name (and trimmed to an empty string)\r
+ // See the getDatabaseName() code in InternalDriver. Since this is a non-null\r
+ // value, it will be selected over any databaseName connection attribute.\r
+ dbName = " ";\r
+ }\r
+\r
+ sb.append(dbName);\r
+\r
+\r
+ String connAttrs = getConnectionAttributes();\r
+ if (connAttrs != null) {\r
+ connAttrs = connAttrs.trim();\r
+ if (connAttrs.length() != 0) {\r
+ sb.append(';');\r
+ sb.append(connectionAttributes);\r
+ }\r
+ }\r
+\r
+ jdbcurl = sb.toString();\r
+ }\r
+\r
+\r
+ public boolean isWrapperFor(Class<?> iface) throws SQLException {\r
+ // TODO Auto-generated method stub\r
+ return false;\r
+ }\r
+\r
+\r
+ public <T> T unwrap(Class<T> iface) throws SQLException {\r
+ // TODO Auto-generated method stub\r
+ return null;\r
+ }\r
+\r
+\r
+ public Logger getParentLogger() throws SQLFeatureNotSupportedException {\r
+ // TODO Auto-generated method stub\r
+ return null;\r
+ }\r
+\r
+\r
+}\r