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 }