001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.ipc; 019 020import static org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.RegionSpecifier.RegionSpecifierType.REGION_NAME; 021 022import edu.umd.cs.findbugs.annotations.Nullable; 023import java.io.IOException; 024import java.io.InterruptedIOException; 025import org.apache.hadoop.hbase.DoNotRetryIOException; 026import org.apache.hadoop.hbase.HConstants; 027import org.apache.hadoop.hbase.exceptions.UnknownProtocolException; 028import org.apache.hadoop.util.StringUtils; 029import org.apache.yetus.audience.InterfaceAudience; 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033import org.apache.hbase.thirdparty.com.google.protobuf.ByteString; 034import org.apache.hbase.thirdparty.com.google.protobuf.Descriptors; 035import org.apache.hbase.thirdparty.com.google.protobuf.Descriptors.MethodDescriptor; 036import org.apache.hbase.thirdparty.com.google.protobuf.Descriptors.ServiceDescriptor; 037import org.apache.hbase.thirdparty.com.google.protobuf.Message; 038import org.apache.hbase.thirdparty.com.google.protobuf.RpcCallback; 039import org.apache.hbase.thirdparty.com.google.protobuf.RpcController; 040import org.apache.hbase.thirdparty.com.google.protobuf.Service; 041 042import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 043import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter; 044import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos; 045import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.CoprocessorServiceCall; 046import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.CoprocessorServiceRequest; 047import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.RegionSpecifier.RegionSpecifierType; 048 049/** 050 * Utilities for handling coprocessor rpc service calls. 051 */ 052@InterfaceAudience.Private 053public final class CoprocessorRpcUtils { 054 private static final Logger LOG = LoggerFactory.getLogger(CoprocessorRpcUtils.class); 055 /** 056 * We assume that all HBase protobuf services share a common package name (defined in the .proto 057 * files). 058 */ 059 private static final String hbaseServicePackage; 060 static { 061 Descriptors.ServiceDescriptor clientService = ClientProtos.ClientService.getDescriptor(); 062 hbaseServicePackage = clientService.getFullName().substring(0, 063 clientService.getFullName().lastIndexOf(clientService.getName())); 064 } 065 066 private CoprocessorRpcUtils() { 067 // private for utility class 068 } 069 070 /** 071 * Returns the name to use for coprocessor service calls. For core HBase services (in the hbase.pb 072 * protobuf package), this returns the unqualified name in order to provide backward compatibility 073 * across the package name change. For all other services, the fully-qualified service name is 074 * used. 075 */ 076 public static String getServiceName(Descriptors.ServiceDescriptor service) { 077 if (service.getFullName().startsWith(hbaseServicePackage)) { 078 return service.getName(); 079 } 080 return service.getFullName(); 081 } 082 083 public static CoprocessorServiceRequest 084 getCoprocessorServiceRequest(final Descriptors.MethodDescriptor method, final Message request) { 085 return getCoprocessorServiceRequest(method, request, HConstants.EMPTY_BYTE_ARRAY, 086 HConstants.EMPTY_BYTE_ARRAY); 087 } 088 089 public static CoprocessorServiceRequest getCoprocessorServiceRequest( 090 final Descriptors.MethodDescriptor method, final Message request, final byte[] row, 091 final byte[] regionName) { 092 return CoprocessorServiceRequest.newBuilder() 093 .setCall(getCoprocessorServiceCall(method, request, row)) 094 .setRegion(RequestConverter.buildRegionSpecifier(REGION_NAME, regionName)).build(); 095 } 096 097 private static CoprocessorServiceCall getCoprocessorServiceCall(final MethodDescriptor method, 098 final Message request, final byte[] row) { 099 return CoprocessorServiceCall.newBuilder() 100 .setRow(org.apache.hbase.thirdparty.com.google.protobuf.UnsafeByteOperations.unsafeWrap(row)) 101 .setServiceName(CoprocessorRpcUtils.getServiceName(method.getService())) 102 .setMethodName(method.getName()) 103 // TODO!!!!! Come back here after!!!!! This is a double copy of the request if I read 104 // it right copying from non-shaded to shaded version!!!!!! FIXXXXX!!!!! 105 .setRequest(org.apache.hbase.thirdparty.com.google.protobuf.UnsafeByteOperations 106 .unsafeWrap(request.toByteArray())) 107 .build(); 108 } 109 110 public static MethodDescriptor getMethodDescriptor(final String methodName, 111 final ServiceDescriptor serviceDesc) throws UnknownProtocolException { 112 MethodDescriptor methodDesc = serviceDesc.findMethodByName(methodName); 113 if (methodDesc == null) { 114 throw new UnknownProtocolException( 115 "Unknown method " + methodName + " called on service " + serviceDesc.getFullName()); 116 } 117 return methodDesc; 118 } 119 120 public static Message getRequest(Service service, MethodDescriptor methodDesc, 121 ByteString shadedRequest) throws IOException { 122 Message.Builder builderForType = service.getRequestPrototype(methodDesc).newBuilderForType(); 123 ProtobufUtil.mergeFrom(builderForType, 124 // TODO: COPY FROM SHADED TO NON_SHADED. DO I HAVE TOO? 125 shadedRequest.toByteArray()); 126 return builderForType.build(); 127 } 128 129 public static Message getResponse(ClientProtos.CoprocessorServiceResponse result, 130 Message responsePrototype) throws IOException { 131 Message response; 132 if (result.getValue().hasValue()) { 133 Message.Builder builder = responsePrototype.newBuilderForType(); 134 builder.mergeFrom(result.getValue().getValue().newInput()); 135 response = builder.build(); 136 } else { 137 response = responsePrototype.getDefaultInstanceForType(); 138 } 139 if (LOG.isTraceEnabled()) { 140 LOG.trace("Master Result is value=" + response); 141 } 142 return response; 143 } 144 145 public static ClientProtos.CoprocessorServiceResponse getResponse(final Message result, 146 final byte[] regionName) { 147 ClientProtos.CoprocessorServiceResponse.Builder builder = 148 ClientProtos.CoprocessorServiceResponse.newBuilder(); 149 builder.setRegion( 150 RequestConverter.buildRegionSpecifier(RegionSpecifierType.REGION_NAME, regionName)); 151 // TODO: UGLY COPY IN HERE!!!! 152 builder.setValue(builder.getValueBuilder().setName(result.getClass().getName()).setValue( 153 org.apache.hbase.thirdparty.com.google.protobuf.ByteString.copyFrom(result.toByteArray()))); 154 return builder.build(); 155 } 156 157 /** 158 * Simple {@link RpcCallback} implementation providing a {@link java.util.concurrent.Future}-like 159 * {@link BlockingRpcCallback#get()} method, which will block util the instance's 160 * {@link BlockingRpcCallback#run(Object)} method has been called. {@code R} is the RPC response 161 * type that will be passed to the {@link #run(Object)} method. 162 */ 163 @InterfaceAudience.Private 164 // Copy of BlockingRpcCallback but deriving from RpcCallback non-shaded. 165 public static class BlockingRpcCallback<R> implements RpcCallback<R> { 166 private R result; 167 private boolean resultSet = false; 168 169 /** 170 * Called on completion of the RPC call with the response object, or {@code null} in the case of 171 * an error. 172 * @param parameter the response object or {@code null} if an error occurred 173 */ 174 @Override 175 public void run(R parameter) { 176 synchronized (this) { 177 result = parameter; 178 resultSet = true; 179 this.notifyAll(); 180 } 181 } 182 183 /** 184 * Returns the parameter passed to {@link #run(Object)} or {@code null} if a null value was 185 * passed. When used asynchronously, this method will block until the {@link #run(Object)} 186 * method has been called. 187 * @return the response object or {@code null} if no response was passed 188 */ 189 public synchronized R get() throws IOException { 190 while (!resultSet) { 191 try { 192 this.wait(); 193 } catch (InterruptedException ie) { 194 InterruptedIOException exception = new InterruptedIOException(ie.getMessage()); 195 exception.initCause(ie); 196 throw exception; 197 } 198 } 199 return result; 200 } 201 } 202 203 /** 204 * Stores an exception encountered during RPC invocation so it can be passed back through to the 205 * client. 206 * @param controller the controller instance provided by the client when calling the service 207 * @param ioe the exception encountered 208 */ 209 public static void setControllerException(RpcController controller, IOException ioe) { 210 if (controller == null) { 211 return; 212 } 213 if (controller instanceof org.apache.hadoop.hbase.ipc.ServerRpcController) { 214 ((ServerRpcController) controller).setFailedOn(ioe); 215 } else { 216 controller.setFailed(StringUtils.stringifyException(ioe)); 217 } 218 } 219 220 /** 221 * Retreivies exception stored during RPC invocation. 222 * @param controller the controller instance provided by the client when calling the service 223 * @return exception if any, or null; Will return DoNotRetryIOException for string represented 224 * failure causes in controller. 225 */ 226 @Nullable 227 public static IOException getControllerException(RpcController controller) throws IOException { 228 if (controller == null || !controller.failed()) { 229 return null; 230 } 231 if (controller instanceof ServerRpcController) { 232 return ((ServerRpcController) controller).getFailedOn(); 233 } 234 return new DoNotRetryIOException(controller.errorText()); 235 } 236}