1 | /* |
2 | |
3 | Derby - Class org.apache.derby.jdbc.ClientBaseDataSource |
4 | |
5 | Copyright (c) 2001, 2005 The Apache Software Foundation or its licensors, where 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 java.io.Serializable; |
24 | import java.io.PrintWriter; |
25 | import java.util.Enumeration; |
26 | import java.util.Properties; |
27 | import java.util.StringTokenizer; |
28 | import java.util.NoSuchElementException; |
29 | import java.lang.reflect.Field; |
30 | import java.lang.reflect.InvocationTargetException; |
31 | import java.lang.reflect.Method; |
32 | import java.lang.reflect.Modifier; |
33 | |
34 | import javax.naming.RefAddr; |
35 | import javax.naming.Referenceable; |
36 | import javax.naming.Reference; |
37 | import javax.naming.NamingException; |
38 | import javax.naming.StringRefAddr; |
39 | |
40 | import org.apache.derby.client.am.Configuration; |
41 | import org.apache.derby.client.am.LogWriter; |
42 | import org.apache.derby.client.am.SqlException; |
43 | import org.apache.derby.client.am.Connection; |
44 | import org.apache.derby.client.am.ClientMessageId; |
45 | import org.apache.derby.client.net.NetConfiguration; |
46 | import org.apache.derby.client.net.NetLogWriter; |
47 | import org.apache.derby.client.ClientDataSourceFactory; |
48 | import org.apache.derby.shared.common.reference.Attribute; |
49 | import org.apache.derby.shared.common.reference.SQLState; |
50 | |
51 | /** |
52 | * Base class for client-side DataSource implementations. |
53 | */ |
54 | public abstract class ClientBaseDataSource implements Serializable, Referenceable { |
55 | private static final long serialVersionUID = -7660172643035173692L; |
56 | |
57 | // Spec requires DH algorithm with 32bytes prime to be used |
58 | // Not all JCE implementations have support for this. E.g. |
59 | // Sun JCE does not support DH(prime of 32bytes). |
60 | // store information if client JVM has JCE loaded that |
61 | // can support the necessary algorithms required for EUSRIDPWD |
62 | // (encrypted userid and password) security mechanism |
63 | // this information is needed to decide if security mechanism |
64 | // can be upgraded to EUSRIDPWD or not |
65 | // See getUpgradedSecurityMechanism() |
66 | static boolean SUPPORTS_EUSRIDPWD = false; |
67 | |
68 | static |
69 | { |
70 | try |
71 | { |
72 | // The EncryptionManager class will instantiate objects of the required |
73 | // security algorithms that are needed for EUSRIDPWD |
74 | // An exception will be thrown if support is not available |
75 | // in the JCE implementation in the JVM in which the client |
76 | // is loaded. |
77 | new org.apache.derby.client.am.EncryptionManager(null); |
78 | SUPPORTS_EUSRIDPWD = true; |
79 | }catch(Exception e) |
80 | { |
81 | // if an exception is thrown, ignore exception. |
82 | // set SUPPORTS_EUSRIDPWD to false indicating that the client |
83 | // does not support EUSRIDPWD security mechanism |
84 | SUPPORTS_EUSRIDPWD = false; |
85 | } |
86 | } |
87 | |
88 | // The loginTimeout jdbc 2 data source property is not supported as a jdbc 1 connection property, |
89 | // because loginTimeout is set by the jdbc 1 api via java.sql.DriverManager.setLoginTimeout(). |
90 | // The databaseName, serverName, and portNumber data source properties are also not supported as connection properties |
91 | // because they are extracted from the jdbc 1 database url passed on the connection request. |
92 | // However, all other data source properties should probably also be supported as connection properties. |
93 | |
94 | //---------------------contructors/finalizers--------------------------------- |
95 | |
96 | // This class is abstract, hide the default constructor |
97 | ClientBaseDataSource() { |
98 | } |
99 | |
100 | // ---------------------------- loginTimeout ----------------------------------- |
101 | // |
102 | // was serialized in 1.0 release |
103 | /** |
104 | * The time in seconds to wait for a connection request on this data source. The default value of zero indicates |
105 | * that either the system time out be used or no timeout limit. |
106 | * |
107 | * @serial |
108 | */ |
109 | private int loginTimeout; |
110 | |
111 | public synchronized void setLoginTimeout(int seconds) { |
112 | this.loginTimeout = seconds; |
113 | } |
114 | |
115 | public int getLoginTimeout() { |
116 | return this.loginTimeout; |
117 | } |
118 | |
119 | // ---------------------------- logWriter ----------------------------------- |
120 | // |
121 | /** |
122 | * The log writer is declared transient, and is not serialized or stored under JNDI. |
123 | * |
124 | * @see #traceLevel |
125 | */ |
126 | private transient PrintWriter logWriter; |
127 | |
128 | public synchronized void setLogWriter(PrintWriter logWriter) { |
129 | this.logWriter = logWriter; |
130 | } |
131 | |
132 | public PrintWriter getLogWriter() { |
133 | return this.logWriter; |
134 | } |
135 | |
136 | // ---------------------------- databaseName ----------------------------------- |
137 | // |
138 | // Stores the relational database name, RDBNAME. |
139 | // The length of the database name may be limited to 18 bytes |
140 | // and therefore may throw an SQLException. |
141 | // |
142 | // |
143 | private String databaseName; |
144 | |
145 | // databaseName is not permitted in a properties object |
146 | |
147 | |
148 | // ---------------------------- description ------------------------------ |
149 | // A description of this data source. |
150 | private String description; |
151 | |
152 | // ---------------------------- dataSourceName ----------------------------------- |
153 | // |
154 | // A data source name; |
155 | // used to name an underlying XADataSource, |
156 | // or ConnectionPoolDataSource when pooling of connections is done. |
157 | // |
158 | private String dataSourceName; |
159 | |
160 | // ---------------------------- portNumber ----------------------------------- |
161 | // |
162 | private int portNumber = propertyDefault_portNumber; |
163 | public final static int propertyDefault_portNumber = 1527; |
164 | |
165 | // ---------------------------- serverName ----------------------------------- |
166 | // |
167 | // Derby-410 fix. |
168 | private String serverName = propertyDefault_serverName; |
169 | public final static String propertyDefault_serverName = "localhost"; |
170 | |
171 | // serverName is not permitted in a properties object |
172 | |
173 | // ---------------------------- user ----------------------------------- |
174 | // |
175 | // This property can be overwritten by specifing the |
176 | // username parameter on the DataSource.getConnection() method |
177 | // call. If user is specified, then password must also be |
178 | // specified, either in the data source object or provided on |
179 | // the DataSource.getConnection() call. |
180 | // |
181 | // Each data source implementation subclass will maintain it's own <code>password</code> property. |
182 | // This password property may or may not be declared transient, and therefore may be serialized |
183 | // to a file in clear-text, care must taken by the user to prevent security breaches. |
184 | // Derby-406 fix |
185 | private String user = propertyDefault_user; |
186 | public final static String propertyDefault_user = "APP"; |
187 | |
188 | public static String getUser(Properties properties) { |
189 | String userString = properties.getProperty(Attribute.USERNAME_ATTR); |
190 | return parseString(userString, propertyDefault_user); |
191 | } |
192 | |
193 | // ---------------------------- securityMechanism ----------------------------------- |
194 | // |
195 | // The source security mechanism to use when connecting to this data source. |
196 | // <p> |
197 | // Security mechanism options are: |
198 | // <ul> |
199 | // <li> USER_ONLY_SECURITY |
200 | // <li> CLEAR_TEXT_PASSWORD_SECURITY |
201 | // <li> ENCRYPTED_PASSWORD_SECURITY |
202 | // <li> ENCRYPTED_USER_AND_PASSWORD_SECURITY - both password and user are encrypted |
203 | // </ul> |
204 | // The default security mechanism is USER_ONLY_SECURITY. |
205 | // <p> |
206 | // If the application specifies a security |
207 | // mechanism then it will be the only one attempted. |
208 | // If the specified security mechanism is not supported by the conversation |
209 | // then an exception will be thrown and there will be no additional retries. |
210 | // <p> |
211 | // This property is currently only available for the DNC driver. |
212 | // <p> |
213 | // Both user and password need to be set for all security mechanism except USER_ONLY_SECURITY |
214 | // When using USER_ONLY_SECURITY, only the user property needs to be specified. |
215 | // |
216 | |
217 | // constant to indicate that the security mechanism has not been |
218 | // explicitly set, either on connection request when using DriverManager |
219 | // or on the Client DataSource object |
220 | private final static short SECMEC_HAS_NOT_EXPLICITLY_SET = 0; |
221 | |
222 | // Security Mechanism can be specified explicitly either when obtaining a |
223 | // connection via a DriverManager or via Datasource. |
224 | // Via DriverManager, securityMechanism can be set on the connection request using |
225 | // the 'securityMechanism' attribute. |
226 | // Via DataSource, securityMechanism can be set by calling setSecurityMechanism() |
227 | // on the ClientDataSource |
228 | // If the security mechanism is not explicitly set as mentioned above, in that case |
229 | // the Client will try to upgrade the security mechanism to a more secure one, if possible. |
230 | // @see #getUpgradedSecurityMechanism |
231 | // Therefore, need to keep track if the seurityMechanism has been explicitly set |
232 | protected short securityMechanism = SECMEC_HAS_NOT_EXPLICITLY_SET; |
233 | // TODO default should be USER_ONLY_SECURITY. Change when working on |
234 | // Network Server |
235 | // public final static short propertyDefault_securityMechanism = (short) |
236 | // org.apache.derby.client.net.NetConfiguration.SECMEC_USRIDONL; |
237 | public final static short propertyDefault_securityMechanism = (short) NetConfiguration.SECMEC_USRIDONL; |
238 | |
239 | |
240 | // We use the NET layer constants to avoid a mapping for the NET driver. |
241 | /** |
242 | * Return security mechanism if it is set, else upgrade the security mechanism |
243 | * if possible and return the upgraded security mechanism |
244 | * @param properties Look in the properties if securityMechanism is set or not |
245 | * if set, return this security mechanism |
246 | * @return security mechanism |
247 | */ |
248 | public static short getSecurityMechanism(Properties properties) { |
249 | short secmec; |
250 | String securityMechanismString = properties.getProperty(Attribute.CLIENT_SECURITY_MECHANISM); |
251 | if ( securityMechanismString != null ) |
252 | { |
253 | // security mechanism has been set, do not override, but instead return |
254 | // the security mechanism that has been set (DERBY-962) |
255 | secmec = Short.parseShort(securityMechanismString); |
256 | } |
257 | else |
258 | { |
259 | // if securityMechanismString is null, this means that |
260 | // security mechanism has not been set explicitly and not available in |
261 | // properties. Hence, do an upgrade of security mechanism if possible |
262 | // The logic for upgrade of security mechanism uses information about |
263 | // if password is available or not, so pass this information also. |
264 | String passwordString = properties.getProperty(Attribute.PASSWORD_ATTR); |
265 | secmec = getUpgradedSecurityMechanism(passwordString); |
266 | } |
267 | return secmec; |
268 | } |
269 | |
270 | /** |
271 | * This method has logic to upgrade security mechanism to a better (more secure) one |
272 | * if it is possible. Currently derby server only has support for USRIDPWD,USRIDONL, |
273 | * EUSRIDPWD and this method only considers these possibilities. |
274 | * USRIDPWD, EUSRIDPWD require a password, USRIDONL is the only security mechanism |
275 | * which does not require password. |
276 | * 1. if password is not available, then security mechanism possible is USRIDONL |
277 | * 2. if password is available, if client supports EUSRIDPWD, then EUSRIDPWD is |
278 | * returned |
279 | * 3. if password is available, if client does not support EUSRIDPWD, then USRIDPWD |
280 | * is returned. |
281 | * @param password password argument |
282 | * @return upgraded security mechanism if possible |
283 | */ |
284 | public static short getUpgradedSecurityMechanism(String password) { |
285 | // if password is null, in that case the only acceptable security |
286 | // mechanism is USRIDONL, which is the default security mechanism. |
287 | if ( password == null ) |
288 | return propertyDefault_securityMechanism; |
289 | |
290 | // if password is available, then a security mechanism is picked in following |
291 | // order if support is available |
292 | // 1. EUSRIDPWD |
293 | // 2. USRIDPWD |
294 | // when we have support for more security mechanisms on server |
295 | // and client, we should update this upgrade logic to pick |
296 | // secure security mechanisms before trying out the USRIDPWD |
297 | |
298 | if (SUPPORTS_EUSRIDPWD) |
299 | return (short)NetConfiguration.SECMEC_EUSRIDPWD; |
300 | else |
301 | return (short)NetConfiguration.SECMEC_USRIDPWD; |
302 | } |
303 | |
304 | // ---------------------------- getServerMessageTextOnGetMessage ----------------------------------- |
305 | // |
306 | private boolean retrieveMessageText = propertyDefault_retrieveMessageText; |
307 | public final static boolean propertyDefault_retrieveMessageText = true; |
308 | |
309 | public static boolean getRetrieveMessageText(Properties properties) { |
310 | String retrieveMessageTextString = properties.getProperty(Attribute.CLIENT_RETIEVE_MESSAGE_TEXT); |
311 | return parseBoolean(retrieveMessageTextString, propertyDefault_retrieveMessageText); |
312 | } |
313 | |
314 | // ---------------------------- traceFile ----------------------------------- |
315 | // |
316 | private String traceFile; |
317 | |
318 | public static String getTraceFile(Properties properties) { |
319 | return properties.getProperty(Attribute.CLIENT_TRACE_FILE); |
320 | } |
321 | |
322 | // ---------------------------- traceDirectory ----------------------------------- |
323 | // For the suffix of the trace file when traceDirectory is enabled. |
324 | private transient int traceFileSuffixIndex_ = 0; |
325 | // |
326 | private String traceDirectory; |
327 | |
328 | public static String getTraceDirectory(Properties properties) { |
329 | return properties.getProperty(Attribute.CLIENT_TRACE_DIRECTORY); |
330 | } |
331 | |
332 | // ---------------------------- traceFileAppend ----------------------------------- |
333 | // |
334 | private boolean traceFileAppend = propertyDefault_traceFileAppend; |
335 | public final static boolean propertyDefault_traceFileAppend = false; |
336 | |
337 | public static boolean getTraceFileAppend(Properties properties) { |
338 | String traceFileAppendString = properties.getProperty(Attribute.CLIENT_TRACE_APPEND); |
339 | return parseBoolean(traceFileAppendString, propertyDefault_traceFileAppend); |
340 | } |
341 | |
342 | // ---------------------------- password ----------------------------------- |
343 | // |
344 | // The password property is defined in subclasses, but the method |
345 | // getPassword (java.util.Properties properties) is in this class to eliminate |
346 | // dependencies on j2ee for connections that go thru the driver manager. |
347 | |
348 | public static String getPassword(Properties properties) { |
349 | return properties.getProperty("password"); |
350 | } |
351 | |
352 | private String password; |
353 | |
354 | synchronized public final void setPassword(String password) { |
355 | this.password = password; |
356 | } |
357 | |
358 | public final String getPassword() { |
359 | return password; |
360 | } |
361 | |
362 | //------------------------ interface methods --------------------------------- |
363 | |
364 | public Reference getReference() throws NamingException { |
365 | // This method creates a new Reference object to represent this data source. |
366 | // The class name of the data source object is saved in the Reference, |
367 | // so that an object factory will know that it should create an instance |
368 | // of that class when a lookup operation is performed. The class |
369 | // name of the object factory, org.apache.derby.client.ClientBaseDataSourceFactory, |
370 | // is also stored in the reference. |
371 | // This is not required by JNDI, but is recommend in practice. |
372 | // JNDI will always use the object factory class specified in the reference when |
373 | // reconstructing an object, if a class name has been specified. |
374 | // See the JNDI SPI documentation |
375 | // for further details on this topic, and for a complete description of the Reference |
376 | // and StringRefAddr classes. |
377 | // |
378 | // This ClientBaseDataSource class provides several standard JDBC properties. |
379 | // The names and values of the data source properties are also stored |
380 | // in the reference using the StringRefAddr class. |
381 | // This is all the information needed to reconstruct a ClientBaseDataSource object. |
382 | |
383 | Reference ref = new Reference(this.getClass().getName(), ClientDataSourceFactory.class.getName(), null); |
384 | |
385 | addBeanProperties(ref); |
386 | return ref; |
387 | } |
388 | |
389 | /** |
390 | * Add Java Bean properties to the reference using |
391 | * StringRefAddr for each property. List of bean properties |
392 | * is defined from the public getXXX() methods on this object |
393 | * that take no arguments and return short, int, boolean or String. |
394 | * The StringRefAddr has a key of the Java bean property name, |
395 | * converted from the method name. E.g. traceDirectory for |
396 | * traceDirectory. |
397 | * |
398 | */ |
399 | private void addBeanProperties(Reference ref) |
400 | { |
401 | // Look for all the getXXX methods in the class that take no arguments. |
402 | Method[] methods = this.getClass().getMethods(); |
403 | |
404 | for (int i = 0; i < methods.length; i++) { |
405 | |
406 | Method m = methods[i]; |
407 | |
408 | // only look for simple getter methods. |
409 | if (m.getParameterTypes().length != 0) |
410 | continue; |
411 | |
412 | // only non-static methods |
413 | if (Modifier.isStatic(m.getModifiers())) |
414 | continue; |
415 | |
416 | // Only getXXX methods |
417 | String methodName = m.getName(); |
418 | if ((methodName.length() < 5) || !methodName.startsWith("get")) |
419 | continue; |
420 | |
421 | Class returnType = m.getReturnType(); |
422 | |
423 | if (Integer.TYPE.equals(returnType) |
424 | || Short.TYPE.equals(returnType) |
425 | || String.class.equals(returnType) |
426 | || Boolean.TYPE.equals(returnType)) { |
427 | |
428 | // setSomeProperty |
429 | // 01234 |
430 | |
431 | String propertyName = methodName.substring(3, 4).toLowerCase( |
432 | java.util.Locale.ENGLISH).concat( |
433 | methodName.substring(4)); |
434 | |
435 | try { |
436 | Object ov = m.invoke(this, null); |
437 | String value = ov == null ? null : ov.toString(); |
438 | ref.add(new StringRefAddr(propertyName, value)); |
439 | } catch (IllegalAccessException iae) { |
440 | } catch (InvocationTargetException ite) { |
441 | } |
442 | |
443 | } |
444 | } |
445 | } |
446 | |
447 | // ----------------------supplemental methods--------------------------------- |
448 | |
449 | |
450 | //---------------------- helper methods -------------------------------------- |
451 | |
452 | // The java.io.PrintWriter overrides the traceFile setting. |
453 | // If neither traceFile nor jdbc logWriter are set, then null is returned. |
454 | // logWriterInUseSuffix used only for trace directories to indicate whether |
455 | // log writer is use is from xads, cpds, sds, ds, driver, config, reset. |
456 | LogWriter computeDncLogWriterForNewConnection(String logWriterInUseSuffix) throws SqlException { |
457 | return computeDncLogWriterForNewConnection(logWriter, traceDirectory, traceFile, traceFileAppend, traceLevel, logWriterInUseSuffix, traceFileSuffixIndex_++); |
458 | } |
459 | |
460 | // Called on for connection requests. |
461 | // The java.io.PrintWriter overrides the traceFile setting. |
462 | // If neither traceFile, nor logWriter, nor traceDirectory are set, then null is returned. |
463 | static LogWriter computeDncLogWriterForNewConnection(PrintWriter logWriter, String traceDirectory, String traceFile, boolean traceFileAppend, int traceLevel, String logWriterInUseSuffix, int traceFileSuffixIndex) throws SqlException { |
464 | int globaltraceFileSuffixIndex = Configuration.traceFileSuffixIndex__++; |
465 | |
466 | // compute regular dnc log writer if there is any |
467 | LogWriter dncLogWriter = computeDncLogWriter(logWriter, traceDirectory, traceFile, traceFileAppend, logWriterInUseSuffix, traceFileSuffixIndex, traceLevel); |
468 | if (dncLogWriter != null) { |
469 | return dncLogWriter; |
470 | } |
471 | // compute global default dnc log writer if there is any |
472 | dncLogWriter = computeDncLogWriter(null, Configuration.traceDirectory__, Configuration.traceFile__, Configuration.traceFileAppend__, "_global", globaltraceFileSuffixIndex, Configuration.traceLevel__); |
473 | return dncLogWriter; |
474 | } |
475 | |
476 | // Compute a DNC log writer before a connection is created. |
477 | static LogWriter computeDncLogWriter(PrintWriter logWriter, String traceDirectory, String traceFile, boolean traceFileAppend, String logWriterInUseSuffix, int traceFileSuffixIndex, int traceLevel) throws SqlException { |
478 | // Otherwise, the trace file will still be created even TRACE_NONE. |
479 | if (traceLevel == TRACE_NONE) { |
480 | return null; |
481 | } |
482 | |
483 | PrintWriter printWriter = computePrintWriter(logWriter, traceDirectory, traceFile, traceFileAppend, logWriterInUseSuffix, traceFileSuffixIndex); |
484 | if (printWriter == null) { |
485 | return null; |
486 | } |
487 | |
488 | LogWriter dncLogWriter = new NetLogWriter(printWriter, traceLevel); |
489 | if (printWriter != logWriter && traceDirectory != null) |
490 | // When printWriter is an internal trace file and |
491 | // traceDirectory is not null, each connection has |
492 | // its own trace file and the trace file is not cached, |
493 | // so we can close it when DNC log writer is closed. |
494 | { |
495 | dncLogWriter.printWriterNeedsToBeClosed_ = true; |
496 | } |
497 | return dncLogWriter; |
498 | } |
499 | |
500 | // Compute a DNC log writer after a connection is created. |
501 | // Declared public for use by am.Connection. Not a public external. |
502 | public static LogWriter computeDncLogWriter(Connection connection, PrintWriter logWriter, String traceDirectory, String traceFile, boolean traceFileAppend, String logWriterInUseSuffix, int traceFileSuffixIndex, int traceLevel) throws SqlException { |
503 | // Otherwise, the trace file will still be created even TRACE_NONE. |
504 | if (traceLevel == TRACE_NONE) { |
505 | return null; |
506 | } |
507 | |
508 | PrintWriter printWriter = computePrintWriter(logWriter, traceDirectory, traceFile, traceFileAppend, logWriterInUseSuffix, traceFileSuffixIndex); |
509 | if (printWriter == null) { |
510 | return null; |
511 | } |
512 | |
513 | LogWriter dncLogWriter = connection.agent_.newLogWriter_(printWriter, traceLevel); |
514 | if (printWriter != logWriter && traceDirectory != null) |
515 | // When printWriter is an internal trace file and |
516 | // traceDirectory is not null, each connection has |
517 | // its own trace file and the trace file is not cached, |
518 | // so we can close it when DNC log writer is closed. |
519 | { |
520 | dncLogWriter.printWriterNeedsToBeClosed_ = true; |
521 | } |
522 | return dncLogWriter; |
523 | } |
524 | |
525 | // This method handles all the override semantics. |
526 | // The logWriter overrides the traceFile, and traceDirectory settings. |
527 | // If neither traceFile, nor logWriter, nor traceDirectory are set, then null is returned. |
528 | static PrintWriter computePrintWriter(PrintWriter logWriter, String traceDirectory, String traceFile, boolean traceFileAppend, String logWriterInUseSuffix, int traceFileSuffixIndex) throws SqlException { |
529 | if (logWriter != null) // java.io.PrintWriter is specified |
530 | { |
531 | return logWriter; |
532 | } else { // check trace file setting. |
533 | if (traceDirectory != null) { |
534 | String fileName; |
535 | if (traceFile == null) { |
536 | fileName = traceDirectory + "/" + logWriterInUseSuffix + "_" + traceFileSuffixIndex; |
537 | } else { |
538 | fileName = traceDirectory + "/" + traceFile + logWriterInUseSuffix + "_" + traceFileSuffixIndex; |
539 | } |
540 | return LogWriter.getPrintWriter(fileName, true); // no file append and not enable caching. |
541 | } else if (traceFile != null) { |
542 | return LogWriter.getPrintWriter(traceFile, traceFileAppend); |
543 | } |
544 | } |
545 | return null; |
546 | } |
547 | |
548 | private static boolean parseBoolean(String boolString, boolean defaultBool) { |
549 | if (boolString != null) { |
550 | return (boolString.equalsIgnoreCase("true") || boolString.equalsIgnoreCase("yes")); |
551 | } |
552 | return defaultBool; |
553 | } |
554 | |
555 | private static String parseString(String string, String defaultString) { |
556 | if (string != null) { |
557 | return string; |
558 | } |
559 | return defaultString; |
560 | } |
561 | |
562 | private static short parseShort(String shortString, short defaultShort) { |
563 | if (shortString != null) { |
564 | return Short.parseShort(shortString); |
565 | } |
566 | return defaultShort; |
567 | } |
568 | |
569 | private static int parseInt(String intString, int defaultInt) { |
570 | if (intString != null) { |
571 | return Integer.parseInt(intString); |
572 | } |
573 | return defaultInt; |
574 | } |
575 | |
576 | // tokenize "property=value;property=value..." and returns new properties object |
577 | //This method is used both by ClientDriver to parse the url and |
578 | // ClientDataSource.setConnectionAttributes |
579 | static Properties tokenizeAttributes(String attributeString, Properties properties) throws SqlException { |
580 | Properties augmentedProperties; |
581 | |
582 | if (attributeString == null) { |
583 | return properties; |
584 | } |
585 | |
586 | if (properties != null) { |
587 | augmentedProperties = (Properties) properties.clone(); |
588 | } else { |
589 | augmentedProperties = new Properties(); |
590 | } |
591 | try { |
592 | StringTokenizer attrTokenizer = new StringTokenizer(attributeString, ";"); |
593 | while (attrTokenizer.hasMoreTokens()) { |
594 | String v = attrTokenizer.nextToken(); |
595 | |
596 | int eqPos = v.indexOf('='); |
597 | if (eqPos == -1) { |
598 | throw new SqlException(null, |
599 | new ClientMessageId(SQLState.INVALID_ATTRIBUTE_SYNTAX), |
600 | attributeString); |
601 | } |
602 | |
603 | augmentedProperties.setProperty((v.substring(0, eqPos)).trim(), (v.substring(eqPos + 1)).trim()); |
604 | } |
605 | } catch (NoSuchElementException e) { |
606 | // A null log writer is passed, because jdbc 1 sqlexceptions are automatically traced |
607 | throw new SqlException(null, |
608 | new ClientMessageId(SQLState.INVALID_ATTRIBUTE_SYNTAX), |
609 | attributeString, e); |
610 | } |
611 | checkBoolean(augmentedProperties, Attribute.CLIENT_RETIEVE_MESSAGE_TEXT); |
612 | return augmentedProperties; |
613 | |
614 | } |
615 | |
616 | private static void checkBoolean(Properties set, String attribute) throws SqlException { |
617 | final String[] booleanChoices = {"true", "false"}; |
618 | checkEnumeration(set, attribute, booleanChoices); |
619 | } |
620 | |
621 | |
622 | private static void checkEnumeration(Properties set, String attribute, String[] choices) throws SqlException { |
623 | String value = set.getProperty(attribute); |
624 | if (value == null) { |
625 | return; |
626 | } |
627 | |
628 | for (int i = 0; i < choices.length; i++) { |
629 | if (value.toUpperCase(java.util.Locale.ENGLISH).equals(choices[i].toUpperCase(java.util.Locale.ENGLISH))) { |
630 | return; |
631 | } |
632 | } |
633 | |
634 | // The attribute value is invalid. Construct a string giving the choices for |
635 | // display in the error message. |
636 | String choicesStr = "{"; |
637 | for (int i = 0; i < choices.length; i++) { |
638 | if (i > 0) { |
639 | choicesStr += "|"; |
640 | } |
641 | choicesStr += choices[i]; |
642 | } |
643 | |
644 | throw new SqlException(null, |
645 | new ClientMessageId(SQLState.INVALID_ATTRIBUTE), |
646 | attribute, value, choicesStr); |
647 | } |
648 | |
649 | /* |
650 | * Properties to be seen by Bean - access thru reflection. |
651 | */ |
652 | |
653 | // -- Stardard JDBC DataSource Properties |
654 | |
655 | public synchronized void setDatabaseName(String databaseName) { |
656 | this.databaseName = databaseName; |
657 | } |
658 | |
659 | public String getDatabaseName() { |
660 | return this.databaseName; |
661 | } |
662 | |
663 | |
664 | public synchronized void setDataSourceName(String dataSourceName) { |
665 | this.dataSourceName = dataSourceName; |
666 | } |
667 | |
668 | public String getDataSourceName() { |
669 | return this.dataSourceName; |
670 | } |
671 | |
672 | public synchronized void setDescription(String description) { |
673 | this.description = description; |
674 | } |
675 | |
676 | public String getDescription() { |
677 | return this.description; |
678 | } |
679 | |
680 | |
681 | public synchronized void setPortNumber(int portNumber) { |
682 | this.portNumber = portNumber; |
683 | } |
684 | |
685 | public int getPortNumber() { |
686 | return this.portNumber; |
687 | } |
688 | |
689 | public synchronized void setServerName(String serverName) { |
690 | this.serverName = serverName; |
691 | } |
692 | |
693 | public String getServerName() { |
694 | return this.serverName; |
695 | } |
696 | |
697 | |
698 | public synchronized void setUser(String user) { |
699 | this.user = user; |
700 | } |
701 | |
702 | public String getUser() { |
703 | return this.user; |
704 | } |
705 | |
706 | synchronized public void setRetrieveMessageText(boolean retrieveMessageText) { |
707 | this.retrieveMessageText = retrieveMessageText; |
708 | } |
709 | |
710 | public boolean getRetrieveMessageText() { |
711 | return this.retrieveMessageText; |
712 | } |
713 | |
714 | // ---------------------------- securityMechanism ----------------------------------- |
715 | /** |
716 | * The source security mechanism to use when connecting to this data source. |
717 | * <p/> |
718 | * Security mechanism options are: <ul> <li> USER_ONLY_SECURITY <li> CLEAR_TEXT_PASSWORD_SECURITY <li> |
719 | * ENCRYPTED_PASSWORD_SECURITY <li> ENCRYPTED_USER_AND_PASSWORD_SECURITY - both password and user are encrypted |
720 | * </ul> The default security mechanism is USER_ONLY SECURITY |
721 | * <p/> |
722 | * If the application specifies a security mechanism then it will be the only one attempted. If the specified |
723 | * security mechanism is not supported by the conversation then an exception will be thrown and there will be no |
724 | * additional retries. |
725 | * <p/> |
726 | * This property is currently only available for the DNC driver. |
727 | * <p/> |
728 | * Both user and password need to be set for all security mechanism except USER_ONLY_SECURITY |
729 | */ |
730 | // We use the NET layer constants to avoid a mapping for the NET driver. |
731 | public final static short USER_ONLY_SECURITY = (short) NetConfiguration.SECMEC_USRIDONL; |
732 | public final static short CLEAR_TEXT_PASSWORD_SECURITY = (short) NetConfiguration.SECMEC_USRIDPWD; |
733 | public final static short ENCRYPTED_PASSWORD_SECURITY = (short) NetConfiguration.SECMEC_USRENCPWD; |
734 | public final static short ENCRYPTED_USER_AND_PASSWORD_SECURITY = (short) NetConfiguration.SECMEC_EUSRIDPWD; |
735 | |
736 | /** |
737 | * sets the security mechanism |
738 | * @param securityMechanism to set |
739 | */ |
740 | synchronized public void setSecurityMechanism(short securityMechanism) { |
741 | this.securityMechanism = securityMechanism; |
742 | } |
743 | |
744 | /** |
745 | * return the security mechanism |
746 | * if security mechanism has not been set explicitly on datasource |
747 | * then upgrade the security mechanism to a more secure one if possible |
748 | * @see #getUpgradedSecurityMechanism(String) |
749 | * @return the security mechanism |
750 | */ |
751 | public short getSecurityMechanism() { |
752 | return getSecurityMechanism(getPassword()); |
753 | } |
754 | |
755 | /** |
756 | * return the security mechanism for this datasource object |
757 | * if security mechanism has not been set explicitly on datasource |
758 | * then upgrade the security mechanism to a more secure one if possible |
759 | * @param password password of user |
760 | * @see #getUpgradedSecurityMechanism(String) |
761 | * @return the security mechanism |
762 | */ |
763 | public short getSecurityMechanism(String password) { |
764 | |
765 | // if security mechanism has not been set explicitly on |
766 | // datasource, then upgrade the security mechanism if possible |
767 | // DERBY-962 |
768 | if ( securityMechanism == SECMEC_HAS_NOT_EXPLICITLY_SET ) |
769 | return getUpgradedSecurityMechanism(password); |
770 | |
771 | return securityMechanism; |
772 | } |
773 | |
774 | protected String connectionAttributes = null; |
775 | |
776 | /** |
777 | * Set this property to pass in more Derby specific connection URL attributes. |
778 | * <BR> |
779 | * Any attributes that can be set using a property of this DataSource implementation |
780 | * (e.g user, password) should not be set in connectionAttributes. Conflicting |
781 | * settings in connectionAttributes and properties of the DataSource will lead to |
782 | * unexpected behaviour. |
783 | * |
784 | * @param prop set to the list of Cloudscape connection attributes separated by semi-colons. E.g., to specify an |
785 | * encryption bootPassword of "x8hhk2adf", and set upgrade to true, do the following: <PRE> |
786 | * ds.setConnectionAttributes("bootPassword=x8hhk2adf;upgrade=true"); </PRE> See Derby documentation for |
787 | * complete list. |
788 | */ |
789 | public final void setConnectionAttributes(String prop) { |
790 | connectionAttributes = prop; |
791 | } |
792 | |
793 | /** |
794 | * @return Derby specific connection URL attributes |
795 | */ |
796 | public final String getConnectionAttributes() { |
797 | return connectionAttributes; |
798 | } |
799 | |
800 | |
801 | // ---------------------------- traceLevel ----------------------------------- |
802 | // |
803 | |
804 | public final static int TRACE_NONE = 0x0; |
805 | public final static int TRACE_CONNECTION_CALLS = 0x1; |
806 | public final static int TRACE_STATEMENT_CALLS = 0x2; |
807 | public final static int TRACE_RESULT_SET_CALLS = 0x4; |
808 | public final static int TRACE_DRIVER_CONFIGURATION = 0x10; |
809 | public final static int TRACE_CONNECTS = 0x20; |
810 | public final static int TRACE_PROTOCOL_FLOWS = 0x40; |
811 | public final static int TRACE_RESULT_SET_META_DATA = 0x80; |
812 | public final static int TRACE_PARAMETER_META_DATA = 0x100; |
813 | public final static int TRACE_DIAGNOSTICS = 0x200; |
814 | public final static int TRACE_XA_CALLS = 0x800; |
815 | public final static int TRACE_ALL = 0xFFFFFFFF; |
816 | |
817 | public final static int propertyDefault_traceLevel = TRACE_ALL; |
818 | |
819 | protected int traceLevel = propertyDefault_traceLevel; |
820 | |
821 | public static int getTraceLevel(Properties properties) { |
822 | String traceLevelString = properties.getProperty(Attribute.CLIENT_TRACE_LEVEL); |
823 | return parseInt(traceLevelString, propertyDefault_traceLevel); |
824 | } |
825 | |
826 | synchronized public void setTraceLevel(int traceLevel) { |
827 | this.traceLevel = traceLevel; |
828 | } |
829 | |
830 | public int getTraceLevel() { |
831 | return this.traceLevel; |
832 | } |
833 | |
834 | |
835 | public synchronized void setTraceFile(String traceFile) { |
836 | this.traceFile = traceFile; |
837 | } |
838 | |
839 | public String getTraceFile() { |
840 | return this.traceFile; |
841 | } |
842 | |
843 | |
844 | public synchronized void setTraceDirectory(String traceDirectory) { |
845 | this.traceDirectory = traceDirectory; |
846 | } |
847 | |
848 | public String getTraceDirectory() { |
849 | return this.traceDirectory; |
850 | } |
851 | |
852 | synchronized public void setTraceFileAppend(boolean traceFileAppend) { |
853 | this.traceFileAppend = traceFileAppend; |
854 | } |
855 | |
856 | public boolean getTraceFileAppend() { |
857 | return this.traceFileAppend; |
858 | } |
859 | |
860 | |
861 | |
862 | |
863 | // --- private helper methods |
864 | |
865 | |
866 | /** |
867 | * The dataSource keeps individual fields for the values that are relevant to the client. These need to be updated |
868 | * when set connection attributes is called. |
869 | */ |
870 | void updateDataSourceValues(Properties prop) { |
871 | if (prop == null) { |
872 | return; |
873 | } |
874 | |
875 | if (prop.containsKey(Attribute.USERNAME_ATTR)) { |
876 | setUser(getUser(prop)); |
877 | } |
878 | if (prop.containsKey(Attribute.CLIENT_SECURITY_MECHANISM)) { |
879 | setSecurityMechanism(getSecurityMechanism(prop)); |
880 | } |
881 | if (prop.containsKey(Attribute.CLIENT_TRACE_FILE)) { |
882 | setTraceFile(getTraceFile(prop)); |
883 | } |
884 | if (prop.containsKey(Attribute.CLIENT_TRACE_DIRECTORY)) { |
885 | setTraceDirectory(getTraceDirectory(prop)); |
886 | } |
887 | if (prop.containsKey(Attribute.CLIENT_TRACE_APPEND)) { |
888 | setTraceFileAppend(getTraceFileAppend(prop)); |
889 | } |
890 | if (prop.containsKey(Attribute.CLIENT_SECURITY_MECHANISM)) { |
891 | setSecurityMechanism(getSecurityMechanism(prop)); |
892 | } |
893 | if (prop.containsKey(Attribute.CLIENT_RETIEVE_MESSAGE_TEXT)) { |
894 | setRetrieveMessageText(getRetrieveMessageText(prop)); |
895 | } |
896 | } |
897 | |
898 | |
899 | } |
900 | |
901 | |