001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
004: * Science And Control (INRIA).
005: * Contact: sequoia@continuent.org
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: * Initial developer(s): Emmanuel Cecchet.
020: * Contributor(s): ______________________.
021: */package org.continuent.sequoia.controller.backend;
022:
023: import java.net.ConnectException;
024: import java.sql.Connection;
025: import java.sql.DatabaseMetaData;
026: import java.sql.ResultSet;
027: import java.sql.Statement;
028:
029: import org.continuent.sequoia.common.i18n.Translate;
030: import org.continuent.sequoia.common.log.Trace;
031: import org.continuent.sequoia.controller.connection.DriverManager;
032:
033: /**
034: * This class checks if a given driver provides the mandatory features necessary
035: * for Sequoia.
036: *
037: * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
038: * @version 1.0
039: */
040: public class DriverCompliance {
041: private boolean isCompliant = false;
042: private boolean hasBeenTested = false;
043: private boolean supportSetQueryTimeout = false;
044: private boolean supportGetGeneratedKeys = false;
045: private boolean supportGetColumnCount = false;
046: private boolean supportGetColumnClassName = false;
047: private boolean supportGetColumnTypeName = false;
048: private boolean supportGetColumnType = false;
049: private boolean supportGetColumnDisplaySize = false;
050: private boolean supportGetTableName = false;
051: private boolean supportSetCursorName = false;
052: private boolean supportSetFetchSize = false;
053: private boolean supportSerializableIsolation = false;
054: private boolean supportSetMaxRows = false;
055:
056: private Trace logger;
057:
058: private static final int TIMEOUT_VALUE = 1000;
059: private static final String DEFAULT_TEST_STATEMENT = "select 1";
060: private String databaseProductName = "Sequoia";
061:
062: /**
063: * Builds a new DriverCompliance object.
064: *
065: * @param logger the logger to use
066: */
067: public DriverCompliance(Trace logger) {
068: this .logger = logger;
069: }
070:
071: /**
072: * Check the driver compliance.
073: *
074: * @param backendUrl the JDBC URL to connect to
075: * @param login the user login
076: * @param password the user password
077: * @param driverPath path for driver
078: * @param driverClassName class name for driver
079: * @param connectionTestStatement SQL statement used to check if a connection
080: * is still valid
081: * @return true if the driver is Sequoia compliant
082: * @throws ConnectException if it is not possible to connect to the backend
083: */
084: public boolean complianceTest(String backendUrl, String login,
085: String password, String driverPath, String driverClassName,
086: String connectionTestStatement) throws ConnectException {
087: if (hasBeenTested)
088: return isCompliant;
089:
090: isCompliant = false;
091:
092: //
093: // Connection test
094: //
095: Connection c = null;
096: try {
097: c = DriverManager.getConnection(backendUrl, login,
098: password, driverPath, driverClassName);
099: } catch (Exception e) {
100: if (logger.isDebugEnabled())
101: logger.error(Translate.get(
102: "backend.driver.test.connection.failed", e), e);
103: else
104: logger.error(Translate.get(
105: "backend.driver.test.connection.failed", e));
106: throw (ConnectException) new ConnectException(e
107: .getMessage()).initCause(e);
108: }
109:
110: if (c == null) { // SEQUOIA-735 fix
111: String msg = Translate.get(
112: "backend.driver.test.connection.failed",
113: "DriverManager returned an unexpected null connection for "
114: + backendUrl + " using driver "
115: + driverClassName);
116: logger.error(msg);
117: throw new ConnectException(msg);
118: }
119:
120: if (logger.isDebugEnabled())
121: logger.debug(Translate
122: .get("backend.driver.test.connection.ok"));
123:
124: //
125: // Transaction isolation (serializable)
126: //
127: try {
128: c
129: .setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
130: supportSerializableIsolation = true;
131: } catch (Exception e) {
132: if (logger.isDebugEnabled())
133: logger
134: .error(
135: Translate
136: .get(
137: "backend.driver.test.serializable.isolation.failed",
138: e), e);
139: else
140: logger
141: .error(Translate
142: .get(
143: "backend.driver.test.serializable.isolation.failed",
144: e));
145: }
146: if (logger.isDebugEnabled())
147: logger
148: .debug(Translate
149: .get("backend.driver.test.serializable.isolation.ok"));
150:
151: //
152: // Connection meta data
153: //
154: DatabaseMetaData connectionMetaData;
155: try {
156: connectionMetaData = c.getMetaData();
157: } catch (Exception e) {
158: if (logger.isDebugEnabled())
159: logger.error(Translate.get(
160: "backend.driver.test.metadata.failed", e), e);
161: else
162: logger.error(Translate.get(
163: "backend.driver.test.metadata.failed", e));
164: return isCompliant;
165: }
166: if (logger.isDebugEnabled())
167: logger.debug(Translate
168: .get("backend.driver.test.metadata.ok"));
169:
170: try {
171: this .databaseProductName = connectionMetaData
172: .getDatabaseProductName();
173: logger.info(Translate.get("backend.detected.as",
174: this .databaseProductName));
175: } catch (Exception e) {
176: logger
177: .warn(Translate
178: .get("backend.driver.test.database.productname.failed"));
179: }
180:
181: //
182: // Statement
183: //
184: Statement s;
185: try {
186: s = c.createStatement();
187: } catch (Exception e) {
188: if (logger.isDebugEnabled())
189: logger.error(Translate.get(
190: "backend.driver.test.statement.failed", e), e);
191: else
192: logger.error(Translate.get(
193: "backend.driver.test.statement.failed", e));
194: return isCompliant;
195: }
196:
197: try {
198: if (connectionTestStatement == null) {
199: if (logger.isDebugEnabled())
200: logger.debug(Translate.get(
201: "backend.driver.using.default.statement",
202: DEFAULT_TEST_STATEMENT));
203: connectionTestStatement = DEFAULT_TEST_STATEMENT;
204: }
205: s.execute(connectionTestStatement);
206: } catch (Exception e) {
207: if (logger.isDebugEnabled())
208: logger.error(Translate.get(
209: "backend.driver.test.statement.invalid",
210: new String[] { connectionTestStatement,
211: e.getMessage() }), e);
212: else
213: logger.error(Translate.get(
214: "backend.driver.test.statement.invalid",
215: new String[] { connectionTestStatement,
216: e.getMessage() }));
217: return isCompliant;
218: }
219: if (logger.isDebugEnabled())
220: logger.debug(Translate
221: .get("backend.driver.test.statement.ok"));
222:
223: //
224: // Set cursor name
225: //
226: try {
227: s.setCursorName("testcursor");
228: supportSetCursorName = true;
229: if (logger.isDebugEnabled())
230: logger
231: .debug(Translate
232: .get("backend.driver.statement.setCursorName.ok"));
233: } catch (Exception e1) {
234: logger
235: .warn(Translate
236: .get("backend.driver.statement.setCursorName.failed"));
237: supportSetMaxRows = false;
238: }
239:
240: //
241: // Set fetch size
242: //
243: try {
244: s.setFetchSize(25);
245: supportSetFetchSize = true;
246: if (logger.isDebugEnabled())
247: logger
248: .debug(Translate
249: .get("backend.driver.statement.setFetchSize.ok"));
250: } catch (Exception e1) {
251: logger
252: .warn(Translate
253: .get("backend.driver.statement.setFetchSize.failed"));
254: supportSetMaxRows = false;
255: }
256:
257: //
258: // Set max rows
259: //
260: try {
261: s.setMaxRows(5);
262: supportSetMaxRows = true;
263: if (logger.isDebugEnabled())
264: logger.debug(Translate
265: .get("backend.driver.statement.setMaxRows.ok"));
266: } catch (Exception e1) {
267: logger.warn(Translate
268: .get("backend.driver.statement.setMaxRows.failed"));
269: supportSetMaxRows = false;
270: }
271:
272: //
273: // Get generated keys
274: //
275: try {
276: s.getGeneratedKeys();
277: supportGetGeneratedKeys = true;
278: if (logger.isDebugEnabled())
279: logger
280: .debug(Translate
281: .get("backend.driver.statement.getGeneratedKeys.ok"));
282: } catch (Exception e1) {
283: logger
284: .warn(Translate
285: .get("backend.driver.statement.getGeneratedKeys.failed"));
286: supportGetGeneratedKeys = false;
287: } catch (AbstractMethodError e1) {
288: logger
289: .warn(Translate
290: .get("backend.driver.statement.getGeneratedKeys.failed"));
291: supportGetGeneratedKeys = false;
292: } catch (java.lang.NoSuchMethodError e1) {
293: logger
294: .warn(Translate
295: .get("backend.driver.statement.getGeneratedKeys.failed"));
296: supportGetGeneratedKeys = false;
297: }
298:
299: // Commented out:
300: // A prepared statement can be sent to the DBMS right away to be compiled
301: // Should fine a work around for this test.
302:
303: // PreparedStatement ps;
304: // try
305: // {
306: // ps = c.prepareStatement("INSERT INTO versions VALUES (?,?)");
307: // ps.setInt(1, 10);
308: // ps.setString(2, "just a test");
309: // }
310: // catch (Exception e)
311: // {
312: // logger.warn(Translate.get("backend.driver.prepared.statement.failed"),
313: // e);
314: // }
315: // if (logger.isDebugEnabled())
316: // logger.debug(Translate.get("backend.driver.prepared.statement.ok"));
317:
318: //
319: // Set Query Timeout
320: //
321: try {
322: s.setQueryTimeout(TIMEOUT_VALUE);
323: supportSetQueryTimeout = true;
324: } catch (Exception e) {
325: logger.warn(Translate.get(
326: "backend.driver.setQueryTimeout.failed", e));
327: }
328: if (supportSetQueryTimeout && logger.isDebugEnabled())
329: logger.debug(Translate
330: .get("backend.driver.setQueryTimeout.ok"));
331:
332: //
333: // Get tables
334: //
335: ResultSet rs;
336: try {
337: String[] types = { "TABLE", "VIEW" };
338: rs = connectionMetaData.getTables(null, null, "%", types);
339: } catch (Exception e) {
340: if (logger.isDebugEnabled())
341: logger.error(Translate.get(
342: "backend.driver.metadata.getTables.failed", e),
343: e);
344: else
345: logger.error(Translate.get(
346: "backend.driver.metadata.getTables.failed", e));
347: return isCompliant;
348: }
349: if (logger.isDebugEnabled())
350: logger.debug(Translate
351: .get("backend.driver.metadata.getTables.ok"));
352:
353: //
354: // Get MetaData
355: //
356: java.sql.ResultSetMetaData rsMetaData;
357: try {
358: rsMetaData = rs.getMetaData();
359: } catch (Exception e) {
360: if (logger.isDebugEnabled())
361: logger.error(Translate.get(
362: "backend.driver.resultset.getMetaData.failed",
363: e), e);
364: else
365: logger.error(Translate.get(
366: "backend.driver.resultset.getMetaData.failed",
367: e));
368: return isCompliant;
369: }
370: if (logger.isDebugEnabled())
371: logger.debug(Translate
372: .get("backend.driver.resultset.getMetaData.ok"));
373:
374: //
375: // ResultSet.getObject()
376: //
377: try {
378: if (rs.next() && (rsMetaData.getColumnCount() > 0)) {
379: rs.getObject(1);
380: } else
381: logger
382: .warn(Translate
383: .get("backend.driver.resultset.getObject.unable"));
384: } catch (Exception e) {
385: if (logger.isDebugEnabled())
386: logger
387: .error(
388: Translate
389: .get(
390: "backend.driver.resultset.getObject.failed",
391: e), e);
392: else
393: logger
394: .error(Translate
395: .get(
396: "backend.driver.resultset.getObject.failed",
397: e));
398: return isCompliant;
399: }
400: if (logger.isDebugEnabled())
401: logger.debug(Translate
402: .get("backend.driver.resultset.getObject.ok"));
403:
404: try {
405: rsMetaData.getColumnCount();
406: supportGetColumnCount = true;
407: } catch (Exception e) {
408: if (logger.isDebugEnabled())
409: logger
410: .error(
411: Translate
412: .get(
413: "backend.driver.metadata.getColumnCount.failed",
414: e), e);
415: else
416: logger
417: .error(Translate
418: .get(
419: "backend.driver.metadata.getColumnCount.failed",
420: e));
421: return isCompliant;
422: }
423: if (supportGetColumnCount && logger.isDebugEnabled())
424: logger.debug(Translate
425: .get("backend.driver.metadata.getColumnCount.ok"));
426:
427: try {
428: rsMetaData.getColumnName(1);
429: } catch (Exception e) {
430: if (logger.isDebugEnabled())
431: logger.error(Translate.get(
432: "backend.driver.metadata.getColumnName.failed",
433: e), e);
434: else
435: logger.error(Translate.get(
436: "backend.driver.metadata.getColumnName.failed",
437: e));
438: return isCompliant;
439: }
440: if (logger.isDebugEnabled())
441: logger.debug(Translate
442: .get("backend.driver.metadata.getColumnName.ok"));
443:
444: try {
445: rsMetaData.getTableName(1);
446: supportGetTableName = true;
447: } catch (Exception e) {
448: logger.warn(Translate.get(
449: "backend.driver.metadata.getTableName.failed", e));
450: }
451:
452: if (supportGetTableName && logger.isDebugEnabled())
453: logger.debug(Translate
454: .get("backend.driver.metadata.getTableName.ok"));
455:
456: try {
457: rsMetaData.getColumnDisplaySize(1);
458: supportGetColumnDisplaySize = true;
459: } catch (Exception e) {
460: logger
461: .warn(Translate
462: .get(
463: "backend.driver.metadata.getColumnDisplaySize.failed",
464: e));
465: }
466: if (supportGetColumnDisplaySize && logger.isDebugEnabled())
467: logger
468: .debug(Translate
469: .get("backend.driver.metadata.getColumnDisplaySize.ok"));
470:
471: try {
472: rsMetaData.getColumnType(1);
473: supportGetColumnType = true;
474: } catch (Exception e) {
475: logger.warn(Translate.get(
476: "backend.driver.metadata.getColumnType.failed", e));
477: }
478: if (supportGetColumnType && logger.isDebugEnabled())
479: logger.debug(Translate
480: .get("backend.driver.metadata.getColumnType.ok"));
481:
482: try {
483: rsMetaData.getColumnTypeName(1);
484: supportGetColumnTypeName = true;
485: } catch (Exception e) {
486: logger.warn(Translate.get(
487: "backend.driver.metadata.getColumnTypeName.failed",
488: e));
489: }
490: if (supportGetColumnTypeName && logger.isDebugEnabled())
491: logger
492: .debug(Translate
493: .get("backend.driver.metadata.getColumnTypeName.ok"));
494:
495: try {
496: rsMetaData.getColumnClassName(1);
497: supportGetColumnClassName = true;
498: } catch (Exception e) {
499: logger
500: .warn(Translate
501: .get(
502: "backend.driver.metadata.getColumnClassName.failed",
503: e));
504: }
505: if (supportGetColumnClassName && logger.isDebugEnabled())
506: logger
507: .debug(Translate
508: .get("backend.driver.metadata.getColumnClassName.ok"));
509:
510: isCompliant = true;
511: hasBeenTested = true;
512: return isCompliant;
513: }
514:
515: /**
516: * Returns the databaseProductName value.
517: *
518: * @return Returns the databaseProductName.
519: */
520: public String getDatabaseProductName() {
521: return this .databaseProductName;
522: }
523:
524: /**
525: * @return true if the driver is compliant to the Sequoia requirements
526: */
527: public boolean isCompliant() {
528: return isCompliant;
529: }
530:
531: /**
532: * @return true if the driver supports getGeneratedKeys
533: */
534: public boolean supportGetGeneratedKeys() {
535: return supportGetGeneratedKeys;
536: }
537:
538: /**
539: * @return true if the driver supports getColumnClassName
540: */
541: public boolean supportGetColumnClassName() {
542: return supportGetColumnClassName;
543: }
544:
545: /**
546: * @return true if the driver supports getColumnCount
547: */
548: public boolean supportGetColumnCount() {
549: return supportGetColumnCount;
550: }
551:
552: /**
553: * @return true if the driver supports getColumnDisplaySize
554: */
555: public boolean supportGetColumnDisplaySize() {
556: return supportGetColumnDisplaySize;
557: }
558:
559: /**
560: * @return true if the driver supports getColumnType
561: */
562: public boolean supportGetColumnType() {
563: return supportGetColumnType;
564: }
565:
566: /**
567: * @return true if the driver supports getColumnTypeName
568: */
569: public boolean supportGetColumnTypeName() {
570: return supportGetColumnTypeName;
571: }
572:
573: /**
574: * @return true if the driver supports getTableName
575: */
576: public boolean supportGetTableName() {
577: return supportGetTableName;
578: }
579:
580: /**
581: * Returns the supportSetCursorName value.
582: *
583: * @return Returns the supportSetCursorName.
584: */
585: public boolean supportSetCursorName() {
586: return supportSetCursorName;
587: }
588:
589: /**
590: * Returns the supportSetFetchSize value.
591: *
592: * @return Returns the supportSetFetchSize.
593: */
594: public boolean supportSetFetchSize() {
595: return supportSetFetchSize;
596: }
597:
598: /**
599: * Returns the supportSerializableIsolation value.
600: *
601: * @return Returns the supportSerializableIsolation.
602: */
603: public final boolean supportSerializableIsolation() {
604: return supportSerializableIsolation;
605: }
606:
607: /**
608: * @return true if the driver supports Statement.setMaxRows
609: */
610: public boolean supportSetMaxRows() {
611: return supportSetMaxRows;
612: }
613:
614: /**
615: * @return true if the driver supports setQueryTimeout
616: */
617: public boolean supportSetQueryTimeout() {
618: return supportSetQueryTimeout;
619: }
620: }
|