/******************************************************************************* * Pentaho Big Data * <p> * Copyright (C) 2002-2017 by Pentaho : http://www.pentaho.com * <p> * ****************************************************************************** * <p> * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. ******************************************************************************/ package org.pentaho.hadoop.shim.common; import org.pentaho.hadoop.shim.common.invocationhandler.DriverInvocationHandler; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Proxy; import java.sql.DatabaseMetaData; import java.sql.Date; import java.sql.Driver; import java.sql.ResultSet; import java.sql.Statement; /** * DriverProxyInvocationChain is a temporary solution for interacting with Hive drivers. At the time this class was * added, many methods from the JDBC API had not yet been implemented and would instead throw SQLExceptions. Also, some * methods such as HiveDatabaseMetaData.getTables() did not return any values. For these reasons, a dynamic proxy chain * was put in place, in order to intercept methods that would otherwise not function properly, and instead inject * working and/or default functionality. * <p> * The "chain" part of this class is a result of not having access to all the necessary objects at driver creation time. * For this reason, we have to intercept methods that would return such objects, then create and return a proxy to those * objects. There are a number of objects and methods to which this applies, so the result is a "chain" of getting * access to objects via a proxy, then returning a proxy to those objects, which in turn may return proxied objects for * its methods, and so on. * <p> * The large amount of reflection used here is because not all Hadoop distributions support both Hive and Hive 2. Thus * before proxying or anything, we need to make sure we have the classes we need at runtime. */ public class DriverProxyInvocationChain { public static final String PENTAHO_CURRENT_DBNAME = "pentaho.current.dbname"; /** * The initialized. */ private static boolean initialized = false; public static final Date NULL_DATE = new Date( 0 ) { private static final long serialVersionUID = 1L; @Override public String toString() { return "NULL"; } }; /** * The hive1 db meta data class. */ protected static Class<? extends DatabaseMetaData> hive1DbMetaDataClass = null; /** * The hive2 db meta data class. */ protected static Class<? extends DatabaseMetaData> hive2DbMetaDataClass = null; /** * The hive1 result set class. */ protected static Class<? extends ResultSet> hive1ResultSetClass = null; /** * The hive2 result set class. */ protected static Class<? extends ResultSet> hive2ResultSetClass = null; /** * The hive1 client class. */ protected static Class<?> hive1ClientClass = null; /** * The hive2 client class. */ protected static Class<?> hive2ClientClass = null; /** * The hive1 statement class. */ protected static Class<? extends Statement> hive1StatementClass = null; /** * The hive2 statement class. */ protected static Class<? extends Statement> hive2StatementClass = null; public static Class<? extends DatabaseMetaData> getHive1DbMetaDataClass() { return hive1DbMetaDataClass; } public static void setHive1DbMetaDataClass( Class<? extends DatabaseMetaData> hive1DbMetaDataClass ) { DriverProxyInvocationChain.hive1DbMetaDataClass = hive1DbMetaDataClass; } public static Class<? extends DatabaseMetaData> getHive2DbMetaDataClass() { return hive2DbMetaDataClass; } public static void setHive2DbMetaDataClass( Class<? extends DatabaseMetaData> hive2DbMetaDataClass ) { DriverProxyInvocationChain.hive2DbMetaDataClass = hive2DbMetaDataClass; } public static Class<? extends ResultSet> getHive1ResultSetClass() { return hive1ResultSetClass; } public static void setHive1ResultSetClass( Class<? extends ResultSet> hive1ResultSetClass ) { DriverProxyInvocationChain.hive1ResultSetClass = hive1ResultSetClass; } public static Class<? extends ResultSet> getHive2ResultSetClass() { return hive2ResultSetClass; } public static void setHive2ResultSetClass( Class<? extends ResultSet> hive2ResultSetClass ) { DriverProxyInvocationChain.hive2ResultSetClass = hive2ResultSetClass; } public static Class<?> getHive1ClientClass() { return hive1ClientClass; } public static void setHive1ClientClass( Class<?> hive1ClientClass ) { DriverProxyInvocationChain.hive1ClientClass = hive1ClientClass; } public static Class<?> getHive2ClientClass() { return hive2ClientClass; } public static void setHive2ClientClass( Class<?> hive2ClientClass ) { DriverProxyInvocationChain.hive2ClientClass = hive2ClientClass; } public static Class<? extends Statement> getHive1StatementClass() { return hive1StatementClass; } public static void setHive1StatementClass( Class<? extends Statement> hive1StatementClass ) { DriverProxyInvocationChain.hive1StatementClass = hive1StatementClass; } public static Class<? extends Statement> getHive2StatementClass() { return hive2StatementClass; } public static void setHive2StatementClass( Class<? extends Statement> hive2StatementClass ) { DriverProxyInvocationChain.hive2StatementClass = hive2StatementClass; } protected static ClassLoader driverProxyClassLoader = null; /** * Gets the proxy. * * @param intf the intf * @param obj the obj * @return the proxy */ public static Driver getProxy( Class<? extends Driver> intf, final Driver obj ) { return getProxy( intf, obj, DriverInvocationHandler.class ); } /** * Gets the proxy. * * @param intf the intf * @param obj the obj * @return the proxy */ public static Driver getProxy( Class<? extends Driver> intf, final Driver obj, Class driverInvocationHandlerClass ) { driverProxyClassLoader = obj.getClass().getClassLoader(); if ( !initialized ) { init(); } InvocationHandler invocationHandler = new DriverInvocationHandler( obj ); try { Constructor driverHandler = driverInvocationHandlerClass.getConstructor( Driver.class ); invocationHandler = (InvocationHandler) driverHandler.newInstance( Driver.class.cast( obj ) ); } catch ( NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e ) { // ignored } return (Driver) Proxy.newProxyInstance( driverProxyClassLoader, new Class[] { intf }, invocationHandler ); } /** * Initializes the Driver proxy chain */ @SuppressWarnings( { "unchecked" } ) protected static void init() { // Get all the Hive 1 and Hive 2 classes we'll need to call methods on later. ClassLoader cl = driverProxyClassLoader; try { hive1DbMetaDataClass = (Class<? extends DatabaseMetaData>) Class.forName( "org.apache.hadoop.hive.jdbc.HiveDatabaseMetaData", false, cl ); hive1ResultSetClass = (Class<? extends ResultSet>) Class.forName( "org.apache.hadoop.hive.jdbc.HiveQueryResultSet", false, cl ); hive1ClientClass = Class.forName( "org.apache.hadoop.hive.service.HiveInterface", false, cl ); hive1StatementClass = (Class<? extends Statement>) Class.forName( "org.apache.hadoop.hive.jdbc.HiveStatement", false, cl ); } catch ( ClassNotFoundException cnfe ) { //ignored } try { hive2DbMetaDataClass = (Class<? extends DatabaseMetaData>) Class.forName( "org.apache.hive.jdbc.HiveDatabaseMetaData", false, cl ); hive2ResultSetClass = (Class<? extends ResultSet>) Class.forName( "org.apache.hive.jdbc.HiveQueryResultSet", false, cl ); hive2ClientClass = Class.forName( "org.apache.hive.service.cli.thrift.TCLIService$Iface", false, cl ); hive2StatementClass = (Class<? extends Statement>) Class.forName( "org.apache.hive.jdbc.HiveStatement", false, cl ); } catch ( ClassNotFoundException cnfe ) { //ignored } initialized = true; } protected static boolean isInitialized() { return initialized; } public static void setInitialized( boolean initialized ) { DriverProxyInvocationChain.initialized = initialized; } }