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.shaded.protobuf;
019
020import edu.umd.cs.findbugs.annotations.Nullable;
021import java.io.IOException;
022import java.util.ArrayList;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026import org.apache.hadoop.hbase.Cell;
027import org.apache.hadoop.hbase.CellScanner;
028import org.apache.hadoop.hbase.DoNotRetryIOException;
029import org.apache.hadoop.hbase.ServerName;
030import org.apache.hadoop.hbase.client.CheckAndMutateResult;
031import org.apache.hadoop.hbase.client.QueryMetrics;
032import org.apache.hadoop.hbase.client.RegionInfo;
033import org.apache.hadoop.hbase.client.Result;
034import org.apache.hadoop.hbase.client.SingleResponse;
035import org.apache.hadoop.hbase.ipc.ServerRpcController;
036import org.apache.hadoop.util.StringUtils;
037import org.apache.yetus.audience.InterfaceAudience;
038import org.slf4j.Logger;
039import org.slf4j.LoggerFactory;
040
041import org.apache.hbase.thirdparty.com.google.protobuf.ByteString;
042import org.apache.hbase.thirdparty.com.google.protobuf.RpcController;
043
044import org.apache.hadoop.hbase.shaded.protobuf.generated.AccessControlProtos.HasPermissionResponse;
045import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos;
046import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CloseRegionResponse;
047import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.GetOnlineRegionResponse;
048import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.GetServerInfoResponse;
049import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.ServerInfo;
050import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos;
051import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.MultiRequest;
052import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.MultiResponse;
053import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.RegionAction;
054import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.RegionActionResult;
055import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ResultOrException;
056import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ScanResponse;
057import org.apache.hadoop.hbase.shaded.protobuf.generated.ClusterStatusProtos.RegionStoreSequenceIds;
058import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos;
059import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.NameBytesPair;
060import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.NameInt64Pair;
061import org.apache.hadoop.hbase.shaded.protobuf.generated.MapReduceProtos.ScanMetrics;
062import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.EnableCatalogJanitorResponse;
063import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.RunCatalogScanResponse;
064import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.RunCleanerChoreResponse;
065import org.apache.hadoop.hbase.shaded.protobuf.generated.RegionServerStatusProtos.GetLastFlushedSequenceIdResponse;
066
067/**
068 * Helper utility to build protocol buffer responses, or retrieve data from protocol buffer
069 * responses.
070 */
071@InterfaceAudience.Private
072public final class ResponseConverter {
073  private static final Logger LOG = LoggerFactory.getLogger(ResponseConverter.class);
074
075  private ResponseConverter() {
076  }
077
078  // Start utilities for Client
079  public static SingleResponse getResult(final ClientProtos.MutateRequest request,
080    final ClientProtos.MutateResponse response, final CellScanner cells) throws IOException {
081    SingleResponse singleResponse = new SingleResponse();
082    SingleResponse.Entry entry = new SingleResponse.Entry();
083    entry.setResult(ProtobufUtil.toResult(response.getResult(), cells));
084    entry.setProcessed(response.getProcessed());
085    singleResponse.setEntry(entry);
086    return singleResponse;
087  }
088
089  /**
090   * Get the results from a protocol buffer MultiResponse
091   * @param request  the original protocol buffer MultiRequest
092   * @param response the protocol buffer MultiResponse to convert
093   * @param cells    Cells to go with the passed in <code>proto</code>. Can be null.
094   * @return the results that were in the MultiResponse (a Result or an Exception).
095   */
096  public static org.apache.hadoop.hbase.client.MultiResponse getResults(final MultiRequest request,
097    final MultiResponse response, final CellScanner cells) throws IOException {
098    return getResults(request, null, response, cells);
099  }
100
101  /**
102   * Get the results from a protocol buffer MultiResponse
103   * @param request  the original protocol buffer MultiRequest
104   * @param indexMap Used to support RowMutations/CheckAndMutate in batch
105   * @param response the protocol buffer MultiResponse to convert
106   * @param cells    Cells to go with the passed in <code>proto</code>. Can be null.
107   * @return the results that were in the MultiResponse (a Result or an Exception).
108   */
109  public static org.apache.hadoop.hbase.client.MultiResponse getResults(final MultiRequest request,
110    final Map<Integer, Integer> indexMap, final MultiResponse response, final CellScanner cells)
111    throws IOException {
112    int requestRegionActionCount = request.getRegionActionCount();
113    int responseRegionActionResultCount = response.getRegionActionResultCount();
114    if (requestRegionActionCount != responseRegionActionResultCount) {
115      throw new IllegalStateException("Request mutation count=" + requestRegionActionCount
116        + " does not match response mutation result count=" + responseRegionActionResultCount);
117    }
118
119    org.apache.hadoop.hbase.client.MultiResponse results =
120      new org.apache.hadoop.hbase.client.MultiResponse();
121
122    for (int i = 0; i < responseRegionActionResultCount; i++) {
123      RegionAction actions = request.getRegionAction(i);
124      RegionActionResult actionResult = response.getRegionActionResult(i);
125      HBaseProtos.RegionSpecifier rs = actions.getRegion();
126      if (
127        rs.hasType()
128          && (rs.getType() != HBaseProtos.RegionSpecifier.RegionSpecifierType.REGION_NAME)
129      ) {
130        throw new IllegalArgumentException(
131          "We support only encoded types for protobuf multi response.");
132      }
133      byte[] regionName = rs.getValue().toByteArray();
134
135      if (actionResult.hasException()) {
136        Throwable regionException = ProtobufUtil.toException(actionResult.getException());
137        results.addException(regionName, regionException);
138        continue;
139      }
140
141      if (actions.getActionCount() != actionResult.getResultOrExceptionCount()) {
142        throw new IllegalStateException("actions.getActionCount=" + actions.getActionCount()
143          + ", actionResult.getResultOrExceptionCount=" + actionResult.getResultOrExceptionCount()
144          + " for region " + actions.getRegion());
145      }
146
147      // For RowMutations/CheckAndMutate action, if there is an exception, the exception is set
148      // at the RegionActionResult level and the ResultOrException is null at the original index
149      Integer index = (indexMap == null ? null : indexMap.get(i));
150      if (index != null) {
151        // This RegionAction is from a RowMutations/CheckAndMutate in a batch.
152        // If there is an exception from the server, the exception is set at
153        // the RegionActionResult level, which has been handled above.
154        if (actions.hasCondition()) {
155          results.add(regionName, index, getCheckAndMutateResult(actionResult, cells));
156        } else {
157          results.add(regionName, index, getMutateRowResult(actionResult, cells));
158        }
159        continue;
160      }
161
162      if (actions.hasCondition()) {
163        results.add(regionName, 0, getCheckAndMutateResult(actionResult, cells));
164      } else {
165        if (actionResult.hasProcessed()) {
166          results.add(regionName, 0, getMutateRowResult(actionResult, cells));
167          continue;
168        }
169        for (ResultOrException roe : actionResult.getResultOrExceptionList()) {
170          Object responseValue;
171          if (roe.hasException()) {
172            responseValue = ProtobufUtil.toException(roe.getException());
173          } else if (roe.hasResult()) {
174            responseValue = ProtobufUtil.toResult(roe.getResult(), cells);
175          } else if (roe.hasServiceResult()) {
176            responseValue = roe.getServiceResult();
177          } else {
178            responseValue = ProtobufUtil.EMPTY_RESULT_EXISTS_TRUE;
179          }
180          results.add(regionName, roe.getIndex(), responseValue);
181        }
182      }
183    }
184
185    if (response.hasRegionStatistics()) {
186      ClientProtos.MultiRegionLoadStats stats = response.getRegionStatistics();
187      for (int i = 0; i < stats.getRegionCount(); i++) {
188        results.addStatistic(stats.getRegion(i).getValue().toByteArray(), stats.getStat(i));
189      }
190    }
191
192    return results;
193  }
194
195  private static CheckAndMutateResult getCheckAndMutateResult(RegionActionResult actionResult,
196    CellScanner cells) throws IOException {
197    Result result = null;
198    QueryMetrics metrics = null;
199    if (actionResult.getResultOrExceptionCount() > 0) {
200      // Get the result of the Increment/Append operations from the first element of the
201      // ResultOrException list
202      ResultOrException roe = actionResult.getResultOrException(0);
203      if (roe.hasResult()) {
204        Result r = ProtobufUtil.toResult(roe.getResult(), cells);
205        if (!r.isEmpty()) {
206          result = r;
207        }
208      }
209
210      if (roe.hasMetrics()) {
211        metrics = ProtobufUtil.toQueryMetrics(roe.getMetrics());
212      }
213    }
214    return new CheckAndMutateResult(actionResult.getProcessed(), result).setMetrics(metrics);
215  }
216
217  private static Result getMutateRowResult(RegionActionResult actionResult, CellScanner cells)
218    throws IOException {
219    if (actionResult.getProcessed()) {
220      Result result = null;
221      if (actionResult.getResultOrExceptionCount() > 0) {
222        // Get the result of the Increment/Append operations from the first element of the
223        // ResultOrException list
224        ResultOrException roe = actionResult.getResultOrException(0);
225        Result r = ProtobufUtil.toResult(roe.getResult(), cells);
226        if (!r.isEmpty()) {
227          r.setExists(true);
228          result = r;
229        }
230      }
231      if (result != null) {
232        return result;
233      } else {
234        return ProtobufUtil.EMPTY_RESULT_EXISTS_TRUE;
235      }
236    } else {
237      return ProtobufUtil.EMPTY_RESULT_EXISTS_FALSE;
238    }
239  }
240
241  /**
242   * Create a CheckAndMutateResult object from a protocol buffer MutateResponse
243   * @return a CheckAndMutateResult object
244   */
245  public static CheckAndMutateResult getCheckAndMutateResult(
246    ClientProtos.MutateResponse mutateResponse, CellScanner cells) throws IOException {
247    boolean success = mutateResponse.getProcessed();
248    Result result = null;
249    QueryMetrics metrics = null;
250    if (mutateResponse.hasResult()) {
251      result = ProtobufUtil.toResult(mutateResponse.getResult(), cells);
252    }
253
254    if (mutateResponse.hasMetrics()) {
255      metrics = ProtobufUtil.toQueryMetrics(mutateResponse.getMetrics());
256    }
257
258    return new CheckAndMutateResult(success, result).setMetrics(metrics);
259  }
260
261  /**
262   * Wrap a throwable to an action result.
263   * @return an action result builder
264   */
265  public static ResultOrException.Builder buildActionResult(final Throwable t) {
266    ResultOrException.Builder builder = ResultOrException.newBuilder();
267    if (t != null) builder.setException(buildException(t));
268    return builder;
269  }
270
271  /**
272   * Wrap a throwable to an action result.
273   * @return an action result builder
274   */
275  public static ResultOrException.Builder buildActionResult(final ClientProtos.Result r) {
276    ResultOrException.Builder builder = ResultOrException.newBuilder();
277    if (r != null) builder.setResult(r);
278    return builder;
279  }
280
281  /** Returns NameValuePair of the exception name to stringified version os exception. */
282  public static NameBytesPair buildException(final Throwable t) {
283    NameBytesPair.Builder parameterBuilder = NameBytesPair.newBuilder();
284    parameterBuilder.setName(t.getClass().getName());
285    parameterBuilder.setValue(ByteString.copyFromUtf8(StringUtils.stringifyException(t)));
286    return parameterBuilder.build();
287  }
288
289  /**
290   * Builds a protocol buffer HasPermissionResponse
291   */
292  public static HasPermissionResponse buildHasPermissionResponse(boolean hasPermission) {
293    HasPermissionResponse.Builder builder = HasPermissionResponse.newBuilder();
294    builder.setHasPermission(hasPermission);
295    return builder.build();
296  }
297
298  // End utilities for Client
299  // Start utilities for Admin
300
301  /**
302   * Get the list of region info from a GetOnlineRegionResponse
303   * @param proto the GetOnlineRegionResponse
304   * @return the list of region info
305   */
306  public static List<RegionInfo> getRegionInfos(final GetOnlineRegionResponse proto) {
307    if (proto == null || proto.getRegionInfoCount() == 0) return null;
308    return ProtobufUtil.getRegionInfos(proto);
309  }
310
311  /**
312   * Check if the region is closed from a CloseRegionResponse
313   * @param proto the CloseRegionResponse
314   * @return the region close state
315   */
316  public static boolean isClosed(final CloseRegionResponse proto) {
317    if (proto == null || !proto.hasClosed()) return false;
318    return proto.getClosed();
319  }
320
321  /**
322   * A utility to build a GetServerInfoResponse.
323   * @return the response
324   */
325  public static GetServerInfoResponse buildGetServerInfoResponse(final ServerName serverName,
326    final int webuiPort) {
327    GetServerInfoResponse.Builder builder = GetServerInfoResponse.newBuilder();
328    ServerInfo.Builder serverInfoBuilder = ServerInfo.newBuilder();
329    serverInfoBuilder.setServerName(ProtobufUtil.toServerName(serverName));
330    if (webuiPort >= 0) {
331      serverInfoBuilder.setWebuiPort(webuiPort);
332    }
333    builder.setServerInfo(serverInfoBuilder.build());
334    return builder.build();
335  }
336
337  /**
338   * A utility to build a GetOnlineRegionResponse.
339   * @return the response
340   */
341  public static GetOnlineRegionResponse
342    buildGetOnlineRegionResponse(final List<RegionInfo> regions) {
343    GetOnlineRegionResponse.Builder builder = GetOnlineRegionResponse.newBuilder();
344    for (RegionInfo region : regions) {
345      builder.addRegionInfo(ProtobufUtil.toRegionInfo(region));
346    }
347    return builder.build();
348  }
349
350  /**
351   * Creates a response for the catalog scan request
352   * @return A RunCatalogScanResponse
353   */
354  public static RunCatalogScanResponse buildRunCatalogScanResponse(int numCleaned) {
355    return RunCatalogScanResponse.newBuilder().setScanResult(numCleaned).build();
356  }
357
358  /**
359   * Creates a response for the catalog scan request
360   * @return A EnableCatalogJanitorResponse
361   */
362  public static EnableCatalogJanitorResponse buildEnableCatalogJanitorResponse(boolean prevValue) {
363    return EnableCatalogJanitorResponse.newBuilder().setPrevValue(prevValue).build();
364  }
365
366  /**
367   * Creates a response for the cleaner chore request
368   * @return A RunCleanerChoreResponse
369   */
370  public static RunCleanerChoreResponse buildRunCleanerChoreResponse(boolean ran) {
371    return RunCleanerChoreResponse.newBuilder().setCleanerChoreRan(ran).build();
372  }
373
374  // End utilities for Admin
375
376  /**
377   * Creates a response for the last flushed sequence Id request
378   * @return A GetLastFlushedSequenceIdResponse
379   */
380  public static GetLastFlushedSequenceIdResponse
381    buildGetLastFlushedSequenceIdResponse(RegionStoreSequenceIds ids) {
382    return GetLastFlushedSequenceIdResponse.newBuilder()
383      .setLastFlushedSequenceId(ids.getLastFlushedSequenceId())
384      .addAllStoreLastFlushedSequenceId(ids.getStoreSequenceIdList()).build();
385  }
386
387  /**
388   * Stores an exception encountered during RPC invocation so it can be passed back through to the
389   * client.
390   * @param controller the controller instance provided by the client when calling the service
391   * @param ioe        the exception encountered
392   */
393  public static void setControllerException(RpcController controller, IOException ioe) {
394    if (controller != null) {
395      if (controller instanceof ServerRpcController) {
396        ((ServerRpcController) controller).setFailedOn(ioe);
397      } else {
398        controller.setFailed(StringUtils.stringifyException(ioe));
399      }
400    }
401  }
402
403  /**
404   * Retreivies exception stored during RPC invocation.
405   * @param controller the controller instance provided by the client when calling the service
406   * @return exception if any, or null; Will return DoNotRetryIOException for string represented
407   *         failure causes in controller.
408   */
409  @Nullable
410  public static IOException getControllerException(RpcController controller) throws IOException {
411    if (controller != null && controller.failed()) {
412      if (controller instanceof ServerRpcController) {
413        return ((ServerRpcController) controller).getFailedOn();
414      } else {
415        return new DoNotRetryIOException(controller.errorText());
416      }
417    }
418    return null;
419  }
420
421  /**
422   * Create Results from the cells using the cells meta data.
423   */
424  public static Result[] getResults(CellScanner cellScanner, ScanResponse response)
425    throws IOException {
426    if (response == null) return null;
427    // If cellscanner, then the number of Results to return is the count of elements in the
428    // cellsPerResult list. Otherwise, it is how many results are embedded inside the response.
429    int noOfResults =
430      cellScanner != null ? response.getCellsPerResultCount() : response.getResultsCount();
431    Result[] results = new Result[noOfResults];
432    List<ClientProtos.QueryMetrics> queryMetrics = response.getQueryMetricsList();
433    for (int i = 0; i < noOfResults; i++) {
434      if (cellScanner != null) {
435        // Cells are out in cellblocks. Group them up again as Results. How many to read at a
436        // time will be found in getCellsLength -- length here is how many Cells in the i'th Result
437        int noOfCells = response.getCellsPerResult(i);
438        boolean isPartial =
439          response.getPartialFlagPerResultCount() > i ? response.getPartialFlagPerResult(i) : false;
440        List<Cell> cells = new ArrayList<>(noOfCells);
441        for (int j = 0; j < noOfCells; j++) {
442          try {
443            if (cellScanner.advance() == false) {
444              // We are not able to retrieve the exact number of cells which ResultCellMeta says us.
445              // We have to scan for the same results again. Throwing DNRIOE as a client retry on
446              // the
447              // same scanner will result in OutOfOrderScannerNextException
448              String msg = "Results sent from server=" + noOfResults + ". But only got " + i
449                + " results completely at client. Resetting the scanner to scan again.";
450              LOG.error(msg);
451              throw new DoNotRetryIOException(msg);
452            }
453          } catch (IOException ioe) {
454            // We are getting IOE while retrieving the cells for Results.
455            // We have to scan for the same results again. Throwing DNRIOE as a client retry on the
456            // same scanner will result in OutOfOrderScannerNextException
457            LOG.error(
458              "Exception while reading cells from result." + "Resetting the scanner to scan again.",
459              ioe);
460            throw new DoNotRetryIOException("Resetting the scanner.", ioe);
461          }
462          cells.add(cellScanner.current());
463        }
464        results[i] = Result.create(cells, null, response.getStale(), isPartial);
465      } else {
466        // Result is pure pb.
467        results[i] = ProtobufUtil.toResult(response.getResults(i));
468      }
469
470      // Populate result metrics if they exist
471      if (queryMetrics.size() > i) {
472        QueryMetrics metrics = ProtobufUtil.toQueryMetrics(queryMetrics.get(i));
473        results[i].setMetrics(metrics);
474      }
475    }
476    return results;
477  }
478
479  public static Map<String, Long> getScanMetrics(ScanResponse response) {
480    Map<String, Long> metricMap = new HashMap<>();
481    if (response == null || !response.hasScanMetrics()) {
482      return metricMap;
483    }
484
485    ScanMetrics metrics = response.getScanMetrics();
486    int numberOfMetrics = metrics.getMetricsCount();
487    for (int i = 0; i < numberOfMetrics; i++) {
488      NameInt64Pair metricPair = metrics.getMetrics(i);
489      if (metricPair != null) {
490        String name = metricPair.getName();
491        Long value = metricPair.getValue();
492        if (name != null && value != null) {
493          metricMap.put(name, value);
494        }
495      }
496    }
497
498    return metricMap;
499  }
500
501  /**
502   * Creates a protocol buffer ClearRegionBlockCacheResponse
503   * @return a ClearRegionBlockCacheResponse
504   */
505  public static AdminProtos.ClearRegionBlockCacheResponse
506    buildClearRegionBlockCacheResponse(final HBaseProtos.CacheEvictionStats cacheEvictionStats) {
507    return AdminProtos.ClearRegionBlockCacheResponse.newBuilder().setStats(cacheEvictionStats)
508      .build();
509  }
510}