All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dagger.producers.internal.DependencyMethodProducer Maven / Gradle / Ivy

There is a newer version: 2.52
Show newest version
/*
 * Copyright (C) 2018 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.internal;

import static com.google.common.util.concurrent.MoreExecutors.directExecutor;

import com.google.common.collect.MapMaker;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import dagger.producers.Producer;
import java.util.Collections;
import java.util.Set;

/**
 * Abstract class for implementing producers derived from methods on component dependencies.
 *
 * 

Unlike most other {@link CancellableProducer} implementations, cancelling the future returned * by a {@linkplain #newDependencyView dependency view} injected into an {@code @Produces} method * will actually cancel the underlying future. This is because the future comes from outside the * component's producer graph (including possibly from another object that isn't a component at * all), so if we don't cancel it when the user asks to cancel it, there might just be no way to * cancel it at all. */ public abstract class DependencyMethodProducer implements CancellableProducer { /** Weak set of all incomplete futures this producer has returned. */ private final Set> futures = Collections.newSetFromMap(new MapMaker().weakKeys()., Boolean>makeMap()); private boolean cancelled = false; /** Calls a method on a component dependency to get a future. */ protected abstract ListenableFuture callDependencyMethod(); @Override public final ListenableFuture get() { synchronized (futures) { if (cancelled) { return Futures.immediateCancelledFuture(); } final ListenableFuture future = callDependencyMethod(); if (!future.isDone() && futures.add(future)) { future.addListener( new Runnable() { @Override public void run() { synchronized (futures) { futures.remove(future); } } }, directExecutor()); } return future; } } @Override public final void cancel(boolean mayInterruptIfRunning) { synchronized (futures) { cancelled = true; for (ListenableFuture future : futures) { // futures is a concurrent set so that the concurrent removal that will happen here is not // a problem future.cancel(mayInterruptIfRunning); } } } @Override public final Producer newDependencyView() { return this; } @Override public final Producer newEntryPointView(final CancellationListener cancellationListener) { return new Producer() { private final Set> entryPointFutures = Collections.newSetFromMap( new MapMaker().weakKeys()., Boolean>makeMap()); @Override public ListenableFuture get() { final ListenableFuture future = DependencyMethodProducer.this.get(); if (!future.isDone() && entryPointFutures.add(future)) { future.addListener( new Runnable() { @Override public void run() { entryPointFutures.remove(future); if (future.isCancelled()) { // TODO(cgdecker): Make this also propagate the actual value that was passed for // mayInterruptIfRunning cancellationListener.onProducerFutureCancelled(true); } } }, directExecutor()); } return future; } }; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy