Skip to content

Commit 0b356e1

Browse files
authored
fix: Cleaning up FirebaseApp state management (#476)
1 parent 062223d commit 0b356e1

15 files changed

+100
-143
lines changed

src/main/java/com/google/firebase/FirebaseApp.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,8 @@ public FirebaseOptions getOptions() {
277277
*/
278278
@Nullable
279279
String getProjectId() {
280+
checkNotDeleted();
281+
280282
// Try to get project ID from user-specified options.
281283
String projectId = options.getProjectId();
282284

@@ -314,8 +316,10 @@ public String toString() {
314316
}
315317

316318
/**
317-
* Deletes the {@link FirebaseApp} and all its data. All calls to this {@link FirebaseApp}
318-
* instance will throw once it has been called.
319+
* Deletes this {@link FirebaseApp} object, and releases any local state and managed resources
320+
* associated with it. All calls to this {@link FirebaseApp} instance will throw once this method
321+
* has been called. This also releases any managed resources allocated by other services
322+
* attached to this object instance (e.g. {@code FirebaseAuth}).
319323
*
320324
* <p>A no-op if delete was called before.
321325
*/

src/main/java/com/google/firebase/ImplFirebaseTrampolines.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import com.google.firebase.internal.NonNull;
2727

2828
import java.util.concurrent.Callable;
29-
import java.util.concurrent.ScheduledExecutorService;
3029
import java.util.concurrent.ScheduledFuture;
3130
import java.util.concurrent.ThreadFactory;
3231

src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import static com.google.common.base.Preconditions.checkArgument;
2020
import static com.google.common.base.Preconditions.checkNotNull;
21-
import static com.google.common.base.Preconditions.checkState;
2221

2322
import com.google.api.client.json.JsonFactory;
2423
import com.google.api.client.util.Clock;
@@ -164,7 +163,6 @@ public ApiFuture<String> createCustomTokenAsync(
164163

165164
private CallableOperation<String, FirebaseAuthException> createCustomTokenOp(
166165
final String uid, final Map<String, Object> developerClaims) {
167-
checkNotDestroyed();
168166
checkArgument(!Strings.isNullOrEmpty(uid), "uid must not be null or empty");
169167
final FirebaseTokenFactory tokenFactory = this.tokenFactory.get();
170168
return new CallableOperation<String, FirebaseAuthException>() {
@@ -208,7 +206,6 @@ public ApiFuture<String> createSessionCookieAsync(
208206

209207
private CallableOperation<String, FirebaseAuthException> createSessionCookieOp(
210208
final String idToken, final SessionCookieOptions options) {
211-
checkNotDestroyed();
212209
checkArgument(!Strings.isNullOrEmpty(idToken), "idToken must not be null or empty");
213210
checkNotNull(options, "options must not be null");
214211
final FirebaseUserManager userManager = getUserManager();
@@ -299,7 +296,6 @@ public ApiFuture<FirebaseToken> verifyIdTokenAsync(@NonNull String idToken) {
299296

300297
private CallableOperation<FirebaseToken, FirebaseAuthException> verifyIdTokenOp(
301298
final String idToken, final boolean checkRevoked) {
302-
checkNotDestroyed();
303299
checkArgument(!Strings.isNullOrEmpty(idToken), "ID token must not be null or empty");
304300
final FirebaseTokenVerifier verifier = getIdTokenVerifier(checkRevoked);
305301
return new CallableOperation<FirebaseToken, FirebaseAuthException>() {
@@ -380,7 +376,6 @@ public ApiFuture<FirebaseToken> verifySessionCookieAsync(String cookie, boolean
380376

381377
private CallableOperation<FirebaseToken, FirebaseAuthException> verifySessionCookieOp(
382378
final String cookie, final boolean checkRevoked) {
383-
checkNotDestroyed();
384379
checkArgument(!Strings.isNullOrEmpty(cookie), "Session cookie must not be null or empty");
385380
final FirebaseTokenVerifier sessionCookieVerifier = getSessionCookieVerifier(checkRevoked);
386381
return new CallableOperation<FirebaseToken, FirebaseAuthException>() {
@@ -434,7 +429,6 @@ public ApiFuture<Void> revokeRefreshTokensAsync(@NonNull String uid) {
434429
}
435430

436431
private CallableOperation<Void, FirebaseAuthException> revokeRefreshTokensOp(final String uid) {
437-
checkNotDestroyed();
438432
checkArgument(!Strings.isNullOrEmpty(uid), "uid must not be null or empty");
439433
final FirebaseUserManager userManager = getUserManager();
440434
return new CallableOperation<Void, FirebaseAuthException>() {
@@ -475,7 +469,6 @@ public ApiFuture<UserRecord> getUserAsync(@NonNull String uid) {
475469
}
476470

477471
private CallableOperation<UserRecord, FirebaseAuthException> getUserOp(final String uid) {
478-
checkNotDestroyed();
479472
checkArgument(!Strings.isNullOrEmpty(uid), "uid must not be null or empty");
480473
final FirebaseUserManager userManager = getUserManager();
481474
return new CallableOperation<UserRecord, FirebaseAuthException>() {
@@ -513,7 +506,6 @@ public ApiFuture<UserRecord> getUserByEmailAsync(@NonNull String email) {
513506

514507
private CallableOperation<UserRecord, FirebaseAuthException> getUserByEmailOp(
515508
final String email) {
516-
checkNotDestroyed();
517509
checkArgument(!Strings.isNullOrEmpty(email), "email must not be null or empty");
518510
final FirebaseUserManager userManager = getUserManager();
519511
return new CallableOperation<UserRecord, FirebaseAuthException>() {
@@ -551,7 +543,6 @@ public ApiFuture<UserRecord> getUserByPhoneNumberAsync(@NonNull String phoneNumb
551543

552544
private CallableOperation<UserRecord, FirebaseAuthException> getUserByPhoneNumberOp(
553545
final String phoneNumber) {
554-
checkNotDestroyed();
555546
checkArgument(!Strings.isNullOrEmpty(phoneNumber), "phone number must not be null or empty");
556547
final FirebaseUserManager userManager = getUserManager();
557548
return new CallableOperation<UserRecord, FirebaseAuthException>() {
@@ -620,7 +611,6 @@ public ApiFuture<ListUsersPage> listUsersAsync(@Nullable String pageToken, int m
620611

621612
private CallableOperation<ListUsersPage, FirebaseAuthException> listUsersOp(
622613
@Nullable final String pageToken, final int maxResults) {
623-
checkNotDestroyed();
624614
final FirebaseUserManager userManager = getUserManager();
625615
final DefaultUserSource source = new DefaultUserSource(userManager, jsonFactory);
626616
final ListUsersPage.Factory factory = new ListUsersPage.Factory(source, maxResults, pageToken);
@@ -661,7 +651,6 @@ public ApiFuture<UserRecord> createUserAsync(@NonNull UserRecord.CreateRequest r
661651

662652
private CallableOperation<UserRecord, FirebaseAuthException> createUserOp(
663653
final UserRecord.CreateRequest request) {
664-
checkNotDestroyed();
665654
checkNotNull(request, "create request must not be null");
666655
final FirebaseUserManager userManager = getUserManager();
667656
return new CallableOperation<UserRecord, FirebaseAuthException>() {
@@ -701,7 +690,6 @@ public ApiFuture<UserRecord> updateUserAsync(@NonNull UserRecord.UpdateRequest r
701690

702691
private CallableOperation<UserRecord, FirebaseAuthException> updateUserOp(
703692
final UserRecord.UpdateRequest request) {
704-
checkNotDestroyed();
705693
checkNotNull(request, "update request must not be null");
706694
final FirebaseUserManager userManager = getUserManager();
707695
return new CallableOperation<UserRecord, FirebaseAuthException>() {
@@ -754,7 +742,6 @@ public ApiFuture<Void> setCustomUserClaimsAsync(
754742

755743
private CallableOperation<Void, FirebaseAuthException> setCustomUserClaimsOp(
756744
final String uid, final Map<String, Object> claims) {
757-
checkNotDestroyed();
758745
checkArgument(!Strings.isNullOrEmpty(uid), "uid must not be null or empty");
759746
final FirebaseUserManager userManager = getUserManager();
760747
return new CallableOperation<Void, FirebaseAuthException>() {
@@ -793,7 +780,6 @@ public ApiFuture<Void> deleteUserAsync(String uid) {
793780
}
794781

795782
private CallableOperation<Void, FirebaseAuthException> deleteUserOp(final String uid) {
796-
checkNotDestroyed();
797783
checkArgument(!Strings.isNullOrEmpty(uid), "uid must not be null or empty");
798784
final FirebaseUserManager userManager = getUserManager();
799785
return new CallableOperation<Void, FirebaseAuthException>() {
@@ -876,7 +862,6 @@ public ApiFuture<UserImportResult> importUsersAsync(
876862

877863
private CallableOperation<UserImportResult, FirebaseAuthException> importUsersOp(
878864
final List<ImportUserRecord> users, final UserImportOptions options) {
879-
checkNotDestroyed();
880865
final UserImportRequest request = new UserImportRequest(users, options, jsonFactory);
881866
final FirebaseUserManager userManager = getUserManager();
882867
return new CallableOperation<UserImportResult, FirebaseAuthException>() {
@@ -931,7 +916,6 @@ public ApiFuture<GetUsersResult> getUsersAsync(@NonNull Collection<UserIdentifie
931916

932917
private CallableOperation<GetUsersResult, FirebaseAuthException> getUsersOp(
933918
@NonNull final Collection<UserIdentifier> identifiers) {
934-
checkNotDestroyed();
935919
checkNotNull(identifiers, "identifiers must not be null");
936920
checkArgument(identifiers.size() <= FirebaseUserManager.MAX_GET_ACCOUNTS_BATCH_SIZE,
937921
"identifiers parameter must have <= " + FirebaseUserManager.MAX_GET_ACCOUNTS_BATCH_SIZE
@@ -1004,7 +988,6 @@ public ApiFuture<DeleteUsersResult> deleteUsersAsync(List<String> uids) {
1004988

1005989
private CallableOperation<DeleteUsersResult, FirebaseAuthException> deleteUsersOp(
1006990
final List<String> uids) {
1007-
checkNotDestroyed();
1008991
checkNotNull(uids, "uids must not be null");
1009992
for (String uid : uids) {
1010993
UserRecord.checkUid(uid);
@@ -1178,7 +1161,6 @@ public ApiFuture<String> generateSignInWithEmailLinkAsync(
11781161

11791162
private CallableOperation<String, FirebaseAuthException> generateEmailActionLinkOp(
11801163
final EmailLinkType type, final String email, final ActionCodeSettings settings) {
1181-
checkNotDestroyed();
11821164
checkArgument(!Strings.isNullOrEmpty(email), "email must not be null or empty");
11831165
if (type == EmailLinkType.EMAIL_SIGNIN) {
11841166
checkNotNull(settings, "ActionCodeSettings must not be null when generating sign-in links");
@@ -1227,7 +1209,6 @@ public ApiFuture<OidcProviderConfig> createOidcProviderConfigAsync(
12271209

12281210
private CallableOperation<OidcProviderConfig, FirebaseAuthException>
12291211
createOidcProviderConfigOp(final OidcProviderConfig.CreateRequest request) {
1230-
checkNotDestroyed();
12311212
checkNotNull(request, "Create request must not be null.");
12321213
OidcProviderConfig.checkOidcProviderId(request.getProviderId());
12331214
final FirebaseUserManager userManager = getUserManager();
@@ -1271,7 +1252,6 @@ public ApiFuture<OidcProviderConfig> updateOidcProviderConfigAsync(
12711252

12721253
private CallableOperation<OidcProviderConfig, FirebaseAuthException> updateOidcProviderConfigOp(
12731254
final OidcProviderConfig.UpdateRequest request) {
1274-
checkNotDestroyed();
12751255
checkNotNull(request, "Update request must not be null.");
12761256
checkArgument(!request.getProperties().isEmpty(),
12771257
"Update request must have at least one property set.");
@@ -1316,7 +1296,6 @@ public ApiFuture<OidcProviderConfig> getOidcProviderConfigAsync(@NonNull String
13161296

13171297
private CallableOperation<OidcProviderConfig, FirebaseAuthException>
13181298
getOidcProviderConfigOp(final String providerId) {
1319-
checkNotDestroyed();
13201299
OidcProviderConfig.checkOidcProviderId(providerId);
13211300
final FirebaseUserManager userManager = getUserManager();
13221301
return new CallableOperation<OidcProviderConfig, FirebaseAuthException>() {
@@ -1400,7 +1379,6 @@ public ApiFuture<ListProviderConfigsPage<OidcProviderConfig>> listOidcProviderCo
14001379

14011380
private CallableOperation<ListProviderConfigsPage<OidcProviderConfig>, FirebaseAuthException>
14021381
listOidcProviderConfigsOp(@Nullable final String pageToken, final int maxResults) {
1403-
checkNotDestroyed();
14041382
final FirebaseUserManager userManager = getUserManager();
14051383
final DefaultOidcProviderConfigSource source = new DefaultOidcProviderConfigSource(userManager);
14061384
final ListProviderConfigsPage.Factory<OidcProviderConfig> factory =
@@ -1443,7 +1421,6 @@ public ApiFuture<Void> deleteOidcProviderConfigAsync(String providerId) {
14431421

14441422
private CallableOperation<Void, FirebaseAuthException> deleteOidcProviderConfigOp(
14451423
final String providerId) {
1446-
checkNotDestroyed();
14471424
OidcProviderConfig.checkOidcProviderId(providerId);
14481425
final FirebaseUserManager userManager = getUserManager();
14491426
return new CallableOperation<Void, FirebaseAuthException>() {
@@ -1490,7 +1467,6 @@ public ApiFuture<SamlProviderConfig> createSamlProviderConfigAsync(
14901467

14911468
private CallableOperation<SamlProviderConfig, FirebaseAuthException>
14921469
createSamlProviderConfigOp(final SamlProviderConfig.CreateRequest request) {
1493-
checkNotDestroyed();
14941470
checkNotNull(request, "Create request must not be null.");
14951471
SamlProviderConfig.checkSamlProviderId(request.getProviderId());
14961472
final FirebaseUserManager userManager = getUserManager();
@@ -1534,7 +1510,6 @@ public ApiFuture<SamlProviderConfig> updateSamlProviderConfigAsync(
15341510

15351511
private CallableOperation<SamlProviderConfig, FirebaseAuthException> updateSamlProviderConfigOp(
15361512
final SamlProviderConfig.UpdateRequest request) {
1537-
checkNotDestroyed();
15381513
checkNotNull(request, "Update request must not be null.");
15391514
checkArgument(!request.getProperties().isEmpty(),
15401515
"Update request must have at least one property set.");
@@ -1579,7 +1554,6 @@ public ApiFuture<SamlProviderConfig> getSamlProviderConfigAsync(@NonNull String
15791554

15801555
private CallableOperation<SamlProviderConfig, FirebaseAuthException>
15811556
getSamlProviderConfigOp(final String providerId) {
1582-
checkNotDestroyed();
15831557
SamlProviderConfig.checkSamlProviderId(providerId);
15841558
final FirebaseUserManager userManager = getUserManager();
15851559
return new CallableOperation<SamlProviderConfig, FirebaseAuthException>() {
@@ -1663,7 +1637,6 @@ public ApiFuture<ListProviderConfigsPage<SamlProviderConfig>> listSamlProviderCo
16631637

16641638
private CallableOperation<ListProviderConfigsPage<SamlProviderConfig>, FirebaseAuthException>
16651639
listSamlProviderConfigsOp(@Nullable final String pageToken, final int maxResults) {
1666-
checkNotDestroyed();
16671640
final FirebaseUserManager userManager = getUserManager();
16681641
final DefaultSamlProviderConfigSource source = new DefaultSamlProviderConfigSource(userManager);
16691642
final ListProviderConfigsPage.Factory<SamlProviderConfig> factory =
@@ -1706,7 +1679,6 @@ public ApiFuture<Void> deleteSamlProviderConfigAsync(String providerId) {
17061679

17071680
private CallableOperation<Void, FirebaseAuthException> deleteSamlProviderConfigOp(
17081681
final String providerId) {
1709-
checkNotDestroyed();
17101682
SamlProviderConfig.checkSamlProviderId(providerId);
17111683
final FirebaseUserManager userManager = getUserManager();
17121684
return new CallableOperation<Void, FirebaseAuthException>() {
@@ -1729,32 +1701,12 @@ <T> Supplier<T> threadSafeMemoize(final Supplier<T> supplier) {
17291701
public T get() {
17301702
checkNotNull(supplier);
17311703
synchronized (lock) {
1732-
checkNotDestroyed();
17331704
return supplier.get();
17341705
}
17351706
}
17361707
});
17371708
}
17381709

1739-
private void checkNotDestroyed() {
1740-
synchronized (lock) {
1741-
checkState(
1742-
!destroyed.get(),
1743-
"FirebaseAuth instance is no longer alive. This happens when "
1744-
+ "the parent FirebaseApp instance has been deleted.");
1745-
}
1746-
}
1747-
1748-
final void destroy() {
1749-
synchronized (lock) {
1750-
doDestroy();
1751-
destroyed.set(true);
1752-
}
1753-
}
1754-
1755-
/** Performs any additional required clean up. */
1756-
protected abstract void doDestroy();
1757-
17581710
protected abstract static class Builder<T extends Builder<T>> {
17591711

17601712
private FirebaseApp firebaseApp;

src/main/java/com/google/firebase/auth/FirebaseAuth.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,6 @@ public static synchronized FirebaseAuth getInstance(FirebaseApp app) {
6969
return service.getInstance();
7070
}
7171

72-
@Override
73-
protected void doDestroy() { }
74-
7572
private static FirebaseAuth fromApp(final FirebaseApp app) {
7673
return populateBuilderFromApp(builder(), app, null)
7774
.setTenantManager(new Supplier<TenantManager>() {
@@ -88,11 +85,6 @@ private static class FirebaseAuthService extends FirebaseService<FirebaseAuth> {
8885
FirebaseAuthService(FirebaseApp app) {
8986
super(SERVICE_ID, FirebaseAuth.fromApp(app));
9087
}
91-
92-
@Override
93-
public void destroy() {
94-
instance.destroy();
95-
}
9688
}
9789

9890
static Builder builder() {

src/main/java/com/google/firebase/auth/multitenancy/TenantAwareFirebaseAuth.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,6 @@ public ApiFuture<String> apply(FirebaseToken input) {
6969
}, MoreExecutors.directExecutor());
7070
}
7171

72-
@Override
73-
protected void doDestroy() {
74-
// Nothing extra needs to be destroyed.
75-
}
76-
7772
static TenantAwareFirebaseAuth fromApp(FirebaseApp app, String tenantId) {
7873
return populateBuilderFromApp(builder(), app, tenantId)
7974
.setTenantId(tenantId)

src/main/java/com/google/firebase/cloud/StorageClient.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,5 @@ private static class StorageClientService extends FirebaseService<StorageClient>
106106
StorageClientService(StorageClient client) {
107107
super(SERVICE_ID, client);
108108
}
109-
110-
@Override
111-
public void destroy() {
112-
// NOTE: We don't explicitly tear down anything here, but public methods of StorageClient
113-
// will now fail because calls to getOptions() and getToken() will hit FirebaseApp,
114-
// which will throw once the app is deleted.
115-
}
116109
}
117-
118110
}

src/main/java/com/google/firebase/database/FirebaseDatabase.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ static FirebaseDatabase createForTests(
173173
return db;
174174
}
175175

176-
/**
176+
/**
177177
* @return The version for this build of the Firebase Database client
178178
*/
179179
public static String getSdkVersion() {
@@ -358,6 +358,10 @@ DatabaseConfig getConfig() {
358358
return this.config;
359359
}
360360

361+
/**
362+
* Tears down the WebSocket connections and background threads started by this {@code
363+
* FirebaseDatabase} instance thus disconnecting from the remote database.
364+
*/
361365
void destroy() {
362366
synchronized (lock) {
363367
if (destroyed.get()) {

src/main/java/com/google/firebase/iid/FirebaseInstanceId.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,5 @@ private static class FirebaseInstanceIdService extends FirebaseService<FirebaseI
200200
FirebaseInstanceIdService(FirebaseApp app) {
201201
super(SERVICE_ID, new FirebaseInstanceId(app));
202202
}
203-
204-
@Override
205-
public void destroy() {
206-
// NOTE: We don't explicitly tear down anything here, but public methods of StorageClient
207-
// will now fail because calls to getOptions() and getToken() will hit FirebaseApp,
208-
// which will throw once the app is deleted.
209-
}
210203
}
211204
}

src/main/java/com/google/firebase/internal/FirebaseService.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
*
2929
* @param <T> Type of the service
3030
*/
31-
public abstract class FirebaseService<T> {
31+
public class FirebaseService<T> {
3232

3333
private final String id;
3434
protected final T instance;
@@ -62,5 +62,7 @@ public final T getInstance() {
6262
* Tear down this FirebaseService instance and the service object wrapped in it, cleaning up
6363
* any allocated resources in the process.
6464
*/
65-
public abstract void destroy();
65+
public void destroy() {
66+
// Child classes can override this method to implement any service-specific cleanup logic.
67+
}
6668
}

src/main/java/com/google/firebase/messaging/FirebaseMessaging.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -403,13 +403,6 @@ private static class FirebaseMessagingService extends FirebaseService<FirebaseMe
403403
FirebaseMessagingService(FirebaseApp app) {
404404
super(SERVICE_ID, FirebaseMessaging.fromApp(app));
405405
}
406-
407-
@Override
408-
public void destroy() {
409-
// NOTE: We don't explicitly tear down anything here, but public methods of FirebaseMessaging
410-
// will now fail because calls to getOptions() and getToken() will hit FirebaseApp,
411-
// which will throw once the app is deleted.
412-
}
413406
}
414407

415408
private static FirebaseMessaging fromApp(final FirebaseApp app) {

0 commit comments

Comments
 (0)