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