/** * Copyright (C) 2016-2019 Expedia, Inc. * * 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 com.hotels.bdp.waggledance.client.compatibility; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import org.apache.hadoop.hive.metastore.api.ThriftHiveMetastore; import org.apache.thrift.TApplicationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.hotels.bdp.waggledance.client.CloseableThriftHiveMetastoreIface; public class HiveCompatibleThriftHiveMetastoreIfaceFactory { private static final Logger log = LoggerFactory.getLogger(HiveCompatibleThriftHiveMetastoreIfaceFactory.class); private static class ThriftMetaStoreClientInvocationHandler implements InvocationHandler { private final ThriftHiveMetastore.Client delegate; private final HiveThriftMetaStoreIfaceCompatibility compatibility; ThriftMetaStoreClientInvocationHandler( ThriftHiveMetastore.Client delegate, HiveThriftMetaStoreIfaceCompatibility compatibility) { this.delegate = delegate; this.compatibility = compatibility; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { return method.invoke(delegate, args); } catch (InvocationTargetException delegateException) { try { log.info("Couldn't invoke method {}", method.toGenericString()); if (delegateException.getCause().getClass().isAssignableFrom(TApplicationException.class)) { log.info("Attempting to invoke with {}", compatibility.getClass().getName()); return invokeCompatibility(method, args); } } catch (InvocationTargetException compatibilityException) { if (compatibilityException.getCause().getClass().isAssignableFrom(TApplicationException.class)) { log .warn( "Invocation of compatibility for metastore client method {} failed. Will rethrow original exception, logging exception from compatibility layer", method.getName(), compatibilityException); } else { throw compatibilityException.getCause(); } } catch (Throwable t) { log .warn( "Unable to invoke compatibility for metastore client method {}. Will rethrow original exception, logging exception from invocation handler", method.getName(), t); } throw delegateException.getCause(); } } private Object invokeCompatibility(Method method, Object[] args) throws Throwable { Class<?>[] argTypes = getTypes(args); Method compatibilityMethod = compatibility.getClass().getMethod(method.getName(), argTypes); return compatibilityMethod.invoke(compatibility, args); } private Class<?>[] getTypes(Object[] args) { if (args == null) { return new Class<?>[] {}; } Class<?>[] argTypes = new Class<?>[args.length]; for (int i = 0; i < args.length; ++i) { argTypes[i] = args[i].getClass(); } return argTypes; } } public CloseableThriftHiveMetastoreIface newInstance(ThriftHiveMetastore.Client delegate) { HiveThriftMetaStoreIfaceCompatibility compatibility = new HiveThriftMetaStoreIfaceCompatibility1xx(delegate); return newInstance(delegate, compatibility); } private CloseableThriftHiveMetastoreIface newInstance( ThriftHiveMetastore.Client delegate, HiveThriftMetaStoreIfaceCompatibility compatibility) { ClassLoader classLoader = CloseableThriftHiveMetastoreIface.class.getClassLoader(); Class<?>[] interfaces = new Class<?>[] { CloseableThriftHiveMetastoreIface.class }; ThriftMetaStoreClientInvocationHandler handler = new ThriftMetaStoreClientInvocationHandler(delegate, compatibility); return (CloseableThriftHiveMetastoreIface) Proxy.newProxyInstance(classLoader, interfaces, handler); } }