View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.protobuf;
19  
20  import java.io.IOException;
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  
26  import edu.umd.cs.findbugs.annotations.Nullable;
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.hadoop.hbase.classification.InterfaceAudience;
30  import org.apache.hadoop.hbase.Cell;
31  import org.apache.hadoop.hbase.CellScanner;
32  import org.apache.hadoop.hbase.DoNotRetryIOException;
33  import org.apache.hadoop.hbase.HRegionInfo;
34  import org.apache.hadoop.hbase.ServerName;
35  import org.apache.hadoop.hbase.client.Result;
36  import org.apache.hadoop.hbase.ipc.ServerRpcController;
37  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.GetUserPermissionsResponse;
38  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.CloseRegionResponse;
39  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetOnlineRegionResponse;
40  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetServerInfoResponse;
41  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.OpenRegionResponse;
42  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.ServerInfo;
43  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
44  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiRequest;
45  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.RegionAction;
46  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.RegionActionResult;
47  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ResultOrException;
48  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ScanResponse;
49  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiResponse;
50  import org.apache.hadoop.hbase.protobuf.generated.ClusterStatusProtos.RegionStoreSequenceIds;
51  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
52  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.NameBytesPair;
53  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.NameInt64Pair;
54  import org.apache.hadoop.hbase.protobuf.generated.MapReduceProtos.ScanMetrics;
55  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.EnableCatalogJanitorResponse;
56  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.RunCatalogScanResponse;
57  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.RunCleanerChoreResponse;
58  import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos.GetLastFlushedSequenceIdResponse;
59  import org.apache.hadoop.hbase.regionserver.RegionOpeningState;
60  import org.apache.hadoop.hbase.security.access.UserPermission;
61  import org.apache.hadoop.util.StringUtils;
62  
63  import com.google.protobuf.ByteString;
64  import com.google.protobuf.RpcController;
65  
66  /**
67   * Helper utility to build protocol buffer responses,
68   * or retrieve data from protocol buffer responses.
69   */
70  @InterfaceAudience.Private
71  public final class ResponseConverter {
72    private static final Log LOG = LogFactory.getLog(ResponseConverter.class);
73  
74    private ResponseConverter() {
75    }
76  
77  // Start utilities for Client
78  
79    /**
80     * Get the results from a protocol buffer MultiResponse
81     *
82     * @param request the original protocol buffer MultiRequest
83     * @param response the protocol buffer MultiResponse to convert
84     * @param cells Cells to go with the passed in <code>proto</code>.  Can be null.
85     * @return the results that were in the MultiResponse (a Result or an Exception).
86     * @throws IOException
87     */
88    public static org.apache.hadoop.hbase.client.MultiResponse getResults(final MultiRequest request,
89        final MultiResponse response, final CellScanner cells)
90    throws IOException {
91      return getResults(request, null, response, cells);
92    }
93  
94    /**
95     * Get the results from a protocol buffer MultiResponse
96     *
97     * @param request the original protocol buffer MultiRequest
98     * @param rowMutationsIndexMap
99     * @param response the protocol buffer MultiResponse to convert
100    * @param cells Cells to go with the passed in <code>proto</code>.  Can be null.
101    * @return the results that were in the MultiResponse (a Result or an Exception).
102    * @throws IOException
103    */
104   public static org.apache.hadoop.hbase.client.MultiResponse getResults(final MultiRequest request,
105       Map<Integer, Integer> rowMutationsIndexMap, final MultiResponse response, final CellScanner cells)
106   throws IOException {
107     int requestRegionActionCount = request.getRegionActionCount();
108     int responseRegionActionResultCount = response.getRegionActionResultCount();
109     if (requestRegionActionCount != responseRegionActionResultCount) {
110       throw new IllegalStateException("Request mutation count=" + requestRegionActionCount +
111           " does not match response mutation result count=" + responseRegionActionResultCount);
112     }
113 
114     org.apache.hadoop.hbase.client.MultiResponse results =
115       new org.apache.hadoop.hbase.client.MultiResponse();
116 
117     for (int i = 0; i < responseRegionActionResultCount; i++) {
118       RegionAction actions = request.getRegionAction(i);
119       RegionActionResult actionResult = response.getRegionActionResult(i);
120       HBaseProtos.RegionSpecifier rs = actions.getRegion();
121       if (rs.hasType() &&
122           (rs.getType() != HBaseProtos.RegionSpecifier.RegionSpecifierType.REGION_NAME)){
123         throw new IllegalArgumentException(
124             "We support only encoded types for protobuf multi response.");
125       }
126       byte[] regionName = rs.getValue().toByteArray();
127 
128       if (actionResult.hasException()) {
129         Throwable regionException =  ProtobufUtil.toException(actionResult.getException());
130         results.addException(regionName, regionException);
131         continue;
132       }
133 
134       if (actions.getActionCount() != actionResult.getResultOrExceptionCount()) {
135         throw new IllegalStateException("actions.getActionCount=" + actions.getActionCount() +
136             ", actionResult.getResultOrExceptionCount=" +
137             actionResult.getResultOrExceptionCount() + " for region " + actions.getRegion());
138       }
139 
140       Object responseValue;
141 
142       Integer rowMutationsIndex =
143           (rowMutationsIndexMap == null ? null : rowMutationsIndexMap.get(i));
144       if (rowMutationsIndex != null) {
145         // This RegionAction is from a RowMutations in a batch.
146         // If there is an exception from the server, the exception is set at
147         // the RegionActionResult level, which has been handled above.
148         responseValue = response.getProcessed() ?
149             ProtobufUtil.EMPTY_RESULT_EXISTS_TRUE :
150             ProtobufUtil.EMPTY_RESULT_EXISTS_FALSE;
151         results.add(regionName, rowMutationsIndex, responseValue);
152         continue;
153       }
154 
155       for (ResultOrException roe : actionResult.getResultOrExceptionList()) {
156         if (roe.hasException()) {
157           responseValue = ProtobufUtil.toException(roe.getException());
158         } else if (roe.hasResult()) {
159           responseValue = ProtobufUtil.toResult(roe.getResult(), cells);
160         } else if (roe.hasServiceResult()) {
161           responseValue = roe.getServiceResult();
162         } else{
163           // Sometimes, the response is just "it was processed". Generally, this occurs for things
164           // like mutateRows where either we get back 'processed' (or not) and optionally some
165           // statistics about the regions we touched.
166           responseValue = response.getProcessed() ?
167                           ProtobufUtil.EMPTY_RESULT_EXISTS_TRUE :
168                           ProtobufUtil.EMPTY_RESULT_EXISTS_FALSE;
169         }
170         results.add(regionName, roe.getIndex(), responseValue);
171       }
172     }
173 
174     if (response.hasRegionStatistics()) {
175       ClientProtos.MultiRegionLoadStats stats = response.getRegionStatistics();
176       for (int i = 0; i < stats.getRegionCount(); i++) {
177         results.addStatistic(stats.getRegion(i).getValue().toByteArray(), stats.getStat(i));
178       }
179     }
180 
181     return results;
182   }
183 
184   /**
185    * Wrap a throwable to an action result.
186    *
187    * @param t
188    * @return an action result builder
189    */
190   public static ResultOrException.Builder buildActionResult(final Throwable t) {
191     ResultOrException.Builder builder = ResultOrException.newBuilder();
192     if (t != null) builder.setException(buildException(t));
193     return builder;
194   }
195 
196   /**
197    * Wrap a throwable to an action result.
198    *
199    * @param r
200    * @return an action result builder
201    */
202   public static ResultOrException.Builder buildActionResult(final ClientProtos.Result r) {
203     ResultOrException.Builder builder = ResultOrException.newBuilder();
204     if (r != null) builder.setResult(r);
205     return builder;
206   }
207 
208   /**
209    * @param t
210    * @return NameValuePair of the exception name to stringified version os exception.
211    */
212   public static NameBytesPair buildException(final Throwable t) {
213     NameBytesPair.Builder parameterBuilder = NameBytesPair.newBuilder();
214     parameterBuilder.setName(t.getClass().getName());
215     parameterBuilder.setValue(
216       ByteString.copyFromUtf8(StringUtils.stringifyException(t)));
217     return parameterBuilder.build();
218   }
219 
220   /**
221    * Converts the permissions list into a protocol buffer GetUserPermissionsResponse
222    */
223   public static GetUserPermissionsResponse buildGetUserPermissionsResponse(
224       final List<UserPermission> permissions) {
225     GetUserPermissionsResponse.Builder builder = GetUserPermissionsResponse.newBuilder();
226     for (UserPermission perm : permissions) {
227       builder.addUserPermission(ProtobufUtil.toUserPermission(perm));
228     }
229     return builder.build();
230   }
231 
232 // End utilities for Client
233 // Start utilities for Admin
234 
235   /**
236    * Get the list of region info from a GetOnlineRegionResponse
237    *
238    * @param proto the GetOnlineRegionResponse
239    * @return the list of region info
240    */
241   public static List<HRegionInfo> getRegionInfos(final GetOnlineRegionResponse proto) {
242     if (proto == null || proto.getRegionInfoCount() == 0) return null;
243     return ProtobufUtil.getRegionInfos(proto);
244   }
245 
246   /**
247    * Get the region opening state from a OpenRegionResponse
248    *
249    * @param proto the OpenRegionResponse
250    * @return the region opening state
251    */
252   public static RegionOpeningState getRegionOpeningState
253       (final OpenRegionResponse proto) {
254     if (proto == null || proto.getOpeningStateCount() != 1) return null;
255     return RegionOpeningState.valueOf(
256       proto.getOpeningState(0).name());
257   }
258 
259   /**
260    * Get a list of region opening state from a OpenRegionResponse
261    * 
262    * @param proto the OpenRegionResponse
263    * @return the list of region opening state
264    */
265   public static List<RegionOpeningState> getRegionOpeningStateList(
266       final OpenRegionResponse proto) {
267     if (proto == null) return null;
268     List<RegionOpeningState> regionOpeningStates = new ArrayList<RegionOpeningState>();
269     for (int i = 0; i < proto.getOpeningStateCount(); i++) {
270       regionOpeningStates.add(RegionOpeningState.valueOf(
271           proto.getOpeningState(i).name()));
272     }
273     return regionOpeningStates;
274   }
275 
276   /**
277    * Check if the region is closed from a CloseRegionResponse
278    *
279    * @param proto the CloseRegionResponse
280    * @return the region close state
281    */
282   public static boolean isClosed
283       (final CloseRegionResponse proto) {
284     if (proto == null || !proto.hasClosed()) return false;
285     return proto.getClosed();
286   }
287 
288   /**
289    * A utility to build a GetServerInfoResponse.
290    *
291    * @param serverName
292    * @param webuiPort
293    * @return the response
294    */
295   public static GetServerInfoResponse buildGetServerInfoResponse(
296       final ServerName serverName, final int webuiPort) {
297     GetServerInfoResponse.Builder builder = GetServerInfoResponse.newBuilder();
298     ServerInfo.Builder serverInfoBuilder = ServerInfo.newBuilder();
299     serverInfoBuilder.setServerName(ProtobufUtil.toServerName(serverName));
300     if (webuiPort >= 0) {
301       serverInfoBuilder.setWebuiPort(webuiPort);
302     }
303     builder.setServerInfo(serverInfoBuilder.build());
304     return builder.build();
305   }
306 
307   /**
308    * A utility to build a GetOnlineRegionResponse.
309    *
310    * @param regions
311    * @return the response
312    */
313   public static GetOnlineRegionResponse buildGetOnlineRegionResponse(
314       final List<HRegionInfo> regions) {
315     GetOnlineRegionResponse.Builder builder = GetOnlineRegionResponse.newBuilder();
316     for (HRegionInfo region: regions) {
317       builder.addRegionInfo(HRegionInfo.convert(region));
318     }
319     return builder.build();
320   }
321 
322   /**
323    * Creates a response for the catalog scan request
324    * @return A RunCatalogScanResponse
325    */
326   public static RunCatalogScanResponse buildRunCatalogScanResponse(int numCleaned) {
327     return RunCatalogScanResponse.newBuilder().setScanResult(numCleaned).build();
328   }
329 
330   /**
331    * Creates a response for the catalog scan request
332    * @return A EnableCatalogJanitorResponse
333    */
334   public static EnableCatalogJanitorResponse buildEnableCatalogJanitorResponse(boolean prevValue) {
335     return EnableCatalogJanitorResponse.newBuilder().setPrevValue(prevValue).build();
336   }
337 
338   /**
339    * Creates a response for the cleaner chore request
340    * @return A RunCleanerChoreResponse
341    */
342   public static RunCleanerChoreResponse buildRunCleanerChoreResponse(boolean ran) {
343     return RunCleanerChoreResponse.newBuilder().setCleanerChoreRan(ran).build();
344   }
345 
346 // End utilities for Admin
347 
348   /**
349    * Creates a response for the last flushed sequence Id request
350    * @return A GetLastFlushedSequenceIdResponse
351    */
352   public static GetLastFlushedSequenceIdResponse buildGetLastFlushedSequenceIdResponse(
353       RegionStoreSequenceIds ids) {
354     return GetLastFlushedSequenceIdResponse.newBuilder()
355         .setLastFlushedSequenceId(ids.getLastFlushedSequenceId())
356         .addAllStoreLastFlushedSequenceId(ids.getStoreSequenceIdList()).build();
357   }
358 
359   /**
360    * Stores an exception encountered during RPC invocation so it can be passed back
361    * through to the client.
362    * @param controller the controller instance provided by the client when calling the service
363    * @param ioe the exception encountered
364    */
365   public static void setControllerException(RpcController controller, IOException ioe) {
366     if (controller != null) {
367       if (controller instanceof ServerRpcController) {
368         ((ServerRpcController)controller).setFailedOn(ioe);
369       } else {
370         controller.setFailed(StringUtils.stringifyException(ioe));
371       }
372     }
373   }
374 
375   /**
376    * Retreivies exception stored during RPC invocation.
377    * @param controller the controller instance provided by the client when calling the service
378    * @return exception if any, or null; Will return DoNotRetryIOException for string represented
379    * failure causes in controller.
380    */
381   @Nullable
382   public static IOException getControllerException(RpcController controller) throws IOException {
383     if (controller != null && controller.failed()) {
384       if (controller instanceof ServerRpcController) {
385         return ((ServerRpcController)controller).getFailedOn();
386       } else {
387         return new DoNotRetryIOException(controller.errorText());
388       }
389     }
390     return null;
391   }
392 
393 
394   /**
395    * Create Results from the cells using the cells meta data. 
396    * @param cellScanner
397    * @param response
398    * @return results
399    */
400   public static Result[] getResults(CellScanner cellScanner, ScanResponse response)
401       throws IOException {
402     if (response == null) return null;
403     // If cellscanner, then the number of Results to return is the count of elements in the
404     // cellsPerResult list.  Otherwise, it is how many results are embedded inside the response.
405     int noOfResults = cellScanner != null?
406       response.getCellsPerResultCount(): response.getResultsCount();
407     Result[] results = new Result[noOfResults];
408     for (int i = 0; i < noOfResults; i++) {
409       if (cellScanner != null) {
410         // Cells are out in cellblocks.  Group them up again as Results.  How many to read at a
411         // time will be found in getCellsLength -- length here is how many Cells in the i'th Result
412         int noOfCells = response.getCellsPerResult(i);
413         boolean isPartial =
414             response.getPartialFlagPerResultCount() > i ?
415                 response.getPartialFlagPerResult(i) : false;
416         List<Cell> cells = new ArrayList<Cell>(noOfCells);
417         for (int j = 0; j < noOfCells; j++) {
418           try {
419             if (cellScanner.advance() == false) {
420               // We are not able to retrieve the exact number of cells which ResultCellMeta says us.
421               // We have to scan for the same results again. Throwing DNRIOE as a client retry on the
422               // same scanner will result in OutOfOrderScannerNextException
423               String msg = "Results sent from server=" + noOfResults + ". But only got " + i
424                 + " results completely at client. Resetting the scanner to scan again.";
425               LOG.error(msg);
426               throw new DoNotRetryIOException(msg);
427             }
428           } catch (IOException ioe) {
429             // We are getting IOE while retrieving the cells for Results.
430             // We have to scan for the same results again. Throwing DNRIOE as a client retry on the
431             // same scanner will result in OutOfOrderScannerNextException
432             LOG.error("Exception while reading cells from result."
433               + "Resetting the scanner to scan again.", ioe);
434             throw new DoNotRetryIOException("Resetting the scanner.", ioe);
435           }
436           cells.add(cellScanner.current());
437         }
438         results[i] = Result.create(cells, null, response.getStale(), isPartial);
439       } else {
440         // Result is pure pb.
441         results[i] = ProtobufUtil.toResult(response.getResults(i));
442       }
443     }
444     return results;
445   }
446 
447   public static Map<String, Long> getScanMetrics(ScanResponse response) {
448     Map<String, Long> metricMap = new HashMap<String, Long>();
449     if (response == null || !response.hasScanMetrics()) {
450       return metricMap;
451     }
452     ScanMetrics metrics = response.getScanMetrics();
453     int numberOfMetrics = metrics.getMetricsCount();
454     for (int i = 0; i < numberOfMetrics; i++) {
455       NameInt64Pair metricPair = metrics.getMetrics(i);
456       if (metricPair != null) {
457         String name = metricPair.getName();
458         Long value = metricPair.getValue();
459         if (name != null && value != null) {
460           metricMap.put(name, value);
461         }
462       }
463     }
464 
465     return metricMap;
466   }
467 }