Coverage Summary for Class: FuturesGetChecked (com.google.common.util.concurrent)

Class Method, % Line, %
FuturesGetChecked 0% (0/15) 0% (0/62)
FuturesGetChecked$1 0% (0/2) 0% (0/2)
FuturesGetChecked$GetCheckedTypeValidatorHolder 0% (0/3) 0% (0/9)
FuturesGetChecked$GetCheckedTypeValidatorHolder$ClassValueValidator 0% (0/2) 0% (0/4)
FuturesGetChecked$GetCheckedTypeValidatorHolder$ClassValueValidator$1 0% (0/2) 0% (0/3)
FuturesGetChecked$GetCheckedTypeValidatorHolder$WeakSetValidator 0% (0/2) 0% (0/10)
Total 0% (0/26) 0% (0/90)


1 /* 2  * Copyright (C) 2006 The Guava Authors 3  * 4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5  * in compliance with the License. You may obtain a copy of the License at 6  * 7  * http://www.apache.org/licenses/LICENSE-2.0 8  * 9  * Unless required by applicable law or agreed to in writing, software distributed under the License 10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11  * or implied. See the License for the specific language governing permissions and limitations under 12  * the License. 13  */ 14  15 package com.google.common.util.concurrent; 16  17 import static com.google.common.base.Preconditions.checkArgument; 18 import static java.lang.Thread.currentThread; 19 import static java.util.Arrays.asList; 20  21 import com.google.common.annotations.GwtIncompatible; 22 import com.google.common.annotations.VisibleForTesting; 23 import com.google.common.base.Function; 24 import com.google.common.collect.Ordering; 25 import com.google.errorprone.annotations.CanIgnoreReturnValue; 26 import com.google.j2objc.annotations.J2ObjCIncompatible; 27 import java.lang.ref.WeakReference; 28 import java.lang.reflect.Constructor; 29 import java.lang.reflect.InvocationTargetException; 30 import java.util.Arrays; 31 import java.util.List; 32 import java.util.Set; 33 import java.util.concurrent.CopyOnWriteArraySet; 34 import java.util.concurrent.ExecutionException; 35 import java.util.concurrent.Future; 36 import java.util.concurrent.TimeUnit; 37 import java.util.concurrent.TimeoutException; 38 import javax.annotation.CheckForNull; 39 import org.checkerframework.checker.nullness.qual.Nullable; 40  41 /** Static methods used to implement {@link Futures#getChecked(Future, Class)}. */ 42 @GwtIncompatible 43 @ElementTypesAreNonnullByDefault 44 final class FuturesGetChecked { 45  @CanIgnoreReturnValue 46  @ParametricNullness 47  static <V extends @Nullable Object, X extends Exception> V getChecked( 48  Future<V> future, Class<X> exceptionClass) throws X { 49  return getChecked(bestGetCheckedTypeValidator(), future, exceptionClass); 50  } 51  52  /** Implementation of {@link Futures#getChecked(Future, Class)}. */ 53  @CanIgnoreReturnValue 54  @VisibleForTesting 55  @ParametricNullness 56  static <V extends @Nullable Object, X extends Exception> V getChecked( 57  GetCheckedTypeValidator validator, Future<V> future, Class<X> exceptionClass) throws X { 58  validator.validateClass(exceptionClass); 59  try { 60  return future.get(); 61  } catch (InterruptedException e) { 62  currentThread().interrupt(); 63  throw newWithCause(exceptionClass, e); 64  } catch (ExecutionException e) { 65  wrapAndThrowExceptionOrError(e.getCause(), exceptionClass); 66  throw new AssertionError(); 67  } 68  } 69  70  /** Implementation of {@link Futures#getChecked(Future, Class, long, TimeUnit)}. */ 71  @CanIgnoreReturnValue 72  @ParametricNullness 73  static <V extends @Nullable Object, X extends Exception> V getChecked( 74  Future<V> future, Class<X> exceptionClass, long timeout, TimeUnit unit) throws X { 75  // TODO(cpovirk): benchmark a version of this method that accepts a GetCheckedTypeValidator 76  bestGetCheckedTypeValidator().validateClass(exceptionClass); 77  try { 78  return future.get(timeout, unit); 79  } catch (InterruptedException e) { 80  currentThread().interrupt(); 81  throw newWithCause(exceptionClass, e); 82  } catch (TimeoutException e) { 83  throw newWithCause(exceptionClass, e); 84  } catch (ExecutionException e) { 85  wrapAndThrowExceptionOrError(e.getCause(), exceptionClass); 86  throw new AssertionError(); 87  } 88  } 89  90  @VisibleForTesting 91  interface GetCheckedTypeValidator { 92  void validateClass(Class<? extends Exception> exceptionClass); 93  } 94  95  private static GetCheckedTypeValidator bestGetCheckedTypeValidator() { 96  return GetCheckedTypeValidatorHolder.BEST_VALIDATOR; 97  } 98  99  @VisibleForTesting 100  static GetCheckedTypeValidator weakSetValidator() { 101  return GetCheckedTypeValidatorHolder.WeakSetValidator.INSTANCE; 102  } 103  104  @J2ObjCIncompatible // ClassValue 105  @VisibleForTesting 106  static GetCheckedTypeValidator classValueValidator() { 107  return GetCheckedTypeValidatorHolder.ClassValueValidator.INSTANCE; 108  } 109  110  /** 111  * Provides a check of whether an exception type is valid for use with {@link 112  * FuturesGetChecked#getChecked(Future, Class)}, possibly using caching. 113  * 114  * <p>Uses reflection to gracefully fall back to when certain implementations aren't available. 115  */ 116  @VisibleForTesting 117  static class GetCheckedTypeValidatorHolder { 118  static final String CLASS_VALUE_VALIDATOR_NAME = 119  GetCheckedTypeValidatorHolder.class.getName() + "$ClassValueValidator"; 120  121  static final GetCheckedTypeValidator BEST_VALIDATOR = getBestValidator(); 122  123  @IgnoreJRERequirement // getChecked falls back to another implementation if necessary 124  @J2ObjCIncompatible // ClassValue 125  enum ClassValueValidator implements GetCheckedTypeValidator { 126  INSTANCE; 127  128  /* 129  * Static final fields are presumed to be fastest, based on our experience with 130  * UnsignedBytesBenchmark. TODO(cpovirk): benchmark this 131  */ 132  private static final ClassValue<Boolean> isValidClass = 133  new ClassValue<Boolean>() { 134  @Override 135  protected Boolean computeValue(Class<?> type) { 136  checkExceptionClassValidity(type.asSubclass(Exception.class)); 137  return true; 138  } 139  }; 140  141  @Override 142  public void validateClass(Class<? extends Exception> exceptionClass) { 143  isValidClass.get(exceptionClass); // throws if invalid; returns safely (and caches) if valid 144  } 145  } 146  147  enum WeakSetValidator implements GetCheckedTypeValidator { 148  INSTANCE; 149  150  /* 151  * Static final fields are presumed to be fastest, based on our experience with 152  * UnsignedBytesBenchmark. TODO(cpovirk): benchmark this 153  */ 154  /* 155  * A CopyOnWriteArraySet<WeakReference> is faster than a newSetFromMap of a MapMaker map with 156  * weakKeys() and concurrencyLevel(1), even up to at least 12 cached exception types. 157  */ 158  private static final Set<WeakReference<Class<? extends Exception>>> validClasses = 159  new CopyOnWriteArraySet<>(); 160  161  @Override 162  public void validateClass(Class<? extends Exception> exceptionClass) { 163  for (WeakReference<Class<? extends Exception>> knownGood : validClasses) { 164  if (exceptionClass.equals(knownGood.get())) { 165  return; 166  } 167  // TODO(cpovirk): if reference has been cleared, remove it? 168  } 169  checkExceptionClassValidity(exceptionClass); 170  171  /* 172  * It's very unlikely that any loaded Futures class will see getChecked called with more 173  * than a handful of exceptions. But it seems prudent to set a cap on how many we'll cache. 174  * This avoids out-of-control memory consumption, and it keeps the cache from growing so 175  * large that doing the lookup is noticeably slower than redoing the work would be. 176  * 177  * Ideally we'd have a real eviction policy, but until we see a problem in practice, I hope 178  * that this will suffice. I have not even benchmarked with different size limits. 179  */ 180  if (validClasses.size() > 1000) { 181  validClasses.clear(); 182  } 183  184  validClasses.add(new WeakReference<Class<? extends Exception>>(exceptionClass)); 185  } 186  } 187  188  /** 189  * Returns the ClassValue-using validator, or falls back to the "weak Set" implementation if 190  * unable to do so. 191  */ 192  static GetCheckedTypeValidator getBestValidator() { 193  try { 194  Class<? extends Enum> theClass = 195  Class.forName(CLASS_VALUE_VALIDATOR_NAME).asSubclass(Enum.class); 196  return (GetCheckedTypeValidator) theClass.getEnumConstants()[0]; 197  } catch (Throwable t) { // ensure we really catch *everything* 198  return weakSetValidator(); 199  } 200  } 201  } 202  203  // TODO(cpovirk): change parameter order to match other helper methods (Class, Throwable)? 204  private static <X extends Exception> void wrapAndThrowExceptionOrError( 205  Throwable cause, Class<X> exceptionClass) throws X { 206  if (cause instanceof Error) { 207  throw new ExecutionError((Error) cause); 208  } 209  if (cause instanceof RuntimeException) { 210  throw new UncheckedExecutionException(cause); 211  } 212  throw newWithCause(exceptionClass, cause); 213  } 214  215  /* 216  * TODO(user): FutureChecker interface for these to be static methods on? If so, refer to it in 217  * the (static-method) Futures.getChecked documentation 218  */ 219  220  private static boolean hasConstructorUsableByGetChecked( 221  Class<? extends Exception> exceptionClass) { 222  try { 223  Exception unused = newWithCause(exceptionClass, new Exception()); 224  return true; 225  } catch (Exception e) { 226  return false; 227  } 228  } 229  230  private static <X extends Exception> X newWithCause(Class<X> exceptionClass, Throwable cause) { 231  // getConstructors() guarantees this as long as we don't modify the array. 232  @SuppressWarnings({"unchecked", "rawtypes"}) 233  List<Constructor<X>> constructors = (List) Arrays.asList(exceptionClass.getConstructors()); 234  for (Constructor<X> constructor : preferringStrings(constructors)) { 235  X instance = newFromConstructor(constructor, cause); 236  if (instance != null) { 237  if (instance.getCause() == null) { 238  instance.initCause(cause); 239  } 240  return instance; 241  } 242  } 243  throw new IllegalArgumentException( 244  "No appropriate constructor for exception of type " 245  + exceptionClass 246  + " in response to chained exception", 247  cause); 248  } 249  250  private static <X extends Exception> List<Constructor<X>> preferringStrings( 251  List<Constructor<X>> constructors) { 252  return WITH_STRING_PARAM_FIRST.sortedCopy(constructors); 253  } 254  255  private static final Ordering<Constructor<?>> WITH_STRING_PARAM_FIRST = 256  Ordering.natural() 257  .onResultOf( 258  new Function<Constructor<?>, Boolean>() { 259  @Override 260  public Boolean apply(Constructor<?> input) { 261  return asList(input.getParameterTypes()).contains(String.class); 262  } 263  }) 264  .reverse(); 265  266  @CheckForNull 267  private static <X> X newFromConstructor(Constructor<X> constructor, Throwable cause) { 268  Class<?>[] paramTypes = constructor.getParameterTypes(); 269  Object[] params = new Object[paramTypes.length]; 270  for (int i = 0; i < paramTypes.length; i++) { 271  Class<?> paramType = paramTypes[i]; 272  if (paramType.equals(String.class)) { 273  params[i] = cause.toString(); 274  } else if (paramType.equals(Throwable.class)) { 275  params[i] = cause; 276  } else { 277  return null; 278  } 279  } 280  try { 281  return constructor.newInstance(params); 282  } catch (IllegalArgumentException 283  | InstantiationException 284  | IllegalAccessException 285  | InvocationTargetException e) { 286  return null; 287  } 288  } 289  290  @VisibleForTesting 291  static boolean isCheckedException(Class<? extends Exception> type) { 292  return !RuntimeException.class.isAssignableFrom(type); 293  } 294  295  @VisibleForTesting 296  static void checkExceptionClassValidity(Class<? extends Exception> exceptionClass) { 297  checkArgument( 298  isCheckedException(exceptionClass), 299  "Futures.getChecked exception type (%s) must not be a RuntimeException", 300  exceptionClass); 301  checkArgument( 302  hasConstructorUsableByGetChecked(exceptionClass), 303  "Futures.getChecked exception type (%s) must be an accessible class with an accessible " 304  + "constructor whose parameters (if any) must be of type String and/or Throwable", 305  exceptionClass); 306  } 307  308  private FuturesGetChecked() {} 309 }