Java tutorial
/* * Copyright (C) 2015 The Dagger Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.producers.monitoring; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import dagger.internal.Beta; import java.util.Collection; import java.util.logging.Level; import java.util.logging.Logger; /** * Utility methods relating to timing. * * @author Jesse Beder */ // TODO(beder): Reduce the visibility of this class to package-private. @Beta public final class TimingRecorders { private static final Logger logger = Logger.getLogger(TimingRecorders.class.getName()); /** * Returns a timing recorder factory that delegates to the given factories, and ensures that any * method called on this object, even transitively, does not throw a {@link RuntimeException} or * return null. * * <p>If the delegate recorders throw an {@link Error}, then that will escape this recorder * implementation. Errors are treated as unrecoverable conditions, and may cause the entire * component's execution to fail. */ public static ProductionComponentTimingRecorder.Factory delegatingProductionComponentTimingRecorderFactory( Collection<ProductionComponentTimingRecorder.Factory> factories) { switch (factories.size()) { case 0: return noOpProductionComponentTimingRecorderFactory(); case 1: return new NonThrowingProductionComponentTimingRecorder.Factory(Iterables.getOnlyElement(factories)); default: return new DelegatingProductionComponentTimingRecorder.Factory(factories); } } /** * A component recorder that delegates to a single recorder, and catches and logs all exceptions * that the delegate throws. */ private static final class NonThrowingProductionComponentTimingRecorder implements ProductionComponentTimingRecorder { private final ProductionComponentTimingRecorder delegate; NonThrowingProductionComponentTimingRecorder(ProductionComponentTimingRecorder delegate) { this.delegate = delegate; } @Override public ProducerTimingRecorder producerTimingRecorderFor(ProducerToken token) { try { ProducerTimingRecorder recorder = delegate.producerTimingRecorderFor(token); return recorder == null ? ProducerTimingRecorder.noOp() : new NonThrowingProducerTimingRecorder(recorder); } catch (RuntimeException e) { logProducerTimingRecorderForException(e, delegate, token); return ProducerTimingRecorder.noOp(); } } static final class Factory implements ProductionComponentTimingRecorder.Factory { private final ProductionComponentTimingRecorder.Factory delegate; Factory(ProductionComponentTimingRecorder.Factory delegate) { this.delegate = delegate; } @Override public ProductionComponentTimingRecorder create(Object component) { try { ProductionComponentTimingRecorder recorder = delegate.create(component); return recorder == null ? noOpProductionComponentTimingRecorder() : new NonThrowingProductionComponentTimingRecorder(recorder); } catch (RuntimeException e) { logCreateException(e, delegate, component); return noOpProductionComponentTimingRecorder(); } } } } /** * A producer recorder that delegates to a single recorder, and catches and logs all exceptions * that the delegate throws. */ private static final class NonThrowingProducerTimingRecorder extends ProducerTimingRecorder { private final ProducerTimingRecorder delegate; NonThrowingProducerTimingRecorder(ProducerTimingRecorder delegate) { this.delegate = delegate; } @Override public void recordMethod(long startedNanos, long durationNanos) { try { delegate.recordMethod(startedNanos, durationNanos); } catch (RuntimeException e) { logProducerTimingRecorderMethodException(e, delegate, "recordMethod"); } } @Override public void recordSuccess(long latencyNanos) { try { delegate.recordSuccess(latencyNanos); } catch (RuntimeException e) { logProducerTimingRecorderMethodException(e, delegate, "recordSuccess"); } } @Override public void recordFailure(Throwable exception, long latencyNanos) { try { delegate.recordFailure(exception, latencyNanos); } catch (RuntimeException e) { logProducerTimingRecorderMethodException(e, delegate, "recordFailure"); } } @Override public void recordSkip(Throwable exception) { try { delegate.recordSkip(exception); } catch (RuntimeException e) { logProducerTimingRecorderMethodException(e, delegate, "recordSkip"); } } } /** * A component recorder that delegates to several recorders, and catches and logs all exceptions * that the delegates throw. */ private static final class DelegatingProductionComponentTimingRecorder implements ProductionComponentTimingRecorder { private final ImmutableList<ProductionComponentTimingRecorder> delegates; DelegatingProductionComponentTimingRecorder(ImmutableList<ProductionComponentTimingRecorder> delegates) { this.delegates = delegates; } @Override public ProducerTimingRecorder producerTimingRecorderFor(ProducerToken token) { ImmutableList.Builder<ProducerTimingRecorder> recordersBuilder = ImmutableList.builder(); for (ProductionComponentTimingRecorder delegate : delegates) { try { ProducerTimingRecorder recorder = delegate.producerTimingRecorderFor(token); if (recorder != null) { recordersBuilder.add(recorder); } } catch (RuntimeException e) { logProducerTimingRecorderForException(e, delegate, token); } } ImmutableList<ProducerTimingRecorder> recorders = recordersBuilder.build(); switch (recorders.size()) { case 0: return ProducerTimingRecorder.noOp(); case 1: return new NonThrowingProducerTimingRecorder(Iterables.getOnlyElement(recorders)); default: return new DelegatingProducerTimingRecorder(recorders); } } static final class Factory implements ProductionComponentTimingRecorder.Factory { private final ImmutableList<? extends ProductionComponentTimingRecorder.Factory> delegates; Factory(Iterable<? extends ProductionComponentTimingRecorder.Factory> delegates) { this.delegates = ImmutableList.copyOf(delegates); } @Override public ProductionComponentTimingRecorder create(Object component) { ImmutableList.Builder<ProductionComponentTimingRecorder> recordersBuilder = ImmutableList.builder(); for (ProductionComponentTimingRecorder.Factory delegate : delegates) { try { ProductionComponentTimingRecorder recorder = delegate.create(component); if (recorder != null) { recordersBuilder.add(recorder); } } catch (RuntimeException e) { logCreateException(e, delegate, component); } } ImmutableList<ProductionComponentTimingRecorder> recorders = recordersBuilder.build(); switch (recorders.size()) { case 0: return noOpProductionComponentTimingRecorder(); case 1: return new NonThrowingProductionComponentTimingRecorder(Iterables.getOnlyElement(recorders)); default: return new DelegatingProductionComponentTimingRecorder(recorders); } } } } /** * A producer recorder that delegates to several recorders, and catches and logs all exceptions * that the delegates throw. */ private static final class DelegatingProducerTimingRecorder extends ProducerTimingRecorder { private final ImmutableList<ProducerTimingRecorder> delegates; DelegatingProducerTimingRecorder(ImmutableList<ProducerTimingRecorder> delegates) { this.delegates = delegates; } @Override public void recordMethod(long startedNanos, long durationNanos) { for (ProducerTimingRecorder delegate : delegates) { try { delegate.recordMethod(startedNanos, durationNanos); } catch (RuntimeException e) { logProducerTimingRecorderMethodException(e, delegate, "recordMethod"); } } } @Override public void recordSuccess(long latencyNanos) { for (ProducerTimingRecorder delegate : delegates) { try { delegate.recordSuccess(latencyNanos); } catch (RuntimeException e) { logProducerTimingRecorderMethodException(e, delegate, "recordSuccess"); } } } @Override public void recordFailure(Throwable exception, long latencyNanos) { for (ProducerTimingRecorder delegate : delegates) { try { delegate.recordFailure(exception, latencyNanos); } catch (RuntimeException e) { logProducerTimingRecorderMethodException(e, delegate, "recordFailure"); } } } @Override public void recordSkip(Throwable exception) { for (ProducerTimingRecorder delegate : delegates) { try { delegate.recordSkip(exception); } catch (RuntimeException e) { logProducerTimingRecorderMethodException(e, delegate, "recordSkip"); } } } } /** Returns a recorder factory that returns no-op component recorders. */ public static ProductionComponentTimingRecorder.Factory noOpProductionComponentTimingRecorderFactory() { return NO_OP_PRODUCTION_COMPONENT_TIMING_RECORDER_FACTORY; } /** Returns a component recorder that returns no-op producer recorders. */ public static ProductionComponentTimingRecorder noOpProductionComponentTimingRecorder() { return NO_OP_PRODUCTION_COMPONENT_TIMING_RECORDER; } private static final ProductionComponentTimingRecorder.Factory NO_OP_PRODUCTION_COMPONENT_TIMING_RECORDER_FACTORY = new ProductionComponentTimingRecorder.Factory() { @Override public ProductionComponentTimingRecorder create(Object component) { return noOpProductionComponentTimingRecorder(); } }; private static final ProductionComponentTimingRecorder NO_OP_PRODUCTION_COMPONENT_TIMING_RECORDER = new ProductionComponentTimingRecorder() { @Override public ProducerTimingRecorder producerTimingRecorderFor(ProducerToken token) { return ProducerTimingRecorder.noOp(); } }; private static void logCreateException(RuntimeException e, ProductionComponentTimingRecorder.Factory factory, Object component) { logger.log(Level.SEVERE, "RuntimeException while calling ProductionComponentTimingRecorder.Factory.create on" + " factory " + factory + " with component " + component, e); } private static void logProducerTimingRecorderForException(RuntimeException e, ProductionComponentTimingRecorder recorder, ProducerToken token) { logger.log(Level.SEVERE, "RuntimeException while calling ProductionComponentTimingRecorder.producerTimingRecorderFor" + "on recorder " + recorder + " with token " + token, e); } private static void logProducerTimingRecorderMethodException(RuntimeException e, ProducerTimingRecorder recorder, String method) { logger.log(Level.SEVERE, "RuntimeException while calling ProducerTimingRecorder." + method + " on recorder " + recorder, e); } private TimingRecorders() { } }