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