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 }