Home | History | Annotate | Download | only in internal
      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