1 /* 2 * Copyright (C) 2015 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package dagger.producers.monitoring.internal; 17 18 import com.google.common.collect.ImmutableList; 19 import com.google.common.collect.Iterables; 20 import com.google.common.util.concurrent.ListenableFuture; 21 import dagger.producers.monitoring.ProducerMonitor; 22 import dagger.producers.monitoring.ProducerToken; 23 import dagger.producers.monitoring.ProductionComponentMonitor; 24 import java.util.Collection; 25 import java.util.logging.Level; 26 import java.util.logging.Logger; 27 import javax.inject.Provider; 28 29 /** 30 * Utility methods relating to monitoring, for use in generated producers code. 31 * 32 * @author Jesse Beder 33 */ 34 public final class Monitors { 35 private static final Logger logger = Logger.getLogger(Monitors.class.getName()); 36 37 /** 38 * Returns a monitor factory that delegates to the given factories, and ensures that any method 39 * called on this object, even transitively, does not throw a {@link RuntimeException} or return 40 * null. 41 * 42 * <p>If the delegate monitors throw an {@link Error}, then that will escape this monitor 43 * implementation. Errors are treated as unrecoverable conditions, and may cause the entire 44 * component's execution to fail. 45 */ 46 public static ProductionComponentMonitor.Factory delegatingProductionComponentMonitorFactory( 47 Collection<? extends ProductionComponentMonitor.Factory> factories) { 48 if (factories.isEmpty()) { 49 return noOpProductionComponentMonitorFactory(); 50 } else if (factories.size() == 1) { 51 return new NonThrowingProductionComponentMonitor.Factory(Iterables.getOnlyElement(factories)); 52 } else { 53 return new DelegatingProductionComponentMonitor.Factory(factories); 54 } 55 } 56 57 /** 58 * A component monitor that delegates to a single monitor, and catches and logs all exceptions 59 * that the delegate throws. 60 */ 61 private static final class NonThrowingProductionComponentMonitor 62 implements ProductionComponentMonitor { 63 private final ProductionComponentMonitor delegate; 64 65 NonThrowingProductionComponentMonitor(ProductionComponentMonitor delegate) { 66 this.delegate = delegate; 67 } 68 69 @Override 70 public ProducerMonitor producerMonitorFor(ProducerToken token) { 71 try { 72 ProducerMonitor monitor = delegate.producerMonitorFor(token); 73 return monitor == null ? noOpProducerMonitor() : new NonThrowingProducerMonitor(monitor); 74 } catch (RuntimeException e) { 75 logProducerMonitorForException(e, delegate, token); 76 return noOpProducerMonitor(); 77 } 78 } 79 80 static final class Factory implements ProductionComponentMonitor.Factory { 81 private final ProductionComponentMonitor.Factory delegate; 82 83 Factory(ProductionComponentMonitor.Factory delegate) { 84 this.delegate = delegate; 85 } 86 87 @Override 88 public ProductionComponentMonitor create(Object component) { 89 try { 90 ProductionComponentMonitor monitor = delegate.create(component); 91 return monitor == null 92 ? noOpProductionComponentMonitor() 93 : new NonThrowingProductionComponentMonitor(monitor); 94 } catch (RuntimeException e) { 95 logCreateException(e, delegate, component); 96 return noOpProductionComponentMonitor(); 97 } 98 } 99 } 100 } 101 102 /** 103 * A producer monitor that delegates to a single monitor, and catches and logs all exceptions 104 * that the delegate throws. 105 */ 106 private static final class NonThrowingProducerMonitor extends ProducerMonitor { 107 private final ProducerMonitor delegate; 108 109 NonThrowingProducerMonitor(ProducerMonitor delegate) { 110 this.delegate = delegate; 111 } 112 113 @Override 114 public void methodStarting() { 115 try { 116 delegate.methodStarting(); 117 } catch (RuntimeException e) { 118 logProducerMonitorMethodException(e, delegate, "methodStarting"); 119 } 120 } 121 122 @Override 123 public void methodFinished() { 124 try { 125 delegate.methodFinished(); 126 } catch (RuntimeException e) { 127 logProducerMonitorMethodException(e, delegate, "methodFinished"); 128 } 129 } 130 131 @Override 132 public void succeeded(Object o) { 133 try { 134 delegate.succeeded(o); 135 } catch (RuntimeException e) { 136 logProducerMonitorArgMethodException(e, delegate, "succeeded", o); 137 } 138 } 139 140 @Override 141 public void failed(Throwable t) { 142 try { 143 delegate.failed(t); 144 } catch (RuntimeException e) { 145 logProducerMonitorArgMethodException(e, delegate, "failed", t); 146 } 147 } 148 } 149 150 /** 151 * A component monitor that delegates to several monitors, and catches and logs all exceptions 152 * that the delegates throw. 153 */ 154 private static final class DelegatingProductionComponentMonitor 155 implements ProductionComponentMonitor { 156 private final ImmutableList<ProductionComponentMonitor> delegates; 157 158 DelegatingProductionComponentMonitor(ImmutableList<ProductionComponentMonitor> delegates) { 159 this.delegates = delegates; 160 } 161 162 @Override 163 public ProducerMonitor producerMonitorFor(ProducerToken token) { 164 ImmutableList.Builder<ProducerMonitor> monitorsBuilder = ImmutableList.builder(); 165 for (ProductionComponentMonitor delegate : delegates) { 166 try { 167 ProducerMonitor monitor = delegate.producerMonitorFor(token); 168 if (monitor != null) { 169 monitorsBuilder.add(monitor); 170 } 171 } catch (RuntimeException e) { 172 logProducerMonitorForException(e, delegate, token); 173 } 174 } 175 ImmutableList<ProducerMonitor> monitors = monitorsBuilder.build(); 176 if (monitors.isEmpty()) { 177 return noOpProducerMonitor(); 178 } else if (monitors.size() == 1) { 179 return new NonThrowingProducerMonitor(Iterables.getOnlyElement(monitors)); 180 } else { 181 return new DelegatingProducerMonitor(monitors); 182 } 183 } 184 185 static final class Factory implements ProductionComponentMonitor.Factory { 186 private final ImmutableList<? extends ProductionComponentMonitor.Factory> delegates; 187 188 Factory(Iterable<? extends ProductionComponentMonitor.Factory> delegates) { 189 this.delegates = ImmutableList.copyOf(delegates); 190 } 191 192 @Override 193 public ProductionComponentMonitor create(Object component) { 194 ImmutableList.Builder<ProductionComponentMonitor> monitorsBuilder = ImmutableList.builder(); 195 for (ProductionComponentMonitor.Factory delegate : delegates) { 196 try { 197 ProductionComponentMonitor monitor = delegate.create(component); 198 if (monitor != null) { 199 monitorsBuilder.add(monitor); 200 } 201 } catch (RuntimeException e) { 202 logCreateException(e, delegate, component); 203 } 204 } 205 ImmutableList<ProductionComponentMonitor> monitors = monitorsBuilder.build(); 206 if (monitors.isEmpty()) { 207 return noOpProductionComponentMonitor(); 208 } else if (monitors.size() == 1) { 209 return new NonThrowingProductionComponentMonitor(Iterables.getOnlyElement(monitors)); 210 } else { 211 return new DelegatingProductionComponentMonitor(monitors); 212 } 213 } 214 } 215 } 216 217 /** 218 * A producer monitor that delegates to several monitors, and catches and logs all exceptions 219 * that the delegates throw. 220 */ 221 private static final class DelegatingProducerMonitor extends ProducerMonitor { 222 private final ImmutableList<ProducerMonitor> delegates; 223 224 DelegatingProducerMonitor(ImmutableList<ProducerMonitor> delegates) { 225 this.delegates = delegates; 226 } 227 228 @Override 229 public void methodStarting() { 230 for (ProducerMonitor delegate : delegates) { 231 try { 232 delegate.methodStarting(); 233 } catch (RuntimeException e) { 234 logProducerMonitorMethodException(e, delegate, "methodStarting"); 235 } 236 } 237 } 238 239 @Override 240 public void methodFinished() { 241 for (ProducerMonitor delegate : delegates.reverse()) { 242 try { 243 delegate.methodFinished(); 244 } catch (RuntimeException e) { 245 logProducerMonitorMethodException(e, delegate, "methodFinished"); 246 } 247 } 248 } 249 250 @Override 251 public void succeeded(Object o) { 252 for (ProducerMonitor delegate : delegates.reverse()) { 253 try { 254 delegate.succeeded(o); 255 } catch (RuntimeException e) { 256 logProducerMonitorArgMethodException(e, delegate, "succeeded", o); 257 } 258 } 259 } 260 261 @Override 262 public void failed(Throwable t) { 263 for (ProducerMonitor delegate : delegates.reverse()) { 264 try { 265 delegate.failed(t); 266 } catch (RuntimeException e) { 267 logProducerMonitorArgMethodException(e, delegate, "failed", t); 268 } 269 } 270 } 271 } 272 273 /** Returns a monitor factory that returns no-op component monitors. */ 274 public static ProductionComponentMonitor.Factory noOpProductionComponentMonitorFactory() { 275 return NO_OP_PRODUCTION_COMPONENT_MONITOR_FACTORY; 276 } 277 278 /** Returns a component monitor that returns no-op producer monitors. */ 279 public static ProductionComponentMonitor noOpProductionComponentMonitor() { 280 return NO_OP_PRODUCTION_COMPONENT_MONITOR; 281 } 282 283 /** Returns a producer monitor that does nothing. */ 284 public static ProducerMonitor noOpProducerMonitor() { 285 return NO_OP_PRODUCER_MONITOR; 286 } 287 288 /** Returns a provider of a no-op component monitor. */ 289 public static Provider<ProductionComponentMonitor> noOpProductionComponentMonitorProvider() { 290 return NO_OP_PRODUCTION_COMPONENT_MONITOR_PROVIDER; 291 } 292 293 private static final ProductionComponentMonitor.Factory 294 NO_OP_PRODUCTION_COMPONENT_MONITOR_FACTORY = 295 new ProductionComponentMonitor.Factory() { 296 @Override 297 public ProductionComponentMonitor create(Object component) { 298 return noOpProductionComponentMonitor(); 299 } 300 }; 301 302 private static final ProductionComponentMonitor NO_OP_PRODUCTION_COMPONENT_MONITOR = 303 new ProductionComponentMonitor() { 304 @Override 305 public ProducerMonitor producerMonitorFor(ProducerToken token) { 306 return noOpProducerMonitor(); 307 } 308 }; 309 310 private static final ProducerMonitor NO_OP_PRODUCER_MONITOR = 311 new ProducerMonitor() { 312 @Override 313 public <T> void addCallbackTo(ListenableFuture<T> future) { 314 // overridden to avoid adding a do-nothing callback 315 } 316 }; 317 318 private static final Provider<ProductionComponentMonitor> 319 NO_OP_PRODUCTION_COMPONENT_MONITOR_PROVIDER = 320 new Provider() { 321 @Override 322 public ProductionComponentMonitor get() { 323 return noOpProductionComponentMonitor(); 324 } 325 }; 326 327 private static void logCreateException( 328 RuntimeException e, ProductionComponentMonitor.Factory factory, Object component) { 329 logger.log( 330 Level.SEVERE, 331 "RuntimeException while calling ProductionComponentMonitor.Factory.create on factory " 332 + factory 333 + " with component " 334 + component, 335 e); 336 } 337 338 private static void logProducerMonitorForException( 339 RuntimeException e, ProductionComponentMonitor monitor, ProducerToken token) { 340 logger.log( 341 Level.SEVERE, 342 "RuntimeException while calling ProductionComponentMonitor.producerMonitorFor on monitor " 343 + monitor 344 + " with token " 345 + token, 346 e); 347 } 348 349 private static void logProducerMonitorMethodException( 350 RuntimeException e, ProducerMonitor monitor, String method) { 351 logger.log( 352 Level.SEVERE, 353 "RuntimeException while calling ProducerMonitor." + method + " on monitor " + monitor, 354 e); 355 } 356 357 private static void logProducerMonitorArgMethodException( 358 RuntimeException e, ProducerMonitor monitor, String method, Object arg) { 359 logger.log( 360 Level.SEVERE, 361 "RuntimeException while calling ProducerMonitor." 362 + method 363 + " on monitor " 364 + monitor 365 + " with " 366 + arg, 367 e); 368 } 369 370 private Monitors() {} 371 } 372