View Javadoc

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 }