From 79315a793b621aded642911d6ed148b8a7c0cd26 Mon Sep 17 00:00:00 2001 From: hiranya911 Date: Wed, 10 Jan 2018 15:32:20 -0800 Subject: [PATCH 1/9] Dropping support for GAE 7 --- .../java/com/google/firebase/FirebaseApp.java | 7 +- .../connection/NettyWebSocketClient.java | 7 +- .../firebase/database/core/GaePlatform.java | 31 ++------- .../firebase/database/core/JvmPlatform.java | 7 +- .../firebase/database/core/Platform.java | 2 - .../database/core/ThreadInitializer.java | 20 ------ .../database/core/ThreadPoolEventTarget.java | 35 ++++------ .../database/utilities/DefaultRunLoop.java | 43 +++---------- .../internal/FirebaseThreadManagers.java | 10 +-- .../firebase/internal/GaeExecutorService.java | 2 +- .../SingleThreadScheduledExecutor.java | 64 +++++++++++++++++++ .../database/core/GaePlatformTest.java | 13 ---- .../utilities/DefaultRunLoopTest.java | 18 +++--- 13 files changed, 109 insertions(+), 150 deletions(-) create mode 100644 src/main/java/com/google/firebase/internal/SingleThreadScheduledExecutor.java diff --git a/src/main/java/com/google/firebase/FirebaseApp.java b/src/main/java/com/google/firebase/FirebaseApp.java index 029b1ea57..f8aec6699 100644 --- a/src/main/java/com/google/firebase/FirebaseApp.java +++ b/src/main/java/com/google/firebase/FirebaseApp.java @@ -34,11 +34,10 @@ import com.google.common.io.BaseEncoding; import com.google.firebase.internal.FirebaseAppStore; import com.google.firebase.internal.FirebaseService; -import com.google.firebase.internal.GaeThreadFactory; import com.google.firebase.internal.NonNull; import com.google.firebase.internal.Nullable; -import com.google.firebase.internal.RevivingScheduledExecutor; +import com.google.firebase.internal.SingleThreadScheduledExecutor; import com.google.firebase.tasks.Task; import com.google.firebase.tasks.Tasks; import java.io.IOException; @@ -357,8 +356,8 @@ private ScheduledExecutorService ensureScheduledExecutorService() { synchronized (lock) { checkNotDeleted(); if (scheduledExecutor == null) { - scheduledExecutor = new RevivingScheduledExecutor(threadManager.getThreadFactory(), - "firebase-scheduled-worker", GaeThreadFactory.isAvailable()); + scheduledExecutor = new SingleThreadScheduledExecutor( + "firebase-scheduled-worker", getThreadFactory()); } } } diff --git a/src/main/java/com/google/firebase/database/connection/NettyWebSocketClient.java b/src/main/java/com/google/firebase/database/connection/NettyWebSocketClient.java index d71a1eebc..c85632443 100644 --- a/src/main/java/com/google/firebase/database/connection/NettyWebSocketClient.java +++ b/src/main/java/com/google/firebase/database/connection/NettyWebSocketClient.java @@ -5,8 +5,7 @@ import static com.google.common.base.Preconditions.checkState; import com.google.common.base.Strings; -import com.google.firebase.internal.GaeThreadFactory; -import com.google.firebase.internal.RevivingScheduledExecutor; +import com.google.firebase.internal.SingleThreadScheduledExecutor; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; @@ -66,8 +65,8 @@ class NettyWebSocketClient implements WebsocketConnection.WSClient { this.uri = checkNotNull(uri, "uri must not be null"); this.eventHandler = checkNotNull(eventHandler, "event handler must not be null"); this.channelHandler = new WebSocketClientHandler(uri, userAgent, eventHandler); - this.executorService = new RevivingScheduledExecutor( - threadFactory, "firebase-websocket-worker", GaeThreadFactory.isAvailable()); + this.executorService = new SingleThreadScheduledExecutor( + "firebase-websocket-worker", threadFactory); this.group = new NioEventLoopGroup(1, this.executorService); } diff --git a/src/main/java/com/google/firebase/database/core/GaePlatform.java b/src/main/java/com/google/firebase/database/core/GaePlatform.java index b7698f3de..ed874fe34 100644 --- a/src/main/java/com/google/firebase/database/core/GaePlatform.java +++ b/src/main/java/com/google/firebase/database/core/GaePlatform.java @@ -29,11 +29,12 @@ import com.google.firebase.database.logging.Logger; import com.google.firebase.database.utilities.DefaultRunLoop; import com.google.firebase.internal.GaeThreadFactory; -import com.google.firebase.internal.RevivingScheduledExecutor; +import com.google.firebase.internal.SingleThreadScheduledExecutor; import java.util.List; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; /** * Represents a Google AppEngine platform. @@ -58,21 +59,21 @@ public Logger newLogger(Context ctx, Logger.Level level, List components return new DefaultLogger(level, components); } - private ThreadFactory getGaeThreadFactory() { + private ThreadFactory getThreadFactory() { return ImplFirebaseTrampolines.getThreadFactory(firebaseApp); } @Override public EventTarget newEventTarget(Context ctx) { - RevivingScheduledExecutor eventExecutor = - new RevivingScheduledExecutor(getGaeThreadFactory(), "FirebaseDatabaseEventTarget", true); + ThreadPoolExecutor eventExecutor = new SingleThreadScheduledExecutor( + "FirebaseDatabaseEventTarget", getThreadFactory()); return new ThreadPoolEventTarget(eventExecutor); } @Override public RunLoop newRunLoop(final Context context) { final LogWrapper logger = context.getLogger(RunLoop.class); - return new DefaultRunLoop(getGaeThreadFactory(), /* periodicRestart= */ true, context) { + return new DefaultRunLoop(getThreadFactory()) { @Override public void handleException(Throwable e) { logger.error(DefaultRunLoop.messageForException(e), e); @@ -108,24 +109,4 @@ public String getPlatformVersion() { public PersistenceManager createPersistenceManager(Context ctx, String namespace) { return null; } - - @Override - public ThreadInitializer getThreadInitializer() { - return new ThreadInitializer() { - @Override - public void setName(Thread t, String name) { - // Unsupported by GAE - } - - @Override - public void setDaemon(Thread t, boolean isDaemon) { - // Unsupported by GAE - } - - @Override - public void setUncaughtExceptionHandler(Thread t, Thread.UncaughtExceptionHandler handler) { - // Unsupported by GAE - } - }; - } } diff --git a/src/main/java/com/google/firebase/database/core/JvmPlatform.java b/src/main/java/com/google/firebase/database/core/JvmPlatform.java index 30a2e25df..0dd02f452 100644 --- a/src/main/java/com/google/firebase/database/core/JvmPlatform.java +++ b/src/main/java/com/google/firebase/database/core/JvmPlatform.java @@ -51,7 +51,7 @@ public Logger newLogger(Context ctx, Logger.Level level, List components @Override public EventTarget newEventTarget(Context ctx) { ThreadFactory threadFactory = ImplFirebaseTrampolines.getThreadFactory(firebaseApp); - return new ThreadPoolEventTarget(threadFactory, ThreadInitializer.defaultInstance); + return new ThreadPoolEventTarget(threadFactory); } @Override @@ -94,9 +94,4 @@ public String getPlatformVersion() { public PersistenceManager createPersistenceManager(Context ctx, String namespace) { return null; } - - @Override - public ThreadInitializer getThreadInitializer() { - return ThreadInitializer.defaultInstance; - } } diff --git a/src/main/java/com/google/firebase/database/core/Platform.java b/src/main/java/com/google/firebase/database/core/Platform.java index 5e701c408..320a851b5 100644 --- a/src/main/java/com/google/firebase/database/core/Platform.java +++ b/src/main/java/com/google/firebase/database/core/Platform.java @@ -48,6 +48,4 @@ PersistentConnection newPersistentConnection( String getPlatformVersion(); PersistenceManager createPersistenceManager(Context ctx, String firebaseId); - - ThreadInitializer getThreadInitializer(); } diff --git a/src/main/java/com/google/firebase/database/core/ThreadInitializer.java b/src/main/java/com/google/firebase/database/core/ThreadInitializer.java index 23c3cab88..662c526d8 100644 --- a/src/main/java/com/google/firebase/database/core/ThreadInitializer.java +++ b/src/main/java/com/google/firebase/database/core/ThreadInitializer.java @@ -16,28 +16,8 @@ package com.google.firebase.database.core; -import java.lang.Thread.UncaughtExceptionHandler; - public interface ThreadInitializer { - ThreadInitializer defaultInstance = - new ThreadInitializer() { - @Override - public void setName(Thread t, String name) { - t.setName(name); - } - - @Override - public void setDaemon(Thread t, boolean isDaemon) { - t.setDaemon(isDaemon); - } - - @Override - public void setUncaughtExceptionHandler(Thread t, UncaughtExceptionHandler handler) { - t.setUncaughtExceptionHandler(handler); - } - }; - void setName(Thread t, String name); void setDaemon(Thread t, boolean isDaemon); diff --git a/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java b/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java index 80b99dfbb..751bd821f 100644 --- a/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java +++ b/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java @@ -18,35 +18,25 @@ import static com.google.common.base.Preconditions.checkNotNull; +import com.google.firebase.internal.SingleThreadScheduledExecutor; import java.lang.Thread.UncaughtExceptionHandler; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; /** ThreadPoolEventTarget is an event target using a configurable threadpool. */ -class ThreadPoolEventTarget implements EventTarget, UncaughtExceptionHandler { +class ThreadPoolEventTarget implements EventTarget { private final ThreadPoolExecutor executor; private UncaughtExceptionHandler exceptionHandler; - public ThreadPoolEventTarget( - final ThreadFactory wrappedFactory, final ThreadInitializer threadInitializer) { - int poolSize = 1; - BlockingQueue queue = new LinkedBlockingQueue<>(); - - executor = new ThreadPoolExecutor(poolSize, poolSize, 3, TimeUnit.SECONDS, queue, - new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - Thread thread = wrappedFactory.newThread(r); - threadInitializer.setName(thread, "FirebaseDatabaseEventTarget"); - threadInitializer.setDaemon(thread, true); - threadInitializer.setUncaughtExceptionHandler(thread, ThreadPoolEventTarget.this); - return thread; - } - }); + public ThreadPoolEventTarget(final ThreadFactory wrappedFactory) { + executor = new SingleThreadScheduledExecutor( + "FirebaseDatabaseEventTarget", wrappedFactory) { + @Override + protected void handleException(Throwable t) { + uncaughtException(t); + } + }; } public ThreadPoolEventTarget(final ThreadPoolExecutor executor) { @@ -86,14 +76,13 @@ synchronized void setExceptionHandler(UncaughtExceptionHandler exceptionHandler) this.exceptionHandler = exceptionHandler; } - @Override - public void uncaughtException(Thread t, Throwable e) { + public void uncaughtException(Throwable e) { UncaughtExceptionHandler delegate; synchronized (this) { delegate = exceptionHandler; } if (delegate != null) { - delegate.uncaughtException(t, e); + delegate.uncaughtException(Thread.currentThread(), e); } } } diff --git a/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java b/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java index d43deb6b3..2f69e8158 100644 --- a/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java +++ b/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java @@ -18,12 +18,9 @@ import com.google.firebase.database.DatabaseException; import com.google.firebase.database.FirebaseDatabase; -import com.google.firebase.database.annotations.Nullable; -import com.google.firebase.database.core.Context; -import com.google.firebase.database.core.RepoManager; import com.google.firebase.database.core.RunLoop; -import com.google.firebase.internal.RevivingScheduledExecutor; +import com.google.firebase.internal.SingleThreadScheduledExecutor; import java.lang.Thread.UncaughtExceptionHandler; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -36,41 +33,17 @@ public abstract class DefaultRunLoop implements RunLoop { private ScheduledThreadPoolExecutor executor; private UncaughtExceptionHandler exceptionHandler; - /** Creates a DefaultRunLoop that does not periodically restart its threads. */ - public DefaultRunLoop(ThreadFactory threadFactory) { - this(threadFactory, false, null); - } - /** * Creates a DefaultRunLoop that optionally restarts its threads periodically. If 'context' is * provided, these restarts will automatically interrupt and resume all Repo connections. */ - public DefaultRunLoop( - final ThreadFactory threadFactory, - final boolean periodicRestart, - @Nullable final Context context) { - executor = - new RevivingScheduledExecutor(threadFactory, "FirebaseDatabaseWorker", periodicRestart) { - @Override - protected void handleException(Throwable throwable) { - DefaultRunLoop.this.handleExceptionInternal(throwable); - } - - @Override - protected void beforeRestart() { - if (context != null) { - RepoManager.interrupt(context); - } - } - - @Override - protected void afterRestart() { - if (context != null) { - RepoManager.resume(context); - } - } - }; - + public DefaultRunLoop(final ThreadFactory threadFactory) { + executor = new SingleThreadScheduledExecutor("FirebaseDatabaseWorker", threadFactory) { + @Override + protected void handleException(Throwable t) { + handleExceptionInternal(t); + } + }; // Core threads don't time out, this only takes effect when we drop the number of required // core threads executor.setKeepAliveTime(3, TimeUnit.SECONDS); diff --git a/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java b/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java index 0e503ba36..b289676a7 100644 --- a/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java +++ b/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java @@ -36,15 +36,7 @@ public class FirebaseThreadManagers { private static final Logger logger = LoggerFactory.getLogger(FirebaseThreadManagers.class); - public static final ThreadManager DEFAULT_THREAD_MANAGER; - - static { - if (GaeThreadFactory.isAvailable()) { - DEFAULT_THREAD_MANAGER = new GaeThreadManager(); - } else { - DEFAULT_THREAD_MANAGER = new DefaultThreadManager(); - } - } + public static final ThreadManager DEFAULT_THREAD_MANAGER = new DefaultThreadManager(); /** * An abstract ThreadManager implementation that uses the same executor service diff --git a/src/main/java/com/google/firebase/internal/GaeExecutorService.java b/src/main/java/com/google/firebase/internal/GaeExecutorService.java index 6d1b323e8..d3de6cae6 100644 --- a/src/main/java/com/google/firebase/internal/GaeExecutorService.java +++ b/src/main/java/com/google/firebase/internal/GaeExecutorService.java @@ -179,7 +179,7 @@ private static ExecutorService newExecutorService( && ((GaeThreadFactory) threadFactory).isUsingBackgroundThreads(); if (background) { // Create a thread pool with long-lived threads if background thread support is available. - return new RevivingScheduledExecutor(threadFactory, threadName, true); + return null; } else { // Create an executor that creates a new thread for each submitted task, when background // thread support is not available. diff --git a/src/main/java/com/google/firebase/internal/SingleThreadScheduledExecutor.java b/src/main/java/com/google/firebase/internal/SingleThreadScheduledExecutor.java new file mode 100644 index 000000000..938c2333b --- /dev/null +++ b/src/main/java/com/google/firebase/internal/SingleThreadScheduledExecutor.java @@ -0,0 +1,64 @@ +/* + * Copyright 2017 Google Inc. + * + * 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 com.google.firebase.internal; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; + +public class SingleThreadScheduledExecutor extends ScheduledThreadPoolExecutor { + + public SingleThreadScheduledExecutor(String name, ThreadFactory threadFactory) { + super(1, new ThreadFactoryBuilder() + .setNameFormat(name) + .setDaemon(true) + .setThreadFactory(threadFactory) + .build()); + setRemoveOnCancelPolicy(true); + } + + @Override + protected final void afterExecute(Runnable runnable, Throwable throwable) { + super.afterExecute(runnable, throwable); + if (throwable == null && runnable instanceof Future) { + Future future = (Future) runnable; + try { + // Not all Futures will be done, e.g. when used with scheduledAtFixedRate + if (future.isDone()) { + future.get(); + } + } catch (CancellationException ce) { + // Cancellation exceptions are okay, we expect them to happen sometimes + } catch (ExecutionException ee) { + throwable = ee.getCause(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + if (throwable != null) { + handleException(throwable); + } + } + + protected void handleException(Throwable t) { + + } +} diff --git a/src/test/java/com/google/firebase/database/core/GaePlatformTest.java b/src/test/java/com/google/firebase/database/core/GaePlatformTest.java index 032de1b4a..c4f2e2f4f 100644 --- a/src/test/java/com/google/firebase/database/core/GaePlatformTest.java +++ b/src/test/java/com/google/firebase/database/core/GaePlatformTest.java @@ -1,8 +1,6 @@ package com.google.firebase.database.core; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -77,17 +75,6 @@ protected ThreadFactory getThreadFactory() { assertEquals("AppEngine/AdminJava", platform.getUserAgent(ctx)); assertEquals("gae-" + FirebaseDatabase.getSdkVersion(), platform.getPlatformVersion()); assertNull(platform.createPersistenceManager(ctx, "test")); - - ThreadInitializer threadInitializer = platform.getThreadInitializer(); - Thread t = new Thread(); - threadInitializer.setName(t, "test-name"); - threadInitializer.setDaemon(t, true); - threadInitializer.setUncaughtExceptionHandler(t, Mockito.mock( - Thread.UncaughtExceptionHandler.class)); - assertNotEquals("test-name", t.getName()); - assertFalse(t.isDaemon()); - assertNotNull(t.getUncaughtExceptionHandler()); - } finally { TestOnlyImplFirebaseTrampolines.clearInstancesForTest(); } diff --git a/src/test/java/com/google/firebase/database/utilities/DefaultRunLoopTest.java b/src/test/java/com/google/firebase/database/utilities/DefaultRunLoopTest.java index 1029ebef8..a552c0aa6 100644 --- a/src/test/java/com/google/firebase/database/utilities/DefaultRunLoopTest.java +++ b/src/test/java/com/google/firebase/database/utilities/DefaultRunLoopTest.java @@ -17,10 +17,10 @@ package com.google.firebase.database.utilities; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import com.google.firebase.database.TestHelpers; import com.google.firebase.testing.TestUtils; import java.lang.Thread.UncaughtExceptionHandler; import java.util.ArrayList; @@ -38,19 +38,21 @@ public class DefaultRunLoopTest { public void testLifecycle() { MockRunLoop runLoop = new MockRunLoop(); try { - assertEquals(0, runLoop.getThreadPool().getCorePoolSize()); + assertEquals(0, runLoop.getThreadPool().getActiveCount()); runLoop.scheduleNow(new Runnable() { @Override public void run() { } }); - assertEquals(1, runLoop.getThreadPool().getCorePoolSize()); + assertEquals(1, runLoop.getThreadPool().getActiveCount()); runLoop.shutdown(); + // It will be a few seconds before the core thread actually terminate. assertEquals(0, runLoop.getThreadPool().getCorePoolSize()); runLoop.restart(); assertEquals(1, runLoop.getThreadPool().getCorePoolSize()); + assertEquals(1, runLoop.getThreadPool().getActiveCount()); assertTrue(runLoop.errors.isEmpty()); } finally { @@ -62,13 +64,13 @@ public void run() { public void testScheduleWithDelay() throws ExecutionException, InterruptedException { MockRunLoop runLoop = new MockRunLoop(); try { - assertEquals(0, runLoop.getThreadPool().getCorePoolSize()); + assertEquals(0, runLoop.getThreadPool().getActiveCount()); ScheduledFuture future = runLoop.schedule(new Runnable() { @Override public void run() { } }, 500L); - assertEquals(1, runLoop.getThreadPool().getCorePoolSize()); + assertEquals(1, runLoop.getThreadPool().getActiveCount()); future.get(); assertTrue(runLoop.errors.isEmpty()); @@ -91,15 +93,15 @@ public void uncaughtException(Thread t, Throwable e) { assertSame(exceptionHandler, runLoop.getExceptionHandler()); try { - assertEquals(0, runLoop.getThreadPool().getCorePoolSize()); + assertEquals(0, runLoop.getThreadPool().getActiveCount()); runLoop.scheduleNow(new Runnable() { @Override public void run() { throw new RuntimeException("test error"); } }); - assertEquals(1, runLoop.getThreadPool().getCorePoolSize()); - semaphore.acquire(); + assertEquals(1, runLoop.getThreadPool().getActiveCount()); + TestHelpers.waitFor(semaphore); synchronized (runLoop.errors) { if (runLoop.errors.isEmpty()) { From 0e71bd43577f52d7adc1e6fe63967e121d84ad60 Mon Sep 17 00:00:00 2001 From: hiranya911 Date: Thu, 11 Jan 2018 16:46:45 -0800 Subject: [PATCH 2/9] Removed GaeThreadFactory, GaeExecutorService and RevivingScheduledExecutor --- .../firebase/database/core/GaePlatform.java | 6 +- .../database/core/ThreadPoolEventTarget.java | 19 +- .../database/utilities/DefaultRunLoop.java | 3 +- .../internal/FirebaseThreadManagers.java | 98 +------- .../firebase/internal/GaeExecutorService.java | 195 -------------- .../firebase/internal/GaeThreadFactory.java | 136 +--------- .../internal/RevivingScheduledExecutor.java | 213 ---------------- .../google/firebase/tasks/TaskExecutors.java | 15 +- .../database/core/GaePlatformTest.java | 6 - .../internal/FirebaseThreadManagersTest.java | 47 +--- .../internal/GaeExecutorServiceTest.java | 129 ---------- .../RevivingScheduledExecutorTest.java | 238 ------------------ 12 files changed, 33 insertions(+), 1072 deletions(-) delete mode 100644 src/main/java/com/google/firebase/internal/GaeExecutorService.java delete mode 100644 src/main/java/com/google/firebase/internal/RevivingScheduledExecutor.java delete mode 100644 src/test/java/com/google/firebase/internal/GaeExecutorServiceTest.java delete mode 100644 src/test/java/com/google/firebase/internal/RevivingScheduledExecutorTest.java diff --git a/src/main/java/com/google/firebase/database/core/GaePlatform.java b/src/main/java/com/google/firebase/database/core/GaePlatform.java index ed874fe34..0332177fb 100644 --- a/src/main/java/com/google/firebase/database/core/GaePlatform.java +++ b/src/main/java/com/google/firebase/database/core/GaePlatform.java @@ -51,7 +51,7 @@ public GaePlatform(FirebaseApp firebaseApp) { } public static boolean isActive() { - return GaeThreadFactory.isAvailable(); + return System.getProperty("com.google.appengine.runtime.environment") != null; } @Override @@ -65,9 +65,7 @@ private ThreadFactory getThreadFactory() { @Override public EventTarget newEventTarget(Context ctx) { - ThreadPoolExecutor eventExecutor = new SingleThreadScheduledExecutor( - "FirebaseDatabaseEventTarget", getThreadFactory()); - return new ThreadPoolEventTarget(eventExecutor); + return new ThreadPoolEventTarget(getThreadFactory()); } @Override diff --git a/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java b/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java index 751bd821f..475cb3940 100644 --- a/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java +++ b/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java @@ -16,22 +16,20 @@ package com.google.firebase.database.core; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.firebase.internal.SingleThreadScheduledExecutor; import java.lang.Thread.UncaughtExceptionHandler; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; -/** ThreadPoolEventTarget is an event target using a configurable threadpool. */ +/** ThreadPoolEventTarget is an event target using a configurable thread pool. */ class ThreadPoolEventTarget implements EventTarget { private final ThreadPoolExecutor executor; private UncaughtExceptionHandler exceptionHandler; - public ThreadPoolEventTarget(final ThreadFactory wrappedFactory) { + ThreadPoolEventTarget(final ThreadFactory wrappedFactory) { executor = new SingleThreadScheduledExecutor( - "FirebaseDatabaseEventTarget", wrappedFactory) { + "firebase-database-event-target", wrappedFactory) { @Override protected void handleException(Throwable t) { uncaughtException(t); @@ -39,10 +37,6 @@ protected void handleException(Throwable t) { }; } - public ThreadPoolEventTarget(final ThreadPoolExecutor executor) { - this.executor = checkNotNull(executor); - } - @Override public void postEvent(Runnable r) { executor.execute(r); @@ -76,11 +70,8 @@ synchronized void setExceptionHandler(UncaughtExceptionHandler exceptionHandler) this.exceptionHandler = exceptionHandler; } - public void uncaughtException(Throwable e) { - UncaughtExceptionHandler delegate; - synchronized (this) { - delegate = exceptionHandler; - } + private void uncaughtException(Throwable e) { + UncaughtExceptionHandler delegate = getExceptionHandler(); if (delegate != null) { delegate.uncaughtException(Thread.currentThread(), e); } diff --git a/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java b/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java index 2f69e8158..2ed1e7203 100644 --- a/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java +++ b/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java @@ -38,7 +38,7 @@ public abstract class DefaultRunLoop implements RunLoop { * provided, these restarts will automatically interrupt and resume all Repo connections. */ public DefaultRunLoop(final ThreadFactory threadFactory) { - executor = new SingleThreadScheduledExecutor("FirebaseDatabaseWorker", threadFactory) { + executor = new SingleThreadScheduledExecutor("firebase-database-worker", threadFactory) { @Override protected void handleException(Throwable t) { handleExceptionInternal(t); @@ -80,6 +80,7 @@ private void handleExceptionInternal(Throwable e) { } } + // TODO: Remove this extension point public abstract void handleException(Throwable e); public ScheduledExecutorService getExecutorService() { diff --git a/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java b/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java index b289676a7..6ee60fd9e 100644 --- a/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java +++ b/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java @@ -16,8 +16,6 @@ package com.google.firebase.internal; -import static com.google.common.base.Preconditions.checkState; - import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.firebase.FirebaseApp; import com.google.firebase.ThreadManager; @@ -36,14 +34,14 @@ public class FirebaseThreadManagers { private static final Logger logger = LoggerFactory.getLogger(FirebaseThreadManagers.class); - public static final ThreadManager DEFAULT_THREAD_MANAGER = new DefaultThreadManager(); + public static final ThreadManager DEFAULT_THREAD_MANAGER = new GlobalThreadManager(); /** - * An abstract ThreadManager implementation that uses the same executor service + * A {@code ThreadManager} implementation that uses the same executor service * across all active apps. The executor service is initialized when the first app is initialized, * and terminated when the last app is deleted. This class is thread safe. */ - abstract static class GlobalThreadManager extends ThreadManager { + static class GlobalThreadManager extends ThreadManager { private final Object lock = new Object(); private final Set apps = new HashSet<>(); @@ -53,7 +51,13 @@ abstract static class GlobalThreadManager extends ThreadManager { protected ExecutorService getExecutor(FirebaseApp app) { synchronized (lock) { if (executorService == null) { - executorService = doInit(); + logger.debug("Initializing default global executor"); + ThreadFactory threadFactory = new ThreadFactoryBuilder() + .setNameFormat("firebase-default-%d") + .setDaemon(true) + .setThreadFactory(getThreadFactory()) + .build(); + executorService = Executors.newCachedThreadPool(threadFactory); } apps.add(app.getName()); return executorService; @@ -64,93 +68,15 @@ protected ExecutorService getExecutor(FirebaseApp app) { protected void releaseExecutor(FirebaseApp app, ExecutorService executor) { synchronized (lock) { if (apps.remove(app.getName()) && apps.isEmpty()) { - doCleanup(executorService); + logger.debug("Shutting down default global executor"); + executorService.shutdownNow(); executorService = null; } } } - /** - * Initializes the executor service. Called when the first application is initialized. - */ - protected abstract ExecutorService doInit(); - - /** - * Cleans up the executor service. Called when the last application is deleted. - */ - protected abstract void doCleanup(ExecutorService executorService); - } - - private static class DefaultThreadManager extends GlobalThreadManager { - - @Override - protected ExecutorService doInit() { - // Create threads as daemons to ensure JVM exit when all foreground jobs are complete. - ThreadFactory threadFactory = new ThreadFactoryBuilder() - .setNameFormat("firebase-default-%d") - .setDaemon(true) - .setThreadFactory(getThreadFactory()) - .build(); - return Executors.newCachedThreadPool(threadFactory); - } - - @Override - protected void doCleanup(ExecutorService executorService) { - logger.debug("Shutting down default executor"); - executorService.shutdownNow(); - } - - @Override protected ThreadFactory getThreadFactory() { return Executors.defaultThreadFactory(); } } - - /** - * The ThreadManager implementation that will be used by default in the Google App Engine - * environment. - * - *

Auto-scaling: Creates an ExecutorService backed by the request-scoped ThreadFactory. This - * can be used for any short-lived task, such as the ones submitted by components like - * FirebaseAuth. {@link #getThreadFactory()} throws an exception, since long-lived threads - * cannot be supported. Therefore task scheduling and RTDB will not work. - * - *

Manual-scaling: Creates a single-threaded ExecutorService backed by the background - * ThreadFactory. Keeps the threads alive indefinitely by periodically restarting them (see - * {@link RevivingScheduledExecutor}). Threads will be terminated only when the method - * {@link #releaseExecutor(FirebaseApp, ExecutorService)} is invoked. The - * {@link #getThreadFactory()} also returns the background ThreadFactory enabling other - * components in the SDK to start long-lived threads when necessary. Therefore task scheduling - * and RTDB can be supported as if running on the regular JVM. - * - *

Basic-scaling: Behavior is similar to manual-scaling. Since the threads are kept alive - * indefinitely, prevents the GAE idle instance shutdown. Developers are advised to use - * a custom ThreadManager implementation if idle instance shutdown should be supported. In - * general, a ThreadManager implementation that uses the request-scoped ThreadFactory, or the - * background ThreadFactory with specific keep-alive times can easily facilitate GAE idle - * instance shutdown. Note that this often comes at the cost of losing scheduled tasks and RTDB - * support. Therefore, for these features, manual-scaling is the recommended GAE deployment mode - * regardless of the ThreadManager implementation used. - */ - private static class GaeThreadManager extends GlobalThreadManager { - - @Override - protected ExecutorService doInit() { - return new GaeExecutorService("gae-firebase-default"); - } - - @Override - protected void doCleanup(ExecutorService executorService) { - executorService.shutdownNow(); - } - - @Override - protected ThreadFactory getThreadFactory() { - GaeThreadFactory threadFactory = GaeThreadFactory.getInstance(); - checkState(threadFactory.isUsingBackgroundThreads(), - "Failed to initialize a GAE background thread factory. Background thread support " - + "is required to create long-lived threads."); - return threadFactory; - } - } } diff --git a/src/main/java/com/google/firebase/internal/GaeExecutorService.java b/src/main/java/com/google/firebase/internal/GaeExecutorService.java deleted file mode 100644 index d3de6cae6..000000000 --- a/src/main/java/com/google/firebase/internal/GaeExecutorService.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * 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 com.google.firebase.internal; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; - -import com.google.common.base.Strings; -import com.google.common.collect.ImmutableList; - -import java.util.Collection; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.SynchronousQueue; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicReference; - -/** - * An ExecutorService instance that can operate in the Google App Engine environment. When - * available, uses background thread support to initialize an ExecutorService with long-lived - * threads. Otherwise, creates an ExecutorService that spawns short-lived threads as tasks - * are submitted. The actual ExecutorService implementation is lazy-loaded to prevent making - * unnecessary RPC calls to the GAE's native ThreadFactory mechanism. - */ -class GaeExecutorService implements ExecutorService { - - private final AtomicReference executor = new AtomicReference<>(); - private final String threadName; - private final ThreadFactory threadFactory; - private boolean shutdown; - - GaeExecutorService(String threadName) { - this(threadName, GaeThreadFactory.getInstance()); - } - - GaeExecutorService(String threadName, ThreadFactory threadFactory) { - checkArgument(!Strings.isNullOrEmpty(threadName)); - this.threadName = threadName; - this.threadFactory = threadFactory; - } - - private ExecutorService ensureExecutorService() { - ExecutorService executorService = executor.get(); - if (executorService == null) { - synchronized (executor) { - checkState(!shutdown); - executorService = executor.get(); - if (executorService == null) { - executorService = newExecutorService(threadFactory, threadName); - executor.compareAndSet(null, executorService); - } - } - } - return executorService; - } - - @Override - public Future submit(Callable task) { - return ensureExecutorService().submit(task); - } - - @Override - public Future submit(Runnable task, T result) { - return ensureExecutorService().submit(task, result); - } - - @Override - public Future submit(Runnable task) { - return ensureExecutorService().submit(task); - } - - @Override - public List> invokeAll(Collection> tasks) - throws InterruptedException { - return ensureExecutorService().invokeAll(tasks); - } - - @Override - public List> invokeAll( - Collection> tasks, long timeout, TimeUnit unit) - throws InterruptedException { - return ensureExecutorService().invokeAll(tasks, timeout, unit); - } - - @Override - public T invokeAny(Collection> tasks) - throws InterruptedException, ExecutionException { - return ensureExecutorService().invokeAny(tasks); - } - - @Override - public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - return ensureExecutorService().invokeAny(tasks, timeout, unit); - } - - @Override - public void shutdown() { - synchronized (executor) { - ExecutorService executorService = executor.get(); - if (executorService != null && !shutdown) { - executorService.shutdown(); - } - shutdown = true; - } - } - - @Override - public List shutdownNow() { - synchronized (executor) { - ExecutorService executorService = executor.get(); - List result; - if (executorService != null && !shutdown) { - result = executorService.shutdownNow(); - } else { - result = ImmutableList.of(); - } - shutdown = true; - return result; - } - } - - @Override - public boolean isShutdown() { - synchronized (executor) { - return shutdown; - } - } - - @Override - public boolean isTerminated() { - synchronized (executor) { - if (!shutdown) { - return false; - } - ExecutorService executorService = executor.get(); - return executorService == null || executorService.isTerminated(); - } - } - - @Override - public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { - ExecutorService executorService; - synchronized (executor) { - executorService = executor.get(); - } - // call await outside the lock - return executorService == null || executorService.awaitTermination(timeout, unit); - } - - @Override - public void execute(Runnable command) { - ensureExecutorService().execute(command); - } - - private static ExecutorService newExecutorService( - ThreadFactory threadFactory, String threadName) { - boolean background = threadFactory instanceof GaeThreadFactory - && ((GaeThreadFactory) threadFactory).isUsingBackgroundThreads(); - if (background) { - // Create a thread pool with long-lived threads if background thread support is available. - return null; - } else { - // Create an executor that creates a new thread for each submitted task, when background - // thread support is not available. - return new ThreadPoolExecutor( - 0, - Integer.MAX_VALUE, - 0L, - TimeUnit.SECONDS, - new SynchronousQueue(), - threadFactory); - } - } -} diff --git a/src/main/java/com/google/firebase/internal/GaeThreadFactory.java b/src/main/java/com/google/firebase/internal/GaeThreadFactory.java index 3791dad78..9b13358d1 100644 --- a/src/main/java/com/google/firebase/internal/GaeThreadFactory.java +++ b/src/main/java/com/google/firebase/internal/GaeThreadFactory.java @@ -16,150 +16,16 @@ package com.google.firebase.internal; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.lang.reflect.InvocationTargetException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicReference; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * GaeThreadFactory is a thread factory that works on App Engine. It uses background threads on * manually-scaled GAE backends and request-scoped threads on automatically scaled instances. * *

This class is thread-safe. */ -public class GaeThreadFactory implements ThreadFactory { +public class GaeThreadFactory { - private static final Logger logger = LoggerFactory.getLogger(GaeThreadFactory.class); - - public static final ExecutorService DEFAULT_EXECUTOR = - new GaeExecutorService("LegacyFirebaseDefault"); private static final String GAE_THREAD_MANAGER_CLASS = "com.google.appengine.api.ThreadManager"; - private static final GaeThreadFactory instance = new GaeThreadFactory(); - private final AtomicReference threadFactory = new AtomicReference<>(null); - - private GaeThreadFactory() {} - - public static GaeThreadFactory getInstance() { - return instance; - } /** Returns whether GaeThreadFactory can be used on this system (true for GAE). */ - public static boolean isAvailable() { - try { - Class.forName(GAE_THREAD_MANAGER_CLASS); - return System.getProperty("com.google.appengine.runtime.environment") != null; - } catch (ClassNotFoundException e) { - return false; - } - } - - private static ThreadFactory createBackgroundFactory() - throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, - IllegalAccessException { - Class gaeThreadManager = Class.forName(GAE_THREAD_MANAGER_CLASS); - return (ThreadFactory) gaeThreadManager.getMethod("backgroundThreadFactory").invoke(null); - } - - private static ThreadFactory createRequestScopedFactory() - throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, - IllegalAccessException { - Class gaeThreadManager = Class.forName(GAE_THREAD_MANAGER_CLASS); - return (ThreadFactory) gaeThreadManager.getMethod("currentRequestThreadFactory").invoke(null); - } - - @Override - public Thread newThread(Runnable r) { - ThreadFactoryWrapper wrapper = threadFactory.get(); - if (wrapper != null) { - return wrapper.getThreadFactory().newThread(r); - } - return initThreadFactory(r); - } - - /** - * Checks whether background thread support is available in the current environment. This method - * forces the ThreadFactory to get fully initialized (if not already initialized), by running a - * no-op thread. - * - * @return true if background thread support is available, and false otherwise. - */ - boolean isUsingBackgroundThreads() { - ThreadFactoryWrapper wrapper = threadFactory.get(); - if (wrapper != null) { - return wrapper.isUsingBackgroundThreads(); - } - - // Create a no-op thread to force initialize the ThreadFactory implementation. - // Start the resulting thread, since GAE code seems to expect that. - initThreadFactory(new Runnable() { - @Override - public void run() {} - }).start(); - return threadFactory.get().isUsingBackgroundThreads(); - } - - private Thread initThreadFactory(Runnable r) { - ThreadFactory threadFactory; - boolean usesBackgroundThreads = false; - Thread thread; - // Since we can't tell manually-scaled GAE instances apart until we spawn a thread (which - // sends an RPC and thus is done after class initialization), we initialize both of GAE's - // thread factories here and discard one once we detect that we are running in an - // automatically scaled instance. - // - // Note: It's fine if multiple threads access this block at the same time. - try { - try { - threadFactory = createBackgroundFactory(); - thread = threadFactory.newThread(r); - usesBackgroundThreads = true; - } catch (IllegalStateException e) { - logger.info("Falling back to GAE's request-scoped threads. Firebase requires " - + "manually-scaled instances for most operations."); - threadFactory = createRequestScopedFactory(); - thread = threadFactory.newThread(r); - } - } catch (ClassNotFoundException - | InvocationTargetException - | NoSuchMethodException - | IllegalAccessException e) { - threadFactory = - new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - logger.warn("Failed to initialize native GAE thread factory. " - + "GaeThreadFactory cannot be used in a non-GAE environment."); - return null; - } - }; - thread = null; - } - - ThreadFactoryWrapper wrapper = new ThreadFactoryWrapper(threadFactory, usesBackgroundThreads); - this.threadFactory.compareAndSet(null, wrapper); - return thread; - } - - private static class ThreadFactoryWrapper { - - private final ThreadFactory threadFactory; - private final boolean usingBackgroundThreads; - - private ThreadFactoryWrapper(ThreadFactory threadFactory, boolean usingBackgroundThreads) { - this.threadFactory = checkNotNull(threadFactory); - this.usingBackgroundThreads = usingBackgroundThreads; - } - - ThreadFactory getThreadFactory() { - return threadFactory; - } - boolean isUsingBackgroundThreads() { - return usingBackgroundThreads; - } - } } diff --git a/src/main/java/com/google/firebase/internal/RevivingScheduledExecutor.java b/src/main/java/com/google/firebase/internal/RevivingScheduledExecutor.java deleted file mode 100644 index efa989943..000000000 --- a/src/main/java/com/google/firebase/internal/RevivingScheduledExecutor.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * 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 com.google.firebase.internal; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.VisibleForTesting; - -import java.security.AccessControlException; -import java.util.concurrent.Callable; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.RunnableScheduledFuture; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * RevivingScheduledExecutor is an implementation of ScheduledThreadPoolExecutor that uses one - * periodically restarting worker thread as its work queue. This allows customers of this class to - * use this executor on App Engine despite App Engine's thread-lifetime limitations. - */ -public class RevivingScheduledExecutor extends ScheduledThreadPoolExecutor { - - private static final Logger logger = LoggerFactory.getLogger(RevivingScheduledExecutor.class); - - /** Exception to throw to shut down the core threads. */ - private static final RuntimeException REVIVE_THREAD_EXCEPTION = new RuntimeException( - "Restarting Firebase Worker Thread. This exception is expected to occur periodically " - + "when deployed in the App Engine environment, and can be ignored."); - - /** The lifetime of a thread. Maximum lifetime of a thread on GAE is 24 hours. */ - private static final long PERIODIC_RESTART_INTERVAL_MS = TimeUnit.HOURS.toMillis(12); - - /** - * Time by which we offset restarts to ensure that not all threads die at the same time. This is - * meant to decrease cross-thread liveliness issues during restarts. - */ - private static final long PERIODIC_RESTART_OFFSET_MS = TimeUnit.MINUTES.toMillis(5); - - private static final AtomicInteger INSTANCE_COUNTER = new AtomicInteger(0); - - private final long initialDelayMs; - private final long timeoutMs; - - // Flag set before throwing a REVIVE_THREAD_EXCEPTION and unset once a new thread has been - // created. Used to call afterRestart() appropriately. - private AtomicBoolean requestedRestart = new AtomicBoolean(); - - /** - * Creates a new RevivingScheduledExecutor that optionally restarts its worker thread every twelve - * hours. - * - * @param threadFactory Thread factory to use to restart threads. - * @param threadName Name of the threads in the pool. - * @param periodicRestart Periodically restart its worked threads. - */ - public RevivingScheduledExecutor( - final ThreadFactory threadFactory, final String threadName, final boolean periodicRestart) { - this( - threadFactory, - threadName, - periodicRestart ? PERIODIC_RESTART_OFFSET_MS * INSTANCE_COUNTER.get() : 0, - periodicRestart ? PERIODIC_RESTART_INTERVAL_MS : -1); - } - - @VisibleForTesting - RevivingScheduledExecutor( - final ThreadFactory threadFactory, - final String threadName, - final long initialDelayMs, - final long timeoutMs) { - super(0); - checkNotNull(threadFactory, "threadFactory must not be null"); - INSTANCE_COUNTER.incrementAndGet(); - this.initialDelayMs = initialDelayMs; - this.timeoutMs = timeoutMs; - setRemoveOnCancelPolicy(true); - setThreadFactory( - new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - logger.debug("Creating new thread for: {}", threadName); - Thread thread = threadFactory.newThread(r); - try { - thread.setName(threadName); - thread.setDaemon(true); - } catch (AccessControlException ignore) { - // Unsupported on App Engine. - } - if (requestedRestart.getAndSet(false)) { - afterRestart(); - } - return thread; - } - }); - } - - @Override - public void execute(Runnable runnable) { - // This gets called when the execute() method from Executor is directly invoked. - ensureRunning(); - super.execute(runnable); - } - - @Override - protected RunnableScheduledFuture decorateTask( - Runnable runnable, RunnableScheduledFuture task) { - // This gets called by ScheduledThreadPoolExecutor before scheduling a Runnable. - ensureRunning(); - return task; - } - - @Override - protected RunnableScheduledFuture decorateTask( - Callable callable, RunnableScheduledFuture task) { - // This gets called by ScheduledThreadPoolExecutor before scheduling a Callable. - ensureRunning(); - return task; - } - - @Override - protected void afterExecute(Runnable runnable, Throwable throwable) { - super.afterExecute(runnable, throwable); - if (throwable == null && runnable instanceof Future) { - Future future = (Future) runnable; - try { - // Not all Futures will be done, e.g. when used with scheduledAtFixedRate - if (future.isDone()) { - future.get(); - } - } catch (CancellationException ce) { - // Cancellation exceptions are okay, we expect them to happen sometimes - } catch (ExecutionException ee) { - throwable = ee.getCause(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - if (throwable == REVIVE_THREAD_EXCEPTION) { - // Re-throwing this exception will kill the thread and cause - // ScheduledThreadPoolExecutor to - // spawn a new thread. - throw (RuntimeException) throwable; - } else if (throwable != null) { - handleException(throwable); - } - } - - /** - * Called when an exception occurs during execution of a Runnable/Callable. The default - * implementation does nothing. - */ - protected void handleException(Throwable throwable) {} - - /** Called before the worker thread gets shutdown before a restart. */ - protected void beforeRestart() {} - - /** Called after the worker thread got recreated after a restart. */ - protected void afterRestart() {} - - private synchronized void ensureRunning() { - if (getCorePoolSize() == 0) { - setCorePoolSize(1); - schedulePeriodicShutdown(); - } - } - - private void schedulePeriodicShutdown() { - if (timeoutMs >= 0) { - @SuppressWarnings("unused") - Future possiblyIgnoredError = - schedule( - new Runnable() { - @Override - public void run() { - // We have to manually reschedule this task here as periodic tasks get - // cancelled after - // throwing exceptions. - @SuppressWarnings("unused") - Future possiblyIgnoredError1 = - RevivingScheduledExecutor.this.schedule( - this, timeoutMs, TimeUnit.MILLISECONDS); - requestedRestart.set(true); - beforeRestart(); - throw REVIVE_THREAD_EXCEPTION; - } - }, - initialDelayMs + timeoutMs, - TimeUnit.MILLISECONDS); - } - } -} diff --git a/src/main/java/com/google/firebase/tasks/TaskExecutors.java b/src/main/java/com/google/firebase/tasks/TaskExecutors.java index 19674d1d1..90c57c636 100644 --- a/src/main/java/com/google/firebase/tasks/TaskExecutors.java +++ b/src/main/java/com/google/firebase/tasks/TaskExecutors.java @@ -39,6 +39,7 @@ public class TaskExecutors { * for now for backward compatibility, since technically it is part of the public API. */ public static final Executor DEFAULT_THREAD_POOL; + /** An Executor that uses the calling thread. */ static final Executor DIRECT = new Executor() { @@ -49,15 +50,11 @@ public void execute(@NonNull Runnable command) { }; static { - if (GaeThreadFactory.isAvailable()) { - DEFAULT_THREAD_POOL = GaeThreadFactory.DEFAULT_EXECUTOR; - } else { - ThreadFactory threadFactory = new ThreadFactoryBuilder() - .setNameFormat("task-exec-%d") - .setDaemon(true) - .build(); - DEFAULT_THREAD_POOL = Executors.newCachedThreadPool(threadFactory); - } + ThreadFactory threadFactory = new ThreadFactoryBuilder() + .setNameFormat("task-exec-%d") + .setDaemon(true) + .build(); + DEFAULT_THREAD_POOL = Executors.newCachedThreadPool(threadFactory); } private TaskExecutors() {} diff --git a/src/test/java/com/google/firebase/database/core/GaePlatformTest.java b/src/test/java/com/google/firebase/database/core/GaePlatformTest.java index c4f2e2f4f..ba4e04fd2 100644 --- a/src/test/java/com/google/firebase/database/core/GaePlatformTest.java +++ b/src/test/java/com/google/firebase/database/core/GaePlatformTest.java @@ -12,7 +12,6 @@ import com.google.firebase.ThreadManager; import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.database.logging.Logger; -import com.google.firebase.internal.GaeThreadFactory; import com.google.firebase.internal.NonNull; import com.google.firebase.testing.ServiceAccount; import com.google.firebase.testing.TestUtils; @@ -26,11 +25,6 @@ public class GaePlatformTest { - @Test - public void testIsActive() { - assertEquals(GaeThreadFactory.isAvailable(), GaePlatform.isActive()); - } - @Test public void testGaePlatform() { final AtomicInteger count = new AtomicInteger(0); diff --git a/src/test/java/com/google/firebase/internal/FirebaseThreadManagersTest.java b/src/test/java/com/google/firebase/internal/FirebaseThreadManagersTest.java index 7a0d2d104..9a201146e 100644 --- a/src/test/java/com/google/firebase/internal/FirebaseThreadManagersTest.java +++ b/src/test/java/com/google/firebase/internal/FirebaseThreadManagersTest.java @@ -16,7 +16,6 @@ package com.google.firebase.internal; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; @@ -34,11 +33,8 @@ import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.ThreadFactory; import org.junit.After; -import org.junit.Assume; import org.junit.Test; public class FirebaseThreadManagersTest { @@ -50,24 +46,19 @@ public void tearDown() { @Test public void testGlobalThreadManager() { - MockThreadManager threadManager = new MockThreadManager(); + GlobalThreadManager threadManager = new GlobalThreadManager(); FirebaseOptions options = new FirebaseOptions.Builder() .setCredentials(new MockGoogleCredentials()) .setThreadManager(threadManager) .build(); FirebaseApp defaultApp = FirebaseApp.initializeApp(options); - assertEquals(1, threadManager.initCount); ExecutorService exec1 = threadManager.getExecutor(defaultApp); - assertEquals(1, threadManager.initCount); - assertFalse(exec1.isShutdown()); - ExecutorService exec2 = threadManager.getExecutor(defaultApp); - assertEquals(1, threadManager.initCount); - assertFalse(exec2.isShutdown()); // Should return the same executor for both invocations. assertSame(exec1, exec2); + assertFalse(exec1.isShutdown()); threadManager.releaseExecutor(defaultApp, exec1); assertTrue(exec1.isShutdown()); @@ -75,21 +66,19 @@ public void testGlobalThreadManager() { @Test public void testGlobalThreadManagerWithMultipleApps() { - MockThreadManager threadManager = new MockThreadManager(); + GlobalThreadManager threadManager = new GlobalThreadManager(); FirebaseOptions options = new FirebaseOptions.Builder() .setCredentials(new MockGoogleCredentials()) .build(); FirebaseApp defaultApp = FirebaseApp.initializeApp(options); FirebaseApp customApp = FirebaseApp.initializeApp(options, "customApp"); - assertEquals(0, threadManager.initCount); ExecutorService exec1 = threadManager.getExecutor(defaultApp); ExecutorService exec2 = threadManager.getExecutor(customApp); - assertEquals(1, threadManager.initCount); - assertFalse(exec1.isShutdown()); // Should return the same executor for both invocations. assertSame(exec1, exec2); + assertFalse(exec1.isShutdown()); threadManager.releaseExecutor(defaultApp, exec1); assertFalse(exec1.isShutdown()); @@ -100,16 +89,14 @@ public void testGlobalThreadManagerWithMultipleApps() { @Test public void testGlobalThreadManagerReInit() { - MockThreadManager threadManager = new MockThreadManager(); + GlobalThreadManager threadManager = new GlobalThreadManager(); FirebaseOptions options = new FirebaseOptions.Builder() .setCredentials(new MockGoogleCredentials()) .setThreadManager(threadManager) .build(); FirebaseApp defaultApp = FirebaseApp.initializeApp(options); - assertEquals(1, threadManager.initCount); ExecutorService exec1 = threadManager.getExecutor(defaultApp); - assertEquals(1, threadManager.initCount); assertFalse(exec1.isShutdown()); // Simulate app.delete() @@ -118,7 +105,6 @@ public void testGlobalThreadManagerReInit() { // Simulate app re-init ExecutorService exec2 = threadManager.getExecutor(defaultApp); - assertEquals(2, threadManager.initCount); assertNotSame(exec1, exec2); threadManager.releaseExecutor(defaultApp, exec2); @@ -127,8 +113,6 @@ public void testGlobalThreadManagerReInit() { @Test public void testDefaultThreadManager() throws Exception { - Assume.assumeFalse(GaeThreadFactory.isAvailable()); - FirebaseOptions options = new FirebaseOptions.Builder() .setCredentials(new MockGoogleCredentials()) .build(); @@ -157,25 +141,4 @@ public Void call() throws Exception { // expected } } - - private static class MockThreadManager extends GlobalThreadManager { - - private int initCount = 0; - - @Override - protected ExecutorService doInit() { - initCount++; - return Executors.newSingleThreadExecutor(); - } - - @Override - protected void doCleanup(ExecutorService executorService) { - executorService.shutdownNow(); - } - - @Override - protected ThreadFactory getThreadFactory() { - return Executors.defaultThreadFactory(); - } - } } diff --git a/src/test/java/com/google/firebase/internal/GaeExecutorServiceTest.java b/src/test/java/com/google/firebase/internal/GaeExecutorServiceTest.java deleted file mode 100644 index 48b2678cd..000000000 --- a/src/test/java/com/google/firebase/internal/GaeExecutorServiceTest.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * 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 com.google.firebase.internal; - -import static com.google.firebase.database.TestHelpers.waitFor; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import com.google.common.collect.ImmutableList; -import com.google.firebase.testing.TestUtils; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.Semaphore; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.Test; - -public class GaeExecutorServiceTest { - - @Test - public void testShutdownBeforeUse() throws InterruptedException { - CountingThreadFactory threadFactory = new CountingThreadFactory(); - GaeExecutorService executorService = new GaeExecutorService("test", threadFactory); - assertFalse(executorService.isShutdown()); - assertFalse(executorService.isTerminated()); - - assertEquals(ImmutableList.of(), executorService.shutdownNow()); - assertTrue(executorService.isShutdown()); - assertTrue(executorService.isTerminated()); - assertTrue(executorService.awaitTermination(1, TimeUnit.SECONDS)); - assertEquals(0, threadFactory.counter.get()); - - executorService = new GaeExecutorService("test", threadFactory); - assertFalse(executorService.isShutdown()); - assertFalse(executorService.isTerminated()); - - executorService.shutdownNow(); - assertTrue(executorService.isShutdown()); - assertTrue(executorService.isTerminated()); - assertTrue(executorService.awaitTermination( - TestUtils.TEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)); - assertEquals(0, threadFactory.counter.get()); - } - - @Test - public void testSubmit() throws InterruptedException, ExecutionException { - CountingThreadFactory threadFactory = new CountingThreadFactory(); - GaeExecutorService executorService = new GaeExecutorService("test", threadFactory); - - final Semaphore semaphore = new Semaphore(0); - Future future = executorService.submit(new Runnable() { - @Override - public void run() { - semaphore.release(); - } - }); - assertNotNull(future); - waitFor(semaphore); - - future = executorService.submit(new Runnable() { - @Override - public void run() { - semaphore.release(); - } - }, "result"); - assertNotNull(future); - waitFor(semaphore); - assertEquals("result", future.get()); - - future = executorService.submit(new Callable() { - @Override - public Object call() throws Exception { - semaphore.release(); - return "result2"; - } - }); - assertNotNull(future); - waitFor(semaphore); - assertEquals("result2", future.get()); - - executorService.execute(new Runnable() { - @Override - public void run() { - semaphore.release(); - } - }); - waitFor(semaphore); - - assertEquals(4, threadFactory.counter.get()); - - executorService.shutdown(); - assertTrue(executorService.awaitTermination( - TestUtils.TEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)); - assertTrue(executorService.isShutdown()); - assertTrue(executorService.isTerminated()); - } - - private static class CountingThreadFactory implements ThreadFactory { - - private final AtomicInteger counter = new AtomicInteger(0); - private final ThreadFactory delegate = Executors.defaultThreadFactory(); - - @Override - public Thread newThread(Runnable r) { - counter.incrementAndGet(); - return delegate.newThread(r); - } - } -} diff --git a/src/test/java/com/google/firebase/internal/RevivingScheduledExecutorTest.java b/src/test/java/com/google/firebase/internal/RevivingScheduledExecutorTest.java deleted file mode 100644 index 3a4da947c..000000000 --- a/src/test/java/com/google/firebase/internal/RevivingScheduledExecutorTest.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * 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 com.google.firebase.internal; - -import java.lang.Thread.UncaughtExceptionHandler; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.Semaphore; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; -import org.junit.Assert; -import org.junit.Test; - -public class RevivingScheduledExecutorTest { - - private static final ThreadFactory THREAD_FACTORY = new ExceptionCatchingThreadFactory(); - - @Test - public void testAppEngineRunnable() throws InterruptedException { - final Semaphore semaphore = new Semaphore(0); - final Set threadIds = new HashSet<>(); - - RevivingScheduledExecutor executor = - new RevivingScheduledExecutor(THREAD_FACTORY, "testAppEngineRunnable", 0, 100); - - for (int i = 0; i < 50; ++i) { - // We delay the execution to give the cleanup handler a chance to run. Otherwise, the - // Executor's BlockingQueue will execute all Runnables before the internal thread gets - // replaced. - Thread.sleep(10); - executor.execute( - new Runnable() { - @Override - public void run() { - threadIds.add(Thread.currentThread().getId()); - semaphore.release(); - } - }); - } - - try { - Assert.assertTrue(semaphore.tryAcquire(50, 10, TimeUnit.SECONDS)); - Assert.assertTrue(threadIds.size() > 1); - } finally { - executor.shutdownNow(); - } - } - - @Test - public void testAppEnginePeriodicRunnable() throws InterruptedException { - final Set threadIds = new HashSet<>(); - final Semaphore semaphore = new Semaphore(0); - - RevivingScheduledExecutor executor = - new RevivingScheduledExecutor(THREAD_FACTORY, "testAppEnginePeriodicRunnable", 0, 100); - - ScheduledFuture future = - executor.scheduleAtFixedRate( - new Runnable() { - @Override - public void run() { - threadIds.add(Thread.currentThread().getId()); - semaphore.release(); - } - }, - 0, - 10, - TimeUnit.MILLISECONDS); - - try { - Assert.assertTrue(semaphore.tryAcquire(50, 10, TimeUnit.SECONDS)); - Assert.assertTrue(threadIds.size() > 1); - } finally { - future.cancel(true); - executor.shutdownNow(); - } - } - - @Test - public void testAppEngineDelayedRunnable() throws InterruptedException { - final Semaphore semaphore = new Semaphore(0); - final AtomicInteger threads = new AtomicInteger(0); - - RevivingScheduledExecutor executor = - new RevivingScheduledExecutor(new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - threads.incrementAndGet(); - return THREAD_FACTORY.newThread(r); - } - }, - "testAppEngineDelayedRunnable", - 0, - 100); - - @SuppressWarnings("unused") - Future possiblyIgnoredError = - executor.schedule( - new Runnable() { - @Override - public void run() { - semaphore.release(); - } - }, - 750, - TimeUnit.MILLISECONDS); - - try { - Assert.assertFalse(semaphore.tryAcquire(1, 500, TimeUnit.MILLISECONDS)); - Assert.assertTrue(semaphore.tryAcquire(1, 500, TimeUnit.MILLISECONDS)); - Assert.assertTrue(threads.get() >= 2); - } finally { - executor.shutdownNow(); - } - } - - @Test - public void testAppEngineDelayedCallable() - throws InterruptedException, TimeoutException, ExecutionException { - final AtomicInteger threads = new AtomicInteger(0); - - RevivingScheduledExecutor executor = - new RevivingScheduledExecutor(new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - threads.incrementAndGet(); - return THREAD_FACTORY.newThread(r); - } - }, - "testAppEngineDelayedCallable", - 0, - 100); - - ScheduledFuture future = - executor.schedule( - new Callable() { - @Override - public Boolean call() throws Exception { - return true; - } - }, - 750, - TimeUnit.MILLISECONDS); - - try { - Assert.assertTrue(future.get(1, TimeUnit.SECONDS)); - Assert.assertTrue(threads.get() >= 2); - } finally { - executor.shutdownNow(); - } - } - - @Test - public void testAppEngineCleanup() throws InterruptedException { - final Semaphore beforeSemaphore = new Semaphore(0); - final Semaphore afterSemaphore = new Semaphore(0); - final AtomicInteger threads = new AtomicInteger(0); - - RevivingScheduledExecutor executor = - new RevivingScheduledExecutor(new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - threads.incrementAndGet(); - return THREAD_FACTORY.newThread(r); - } - }, - "testAppEngineCleanup", - 0, - 100) { - @Override - protected void beforeRestart() { - beforeSemaphore.release(); - } - - @Override - protected void afterRestart() { - afterSemaphore.release(); - } - }; - - @SuppressWarnings("unused") - Future possiblyIgnoredError = - executor.submit( - new Runnable() { - @Override - public void run() {} - }); - - try { - Assert.assertTrue(beforeSemaphore.tryAcquire(2, 10, TimeUnit.SECONDS)); - Assert.assertTrue(afterSemaphore.tryAcquire(2, 10, TimeUnit.SECONDS)); - Assert.assertEquals(3, threads.get()); - Assert.assertEquals(0, beforeSemaphore.availablePermits()); - Assert.assertEquals(0, afterSemaphore.availablePermits()); - } finally { - executor.shutdownNow(); - } - } - - private static class ExceptionCatchingThreadFactory implements ThreadFactory { - @Override - public Thread newThread(Runnable r) { - if (r == null) { - return null; - } - Thread thread = Executors.defaultThreadFactory().newThread(r); - thread.setUncaughtExceptionHandler( - new UncaughtExceptionHandler() { - @Override - public void uncaughtException(Thread t, Throwable e) { - // ignore -- to prevent the test output from getting cluttered - } - }); - return thread; - } - } -} From 9cb508169d6d2c02b44dce02a15e142e5a6092c4 Mon Sep 17 00:00:00 2001 From: hiranya911 Date: Mon, 26 Feb 2018 14:09:00 -0800 Subject: [PATCH 3/9] Removed the deprecated FirebaseCredential API --- .../com/google/firebase/FirebaseOptions.java | 44 ++- .../firebase/auth/FirebaseCredential.java | 39 --- .../firebase/auth/FirebaseCredentials.java | 223 ------------- .../firebase/auth/GoogleOAuthAccessToken.java | 62 ---- .../auth/internal/BaseCredential.java | 77 ----- .../internal/FirebaseCredentialsAdapter.java | 51 --- .../google/firebase/FirebaseOptionsTest.java | 35 +-- .../firebase/auth/FirebaseAuthTest.java | 42 +-- .../auth/FirebaseCredentialsTest.java | 292 ------------------ 9 files changed, 30 insertions(+), 835 deletions(-) delete mode 100644 src/main/java/com/google/firebase/auth/FirebaseCredential.java delete mode 100644 src/main/java/com/google/firebase/auth/FirebaseCredentials.java delete mode 100644 src/main/java/com/google/firebase/auth/GoogleOAuthAccessToken.java delete mode 100644 src/main/java/com/google/firebase/auth/internal/BaseCredential.java delete mode 100644 src/main/java/com/google/firebase/auth/internal/FirebaseCredentialsAdapter.java delete mode 100644 src/test/java/com/google/firebase/auth/FirebaseCredentialsTest.java diff --git a/src/main/java/com/google/firebase/FirebaseOptions.java b/src/main/java/com/google/firebase/FirebaseOptions.java index fc4df2658..948d6a0a6 100644 --- a/src/main/java/com/google/firebase/FirebaseOptions.java +++ b/src/main/java/com/google/firebase/FirebaseOptions.java @@ -25,21 +25,35 @@ import com.google.api.client.util.Key; import com.google.auth.oauth2.GoogleCredentials; import com.google.common.base.Strings; -import com.google.firebase.auth.FirebaseCredential; -import com.google.firebase.auth.FirebaseCredentials; -import com.google.firebase.auth.internal.BaseCredential; -import com.google.firebase.auth.internal.FirebaseCredentialsAdapter; +import com.google.common.collect.ImmutableList; import com.google.firebase.internal.FirebaseThreadManagers; import com.google.firebase.internal.NonNull; import com.google.firebase.internal.Nullable; import java.util.HashMap; +import java.util.List; import java.util.Map; /** Configurable Firebase options. */ public final class FirebaseOptions { - // TODO: deprecate and remove it once we can fetch these from Remote Config. + private static final List FIREBASE_SCOPES = + ImmutableList.of( + // Enables access to Firebase Realtime Database. + "https://www.googleapis.com/auth/firebase.database", + + // Enables access to the email address associated with a project. + "https://www.googleapis.com/auth/userinfo.email", + + // Enables access to Google Identity Toolkit (for user management APIs). + "https://www.googleapis.com/auth/identitytoolkit", + + // Enables access to Google Cloud Storage. + "https://www.googleapis.com/auth/devstorage.full_control", + + // Enables access to Google Cloud Firestore + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/datastore"); private final String databaseUrl; private final String storageBucket; @@ -53,7 +67,7 @@ public final class FirebaseOptions { private FirebaseOptions(@NonNull FirebaseOptions.Builder builder) { this.credentials = checkNotNull(builder.credentials, "FirebaseOptions must be initialized with setCredentials().") - .createScoped(BaseCredential.FIREBASE_SCOPES); + .createScoped(FIREBASE_SCOPES); this.databaseUrl = builder.databaseUrl; this.databaseAuthVariableOverride = builder.databaseAuthVariableOverride; this.projectId = builder.projectId; @@ -230,24 +244,6 @@ public Builder setCredentials(GoogleCredentials credentials) { return this; } - /** - * Sets the FirebaseCredential to use to authenticate the SDK. - * - * @param credential A FirebaseCredential used to authenticate the SDK. See {@link - * FirebaseCredentials} for default implementations. - * @return This Builder instance is returned so subsequent calls can be chained. - * @deprecated Use {@link FirebaseOptions.Builder#setCredentials(GoogleCredentials)}. - */ - public Builder setCredential(@NonNull FirebaseCredential credential) { - checkNotNull(credential); - if (credential instanceof BaseCredential) { - this.credentials = ((BaseCredential) credential).getGoogleCredentials(); - } else { - this.credentials = new FirebaseCredentialsAdapter(credential); - } - return this; - } - /** * Sets the auth variable to be used by the Realtime Database rules. * diff --git a/src/main/java/com/google/firebase/auth/FirebaseCredential.java b/src/main/java/com/google/firebase/auth/FirebaseCredential.java deleted file mode 100644 index b238d299c..000000000 --- a/src/main/java/com/google/firebase/auth/FirebaseCredential.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * 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 com.google.firebase.auth; - -import com.google.firebase.tasks.Task; - -/** - * Provides Google OAuth2 access tokens used to authenticate with Firebase services. In most cases, - * you will not need to implement this yourself and can instead use the default implementations - * provided by {@link FirebaseCredentials}. - * - * @deprecated Use {@code GoogleCredentials}. - */ -public interface FirebaseCredential { - - /** - * Returns a Google OAuth2 access token which can be used to authenticate with Firebase services. - * This method does not cache tokens, and therefore each invocation will fetch a fresh token. - * The caller is expected to implement caching by referencing the token expiry details - * available in the returned GoogleOAuthAccessToken instance. - * - * @return A {@link Task} providing a Google OAuth access token. - */ - Task getAccessToken(); -} diff --git a/src/main/java/com/google/firebase/auth/FirebaseCredentials.java b/src/main/java/com/google/firebase/auth/FirebaseCredentials.java deleted file mode 100644 index 4ae1d62da..000000000 --- a/src/main/java/com/google/firebase/auth/FirebaseCredentials.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * 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 com.google.firebase.auth; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.api.client.googleapis.util.Utils; -import com.google.api.client.http.HttpTransport; -import com.google.api.client.json.JsonFactory; -import com.google.auth.http.HttpTransportFactory; -import com.google.auth.oauth2.GoogleCredentials; -import com.google.auth.oauth2.ServiceAccountCredentials; -import com.google.auth.oauth2.UserCredentials; -import com.google.common.base.Strings; -import com.google.firebase.auth.internal.BaseCredential; -import com.google.firebase.internal.NonNull; - -import java.io.IOException; -import java.io.InputStream; - -/** - * Standard {@link FirebaseCredential} implementations for use with {@link - * com.google.firebase.FirebaseOptions}. - * - * @deprecated Use {@code GoogleCredentials}. - */ -public class FirebaseCredentials { - - private FirebaseCredentials() { - } - - /** - * Returns a {@link FirebaseCredential} based on Google Application Default Credentials which can - * be used to authenticate the SDK. - * - *

See Google - * Application Default Credentials for details on Google Application Deafult Credentials. - * - *

See Initialize the SDK for code samples - * and detailed documentation. - * - * @return A {@link FirebaseCredential} based on Google Application Default Credentials which can - * be used to authenticate the SDK. - */ - @NonNull - public static FirebaseCredential applicationDefault() { - return DefaultCredentialsHolder.INSTANCE; - } - - /** - * Returns a {@link FirebaseCredential} based on Google Application Default Credentials which can - * be used to authenticate the SDK. Allows specifying the HttpTransport and the - * JsonFactory to be used when communicating with the remote authentication server. - * - *

See Google - * Application Default Credentials for details on Google Application Deafult Credentials. - * - *

See Initialize the SDK for code samples - * and detailed documentation. - * - * @param transport HttpTransport used to communicate with the remote - * authentication server. - * @param jsonFactory JsonFactory used to parse JSON responses from the remote - * authentication server. - * @return A {@link FirebaseCredential} based on Google Application Default Credentials which can - * be used to authenticate the SDK. - */ - @NonNull - public static FirebaseCredential applicationDefault( - HttpTransport transport, JsonFactory jsonFactory) { - try { - return new ApplicationDefaultCredential(transport); - } catch (IOException e) { - // To prevent a breaking API change, we throw an unchecked exception. - throw new RuntimeException(e); - } - } - - /** - * Returns a {@link FirebaseCredential} generated from the provided service account certificate - * which can be used to authenticate the SDK. - * - *

See Initialize the SDK for code samples - * and detailed documentation. - * - * @param serviceAccount An InputStream containing the JSON representation of a - * service account certificate. - * @return A {@link FirebaseCredential} generated from the provided service account certificate - * which can be used to authenticate the SDK. - * @throws IOException If an error occurs while parsing the service account certificate. - */ - @NonNull - public static FirebaseCredential fromCertificate(InputStream serviceAccount) throws IOException { - return fromCertificate(serviceAccount, Utils.getDefaultTransport(), - Utils.getDefaultJsonFactory()); - } - - /** - * Returns a {@link FirebaseCredential} generated from the provided service account certificate - * which can be used to authenticate the SDK. Allows specifying the HttpTransport - * and the JsonFactory to be used when communicating with the remote authentication - * server. - * - *

See Initialize the SDK for code samples - * and detailed documentation. - * - * @param serviceAccount An InputStream containing the JSON representation of a - * service account certificate. - * @param transport HttpTransport used to communicate with the remote - * authentication server. - * @param jsonFactory JsonFactory used to parse JSON responses from the remote - * authentication server. - * @return A {@link FirebaseCredential} generated from the provided service account certificate - * which can be used to authenticate the SDK. - * @throws IOException If an error occurs while parsing the service account certificate. - */ - @NonNull - public static FirebaseCredential fromCertificate(InputStream serviceAccount, - HttpTransport transport, JsonFactory jsonFactory) throws IOException { - ServiceAccountCredentials credentials = ServiceAccountCredentials.fromStream( - serviceAccount, wrap(transport)); - checkArgument(!Strings.isNullOrEmpty(credentials.getProjectId()), - "Failed to parse service account: 'project_id' must be set"); - return new CertCredential(credentials); - } - - /** - * Returns a {@link FirebaseCredential} generated from the provided refresh token which can be - * used to authenticate the SDK. - * - *

See Initialize the SDK for code samples - * and detailed documentation. - * - * @param refreshToken An InputStream containing the JSON representation of a refresh - * token. - * @return A {@link FirebaseCredential} generated from the provided service account credential - * which can be used to authenticate the SDK. - * @throws IOException If an error occurs while parsing the refresh token. - */ - @NonNull - public static FirebaseCredential fromRefreshToken(InputStream refreshToken) throws IOException { - return fromRefreshToken( - refreshToken, Utils.getDefaultTransport(), Utils.getDefaultJsonFactory()); - } - - /** - * Returns a {@link FirebaseCredential} generated from the provided refresh token which can be - * used to authenticate the SDK. Allows specifying the HttpTransport and the - * JsonFactory to be used when communicating with the remote authentication server. - * - *

See Initialize the SDK for code samples - * and detailed documentation. - * - * @param refreshToken An InputStream containing the JSON representation of a refresh - * token. - * @param transport HttpTransport used to communicate with the remote - * authentication server. - * @param jsonFactory JsonFactory used to parse JSON responses from the remote - * authentication server. - * @return A {@link FirebaseCredential} generated from the provided service account credential - * which can be used to authenticate the SDK. - * @throws IOException If an error occurs while parsing the refresh token. - */ - @NonNull - public static FirebaseCredential fromRefreshToken(final InputStream refreshToken, - HttpTransport transport, JsonFactory jsonFactory) throws IOException { - return new RefreshTokenCredential(refreshToken, transport); - } - - static class CertCredential extends BaseCredential { - - CertCredential(ServiceAccountCredentials credentials) throws IOException { - super(credentials); - } - } - - static class ApplicationDefaultCredential extends BaseCredential { - - ApplicationDefaultCredential(HttpTransport transport) throws IOException { - super(GoogleCredentials.getApplicationDefault(wrap(transport))); - } - } - - static class RefreshTokenCredential extends BaseCredential { - - RefreshTokenCredential(InputStream inputStream, HttpTransport transport) throws IOException { - super(UserCredentials.fromStream(inputStream, wrap(transport))); - } - } - - private static class DefaultCredentialsHolder { - - static final FirebaseCredential INSTANCE = - applicationDefault(Utils.getDefaultTransport(), Utils.getDefaultJsonFactory()); - } - - private static HttpTransportFactory wrap(final HttpTransport transport) { - checkNotNull(transport, "HttpTransport must not be null"); - return new HttpTransportFactory() { - @Override - public HttpTransport create() { - return transport; - } - }; - } -} diff --git a/src/main/java/com/google/firebase/auth/GoogleOAuthAccessToken.java b/src/main/java/com/google/firebase/auth/GoogleOAuthAccessToken.java deleted file mode 100644 index 85e6021e3..000000000 --- a/src/main/java/com/google/firebase/auth/GoogleOAuthAccessToken.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * 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 com.google.firebase.auth; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.base.Strings; - -/** - * Represents an OAuth access token, which can be used to access Firebase and other qualified - * Google APIs. Encapsulates both the token string, and its expiration time. - * - * @deprecated Use GoogleCredentials and associated classes. - */ -public class GoogleOAuthAccessToken { - - - private final String accessToken; - private final long expiryTime; - - /** - * Create a new GoogleOAuthAccessToken instance - * - * @param accessToken JWT access token string - * @param expiryTime Time at which the token will expire (milliseconds since epoch) - * @throws IllegalArgumentException If the token is null or empty - */ - public GoogleOAuthAccessToken(String accessToken, long expiryTime) { - checkArgument(!Strings.isNullOrEmpty(accessToken), "Access token must not be null"); - this.accessToken = accessToken; - this.expiryTime = expiryTime; - } - - /** - * Returns the JWT access token. - */ - public String getAccessToken() { - return accessToken; - } - - /** - * Returns the expiration time as a milliseconds since epoch timestamp. - */ - public long getExpiryTime() { - return expiryTime; - } - -} diff --git a/src/main/java/com/google/firebase/auth/internal/BaseCredential.java b/src/main/java/com/google/firebase/auth/internal/BaseCredential.java deleted file mode 100644 index d15832a55..000000000 --- a/src/main/java/com/google/firebase/auth/internal/BaseCredential.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * 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 com.google.firebase.auth.internal; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.auth.oauth2.AccessToken; -import com.google.auth.oauth2.GoogleCredentials; -import com.google.common.collect.ImmutableList; -import com.google.firebase.auth.FirebaseCredential; -import com.google.firebase.auth.GoogleOAuthAccessToken; -import com.google.firebase.tasks.Task; -import com.google.firebase.tasks.Tasks; - -import java.io.IOException; -import java.util.List; - -/** - * Internal base class for built-in FirebaseCredential implementations. - */ -public abstract class BaseCredential implements FirebaseCredential { - - public static final List FIREBASE_SCOPES = - ImmutableList.of( - // Enables access to Firebase Realtime Database. - "https://www.googleapis.com/auth/firebase.database", - - // Enables access to the email address associated with a project. - "https://www.googleapis.com/auth/userinfo.email", - - // Enables access to Google Identity Toolkit (for user management APIs). - "https://www.googleapis.com/auth/identitytoolkit", - - // Enables access to Google Cloud Storage. - "https://www.googleapis.com/auth/devstorage.full_control", - - // Enables access to Google Cloud Firestore - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/datastore"); - - private final GoogleCredentials googleCredentials; - - public BaseCredential(GoogleCredentials googleCredentials) { - this.googleCredentials = checkNotNull(googleCredentials).createScoped(FIREBASE_SCOPES); - } - - public final GoogleCredentials getGoogleCredentials() { - return googleCredentials; - } - - @Override - public Task getAccessToken() { - try { - AccessToken accessToken = googleCredentials.refreshAccessToken(); - GoogleOAuthAccessToken googleToken = new GoogleOAuthAccessToken(accessToken.getTokenValue(), - accessToken.getExpirationTime().getTime()); - return Tasks.forResult(googleToken); - } catch (Exception e) { - return Tasks.forException(e); - } - } - -} diff --git a/src/main/java/com/google/firebase/auth/internal/FirebaseCredentialsAdapter.java b/src/main/java/com/google/firebase/auth/internal/FirebaseCredentialsAdapter.java deleted file mode 100644 index e1b069eda..000000000 --- a/src/main/java/com/google/firebase/auth/internal/FirebaseCredentialsAdapter.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * 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 com.google.firebase.auth.internal; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.auth.oauth2.AccessToken; -import com.google.auth.oauth2.GoogleCredentials; -import com.google.firebase.auth.FirebaseCredential; -import com.google.firebase.auth.GoogleOAuthAccessToken; -import com.google.firebase.tasks.Tasks; -import java.io.IOException; -import java.util.Date; -import java.util.concurrent.ExecutionException; - -/** - * An adapter for converting custom {@link FirebaseCredential} implementations into - * GoogleCredentials. - */ -public final class FirebaseCredentialsAdapter extends GoogleCredentials { - - private final FirebaseCredential credential; - - public FirebaseCredentialsAdapter(FirebaseCredential credential) { - this.credential = checkNotNull(credential); - } - - @Override - public AccessToken refreshAccessToken() throws IOException { - try { - GoogleOAuthAccessToken token = Tasks.await(credential.getAccessToken()); - return new AccessToken(token.getAccessToken(), new Date(token.getExpiryTime())); - } catch (ExecutionException | InterruptedException e) { - throw new IOException("Error while obtaining OAuth2 token", e); - } - } -} diff --git a/src/test/java/com/google/firebase/FirebaseOptionsTest.java b/src/test/java/com/google/firebase/FirebaseOptionsTest.java index b02fbbdc9..8c81df7b5 100644 --- a/src/test/java/com/google/firebase/FirebaseOptionsTest.java +++ b/src/test/java/com/google/firebase/FirebaseOptionsTest.java @@ -27,12 +27,9 @@ import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.gson.GsonFactory; +import com.google.auth.oauth2.AccessToken; import com.google.auth.oauth2.GoogleCredentials; import com.google.auth.oauth2.ServiceAccountCredentials; -import com.google.firebase.auth.FirebaseCredential; -import com.google.firebase.auth.FirebaseCredentials; -import com.google.firebase.auth.GoogleOAuthAccessToken; -import com.google.firebase.tasks.Task; import com.google.firebase.testing.ServiceAccount; import com.google.firebase.testing.TestUtils; import java.io.IOException; @@ -75,7 +72,7 @@ protected ThreadFactory getThreadFactory() { }; @Test - public void createOptionsWithAllValuesSet() throws IOException, InterruptedException { + public void createOptionsWithAllValuesSet() throws IOException { GsonFactory jsonFactory = new GsonFactory(); NetHttpTransport httpTransport = new NetHttpTransport(); FirebaseOptions firebaseOptions = @@ -104,7 +101,7 @@ public void createOptionsWithAllValuesSet() throws IOException, InterruptedExcep } @Test - public void createOptionsWithOnlyMandatoryValuesSet() throws IOException, InterruptedException { + public void createOptionsWithOnlyMandatoryValuesSet() throws IOException { FirebaseOptions firebaseOptions = new FirebaseOptions.Builder() .setCredentials(GoogleCredentials.fromStream(ServiceAccount.EDITOR.asStream())) @@ -124,32 +121,12 @@ public void createOptionsWithOnlyMandatoryValuesSet() throws IOException, Interr } @Test - public void createOptionsWithFirebaseCredential() throws IOException { + public void createOptionsWithCustomFirebaseCredential() { FirebaseOptions firebaseOptions = new FirebaseOptions.Builder() - .setCredential(FirebaseCredentials.fromCertificate(ServiceAccount.EDITOR.asStream())) - .build(); - - assertNotNull(firebaseOptions.getJsonFactory()); - assertNotNull(firebaseOptions.getHttpTransport()); - assertNull(firebaseOptions.getDatabaseUrl()); - assertNull(firebaseOptions.getStorageBucket()); - - GoogleCredentials credentials = firebaseOptions.getCredentials(); - assertNotNull(credentials); - assertTrue(credentials instanceof ServiceAccountCredentials); - assertEquals( - GoogleCredential.fromStream(ServiceAccount.EDITOR.asStream()).getServiceAccountId(), - ((ServiceAccountCredentials) credentials).getClientEmail()); - } - - @Test - public void createOptionsWithCustomFirebaseCredential() throws IOException { - FirebaseOptions firebaseOptions = - new FirebaseOptions.Builder() - .setCredential(new FirebaseCredential() { + .setCredentials(new GoogleCredentials() { @Override - public Task getAccessToken() { + public AccessToken refreshAccessToken() { return null; } }) diff --git a/src/test/java/com/google/firebase/auth/FirebaseAuthTest.java b/src/test/java/com/google/firebase/auth/FirebaseAuthTest.java index eb19bafd5..bb430d285 100644 --- a/src/test/java/com/google/firebase/auth/FirebaseAuthTest.java +++ b/src/test/java/com/google/firebase/auth/FirebaseAuthTest.java @@ -106,16 +106,6 @@ public static Collection data() throws Exception { .build(), /* isCertCredential */ false }, - { - new FirebaseOptions.Builder().setCredential( - createFirebaseCertificateCredential()).build(), - /* isCertCredential */ true - }, - { - new FirebaseOptions.Builder().setCredential( - createFirebaseRefreshTokenCredential()).build(), - /* isCertCredential */ false - }, }); } @@ -141,23 +131,6 @@ public HttpTransport create() { }); } - private static FirebaseCredential createFirebaseRefreshTokenCredential() - throws IOException { - - final MockTokenServerTransport transport = new MockTokenServerTransport(); - transport.addClient(CLIENT_ID, CLIENT_SECRET); - transport.addRefreshToken(REFRESH_TOKEN, ACCESS_TOKEN); - - Map secretJson = new HashMap<>(); - secretJson.put("client_id", CLIENT_ID); - secretJson.put("client_secret", CLIENT_SECRET); - secretJson.put("refresh_token", REFRESH_TOKEN); - secretJson.put("type", "authorized_user"); - InputStream refreshTokenStream = - new ByteArrayInputStream(JSON_FACTORY.toByteArray(secretJson)); - return FirebaseCredentials.fromRefreshToken(refreshTokenStream, transport, JSON_FACTORY); - } - private static GoogleCredentials createCertificateCredential() throws IOException { final MockTokenServerTransport transport = new MockTokenServerTransport(); transport.addServiceAccount(ServiceAccount.EDITOR.getEmail(), ACCESS_TOKEN); @@ -170,13 +143,6 @@ public HttpTransport create() { }); } - private static FirebaseCredential createFirebaseCertificateCredential() throws IOException { - final MockTokenServerTransport transport = new MockTokenServerTransport(); - transport.addServiceAccount(ServiceAccount.EDITOR.getEmail(), ACCESS_TOKEN); - return FirebaseCredentials.fromCertificate(ServiceAccount.EDITOR.asStream(), - transport, JSON_FACTORY); - } - @Before public void setup() { TestOnlyImplFirebaseTrampolines.clearInstancesForTest(); @@ -189,7 +155,7 @@ public void cleanup() { } @Test - public void testGetInstance() throws ExecutionException, InterruptedException { + public void testGetInstance() { FirebaseAuth defaultAuth = FirebaseAuth.getInstance(); assertNotNull(defaultAuth); assertSame(defaultAuth, FirebaseAuth.getInstance()); @@ -198,7 +164,7 @@ public void testGetInstance() throws ExecutionException, InterruptedException { } @Test - public void testGetInstanceForApp() throws ExecutionException, InterruptedException { + public void testGetInstanceForApp() { FirebaseApp app = FirebaseApp.initializeApp(firebaseOptions, "testGetInstanceForApp"); FirebaseAuth auth = FirebaseAuth.getInstance(app); assertNotNull(auth); @@ -208,7 +174,7 @@ public void testGetInstanceForApp() throws ExecutionException, InterruptedExcept } @Test - public void testAppDelete() throws ExecutionException, InterruptedException { + public void testAppDelete() { FirebaseApp app = FirebaseApp.initializeApp(firebaseOptions, "testAppDelete"); FirebaseAuth auth = FirebaseAuth.getInstance(app); assertNotNull(auth); @@ -272,7 +238,7 @@ public void testInitAfterAppDelete() throws ExecutionException, InterruptedExcep } @Test - public void testAppWithAuthVariableOverrides() throws ExecutionException, InterruptedException { + public void testAppWithAuthVariableOverrides() { Map authVariableOverrides = Collections.singletonMap("uid", (Object) "uid1"); FirebaseOptions options = new FirebaseOptions.Builder(firebaseOptions) diff --git a/src/test/java/com/google/firebase/auth/FirebaseCredentialsTest.java b/src/test/java/com/google/firebase/auth/FirebaseCredentialsTest.java deleted file mode 100644 index dd52e941a..000000000 --- a/src/test/java/com/google/firebase/auth/FirebaseCredentialsTest.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * 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 com.google.firebase.auth; - -import com.google.api.client.googleapis.testing.auth.oauth2.MockTokenServerTransport; -import com.google.api.client.googleapis.util.Utils; -import com.google.api.client.json.JsonFactory; -import com.google.auth.oauth2.AccessToken; -import com.google.auth.oauth2.GoogleCredentials; -import com.google.firebase.auth.internal.BaseCredential; -import com.google.firebase.auth.internal.FirebaseCredentialsAdapter; -import com.google.firebase.tasks.Task; -import com.google.firebase.tasks.Tasks; -import com.google.firebase.testing.ServiceAccount; -import com.google.firebase.testing.TestUtils; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicInteger; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -/** - * Tests for {@link FirebaseCredentials}. - */ -public class FirebaseCredentialsTest { - - private static final String ACCESS_TOKEN = "mockaccesstoken"; - private static final String CLIENT_SECRET = "mockclientsecret"; - private static final String CLIENT_ID = "mockclientid"; - private static final String REFRESH_TOKEN = "mockrefreshtoken"; - private static final JsonFactory JSON_FACTORY = Utils.getDefaultJsonFactory(); - - @Rule public final ExpectedException thrown = ExpectedException.none(); - - @BeforeClass - public static void setupClass() throws IOException { - TestUtils.getApplicationDefaultCredentials(); - } - - @Test(expected = NullPointerException.class) - public void testNullCertificate() throws IOException { - FirebaseCredentials.fromCertificate(null); - } - - @Test(expected = NullPointerException.class) - public void testNullRefreshToken() throws IOException { - FirebaseCredentials.fromRefreshToken(null); - } - - @Test - public void defaultCredentialDoesntRefetch() throws Exception { - FirebaseCredential credential = FirebaseCredentials.applicationDefault( - Utils.getDefaultTransport(), Utils.getDefaultJsonFactory()); - GoogleOAuthAccessToken token = Tasks.await(credential.getAccessToken()); - Assert.assertEquals(TestUtils.TEST_ADC_ACCESS_TOKEN, token.getAccessToken()); - Assert.assertNotNull(((BaseCredential) credential).getGoogleCredentials()); - - // We should still be able to fetch the token since the certificate is cached - credential = FirebaseCredentials.applicationDefault(); - token = Tasks.await(credential.getAccessToken()); - Assert.assertNotNull(token); - Assert.assertEquals(TestUtils.TEST_ADC_ACCESS_TOKEN, token.getAccessToken()); - Assert.assertNotNull(((BaseCredential) credential).getGoogleCredentials()); - } - - @Test - public void canResolveTokenMoreThanOnce() - throws ExecutionException, InterruptedException, IOException { - MockTokenServerTransport transport = new MockTokenServerTransport(); - transport.addServiceAccount(ServiceAccount.EDITOR.getEmail(), ACCESS_TOKEN); - - FirebaseCredential credential = - FirebaseCredentials.fromCertificate( - ServiceAccount.EDITOR.asStream(), transport, Utils.getDefaultJsonFactory()); - - Tasks.await(credential.getAccessToken()); - Tasks.await(credential.getAccessToken()); - } - - @Test - public void certificateReadIsDoneSynchronously() - throws ExecutionException, InterruptedException, IOException { - MockTokenServerTransport transport = new MockTokenServerTransport(); - transport.addServiceAccount(ServiceAccount.EDITOR.getEmail(), ACCESS_TOKEN); - - ByteArrayInputStream inputStream = - new ByteArrayInputStream( - ServiceAccount.EDITOR.asString().getBytes(Charset.defaultCharset())); - - FirebaseCredential credential = - FirebaseCredentials.fromCertificate(inputStream, transport, Utils.getDefaultJsonFactory()); - - Assert.assertEquals(0, inputStream.available()); - inputStream.close(); - - Assert.assertNotNull(((BaseCredential) credential).getGoogleCredentials()); - Assert.assertEquals(ACCESS_TOKEN, Tasks.await(credential.getAccessToken()).getAccessToken()); - } - - @Test - public void certificateReadChecksForProjectId() - throws ExecutionException, InterruptedException, IOException { - MockTokenServerTransport transport = new MockTokenServerTransport(); - transport.addServiceAccount(ServiceAccount.EDITOR.getEmail(), ACCESS_TOKEN); - - String accountWithoutProjectId = - ServiceAccount.EDITOR.asString().replace("project_id", "missing"); - ByteArrayInputStream inputStream = - new ByteArrayInputStream(accountWithoutProjectId.getBytes(Charset.defaultCharset())); - - try { - FirebaseCredentials.fromCertificate(inputStream, transport, Utils.getDefaultJsonFactory()); - Assert.fail(); - } catch (IllegalArgumentException e) { - Assert.assertEquals( - "Failed to parse service account: 'project_id' must be set", e.getMessage()); - } - } - - @Test - public void certificateReadThrowsIOException() - throws ExecutionException, InterruptedException { - MockTokenServerTransport transport = new MockTokenServerTransport(); - transport.addServiceAccount(ServiceAccount.EDITOR.getEmail(), ACCESS_TOKEN); - - InputStream inputStream = - new InputStream() { - @Override - public int read() throws IOException { - throw new IOException("Expected"); - } - }; - - - try { - FirebaseCredentials.fromCertificate(inputStream, transport, Utils.getDefaultJsonFactory()); - Assert.fail(); - } catch (IOException e) { - Assert.assertEquals("Expected", e.getMessage()); - } - } - - @Test - public void nullThrowsRuntimeExceptionFromCertificate() - throws ExecutionException, InterruptedException, IOException { - final MockTokenServerTransport transport = new MockTokenServerTransport(); - transport.addServiceAccount(ServiceAccount.EDITOR.getEmail(), ACCESS_TOKEN); - thrown.expect(NullPointerException.class); - FirebaseCredentials.fromCertificate(null, transport, Utils.getDefaultJsonFactory()); - } - - @Test - public void nullThrowsRuntimeExceptionFromRefreshToken() - throws ExecutionException, InterruptedException, IOException { - final MockTokenServerTransport transport = new MockTokenServerTransport(); - transport.addServiceAccount(ServiceAccount.EDITOR.getEmail(), ACCESS_TOKEN); - thrown.expect(NullPointerException.class); - FirebaseCredentials.fromRefreshToken(null, transport, Utils.getDefaultJsonFactory()); - } - - @Test - public void refreshTokenReadIsDoneSynchronously() - throws ExecutionException, InterruptedException, IOException { - MockTokenServerTransport transport = new MockTokenServerTransport(); - transport.addClient(CLIENT_ID, CLIENT_SECRET); - transport.addRefreshToken(REFRESH_TOKEN, ACCESS_TOKEN); - - Map secretJson = new HashMap<>(); - secretJson.put("client_id", CLIENT_ID); - secretJson.put("client_secret", CLIENT_SECRET); - secretJson.put("refresh_token", REFRESH_TOKEN); - secretJson.put("type", "authorized_user"); - InputStream inputStream = new ByteArrayInputStream(JSON_FACTORY.toByteArray(secretJson)); - - FirebaseCredential credential = - FirebaseCredentials.fromRefreshToken(inputStream, transport, Utils.getDefaultJsonFactory()); - - Assert.assertEquals(0, inputStream.available()); - inputStream.close(); - - Assert.assertNotNull(((BaseCredential) credential).getGoogleCredentials()); - Assert.assertEquals(ACCESS_TOKEN, Tasks.await(credential.getAccessToken()).getAccessToken()); - } - - @Test - public void refreshTokenReadThrowsIOException() - throws ExecutionException, InterruptedException { - MockTokenServerTransport transport = new MockTokenServerTransport(); - transport.addServiceAccount(ServiceAccount.EDITOR.getEmail(), ACCESS_TOKEN); - - InputStream inputStream = - new InputStream() { - @Override - public int read() throws IOException { - throw new IOException("Expected"); - } - }; - - try { - FirebaseCredentials.fromRefreshToken(inputStream, transport, Utils.getDefaultJsonFactory()); - Assert.fail(); - } catch (IOException e) { - Assert.assertEquals("Expected", e.getMessage()); - } - } - - @Test(expected = Exception.class) - public void serviceAccountUsedAsRefreshToken() throws Exception { - FirebaseCredentials.fromRefreshToken(ServiceAccount.EDITOR.asStream()); - } - - @Test - public void tokenNotCached() throws Exception { - TestCredential credential = new TestCredential(new MockGoogleCredentials(ACCESS_TOKEN, 10L)); - - for (long i = 0; i < 10; i++) { - Assert.assertEquals(ACCESS_TOKEN, Tasks.await(credential.getAccessToken()).getAccessToken()); - Assert.assertEquals(i + 1, credential.getFetchCount()); - } - } - - @Test - public void testTokenExpiration() throws Exception { - final MockGoogleCredentials googleCredentials = new MockGoogleCredentials(ACCESS_TOKEN); - TestCredential credential = new TestCredential(googleCredentials); - - for (long i = 0; i < 10; i++) { - long expiryTime = (i + 1) * 10L; - googleCredentials.setExpiryTime(expiryTime); - GoogleOAuthAccessToken googleToken = Tasks.await(credential.getAccessToken()); - Assert.assertEquals(ACCESS_TOKEN, googleToken.getAccessToken()); - Assert.assertEquals(expiryTime, googleToken.getExpiryTime()); - } - } - - @Test - public void testCustomFirebaseCredential() throws IOException { - final Date date = new Date(); - FirebaseCredential credential = new FirebaseCredential() { - @Override - public Task getAccessToken() { - return Tasks.forResult(new GoogleOAuthAccessToken("token", date.getTime())); - } - }; - GoogleCredentials googleCredentials = new FirebaseCredentialsAdapter(credential); - AccessToken accessToken = googleCredentials.refreshAccessToken(); - Assert.assertEquals("token", accessToken.getTokenValue()); - Assert.assertEquals(date, accessToken.getExpirationTime()); - } - - private static class TestCredential extends BaseCredential { - - private final AtomicInteger fetchCount = new AtomicInteger(0); - - TestCredential(GoogleCredentials googleCredentials) { - super(googleCredentials); - } - - @Override - public Task getAccessToken() { - fetchCount.incrementAndGet(); - return super.getAccessToken(); - } - - int getFetchCount() { - return fetchCount.get(); - } - } -} From 25c2b166118a09b93b0588ad91fecc935208a13f Mon Sep 17 00:00:00 2001 From: hiranya911 Date: Fri, 2 Mar 2018 13:56:17 -0800 Subject: [PATCH 4/9] Removing GAE java7 related APIs (GaeThreadFactory, RevivingScheduledExecutor) --- .../java/com/google/firebase/FirebaseApp.java | 9 +- .../firebase/ImplFirebaseTrampolines.java | 9 - .../connection/NettyWebSocketClient.java | 8 +- .../firebase/database/core/GaePlatform.java | 20 +- .../database/core/ThreadPoolEventTarget.java | 13 +- .../database/utilities/DefaultRunLoop.java | 63 +++-- .../internal/FirebaseThreadManagers.java | 64 +---- .../firebase/internal/GaeExecutorService.java | 195 -------------- .../firebase/internal/GaeThreadFactory.java | 165 ------------ .../internal/RevivingScheduledExecutor.java | 213 ---------------- .../google/firebase/tasks/TaskExecutors.java | 15 +- .../database/core/GaePlatformTest.java | 6 - .../utilities/DefaultRunLoopTest.java | 6 +- .../internal/FirebaseThreadManagersTest.java | 3 - .../internal/GaeExecutorServiceTest.java | 129 ---------- .../RevivingScheduledExecutorTest.java | 238 ------------------ 16 files changed, 70 insertions(+), 1086 deletions(-) delete mode 100644 src/main/java/com/google/firebase/internal/GaeExecutorService.java delete mode 100644 src/main/java/com/google/firebase/internal/GaeThreadFactory.java delete mode 100644 src/main/java/com/google/firebase/internal/RevivingScheduledExecutor.java delete mode 100644 src/test/java/com/google/firebase/internal/GaeExecutorServiceTest.java delete mode 100644 src/test/java/com/google/firebase/internal/RevivingScheduledExecutorTest.java diff --git a/src/main/java/com/google/firebase/FirebaseApp.java b/src/main/java/com/google/firebase/FirebaseApp.java index e47dc647b..9325b8276 100644 --- a/src/main/java/com/google/firebase/FirebaseApp.java +++ b/src/main/java/com/google/firebase/FirebaseApp.java @@ -38,11 +38,10 @@ import com.google.common.io.BaseEncoding; import com.google.firebase.internal.FirebaseAppStore; import com.google.firebase.internal.FirebaseService; -import com.google.firebase.internal.GaeThreadFactory; +import com.google.firebase.internal.FirebaseThreadManagers; import com.google.firebase.internal.ListenableFuture2ApiFuture; import com.google.firebase.internal.NonNull; import com.google.firebase.internal.Nullable; -import com.google.firebase.internal.RevivingScheduledExecutor; import java.io.FileReader; import java.io.IOException; @@ -54,6 +53,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; +import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -385,8 +385,9 @@ private ScheduledExecutorService ensureScheduledExecutorService() { synchronized (lock) { checkNotDeleted(); if (scheduledExecutor == null) { - scheduledExecutor = new RevivingScheduledExecutor(threadManager.getThreadFactory(), - "firebase-scheduled-worker", GaeThreadFactory.isAvailable()); + ThreadFactory threadFactory = FirebaseThreadManagers.wrapThreadFactory( + getThreadFactory(), "firebase-scheduled-worker"); + scheduledExecutor = Executors.newSingleThreadScheduledExecutor(threadFactory); } } } diff --git a/src/main/java/com/google/firebase/ImplFirebaseTrampolines.java b/src/main/java/com/google/firebase/ImplFirebaseTrampolines.java index 75eeb9635..5c173ae28 100644 --- a/src/main/java/com/google/firebase/ImplFirebaseTrampolines.java +++ b/src/main/java/com/google/firebase/ImplFirebaseTrampolines.java @@ -21,7 +21,6 @@ import com.google.firebase.internal.FirebaseService; import com.google.firebase.internal.NonNull; -import com.google.firebase.tasks.Task; import java.util.concurrent.Callable; import java.util.concurrent.ThreadFactory; @@ -48,14 +47,6 @@ public static boolean isDefaultApp(@NonNull FirebaseApp app) { return app.isDefaultApp(); } - public static String getPersistenceKey(@NonNull FirebaseApp app) { - return app.getPersistenceKey(); - } - - public static String getPersistenceKey(String name, FirebaseOptions options) { - return FirebaseApp.getPersistenceKey(name, options); - } - public static T getService( @NonNull FirebaseApp app, @NonNull String id, @NonNull Class type) { return type.cast(app.getService(id)); diff --git a/src/main/java/com/google/firebase/database/connection/NettyWebSocketClient.java b/src/main/java/com/google/firebase/database/connection/NettyWebSocketClient.java index d71a1eebc..d3b720c6a 100644 --- a/src/main/java/com/google/firebase/database/connection/NettyWebSocketClient.java +++ b/src/main/java/com/google/firebase/database/connection/NettyWebSocketClient.java @@ -5,8 +5,7 @@ import static com.google.common.base.Preconditions.checkState; import com.google.common.base.Strings; -import com.google.firebase.internal.GaeThreadFactory; -import com.google.firebase.internal.RevivingScheduledExecutor; +import com.google.firebase.internal.FirebaseThreadManagers; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; @@ -36,6 +35,7 @@ import java.net.URI; import java.security.KeyStore; import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import javax.net.ssl.TrustManagerFactory; @@ -66,8 +66,8 @@ class NettyWebSocketClient implements WebsocketConnection.WSClient { this.uri = checkNotNull(uri, "uri must not be null"); this.eventHandler = checkNotNull(eventHandler, "event handler must not be null"); this.channelHandler = new WebSocketClientHandler(uri, userAgent, eventHandler); - this.executorService = new RevivingScheduledExecutor( - threadFactory, "firebase-websocket-worker", GaeThreadFactory.isAvailable()); + this.executorService = Executors.newSingleThreadExecutor( + FirebaseThreadManagers.wrapThreadFactory(threadFactory, "firebase-websocket-worker")); this.group = new NioEventLoopGroup(1, this.executorService); } diff --git a/src/main/java/com/google/firebase/database/core/GaePlatform.java b/src/main/java/com/google/firebase/database/core/GaePlatform.java index b7698f3de..45e07cea1 100644 --- a/src/main/java/com/google/firebase/database/core/GaePlatform.java +++ b/src/main/java/com/google/firebase/database/core/GaePlatform.java @@ -28,13 +28,12 @@ import com.google.firebase.database.logging.LogWrapper; import com.google.firebase.database.logging.Logger; import com.google.firebase.database.utilities.DefaultRunLoop; -import com.google.firebase.internal.GaeThreadFactory; -import com.google.firebase.internal.RevivingScheduledExecutor; import java.util.List; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; + /** * Represents a Google AppEngine platform. * @@ -42,15 +41,22 @@ */ class GaePlatform implements Platform { + private static final String GAE_THREAD_MANAGER_CLASS = "com.google.appengine.api.ThreadManager"; + private static final String PROCESS_PLATFORM = "AppEngine"; private final FirebaseApp firebaseApp; - public GaePlatform(FirebaseApp firebaseApp) { + GaePlatform(FirebaseApp firebaseApp) { this.firebaseApp = firebaseApp; } public static boolean isActive() { - return GaeThreadFactory.isAvailable(); + try { + Class.forName(GAE_THREAD_MANAGER_CLASS); + return System.getProperty("com.google.appengine.runtime.environment") != null; + } catch (ClassNotFoundException e) { + return false; + } } @Override @@ -64,15 +70,13 @@ private ThreadFactory getGaeThreadFactory() { @Override public EventTarget newEventTarget(Context ctx) { - RevivingScheduledExecutor eventExecutor = - new RevivingScheduledExecutor(getGaeThreadFactory(), "FirebaseDatabaseEventTarget", true); - return new ThreadPoolEventTarget(eventExecutor); + return new ThreadPoolEventTarget(getGaeThreadFactory(), ThreadInitializer.defaultInstance); } @Override public RunLoop newRunLoop(final Context context) { final LogWrapper logger = context.getLogger(RunLoop.class); - return new DefaultRunLoop(getGaeThreadFactory(), /* periodicRestart= */ true, context) { + return new DefaultRunLoop(getGaeThreadFactory()) { @Override public void handleException(Throwable e) { logger.error(DefaultRunLoop.messageForException(e), e); diff --git a/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java b/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java index abe599fdc..f56a185c6 100644 --- a/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java +++ b/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java @@ -16,8 +16,6 @@ package com.google.firebase.database.core; -import static com.google.common.base.Preconditions.checkNotNull; - import java.lang.Thread.UncaughtExceptionHandler; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; @@ -35,11 +33,10 @@ class ThreadPoolEventTarget implements EventTarget, UncaughtExceptionHandler { private final ThreadPoolExecutor executor; private UncaughtExceptionHandler exceptionHandler; - public ThreadPoolEventTarget( - final ThreadFactory wrappedFactory, final ThreadInitializer threadInitializer) { - int poolSize = 1; + ThreadPoolEventTarget( + final ThreadFactory wrappedFactory, final ThreadInitializer threadInitializer) { + final int poolSize = 1; BlockingQueue queue = new LinkedBlockingQueue<>(); - executor = new ThreadPoolExecutor(poolSize, poolSize, 3, TimeUnit.SECONDS, queue, new ThreadFactory() { @Override @@ -53,10 +50,6 @@ public Thread newThread(Runnable r) { }); } - public ThreadPoolEventTarget(final ThreadPoolExecutor executor) { - this.executor = checkNotNull(executor); - } - @Override public void postEvent(Runnable r) { executor.execute(r); diff --git a/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java b/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java index d43deb6b3..98f585ccd 100644 --- a/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java +++ b/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java @@ -18,13 +18,13 @@ import com.google.firebase.database.DatabaseException; import com.google.firebase.database.FirebaseDatabase; -import com.google.firebase.database.annotations.Nullable; -import com.google.firebase.database.core.Context; -import com.google.firebase.database.core.RepoManager; import com.google.firebase.database.core.RunLoop; -import com.google.firebase.internal.RevivingScheduledExecutor; +import com.google.firebase.internal.FirebaseThreadManagers; import java.lang.Thread.UncaughtExceptionHandler; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; @@ -33,46 +33,45 @@ public abstract class DefaultRunLoop implements RunLoop { - private ScheduledThreadPoolExecutor executor; + private final ScheduledThreadPoolExecutor executor; private UncaughtExceptionHandler exceptionHandler; - /** Creates a DefaultRunLoop that does not periodically restart its threads. */ - public DefaultRunLoop(ThreadFactory threadFactory) { - this(threadFactory, false, null); - } - /** * Creates a DefaultRunLoop that optionally restarts its threads periodically. If 'context' is * provided, these restarts will automatically interrupt and resume all Repo connections. */ - public DefaultRunLoop( - final ThreadFactory threadFactory, - final boolean periodicRestart, - @Nullable final Context context) { - executor = - new RevivingScheduledExecutor(threadFactory, "FirebaseDatabaseWorker", periodicRestart) { - @Override - protected void handleException(Throwable throwable) { - DefaultRunLoop.this.handleExceptionInternal(throwable); - } - - @Override - protected void beforeRestart() { - if (context != null) { - RepoManager.interrupt(context); + protected DefaultRunLoop(final ThreadFactory threadFactory) { + ThreadFactory wrappedThreadFactory = FirebaseThreadManagers.wrapThreadFactory( + threadFactory, "firebase-database-worker"); + executor = new ScheduledThreadPoolExecutor(1, wrappedThreadFactory) { + @Override + protected void afterExecute(Runnable runnable, Throwable throwable) { + super.afterExecute(runnable, throwable); + if (throwable == null && runnable instanceof Future) { + Future future = (Future) runnable; + try { + // Not all Futures will be done, e.g. when used with scheduledAtFixedRate + if (future.isDone()) { + future.get(); } + } catch (CancellationException ce) { + // Cancellation exceptions are okay, we expect them to happen sometimes + } catch (ExecutionException ee) { + throwable = ee.getCause(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); } + } - @Override - protected void afterRestart() { - if (context != null) { - RepoManager.resume(context); - } - } - }; + if (throwable != null) { + handleExceptionInternal(throwable); + } + } + }; // Core threads don't time out, this only takes effect when we drop the number of required // core threads + executor.setRemoveOnCancelPolicy(true); executor.setKeepAliveTime(3, TimeUnit.SECONDS); } diff --git a/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java b/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java index 0e503ba36..d5ef26b8c 100644 --- a/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java +++ b/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java @@ -16,8 +16,6 @@ package com.google.firebase.internal; -import static com.google.common.base.Preconditions.checkState; - import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.firebase.FirebaseApp; import com.google.firebase.ThreadManager; @@ -36,14 +34,14 @@ public class FirebaseThreadManagers { private static final Logger logger = LoggerFactory.getLogger(FirebaseThreadManagers.class); - public static final ThreadManager DEFAULT_THREAD_MANAGER; + public static final ThreadManager DEFAULT_THREAD_MANAGER = new DefaultThreadManager(); - static { - if (GaeThreadFactory.isAvailable()) { - DEFAULT_THREAD_MANAGER = new GaeThreadManager(); - } else { - DEFAULT_THREAD_MANAGER = new DefaultThreadManager(); - } + public static ThreadFactory wrapThreadFactory(ThreadFactory threadFactory, String name) { + return new ThreadFactoryBuilder() + .setThreadFactory(threadFactory) + .setNameFormat(name) + .setDaemon(true) + .build(); } /** @@ -113,52 +111,4 @@ protected ThreadFactory getThreadFactory() { return Executors.defaultThreadFactory(); } } - - /** - * The ThreadManager implementation that will be used by default in the Google App Engine - * environment. - * - *

Auto-scaling: Creates an ExecutorService backed by the request-scoped ThreadFactory. This - * can be used for any short-lived task, such as the ones submitted by components like - * FirebaseAuth. {@link #getThreadFactory()} throws an exception, since long-lived threads - * cannot be supported. Therefore task scheduling and RTDB will not work. - * - *

Manual-scaling: Creates a single-threaded ExecutorService backed by the background - * ThreadFactory. Keeps the threads alive indefinitely by periodically restarting them (see - * {@link RevivingScheduledExecutor}). Threads will be terminated only when the method - * {@link #releaseExecutor(FirebaseApp, ExecutorService)} is invoked. The - * {@link #getThreadFactory()} also returns the background ThreadFactory enabling other - * components in the SDK to start long-lived threads when necessary. Therefore task scheduling - * and RTDB can be supported as if running on the regular JVM. - * - *

Basic-scaling: Behavior is similar to manual-scaling. Since the threads are kept alive - * indefinitely, prevents the GAE idle instance shutdown. Developers are advised to use - * a custom ThreadManager implementation if idle instance shutdown should be supported. In - * general, a ThreadManager implementation that uses the request-scoped ThreadFactory, or the - * background ThreadFactory with specific keep-alive times can easily facilitate GAE idle - * instance shutdown. Note that this often comes at the cost of losing scheduled tasks and RTDB - * support. Therefore, for these features, manual-scaling is the recommended GAE deployment mode - * regardless of the ThreadManager implementation used. - */ - private static class GaeThreadManager extends GlobalThreadManager { - - @Override - protected ExecutorService doInit() { - return new GaeExecutorService("gae-firebase-default"); - } - - @Override - protected void doCleanup(ExecutorService executorService) { - executorService.shutdownNow(); - } - - @Override - protected ThreadFactory getThreadFactory() { - GaeThreadFactory threadFactory = GaeThreadFactory.getInstance(); - checkState(threadFactory.isUsingBackgroundThreads(), - "Failed to initialize a GAE background thread factory. Background thread support " - + "is required to create long-lived threads."); - return threadFactory; - } - } } diff --git a/src/main/java/com/google/firebase/internal/GaeExecutorService.java b/src/main/java/com/google/firebase/internal/GaeExecutorService.java deleted file mode 100644 index 6d1b323e8..000000000 --- a/src/main/java/com/google/firebase/internal/GaeExecutorService.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * 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 com.google.firebase.internal; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; - -import com.google.common.base.Strings; -import com.google.common.collect.ImmutableList; - -import java.util.Collection; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.SynchronousQueue; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicReference; - -/** - * An ExecutorService instance that can operate in the Google App Engine environment. When - * available, uses background thread support to initialize an ExecutorService with long-lived - * threads. Otherwise, creates an ExecutorService that spawns short-lived threads as tasks - * are submitted. The actual ExecutorService implementation is lazy-loaded to prevent making - * unnecessary RPC calls to the GAE's native ThreadFactory mechanism. - */ -class GaeExecutorService implements ExecutorService { - - private final AtomicReference executor = new AtomicReference<>(); - private final String threadName; - private final ThreadFactory threadFactory; - private boolean shutdown; - - GaeExecutorService(String threadName) { - this(threadName, GaeThreadFactory.getInstance()); - } - - GaeExecutorService(String threadName, ThreadFactory threadFactory) { - checkArgument(!Strings.isNullOrEmpty(threadName)); - this.threadName = threadName; - this.threadFactory = threadFactory; - } - - private ExecutorService ensureExecutorService() { - ExecutorService executorService = executor.get(); - if (executorService == null) { - synchronized (executor) { - checkState(!shutdown); - executorService = executor.get(); - if (executorService == null) { - executorService = newExecutorService(threadFactory, threadName); - executor.compareAndSet(null, executorService); - } - } - } - return executorService; - } - - @Override - public Future submit(Callable task) { - return ensureExecutorService().submit(task); - } - - @Override - public Future submit(Runnable task, T result) { - return ensureExecutorService().submit(task, result); - } - - @Override - public Future submit(Runnable task) { - return ensureExecutorService().submit(task); - } - - @Override - public List> invokeAll(Collection> tasks) - throws InterruptedException { - return ensureExecutorService().invokeAll(tasks); - } - - @Override - public List> invokeAll( - Collection> tasks, long timeout, TimeUnit unit) - throws InterruptedException { - return ensureExecutorService().invokeAll(tasks, timeout, unit); - } - - @Override - public T invokeAny(Collection> tasks) - throws InterruptedException, ExecutionException { - return ensureExecutorService().invokeAny(tasks); - } - - @Override - public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - return ensureExecutorService().invokeAny(tasks, timeout, unit); - } - - @Override - public void shutdown() { - synchronized (executor) { - ExecutorService executorService = executor.get(); - if (executorService != null && !shutdown) { - executorService.shutdown(); - } - shutdown = true; - } - } - - @Override - public List shutdownNow() { - synchronized (executor) { - ExecutorService executorService = executor.get(); - List result; - if (executorService != null && !shutdown) { - result = executorService.shutdownNow(); - } else { - result = ImmutableList.of(); - } - shutdown = true; - return result; - } - } - - @Override - public boolean isShutdown() { - synchronized (executor) { - return shutdown; - } - } - - @Override - public boolean isTerminated() { - synchronized (executor) { - if (!shutdown) { - return false; - } - ExecutorService executorService = executor.get(); - return executorService == null || executorService.isTerminated(); - } - } - - @Override - public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { - ExecutorService executorService; - synchronized (executor) { - executorService = executor.get(); - } - // call await outside the lock - return executorService == null || executorService.awaitTermination(timeout, unit); - } - - @Override - public void execute(Runnable command) { - ensureExecutorService().execute(command); - } - - private static ExecutorService newExecutorService( - ThreadFactory threadFactory, String threadName) { - boolean background = threadFactory instanceof GaeThreadFactory - && ((GaeThreadFactory) threadFactory).isUsingBackgroundThreads(); - if (background) { - // Create a thread pool with long-lived threads if background thread support is available. - return new RevivingScheduledExecutor(threadFactory, threadName, true); - } else { - // Create an executor that creates a new thread for each submitted task, when background - // thread support is not available. - return new ThreadPoolExecutor( - 0, - Integer.MAX_VALUE, - 0L, - TimeUnit.SECONDS, - new SynchronousQueue(), - threadFactory); - } - } -} diff --git a/src/main/java/com/google/firebase/internal/GaeThreadFactory.java b/src/main/java/com/google/firebase/internal/GaeThreadFactory.java deleted file mode 100644 index 3791dad78..000000000 --- a/src/main/java/com/google/firebase/internal/GaeThreadFactory.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * 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 com.google.firebase.internal; - -import static com.google.common.base.Preconditions.checkNotNull; - -import java.lang.reflect.InvocationTargetException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicReference; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * GaeThreadFactory is a thread factory that works on App Engine. It uses background threads on - * manually-scaled GAE backends and request-scoped threads on automatically scaled instances. - * - *

This class is thread-safe. - */ -public class GaeThreadFactory implements ThreadFactory { - - private static final Logger logger = LoggerFactory.getLogger(GaeThreadFactory.class); - - public static final ExecutorService DEFAULT_EXECUTOR = - new GaeExecutorService("LegacyFirebaseDefault"); - private static final String GAE_THREAD_MANAGER_CLASS = "com.google.appengine.api.ThreadManager"; - private static final GaeThreadFactory instance = new GaeThreadFactory(); - private final AtomicReference threadFactory = new AtomicReference<>(null); - - private GaeThreadFactory() {} - - public static GaeThreadFactory getInstance() { - return instance; - } - - /** Returns whether GaeThreadFactory can be used on this system (true for GAE). */ - public static boolean isAvailable() { - try { - Class.forName(GAE_THREAD_MANAGER_CLASS); - return System.getProperty("com.google.appengine.runtime.environment") != null; - } catch (ClassNotFoundException e) { - return false; - } - } - - private static ThreadFactory createBackgroundFactory() - throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, - IllegalAccessException { - Class gaeThreadManager = Class.forName(GAE_THREAD_MANAGER_CLASS); - return (ThreadFactory) gaeThreadManager.getMethod("backgroundThreadFactory").invoke(null); - } - - private static ThreadFactory createRequestScopedFactory() - throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, - IllegalAccessException { - Class gaeThreadManager = Class.forName(GAE_THREAD_MANAGER_CLASS); - return (ThreadFactory) gaeThreadManager.getMethod("currentRequestThreadFactory").invoke(null); - } - - @Override - public Thread newThread(Runnable r) { - ThreadFactoryWrapper wrapper = threadFactory.get(); - if (wrapper != null) { - return wrapper.getThreadFactory().newThread(r); - } - return initThreadFactory(r); - } - - /** - * Checks whether background thread support is available in the current environment. This method - * forces the ThreadFactory to get fully initialized (if not already initialized), by running a - * no-op thread. - * - * @return true if background thread support is available, and false otherwise. - */ - boolean isUsingBackgroundThreads() { - ThreadFactoryWrapper wrapper = threadFactory.get(); - if (wrapper != null) { - return wrapper.isUsingBackgroundThreads(); - } - - // Create a no-op thread to force initialize the ThreadFactory implementation. - // Start the resulting thread, since GAE code seems to expect that. - initThreadFactory(new Runnable() { - @Override - public void run() {} - }).start(); - return threadFactory.get().isUsingBackgroundThreads(); - } - - private Thread initThreadFactory(Runnable r) { - ThreadFactory threadFactory; - boolean usesBackgroundThreads = false; - Thread thread; - // Since we can't tell manually-scaled GAE instances apart until we spawn a thread (which - // sends an RPC and thus is done after class initialization), we initialize both of GAE's - // thread factories here and discard one once we detect that we are running in an - // automatically scaled instance. - // - // Note: It's fine if multiple threads access this block at the same time. - try { - try { - threadFactory = createBackgroundFactory(); - thread = threadFactory.newThread(r); - usesBackgroundThreads = true; - } catch (IllegalStateException e) { - logger.info("Falling back to GAE's request-scoped threads. Firebase requires " - + "manually-scaled instances for most operations."); - threadFactory = createRequestScopedFactory(); - thread = threadFactory.newThread(r); - } - } catch (ClassNotFoundException - | InvocationTargetException - | NoSuchMethodException - | IllegalAccessException e) { - threadFactory = - new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - logger.warn("Failed to initialize native GAE thread factory. " - + "GaeThreadFactory cannot be used in a non-GAE environment."); - return null; - } - }; - thread = null; - } - - ThreadFactoryWrapper wrapper = new ThreadFactoryWrapper(threadFactory, usesBackgroundThreads); - this.threadFactory.compareAndSet(null, wrapper); - return thread; - } - - private static class ThreadFactoryWrapper { - - private final ThreadFactory threadFactory; - private final boolean usingBackgroundThreads; - - private ThreadFactoryWrapper(ThreadFactory threadFactory, boolean usingBackgroundThreads) { - this.threadFactory = checkNotNull(threadFactory); - this.usingBackgroundThreads = usingBackgroundThreads; - } - - ThreadFactory getThreadFactory() { - return threadFactory; - } - - boolean isUsingBackgroundThreads() { - return usingBackgroundThreads; - } - } -} diff --git a/src/main/java/com/google/firebase/internal/RevivingScheduledExecutor.java b/src/main/java/com/google/firebase/internal/RevivingScheduledExecutor.java deleted file mode 100644 index efa989943..000000000 --- a/src/main/java/com/google/firebase/internal/RevivingScheduledExecutor.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * 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 com.google.firebase.internal; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.VisibleForTesting; - -import java.security.AccessControlException; -import java.util.concurrent.Callable; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.RunnableScheduledFuture; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * RevivingScheduledExecutor is an implementation of ScheduledThreadPoolExecutor that uses one - * periodically restarting worker thread as its work queue. This allows customers of this class to - * use this executor on App Engine despite App Engine's thread-lifetime limitations. - */ -public class RevivingScheduledExecutor extends ScheduledThreadPoolExecutor { - - private static final Logger logger = LoggerFactory.getLogger(RevivingScheduledExecutor.class); - - /** Exception to throw to shut down the core threads. */ - private static final RuntimeException REVIVE_THREAD_EXCEPTION = new RuntimeException( - "Restarting Firebase Worker Thread. This exception is expected to occur periodically " - + "when deployed in the App Engine environment, and can be ignored."); - - /** The lifetime of a thread. Maximum lifetime of a thread on GAE is 24 hours. */ - private static final long PERIODIC_RESTART_INTERVAL_MS = TimeUnit.HOURS.toMillis(12); - - /** - * Time by which we offset restarts to ensure that not all threads die at the same time. This is - * meant to decrease cross-thread liveliness issues during restarts. - */ - private static final long PERIODIC_RESTART_OFFSET_MS = TimeUnit.MINUTES.toMillis(5); - - private static final AtomicInteger INSTANCE_COUNTER = new AtomicInteger(0); - - private final long initialDelayMs; - private final long timeoutMs; - - // Flag set before throwing a REVIVE_THREAD_EXCEPTION and unset once a new thread has been - // created. Used to call afterRestart() appropriately. - private AtomicBoolean requestedRestart = new AtomicBoolean(); - - /** - * Creates a new RevivingScheduledExecutor that optionally restarts its worker thread every twelve - * hours. - * - * @param threadFactory Thread factory to use to restart threads. - * @param threadName Name of the threads in the pool. - * @param periodicRestart Periodically restart its worked threads. - */ - public RevivingScheduledExecutor( - final ThreadFactory threadFactory, final String threadName, final boolean periodicRestart) { - this( - threadFactory, - threadName, - periodicRestart ? PERIODIC_RESTART_OFFSET_MS * INSTANCE_COUNTER.get() : 0, - periodicRestart ? PERIODIC_RESTART_INTERVAL_MS : -1); - } - - @VisibleForTesting - RevivingScheduledExecutor( - final ThreadFactory threadFactory, - final String threadName, - final long initialDelayMs, - final long timeoutMs) { - super(0); - checkNotNull(threadFactory, "threadFactory must not be null"); - INSTANCE_COUNTER.incrementAndGet(); - this.initialDelayMs = initialDelayMs; - this.timeoutMs = timeoutMs; - setRemoveOnCancelPolicy(true); - setThreadFactory( - new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - logger.debug("Creating new thread for: {}", threadName); - Thread thread = threadFactory.newThread(r); - try { - thread.setName(threadName); - thread.setDaemon(true); - } catch (AccessControlException ignore) { - // Unsupported on App Engine. - } - if (requestedRestart.getAndSet(false)) { - afterRestart(); - } - return thread; - } - }); - } - - @Override - public void execute(Runnable runnable) { - // This gets called when the execute() method from Executor is directly invoked. - ensureRunning(); - super.execute(runnable); - } - - @Override - protected RunnableScheduledFuture decorateTask( - Runnable runnable, RunnableScheduledFuture task) { - // This gets called by ScheduledThreadPoolExecutor before scheduling a Runnable. - ensureRunning(); - return task; - } - - @Override - protected RunnableScheduledFuture decorateTask( - Callable callable, RunnableScheduledFuture task) { - // This gets called by ScheduledThreadPoolExecutor before scheduling a Callable. - ensureRunning(); - return task; - } - - @Override - protected void afterExecute(Runnable runnable, Throwable throwable) { - super.afterExecute(runnable, throwable); - if (throwable == null && runnable instanceof Future) { - Future future = (Future) runnable; - try { - // Not all Futures will be done, e.g. when used with scheduledAtFixedRate - if (future.isDone()) { - future.get(); - } - } catch (CancellationException ce) { - // Cancellation exceptions are okay, we expect them to happen sometimes - } catch (ExecutionException ee) { - throwable = ee.getCause(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - if (throwable == REVIVE_THREAD_EXCEPTION) { - // Re-throwing this exception will kill the thread and cause - // ScheduledThreadPoolExecutor to - // spawn a new thread. - throw (RuntimeException) throwable; - } else if (throwable != null) { - handleException(throwable); - } - } - - /** - * Called when an exception occurs during execution of a Runnable/Callable. The default - * implementation does nothing. - */ - protected void handleException(Throwable throwable) {} - - /** Called before the worker thread gets shutdown before a restart. */ - protected void beforeRestart() {} - - /** Called after the worker thread got recreated after a restart. */ - protected void afterRestart() {} - - private synchronized void ensureRunning() { - if (getCorePoolSize() == 0) { - setCorePoolSize(1); - schedulePeriodicShutdown(); - } - } - - private void schedulePeriodicShutdown() { - if (timeoutMs >= 0) { - @SuppressWarnings("unused") - Future possiblyIgnoredError = - schedule( - new Runnable() { - @Override - public void run() { - // We have to manually reschedule this task here as periodic tasks get - // cancelled after - // throwing exceptions. - @SuppressWarnings("unused") - Future possiblyIgnoredError1 = - RevivingScheduledExecutor.this.schedule( - this, timeoutMs, TimeUnit.MILLISECONDS); - requestedRestart.set(true); - beforeRestart(); - throw REVIVE_THREAD_EXCEPTION; - } - }, - initialDelayMs + timeoutMs, - TimeUnit.MILLISECONDS); - } - } -} diff --git a/src/main/java/com/google/firebase/tasks/TaskExecutors.java b/src/main/java/com/google/firebase/tasks/TaskExecutors.java index 19674d1d1..b324a61c8 100644 --- a/src/main/java/com/google/firebase/tasks/TaskExecutors.java +++ b/src/main/java/com/google/firebase/tasks/TaskExecutors.java @@ -17,7 +17,6 @@ package com.google.firebase.tasks; import com.google.common.util.concurrent.ThreadFactoryBuilder; -import com.google.firebase.internal.GaeThreadFactory; import com.google.firebase.internal.NonNull; import java.util.concurrent.Executor; @@ -49,15 +48,11 @@ public void execute(@NonNull Runnable command) { }; static { - if (GaeThreadFactory.isAvailable()) { - DEFAULT_THREAD_POOL = GaeThreadFactory.DEFAULT_EXECUTOR; - } else { - ThreadFactory threadFactory = new ThreadFactoryBuilder() - .setNameFormat("task-exec-%d") - .setDaemon(true) - .build(); - DEFAULT_THREAD_POOL = Executors.newCachedThreadPool(threadFactory); - } + ThreadFactory threadFactory = new ThreadFactoryBuilder() + .setNameFormat("task-exec-%d") + .setDaemon(true) + .build(); + DEFAULT_THREAD_POOL = Executors.newCachedThreadPool(threadFactory); } private TaskExecutors() {} diff --git a/src/test/java/com/google/firebase/database/core/GaePlatformTest.java b/src/test/java/com/google/firebase/database/core/GaePlatformTest.java index 032de1b4a..d16c25e7f 100644 --- a/src/test/java/com/google/firebase/database/core/GaePlatformTest.java +++ b/src/test/java/com/google/firebase/database/core/GaePlatformTest.java @@ -14,7 +14,6 @@ import com.google.firebase.ThreadManager; import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.database.logging.Logger; -import com.google.firebase.internal.GaeThreadFactory; import com.google.firebase.internal.NonNull; import com.google.firebase.testing.ServiceAccount; import com.google.firebase.testing.TestUtils; @@ -28,11 +27,6 @@ public class GaePlatformTest { - @Test - public void testIsActive() { - assertEquals(GaeThreadFactory.isAvailable(), GaePlatform.isActive()); - } - @Test public void testGaePlatform() { final AtomicInteger count = new AtomicInteger(0); diff --git a/src/test/java/com/google/firebase/database/utilities/DefaultRunLoopTest.java b/src/test/java/com/google/firebase/database/utilities/DefaultRunLoopTest.java index 1029ebef8..065612568 100644 --- a/src/test/java/com/google/firebase/database/utilities/DefaultRunLoopTest.java +++ b/src/test/java/com/google/firebase/database/utilities/DefaultRunLoopTest.java @@ -38,7 +38,7 @@ public class DefaultRunLoopTest { public void testLifecycle() { MockRunLoop runLoop = new MockRunLoop(); try { - assertEquals(0, runLoop.getThreadPool().getCorePoolSize()); + assertEquals(1, runLoop.getThreadPool().getCorePoolSize()); runLoop.scheduleNow(new Runnable() { @Override public void run() { @@ -62,7 +62,7 @@ public void run() { public void testScheduleWithDelay() throws ExecutionException, InterruptedException { MockRunLoop runLoop = new MockRunLoop(); try { - assertEquals(0, runLoop.getThreadPool().getCorePoolSize()); + assertEquals(1, runLoop.getThreadPool().getCorePoolSize()); ScheduledFuture future = runLoop.schedule(new Runnable() { @Override public void run() { @@ -91,7 +91,7 @@ public void uncaughtException(Thread t, Throwable e) { assertSame(exceptionHandler, runLoop.getExceptionHandler()); try { - assertEquals(0, runLoop.getThreadPool().getCorePoolSize()); + assertEquals(1, runLoop.getThreadPool().getCorePoolSize()); runLoop.scheduleNow(new Runnable() { @Override public void run() { diff --git a/src/test/java/com/google/firebase/internal/FirebaseThreadManagersTest.java b/src/test/java/com/google/firebase/internal/FirebaseThreadManagersTest.java index 8abb96f1b..2b117f29f 100644 --- a/src/test/java/com/google/firebase/internal/FirebaseThreadManagersTest.java +++ b/src/test/java/com/google/firebase/internal/FirebaseThreadManagersTest.java @@ -37,7 +37,6 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadFactory; import org.junit.After; -import org.junit.Assume; import org.junit.Test; public class FirebaseThreadManagersTest { @@ -126,8 +125,6 @@ public void testGlobalThreadManagerReInit() { @Test public void testDefaultThreadManager() throws Exception { - Assume.assumeFalse(GaeThreadFactory.isAvailable()); - FirebaseOptions options = new FirebaseOptions.Builder() .setCredentials(new MockGoogleCredentials()) .build(); diff --git a/src/test/java/com/google/firebase/internal/GaeExecutorServiceTest.java b/src/test/java/com/google/firebase/internal/GaeExecutorServiceTest.java deleted file mode 100644 index 48b2678cd..000000000 --- a/src/test/java/com/google/firebase/internal/GaeExecutorServiceTest.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * 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 com.google.firebase.internal; - -import static com.google.firebase.database.TestHelpers.waitFor; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import com.google.common.collect.ImmutableList; -import com.google.firebase.testing.TestUtils; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.Semaphore; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.Test; - -public class GaeExecutorServiceTest { - - @Test - public void testShutdownBeforeUse() throws InterruptedException { - CountingThreadFactory threadFactory = new CountingThreadFactory(); - GaeExecutorService executorService = new GaeExecutorService("test", threadFactory); - assertFalse(executorService.isShutdown()); - assertFalse(executorService.isTerminated()); - - assertEquals(ImmutableList.of(), executorService.shutdownNow()); - assertTrue(executorService.isShutdown()); - assertTrue(executorService.isTerminated()); - assertTrue(executorService.awaitTermination(1, TimeUnit.SECONDS)); - assertEquals(0, threadFactory.counter.get()); - - executorService = new GaeExecutorService("test", threadFactory); - assertFalse(executorService.isShutdown()); - assertFalse(executorService.isTerminated()); - - executorService.shutdownNow(); - assertTrue(executorService.isShutdown()); - assertTrue(executorService.isTerminated()); - assertTrue(executorService.awaitTermination( - TestUtils.TEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)); - assertEquals(0, threadFactory.counter.get()); - } - - @Test - public void testSubmit() throws InterruptedException, ExecutionException { - CountingThreadFactory threadFactory = new CountingThreadFactory(); - GaeExecutorService executorService = new GaeExecutorService("test", threadFactory); - - final Semaphore semaphore = new Semaphore(0); - Future future = executorService.submit(new Runnable() { - @Override - public void run() { - semaphore.release(); - } - }); - assertNotNull(future); - waitFor(semaphore); - - future = executorService.submit(new Runnable() { - @Override - public void run() { - semaphore.release(); - } - }, "result"); - assertNotNull(future); - waitFor(semaphore); - assertEquals("result", future.get()); - - future = executorService.submit(new Callable() { - @Override - public Object call() throws Exception { - semaphore.release(); - return "result2"; - } - }); - assertNotNull(future); - waitFor(semaphore); - assertEquals("result2", future.get()); - - executorService.execute(new Runnable() { - @Override - public void run() { - semaphore.release(); - } - }); - waitFor(semaphore); - - assertEquals(4, threadFactory.counter.get()); - - executorService.shutdown(); - assertTrue(executorService.awaitTermination( - TestUtils.TEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)); - assertTrue(executorService.isShutdown()); - assertTrue(executorService.isTerminated()); - } - - private static class CountingThreadFactory implements ThreadFactory { - - private final AtomicInteger counter = new AtomicInteger(0); - private final ThreadFactory delegate = Executors.defaultThreadFactory(); - - @Override - public Thread newThread(Runnable r) { - counter.incrementAndGet(); - return delegate.newThread(r); - } - } -} diff --git a/src/test/java/com/google/firebase/internal/RevivingScheduledExecutorTest.java b/src/test/java/com/google/firebase/internal/RevivingScheduledExecutorTest.java deleted file mode 100644 index 3a4da947c..000000000 --- a/src/test/java/com/google/firebase/internal/RevivingScheduledExecutorTest.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * 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 com.google.firebase.internal; - -import java.lang.Thread.UncaughtExceptionHandler; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.Semaphore; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; -import org.junit.Assert; -import org.junit.Test; - -public class RevivingScheduledExecutorTest { - - private static final ThreadFactory THREAD_FACTORY = new ExceptionCatchingThreadFactory(); - - @Test - public void testAppEngineRunnable() throws InterruptedException { - final Semaphore semaphore = new Semaphore(0); - final Set threadIds = new HashSet<>(); - - RevivingScheduledExecutor executor = - new RevivingScheduledExecutor(THREAD_FACTORY, "testAppEngineRunnable", 0, 100); - - for (int i = 0; i < 50; ++i) { - // We delay the execution to give the cleanup handler a chance to run. Otherwise, the - // Executor's BlockingQueue will execute all Runnables before the internal thread gets - // replaced. - Thread.sleep(10); - executor.execute( - new Runnable() { - @Override - public void run() { - threadIds.add(Thread.currentThread().getId()); - semaphore.release(); - } - }); - } - - try { - Assert.assertTrue(semaphore.tryAcquire(50, 10, TimeUnit.SECONDS)); - Assert.assertTrue(threadIds.size() > 1); - } finally { - executor.shutdownNow(); - } - } - - @Test - public void testAppEnginePeriodicRunnable() throws InterruptedException { - final Set threadIds = new HashSet<>(); - final Semaphore semaphore = new Semaphore(0); - - RevivingScheduledExecutor executor = - new RevivingScheduledExecutor(THREAD_FACTORY, "testAppEnginePeriodicRunnable", 0, 100); - - ScheduledFuture future = - executor.scheduleAtFixedRate( - new Runnable() { - @Override - public void run() { - threadIds.add(Thread.currentThread().getId()); - semaphore.release(); - } - }, - 0, - 10, - TimeUnit.MILLISECONDS); - - try { - Assert.assertTrue(semaphore.tryAcquire(50, 10, TimeUnit.SECONDS)); - Assert.assertTrue(threadIds.size() > 1); - } finally { - future.cancel(true); - executor.shutdownNow(); - } - } - - @Test - public void testAppEngineDelayedRunnable() throws InterruptedException { - final Semaphore semaphore = new Semaphore(0); - final AtomicInteger threads = new AtomicInteger(0); - - RevivingScheduledExecutor executor = - new RevivingScheduledExecutor(new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - threads.incrementAndGet(); - return THREAD_FACTORY.newThread(r); - } - }, - "testAppEngineDelayedRunnable", - 0, - 100); - - @SuppressWarnings("unused") - Future possiblyIgnoredError = - executor.schedule( - new Runnable() { - @Override - public void run() { - semaphore.release(); - } - }, - 750, - TimeUnit.MILLISECONDS); - - try { - Assert.assertFalse(semaphore.tryAcquire(1, 500, TimeUnit.MILLISECONDS)); - Assert.assertTrue(semaphore.tryAcquire(1, 500, TimeUnit.MILLISECONDS)); - Assert.assertTrue(threads.get() >= 2); - } finally { - executor.shutdownNow(); - } - } - - @Test - public void testAppEngineDelayedCallable() - throws InterruptedException, TimeoutException, ExecutionException { - final AtomicInteger threads = new AtomicInteger(0); - - RevivingScheduledExecutor executor = - new RevivingScheduledExecutor(new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - threads.incrementAndGet(); - return THREAD_FACTORY.newThread(r); - } - }, - "testAppEngineDelayedCallable", - 0, - 100); - - ScheduledFuture future = - executor.schedule( - new Callable() { - @Override - public Boolean call() throws Exception { - return true; - } - }, - 750, - TimeUnit.MILLISECONDS); - - try { - Assert.assertTrue(future.get(1, TimeUnit.SECONDS)); - Assert.assertTrue(threads.get() >= 2); - } finally { - executor.shutdownNow(); - } - } - - @Test - public void testAppEngineCleanup() throws InterruptedException { - final Semaphore beforeSemaphore = new Semaphore(0); - final Semaphore afterSemaphore = new Semaphore(0); - final AtomicInteger threads = new AtomicInteger(0); - - RevivingScheduledExecutor executor = - new RevivingScheduledExecutor(new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - threads.incrementAndGet(); - return THREAD_FACTORY.newThread(r); - } - }, - "testAppEngineCleanup", - 0, - 100) { - @Override - protected void beforeRestart() { - beforeSemaphore.release(); - } - - @Override - protected void afterRestart() { - afterSemaphore.release(); - } - }; - - @SuppressWarnings("unused") - Future possiblyIgnoredError = - executor.submit( - new Runnable() { - @Override - public void run() {} - }); - - try { - Assert.assertTrue(beforeSemaphore.tryAcquire(2, 10, TimeUnit.SECONDS)); - Assert.assertTrue(afterSemaphore.tryAcquire(2, 10, TimeUnit.SECONDS)); - Assert.assertEquals(3, threads.get()); - Assert.assertEquals(0, beforeSemaphore.availablePermits()); - Assert.assertEquals(0, afterSemaphore.availablePermits()); - } finally { - executor.shutdownNow(); - } - } - - private static class ExceptionCatchingThreadFactory implements ThreadFactory { - @Override - public Thread newThread(Runnable r) { - if (r == null) { - return null; - } - Thread thread = Executors.defaultThreadFactory().newThread(r); - thread.setUncaughtExceptionHandler( - new UncaughtExceptionHandler() { - @Override - public void uncaughtException(Thread t, Throwable e) { - // ignore -- to prevent the test output from getting cluttered - } - }); - return thread; - } - } -} From 765fe2ed60b6d5c44c3aa186b50655fbd0dfc53f Mon Sep 17 00:00:00 2001 From: hiranya911 Date: Fri, 2 Mar 2018 14:34:56 -0800 Subject: [PATCH 5/9] Removed GaePlatform implementation --- .../java/com/google/firebase/FirebaseApp.java | 26 +--- .../connection/NettyWebSocketClient.java | 8 +- .../firebase/database/core/Context.java | 52 ++----- .../firebase/database/core/GaePlatform.java | 135 ------------------ .../firebase/database/core/JvmPlatform.java | 9 +- .../firebase/database/core/Platform.java | 2 - .../database/core/ThreadInitializer.java | 46 ------ .../database/core/ThreadPoolEventTarget.java | 17 +-- .../database/utilities/DefaultRunLoop.java | 9 +- .../internal/FirebaseThreadManagers.java | 17 +-- .../google/firebase/internal/ThreadUtils.java | 33 +++++ .../com/google/firebase/FirebaseAppTest.java | 11 -- .../database/core/GaePlatformTest.java | 89 ------------ 13 files changed, 67 insertions(+), 387 deletions(-) delete mode 100644 src/main/java/com/google/firebase/database/core/GaePlatform.java delete mode 100644 src/main/java/com/google/firebase/database/core/ThreadInitializer.java create mode 100644 src/main/java/com/google/firebase/internal/ThreadUtils.java delete mode 100644 src/test/java/com/google/firebase/database/core/GaePlatformTest.java diff --git a/src/main/java/com/google/firebase/FirebaseApp.java b/src/main/java/com/google/firebase/FirebaseApp.java index 9325b8276..4c1364047 100644 --- a/src/main/java/com/google/firebase/FirebaseApp.java +++ b/src/main/java/com/google/firebase/FirebaseApp.java @@ -19,7 +19,6 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; -import static java.nio.charset.StandardCharsets.UTF_8; import com.google.api.client.googleapis.util.Utils; import com.google.api.client.json.JsonFactory; @@ -35,14 +34,13 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; -import com.google.common.io.BaseEncoding; import com.google.firebase.internal.FirebaseAppStore; import com.google.firebase.internal.FirebaseService; -import com.google.firebase.internal.FirebaseThreadManagers; import com.google.firebase.internal.ListenableFuture2ApiFuture; import com.google.firebase.internal.NonNull; import com.google.firebase.internal.Nullable; +import com.google.firebase.internal.ThreadUtils; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; @@ -243,19 +241,6 @@ static void clearInstancesForTest() { } } - /** - * Returns persistence key. Exists to support getting {@link FirebaseApp} persistence key after - * the app has been deleted. - */ - static String getPersistenceKey(String name, FirebaseOptions options) { - return BaseEncoding.base64Url().omitPadding().encode(name.getBytes(UTF_8)); - } - - /** Use this key to store data per FirebaseApp. */ - String getPersistenceKey() { - return FirebaseApp.getPersistenceKey(getName(), getOptions()); - } - private static List getAllAppNames() { Set allAppNames = new HashSet<>(); synchronized (appsLock) { @@ -319,10 +304,7 @@ String getProjectId() { @Override public boolean equals(Object o) { - if (!(o instanceof FirebaseApp)) { - return false; - } - return name.equals(((FirebaseApp) o).getName()); + return o instanceof FirebaseApp && name.equals(((FirebaseApp) o).getName()); } @Override @@ -385,7 +367,7 @@ private ScheduledExecutorService ensureScheduledExecutorService() { synchronized (lock) { checkNotDeleted(); if (scheduledExecutor == null) { - ThreadFactory threadFactory = FirebaseThreadManagers.wrapThreadFactory( + ThreadFactory threadFactory = ThreadUtils.decorateThreadFactory( getThreadFactory(), "firebase-scheduled-worker"); scheduledExecutor = Executors.newSingleThreadScheduledExecutor(threadFactory); } @@ -464,7 +446,7 @@ static class TokenRefresher implements CredentialsChangedListener { } @Override - public final synchronized void onChanged(OAuth2Credentials credentials) throws IOException { + public final synchronized void onChanged(OAuth2Credentials credentials) { if (state.get() != State.STARTED) { return; } diff --git a/src/main/java/com/google/firebase/database/connection/NettyWebSocketClient.java b/src/main/java/com/google/firebase/database/connection/NettyWebSocketClient.java index d3b720c6a..3f44b5ab1 100644 --- a/src/main/java/com/google/firebase/database/connection/NettyWebSocketClient.java +++ b/src/main/java/com/google/firebase/database/connection/NettyWebSocketClient.java @@ -5,7 +5,7 @@ import static com.google.common.base.Preconditions.checkState; import com.google.common.base.Strings; -import com.google.firebase.internal.FirebaseThreadManagers; +import com.google.firebase.internal.ThreadUtils; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; @@ -67,7 +67,7 @@ class NettyWebSocketClient implements WebsocketConnection.WSClient { this.eventHandler = checkNotNull(eventHandler, "event handler must not be null"); this.channelHandler = new WebSocketClientHandler(uri, userAgent, eventHandler); this.executorService = Executors.newSingleThreadExecutor( - FirebaseThreadManagers.wrapThreadFactory(threadFactory, "firebase-websocket-worker")); + ThreadUtils.decorateThreadFactory(threadFactory, "firebase-websocket-worker")); this.group = new NioEventLoopGroup(1, this.executorService); } @@ -103,7 +103,7 @@ protected void initChannel(SocketChannel ch) { channelFuture.addListener( new ChannelFutureListener() { @Override - public void operationComplete(ChannelFuture future) throws Exception { + public void operationComplete(ChannelFuture future) { if (!future.isSuccess()) { eventHandler.onError(future.cause()); } @@ -169,7 +169,7 @@ public void channelInactive(ChannelHandlerContext context) { } @Override - public void channelRead0(ChannelHandlerContext context, Object message) throws Exception { + public void channelRead0(ChannelHandlerContext context, Object message) { Channel channel = context.channel(); if (message instanceof FullHttpResponse) { checkState(!handshaker.isHandshakeComplete()); diff --git a/src/main/java/com/google/firebase/database/core/Context.java b/src/main/java/com/google/firebase/database/core/Context.java index 52c07b8c6..3d8882b65 100644 --- a/src/main/java/com/google/firebase/database/core/Context.java +++ b/src/main/java/com/google/firebase/database/core/Context.java @@ -38,17 +38,18 @@ public class Context { private static final long DEFAULT_CACHE_SIZE = 10 * 1024 * 1024; protected Logger logger; - protected EventTarget eventTarget; - protected AuthTokenProvider authTokenProvider; - protected RunLoop runLoop; - protected String persistenceKey; - protected List loggedComponents; - protected String userAgent; - protected Logger.Level logLevel = Logger.Level.INFO; - protected boolean persistenceEnabled; - protected long cacheSize = DEFAULT_CACHE_SIZE; protected FirebaseApp firebaseApp; - private PersistenceManager forcedPersistenceManager; + + EventTarget eventTarget; + AuthTokenProvider authTokenProvider; + RunLoop runLoop; + String persistenceKey; + List loggedComponents; + Logger.Level logLevel = Logger.Level.INFO; + boolean persistenceEnabled; + long cacheSize = DEFAULT_CACHE_SIZE; + + private String userAgent; private boolean frozen = false; private boolean stopped = false; @@ -78,19 +79,11 @@ public void onError(String error) { private Platform getPlatform() { if (platform == null) { - if (GaePlatform.isActive()) { - platform = new GaePlatform(firebaseApp); - } else { - platform = new JvmPlatform(firebaseApp); - } + platform = new JvmPlatform(firebaseApp); } return platform; } - public boolean isFrozen() { - return frozen; - } - public boolean isStopped() { return stopped; } @@ -137,8 +130,8 @@ void stop() { } } - protected void assertUnfrozen() { - if (isFrozen()) { + void assertUnfrozen() { + if (frozen) { throw new DatabaseException( "Modifications to DatabaseConfig objects must occur before they are in use"); } @@ -168,10 +161,6 @@ public ConnectionContext getConnectionContext() { } PersistenceManager getPersistenceManager(String firebaseId) { - // TODO[persistence]: Create this once and store it. - if (forcedPersistenceManager != null) { - return forcedPersistenceManager; - } if (this.persistenceEnabled) { PersistenceManager cache = platform.createPersistenceManager(this, firebaseId); if (cache == null) { @@ -193,11 +182,6 @@ public long getPersistenceCacheSizeBytes() { return this.cacheSize; } - // For testing - void forcePersistenceManager(PersistenceManager persistenceManager) { - this.forcedPersistenceManager = persistenceManager; - } - public EventTarget getEventTarget() { return eventTarget; } @@ -210,14 +194,6 @@ public String getUserAgent() { return userAgent; } - public String getPlatformVersion() { - return getPlatform().getPlatformVersion(); - } - - public String getSessionPersistenceKey() { - return this.persistenceKey; - } - public AuthTokenProvider getAuthTokenProvider() { return this.authTokenProvider; } diff --git a/src/main/java/com/google/firebase/database/core/GaePlatform.java b/src/main/java/com/google/firebase/database/core/GaePlatform.java deleted file mode 100644 index 45e07cea1..000000000 --- a/src/main/java/com/google/firebase/database/core/GaePlatform.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * 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 com.google.firebase.database.core; - -import com.google.firebase.FirebaseApp; -import com.google.firebase.ImplFirebaseTrampolines; -import com.google.firebase.database.FirebaseDatabase; -import com.google.firebase.database.connection.ConnectionContext; -import com.google.firebase.database.connection.HostInfo; -import com.google.firebase.database.connection.PersistentConnection; -import com.google.firebase.database.connection.PersistentConnectionImpl; -import com.google.firebase.database.core.persistence.PersistenceManager; -import com.google.firebase.database.logging.DefaultLogger; -import com.google.firebase.database.logging.LogWrapper; -import com.google.firebase.database.logging.Logger; -import com.google.firebase.database.utilities.DefaultRunLoop; - -import java.util.List; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; - - -/** - * Represents a Google AppEngine platform. - * - *

This class is not thread-safe. - */ -class GaePlatform implements Platform { - - private static final String GAE_THREAD_MANAGER_CLASS = "com.google.appengine.api.ThreadManager"; - - private static final String PROCESS_PLATFORM = "AppEngine"; - private final FirebaseApp firebaseApp; - - GaePlatform(FirebaseApp firebaseApp) { - this.firebaseApp = firebaseApp; - } - - public static boolean isActive() { - try { - Class.forName(GAE_THREAD_MANAGER_CLASS); - return System.getProperty("com.google.appengine.runtime.environment") != null; - } catch (ClassNotFoundException e) { - return false; - } - } - - @Override - public Logger newLogger(Context ctx, Logger.Level level, List components) { - return new DefaultLogger(level, components); - } - - private ThreadFactory getGaeThreadFactory() { - return ImplFirebaseTrampolines.getThreadFactory(firebaseApp); - } - - @Override - public EventTarget newEventTarget(Context ctx) { - return new ThreadPoolEventTarget(getGaeThreadFactory(), ThreadInitializer.defaultInstance); - } - - @Override - public RunLoop newRunLoop(final Context context) { - final LogWrapper logger = context.getLogger(RunLoop.class); - return new DefaultRunLoop(getGaeThreadFactory()) { - @Override - public void handleException(Throwable e) { - logger.error(DefaultRunLoop.messageForException(e), e); - } - }; - } - - @Override - public AuthTokenProvider newAuthTokenProvider(ScheduledExecutorService executorService) { - return new JvmAuthTokenProvider(this.firebaseApp, executorService); - } - - @Override - public PersistentConnection newPersistentConnection( - Context context, - ConnectionContext connectionContext, - HostInfo info, - PersistentConnection.Delegate delegate) { - return new PersistentConnectionImpl(context.getConnectionContext(), info, delegate); - } - - @Override - public String getUserAgent(Context ctx) { - return PROCESS_PLATFORM + "/" + DEVICE; - } - - @Override - public String getPlatformVersion() { - return "gae-" + FirebaseDatabase.getSdkVersion(); - } - - @Override - public PersistenceManager createPersistenceManager(Context ctx, String namespace) { - return null; - } - - @Override - public ThreadInitializer getThreadInitializer() { - return new ThreadInitializer() { - @Override - public void setName(Thread t, String name) { - // Unsupported by GAE - } - - @Override - public void setDaemon(Thread t, boolean isDaemon) { - // Unsupported by GAE - } - - @Override - public void setUncaughtExceptionHandler(Thread t, Thread.UncaughtExceptionHandler handler) { - // Unsupported by GAE - } - }; - } -} diff --git a/src/main/java/com/google/firebase/database/core/JvmPlatform.java b/src/main/java/com/google/firebase/database/core/JvmPlatform.java index 30a2e25df..2502b6b32 100644 --- a/src/main/java/com/google/firebase/database/core/JvmPlatform.java +++ b/src/main/java/com/google/firebase/database/core/JvmPlatform.java @@ -39,7 +39,7 @@ class JvmPlatform implements Platform { private final FirebaseApp firebaseApp; - public JvmPlatform(FirebaseApp firebaseApp) { + JvmPlatform(FirebaseApp firebaseApp) { this.firebaseApp = firebaseApp; } @@ -51,7 +51,7 @@ public Logger newLogger(Context ctx, Logger.Level level, List components @Override public EventTarget newEventTarget(Context ctx) { ThreadFactory threadFactory = ImplFirebaseTrampolines.getThreadFactory(firebaseApp); - return new ThreadPoolEventTarget(threadFactory, ThreadInitializer.defaultInstance); + return new ThreadPoolEventTarget(threadFactory); } @Override @@ -94,9 +94,4 @@ public String getPlatformVersion() { public PersistenceManager createPersistenceManager(Context ctx, String namespace) { return null; } - - @Override - public ThreadInitializer getThreadInitializer() { - return ThreadInitializer.defaultInstance; - } } diff --git a/src/main/java/com/google/firebase/database/core/Platform.java b/src/main/java/com/google/firebase/database/core/Platform.java index 5e701c408..320a851b5 100644 --- a/src/main/java/com/google/firebase/database/core/Platform.java +++ b/src/main/java/com/google/firebase/database/core/Platform.java @@ -48,6 +48,4 @@ PersistentConnection newPersistentConnection( String getPlatformVersion(); PersistenceManager createPersistenceManager(Context ctx, String firebaseId); - - ThreadInitializer getThreadInitializer(); } diff --git a/src/main/java/com/google/firebase/database/core/ThreadInitializer.java b/src/main/java/com/google/firebase/database/core/ThreadInitializer.java deleted file mode 100644 index 23c3cab88..000000000 --- a/src/main/java/com/google/firebase/database/core/ThreadInitializer.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * 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 com.google.firebase.database.core; - -import java.lang.Thread.UncaughtExceptionHandler; - -public interface ThreadInitializer { - - ThreadInitializer defaultInstance = - new ThreadInitializer() { - @Override - public void setName(Thread t, String name) { - t.setName(name); - } - - @Override - public void setDaemon(Thread t, boolean isDaemon) { - t.setDaemon(isDaemon); - } - - @Override - public void setUncaughtExceptionHandler(Thread t, UncaughtExceptionHandler handler) { - t.setUncaughtExceptionHandler(handler); - } - }; - - void setName(Thread t, String name); - - void setDaemon(Thread t, boolean isDaemon); - - void setUncaughtExceptionHandler(Thread t, Thread.UncaughtExceptionHandler handler); -} diff --git a/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java b/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java index f56a185c6..959fa2095 100644 --- a/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java +++ b/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java @@ -16,6 +16,7 @@ package com.google.firebase.database.core; +import com.google.firebase.internal.ThreadUtils; import java.lang.Thread.UncaughtExceptionHandler; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; @@ -25,7 +26,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -/** ThreadPoolEventTarget is an event target using a configurable threadpool. */ +/** ThreadPoolEventTarget is an event target using a configurable thread pool. */ class ThreadPoolEventTarget implements EventTarget, UncaughtExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(ThreadPoolEventTarget.class); @@ -33,21 +34,11 @@ class ThreadPoolEventTarget implements EventTarget, UncaughtExceptionHandler { private final ThreadPoolExecutor executor; private UncaughtExceptionHandler exceptionHandler; - ThreadPoolEventTarget( - final ThreadFactory wrappedFactory, final ThreadInitializer threadInitializer) { + ThreadPoolEventTarget(ThreadFactory threadFactory) { final int poolSize = 1; BlockingQueue queue = new LinkedBlockingQueue<>(); executor = new ThreadPoolExecutor(poolSize, poolSize, 3, TimeUnit.SECONDS, queue, - new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - Thread thread = wrappedFactory.newThread(r); - threadInitializer.setName(thread, "FirebaseDatabaseEventTarget"); - threadInitializer.setDaemon(thread, true); - threadInitializer.setUncaughtExceptionHandler(thread, ThreadPoolEventTarget.this); - return thread; - } - }); + ThreadUtils.decorateThreadFactory(threadFactory, "firebase-database-event-target", this)); } @Override diff --git a/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java b/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java index 98f585ccd..5c50813ef 100644 --- a/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java +++ b/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java @@ -20,7 +20,7 @@ import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.database.core.RunLoop; -import com.google.firebase.internal.FirebaseThreadManagers; +import com.google.firebase.internal.ThreadUtils; import java.lang.Thread.UncaughtExceptionHandler; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; @@ -40,13 +40,12 @@ public abstract class DefaultRunLoop implements RunLoop { * Creates a DefaultRunLoop that optionally restarts its threads periodically. If 'context' is * provided, these restarts will automatically interrupt and resume all Repo connections. */ - protected DefaultRunLoop(final ThreadFactory threadFactory) { - ThreadFactory wrappedThreadFactory = FirebaseThreadManagers.wrapThreadFactory( + protected DefaultRunLoop(ThreadFactory threadFactory) { + ThreadFactory wrappedThreadFactory = ThreadUtils.decorateThreadFactory( threadFactory, "firebase-database-worker"); executor = new ScheduledThreadPoolExecutor(1, wrappedThreadFactory) { @Override protected void afterExecute(Runnable runnable, Throwable throwable) { - super.afterExecute(runnable, throwable); if (throwable == null && runnable instanceof Future) { Future future = (Future) runnable; try { @@ -71,8 +70,8 @@ protected void afterExecute(Runnable runnable, Throwable throwable) { // Core threads don't time out, this only takes effect when we drop the number of required // core threads - executor.setRemoveOnCancelPolicy(true); executor.setKeepAliveTime(3, TimeUnit.SECONDS); + executor.setRemoveOnCancelPolicy(true); } public static String messageForException(Throwable t) { diff --git a/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java b/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java index d5ef26b8c..969ceb467 100644 --- a/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java +++ b/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java @@ -16,7 +16,6 @@ package com.google.firebase.internal; -import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.firebase.FirebaseApp; import com.google.firebase.ThreadManager; @@ -36,14 +35,6 @@ public class FirebaseThreadManagers { public static final ThreadManager DEFAULT_THREAD_MANAGER = new DefaultThreadManager(); - public static ThreadFactory wrapThreadFactory(ThreadFactory threadFactory, String name) { - return new ThreadFactoryBuilder() - .setThreadFactory(threadFactory) - .setNameFormat(name) - .setDaemon(true) - .build(); - } - /** * An abstract ThreadManager implementation that uses the same executor service * across all active apps. The executor service is initialized when the first app is initialized, @@ -91,12 +82,8 @@ private static class DefaultThreadManager extends GlobalThreadManager { @Override protected ExecutorService doInit() { - // Create threads as daemons to ensure JVM exit when all foreground jobs are complete. - ThreadFactory threadFactory = new ThreadFactoryBuilder() - .setNameFormat("firebase-default-%d") - .setDaemon(true) - .setThreadFactory(getThreadFactory()) - .build(); + ThreadFactory threadFactory = ThreadUtils.decorateThreadFactory( + getThreadFactory(), "firebase-default-%d"); return Executors.newCachedThreadPool(threadFactory); } diff --git a/src/main/java/com/google/firebase/internal/ThreadUtils.java b/src/main/java/com/google/firebase/internal/ThreadUtils.java new file mode 100644 index 000000000..b6383ba60 --- /dev/null +++ b/src/main/java/com/google/firebase/internal/ThreadUtils.java @@ -0,0 +1,33 @@ +package com.google.firebase.internal; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.base.Strings; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.util.concurrent.ThreadFactory; + +public class ThreadUtils { + + public static ThreadFactory decorateThreadFactory(ThreadFactory threadFactory, String name) { + checkArgument(!Strings.isNullOrEmpty(name)); + // Create threads as daemons to ensure JVM exit when all foreground jobs are complete. + return new ThreadFactoryBuilder() + .setThreadFactory(threadFactory) + .setNameFormat(name) + .setDaemon(true) + .build(); + } + + public static ThreadFactory decorateThreadFactory(ThreadFactory threadFactory, String name, + Thread.UncaughtExceptionHandler handler) { + checkArgument(!Strings.isNullOrEmpty(name)); + // Create threads as daemons to ensure JVM exit when all foreground jobs are complete. + return new ThreadFactoryBuilder() + .setThreadFactory(threadFactory) + .setNameFormat(name) + .setDaemon(true) + .setUncaughtExceptionHandler(handler) + .build(); + } + +} diff --git a/src/test/java/com/google/firebase/FirebaseAppTest.java b/src/test/java/com/google/firebase/FirebaseAppTest.java index d8238360c..e549c0955 100644 --- a/src/test/java/com/google/firebase/FirebaseAppTest.java +++ b/src/test/java/com/google/firebase/FirebaseAppTest.java @@ -16,7 +16,6 @@ package com.google.firebase; -import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotSame; @@ -35,7 +34,6 @@ import com.google.auth.oauth2.OAuth2Credentials.CredentialsChangedListener; import com.google.common.base.Defaults; import com.google.common.collect.ImmutableMap; -import com.google.common.io.BaseEncoding; import com.google.firebase.FirebaseApp.TokenRefresher; import com.google.firebase.FirebaseOptions.Builder; import com.google.firebase.database.FirebaseDatabase; @@ -233,15 +231,6 @@ public void testInvokeAfterDeleteThrows() throws Exception { } } - @Test - public void testPersistenceKey() { - String name = "myApp"; - FirebaseApp firebaseApp = FirebaseApp.initializeApp(OPTIONS, name); - String persistenceKey = firebaseApp.getPersistenceKey(); - assertEquals(name, new String(BaseEncoding.base64Url().omitPadding().decode(persistenceKey), - UTF_8)); - } - // Order of test cases matters. @Test(expected = IllegalStateException.class) public void testMissingInit() { diff --git a/src/test/java/com/google/firebase/database/core/GaePlatformTest.java b/src/test/java/com/google/firebase/database/core/GaePlatformTest.java deleted file mode 100644 index d16c25e7f..000000000 --- a/src/test/java/com/google/firebase/database/core/GaePlatformTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.google.firebase.database.core; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import com.google.common.collect.ImmutableList; -import com.google.firebase.FirebaseApp; -import com.google.firebase.FirebaseOptions; -import com.google.firebase.TestOnlyImplFirebaseTrampolines; -import com.google.firebase.ThreadManager; -import com.google.firebase.database.FirebaseDatabase; -import com.google.firebase.database.logging.Logger; -import com.google.firebase.internal.NonNull; -import com.google.firebase.testing.ServiceAccount; -import com.google.firebase.testing.TestUtils; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicInteger; -import org.junit.Test; -import org.mockito.Mockito; - -public class GaePlatformTest { - - @Test - public void testGaePlatform() { - final AtomicInteger count = new AtomicInteger(0); - final ThreadManager threadManager = new ThreadManager() { - @Override - protected ExecutorService getExecutor(@NonNull FirebaseApp app) { - return Executors.newSingleThreadExecutor(); - } - - @Override - protected void releaseExecutor(@NonNull FirebaseApp app, - @NonNull ExecutorService executor) { - } - - @Override - protected ThreadFactory getThreadFactory() { - count.incrementAndGet(); - return Executors.defaultThreadFactory(); - } - }; - - FirebaseOptions options = new FirebaseOptions.Builder() - .setCredentials(TestUtils.getCertCredential(ServiceAccount.EDITOR.asStream())) - .setThreadManager(threadManager) - .build(); - FirebaseApp app = FirebaseApp.initializeApp(options, "gaeApp"); - try { - GaePlatform platform = new GaePlatform(app); - Context ctx = Mockito.mock(Context.class); - assertNotNull(platform.newLogger(ctx, Logger.Level.DEBUG, ImmutableList.of())); - - assertNotNull(platform.newEventTarget(ctx)); - assertEquals(1, count.get()); - - assertNotNull(platform.newRunLoop(ctx)); - assertEquals(2, count.get()); - - AuthTokenProvider authTokenProvider = platform.newAuthTokenProvider( - Mockito.mock(ScheduledExecutorService.class)); - assertTrue(authTokenProvider instanceof JvmAuthTokenProvider); - - assertEquals("AppEngine/AdminJava", platform.getUserAgent(ctx)); - assertEquals("gae-" + FirebaseDatabase.getSdkVersion(), platform.getPlatformVersion()); - assertNull(platform.createPersistenceManager(ctx, "test")); - - ThreadInitializer threadInitializer = platform.getThreadInitializer(); - Thread t = new Thread(); - threadInitializer.setName(t, "test-name"); - threadInitializer.setDaemon(t, true); - threadInitializer.setUncaughtExceptionHandler(t, Mockito.mock( - Thread.UncaughtExceptionHandler.class)); - assertNotEquals("test-name", t.getName()); - assertFalse(t.isDaemon()); - assertNotNull(t.getUncaughtExceptionHandler()); - - } finally { - TestOnlyImplFirebaseTrampolines.clearInstancesForTest(); - } - } -} From baf4040bc8de6dba6ee81866e1f33457b60eeed7 Mon Sep 17 00:00:00 2001 From: hiranya911 Date: Fri, 2 Mar 2018 15:43:28 -0800 Subject: [PATCH 6/9] Added FirebaseScheduledExecutor --- .../java/com/google/firebase/FirebaseApp.java | 8 ++-- .../connection/NettyWebSocketClient.java | 7 ++-- .../database/core/ThreadPoolEventTarget.java | 10 ++--- .../database/utilities/DefaultRunLoop.java | 7 +--- .../internal/FirebaseScheduledExecutor.java | 38 +++++++++++++++++++ .../internal/FirebaseThreadManagers.java | 2 +- .../google/firebase/internal/ThreadUtils.java | 33 ---------------- 7 files changed, 50 insertions(+), 55 deletions(-) create mode 100644 src/main/java/com/google/firebase/internal/FirebaseScheduledExecutor.java delete mode 100644 src/main/java/com/google/firebase/internal/ThreadUtils.java diff --git a/src/main/java/com/google/firebase/FirebaseApp.java b/src/main/java/com/google/firebase/FirebaseApp.java index 4c1364047..bd8527fa3 100644 --- a/src/main/java/com/google/firebase/FirebaseApp.java +++ b/src/main/java/com/google/firebase/FirebaseApp.java @@ -35,12 +35,12 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.firebase.internal.FirebaseAppStore; +import com.google.firebase.internal.FirebaseScheduledExecutor; import com.google.firebase.internal.FirebaseService; import com.google.firebase.internal.ListenableFuture2ApiFuture; import com.google.firebase.internal.NonNull; import com.google.firebase.internal.Nullable; -import com.google.firebase.internal.ThreadUtils; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; @@ -51,7 +51,6 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; -import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -367,9 +366,8 @@ private ScheduledExecutorService ensureScheduledExecutorService() { synchronized (lock) { checkNotDeleted(); if (scheduledExecutor == null) { - ThreadFactory threadFactory = ThreadUtils.decorateThreadFactory( - getThreadFactory(), "firebase-scheduled-worker"); - scheduledExecutor = Executors.newSingleThreadScheduledExecutor(threadFactory); + scheduledExecutor = new FirebaseScheduledExecutor(getThreadFactory(), + "firebase-scheduled-worker"); } } } diff --git a/src/main/java/com/google/firebase/database/connection/NettyWebSocketClient.java b/src/main/java/com/google/firebase/database/connection/NettyWebSocketClient.java index 3f44b5ab1..870bf01a3 100644 --- a/src/main/java/com/google/firebase/database/connection/NettyWebSocketClient.java +++ b/src/main/java/com/google/firebase/database/connection/NettyWebSocketClient.java @@ -5,7 +5,7 @@ import static com.google.common.base.Preconditions.checkState; import com.google.common.base.Strings; -import com.google.firebase.internal.ThreadUtils; +import com.google.firebase.internal.FirebaseScheduledExecutor; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; @@ -35,7 +35,6 @@ import java.net.URI; import java.security.KeyStore; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import javax.net.ssl.TrustManagerFactory; @@ -66,8 +65,8 @@ class NettyWebSocketClient implements WebsocketConnection.WSClient { this.uri = checkNotNull(uri, "uri must not be null"); this.eventHandler = checkNotNull(eventHandler, "event handler must not be null"); this.channelHandler = new WebSocketClientHandler(uri, userAgent, eventHandler); - this.executorService = Executors.newSingleThreadExecutor( - ThreadUtils.decorateThreadFactory(threadFactory, "firebase-websocket-worker")); + this.executorService = new FirebaseScheduledExecutor(threadFactory, + "firebase-websocket-worker"); this.group = new NioEventLoopGroup(1, this.executorService); } diff --git a/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java b/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java index 959fa2095..34162fbe2 100644 --- a/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java +++ b/src/main/java/com/google/firebase/database/core/ThreadPoolEventTarget.java @@ -16,10 +16,8 @@ package com.google.firebase.database.core; -import com.google.firebase.internal.ThreadUtils; +import com.google.firebase.internal.FirebaseScheduledExecutor; import java.lang.Thread.UncaughtExceptionHandler; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -35,10 +33,8 @@ class ThreadPoolEventTarget implements EventTarget, UncaughtExceptionHandler { private UncaughtExceptionHandler exceptionHandler; ThreadPoolEventTarget(ThreadFactory threadFactory) { - final int poolSize = 1; - BlockingQueue queue = new LinkedBlockingQueue<>(); - executor = new ThreadPoolExecutor(poolSize, poolSize, 3, TimeUnit.SECONDS, queue, - ThreadUtils.decorateThreadFactory(threadFactory, "firebase-database-event-target", this)); + executor = new FirebaseScheduledExecutor(threadFactory, "firebase-database-event-target", this); + executor.setKeepAliveTime(3, TimeUnit.SECONDS); } @Override diff --git a/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java b/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java index 5c50813ef..164b7adee 100644 --- a/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java +++ b/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java @@ -20,7 +20,7 @@ import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.database.core.RunLoop; -import com.google.firebase.internal.ThreadUtils; +import com.google.firebase.internal.FirebaseScheduledExecutor; import java.lang.Thread.UncaughtExceptionHandler; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; @@ -41,9 +41,7 @@ public abstract class DefaultRunLoop implements RunLoop { * provided, these restarts will automatically interrupt and resume all Repo connections. */ protected DefaultRunLoop(ThreadFactory threadFactory) { - ThreadFactory wrappedThreadFactory = ThreadUtils.decorateThreadFactory( - threadFactory, "firebase-database-worker"); - executor = new ScheduledThreadPoolExecutor(1, wrappedThreadFactory) { + executor = new FirebaseScheduledExecutor(threadFactory, "firebase-database-worker") { @Override protected void afterExecute(Runnable runnable, Throwable throwable) { if (throwable == null && runnable instanceof Future) { @@ -71,7 +69,6 @@ protected void afterExecute(Runnable runnable, Throwable throwable) { // Core threads don't time out, this only takes effect when we drop the number of required // core threads executor.setKeepAliveTime(3, TimeUnit.SECONDS); - executor.setRemoveOnCancelPolicy(true); } public static String messageForException(Throwable t) { diff --git a/src/main/java/com/google/firebase/internal/FirebaseScheduledExecutor.java b/src/main/java/com/google/firebase/internal/FirebaseScheduledExecutor.java new file mode 100644 index 000000000..98f9ef15c --- /dev/null +++ b/src/main/java/com/google/firebase/internal/FirebaseScheduledExecutor.java @@ -0,0 +1,38 @@ +package com.google.firebase.internal; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.base.Strings; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; + +public class FirebaseScheduledExecutor extends ScheduledThreadPoolExecutor { + + public FirebaseScheduledExecutor(ThreadFactory threadFactory, String name) { + this(threadFactory, name, null); + } + + public FirebaseScheduledExecutor( + ThreadFactory threadFactory, String name, Thread.UncaughtExceptionHandler handler) { + super(1, decorateThreadFactory(threadFactory, name, handler)); + setRemoveOnCancelPolicy(true); + } + + static ThreadFactory decorateThreadFactory(ThreadFactory threadFactory, String name) { + return decorateThreadFactory(threadFactory, name, null); + } + + private static ThreadFactory decorateThreadFactory( + ThreadFactory threadFactory, String name, Thread.UncaughtExceptionHandler handler) { + checkArgument(!Strings.isNullOrEmpty(name)); + ThreadFactoryBuilder builder = new ThreadFactoryBuilder() + .setThreadFactory(threadFactory) + .setNameFormat(name) + .setDaemon(true); + if (handler != null) { + builder.setUncaughtExceptionHandler(handler); + } + return builder.build(); + } +} diff --git a/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java b/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java index 969ceb467..040c87cf8 100644 --- a/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java +++ b/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java @@ -82,7 +82,7 @@ private static class DefaultThreadManager extends GlobalThreadManager { @Override protected ExecutorService doInit() { - ThreadFactory threadFactory = ThreadUtils.decorateThreadFactory( + ThreadFactory threadFactory = FirebaseScheduledExecutor.decorateThreadFactory( getThreadFactory(), "firebase-default-%d"); return Executors.newCachedThreadPool(threadFactory); } diff --git a/src/main/java/com/google/firebase/internal/ThreadUtils.java b/src/main/java/com/google/firebase/internal/ThreadUtils.java deleted file mode 100644 index b6383ba60..000000000 --- a/src/main/java/com/google/firebase/internal/ThreadUtils.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.google.firebase.internal; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.base.Strings; -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import java.util.concurrent.ThreadFactory; - -public class ThreadUtils { - - public static ThreadFactory decorateThreadFactory(ThreadFactory threadFactory, String name) { - checkArgument(!Strings.isNullOrEmpty(name)); - // Create threads as daemons to ensure JVM exit when all foreground jobs are complete. - return new ThreadFactoryBuilder() - .setThreadFactory(threadFactory) - .setNameFormat(name) - .setDaemon(true) - .build(); - } - - public static ThreadFactory decorateThreadFactory(ThreadFactory threadFactory, String name, - Thread.UncaughtExceptionHandler handler) { - checkArgument(!Strings.isNullOrEmpty(name)); - // Create threads as daemons to ensure JVM exit when all foreground jobs are complete. - return new ThreadFactoryBuilder() - .setThreadFactory(threadFactory) - .setNameFormat(name) - .setDaemon(true) - .setUncaughtExceptionHandler(handler) - .build(); - } - -} From d0ce93fb0fca305069bd9bd69f4c9b86765537ef Mon Sep 17 00:00:00 2001 From: hiranya911 Date: Mon, 12 Mar 2018 12:06:10 -0700 Subject: [PATCH 7/9] Updated documentation --- .../internal/FirebaseScheduledExecutor.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/com/google/firebase/internal/FirebaseScheduledExecutor.java b/src/main/java/com/google/firebase/internal/FirebaseScheduledExecutor.java index 98f9ef15c..225bb4413 100644 --- a/src/main/java/com/google/firebase/internal/FirebaseScheduledExecutor.java +++ b/src/main/java/com/google/firebase/internal/FirebaseScheduledExecutor.java @@ -1,3 +1,19 @@ +/* + * Copyright 2018 Google Inc. + * + * 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 com.google.firebase.internal; import static com.google.common.base.Preconditions.checkArgument; @@ -7,6 +23,10 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; +/** + * A single-threaded scheduled executor implementation. Allows naming the threads, and spawns + * new threads as daemons. + */ public class FirebaseScheduledExecutor extends ScheduledThreadPoolExecutor { public FirebaseScheduledExecutor(ThreadFactory threadFactory, String name) { From cf33f09eea795589d1d1aff80212c3ca9585e92f Mon Sep 17 00:00:00 2001 From: hiranya911 Date: Mon, 19 Mar 2018 15:05:47 -0700 Subject: [PATCH 8/9] Some minor nits from code reviews --- CHANGELOG.md | 4 +++- .../java/com/google/firebase/database/core/Context.java | 2 +- .../firebase/internal/FirebaseScheduledExecutor.java | 8 +++++--- .../google/firebase/internal/FirebaseThreadManagers.java | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b5e9f52e..f91d8e00a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,9 @@ - [changed] Removed the deprecated `FirebaseCredential` interface. - [changed] Removed the deprecated `Task` interface along with the `com.google.firebase.tasks` package. -- [changed] Dropped support for App Engine Java 7 runtime. +- [changed] Dropped support for App Engine's Java 7 runtime. Developers + are advised to use the Admin SDK with Java 8 when deploying to App + Engine. # v5.9.0 diff --git a/src/main/java/com/google/firebase/database/core/Context.java b/src/main/java/com/google/firebase/database/core/Context.java index 3d8882b65..fa9943c3a 100644 --- a/src/main/java/com/google/firebase/database/core/Context.java +++ b/src/main/java/com/google/firebase/database/core/Context.java @@ -38,7 +38,7 @@ public class Context { private static final long DEFAULT_CACHE_SIZE = 10 * 1024 * 1024; protected Logger logger; - protected FirebaseApp firebaseApp; + FirebaseApp firebaseApp; EventTarget eventTarget; AuthTokenProvider authTokenProvider; diff --git a/src/main/java/com/google/firebase/internal/FirebaseScheduledExecutor.java b/src/main/java/com/google/firebase/internal/FirebaseScheduledExecutor.java index 225bb4413..0a472ba47 100644 --- a/src/main/java/com/google/firebase/internal/FirebaseScheduledExecutor.java +++ b/src/main/java/com/google/firebase/internal/FirebaseScheduledExecutor.java @@ -29,17 +29,19 @@ */ public class FirebaseScheduledExecutor extends ScheduledThreadPoolExecutor { - public FirebaseScheduledExecutor(ThreadFactory threadFactory, String name) { + public FirebaseScheduledExecutor(@NonNull ThreadFactory threadFactory, @NonNull String name) { this(threadFactory, name, null); } public FirebaseScheduledExecutor( - ThreadFactory threadFactory, String name, Thread.UncaughtExceptionHandler handler) { + @NonNull ThreadFactory threadFactory, @NonNull String name, + @Nullable Thread.UncaughtExceptionHandler handler) { super(1, decorateThreadFactory(threadFactory, name, handler)); setRemoveOnCancelPolicy(true); } - static ThreadFactory decorateThreadFactory(ThreadFactory threadFactory, String name) { + static ThreadFactory getThreadFactoryWithName( + @NonNull ThreadFactory threadFactory, @NonNull String name) { return decorateThreadFactory(threadFactory, name, null); } diff --git a/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java b/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java index 040c87cf8..877450fcc 100644 --- a/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java +++ b/src/main/java/com/google/firebase/internal/FirebaseThreadManagers.java @@ -82,7 +82,7 @@ private static class DefaultThreadManager extends GlobalThreadManager { @Override protected ExecutorService doInit() { - ThreadFactory threadFactory = FirebaseScheduledExecutor.decorateThreadFactory( + ThreadFactory threadFactory = FirebaseScheduledExecutor.getThreadFactoryWithName( getThreadFactory(), "firebase-default-%d"); return Executors.newCachedThreadPool(threadFactory); } From eb52f963a5242b210f6a024d7eafd723746e856b Mon Sep 17 00:00:00 2001 From: hiranya911 Date: Tue, 20 Mar 2018 10:45:05 -0700 Subject: [PATCH 9/9] Calling super method in DefaultRunLoop executor --- .../com/google/firebase/database/utilities/DefaultRunLoop.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java b/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java index 164b7adee..e84b7b561 100644 --- a/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java +++ b/src/main/java/com/google/firebase/database/utilities/DefaultRunLoop.java @@ -44,6 +44,7 @@ protected DefaultRunLoop(ThreadFactory threadFactory) { executor = new FirebaseScheduledExecutor(threadFactory, "firebase-database-worker") { @Override protected void afterExecute(Runnable runnable, Throwable throwable) { + super.afterExecute(runnable, throwable); if (throwable == null && runnable instanceof Future) { Future future = (Future) runnable; try {