com.apollographql.apollo.internal.ApolloCallTracker Maven / Gradle / Ivy
/**
* Copyright 2018-2019 Amazon.com,
* Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
package com.apollographql.apollo.internal;
import com.amazonaws.mobileconnectors.appsync.AppSyncSubscriptionCall;
import com.apollographql.apollo.GraphQLCall;
import com.amazonaws.mobileconnectors.appsync.AppSyncMutationCall;
import com.amazonaws.mobileconnectors.appsync.AppSyncQueryCall;
import com.amazonaws.mobileconnectors.appsync.AppSyncPrefetch;
import com.amazonaws.mobileconnectors.appsync.AppSyncQueryWatcher;
import com.apollographql.apollo.IdleResourceCallback;
import com.apollographql.apollo.api.Mutation;
import com.apollographql.apollo.api.Operation;
import com.apollographql.apollo.api.OperationName;
import com.apollographql.apollo.api.Query;
import com.apollographql.apollo.api.Subscription;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nonnull;
import static com.apollographql.apollo.api.internal.Utils.checkNotNull;
/**
* ApolloCallTracker is responsible for keeping track of running {@link AppSyncPrefetch} & {@link AppSyncQueryCall}
* & {@link AppSyncMutationCall} & {@link AppSyncQueryWatcher} calls.
*/
@SuppressWarnings("WeakerAccess") public final class ApolloCallTracker {
private final Map> activePrefetchCalls = new HashMap<>();
private final Map> activeQueryCalls = new HashMap<>();
private final Map> activeMutationCalls = new HashMap<>();
private final Map> activeQueryWatchers = new HashMap<>();
private final AtomicInteger activeCallCount = new AtomicInteger();
private IdleResourceCallback idleResourceCallback;
public ApolloCallTracker() {
}
/**
* Adds provided {@link GraphQLCall} that is currently in progress.
*
* Note: This method needs to be called right before an apolloCall is executed.
*/
void registerCall(@Nonnull GraphQLCall call) {
checkNotNull(call, "call == null");
Operation operation = call.operation();
if (operation instanceof Query) {
registerQueryCall((AppSyncQueryCall) call);
} else if (operation instanceof Mutation) {
registerMutationCall((AppSyncMutationCall) call);
} else if (operation instanceof Subscription) {
} else {
throw new IllegalArgumentException("Unknown call type");
}
}
/**
* Removes provided {@link GraphQLCall} that finished his execution, if it is found, else throws an
* {@link AssertionError}.
*
* If the removal operation is successful and no active running calls are found, then the registered
* {@link ApolloCallTracker#idleResourceCallback} is invoked.
*
* Note: This method needs to be called right after an apolloCall is completed (whether successful or
* failed).
*/
void unregisterCall(@Nonnull GraphQLCall call) {
checkNotNull(call, "call == null");
Operation operation = call.operation();
if (operation instanceof Query) {
unregisterQueryCall((AppSyncQueryCall) call);
} else if (operation instanceof Mutation) {
unregisterMutationCall((AppSyncMutationCall) call);
} else if (operation instanceof Subscription) {
} else {
throw new IllegalArgumentException("Unknown call type");
}
}
/**
* Adds provided {@link AppSyncPrefetch} that is currently in progress.
*
* Note: This method needs to be called right before a prefetch call is executed.
*/
void registerPrefetchCall(@Nonnull AppSyncPrefetch appSyncPrefetch) {
checkNotNull(appSyncPrefetch, "appSyncPrefetch == null");
OperationName operationName = appSyncPrefetch.operation().name();
registerCall(activePrefetchCalls, operationName, appSyncPrefetch);
}
/**
* Removes provided {@link AppSyncPrefetch} that finished his execution, if it is found, else throws an
* {@link AssertionError}.
*
* If the removal operation is successful and no active running calls are found, then the registered
* {@link ApolloCallTracker#idleResourceCallback} is invoked.
*
* Note: This method needs to be called right after a prefetch call is completed (whether successful or
* failed).
*/
void unregisterPrefetchCall(@Nonnull AppSyncPrefetch appSyncPrefetch) {
checkNotNull(appSyncPrefetch, "appSyncPrefetch == null");
OperationName operationName = appSyncPrefetch.operation().name();
unregisterCall(activePrefetchCalls, operationName, appSyncPrefetch);
}
/**
* Returns currently active {@link AppSyncPrefetch} calls by operation name.
*
* @param operationName prefetch operation name
* @return set of active prefetch calls
*/
@Nonnull Set activePrefetchCalls(@Nonnull OperationName operationName) {
return activeCalls(activePrefetchCalls, operationName);
}
/**
* Adds provided {@link AppSyncQueryCall} that is currently in progress.
*
* Note: This method needs to be called right before an apolloCall is executed.
*/
void registerQueryCall(@Nonnull AppSyncQueryCall appSyncQueryCall) {
checkNotNull(appSyncQueryCall, "appSyncQueryCall == null");
OperationName operationName = appSyncQueryCall.operation().name();
registerCall(activeQueryCalls, operationName, appSyncQueryCall);
}
/**
* Removes provided {@link AppSyncQueryCall} that finished his execution, if it is found, else throws an
* {@link AssertionError}.
*
* If the removal operation is successful and no active running calls are found, then the registered
* {@link ApolloCallTracker#idleResourceCallback} is invoked.
*
* Note: This method needs to be called right after an apolloCall is completed (whether successful or
* failed).
*/
void unregisterQueryCall(@Nonnull AppSyncQueryCall appSyncQueryCall) {
checkNotNull(appSyncQueryCall, "appSyncQueryCall == null");
OperationName operationName = appSyncQueryCall.operation().name();
unregisterCall(activeQueryCalls, operationName, appSyncQueryCall);
}
/**
* Returns currently active {@link AppSyncQueryCall} calls by operation name.
*
* @param operationName query operation name
* @return set of active query calls
*/
@Nonnull Set activeQueryCalls(@Nonnull OperationName operationName) {
return activeCalls(activeQueryCalls, operationName);
}
/**
* Adds provided {@link AppSyncMutationCall} that is currently in progress.
*
* Note: This method needs to be called right before an apolloCall is executed.
*/
void registerMutationCall(@Nonnull AppSyncMutationCall appSyncMutationCall) {
checkNotNull(appSyncMutationCall, "appSyncMutationCall == null");
OperationName operationName = appSyncMutationCall.operation().name();
registerCall(activeMutationCalls, operationName, appSyncMutationCall);
}
/**
* Removes provided {@link AppSyncMutationCall} that finished his execution, if it is found, else throws an
* {@link AssertionError}.
*
* If the removal operation is successful and no active running calls are found, then the registered
* {@link ApolloCallTracker#idleResourceCallback} is invoked.
*
* Note: This method needs to be called right after an apolloCall is completed (whether successful or
* failed).
*/
void unregisterMutationCall(@Nonnull AppSyncMutationCall appSyncMutationCall) {
checkNotNull(appSyncMutationCall, "appSyncMutationCall == null");
OperationName operationName = appSyncMutationCall.operation().name();
unregisterCall(activeMutationCalls, operationName, appSyncMutationCall);
}
/**
* Returns currently active {@link AppSyncMutationCall} calls by operation name.
*
* @param operationName query operation name
* @return set of active mutation calls
*/
@Nonnull Set activeMutationCalls(@Nonnull OperationName operationName) {
return activeCalls(activeMutationCalls, operationName);
}
/**
* Adds provided {@link AppSyncQueryWatcher} that is currently in progress.
*
* Note: This method needs to be called right before
* {@link AppSyncQueryWatcher#enqueueAndWatch(GraphQLCall.Callback)}.
*/
void registerQueryWatcher(@Nonnull AppSyncQueryWatcher queryWatcher) {
checkNotNull(queryWatcher, "queryWatcher == null");
OperationName operationName = queryWatcher.operation().name();
registerCall(activeQueryWatchers, operationName, queryWatcher);
}
/**
* Removes provided {@link AppSyncQueryWatcher} that finished his execution, if it is found, else throws an
* {@link AssertionError}.
*
* If the removal operation is successful and no active running calls are found, then the registered
* {@link ApolloCallTracker#idleResourceCallback} is invoked.
*
* Note: This method needs to be called right after an apolloCall is completed (whether successful or
* failed).
*/
void unregisterQueryWatcher(@Nonnull AppSyncQueryWatcher queryWatcher) {
checkNotNull(queryWatcher, "queryWatcher == null");
OperationName operationName = queryWatcher.operation().name();
unregisterCall(activeQueryWatchers, operationName, queryWatcher);
}
/**
* Returns currently active {@link AppSyncQueryWatcher} query watchers by operation name.
*
* @param operationName query watcher operation name
* @return set of active query watchers
*/
@Nonnull Set activeQueryWatchers(@Nonnull OperationName operationName) {
return activeCalls(activeQueryWatchers, operationName);
}
/**
* Registers idleResourceCallback which is invoked when the apolloClient becomes idle.
*/
public synchronized void setIdleResourceCallback(IdleResourceCallback idleResourceCallback) {
this.idleResourceCallback = idleResourceCallback;
}
/**
* Returns a total count of in progress {@link GraphQLCall} & {@link AppSyncPrefetch} objects.
*/
public int activeCallsCount() {
return activeCallCount.get();
}
private void registerCall(Map> registry, OperationName operationName, CALL call) {
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (registry) {
Set calls = registry.get(operationName);
if (calls == null) {
calls = new HashSet<>();
registry.put(operationName, calls);
}
calls.add(call);
}
activeCallCount.incrementAndGet();
}
private void unregisterCall(Map> registry, OperationName operationName, CALL call) {
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (registry) {
Set calls = registry.get(operationName);
if (calls == null || !calls.remove(call)) {
throw new AssertionError("Call wasn't registered before");
}
if (calls.isEmpty()) {
registry.remove(operationName);
}
}
if (activeCallCount.decrementAndGet() == 0) {
notifyIdleResource();
}
}
private Set activeCalls(Map> registry, @Nonnull OperationName operationName) {
checkNotNull(operationName, "operationName == null");
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (registry) {
Set calls = registry.get(operationName);
return calls != null ? new HashSet<>(calls) : Collections.emptySet();
}
}
private void notifyIdleResource() {
IdleResourceCallback callback = idleResourceCallback;
if (callback != null) {
callback.onIdle();
}
}
}