Skip to content

Commit 1b6c34f

Browse files
author
Brian Chen
authored
Throw error in subsequent function calls after the client is shutdown (#375)
1 parent b2b716a commit 1b6c34f

File tree

3 files changed

+29
-5
lines changed

3 files changed

+29
-5
lines changed

firebase-firestore/src/androidTest/java/com/google/firebase/firestore/FirestoreTest.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -953,4 +953,11 @@ public void testCanDisableAndEnableNetworking() {
953953
waitFor(firestore.disableNetwork());
954954
waitFor(firestore.enableNetwork());
955955
}
956+
957+
@Test
958+
public void testClientCallsAfterShutdownFails() {
959+
FirebaseFirestore firestore = testFirestore();
960+
waitFor(firestore.shutdown());
961+
expectError(() -> waitFor(firestore.disableNetwork()), "The client has already been shutdown");
962+
}
956963
}

firebase-firestore/src/main/java/com/google/firebase/firestore/FirebaseFirestore.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -333,11 +333,9 @@ public Task<Void> runBatch(@NonNull WriteBatch.Function batchFunction) {
333333

334334
@VisibleForTesting
335335
Task<Void> shutdown() {
336-
if (client == null) {
337-
return Tasks.forResult(null);
338-
} else {
339-
return client.shutdown();
340-
}
336+
// The client must be initialized to ensure that all subsequent API usage throws an exception.
337+
this.ensureClientConfigured();
338+
return client.shutdown();
341339
}
342340

343341
@VisibleForTesting

firebase-firestore/src/main/java/com/google/firebase/firestore/core/FirestoreClient.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public final class FirestoreClient implements RemoteStore.RemoteStoreCallback {
7575
private RemoteStore remoteStore;
7676
private SyncEngine syncEngine;
7777
private EventManager eventManager;
78+
private boolean isShutdown = false;
7879

7980
// LRU-related
8081
@Nullable private LruGarbageCollector.Scheduler lruScheduler;
@@ -125,15 +126,20 @@ public FirestoreClient(
125126
}
126127

127128
public Task<Void> disableNetwork() {
129+
this.verifyNotShutdown();
128130
return asyncQueue.enqueue(() -> remoteStore.disableNetwork());
129131
}
130132

131133
public Task<Void> enableNetwork() {
134+
this.verifyNotShutdown();
132135
return asyncQueue.enqueue(() -> remoteStore.enableNetwork());
133136
}
134137

135138
/** Shuts down this client, cancels all writes / listeners, and releases all resources. */
136139
public Task<Void> shutdown() {
140+
if (this.isShutdown) {
141+
return Tasks.forResult(null);
142+
}
137143
credentialsProvider.removeChangeListener();
138144
return asyncQueue.enqueue(
139145
() -> {
@@ -142,23 +148,27 @@ public Task<Void> shutdown() {
142148
if (lruScheduler != null) {
143149
lruScheduler.stop();
144150
}
151+
this.isShutdown = true;
145152
});
146153
}
147154

148155
/** Starts listening to a query. */
149156
public QueryListener listen(
150157
Query query, ListenOptions options, EventListener<ViewSnapshot> listener) {
158+
this.verifyNotShutdown();
151159
QueryListener queryListener = new QueryListener(query, options, listener);
152160
asyncQueue.enqueueAndForget(() -> eventManager.addQueryListener(queryListener));
153161
return queryListener;
154162
}
155163

156164
/** Stops listening to a query previously listened to. */
157165
public void stopListening(QueryListener listener) {
166+
this.verifyNotShutdown();
158167
asyncQueue.enqueueAndForget(() -> eventManager.removeQueryListener(listener));
159168
}
160169

161170
public Task<Document> getDocumentFromLocalCache(DocumentKey docKey) {
171+
this.verifyNotShutdown();
162172
return asyncQueue
163173
.enqueue(() -> localStore.readDocument(docKey))
164174
.continueWith(
@@ -180,6 +190,7 @@ public Task<Document> getDocumentFromLocalCache(DocumentKey docKey) {
180190
}
181191

182192
public Task<ViewSnapshot> getDocumentsFromLocalCache(Query query) {
193+
this.verifyNotShutdown();
183194
return asyncQueue.enqueue(
184195
() -> {
185196
ImmutableSortedMap<DocumentKey, Document> docs = localStore.executeQuery(query);
@@ -196,6 +207,7 @@ public Task<ViewSnapshot> getDocumentsFromLocalCache(Query query) {
196207

197208
/** Writes mutations. The returned task will be notified when it's written to the backend. */
198209
public Task<Void> write(final List<Mutation> mutations) {
210+
this.verifyNotShutdown();
199211
final TaskCompletionSource<Void> source = new TaskCompletionSource<>();
200212
asyncQueue.enqueueAndForget(() -> syncEngine.writeMutations(mutations, source));
201213
return source.getTask();
@@ -204,6 +216,7 @@ public Task<Void> write(final List<Mutation> mutations) {
204216
/** Tries to execute the transaction in updateFunction up to retries times. */
205217
public <TResult> Task<TResult> transaction(
206218
Function<Transaction, Task<TResult>> updateFunction, int retries) {
219+
this.verifyNotShutdown();
207220
return AsyncQueue.callTask(
208221
asyncQueue.getExecutor(),
209222
() -> syncEngine.transaction(asyncQueue, updateFunction, retries));
@@ -255,6 +268,12 @@ private void initialize(Context context, User user, boolean usePersistence, long
255268
remoteStore.start();
256269
}
257270

271+
private void verifyNotShutdown() {
272+
if (this.isShutdown) {
273+
throw new IllegalArgumentException("The client has already been shutdown");
274+
}
275+
}
276+
258277
@Override
259278
public void handleRemoteEvent(RemoteEvent remoteEvent) {
260279
syncEngine.handleRemoteEvent(remoteEvent);

0 commit comments

Comments
 (0)