1 /* 2 * Copyright 2010 The Apache Software Foundation 3 * 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21 package org.apache.hadoop.hbase.client.coprocessor; 22 23 import org.apache.commons.lang.reflect.MethodUtils; 24 import org.apache.commons.logging.Log; 25 import org.apache.commons.logging.LogFactory; 26 import org.apache.hadoop.hbase.ipc.CoprocessorProtocol; 27 28 import java.io.IOException; 29 import java.lang.reflect.InvocationHandler; 30 import java.lang.reflect.InvocationTargetException; 31 import java.lang.reflect.Method; 32 import java.lang.reflect.Proxy; 33 34 35 /** 36 * A collection of interfaces and utilities used for interacting with custom RPC 37 * interfaces exposed by Coprocessors. 38 */ 39 public abstract class Batch { 40 private static Log LOG = LogFactory.getLog(Batch.class); 41 42 /** 43 * Creates a new {@link Batch.Call} instance that invokes a method 44 * with the given parameters and returns the result. 45 * 46 * <p> 47 * Note that currently the method is naively looked up using the method name 48 * and class types of the passed arguments, which means that 49 * <em>none of the arguments can be <code>null</code></em>. 50 * For more flexibility, see 51 * {@link Batch#forMethod(java.lang.reflect.Method, Object...)}. 52 * </p> 53 * 54 * @param protocol the protocol class being called 55 * @param method the method name 56 * @param args zero or more arguments to be passed to the method 57 * (individual args cannot be <code>null</code>!) 58 * @param <T> the class type of the protocol implementation being invoked 59 * @param <R> the return type for the method call 60 * @return a {@code Callable} instance that will invoke the given method 61 * and return the results 62 * @throws NoSuchMethodException if the method named, with the given argument 63 * types, cannot be found in the protocol class 64 * @see Batch#forMethod(java.lang.reflect.Method, Object...) 65 * @see org.apache.hadoop.hbase.client.HTable#coprocessorExec(Class, byte[], byte[], org.apache.hadoop.hbase.client.coprocessor.Batch.Call, org.apache.hadoop.hbase.client.coprocessor.Batch.Callback) 66 */ 67 public static <T extends CoprocessorProtocol,R> Call<T,R> forMethod( 68 final Class<T> protocol, final String method, final Object... args) 69 throws NoSuchMethodException { 70 Class[] types = new Class[args.length]; 71 for (int i=0; i<args.length; i++) { 72 if (args[i] == null) { 73 throw new NullPointerException("Method argument cannot be null"); 74 } 75 types[i] = args[i].getClass(); 76 } 77 78 Method m = MethodUtils.getMatchingAccessibleMethod(protocol, method, types); 79 if (m == null) { 80 throw new NoSuchMethodException("No matching method found for '" + 81 method + "'"); 82 } 83 84 m.setAccessible(true); 85 return forMethod(m, args); 86 } 87 88 /** 89 * Creates a new {@link Batch.Call} instance that invokes a method 90 * with the given parameters and returns the result. 91 * 92 * @param method the method reference to invoke 93 * @param args zero or more arguments to be passed to the method 94 * @param <T> the class type of the protocol implementation being invoked 95 * @param <R> the return type for the method call 96 * @return a {@code Callable} instance that will invoke the given method and 97 * return the results 98 * @see org.apache.hadoop.hbase.client.HTable#coprocessorExec(Class, byte[], byte[], org.apache.hadoop.hbase.client.coprocessor.Batch.Call, org.apache.hadoop.hbase.client.coprocessor.Batch.Callback) 99 */ 100 public static <T extends CoprocessorProtocol,R> Call<T,R> forMethod( 101 final Method method, final Object... args) { 102 return new Call<T,R>() { 103 public R call(T instance) throws IOException { 104 try { 105 if (Proxy.isProxyClass(instance.getClass())) { 106 InvocationHandler invoker = Proxy.getInvocationHandler(instance); 107 return (R)invoker.invoke(instance, method, args); 108 } else { 109 LOG.warn("Non proxied invocation of method '"+method.getName()+"'!"); 110 return (R)method.invoke(instance, args); 111 } 112 } 113 catch (IllegalAccessException iae) { 114 throw new IOException("Unable to invoke method '"+ 115 method.getName()+"'", iae); 116 } 117 catch (InvocationTargetException ite) { 118 throw new IOException(ite.toString(), ite); 119 } 120 catch (Throwable t) { 121 throw new IOException(t.toString(), t); 122 } 123 } 124 }; 125 } 126 127 /** 128 * Defines a unit of work to be executed. 129 * 130 * <p> 131 * When used with 132 * {@link org.apache.hadoop.hbase.client.HTable#coprocessorExec(Class, byte[], byte[], org.apache.hadoop.hbase.client.coprocessor.Batch.Call, org.apache.hadoop.hbase.client.coprocessor.Batch.Callback)} 133 * the implementations {@link Batch.Call#call(Object)} method will be invoked 134 * with a proxy to the 135 * {@link org.apache.hadoop.hbase.ipc.CoprocessorProtocol} 136 * sub-type instance. 137 * </p> 138 * @see org.apache.hadoop.hbase.client.coprocessor 139 * @see org.apache.hadoop.hbase.client.HTable#coprocessorExec(Class, byte[], byte[], org.apache.hadoop.hbase.client.coprocessor.Batch.Call) 140 * @see org.apache.hadoop.hbase.client.HTable#coprocessorExec(Class, byte[], byte[], org.apache.hadoop.hbase.client.coprocessor.Batch.Call, org.apache.hadoop.hbase.client.coprocessor.Batch.Callback) 141 * @param <T> the instance type to be passed to 142 * {@link Batch.Call#call(Object)} 143 * @param <R> the return type from {@link Batch.Call#call(Object)} 144 */ 145 public static interface Call<T,R> { 146 public R call(T instance) throws IOException; 147 } 148 149 /** 150 * Defines a generic callback to be triggered for each {@link Batch.Call#call(Object)} 151 * result. 152 * 153 * <p> 154 * When used with 155 * {@link org.apache.hadoop.hbase.client.HTable#coprocessorExec(Class, byte[], byte[], org.apache.hadoop.hbase.client.coprocessor.Batch.Call, org.apache.hadoop.hbase.client.coprocessor.Batch.Callback)}, 156 * the implementation's {@link Batch.Callback#update(byte[], byte[], Object)} 157 * method will be called with the {@link Batch.Call#call(Object)} return value 158 * from each region in the selected range. 159 * </p> 160 * @param <R> the return type from the associated {@link Batch.Call#call(Object)} 161 * @see org.apache.hadoop.hbase.client.HTable#coprocessorExec(Class, byte[], byte[], org.apache.hadoop.hbase.client.coprocessor.Batch.Call, org.apache.hadoop.hbase.client.coprocessor.Batch.Callback) 162 */ 163 public static interface Callback<R> { 164 public void update(byte[] region, byte[] row, R result); 165 } 166 }