001 /*
002 
003    Derby - Class org.apache.derby.jdbc.ClientDriver
004 
005    Copyright (c) 2001, 2005 The Apache Software Foundation or its licensors, where applicable.
006 
007    Licensed under the Apache License, Version 2.0 (the "License");
008    you may not use this file except in compliance with the License.
009    You may obtain a copy of the License at
010 
011       http://www.apache.org/licenses/LICENSE-2.0
012 
013    Unless required by applicable law or agreed to in writing, software
014    distributed under the License is distributed on an "AS IS" BASIS,
015    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016    See the License for the specific language governing permissions and
017    limitations under the License.
018 
019 */
020 
021 package org.apache.derby.jdbc;
022 
023 import org.apache.derby.client.am.Configuration;
024 import org.apache.derby.client.am.ResourceUtilities;
025 import org.apache.derby.client.am.SqlException;
026 import org.apache.derby.client.am.Utils;
027 import org.apache.derby.client.am.Version;
028 import org.apache.derby.client.resources.ResourceKeys;
029 
030 
031 public class ClientDriver implements java.sql.Driver {
032     private transient int traceFileSuffixIndex_ = 0;
033 
034     private final static int DERBY_REMOTE_PROTOCOL = 1;
035 
036     static private SqlException exceptionsOnLoadDriver__ = null;
037     // Keep track of the registere driver so that we can deregister it if we're a stored proc.
038     static private ClientDriver registeredDriver__ = null;
039 
040     static {
041         // This may possibly hit the race-condition bug of java 1.1.
042         // The Configuration static clause should execute before the following line does.
043         if (Configuration.exceptionsOnLoadResources != null) {
044             exceptionsOnLoadDriver__ =
045                     Utils.accumulateSQLException(Configuration.exceptionsOnLoadResources,
046                             exceptionsOnLoadDriver__);
047         }
048         try {
049             registeredDriver__ = new ClientDriver();
050             java.sql.DriverManager.registerDriver(registeredDriver__);
051         catch (java.sql.SQLException e) {
052             // A null log writer is passed, because jdbc 1 sql exceptions are automatically traced
053             exceptionsOnLoadDriver__ =
054                     new SqlException(null, "Error occurred while trying to register Dnc driver with JDBC 1 Driver Manager");
055             exceptionsOnLoadDriver__.setNextException(e);
056         }
057     }
058 
059     public ClientDriver() {
060     }
061 
062     public java.sql.Connection connect(String url,
063                                        java.util.Properties propertiesthrows java.sql.SQLException {
064         if (exceptionsOnLoadDriver__ != null) {
065             throw exceptionsOnLoadDriver__;
066         }
067 
068         if (properties == null) {
069             properties = new java.util.Properties();
070         }
071 
072         java.util.StringTokenizer urlTokenizer =
073                 new java.util.StringTokenizer(url, "/:= \t\n\r\f"true);
074 
075         int protocol = tokenizeProtocol(url, urlTokenizer);
076         if (protocol == 0) {
077             return null// unrecognized database URL prefix.
078         }
079 
080         String slashOrNull = null;
081         if (protocol == DERBY_REMOTE_PROTOCOL) {
082             try {
083                 slashOrNull = urlTokenizer.nextToken(":/");
084             catch (java.util.NoSuchElementException e) {
085                 // A null log writer is passed, because jdbc 1 sqlexceptions are automatically traced
086                 throw new SqlException(null, e, "Invalid database url syntax: " + url);
087             }
088         }
089         String server = tokenizeServerName(urlTokenizer, url);    // "/server"
090         int port = tokenizeOptionalPortNumber(urlTokenizer, url)// "[:port]/"
091         if (port == 0) {
092             port = ClientDataSource.propertyDefault_portNumber;
093         }
094 
095         // longDatabase is the databaseName and attributes.  This will be
096         // sent to network server as the databaseName
097         String database = tokenizeDatabase(urlTokenizer, url)// "database"
098         java.util.Properties augmentedProperties = tokenizeURLProperties(url, properties);
099 
100 
101         int traceLevel;
102         try {
103             traceLevel = ClientDataSource.getTraceLevel(augmentedProperties);
104         catch (java.lang.NumberFormatException e) {
105             // A null log writer is passed, because jdbc 1 sqlexceptions are automatically traced
106             throw new SqlException(null, e, "trouble reading traceLevel connection property");
107         }
108 
109         // Jdbc 1 connections will write driver trace info on a
110         // driver-wide basis using the jdbc 1 driver manager log writer.
111         // This log writer may be narrowed to the connection-level
112         // This log writer will be passed to the agent constructor.
113         org.apache.derby.client.am.LogWriter dncLogWriter = 
114                 ClientDataSource.computeDncLogWriterForNewConnection(java.sql.DriverManager.getLogWriter(),
115                         ClientDataSource.getTraceDirectory(augmentedProperties),
116                         ClientDataSource.getTraceFile(augmentedProperties),
117                         ClientDataSource.getTraceFileAppend(augmentedProperties),
118                         traceLevel,
119                         "_driver",
120                         traceFileSuffixIndex_++);
121 
122         org.apache.derby.client.net.NetConnection conn =
123                 new org.apache.derby.client.net.NetConnection((org.apache.derby.client.net.NetLogWriterdncLogWriter,
124                         java.sql.DriverManager.getLoginTimeout(),
125                         server,
126                         port,
127                         database,
128                         augmentedProperties);
129         return conn;
130     }
131 
132     public boolean acceptsURL(String urlthrows java.sql.SQLException {
133         java.util.StringTokenizer urlTokenizer = new java.util.StringTokenizer(url, "/:=; \t\n\r\f"true);
134         int protocol = tokenizeProtocol(url, urlTokenizer);
135         return protocol != 0;
136     }
137 
138     public java.sql.DriverPropertyInfo[] getPropertyInfo(String url,
139                                                          java.util.Properties propertiesthrows java.sql.SQLException {
140         java.sql.DriverPropertyInfo driverPropertyInfo[] new java.sql.DriverPropertyInfo[2];
141 
142         // If there are no properties set already,
143         // then create a dummy properties just to make the calls go thru.
144         if (properties == null) {
145             properties = new java.util.Properties();
146         }
147 
148         driverPropertyInfo[0=
149                 new java.sql.DriverPropertyInfo(ClientDataSource.propertyKey_user,
150                         properties.getProperty(ClientDataSource.propertyKey_user, ClientDataSource.propertyDefault_user));
151 
152         driverPropertyInfo[1=
153                 new java.sql.DriverPropertyInfo(ClientDataSource.propertyKey_password,
154                         properties.getProperty(ClientDataSource.propertyKey_password));
155 
156         driverPropertyInfo[0].description =
157                 ResourceUtilities.getResource(ResourceKeys.propertyDescription__user);
158         driverPropertyInfo[1].description =
159                 ResourceUtilities.getResource(ResourceKeys.propertyDescription__password);
160 
161         driverPropertyInfo[0].required = true;
162         driverPropertyInfo[1].required = false// depending on the security mechanism
163 
164         return driverPropertyInfo;
165     }
166 
167     public int getMajorVersion() {
168         return Version.getMajorVersion();
169     }
170 
171     public int getMinorVersion() {
172         return Version.getMinorVersion();
173     }
174 
175     public boolean jdbcCompliant() {
176         return Configuration.jdbcCompliant;
177     }
178 
179     // ----------------helper methods---------------------------------------------
180 
181     // Tokenize one of the following:
182     //  "jdbc:derby:"
183     // and return 0 if the protcol is unrecognized
184     // return DERBY_PROTOCOL for "jdbc:derby"
185     private static int tokenizeProtocol(String url, java.util.StringTokenizer urlTokenizerthrows SqlException {
186         // Is this condition necessary, StringTokenizer constructor may do this for us
187         if (url == null) {
188             return 0;
189         }
190 
191         if (urlTokenizer == null) {
192             return 0;
193         }
194 
195         try {
196             String jdbc = urlTokenizer.nextToken(":");
197             if (!jdbc.equals("jdbc")) {
198                 return 0;
199             }
200             if (!urlTokenizer.nextToken(":").equals(":")) {
201                 return 0// Skip over the first colon in jdbc:derby:
202             }
203             String dbname = urlTokenizer.nextToken(":");
204             int protocol = 0;
205             if (dbname.equals("derby"&& (url.indexOf("derby://"!= -1)) {
206                 // For Derby AS need to check for // since jdbc:derby: is also the
207                 // embedded prefix
208                 protocol = DERBY_REMOTE_PROTOCOL;
209             else {
210                 return 0;
211             }
212 
213             if (!urlTokenizer.nextToken(":").equals(":")) {
214                 return 0// Skip over the second colon in jdbc:derby:
215             }
216 
217             return protocol;
218         catch (java.util.NoSuchElementException e) {
219             return 0;
220         }
221     }
222 
223     // tokenize "/server" from URL jdbc:derby://server:port/
224     // returns server name
225     private static String tokenizeServerName(java.util.StringTokenizer urlTokenizer,
226                                              String urlthrows SqlException {
227         try {
228             if (!urlTokenizer.nextToken("/").equals("/"))
229             // A null log writer is passed, because jdbc 1 sqlexceptions are automatically traced
230             {
231                 throw new SqlException(null, "Invalid database url syntax: " + url);
232             }
233             return urlTokenizer.nextToken("/:");
234         catch (java.util.NoSuchElementException e) {
235             // A null log writer is passed, because jdbc 1 sqlexceptions are automatically traced
236             throw new SqlException(null, e, "Invalid database url syntax: " + url);
237         }
238     }
239 
240     // tokenize "[:portNumber]/" from URL jdbc:derby://server[:port]/
241     // returns the portNumber or zero if portNumber is not specified.
242     private static int tokenizeOptionalPortNumber(java.util.StringTokenizer urlTokenizer,
243                                                   String urlthrows SqlException {
244         try {
245             String firstToken = urlTokenizer.nextToken(":/");
246             if (firstToken.equals(":")) {
247                 String port = urlTokenizer.nextToken("/");
248                 if (!urlTokenizer.nextToken("/").equals("/")) {
249                     // A null log writer is passed, because jdbc 1 sqlexceptions are automatically traced
250                     throw new SqlException(null, "Invalid database url syntax: " + url);
251                 }
252                 return Integer.parseInt(port);
253             else if (firstToken.equals("/")) {
254                 return 0;
255             else {
256                 // A null log writer is passed, because jdbc 1 sqlexceptions are automatically traced
257                 throw new SqlException(null, "Invalid database url syntax: " + url);
258             }
259         catch (java.util.NoSuchElementException e) {
260             // A null log writer is passed, because jdbc 1 sqlexceptions are automatically traced
261             throw new SqlException(null, e, "Invalid database url syntax: " + url);
262         }
263     }
264 
265     //return database name and attributes
266     private static String tokenizeDatabase(java.util.StringTokenizer urlTokenizer,
267                                            String urlthrows SqlException {
268         try {
269             String databaseName = urlTokenizer.nextToken(" \t\n\r\f");
270             return databaseName;
271         catch (java.util.NoSuchElementException e) {
272             // A null log writer is passed, because jdbc 1 sqlexceptions are automatically traced
273             throw new SqlException(null, e, "Invalid database url syntax: " + url);
274         }
275     }
276 
277     private static java.util.Properties tokenizeURLProperties(String url,
278                                                               java.util.Properties properties)
279             throws SqlException {
280         String attributeString = null;
281         int attributeIndex = -1;
282 
283         if ((url != null&&
284                 ((attributeIndex = url.indexOf(";")) != -1)) {
285             attributeString = url.substring(attributeIndex);
286         }
287         return ClientDataSource.tokenizeAttributes(attributeString, properties);
288     }
289 
290 
291 }
292 
293