001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2007-2013 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.olap4j;
011
012import mondrian.rolap.RolapConnectionProperties;
013
014import java.sql.*;
015import java.util.*;
016import java.util.logging.Logger;
017
018/**
019 * Olap4j driver for Mondrian.
020 *
021 * <p>Since olap4j is a superset of JDBC, you register this driver as you would
022 * any JDBC driver:
023 *
024 * <blockquote>
025 * <code>Class.forName("mondrian.olap4j.MondrianOlap4jDriver");</code>
026 * </blockquote>
027 *
028 * <p>Then create a connection using a URL with the prefix "jdbc:mondrian:".
029 * For example,
030 *
031 * <blockquote>
032 * <code>import java.sql.Connection;<br/>
033 * import java.sql.DriverManager;<br/>
034 * import org.olap4j.OlapConnection;<br/>
035 * <br/>
036 * Connection connection =<br/>
037 * &nbsp;&nbsp;DriverManager.getConnection(<br/>
038 * &nbsp;&nbsp;&nbsp;&nbsp;"jdbc:mondrian:Jdbc=jdbc:odbc:MondrianFoodMart;
039 * Catalog=file:/mondrian/demo/FoodMart.xml;
040 * JdbcDrivers=sun.jdbc.odbc.JdbcOdbcDriver");<br/>
041 * OlapConnection olapConnection =<br/>
042 * &nbsp;&nbsp;connection.unwrap(OlapConnection.class);</code>
043 * </blockquote>
044 *
045 * <p>Note how we use the {@link Connection#unwrap(Class)} method to down-cast
046 * the JDBC connection object to the extension {@link org.olap4j.OlapConnection}
047 * object. This method is only available in the JDBC 4.0 (JDK 1.6 onwards).
048 *
049 * <h3>Connection properties</h3>
050 *
051 * <p>The driver supports the same set of properties as a traditional mondrian
052 * connection. See {@link mondrian.rolap.RolapConnectionProperties}.
053 *
054 * <h3>Catalogs and schemas</h3>
055 *
056 * <p>Mondrian has a sole catalog, called "LOCALDB". You will get an error
057 * if you attempt to use {@link java.sql.Connection#setCatalog(String)} to set
058 * it to anything else.
059 *
060 * @author jhyde
061 * @since May 22, 2007
062 */
063public class MondrianOlap4jDriver implements Driver {
064    protected final Factory factory;
065
066    static {
067        try {
068            register();
069        } catch (SQLException e) {
070            e.printStackTrace();
071        }
072    }
073
074    /**
075     * Creates a MondrianOlap4jDriver.
076     */
077    public MondrianOlap4jDriver() {
078        this.factory = createFactory();
079    }
080
081    private static Factory createFactory() {
082        final String factoryClassName = getFactoryClassName();
083        try {
084            // Cannot use ClassResolver here, because factory's constructor has
085            // package access.
086            final Class<?> clazz = Class.forName(factoryClassName);
087            return (Factory) clazz.newInstance();
088        } catch (ClassNotFoundException e) {
089            throw new RuntimeException(e);
090        } catch (IllegalAccessException e) {
091            throw new RuntimeException(e);
092        } catch (InstantiationException e) {
093            throw new RuntimeException(e);
094        }
095    }
096
097    private static String getFactoryClassName() {
098        try {
099            // If java.sql.PseudoColumnUsage is present, we are running JDBC 4.1
100            // or later.
101            Class.forName("java.sql.PseudoColumnUsage");
102            return "mondrian.olap4j.FactoryJdbc41Impl";
103        } catch (ClassNotFoundException e) {
104            // java.sql.PseudoColumnUsage is not present. This means we are
105            // running JDBC 4.0 or earlier.
106            try {
107                Class.forName("java.sql.Wrapper");
108                return "mondrian.olap4j.FactoryJdbc4Impl";
109            } catch (ClassNotFoundException e2) {
110                // java.sql.Wrapper is not present. This means we are running
111                // JDBC 3.0 or earlier (probably JDK 1.5). Load the JDBC 3.0
112                // factory.
113                return "mondrian.olap4j.FactoryJdbc3Impl";
114            }
115        }
116    }
117
118    /**
119     * Registers an instance of MondrianOlap4jDriver.
120     *
121     * <p>Called implicitly on class load, and implements the traditional
122     * 'Class.forName' way of registering JDBC drivers.
123     *
124     * @throws SQLException on error
125     */
126    private static void register() throws SQLException {
127        DriverManager.registerDriver(new MondrianOlap4jDriver());
128    }
129
130    public Connection connect(String url, Properties info) throws SQLException {
131        if (!MondrianOlap4jConnection.acceptsURL(url)) {
132            return null;
133        }
134        return factory.newConnection(this, url, info);
135    }
136
137    public boolean acceptsURL(String url) throws SQLException {
138        return MondrianOlap4jConnection.acceptsURL(url);
139    }
140
141    public DriverPropertyInfo[] getPropertyInfo(
142        String url, Properties info) throws SQLException
143    {
144        List<DriverPropertyInfo> list = new ArrayList<DriverPropertyInfo>();
145
146        // First, add the contents of info
147        for (Map.Entry<Object, Object> entry : info.entrySet()) {
148            list.add(
149                new DriverPropertyInfo(
150                    (String) entry.getKey(),
151                    (String) entry.getValue()));
152        }
153        // Next, add property defns not mentioned in info
154        for (RolapConnectionProperties p : RolapConnectionProperties.values()) {
155            if (info.containsKey(p.name())) {
156                continue;
157            }
158            list.add(
159                new DriverPropertyInfo(
160                    p.name(),
161                    null));
162        }
163        return list.toArray(new DriverPropertyInfo[list.size()]);
164    }
165
166    // JDBC 4.1 support (JDK 1.7 and higher)
167    public Logger getParentLogger() {
168        return Logger.getLogger("");
169    }
170
171    /**
172     * Returns the driver name. Not in the JDBC API.
173     * @return Driver name
174     */
175    String getName() {
176        return MondrianOlap4jDriverVersion.NAME;
177    }
178
179    /**
180     * Returns the driver version. Not in the JDBC API.
181     * @return Driver version
182     */
183    String getVersion() {
184        return MondrianOlap4jDriverVersion.VERSION;
185    }
186
187    public int getMajorVersion() {
188        return MondrianOlap4jDriverVersion.MAJOR_VERSION;
189    }
190
191    public int getMinorVersion() {
192        return MondrianOlap4jDriverVersion.MINOR_VERSION;
193    }
194
195    public boolean jdbcCompliant() {
196        return false;
197    }
198}
199
200// End MondrianOlap4jDriver.java