Java tutorial
/* * Copyright 2010 The Apache Software Foundation * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.hadoop.hbase.client.coprocessor; import org.apache.commons.lang.reflect.MethodUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.ipc.CoprocessorProtocol; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * A collection of interfaces and utilities used for interacting with custom RPC * interfaces exposed by Coprocessors. */ public abstract class Batch { private static Log LOG = LogFactory.getLog(Batch.class); /** * Creates a new {@link Batch.Call} instance that invokes a method * with the given parameters and returns the result. * * <p> * Note that currently the method is naively looked up using the method name * and class types of the passed arguments, which means that * <em>none of the arguments can be <code>null</code></em>. * For more flexibility, see * {@link Batch#forMethod(java.lang.reflect.Method, Object...)}. * </p> * * @param protocol the protocol class being called * @param method the method name * @param args zero or more arguments to be passed to the method * (individual args cannot be <code>null</code>!) * @param <T> the class type of the protocol implementation being invoked * @param <R> the return type for the method call * @return a {@code Callable} instance that will invoke the given method * and return the results * @throws NoSuchMethodException if the method named, with the given argument * types, cannot be found in the protocol class * @see Batch#forMethod(java.lang.reflect.Method, Object...) * @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) */ public static <T extends CoprocessorProtocol, R> Call<T, R> forMethod(final Class<T> protocol, final String method, final Object... args) throws NoSuchMethodException { Class[] types = new Class[args.length]; for (int i = 0; i < args.length; i++) { if (args[i] == null) { throw new NullPointerException("Method argument cannot be null"); } types[i] = args[i].getClass(); } Method m = MethodUtils.getMatchingAccessibleMethod(protocol, method, types); if (m == null) { throw new NoSuchMethodException("No matching method found for '" + method + "'"); } m.setAccessible(true); return forMethod(m, args); } /** * Creates a new {@link Batch.Call} instance that invokes a method * with the given parameters and returns the result. * * @param method the method reference to invoke * @param args zero or more arguments to be passed to the method * @param <T> the class type of the protocol implementation being invoked * @param <R> the return type for the method call * @return a {@code Callable} instance that will invoke the given method and * return the results * @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) */ public static <T extends CoprocessorProtocol, R> Call<T, R> forMethod(final Method method, final Object... args) { return new Call<T, R>() { public R call(T instance) throws IOException { try { if (Proxy.isProxyClass(instance.getClass())) { InvocationHandler invoker = Proxy.getInvocationHandler(instance); return (R) invoker.invoke(instance, method, args); } else { LOG.warn("Non proxied invocation of method '" + method.getName() + "'!"); return (R) method.invoke(instance, args); } } catch (IllegalAccessException iae) { throw new IOException("Unable to invoke method '" + method.getName() + "'", iae); } catch (InvocationTargetException ite) { throw new IOException(ite.toString(), ite); } catch (Throwable t) { throw new IOException(t.toString(), t); } } }; } /** * Defines a unit of work to be executed. * * <p> * When used with * {@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)} * the implementations {@link Batch.Call#call(Object)} method will be invoked * with a proxy to the * {@link org.apache.hadoop.hbase.ipc.CoprocessorProtocol} * sub-type instance. * </p> * @see org.apache.hadoop.hbase.client.coprocessor * @see org.apache.hadoop.hbase.client.HTable#coprocessorExec(Class, byte[], byte[], org.apache.hadoop.hbase.client.coprocessor.Batch.Call) * @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) * @param <T> the instance type to be passed to * {@link Batch.Call#call(Object)} * @param <R> the return type from {@link Batch.Call#call(Object)} */ public static interface Call<T, R> { public R call(T instance) throws IOException; } /** * Defines a generic callback to be triggered for each {@link Batch.Call#call(Object)} * result. * * <p> * When used with * {@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)}, * the implementation's {@link Batch.Callback#update(byte[], byte[], Object)} * method will be called with the {@link Batch.Call#call(Object)} return value * from each region in the selected range. * </p> * @param <R> the return type from the associated {@link Batch.Call#call(Object)} * @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) */ public static interface Callback<R> { public void update(byte[] region, byte[] row, R result); } }