1 | /* |
2 | |
3 | Derby - Class org.apache.derby.jdbc.EmbeddedSimpleDataSource |
4 | |
5 | Copyright 2001, 2005 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.jdbc; |
22 | |
23 | import org.apache.derby.iapi.jdbc.JDBCBoot; |
24 | import org.apache.derby.iapi.reference.Attribute; |
25 | import org.apache.derby.iapi.reference.MessageId; |
26 | |
27 | import java.sql.Connection; |
28 | import java.sql.SQLException; |
29 | |
30 | import java.io.PrintWriter; |
31 | import java.util.Properties; |
32 | |
33 | /* -- New jdbc 20 extension types --- */ |
34 | import javax.sql.DataSource; |
35 | |
36 | import org.apache.derby.iapi.reference.SQLState; |
37 | import org.apache.derby.iapi.services.i18n.MessageService; |
38 | import org.apache.derby.impl.jdbc.Util; |
39 | |
40 | /** |
41 | * |
42 | * |
43 | * EmbeddedSimpleDataSource is Derby's DataSource implementation |
44 | * for J2ME/CDC/Foundation. It is also supports J2SE platforms. |
45 | * |
46 | * |
47 | * Supports the same properties as EmbeddedDataSource, see that class for details. |
48 | * <P> |
49 | EmbeddedSimpleDataSource automatically supports the correct JDBC specification version |
50 | for the Java Virtual Machine's environment. |
51 | <UL> |
52 | <LI> JDBC Optional Package for CDC/Foundation Profile(JSR-169) - J2ME - CDC/Foundation |
53 | <LI> JDBC 3.0 - Java 2 - JDK 1.4, J2SE 5.0 |
54 | <LI> JDBC 2.0 - Java 2 - JDK 1.2,1.3 |
55 | </UL> |
56 | * @see EmbeddedDataSource |
57 | * |
58 | */ |
59 | public final class EmbeddedSimpleDataSource implements DataSource { |
60 | |
61 | private String password; |
62 | |
63 | private String user; |
64 | |
65 | /** |
66 | * The database name. |
67 | * |
68 | * @serial |
69 | */ |
70 | private String databaseName; |
71 | |
72 | /** |
73 | * The data source name. |
74 | * |
75 | * @serial |
76 | */ |
77 | private String dataSourceName; |
78 | |
79 | /** |
80 | * Description of the database. |
81 | * |
82 | * @serial |
83 | */ |
84 | private String description; |
85 | |
86 | /** |
87 | * Set to "create" if the database should be created. |
88 | * |
89 | * @serial |
90 | */ |
91 | private String createDatabase; |
92 | |
93 | /** |
94 | * Set to "shutdown" if the database should be shutdown. |
95 | * |
96 | * @serial |
97 | */ |
98 | private String shutdownDatabase; |
99 | |
100 | /** |
101 | * Cloudscape specific connection attributes. |
102 | * |
103 | * @serial |
104 | */ |
105 | private String connectionAttributes; |
106 | |
107 | /** instance variables that will not be serialized */ |
108 | transient private PrintWriter printer; |
109 | |
110 | transient private int loginTimeout; |
111 | |
112 | // Unlike a DataSource, LocalDriver is shared by all |
113 | // Cloudscape databases in the same jvm. |
114 | transient private InternalDriver driver; |
115 | |
116 | transient private String jdbcurl; |
117 | |
118 | /** |
119 | * No-arg constructor. |
120 | */ |
121 | public EmbeddedSimpleDataSource() { |
122 | } |
123 | |
124 | /* |
125 | * DataSource methods |
126 | */ |
127 | |
128 | /** |
129 | * Gets the maximum time in seconds that this data source can wait while |
130 | * attempting to connect to a database. A value of zero means that the |
131 | * timeout is the default system timeout if there is one; otherwise it means |
132 | * that there is no timeout. When a data source object is created, the login |
133 | * timeout is initially zero. |
134 | * |
135 | * @return the data source login time limit |
136 | * @exception SQLException |
137 | * if a database access error occurs. |
138 | */ |
139 | public int getLoginTimeout() throws SQLException { |
140 | return loginTimeout; |
141 | } |
142 | |
143 | /** |
144 | * Sets the maximum time in seconds that this data source will wait while |
145 | * attempting to connect to a database. A value of zero specifies that the |
146 | * timeout is the default system timeout if there is one; otherwise it |
147 | * specifies that there is no timeout. When a data source object is created, |
148 | * the login timeout is initially zero. |
149 | * <P> |
150 | * Cloudscape ignores this property. |
151 | * |
152 | * @param seconds |
153 | * the data source login time limit |
154 | * @exception SQLException |
155 | * if a database access error occurs. |
156 | */ |
157 | public void setLoginTimeout(int seconds) throws SQLException { |
158 | loginTimeout = seconds; |
159 | } |
160 | |
161 | /** |
162 | * Get the log writer for this data source. |
163 | * |
164 | * <p> |
165 | * The log writer is a character output stream to which all logging and |
166 | * tracing messages for this data source object instance will be printed. |
167 | * This includes messages printed by the methods of this object, messages |
168 | * printed by methods of other objects manufactured by this object, and so |
169 | * on. Messages printed to a data source specific log writer are not printed |
170 | * to the log writer associated with the java.sql.Drivermanager class. When |
171 | * a data source object is created the log writer is initially null, in |
172 | * other words, logging is disabled. |
173 | * |
174 | * @return the log writer for this data source, null if disabled |
175 | * @exception SQLException |
176 | * if a database-access error occurs. |
177 | */ |
178 | public PrintWriter getLogWriter() throws SQLException { |
179 | return printer; |
180 | } |
181 | |
182 | /** |
183 | * Set the log writer for this data source. |
184 | * |
185 | * <p> |
186 | * The log writer is a character output stream to which all logging and |
187 | * tracing messages for this data source object instance will be printed. |
188 | * This includes messages printed by the methods of this object, messages |
189 | * printed by methods of other objects manufactured by this object, and so |
190 | * on. Messages printed to a data source specific log writer are not printed |
191 | * to the log writer associated with the java.sql.Drivermanager class. When |
192 | * a data source object is created the log writer is initially null, in |
193 | * other words, logging is disabled. |
194 | * |
195 | * @param out |
196 | * the new log writer; to disable, set to null |
197 | * @exception SQLException |
198 | * if a database-access error occurs. |
199 | */ |
200 | public void setLogWriter(PrintWriter out) throws SQLException { |
201 | printer = out; |
202 | } |
203 | |
204 | /* |
205 | * Properties to be seen by Bean - access thru reflection. |
206 | */ |
207 | /** |
208 | * Set the database name. Setting this property is mandatory. If a database |
209 | * named wombat at g:/db needs to be accessed, database name should be set |
210 | * to "g:/db/wombat". The database will be booted if it is not already |
211 | * running in the system. |
212 | * |
213 | * @param databaseName |
214 | * the name of the database |
215 | */ |
216 | public final synchronized void setDatabaseName(String databaseName) { |
217 | this.databaseName = databaseName; |
218 | update(); |
219 | } |
220 | |
221 | public String getDatabaseName() { |
222 | return databaseName; |
223 | } |
224 | |
225 | /** |
226 | * Set the data source name. The property is not mandatory. It is used for |
227 | * informational purposes only. |
228 | * |
229 | * @param dsn |
230 | * the name of the data source |
231 | */ |
232 | public final void setDataSourceName(String dsn) { |
233 | dataSourceName = dsn; |
234 | } |
235 | |
236 | /** @return data source name */ |
237 | public final String getDataSourceName() { |
238 | return dataSourceName; |
239 | } |
240 | |
241 | /** |
242 | * Set the data source descripton. This property is not mandatory. It is |
243 | * used for informational purposes only. |
244 | * |
245 | * @param desc |
246 | * the description of the data source |
247 | */ |
248 | public final void setDescription(String desc) { |
249 | description = desc; |
250 | } |
251 | |
252 | /** @return description */ |
253 | public final String getDescription() { |
254 | return description; |
255 | } |
256 | |
257 | /** |
258 | * Set the <code>user</code> property for the data source. This is user |
259 | * name for any data source getConnection() call that takes no arguments. |
260 | */ |
261 | public final void setUser(String user) { |
262 | this.user = user; |
263 | } |
264 | |
265 | /** @return user */ |
266 | public final String getUser() { |
267 | return user; |
268 | } |
269 | |
270 | /** |
271 | * Set the <code>password</code> property for the data source. This is |
272 | * user's password for any data source getConnection() call that takes no |
273 | * arguments. |
274 | */ |
275 | public final void setPassword(String password) { |
276 | this.password = password; |
277 | } |
278 | |
279 | /** @return password */ |
280 | public final String getPassword() { |
281 | return password; |
282 | } |
283 | |
284 | /** |
285 | * Set this property to create a new database. If this property is not set, |
286 | * the database (identified by databaseName) is assumed to be already |
287 | * existing. |
288 | * |
289 | * @param create |
290 | * if set to the string "create", this data source will try to |
291 | * create a new database of databaseName, or boot the database if |
292 | * one by that name already exists. |
293 | */ |
294 | public final void setCreateDatabase(String create) { |
295 | if (create != null |
296 | && create.toLowerCase(java.util.Locale.ENGLISH) |
297 | .equals("create")) |
298 | createDatabase = create; |
299 | else |
300 | createDatabase = null; |
301 | } |
302 | |
303 | /** @return "create" if create is set, or null if not */ |
304 | public final String getCreateDatabase() { |
305 | return createDatabase; |
306 | } |
307 | |
308 | /** |
309 | * Set this property if one wishes to shutdown the database identified by |
310 | * databaseName. |
311 | * |
312 | * @param shutdown |
313 | * if set to the string "shutdown", this data source will |
314 | * shutdown the database if it is running. |
315 | */ |
316 | public final void setShutdownDatabase(String shutdown) { |
317 | if (shutdown != null && shutdown.equalsIgnoreCase("shutdown")) |
318 | shutdownDatabase = shutdown; |
319 | else |
320 | shutdownDatabase = null; |
321 | } |
322 | |
323 | /** @return "shutdown" if shutdown is set, or null if not */ |
324 | public final String getShutdownDatabase() { |
325 | return shutdownDatabase; |
326 | } |
327 | |
328 | /** |
329 | * Set this property to pass in more Cloudscape specific connection URL |
330 | * attributes. |
331 | <BR> |
332 | Any attributes that can be set using a property of this DataSource implementation |
333 | (e.g user, password) should not be set in connectionAttributes. Conflicting |
334 | settings in connectionAttributes and properties of the DataSource will lead to |
335 | unexpected behaviour. |
336 | * |
337 | * @param prop |
338 | * set to the list of Cloudscape connection attributes separated |
339 | * by semi-colons. E.g., to specify an encryption bootPassword of |
340 | * "x8hhk2adf", and set upgrade to true, do the following: |
341 | * |
342 | * <PRE> |
343 | * |
344 | * ds.setConnectionAttributes("bootPassword=x8hhk2adf;upgrade=true"); |
345 | * |
346 | * </PRE> |
347 | * |
348 | * See Cloudscape's documentation for complete list. |
349 | */ |
350 | public final void setConnectionAttributes(String prop) { |
351 | connectionAttributes = prop; |
352 | update(); |
353 | } |
354 | |
355 | /** @return Cloudscape specific connection URL attributes */ |
356 | public final String getConnectionAttributes() { |
357 | return connectionAttributes; |
358 | } |
359 | |
360 | /* |
361 | * DataSource methods |
362 | */ |
363 | |
364 | /** |
365 | * Attempt to establish a database connection. |
366 | * |
367 | * @return a Connection to the database |
368 | * @exception SQLException |
369 | * if a database-access error occurs. |
370 | */ |
371 | public final Connection getConnection() throws SQLException { |
372 | return this.getConnection(getUser(), getPassword()); |
373 | } |
374 | |
375 | /** |
376 | * Attempt to establish a database connection with the given username and |
377 | * password. If the attributeAsPassword property is set to true then the |
378 | * password argument is taken to be a list of connection attributes with the |
379 | * same format as the connectionAttributes property. |
380 | * |
381 | * |
382 | * @param username |
383 | * the database user on whose behalf the Connection is being made |
384 | * @param password |
385 | * the user's password |
386 | * @return a Connection to the database |
387 | * @exception SQLException |
388 | * if a database-access error occurs. |
389 | */ |
390 | public final Connection getConnection(String username, String password) |
391 | throws SQLException { |
392 | |
393 | Properties info = new Properties(); |
394 | if (username != null) |
395 | info.put(Attribute.USERNAME_ATTR, username); |
396 | |
397 | if (password != null) |
398 | info.put(Attribute.PASSWORD_ATTR, password); |
399 | |
400 | if (createDatabase != null) |
401 | info.put(Attribute.CREATE_ATTR, "true"); |
402 | if (shutdownDatabase != null) |
403 | info.put(Attribute.SHUTDOWN_ATTR, "true"); |
404 | |
405 | Connection conn = findDriver().connect(jdbcurl, info); |
406 | |
407 | // JDBC driver's getConnection method returns null if |
408 | // the driver does not handle the request's URL. |
409 | if (conn == null) |
410 | throw Util.generateCsSQLException(SQLState.PROPERTY_INVALID_VALUE, |
411 | Attribute.DBNAME_ATTR, getDatabaseName()); |
412 | |
413 | return conn; |
414 | } |
415 | |
416 | private InternalDriver findDriver() throws SQLException { |
417 | String url = jdbcurl; |
418 | |
419 | if (driver == null || !driver.acceptsURL(url)) { |
420 | synchronized (this) { |
421 | // The driver has either never been booted, or it has been |
422 | // shutdown by a 'jdbc:derby:;shutdown=true' |
423 | if (driver == null || !driver.acceptsURL(url)) { |
424 | |
425 | |
426 | new JDBCBoot().boot(Attribute.PROTOCOL, System.err); |
427 | |
428 | // If we know the driver, we loaded it. Otherwise only |
429 | // work if DriverManager has already loaded it. |
430 | |
431 | driver = InternalDriver.activeDriver(); |
432 | |
433 | if (driver == null) |
434 | throw new SQLException(MessageService.getTextMessage(MessageId.CORE_JDBC_DRIVER_UNREGISTERED)); |
435 | } |
436 | } |
437 | } |
438 | return driver; |
439 | // else driver != null and driver can accept url |
440 | } |
441 | |
442 | private void update() { |
443 | StringBuffer sb = new StringBuffer(64); |
444 | |
445 | sb.append(Attribute.PROTOCOL); |
446 | |
447 | // Set the database name from the databaseName property |
448 | String dbName = getDatabaseName(); |
449 | |
450 | if (dbName != null) { |
451 | dbName = dbName.trim(); |
452 | } |
453 | |
454 | if (dbName == null || dbName.length() == 0) { |
455 | // need to put something in so that we do not allow the |
456 | // database name to be set from the request or from the |
457 | // connection attributes. |
458 | |
459 | // this space will selected as the database name (and trimmed to an |
460 | // empty string) |
461 | // See the getDatabaseName() code in InternalDriver. Since this is a |
462 | // non-null |
463 | // value, it will be selected over any databaseName connection |
464 | // attribute. |
465 | dbName = " "; |
466 | } |
467 | |
468 | sb.append(dbName); |
469 | |
470 | String connAttrs = getConnectionAttributes(); |
471 | if (connAttrs != null) { |
472 | connAttrs = connAttrs.trim(); |
473 | if (connAttrs.length() != 0) { |
474 | sb.append(';'); |
475 | sb.append(connectionAttributes); |
476 | } |
477 | } |
478 | |
479 | jdbcurl = sb.toString(); |
480 | } |
481 | } |
482 | |