001: //
002: // Copyright 1998 CDS Networks, Inc., Medford Oregon
003: //
004: // All rights reserved.
005: //
006: // Redistribution and use in source and binary forms, with or without
007: // modification, are permitted provided that the following conditions are met:
008: // 1. Redistributions of source code must retain the above copyright
009: // notice, this list of conditions and the following disclaimer.
010: // 2. Redistributions in binary form must reproduce the above copyright
011: // notice, this list of conditions and the following disclaimer in the
012: // documentation and/or other materials provided with the distribution.
013: // 3. All advertising materials mentioning features or use of this software
014: // must display the following acknowledgement:
015: // This product includes software developed by CDS Networks, Inc.
016: // 4. The name of CDS Networks, Inc. may not be used to endorse or promote
017: // products derived from this software without specific prior
018: // written permission.
019: //
020: // THIS SOFTWARE IS PROVIDED BY CDS NETWORKS, INC. ``AS IS'' AND
021: // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
022: // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
023: // ARE DISCLAIMED. IN NO EVENT SHALL CDS NETWORKS, INC. BE LIABLE
024: // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
025: // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
026: // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
027: // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
028: // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
029: // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
030: // SUCH DAMAGE.
031: //
032:
033: package com.internetcds.jdbc.tds;
034:
035: import java.sql.*;
036: import java.util.*;
037: import com.internetcds.jdbc.tds.TdsException;
038:
039: /**
040: * <P>The Java SQL framework allows for multiple database drivers.
041: *
042: * <P>Each driver should supply a class that implements
043: * the Driver interface.
044: *
045: * <P>The DriverManager will try to load as many drivers as it can
046: * find and then for any given connection request, it will ask each
047: * driver in turn to try to connect to the target URL.
048: *
049: * <P>It is strongly recommended that each Driver class should be
050: * small and standalone so that the Driver class can be loaded and
051: * queried without bringing in vast quantities of supporting code.
052: *
053: * <P>When a Driver class is loaded, it should create an instance of
054: * itself and register it with the DriverManager. This means that a
055: * user can load and register a driver by doing
056: * Class.forName("foo.bah.Driver").
057: *
058: * @author Craig Spannring
059: * @author Igor Petrovski
060: * @version $Id: Driver.java,v 1.2 2007-10-19 13:21:40 sinisa Exp $
061: *
062: * @see DriverManager
063: * @see Connection
064: */
065: public class Driver implements java.sql.Driver {
066: public static final String cvsVersion = "$Id: Driver.java,v 1.2 2007-10-19 13:21:40 sinisa Exp $";
067:
068: //
069: // Register ourselves with the DriverManager
070: //
071: static {
072: try {
073: java.sql.DriverManager.registerDriver(new Driver());
074: } catch (SQLException E) {
075: E.printStackTrace();
076: }
077: }
078:
079: static final boolean debug = false;
080: static final String oldSQLServerUrlPrefix = "jdbc:freetds://";
081: static final String newSQLServerUrlPrefix = "jdbc:freetds:sqlserver://";
082: static final String sybaseUrlPrefix = "jdbc:freetds:sybase://";
083: static final String defaultSQLServerPort = "1433";
084: static final String defaultSybasePort = "7100";
085:
086: private boolean isValidHostname(String host) {
087: return true; // XXX
088: }
089:
090: /**
091: * Parses the properties specified in <i>url</i> and adds them to
092: * <i>result</i>.
093: *
094: * @return true if the URL could be parsed successfully.
095: */
096: protected boolean parseUrl(String url, Properties result) {
097: String tmpUrl = url;
098: int serverType = -1;
099:
100: if (tmpUrl.startsWith(oldSQLServerUrlPrefix)
101: || tmpUrl.startsWith(newSQLServerUrlPrefix)
102: || tmpUrl.startsWith(sybaseUrlPrefix)) {
103: if (tmpUrl.startsWith(oldSQLServerUrlPrefix)) {
104: serverType = Tds.SQLSERVER;
105: tmpUrl = tmpUrl.substring(oldSQLServerUrlPrefix
106: .length());
107: } else if (tmpUrl.startsWith(newSQLServerUrlPrefix)) {
108: serverType = Tds.SQLSERVER;
109: tmpUrl = tmpUrl.substring(newSQLServerUrlPrefix
110: .length());
111: } else if (tmpUrl.startsWith(sybaseUrlPrefix)) {
112: serverType = Tds.SYBASE;
113: tmpUrl = url.substring(sybaseUrlPrefix.length());
114: }
115:
116: try {
117: StringTokenizer tokenizer = new StringTokenizer(tmpUrl,
118: ":/;", true);
119: String tmp;
120: String host = null;
121: String port = (serverType == Tds.SYBASE ? defaultSybasePort
122: : defaultSQLServerPort);
123: String database = null;
124: // String tdsVer = "42";
125: //Sinisa
126: String tdsVer = "7.0";
127:
128: // Get the hostname
129: host = tokenizer.nextToken();
130:
131: // Find the port if it has one.
132: tmp = tokenizer.nextToken();
133: if (tmp.equals(":")) {
134: port = tokenizer.nextToken();
135: // Skip the '/' character
136: tmp = tokenizer.nextToken();
137: }
138:
139: if (tmp.equals("/")) {
140: // find the database name
141: database = tokenizer.nextToken();
142: if (tokenizer.hasMoreTokens())
143: tmp = tokenizer.nextToken();
144: }
145:
146: // XXX The next loop is a bit too permisive.
147: while (tmp.equals(";")) {
148: // Extract the additional attribute.
149: String extra = tokenizer.nextToken();
150: StringTokenizer tok2 = new StringTokenizer(extra,
151: "=", false);
152: String key = tok2.nextToken().toUpperCase();
153: if (tok2.hasMoreTokens()) {
154: result.put(key, tok2.nextToken());
155: }
156:
157: if (tokenizer.hasMoreTokens()) {
158: tmp = tokenizer.nextToken();
159: } else {
160: break;
161: }
162: }
163:
164: // if there are anymore tokens then don't recognoze this URL
165: if ((!tokenizer.hasMoreTokens())
166: && isValidHostname(host) && database != null) {
167: result.put("HOST", host);
168: result.put("SERVERTYPE", "" + serverType);
169: result.put("PORT", port);
170: result.put("DBNAME", database);
171: } else {
172: return false;
173: }
174: } catch (NoSuchElementException e) {
175: return false;
176: }
177: } else {
178: return false;
179: }
180:
181: return true;
182: }
183:
184: /**
185: * Construct a new driver and register it with DriverManager
186: *
187: * @exception SQLException
188: */
189: public Driver() throws SQLException {
190: }
191:
192: /**
193: * Try to make a database connection to the given URL. The driver
194: * should return "null" if it realizes it is the wrong kind of
195: * driver to connect to the given URL. This will be common, as
196: * when the JDBC driverManager is asked to connect to a given URL,
197: * it passes the URL to each loaded driver in turn.
198: *
199: * <p>The driver should raise an SQLException if it is the right driver
200: * to connect to the given URL, but has trouble connecting to the
201: * database.
202: *
203: * <p>The java.util.Properties argument can be used to pass arbitrary
204: * string tag/value pairs as connection arguments.
205: *
206: * This driver handles URLs of the form:
207: * <PRE>
208: * jdbc:freetds://servername/database
209: * jdbc:freetds://servername:port/database
210: * jdbc:freetds:sqlserver://servername/database
211: * jdbc:freetds:sqlserver://servername:port/database
212: * jdbc:freetds:sybase://servername/database
213: * jdbc:freetds:sybase://servername:port/database
214: * </PRE>
215: * <p>
216: *
217: * <table><thead>Recognized Properties</thead>
218: * <tbody>
219: * <tr><td>PROGNAME<td>Send this name to server to identify the program</tr>
220: * <tr><td>APPNAME<td>Send this name to server to identify the app</tr>
221: * </tbody>
222: * </table>
223: *
224: * @param url the URL of the database to connect to
225: * @param info a list of arbitrary tag/value pairs as connection
226: * arguments
227: * @return a connection to the URL or null if it isnt us
228: * @exception SQLException if a database access error occurs
229: * @see java.sql.Driver#connect
230: */
231: public java.sql.Connection connect(String Url, Properties info)
232: throws SQLException {
233: java.sql.Connection result = null;
234:
235: if (!parseUrl(Url, info)) {
236: return null;
237: } else {
238: try {
239: result = Constructors.newConnection(info);
240: } catch (NumberFormatException e) {
241: throw new SQLException(
242: "NumberFormatException converting port number");
243: } catch (com.internetcds.jdbc.tds.TdsException e) {
244: throw new SQLException(e.getMessage());
245: }
246: }
247: return result;
248: }
249:
250: /**
251: * Returns true if the driver thinks it can open a connection to the
252: * given URL. Typically, drivers will return true if they understand
253: * the subprotocol specified in the URL and false if they don't. This
254: * driver's protocols start with jdbc:freetds:
255: *
256: * This driver handles URLs of the form:
257: * <PRE>
258: * jdbc:freetds://host:port/database
259: * </PRE>
260: * or
261: * <PRE>
262: * jdbc:freetds://host/database
263: * </PRE>
264: * <PRE>
265: * jdbc:freetds:sqlserver://host:port/database
266: * </PRE>
267: * or
268: * <PRE>
269: * jdbc:freetds:sqlserver://host/database
270: * </PRE>
271: * <PRE>
272: * jdbc:freetds:sybase://host:port/database
273: * </PRE>
274: * or
275: * <PRE>
276: * jdbc:freetds:sybase://host/database
277: * </PRE>
278: *
279: * @see java.sql.Driver#acceptsURL
280: * @param url the URL of the driver
281: * @return true if this driver accepts the given URL
282: * @exception SQLException if a database-access error occurs
283: */
284: public boolean acceptsURL(String url) throws SQLException {
285: boolean result = parseUrl(url, new Properties());
286: return result;
287: }
288:
289: /**
290: * <p>The getPropertyInfo method is intended to allow a generic GUI tool to
291: * discover what properties it should prompt a human for in order to get
292: * enough information to connect to a database. Note that depending on
293: * the values the human has supplied so far, additional values may become
294: * necessary, so it may be necessary to iterate though several calls
295: * to getPropertyInfo.
296: *
297: * @param url The URL of the database to connect to.
298: * @param info A proposed list of tag/value pairs that will be sent on
299: * connect open.
300: * @return An array of DriverPropertyInfo objects describing possible
301: * properties. This array may be an empty array if no properties
302: * are required.
303: * @exception SQLException if a database-access error occurs.
304: */
305: public DriverPropertyInfo[] getPropertyInfo(String Url,
306: Properties Info) throws SQLException {
307: DriverPropertyInfo result[] = new DriverPropertyInfo[0];
308:
309: return result;
310: }
311:
312: /**
313: * Gets the drivers major version number
314: *
315: * @return the drivers major version number
316: */
317: public int getMajorVersion() {
318: return DriverVersion.getDriverMajorVersion();
319: }
320:
321: /**
322: * Get the driver's minor version number. Initially this should be 0.
323: */
324: public int getMinorVersion() {
325: return DriverVersion.getDriverMinorVersion();
326: }
327:
328: /**
329: * Report whether the Driver is a genuine JDBC COMPLIANT (tm) driver.
330: * A driver may only report "true" here if it passes the JDBC compliance
331: * tests, otherwise it is required to return false.
332: *
333: * JDBC compliance requires full support for the JDBC API and full support
334: * for SQL 92 Entry Level. It is expected that JDBC compliant drivers will
335: * be available for all the major commercial databases.
336: *
337: * This method is not intended to encourage the development of non-JDBC
338: * compliant drivers, but is a recognition of the fact that some vendors
339: * are interested in using the JDBC API and framework for lightweight
340: * databases that do not support full database functionality, or for
341: * special databases such as document information retrieval where a SQL
342: * implementation may not be feasible.
343: */
344: public boolean jdbcCompliant() {
345: // :-( MS SQLServer 6.5 doesn't provide what JDBC wants.
346: // See DatabaseMetaData.nullPlusNonNullIsNull() for more details.
347: // XXX Need to check if Sybase could be jdbcCompliant
348: return false;
349: }
350:
351: }
|