/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2017 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.invocationhandler;
/**
* User: Dzmitry Stsiapanau Date: 01/17/2017 Time: 15:15
*/
import org.pentaho.hadoop.shim.common.HiveSQLUtils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
/**
* ConnectionInvocationHandler is a proxy handler class for java.sql.Connection. However the code in this file is
* specifically for handling Hive JDBC calls, and therefore should not be used to proxy any other JDBC objects besides
* those provided by Hive.
*/
public class ConnectionInvocationHandler implements InvocationHandler {
/**
* The "real" connection.
*/
Connection connection;
/**
* Instantiates a new connection invocation handler.
*
* @param obj the obj
*/
public ConnectionInvocationHandler( Connection obj ) {
connection = obj;
}
/**
* Intercepts methods called on the Connection to possibly perform alternate processing.
*
* @param proxy the proxy
* @param method the method
* @param args the args
* @return the object
* @throws Throwable the throwable
*/
@Override
public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable {
Object o = null;
try {
if ( "prepareStatement".equals( method.getName() ) ) {
String sql = (String) args[ 0 ];
args[ 0 ] = HiveSQLUtils.processSQLString( sql );
}
o = method.invoke( connection, args );
} catch ( Throwable t ) {
if ( t instanceof InvocationTargetException ) {
Throwable cause = t.getCause();
if ( cause instanceof SQLException ) {
String methodName = method.getName();
if ( cause.getMessage().startsWith( "Method not supported" )
|| cause.getMessage().equals( "enabling autocommit is not supported" ) ) {
if ( "createStatement".equals( methodName ) ) {
o = createStatement( connection, args );
} else if ( "isReadOnly".equals( methodName ) ) {
o = Boolean.FALSE;
} else if ( "setReadOnly".equals( methodName ) ) {
o = (Void) null;
} else if ( "setAutoCommit".equals( methodName ) ) {
o = (Void) null;
} else {
throw cause;
}
} else {
throw cause;
}
} else {
throw cause;
}
} else {
throw t;
}
}
if ( o instanceof DatabaseMetaData ) {
DatabaseMetaData dbmd = (DatabaseMetaData) o;
// Intercept the DatabaseMetaData object so we can proxy that too
return (DatabaseMetaData) Proxy.newProxyInstance( dbmd.getClass().getClassLoader(),
new Class[] { DatabaseMetaData.class }, new DatabaseMetaDataInvocationHandler( dbmd, this ) );
} else if ( o instanceof PreparedStatement ) {
PreparedStatement st = (PreparedStatement) o;
// Intercept the Statement object so we can proxy that too
return (PreparedStatement) Proxy.newProxyInstance( st.getClass().getClassLoader(),
new Class[] { PreparedStatement.class }, new CaptureResultSetInvocationHandler<PreparedStatement>( st ) );
} else if ( o instanceof Statement ) {
Statement st = (Statement) o;
// Intercept the Statement object so we can proxy that too
return (Statement) Proxy.newProxyInstance( st.getClass().getClassLoader(),
new Class[] { Statement.class }, new CaptureResultSetInvocationHandler<Statement>( st ) );
} else {
return o;
}
}
/**
* Creates a statement for the given Connection with the specified arguments
*
* @param c the connection object
* @param args the arguments
* @return the statement
* @throws SQLException the sQL exception
* @see java.sql.Connection#createStatement(int, int)
*/
public Statement createStatement( Connection c, Object[] args ) throws SQLException {
if ( c.isClosed() ) {
throw new SQLException( "Can't create Statement, connection is closed " );
}
/* Ignore these for now -- this proxy stuff should go away anyway when the fixes are made to Apache Hive
int resultSetType = (Integer)args[0];
int resultSetConcurrency = (Integer)args[1];
if(resultSetType != ResultSet.TYPE_FORWARD_ONLY) {
throw new SQLException(
"Invalid parameter to createStatement() only TYPE_FORWARD_ONLY is supported
("+resultSetType+"!="+ResultSet.TYPE_FORWARD_ONLY+")");
}
if(resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) {
throw new SQLException(
"Invalid parameter to createStatement() only CONCUR_READ_ONLY is supported");
}*/
return c.createStatement();
}
}