/* * This software is subject to the terms of the Eclipse Public License v1.0 * Agreement, available at the following URL: * http://www.eclipse.org/legal/epl-v10.html. * You must accept the terms of that agreement to use this software. * * Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved. */ package mondrian.spi.impl; import mondrian.olap.Util; import mondrian.spi.Dialect; import mondrian.spi.DialectFactory; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.sql.*; import javax.sql.DataSource; /** * Implementation of {@link mondrian.spi.DialectFactory} for subclasses * of {@link mondrian.spi.impl.JdbcDialectImpl}. * * <p>Assumes that the dialect has a public constructor that takes a * {@link java.sql.Connection} as a parameter. */ public class JdbcDialectFactory implements DialectFactory { private final Dialect.DatabaseProduct databaseProduct; private final Constructor<? extends JdbcDialectImpl> constructor; /** * Creates a JdbcDialectFactory. * * @param dialectClass Dialect class * @param databaseProduct Database type (e.g. Oracle) if this is a * common RDBMS, or null if it is an uncommon one */ public JdbcDialectFactory( Class<? extends JdbcDialectImpl> dialectClass, Dialect.DatabaseProduct databaseProduct) { this.databaseProduct = databaseProduct; try { constructor = dialectClass.getConstructor(Connection.class); } catch (NoSuchMethodException e) { throw Util.newError( e, "Class does not contain constructor " + "'public <init>(Connection connection)' required " + "for subclasses of JdbcDialectImpl"); } } /** * Creates a temporary connection and calls * {@link mondrian.spi.DialectFactory#createDialect(javax.sql.DataSource, java.sql.Connection)}. * * <p>Helper method, called when {@code createDialect} is called without a * {@link java.sql.Connection} and the dialect factory * cannot create a dialect with {@link javax.sql.DataSource} alone. * * <p>It is a user error if {@code dataSource} is null (since this implies * that {@code createDialect} was called with {@code dataSource} and * {@code connection} both null.</p> * * @param factory Dialect factory * @param dataSource Data source, must not be null * @return Dialect, or null if factory cannot create suitable dialect */ public static Dialect createDialectHelper( DialectFactory factory, DataSource dataSource) { if (dataSource == null) { throw new IllegalArgumentException( "Must specify either dataSource or connection"); } Connection connection = null; try { connection = dataSource.getConnection(); if (connection == null) { // DataSource.getConnection does not return null. But // a null value here would cause infinite recursion, so // let's be cautious. throw new IllegalArgumentException(); } final Dialect dialect = factory.createDialect(dataSource, connection); // Close the connection in such a way that if there is a // SQLException, // (a) we propagate the exception, // (b) we don't try to close the connection again. Connection connection2 = connection; connection = null; connection2.close(); return dialect; } catch (SQLException e) { throw Util.newError( e, "Error while creating dialect"); } finally { if (connection != null) { try { connection.close(); } catch (SQLException e) { // ignore } } } } public Dialect createDialect(DataSource dataSource, Connection connection) { // If connection is null, create a temporary connection and // recursively call this method. if (connection == null) { return createDialectHelper(this, dataSource); } assert connection != null; if (acceptsConnection(connection)) { try { return constructor.newInstance(connection); } catch (InstantiationException e) { throw Util.newError( e, "Error while instantiating dialect"); } catch (IllegalAccessException e) { throw Util.newError( e, "Error while instantiating dialect"); } catch (InvocationTargetException e) { throw Util.newError( e, "Error while instantiating dialect"); } } return null; } /** * Returns whether this dialect is suitable for the given connection. * * @param connection Connection * @return Whether suitable */ protected boolean acceptsConnection(Connection connection) { try { final DatabaseMetaData metaData = connection.getMetaData(); final String productName = metaData.getDatabaseProductName(); final String productVersion = metaData.getDatabaseProductVersion(); final Dialect.DatabaseProduct product = JdbcDialectImpl.getProduct(productName, productVersion); return product == this.databaseProduct; } catch (SQLException e) { throw Util.newError( e, "Error while instantiating dialect"); } } } // End JdbcDialectFactory.java