From 79315a793b621aded642911d6ed148b8a7c0cd26 Mon Sep 17 00:00:00 2001 From: hiranya911 Date: Wed, 10 Jan 2018 15:32:20 -0800 Subject: [PATCH 01/12] 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 02/12] 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 03/12] 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 04/12] 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 05/12] 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 06/12] 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 07/12] 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 7a9c1a588f02779c882da55b8fa5f40a6c5a0834 Mon Sep 17 00:00:00 2001 From: hiranya911 Date: Thu, 15 Mar 2018 15:24:14 -0700 Subject: [PATCH 08/12] Removing LogWrapper API --- logging.properties | 0 .../firebase/database/FirebaseDatabase.java | 18 --- .../database/connection/Connection.java | 79 +++-------- .../connection/ConnectionContext.java | 9 -- .../connection/PersistentConnectionImpl.java | 133 +++++------------- .../database/connection/PrefixedLogger.java | 58 ++++++++ .../connection/WebsocketConnection.java | 49 ++----- .../database/connection/util/RetryHelper.java | 17 +-- .../firebase/database/core/Context.java | 25 ---- .../database/core/DatabaseConfig.java | 63 --------- .../firebase/database/core/JvmPlatform.java | 13 +- .../firebase/database/core/Platform.java | 4 - .../google/firebase/database/core/Repo.java | 70 +++------ .../firebase/database/core/SyncTree.java | 12 +- .../DefaultPersistenceManager.java | 26 ++-- .../core/persistence/TrackedQueryManager.java | 19 +-- .../database/core/view/EventRaiser.java | 15 +- .../database/logging/DefaultLogger.java | 93 ------------ .../firebase/database/logging/LogWrapper.java | 127 ----------------- .../firebase/database/logging/Logger.java | 47 ------- .../database/logging/package-info.java | 20 --- src/test/java/com/google/firebase/Main.java | 50 +++++++ .../google/firebase/database/TestHelpers.java | 14 +- .../database/connection/ConnectionTest.java | 2 - .../firebase/database/core/SyncPointTest.java | 17 +-- .../DefaultPersistenceManagerTest.java | 4 +- .../persistence/RandomPersistenceTest.java | 4 +- .../persistence/TrackedQueryManagerTest.java | 9 +- .../database/integration/ShutdownExample.java | 2 - 29 files changed, 254 insertions(+), 745 deletions(-) create mode 100644 logging.properties create mode 100644 src/main/java/com/google/firebase/database/connection/PrefixedLogger.java delete mode 100644 src/main/java/com/google/firebase/database/logging/DefaultLogger.java delete mode 100644 src/main/java/com/google/firebase/database/logging/LogWrapper.java delete mode 100644 src/main/java/com/google/firebase/database/logging/Logger.java delete mode 100644 src/main/java/com/google/firebase/database/logging/package-info.java create mode 100644 src/test/java/com/google/firebase/Main.java diff --git a/logging.properties b/logging.properties new file mode 100644 index 000000000..e69de29bb diff --git a/src/main/java/com/google/firebase/database/FirebaseDatabase.java b/src/main/java/com/google/firebase/database/FirebaseDatabase.java index 6a196f913..2ddb55d58 100644 --- a/src/main/java/com/google/firebase/database/FirebaseDatabase.java +++ b/src/main/java/com/google/firebase/database/FirebaseDatabase.java @@ -256,24 +256,6 @@ public void goOffline() { RepoManager.interrupt(ensureRepo()); } - /** - * By default, this is set to {@link Logger.Level#INFO INFO}. This includes any internal errors - * ({@link Logger.Level#ERROR ERROR}) and any security debug messages ({@link Logger.Level#INFO - * INFO}) that the client receives. Set to {@link Logger.Level#DEBUG DEBUG} to turn on the - * diagnostic logging, and {@link Logger.Level#NONE NONE} to disable all logging. - * - * @param logLevel The desired minimum log level - * @deprecated This method will be removed in a future release. Use SLF4J-based logging instead. - * For example, add the slf4j-simple.jar to the classpath to log to STDERR. See - * SLF4J user manual for more details. - */ - public synchronized void setLogLevel(Logger.Level logLevel) { - synchronized (lock) { - assertUnfrozen("setLogLevel"); - this.config.setLogLevel(logLevel); - } - } - /** * The Firebase Database client will cache synchronized data and keep track of all writes you've * initiated while your application is running. It seamlessly handles intermittent network diff --git a/src/main/java/com/google/firebase/database/connection/Connection.java b/src/main/java/com/google/firebase/database/connection/Connection.java index 4059e46e8..436ef8c6a 100644 --- a/src/main/java/com/google/firebase/database/connection/Connection.java +++ b/src/main/java/com/google/firebase/database/connection/Connection.java @@ -17,10 +17,10 @@ package com.google.firebase.database.connection; import com.google.common.annotations.VisibleForTesting; -import com.google.firebase.database.logging.LogWrapper; import java.util.HashMap; import java.util.Map; +import org.slf4j.LoggerFactory; class Connection implements WebsocketConnection.Delegate { @@ -41,7 +41,7 @@ class Connection implements WebsocketConnection.Delegate { private static final String SERVER_HELLO_SESSION_ID = "s"; private static long connectionIds = 0; - private final LogWrapper logger; + private final PrefixedLogger logger; private final HostInfo hostInfo; private final Delegate delegate; @@ -54,36 +54,32 @@ class Connection implements WebsocketConnection.Delegate { String cachedHost, Delegate delegate, String optLastSessionId) { - this(context, hostInfo, delegate, + this(hostInfo, delegate, new DefaultWebsocketConnectionFactory(context, hostInfo, cachedHost, optLastSessionId)); } @VisibleForTesting Connection( - ConnectionContext context, HostInfo hostInfo, Delegate delegate, WebsocketConnectionFactory connFactory) { long connId = connectionIds++; this.hostInfo = hostInfo; this.delegate = delegate; - this.logger = new LogWrapper(context.getLogger(), Connection.class, "conn_" + connId); + this.logger = new PrefixedLogger(LoggerFactory.getLogger(Connection.class), + "[conn_" + connId + "]"); this.state = State.REALTIME_CONNECTING; this.conn = connFactory.newConnection(this); } public void open() { - if (logger.logsDebug()) { - logger.debug("Opening a connection"); - } + logger.debug("Opening a connection"); conn.open(); } public void close(DisconnectReason reason) { if (state != State.REALTIME_DISCONNECTED) { - if (logger.logsDebug()) { - logger.debug("closing realtime connection"); - } + logger.debug("closing realtime connection"); state = State.REALTIME_DISCONNECTED; if (conn != null) { @@ -123,21 +119,14 @@ public void onMessage(Map message) { Map data = (Map) message.get(SERVER_ENVELOPE_DATA); onControlMessage(data); } else { - if (logger.logsDebug()) { - logger.debug("Ignoring unknown server message type: " + messageType); - } + logger.debug("Ignoring unknown server message type: {}", messageType); } } else { - if (logger.logsDebug()) { - logger.debug( - "Failed to parse server message: missing message type:" + message.toString()); - } + logger.debug("Failed to parse server message: missing message type: {}", message); close(); } } catch (ClassCastException e) { - if (logger.logsDebug()) { - logger.debug("Failed to parse server message: " + e.toString()); - } + logger.debug("Failed to parse server message", e); close(); } } @@ -146,30 +135,22 @@ public void onMessage(Map message) { public void onDisconnect(boolean wasEverConnected) { conn = null; if (!wasEverConnected && state == State.REALTIME_CONNECTING) { - if (logger.logsDebug()) { - logger.debug("Realtime connection failed"); - } + logger.debug("Realtime connection failed"); } else { - if (logger.logsDebug()) { - logger.debug("Realtime connection lost"); - } + logger.debug("Realtime connection lost"); } close(); } private void onDataMessage(Map data) { - if (logger.logsDebug()) { - logger.debug("received data message: " + data.toString()); - } + logger.debug("received data message: {}", data); // We don't do anything with data messages, just kick them up a level delegate.onDataMessage(data); } private void onControlMessage(Map data) { - if (logger.logsDebug()) { - logger.debug("Got control message: " + data.toString()); - } + logger.debug("Got control message: {}", data); try { String messageType = (String) data.get(SERVER_CONTROL_MESSAGE_TYPE); if (messageType != null) { @@ -185,28 +166,20 @@ private void onControlMessage(Map data) { (Map) data.get(SERVER_CONTROL_MESSAGE_DATA); onHandshake(handshakeData); } else { - if (logger.logsDebug()) { - logger.debug("Ignoring unknown control message: " + messageType); - } + logger.debug("Ignoring unknown control message: {}", messageType); } } else { - if (logger.logsDebug()) { - logger.debug("Got invalid control message: " + data.toString()); - } + logger.debug("Got invalid control message: {}", data); close(); } } catch (ClassCastException e) { - if (logger.logsDebug()) { - logger.debug("Failed to parse control message: " + e.toString()); - } + logger.debug("Failed to parse control message", e); close(); } } private void onConnectionShutdown(String reason) { - if (logger.logsDebug()) { - logger.debug("Connection shutdown command received. Shutting down..."); - } + logger.debug("Connection shutdown command received. Shutting down..."); delegate.onKill(reason); close(); } @@ -224,21 +197,15 @@ private void onHandshake(Map handshake) { } private void onConnectionReady(long timestamp, String sessionId) { - if (logger.logsDebug()) { - logger.debug("realtime connection established"); - } + logger.debug("realtime connection established"); state = State.REALTIME_CONNECTED; delegate.onReady(timestamp, sessionId); } private void onReset(String host) { - if (logger.logsDebug()) { - logger.debug( - "Got a reset; killing connection to " - + this.hostInfo.getHost() - + "; Updating internalHost to " - + host); - } + logger.debug( + "Got a reset; killing connection to {}; Updating internalHost to {}", + this.hostInfo.getHost(), host); delegate.onCacheHost(host); // Explicitly close the connection with SERVER_RESET so calling code knows to reconnect @@ -253,7 +220,7 @@ private void sendData(Map data, boolean isSensitive) { if (isSensitive) { logger.debug("Sending data (contents hidden)"); } else { - logger.debug("Sending data: %s", data); + logger.debug("Sending data: {}", data); } conn.send(data); } diff --git a/src/main/java/com/google/firebase/database/connection/ConnectionContext.java b/src/main/java/com/google/firebase/database/connection/ConnectionContext.java index 03b4b45f4..574bc1df9 100644 --- a/src/main/java/com/google/firebase/database/connection/ConnectionContext.java +++ b/src/main/java/com/google/firebase/database/connection/ConnectionContext.java @@ -16,8 +16,6 @@ package com.google.firebase.database.connection; -import com.google.firebase.database.logging.Logger; - import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; @@ -25,21 +23,18 @@ public class ConnectionContext { private final ScheduledExecutorService executorService; private final ConnectionAuthTokenProvider authTokenProvider; - private final Logger logger; private final boolean persistenceEnabled; private final String clientSdkVersion; private final String userAgent; private final ThreadFactory threadFactory; public ConnectionContext( - Logger logger, ConnectionAuthTokenProvider authTokenProvider, ScheduledExecutorService executorService, boolean persistenceEnabled, String clientSdkVersion, String userAgent, ThreadFactory threadFactory) { - this.logger = logger; this.authTokenProvider = authTokenProvider; this.executorService = executorService; this.persistenceEnabled = persistenceEnabled; @@ -48,10 +43,6 @@ public ConnectionContext( this.threadFactory = threadFactory; } - public Logger getLogger() { - return this.logger; - } - public ConnectionAuthTokenProvider getAuthTokenProvider() { return this.authTokenProvider; } diff --git a/src/main/java/com/google/firebase/database/connection/PersistentConnectionImpl.java b/src/main/java/com/google/firebase/database/connection/PersistentConnectionImpl.java index 5f2f71303..ae5aa708e 100644 --- a/src/main/java/com/google/firebase/database/connection/PersistentConnectionImpl.java +++ b/src/main/java/com/google/firebase/database/connection/PersistentConnectionImpl.java @@ -19,7 +19,6 @@ import static com.google.firebase.database.connection.ConnectionUtils.hardAssert; import com.google.firebase.database.connection.util.RetryHelper; -import com.google.firebase.database.logging.LogWrapper; import com.google.firebase.database.util.GAuthToken; import java.util.ArrayList; @@ -33,6 +32,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import org.slf4j.LoggerFactory; public class PersistentConnectionImpl implements Connection.Delegate, PersistentConnection { @@ -99,7 +99,7 @@ public class PersistentConnectionImpl implements Connection.Delegate, Persistent private final ConnectionFactory connFactory; private final ConnectionAuthTokenProvider authTokenProvider; private final ScheduledExecutorService executorService; - private final LogWrapper logger; + private final PrefixedLogger logger; private final RetryHelper retryHelper; private String cachedHost; @@ -143,7 +143,7 @@ public PersistentConnectionImpl(ConnectionContext context, HostInfo info, Delega this.outstandingPuts = new HashMap<>(); this.onDisconnectRequestQueue = new ArrayList<>(); this.retryHelper = - new RetryHelper.Builder(this.executorService, context.getLogger(), RetryHelper.class) + new RetryHelper.Builder(this.executorService, RetryHelper.class) .withMinDelayAfterFailure(1000) .withRetryExponent(1.3) .withMaxDelay(30 * 1000) @@ -151,7 +151,8 @@ public PersistentConnectionImpl(ConnectionContext context, HostInfo info, Delega .build(); long connId = connectionIds++; - this.logger = new LogWrapper(context.getLogger(), PersistentConnection.class, "pc_" + connId); + this.logger = new PrefixedLogger(LoggerFactory.getLogger(PersistentConnection.class), + "[pc_" + connId + "]"); this.lastSessionId = null; doIdleCheck(); } @@ -159,9 +160,7 @@ public PersistentConnectionImpl(ConnectionContext context, HostInfo info, Delega // Connection.Delegate methods @Override public void onReady(long timestamp, String sessionId) { - if (logger.logsDebug()) { - logger.debug("onReady"); - } + logger.debug("onReady"); lastConnectionEstablishedTime = System.currentTimeMillis(); handleTimestamp(timestamp); @@ -188,16 +187,12 @@ public void listen( Long tag, RequestResultCallback listener) { ListenQuerySpec query = new ListenQuerySpec(path, queryParams); - if (logger.logsDebug()) { - logger.debug("Listening on " + query); - } + logger.debug("Listening on {}", query); // TODO: Fix this somehow? //hardAssert(query.isDefault() || !query.loadsAllData(), "listen() called for non-default but " // + "complete query"); hardAssert(!listens.containsKey(query), "listen() called twice for same QuerySpec."); - if (logger.logsDebug()) { - logger.debug("Adding listen query: " + query); - } + logger.debug("Adding listen query: {}", query); OutstandingListen outstandingListen = new OutstandingListen(listener, query, tag, currentHashFn); listens.put(query, outstandingListen); @@ -277,17 +272,13 @@ public void onDataMessage(Map message) { Map body = (Map) message.get(SERVER_ASYNC_PAYLOAD); onDataPush(action, body); } else { - if (logger.logsDebug()) { - logger.debug("Ignoring unknown message: " + message); - } + logger.debug("Ignoring unknown message: {}", message); } } @Override public void onDisconnect(Connection.DisconnectReason reason) { - if (logger.logsDebug()) { - logger.debug("Got on disconnect due to " + reason.name()); - } + logger.debug("Got on disconnect due to {}", reason.name()); this.connectionState = ConnectionState.Disconnected; this.realtime = null; this.hasOnDisconnects = false; @@ -319,21 +310,16 @@ public void onDisconnect(Connection.DisconnectReason reason) { @Override public void onKill(String reason) { - if (logger.logsDebug()) { - logger.debug( - "Firebase Database connection was forcefully killed by the server. Will not attempt " - + "reconnect. Reason: " - + reason); - } + logger.debug( + "Firebase Database connection was forcefully killed by the server. Will not attempt " + + "reconnect. Reason: {}", reason); interrupt(SERVER_KILL_INTERRUPT_REASON); } @Override public void unlisten(List path, Map queryParams) { ListenQuerySpec query = new ListenQuerySpec(path, queryParams); - if (logger.logsDebug()) { - logger.debug("unlistening on " + query); - } + logger.debug("unlistening on {}", query); // TODO: fix this by understanding query params? //Utilities.hardAssert(query.isDefault() || !query.loadsAllData(), @@ -395,9 +381,7 @@ public void onDisconnectCancel(List path, RequestResultCallback onComple @Override public void interrupt(String reason) { - if (logger.logsDebug()) { - logger.debug("Connection interrupted for: " + reason); - } + logger.debug("Connection interrupted for: {}", reason); interruptReasons.add(reason); if (realtime != null) { @@ -414,10 +398,7 @@ public void interrupt(String reason) { @Override public void resume(String reason) { - if (logger.logsDebug()) { - logger.debug("Connection no longer interrupted for: " + reason); - } - + logger.debug("Connection no longer interrupted for: {}", reason); interruptReasons.remove(reason); if (shouldReconnect() && connectionState == ConnectionState.Disconnected) { @@ -609,14 +590,10 @@ private void sendUnlisten(OutstandingListen listen) { } private OutstandingListen removeListen(ListenQuerySpec query) { - if (logger.logsDebug()) { - logger.debug("removing query " + query); - } + logger.debug("removing query {}", query); if (!listens.containsKey(query)) { - if (logger.logsDebug()) { - logger.debug( - "Trying to remove listener for QuerySpec " + query + " but no listener exists."); - } + logger.debug( + "Trying to remove listener for QuerySpec {} but no listener exists.", query); return null; } else { OutstandingListen oldListen = listens.get(query); @@ -627,9 +604,7 @@ private OutstandingListen removeListen(ListenQuerySpec query) { } private Collection removeListens(List path) { - if (logger.logsDebug()) { - logger.debug("removing all listens at path " + path); - } + logger.debug("removing all listens at path {}", path); List removedListens = new ArrayList<>(); for (Map.Entry entry : listens.entrySet()) { ListenQuerySpec query = entry.getKey(); @@ -649,9 +624,7 @@ private Collection removeListens(List path) { } private void onDataPush(String action, Map body) { - if (logger.logsDebug()) { - logger.debug("handleServerMessage: " + action + " " + body); - } + logger.debug("handleServerMessage: {} {}", action, body); if (action.equals(SERVER_ASYNC_DATA_UPDATE) || action.equals(SERVER_ASYNC_DATA_MERGE)) { boolean isMerge = action.equals(SERVER_ASYNC_DATA_MERGE); @@ -660,9 +633,7 @@ private void onDataPush(String action, Map body) { Long tagNumber = ConnectionUtils.longFromObject(body.get(SERVER_DATA_TAG)); // ignore empty merges if (isMerge && (payloadData instanceof Map) && ((Map) payloadData).size() == 0) { - if (logger.logsDebug()) { - logger.debug("ignoring empty merge for path " + pathString); - } + logger.debug("ignoring empty merge for path {}", pathString); } else { List path = ConnectionUtils.stringToPath(pathString); delegate.onDataUpdate(path, payloadData, isMerge, tagNumber); @@ -684,9 +655,7 @@ private void onDataPush(String action, Map body) { rangeMerges.add(new RangeMerge(start, end, update)); } if (rangeMerges.isEmpty()) { - if (logger.logsDebug()) { - logger.debug("Ignoring empty range merge for path " + pathString); - } + logger.debug("Ignoring empty range merge for path {}", pathString); } else { this.delegate.onRangeMergeUpdate(path, rangeMerges, tag); } @@ -701,9 +670,7 @@ private void onDataPush(String action, Map body) { } else if (action.equals(SERVER_ASYNC_SECURITY_DEBUG)) { onSecurityDebugPacket(body); } else { - if (logger.logsDebug()) { - logger.debug("Unrecognized action from server: " + action); - } + logger.debug("Unrecognized action from server: {}", action); } } @@ -814,9 +781,7 @@ private void sendUnauth() { } private void restoreAuth() { - if (logger.logsDebug()) { - logger.debug("calling restore state"); - } + logger.debug("calling restore state"); hardAssert( this.connectionState == ConnectionState.Connecting, @@ -824,15 +789,11 @@ private void restoreAuth() { this.connectionState); if (authToken == null) { - if (logger.logsDebug()) { - logger.debug("Not restoring auth because token is null."); - } + logger.debug("Not restoring auth because token is null."); this.connectionState = ConnectionState.Connected; restoreState(); } else { - if (logger.logsDebug()) { - logger.debug("Restoring auth."); - } + logger.debug("Restoring auth."); this.connectionState = ConnectionState.Authenticating; sendAuthAndRestoreState(); } @@ -845,19 +806,13 @@ private void restoreState() { this.connectionState); // Restore listens - if (logger.logsDebug()) { - logger.debug("Restoring outstanding listens"); - } + logger.debug("Restoring outstanding listens"); for (OutstandingListen listen : listens.values()) { - if (logger.logsDebug()) { - logger.debug("Restoring listen " + listen.getQuery()); - } + logger.debug("Restoring listen {}", listen.getQuery()); sendListen(listen); } - if (logger.logsDebug()) { - logger.debug("Restoring writes."); - } + logger.debug("Restoring writes."); // Restore puts ArrayList outstanding = new ArrayList<>(outstandingPuts.keySet()); // Make sure puts are restored in order @@ -878,9 +833,7 @@ private void restoreState() { } private void handleTimestamp(long timestamp) { - if (logger.logsDebug()) { - logger.debug("handling timestamp"); - } + logger.debug("handling timestamp"); long timestampDelta = timestamp - System.currentTimeMillis(); Map updates = new HashMap<>(); updates.put(Constants.DOT_INFO_SERVERTIME_OFFSET, timestampDelta); @@ -930,9 +883,7 @@ assert canSendWrites() new ConnectionRequestCallback() { @Override public void onResponse(Map response) { - if (logger.logsDebug()) { - logger.debug(action + " response: " + response); - } + logger.debug("{} response: {}", action, response); OutstandingPut currentPut = outstandingPuts.get(putId); if (currentPut == put) { @@ -948,10 +899,8 @@ public void onResponse(Map response) { } } } else { - if (logger.logsDebug()) { - logger.debug( - "Ignoring on complete for put " + putId + " because it was removed already."); - } + logger.debug( + "Ignoring on complete for put {} because it was removed already.", putId); } doIdleCheck(); } @@ -1032,17 +981,13 @@ public void onResponse(Map response) { String status = (String) response.get(REQUEST_STATUS); if (!status.equals("ok")) { String errorMessage = (String) response.get(SERVER_DATA_UPDATE_BODY); - if (logger.logsDebug()) { - logger.debug( - "Failed to send stats: " + status + " (message: " + errorMessage + ")"); - } + logger.debug( + "Failed to send stats: {} (message: {})", stats, errorMessage); } } }); } else { - if (logger.logsDebug()) { - logger.debug("Not sending stats because stats are empty"); - } + logger.debug("Not sending stats because stats are empty"); } } @@ -1064,9 +1009,7 @@ private void sendConnectStats() { assert !this.context.isPersistenceEnabled() : "Stats for persistence on JVM missing (persistence not yet supported)"; stats.put("sdk.admin_java." + context.getClientSdkVersion().replace('.', '-'), 1); - if (logger.logsDebug()) { - logger.debug("Sending first connection stats"); - } + logger.debug("Sending first connection stats"); sendStats(stats); } diff --git a/src/main/java/com/google/firebase/database/connection/PrefixedLogger.java b/src/main/java/com/google/firebase/database/connection/PrefixedLogger.java new file mode 100644 index 000000000..293c22d32 --- /dev/null +++ b/src/main/java/com/google/firebase/database/connection/PrefixedLogger.java @@ -0,0 +1,58 @@ +/* + * 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.connection; + +import org.slf4j.Logger; + +public class PrefixedLogger { + + private final Logger logger; + private final String prefix; + + public PrefixedLogger(Logger logger, String prefix) { + this.logger = logger; + this.prefix = prefix; + } + + public void debug(String msg, Object... args) { + if (logger.isDebugEnabled()) { + logger.debug(addPrefix(msg), args); + } + } + + public void debug(String msg, Throwable t) { + if (logger.isDebugEnabled()) { + logger.debug(addPrefix(msg), t); + } + } + + public void error(String msg, Throwable t) { + logger.error(addPrefix(msg), t); + } + + public void warn(String msg) { + logger.warn(addPrefix(msg)); + } + + public void info(String msg) { + logger.info(addPrefix(msg)); + } + + private String addPrefix(String msg) { + return prefix + " " + msg; + } +} diff --git a/src/main/java/com/google/firebase/database/connection/WebsocketConnection.java b/src/main/java/com/google/firebase/database/connection/WebsocketConnection.java index d58a1af80..512f81023 100644 --- a/src/main/java/com/google/firebase/database/connection/WebsocketConnection.java +++ b/src/main/java/com/google/firebase/database/connection/WebsocketConnection.java @@ -20,7 +20,6 @@ import static com.google.common.base.Preconditions.checkState; import com.google.common.collect.ImmutableList; -import com.google.firebase.database.logging.LogWrapper; import com.google.firebase.database.util.JsonMapper; import java.io.EOFException; @@ -33,6 +32,8 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Represents a WebSocket connection to the Firebase Realtime Database. This abstraction acts as @@ -49,7 +50,7 @@ class WebsocketConnection { private static final AtomicLong CONN_ID = new AtomicLong(0); private final ScheduledExecutorService executorService; - private final LogWrapper logger; + private final PrefixedLogger logger; private final WSClient conn; private final Delegate delegate; @@ -75,8 +76,8 @@ class WebsocketConnection { WSClientFactory clientFactory) { this.executorService = connectionContext.getExecutorService(); this.delegate = delegate; - this.logger = new LogWrapper(connectionContext.getLogger(), WebsocketConnection.class, - "ws_" + CONN_ID.getAndIncrement()); + this.logger = new PrefixedLogger(LoggerFactory.getLogger(WebsocketConnection.class), + "[ws_" + CONN_ID.getAndIncrement() + "]"); this.conn = clientFactory.newClient(new WSClientHandlerImpl()); } @@ -99,9 +100,7 @@ void start() { } void close() { - if (logger.logsDebug()) { - logger.debug("websocket is being closed"); - } + logger.debug("websocket is being closed"); isClosed = true; conn.close(); @@ -150,9 +149,7 @@ private List splitIntoFrames(String src, int maxFrameSize) { } private void handleNewFrameCount(int numFrames) { - if (logger.logsDebug()) { - logger.debug("HandleNewFrameCount: " + numFrames); - } + logger.debug("HandleNewFrameCount: {}", numFrames); buffer = new StringList(numFrames); } @@ -165,9 +162,7 @@ private void appendFrame(String message) { String combined = buffer.combine(); try { Map decoded = JsonMapper.parseJson(combined); - if (logger.logsDebug()) { - logger.debug("handleIncomingFrame complete frame: " + decoded); - } + logger.debug("handleIncomingFrame complete frame: {}", decoded); delegate.onMessage(decoded); } catch (IOException e) { logger.error("Error parsing frame: " + combined, e); @@ -218,13 +213,9 @@ private void resetKeepAlive() { } if (keepAlive != null) { keepAlive.cancel(false); - if (logger.logsDebug()) { - logger.debug("Reset keepAlive. Remaining: " + keepAlive.getDelay(TimeUnit.MILLISECONDS)); - } + logger.debug("Reset keepAlive. Remaining: {}", keepAlive.getDelay(TimeUnit.MILLISECONDS)); } else { - if (logger.logsDebug()) { - logger.debug("Reset keepAlive"); - } + logger.debug("Reset keepAlive"); } keepAlive = executorService.schedule(nop(), KEEP_ALIVE_TIMEOUT_MS, TimeUnit.MILLISECONDS); } @@ -253,18 +244,14 @@ private void closeAndNotify() { private void onClosed() { if (!isClosed) { - if (logger.logsDebug()) { - logger.debug("closing itself"); - } + logger.debug("closing itself"); closeAndNotify(); } } private void closeIfNeverConnected() { if (!everConnected && !isClosed) { - if (logger.logsDebug()) { - logger.debug("timed out on connect"); - } + logger.debug("timed out on connect"); closeAndNotify(); } } @@ -278,9 +265,7 @@ private class WSClientHandlerImpl implements WSClientEventHandler { @Override public void onOpen() { - if (logger.logsDebug()) { - logger.debug("websocket opened"); - } + logger.debug("websocket opened"); executorService.execute(new Runnable() { @Override public void run() { @@ -293,9 +278,7 @@ public void run() { @Override public void onMessage(final String message) { - if (logger.logsDebug()) { - logger.debug("ws message: " + message); - } + logger.debug("ws message: {}", message); executorService.execute(new Runnable() { @Override public void run() { @@ -306,9 +289,7 @@ public void run() { @Override public void onClose() { - if (logger.logsDebug()) { - logger.debug("closed"); - } + logger.debug("closed"); if (!isClosed) { // If the connection tear down was initiated by the higher-layer, isClosed will already // be true. Nothing more to do in that case. diff --git a/src/main/java/com/google/firebase/database/connection/util/RetryHelper.java b/src/main/java/com/google/firebase/database/connection/util/RetryHelper.java index 2563a30ca..f8f6bb77a 100644 --- a/src/main/java/com/google/firebase/database/connection/util/RetryHelper.java +++ b/src/main/java/com/google/firebase/database/connection/util/RetryHelper.java @@ -16,18 +16,18 @@ package com.google.firebase.database.connection.util; -import com.google.firebase.database.logging.LogWrapper; -import com.google.firebase.database.logging.Logger; - import java.util.Random; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RetryHelper { + private static final Logger logger = LoggerFactory.getLogger(RetryHelper.class); + private final ScheduledExecutorService executorService; - private final LogWrapper logger; /** The minimum delay for a retry in ms. */ private final long minRetryDelayAfterFailure; /** The maximum retry delay in ms. */ @@ -49,13 +49,11 @@ public class RetryHelper { private RetryHelper( ScheduledExecutorService executorService, - LogWrapper logger, long minRetryDelayAfterFailure, long maxRetryDelay, double retryExponent, double jitterFactor) { this.executorService = executorService; - this.logger = logger; this.minRetryDelayAfterFailure = minRetryDelayAfterFailure; this.maxRetryDelay = maxRetryDelay; this.retryExponent = retryExponent; @@ -84,7 +82,7 @@ public void retry(final Runnable runnable) { + (jitterFactor * currentRetryDelay * random.nextDouble())); } this.lastWasSuccess = false; - logger.debug("Scheduling retry in %dms", delay); + logger.debug("Scheduling retry in {}ms", delay); Runnable wrapped = new Runnable() { @Override @@ -119,15 +117,13 @@ public void cancel() { public static class Builder { private final ScheduledExecutorService service; - private final LogWrapper logger; private long minRetryDelayAfterFailure = 1000; private double jitterFactor = 0.5; private long retryMaxDelay = 30 * 1000; private double retryExponent = 1.3; - public Builder(ScheduledExecutorService service, Logger logger, Class tag) { + public Builder(ScheduledExecutorService service, Class tag) { this.service = service; - this.logger = new LogWrapper(logger, tag); } public Builder withMinDelayAfterFailure(long delay) { @@ -156,7 +152,6 @@ public Builder withJitterFactor(double random) { public RetryHelper build() { return new RetryHelper( this.service, - this.logger, this.minRetryDelayAfterFailure, this.retryMaxDelay, this.retryExponent, 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..af3e5cf64 100644 --- a/src/main/java/com/google/firebase/database/core/Context.java +++ b/src/main/java/com/google/firebase/database/core/Context.java @@ -26,8 +26,6 @@ import com.google.firebase.database.connection.PersistentConnection; import com.google.firebase.database.core.persistence.NoopPersistenceManager; import com.google.firebase.database.core.persistence.PersistenceManager; -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; @@ -37,7 +35,6 @@ public class Context { private static final long DEFAULT_CACHE_SIZE = 10 * 1024 * 1024; - protected Logger logger; protected FirebaseApp firebaseApp; EventTarget eventTarget; @@ -45,7 +42,6 @@ public class Context { RunLoop runLoop; String persistenceKey; List loggedComponents; - Logger.Level logLevel = Logger.Level.INFO; boolean persistenceEnabled; long cacheSize = DEFAULT_CACHE_SIZE; @@ -103,8 +99,6 @@ public void requireStarted() { } private void initServices() { - // Do the logger first, so that other components can get a LogWrapper - ensureLogger(); // Cache platform getPlatform(); ensureUserAgent(); @@ -137,21 +131,8 @@ void assertUnfrozen() { } } - public LogWrapper getLogger(String component) { - return new LogWrapper(logger, component, null); - } - - public LogWrapper getLogger(Class component) { - return new LogWrapper(logger, component); - } - - public LogWrapper getLogger(Class component, String prefix) { - return new LogWrapper(logger, component, prefix); - } - public ConnectionContext getConnectionContext() { return new ConnectionContext( - this.logger, wrapAuthTokenProvider(this.getAuthTokenProvider()), this.getExecutorService(), this.isPersistenceEnabled(), @@ -213,12 +194,6 @@ private ScheduledExecutorService getExecutorService() { return ((DefaultRunLoop) loop).getExecutorService(); } - private void ensureLogger() { - if (logger == null) { - logger = getPlatform().newLogger(this, logLevel, loggedComponents); - } - } - private void ensureRunLoop() { if (runLoop == null) { runLoop = platform.newRunLoop(this); diff --git a/src/main/java/com/google/firebase/database/core/DatabaseConfig.java b/src/main/java/com/google/firebase/database/core/DatabaseConfig.java index 491098d6a..844f59266 100644 --- a/src/main/java/com/google/firebase/database/core/DatabaseConfig.java +++ b/src/main/java/com/google/firebase/database/core/DatabaseConfig.java @@ -18,9 +18,6 @@ import com.google.firebase.FirebaseApp; import com.google.firebase.database.DatabaseException; -import com.google.firebase.database.Logger; - -import java.util.List; /** * TODO: Since this is no longer public, we should merge it with Context and clean all @@ -29,21 +26,6 @@ */ public class DatabaseConfig extends Context { - // TODO: Remove this from the public API since we currently can't pass logging - // across AIDL interface. - - /** - * If you would like to provide a custom log target, pass an object that implements the {@link - * com.google.firebase.database.Logger Logger} interface. - * - * @hide - * @param logger The custom logger that will be called with all log messages - */ - public synchronized void setLogger(com.google.firebase.database.logging.Logger logger) { - assertUnfrozen(); - this.logger = logger; - } - /** * In the default setup, the Firebase Database library will create a thread to handle all * callbacks. On Android, it will attempt to use the main debugComponents) { - assertUnfrozen(); - setLogLevel(Logger.Level.DEBUG); - loggedComponents = debugComponents; - } - public void setRunLoop(RunLoop runLoop) { this.runLoop = runLoop; } 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 2502b6b32..2a226c078 100644 --- a/src/main/java/com/google/firebase/database/core/JvmPlatform.java +++ b/src/main/java/com/google/firebase/database/core/JvmPlatform.java @@ -24,14 +24,12 @@ 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; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; class JvmPlatform implements Platform { @@ -43,11 +41,6 @@ class JvmPlatform implements Platform { this.firebaseApp = firebaseApp; } - @Override - public Logger newLogger(Context ctx, Logger.Level level, List components) { - return new DefaultLogger(level, components); - } - @Override public EventTarget newEventTarget(Context ctx) { ThreadFactory threadFactory = ImplFirebaseTrampolines.getThreadFactory(firebaseApp); @@ -56,7 +49,7 @@ public EventTarget newEventTarget(Context ctx) { @Override public RunLoop newRunLoop(final Context context) { - final LogWrapper logger = context.getLogger(RunLoop.class); + final Logger logger = LoggerFactory.getLogger(RunLoop.class); ThreadFactory threadFactory = ImplFirebaseTrampolines.getThreadFactory(firebaseApp); return new DefaultRunLoop(threadFactory) { @Override 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 320a851b5..2d2742a62 100644 --- a/src/main/java/com/google/firebase/database/core/Platform.java +++ b/src/main/java/com/google/firebase/database/core/Platform.java @@ -20,17 +20,13 @@ import com.google.firebase.database.connection.HostInfo; import com.google.firebase.database.connection.PersistentConnection; import com.google.firebase.database.core.persistence.PersistenceManager; -import com.google.firebase.database.logging.Logger; -import java.util.List; import java.util.concurrent.ScheduledExecutorService; public interface Platform { String DEVICE = "AdminJava"; - Logger newLogger(Context ctx, Logger.Level level, List components); - EventTarget newEventTarget(Context ctx); RunLoop newRunLoop(Context ctx); diff --git a/src/main/java/com/google/firebase/database/core/Repo.java b/src/main/java/com/google/firebase/database/core/Repo.java index add6ff3e0..0668506a9 100644 --- a/src/main/java/com/google/firebase/database/core/Repo.java +++ b/src/main/java/com/google/firebase/database/core/Repo.java @@ -38,7 +38,6 @@ import com.google.firebase.database.core.view.Event; import com.google.firebase.database.core.view.EventRaiser; import com.google.firebase.database.core.view.QuerySpec; -import com.google.firebase.database.logging.LogWrapper; import com.google.firebase.database.snapshot.ChildKey; import com.google.firebase.database.snapshot.EmptyNode; import com.google.firebase.database.snapshot.IndexedNode; @@ -54,6 +53,8 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Repo implements PersistentConnection.Delegate { @@ -67,14 +68,14 @@ public class Repo implements PersistentConnection.Delegate { private static final String TRANSACTION_TOO_MANY_RETRIES = "maxretries"; private static final String TRANSACTION_OVERRIDE_BY_SET = "overriddenBySet"; + + private static final Logger operationLogger = LoggerFactory.getLogger(Repo.class); + private final RepoInfo repoInfo; private final OffsetClock serverClock = new OffsetClock(new DefaultClock(), 0); private final PersistentConnection connection; private final EventRaiser eventRaiser; private final Context ctx; - private final LogWrapper operationLogger; - private final LogWrapper transactionLogger; - private final LogWrapper dataLogger; private SnapshotHolder infoData; private SparseSnapshotTree onDisconnect; private Tree> transactionQueueTree; @@ -90,11 +91,6 @@ public class Repo implements PersistentConnection.Delegate { this.repoInfo = repoInfo; this.ctx = ctx; this.database = database; - - operationLogger = this.ctx.getLogger(Repo.class); - transactionLogger = this.ctx.getLogger(Repo.class.getName() + ".Transaction"); - dataLogger = this.ctx.getLogger(Repo.class.getName() + ".DataOperation"); - this.eventRaiser = new EventRaiser(this.ctx); HostInfo hostInfo = new HostInfo(repoInfo.host, repoInfo.namespace, repoInfo.secure); @@ -150,7 +146,7 @@ public void onTokenChange(String token) { transactionQueueTree = new Tree<>(); - infoSyncTree = new SyncTree(ctx, new NoopPersistenceManager(), + infoSyncTree = new SyncTree(new NoopPersistenceManager(), new SyncTree.ListenProvider() { @Override public void startListening( @@ -178,7 +174,7 @@ public void run() { public void stopListening(QuerySpec query, Tag tag) {} }); - serverSyncTree = new SyncTree(ctx, persistenceManager, + serverSyncTree = new SyncTree(persistenceManager, new SyncTree.ListenProvider() { @Override public void startListening( @@ -263,12 +259,8 @@ boolean hasListeners() { public void onDataUpdate( List pathSegments, Object message, boolean isMerge, Long optTag) { Path path = new Path(pathSegments); - if (operationLogger.logsDebug()) { - operationLogger.debug("onDataUpdate: " + path); - } - if (dataLogger.logsDebug()) { - operationLogger.debug("onDataUpdate: " + path + " " + message); - } + operationLogger.debug("onDataUpdate: {} {}", path, message); + List events; try { @@ -316,12 +308,7 @@ public void onRangeMergeUpdate( List merges, Long tagNumber) { Path path = new Path(pathSegments); - if (operationLogger.logsDebug()) { - operationLogger.debug("onRangeMergeUpdate: " + path); - } - if (dataLogger.logsDebug()) { - operationLogger.debug("onRangeMergeUpdate: " + path + " " + merges); - } + operationLogger.debug("onRangeMergeUpdate: {} {}", path, merges); List parsedMerges = new ArrayList<>(merges.size()); for (com.google.firebase.database.connection.RangeMerge merge : merges) { @@ -383,12 +370,7 @@ public void setValue( final Path path, Node newValueUnresolved, final DatabaseReference.CompletionListener onComplete) { - if (operationLogger.logsDebug()) { - operationLogger.debug("set: " + path); - } - if (dataLogger.logsDebug()) { - dataLogger.debug("set: " + path + " " + newValueUnresolved); - } + operationLogger.debug("set: {} {}", path, newValueUnresolved); Map serverValues = ServerValues.generateServerValues(serverClock); Node newValue = ServerValues.resolveDeferredValueSnapshot(newValueUnresolved, serverValues); @@ -421,16 +403,9 @@ public void updateChildren( CompoundWrite updates, final DatabaseReference.CompletionListener onComplete, Map unParsedUpdates) { - if (operationLogger.logsDebug()) { - operationLogger.debug("update: " + path); - } - if (dataLogger.logsDebug()) { - dataLogger.debug("update: " + path + " " + unParsedUpdates); - } + operationLogger.debug("update: {} {}", path, unParsedUpdates); if (updates.isEmpty()) { - if (operationLogger.logsDebug()) { - operationLogger.debug("update called with no changes. No-op"); - } + operationLogger.debug("update called with no changes. No-op"); // dispatch on complete callOnComplete(onComplete, null, path); return; @@ -468,9 +443,7 @@ public void onRequestResult(String optErrorCode, String optErrorMessage) { } public void purgeOutstandingWrites() { - if (operationLogger.logsDebug()) { - operationLogger.debug("Purging writes"); - } + operationLogger.debug("Purging writes"); List events = serverSyncTree.removeAllWrites(); postEvents(events); // Abort any transactions @@ -657,16 +630,11 @@ private void warnIfWriteFailed(String writeType, Path path, DatabaseError error) } public void startTransaction(Path path, final Transaction.Handler handler, boolean applyLocally) { - if (operationLogger.logsDebug()) { - operationLogger.debug("transaction: " + path); - } - if (dataLogger.logsDebug()) { - operationLogger.debug("transaction: " + path); - } + operationLogger.debug("transaction: {}", path); if (this.ctx.isPersistenceEnabled() && !loggedTransactionPersistenceWarning) { loggedTransactionPersistenceWarning = true; - transactionLogger.info( + operationLogger.info( "runTransaction() usage detected while persistence is enabled. Please be aware that " + "transactions *will not* be persisted across database restarts. See " + "https://www.firebase.com/docs/android/guide/offline-capabilities.html" @@ -1142,11 +1110,7 @@ public void visitTree(Tree> tree) { private Path abortTransactions(Path path, final int reason) { Path affectedPath = getAncestorTransactionNode(path).getPath(); - - if (transactionLogger.logsDebug()) { - operationLogger.debug( - "Aborting transactions for path: " + path + ". Affected: " + affectedPath); - } + operationLogger.debug("Aborting transactions for path: {}. Affected: {}", path, affectedPath); Tree> transactionNode = transactionQueueTree.subTree(path); transactionNode.forEachAncestor( diff --git a/src/main/java/com/google/firebase/database/core/SyncTree.java b/src/main/java/com/google/firebase/database/core/SyncTree.java index 17fcad445..f7da11750 100644 --- a/src/main/java/com/google/firebase/database/core/SyncTree.java +++ b/src/main/java/com/google/firebase/database/core/SyncTree.java @@ -37,7 +37,6 @@ import com.google.firebase.database.core.view.Event; import com.google.firebase.database.core.view.QuerySpec; import com.google.firebase.database.core.view.View; -import com.google.firebase.database.logging.LogWrapper; import com.google.firebase.database.snapshot.ChildKey; import com.google.firebase.database.snapshot.CompoundHash; import com.google.firebase.database.snapshot.EmptyNode; @@ -57,6 +56,8 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * SyncTree is the central class for managing event callback registration, data caching, views @@ -80,6 +81,9 @@ public class SyncTree { // Size after which we start including the compound hash private static final long SIZE_THRESHOLD_FOR_COMPOUND_HASH = 1024; + + private static final Logger logger = LoggerFactory.getLogger(SyncTree.class); + /** * A tree of all pending user writes (user-initiated set()'s, transaction()'s, update()'s, etc.). */ @@ -90,14 +94,13 @@ public class SyncTree { private final Set keepSyncedQueries; private final ListenProvider listenProvider; private final PersistenceManager persistenceManager; - private final LogWrapper logger; /** Tree of SyncPoints. There's a SyncPoint at any location that has 1 or more views. */ private ImmutableTree syncPointTree; /** Static tracker for next query tag. */ private long nextQueryTag = 1L; public SyncTree( - Context context, PersistenceManager persistenceManager, ListenProvider listenProvider) { + PersistenceManager persistenceManager, ListenProvider listenProvider) { this.syncPointTree = ImmutableTree.emptyInstance(); this.pendingWriteTree = new WriteTree(); this.tagToQueryMap = new HashMap<>(); @@ -105,7 +108,6 @@ public SyncTree( this.keepSyncedQueries = new HashSet<>(); this.listenProvider = listenProvider; this.persistenceManager = persistenceManager; - this.logger = context.getLogger(SyncTree.class); } public boolean isEmpty() { @@ -968,7 +970,7 @@ public List onListenComplete(DatabaseError error) { return SyncTree.this.applyListenComplete(query.getPath()); } } else { - logger.warn("Listen at " + view.getQuery().getPath() + " failed: " + error.toString()); + logger.warn("Listen at {} failed: {}", view.getQuery().getPath(), error); // If a listen failed, kill all of the listeners here, not just the one that triggered the // error. Note that this may need to be scoped to just this listener if we change diff --git a/src/main/java/com/google/firebase/database/core/persistence/DefaultPersistenceManager.java b/src/main/java/com/google/firebase/database/core/persistence/DefaultPersistenceManager.java index 1bc546295..40e1b99dc 100644 --- a/src/main/java/com/google/firebase/database/core/persistence/DefaultPersistenceManager.java +++ b/src/main/java/com/google/firebase/database/core/persistence/DefaultPersistenceManager.java @@ -17,12 +17,10 @@ package com.google.firebase.database.core.persistence; import com.google.firebase.database.core.CompoundWrite; -import com.google.firebase.database.core.Context; import com.google.firebase.database.core.Path; import com.google.firebase.database.core.UserWriteRecord; import com.google.firebase.database.core.view.CacheNode; import com.google.firebase.database.core.view.QuerySpec; -import com.google.firebase.database.logging.LogWrapper; import com.google.firebase.database.snapshot.ChildKey; import com.google.firebase.database.snapshot.EmptyNode; import com.google.firebase.database.snapshot.IndexedNode; @@ -34,24 +32,26 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class DefaultPersistenceManager implements PersistenceManager { + private static final Logger logger = LoggerFactory.getLogger(PersistenceManager.class); + private final PersistenceStorageEngine storageLayer; private final TrackedQueryManager trackedQueryManager; - private final LogWrapper logger; private final CachePolicy cachePolicy; private long serverCacheUpdatesSinceLastPruneCheck = 0; public DefaultPersistenceManager( - Context ctx, PersistenceStorageEngine engine, CachePolicy cachePolicy) { - this(ctx, engine, cachePolicy, new DefaultClock()); + PersistenceStorageEngine engine, CachePolicy cachePolicy) { + this(engine, cachePolicy, new DefaultClock()); } public DefaultPersistenceManager( - Context ctx, PersistenceStorageEngine engine, CachePolicy cachePolicy, Clock clock) { + PersistenceStorageEngine engine, CachePolicy cachePolicy, Clock clock) { this.storageLayer = engine; - this.logger = ctx.getLogger(PersistenceManager.class); this.trackedQueryManager = new TrackedQueryManager(storageLayer, logger, clock); this.cachePolicy = cachePolicy; } @@ -253,15 +253,11 @@ public T runInTransaction(Callable callable) { private void doPruneCheckAfterServerUpdate() { serverCacheUpdatesSinceLastPruneCheck++; if (cachePolicy.shouldCheckCacheSize(serverCacheUpdatesSinceLastPruneCheck)) { - if (logger.logsDebug()) { - logger.debug("Reached prune check threshold."); - } + logger.debug("Reached prune check threshold."); serverCacheUpdatesSinceLastPruneCheck = 0; boolean canPrune = true; long cacheSize = storageLayer.serverCacheEstimatedSizeInBytes(); - if (logger.logsDebug()) { - logger.debug("Cache size: " + cacheSize); - } + logger.debug("Cache size: {}", cacheSize); while (canPrune && cachePolicy.shouldPrune(cacheSize, trackedQueryManager.countOfPrunableQueries())) { PruneForest pruneForest = this.trackedQueryManager.pruneOldQueries(cachePolicy); @@ -271,9 +267,7 @@ private void doPruneCheckAfterServerUpdate() { canPrune = false; } cacheSize = storageLayer.serverCacheEstimatedSizeInBytes(); - if (logger.logsDebug()) { - logger.debug("Cache size after prune: " + cacheSize); - } + logger.debug("Cache size after prune: {}", cacheSize); } } } diff --git a/src/main/java/com/google/firebase/database/core/persistence/TrackedQueryManager.java b/src/main/java/com/google/firebase/database/core/persistence/TrackedQueryManager.java index b4f6b545c..20278b947 100644 --- a/src/main/java/com/google/firebase/database/core/persistence/TrackedQueryManager.java +++ b/src/main/java/com/google/firebase/database/core/persistence/TrackedQueryManager.java @@ -23,7 +23,6 @@ import com.google.firebase.database.core.utilities.Predicate; import com.google.firebase.database.core.view.QueryParams; import com.google.firebase.database.core.view.QuerySpec; -import com.google.firebase.database.logging.LogWrapper; import com.google.firebase.database.snapshot.ChildKey; import com.google.firebase.database.utilities.Clock; import com.google.firebase.database.utilities.Utilities; @@ -36,6 +35,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.slf4j.Logger; public class TrackedQueryManager { @@ -74,7 +74,7 @@ public boolean evaluate(TrackedQuery query) { }; // DB, where we permanently store tracked queries. private final PersistenceStorageEngine storageLayer; - private final LogWrapper logger; + private final Logger logger; private final Clock clock; // In-memory cache of tracked queries. Should always be in-sync with the DB. private ImmutableTree> trackedQueryTree; @@ -82,7 +82,7 @@ public boolean evaluate(TrackedQuery query) { private long currentQueryId = 0; public TrackedQueryManager( - PersistenceStorageEngine storageLayer, LogWrapper logger, Clock clock) { + PersistenceStorageEngine storageLayer, Logger logger, Clock clock) { this.storageLayer = storageLayer; this.logger = logger; this.clock = clock; @@ -226,13 +226,8 @@ public PruneForest pruneOldQueries(CachePolicy cachePolicy) { long countToPrune = calculateCountToPrune(cachePolicy, prunable.size()); PruneForest forest = new PruneForest(); - if (logger.logsDebug()) { - logger.debug( - "Pruning old queries. Prunable: " - + prunable.size() - + " Count to prune: " - + countToPrune); - } + logger.debug( + "Pruning old queries. Prunable: {} Count to prune: {}", prunable.size(), countToPrune); Collections.sort( prunable, @@ -257,9 +252,7 @@ public int compare(TrackedQuery q1, TrackedQuery q2) { // Also keep the unprunable queries. List unprunable = getQueriesMatching(IS_QUERY_UNPRUNABLE_PREDICATE); - if (logger.logsDebug()) { - logger.debug("Unprunable queries: " + unprunable.size()); - } + logger.debug("Unprunable queries: {}", unprunable.size()); for (TrackedQuery toKeep : unprunable) { forest = forest.keep(toKeep.querySpec.getPath()); } diff --git a/src/main/java/com/google/firebase/database/core/view/EventRaiser.java b/src/main/java/com/google/firebase/database/core/view/EventRaiser.java index 47579abd8..d2bbacee6 100644 --- a/src/main/java/com/google/firebase/database/core/view/EventRaiser.java +++ b/src/main/java/com/google/firebase/database/core/view/EventRaiser.java @@ -18,10 +18,11 @@ import com.google.firebase.database.core.Context; import com.google.firebase.database.core.EventTarget; -import com.google.firebase.database.logging.LogWrapper; import java.util.ArrayList; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Each view owns an instance of this class, and it is used to send events to the event target @@ -33,18 +34,16 @@ */ public class EventRaiser { + private static final Logger logger = LoggerFactory.getLogger(EventRaiser.class); + private final EventTarget eventTarget; - private final LogWrapper logger; public EventRaiser(Context ctx) { eventTarget = ctx.getEventTarget(); - logger = ctx.getLogger(EventRaiser.class); } public void raiseEvents(final List events) { - if (logger.logsDebug()) { - logger.debug("Raising " + events.size() + " event(s)"); - } + logger.debug("Raising {} event(s)", events.size()); // TODO: Use an immutable data structure for events so we don't have to clone to be safe. final ArrayList eventsClone = new ArrayList<>(events); eventTarget.postEvent( @@ -52,9 +51,7 @@ public void raiseEvents(final List events) { @Override public void run() { for (Event event : eventsClone) { - if (logger.logsDebug()) { - logger.debug("Raising " + event.toString()); - } + logger.debug("Raising {}", event); event.fire(); } } diff --git a/src/main/java/com/google/firebase/database/logging/DefaultLogger.java b/src/main/java/com/google/firebase/database/logging/DefaultLogger.java deleted file mode 100644 index d9ba110c7..000000000 --- a/src/main/java/com/google/firebase/database/logging/DefaultLogger.java +++ /dev/null @@ -1,93 +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.logging; - -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class DefaultLogger implements Logger { - - private final Set enabledComponents; - private final Level minLevel; - - public DefaultLogger(Level level, List enabledComponents) { - if (enabledComponents != null) { - this.enabledComponents = new HashSet<>(enabledComponents); - } else { - this.enabledComponents = null; - } - minLevel = level; - } - - @Override - public Level getLogLevel() { - return this.minLevel; - } - - @Override - public void onLogMessage(Level level, String tag, String message, long msTimestamp) { - if (shouldLog(level, tag)) { - String toLog = buildLogMessage(level, tag, message, msTimestamp); - switch (level) { - case ERROR: - error(tag, toLog); - break; - case WARN: - warn(tag, toLog); - break; - case INFO: - info(tag, toLog); - break; - case DEBUG: - debug(tag, toLog); - break; - default: - throw new RuntimeException("Should not reach here!"); - } - } - } - - protected String buildLogMessage(Level level, String tag, String message, long msTimestamp) { - Date now = new Date(msTimestamp); - return now.toString() + " " + "[" + level + "] " + tag + ": " + message; - } - - protected void error(String tag, String toLog) { - System.err.println(toLog); - } - - protected void warn(String tag, String toLog) { - System.out.println(toLog); - } - - protected void info(String tag, String toLog) { - System.out.println(toLog); - } - - protected void debug(String tag, String toLog) { - System.out.println(toLog); - } - - protected boolean shouldLog(Level level, String tag) { - return (level.ordinal() >= minLevel.ordinal() - && (enabledComponents == null - || level.ordinal() > Level.DEBUG.ordinal() - || enabledComponents.contains(tag))); - } -} diff --git a/src/main/java/com/google/firebase/database/logging/LogWrapper.java b/src/main/java/com/google/firebase/database/logging/LogWrapper.java deleted file mode 100644 index 21e28e526..000000000 --- a/src/main/java/com/google/firebase/database/logging/LogWrapper.java +++ /dev/null @@ -1,127 +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.logging; - -import java.io.PrintWriter; -import java.io.StringWriter; -import org.slf4j.LoggerFactory; - -/** - * Legacy logging interface for database implementation. This class attempts to reconcile SLF4J - * with the old logging implementation of the Admin SDK. When SLF4J is available, logs using that - * API. Otherwise falls back to the old logging implementation. This prevents individual log - * statements from being written to both log APIs. - * - * @deprecated This class will be removed in a future release, and SLF4J will be used universally - * throughout the codebase. - */ -public class LogWrapper { - - private final org.slf4j.Logger slf4jLogger; - private final Logger logger; - private final String component; - private final String prefix; - - public LogWrapper(Logger logger, Class component) { - this(logger, component, null); - } - - public LogWrapper(Logger logger, Class component, String prefix) { - this.slf4jLogger = LoggerFactory.getLogger(component); - this.logger = logger; - this.component = component.getName(); - this.prefix = prefix; - } - - public LogWrapper(Logger logger, String component, String prefix) { - this.slf4jLogger = LoggerFactory.getLogger(component); - this.logger = logger; - this.component = component; - this.prefix = prefix; - } - - private static String exceptionStacktrace(Throwable e) { - StringWriter writer = new StringWriter(); - PrintWriter printWriter = new PrintWriter(writer); - e.printStackTrace(printWriter); - return writer.toString(); - } - - public void error(String message, Throwable e) { - if (slf4jLogger.isErrorEnabled()) { - slf4jLogger.error(toLog(message), e); - } else { - String logMsg = toLog(message) + "\n" + exceptionStacktrace(e); - logger.onLogMessage(Logger.Level.ERROR, component, logMsg, now()); - } - } - - public void warn(String message) { - warn(message, null); - } - - public void warn(String message, Throwable e) { - if (slf4jLogger.isWarnEnabled()) { - slf4jLogger.warn(toLog(message), e); - } else { - String logMsg = toLog(message); - if (e != null) { - logMsg = logMsg + "\n" + exceptionStacktrace(e); - } - logger.onLogMessage(Logger.Level.WARN, component, logMsg, now()); - } - } - - public void info(String message) { - if (slf4jLogger.isInfoEnabled()) { - slf4jLogger.info(toLog(message)); - } else { - logger.onLogMessage(Logger.Level.INFO, component, toLog(message), now()); - } - } - - public void debug(String message, Object... args) { - this.debug(message, null, args); - } - - /** Log a non-fatal exception. Typically something like an IO error on a failed connection */ - public void debug(String message, Throwable e, Object... args) { - if (slf4jLogger.isDebugEnabled()) { - slf4jLogger.debug(toLog(message, args), e); - } else { - String logMsg = toLog(message, args); - if (e != null) { - logMsg = logMsg + "\n" + exceptionStacktrace(e); - } - logger.onLogMessage(Logger.Level.DEBUG, component, logMsg, now()); - } - } - - public boolean logsDebug() { - return this.logger.getLogLevel().ordinal() <= Logger.Level.DEBUG.ordinal() - || slf4jLogger.isDebugEnabled(); - } - - private long now() { - return System.currentTimeMillis(); - } - - private String toLog(String message, Object... args) { - String formatted = (args.length > 0) ? String.format(message, args) : message; - return prefix == null ? formatted : prefix + " - " + formatted; - } -} diff --git a/src/main/java/com/google/firebase/database/logging/Logger.java b/src/main/java/com/google/firebase/database/logging/Logger.java deleted file mode 100644 index 7fd2e6e0e..000000000 --- a/src/main/java/com/google/firebase/database/logging/Logger.java +++ /dev/null @@ -1,47 +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.logging; - -/** - * Private (internal) logging interface used by Firebase Database. See {@link - * com.google.firebase.database.core.DatabaseConfig DatabaseConfig} for more information. - * - * @deprecated Use SLF4J-based logging - */ -public interface Logger { - - /** - * This method will be triggered whenever the library has something to log - * - * @param level The level of the log message - * @param tag The component that this log message is coming from - * @param message The message to be logged - * @param msTimestamp The timestamp, in milliseconds, at which this message was generated - */ - void onLogMessage(Level level, String tag, String message, long msTimestamp); - - Level getLogLevel(); - - /** The log levels used by the Firebase Database library */ - enum Level { - DEBUG, - INFO, - WARN, - ERROR, - NONE - } -} diff --git a/src/main/java/com/google/firebase/database/logging/package-info.java b/src/main/java/com/google/firebase/database/logging/package-info.java deleted file mode 100644 index 37bc5cf37..000000000 --- a/src/main/java/com/google/firebase/database/logging/package-info.java +++ /dev/null @@ -1,20 +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. - */ - -/** - * @hide - */ -package com.google.firebase.database.logging; diff --git a/src/test/java/com/google/firebase/Main.java b/src/test/java/com/google/firebase/Main.java new file mode 100644 index 000000000..684ad7e2f --- /dev/null +++ b/src/test/java/com/google/firebase/Main.java @@ -0,0 +1,50 @@ +/* + * 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; + +import com.google.firebase.database.DataSnapshot; +import com.google.firebase.database.DatabaseError; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; +import com.google.firebase.database.ValueEventListener; +import com.google.firebase.testing.IntegrationTestUtils; + +public class Main { + + public static void main(String[] args) throws Exception { + IntegrationTestUtils.ensureDefaultApp(); + DatabaseReference foo = FirebaseDatabase.getInstance().getReference("foo"); + foo.addValueEventListener( + new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot snapshot) { + System.out.println(snapshot.getValue()); + } + + @Override + public void onCancelled(DatabaseError error) { + System.out.println(error); + } + }); + for (int i = 0; i < 10; i++) { + Thread.sleep(1000); + foo.setValueAsync(System.currentTimeMillis()).get(); + } + Thread.sleep(60000); + } + +} diff --git a/src/test/java/com/google/firebase/database/TestHelpers.java b/src/test/java/com/google/firebase/database/TestHelpers.java index da05f8adf..6f077b8b2 100644 --- a/src/test/java/com/google/firebase/database/TestHelpers.java +++ b/src/test/java/com/google/firebase/database/TestHelpers.java @@ -20,7 +20,6 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import com.google.common.collect.ImmutableList; import com.google.firebase.FirebaseApp; import com.google.firebase.database.connection.ConnectionAuthTokenProvider; import com.google.firebase.database.connection.ConnectionContext; @@ -31,8 +30,6 @@ import com.google.firebase.database.core.RepoManager; import com.google.firebase.database.core.view.QuerySpec; import com.google.firebase.database.future.WriteFuture; -import com.google.firebase.database.logging.DefaultLogger; -import com.google.firebase.database.logging.Logger.Level; import com.google.firebase.database.snapshot.ChildKey; import com.google.firebase.database.util.JsonMapper; import com.google.firebase.database.utilities.DefaultRunLoop; @@ -67,7 +64,6 @@ public static DatabaseConfig newFrozenTestConfig(FirebaseApp app) { public static DatabaseConfig newTestConfig(FirebaseApp app) { DatabaseConfig config = new DatabaseConfig(); - config.setLogLevel(Logger.Level.WARN); config.setFirebaseApp(app); return config; } @@ -93,12 +89,6 @@ public static ScheduledExecutorService getExecutorService(DatabaseConfig config) DefaultRunLoop runLoop = (DefaultRunLoop) config.getRunLoop(); return runLoop.getExecutorService(); } - - public static void setLogger( - DatabaseConfig ctx, com.google.firebase.database.logging.Logger logger) { - ctx.setLogger(logger); - } - public static void waitFor(Semaphore semaphore) throws InterruptedException { waitFor(semaphore, 1); } @@ -323,15 +313,13 @@ public Object answer(InvocationOnMock invocation) throws Throwable { } public static ConnectionContext newConnectionContext(ScheduledExecutorService executor) { - com.google.firebase.database.logging.Logger logger = new DefaultLogger( - Level.NONE, ImmutableList.of()); ConnectionAuthTokenProvider tokenProvider = new ConnectionAuthTokenProvider() { @Override public void getToken(boolean forceRefresh, GetTokenCallback callback) { callback.onSuccess("gauth|{\"token\":\"test-token\"}"); } }; - return new ConnectionContext(logger, tokenProvider, executor, false, "testVersion", + return new ConnectionContext(tokenProvider, executor, false, "testVersion", "testUserAgent", Executors.defaultThreadFactory()); } diff --git a/src/test/java/com/google/firebase/database/connection/ConnectionTest.java b/src/test/java/com/google/firebase/database/connection/ConnectionTest.java index 56ff9a95a..4e413fb48 100644 --- a/src/test/java/com/google/firebase/database/connection/ConnectionTest.java +++ b/src/test/java/com/google/firebase/database/connection/ConnectionTest.java @@ -17,7 +17,6 @@ package com.google.firebase.database.connection; import com.google.common.collect.ImmutableMap; -import com.google.firebase.database.TestHelpers; import com.google.firebase.database.connection.Connection.Delegate; import com.google.firebase.database.connection.Connection.DisconnectReason; import com.google.firebase.database.connection.Connection.WebsocketConnectionFactory; @@ -187,7 +186,6 @@ private static class MockConnectionFactory implements WebsocketConnectionFactory MockConnectionFactory() { this.connection = new Connection( - TestHelpers.newConnectionContext(executor), Mockito.mock(HostInfo.class), delegate, this); diff --git a/src/test/java/com/google/firebase/database/core/SyncPointTest.java b/src/test/java/com/google/firebase/database/core/SyncPointTest.java index 4d70fd06b..9b9e47207 100644 --- a/src/test/java/com/google/firebase/database/core/SyncPointTest.java +++ b/src/test/java/com/google/firebase/database/core/SyncPointTest.java @@ -28,7 +28,6 @@ import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.InternalHelpers; import com.google.firebase.database.Query; -import com.google.firebase.database.TestHelpers; import com.google.firebase.database.annotations.NotNull; import com.google.firebase.database.connection.ListenHashProvider; import com.google.firebase.database.core.persistence.NoopPersistenceManager; @@ -37,9 +36,6 @@ import com.google.firebase.database.core.view.DataEvent; import com.google.firebase.database.core.view.Event; import com.google.firebase.database.core.view.QuerySpec; -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.snapshot.IndexedNode; import com.google.firebase.database.snapshot.Node; import com.google.firebase.database.snapshot.NodeUtilities; @@ -60,9 +56,13 @@ import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class SyncPointTest { + private static final Logger logger = LoggerFactory.getLogger(SyncPointTest.class); + private static FirebaseApp testApp; @BeforeClass @@ -79,7 +79,7 @@ public static void tearDownClass() { TestOnlyImplFirebaseTrampolines.clearInstancesForTest(); } - private static SyncTree.ListenProvider getNewListenProvider(final LogWrapper logger) { + private static SyncTree.ListenProvider getNewListenProvider() { return new SyncTree.ListenProvider() { private final HashSet listens = new HashSet<>(); @@ -314,12 +314,9 @@ private static Map parseMergePaths(Map merges) { @SuppressWarnings("unchecked") private static void runTest(Map testSpec, String basePath) { - DatabaseConfig config = TestHelpers.newTestConfig(testApp); - TestHelpers.setLogger(config, new DefaultLogger(Logger.Level.WARN, null)); - LogWrapper logger = config.getLogger("SyncPointTest"); logger.info("Running \"" + testSpec.get("name") + '"'); - SyncTree.ListenProvider listenProvider = getNewListenProvider(logger); - SyncTree syncTree = new SyncTree(config, new NoopPersistenceManager(), listenProvider); + SyncTree.ListenProvider listenProvider = getNewListenProvider(); + SyncTree syncTree = new SyncTree(new NoopPersistenceManager(), listenProvider); int currentWriteId = 0; diff --git a/src/test/java/com/google/firebase/database/core/persistence/DefaultPersistenceManagerTest.java b/src/test/java/com/google/firebase/database/core/persistence/DefaultPersistenceManagerTest.java index b06cfb841..99450944c 100644 --- a/src/test/java/com/google/firebase/database/core/persistence/DefaultPersistenceManagerTest.java +++ b/src/test/java/com/google/firebase/database/core/persistence/DefaultPersistenceManagerTest.java @@ -70,7 +70,7 @@ public static void tearDownClass() { private PersistenceManager newTestPersistenceManager() { MockPersistenceStorageEngine engine = new MockPersistenceStorageEngine(); engine.disableTransactionCheck = true; - return new DefaultPersistenceManager(newFrozenTestConfig(testApp), engine, CachePolicy.NONE); + return new DefaultPersistenceManager(engine, CachePolicy.NONE); } @Test @@ -131,7 +131,7 @@ public void applyUserMergeUsesRelativePath() { engine.overwriteServerCache(path(""), initialData); DefaultPersistenceManager manager = - new DefaultPersistenceManager(newFrozenTestConfig(testApp), engine, CachePolicy.NONE); + new DefaultPersistenceManager(engine, CachePolicy.NONE); CompoundWrite write = CompoundWrite.fromValue(fromSingleQuotedString("{'baz': 'new-baz', 'qux': 'qux'}")); diff --git a/src/test/java/com/google/firebase/database/core/persistence/RandomPersistenceTest.java b/src/test/java/com/google/firebase/database/core/persistence/RandomPersistenceTest.java index ce9a641db..c4f3cd371 100644 --- a/src/test/java/com/google/firebase/database/core/persistence/RandomPersistenceTest.java +++ b/src/test/java/com/google/firebase/database/core/persistence/RandomPersistenceTest.java @@ -181,10 +181,10 @@ public void randomOperations() { DatabaseConfig cfg = TestHelpers.newFrozenTestConfig(testApp); MockPersistenceStorageEngine storageEngine = new MockPersistenceStorageEngine(); DefaultPersistenceManager manager = - new DefaultPersistenceManager(cfg, storageEngine, CachePolicy.NONE); + new DefaultPersistenceManager(storageEngine, CachePolicy.NONE); final HashMap tagMap = new HashMap<>(); SyncTree syncTree = - new SyncTree(cfg, manager, new SyncTree.ListenProvider() { + new SyncTree( manager, new SyncTree.ListenProvider() { @Override public void startListening( QuerySpec query, diff --git a/src/test/java/com/google/firebase/database/core/persistence/TrackedQueryManagerTest.java b/src/test/java/com/google/firebase/database/core/persistence/TrackedQueryManagerTest.java index 66b5a515f..8b293cedf 100644 --- a/src/test/java/com/google/firebase/database/core/persistence/TrackedQueryManagerTest.java +++ b/src/test/java/com/google/firebase/database/core/persistence/TrackedQueryManagerTest.java @@ -31,14 +31,12 @@ import com.google.firebase.database.core.utilities.TestClock; import com.google.firebase.database.core.view.QueryParams; import com.google.firebase.database.core.view.QuerySpec; -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.snapshot.ChildKey; import com.google.firebase.database.snapshot.PathIndex; import com.google.firebase.database.utilities.Clock; import java.util.Collections; import org.junit.Test; +import org.slf4j.LoggerFactory; public class TrackedQueryManagerTest { @@ -66,9 +64,8 @@ private TrackedQueryManager newManager(PersistenceStorageEngine engine, Clock cl e.disableTransactionCheck = true; engine = e; } - LogWrapper logWrapper = - new LogWrapper(new DefaultLogger(Logger.Level.WARN, null), TrackedQueryManagerTest.class); - return new TrackedQueryManager(engine, logWrapper, clock); + return new TrackedQueryManager( + engine, LoggerFactory.getLogger(TrackedQueryManagerTest.class), clock); } @Test diff --git a/src/test/java/com/google/firebase/database/integration/ShutdownExample.java b/src/test/java/com/google/firebase/database/integration/ShutdownExample.java index 68b6ac384..020e0416e 100644 --- a/src/test/java/com/google/firebase/database/integration/ShutdownExample.java +++ b/src/test/java/com/google/firebase/database/integration/ShutdownExample.java @@ -22,7 +22,6 @@ import com.google.firebase.database.DatabaseError; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; -import com.google.firebase.database.Logger.Level; import com.google.firebase.database.ValueEventListener; import java.util.concurrent.Semaphore; @@ -38,7 +37,6 @@ public static void main(String[] args) { .build()); FirebaseDatabase db = FirebaseDatabase.getInstance(app); - db.setLogLevel(Level.DEBUG); DatabaseReference ref = db.getReference(); ValueEventListener listener = From a91dad7a76f44e97b92ddd20b7e41735e9eb182b Mon Sep 17 00:00:00 2001 From: hiranya911 Date: Thu, 15 Mar 2018 17:24:14 -0700 Subject: [PATCH 09/12] Removing PrefixedLogger --- .../database/connection/Connection.java | 47 +++---- .../connection/PersistentConnectionImpl.java | 115 +++++++++--------- .../database/connection/PrefixedLogger.java | 58 --------- .../connection/WebsocketConnection.java | 37 +++--- src/test/java/com/google/firebase/Main.java | 50 -------- .../google/firebase/database/TestHelpers.java | 1 + .../firebase/database/core/SyncPointTest.java | 4 +- .../persistence/RandomPersistenceTest.java | 2 +- 8 files changed, 107 insertions(+), 207 deletions(-) delete mode 100644 src/main/java/com/google/firebase/database/connection/PrefixedLogger.java delete mode 100644 src/test/java/com/google/firebase/Main.java diff --git a/src/main/java/com/google/firebase/database/connection/Connection.java b/src/main/java/com/google/firebase/database/connection/Connection.java index 436ef8c6a..83e377304 100644 --- a/src/main/java/com/google/firebase/database/connection/Connection.java +++ b/src/main/java/com/google/firebase/database/connection/Connection.java @@ -20,6 +20,7 @@ import java.util.HashMap; import java.util.Map; +import org.slf4j.Logger; import org.slf4j.LoggerFactory; class Connection implements WebsocketConnection.Delegate { @@ -39,11 +40,14 @@ class Connection implements WebsocketConnection.Delegate { private static final String SERVER_HELLO_TIMESTAMP = "ts"; private static final String SERVER_HELLO_HOST = "h"; private static final String SERVER_HELLO_SESSION_ID = "s"; + + private static final Logger logger = LoggerFactory.getLogger(Connection.class); + private static long connectionIds = 0; - private final PrefixedLogger logger; private final HostInfo hostInfo; private final Delegate delegate; + private final String label; private WebsocketConnection conn; private State state; @@ -66,20 +70,19 @@ class Connection implements WebsocketConnection.Delegate { long connId = connectionIds++; this.hostInfo = hostInfo; this.delegate = delegate; - this.logger = new PrefixedLogger(LoggerFactory.getLogger(Connection.class), - "[conn_" + connId + "]"); + this.label = "[conn_" + connId + "]"; this.state = State.REALTIME_CONNECTING; this.conn = connFactory.newConnection(this); } public void open() { - logger.debug("Opening a connection"); + logger.debug("{} Opening a connection", label); conn.open(); } public void close(DisconnectReason reason) { if (state != State.REALTIME_DISCONNECTED) { - logger.debug("closing realtime connection"); + logger.debug("{} Closing realtime connection", label); state = State.REALTIME_DISCONNECTED; if (conn != null) { @@ -119,14 +122,14 @@ public void onMessage(Map message) { Map data = (Map) message.get(SERVER_ENVELOPE_DATA); onControlMessage(data); } else { - logger.debug("Ignoring unknown server message type: {}", messageType); + logger.debug("{} Ignoring unknown server message type: {}", label, messageType); } } else { - logger.debug("Failed to parse server message: missing message type: {}", message); + logger.debug("{} Failed to parse server message: missing message type: {}", label, message); close(); } } catch (ClassCastException e) { - logger.debug("Failed to parse server message", e); + logger.debug("{} Failed to parse server message", label, e); close(); } } @@ -135,22 +138,22 @@ public void onMessage(Map message) { public void onDisconnect(boolean wasEverConnected) { conn = null; if (!wasEverConnected && state == State.REALTIME_CONNECTING) { - logger.debug("Realtime connection failed"); + logger.debug("{} Realtime connection failed", label); } else { - logger.debug("Realtime connection lost"); + logger.debug("{} Realtime connection lost", label); } close(); } private void onDataMessage(Map data) { - logger.debug("received data message: {}", data); + logger.debug("{} Received data message: {}", label, data); // We don't do anything with data messages, just kick them up a level delegate.onDataMessage(data); } private void onControlMessage(Map data) { - logger.debug("Got control message: {}", data); + logger.debug("{} Got control message: {}", label, data); try { String messageType = (String) data.get(SERVER_CONTROL_MESSAGE_TYPE); if (messageType != null) { @@ -166,20 +169,20 @@ private void onControlMessage(Map data) { (Map) data.get(SERVER_CONTROL_MESSAGE_DATA); onHandshake(handshakeData); } else { - logger.debug("Ignoring unknown control message: {}", messageType); + logger.debug("{} Ignoring unknown control message: {}", label, messageType); } } else { - logger.debug("Got invalid control message: {}", data); + logger.debug("{} Got invalid control message: {}", label, data); close(); } } catch (ClassCastException e) { - logger.debug("Failed to parse control message", e); + logger.debug("{} Failed to parse control message", label, e); close(); } } private void onConnectionShutdown(String reason) { - logger.debug("Connection shutdown command received. Shutting down..."); + logger.debug("{} Connection shutdown command received. Shutting down...", label); delegate.onKill(reason); close(); } @@ -197,15 +200,15 @@ private void onHandshake(Map handshake) { } private void onConnectionReady(long timestamp, String sessionId) { - logger.debug("realtime connection established"); + logger.debug("{} Realtime connection established", label); state = State.REALTIME_CONNECTED; delegate.onReady(timestamp, sessionId); } private void onReset(String host) { logger.debug( - "Got a reset; killing connection to {}; Updating internalHost to {}", - this.hostInfo.getHost(), host); + "{} Got a reset; killing connection to {}; Updating internalHost to {}", + label, hostInfo.getHost(), host); delegate.onCacheHost(host); // Explicitly close the connection with SERVER_RESET so calling code knows to reconnect @@ -215,12 +218,12 @@ private void onReset(String host) { private void sendData(Map data, boolean isSensitive) { if (state != State.REALTIME_CONNECTED) { - logger.debug("Tried to send on an unconnected connection"); + logger.debug("{} Tried to send on an unconnected connection", label); } else { if (isSensitive) { - logger.debug("Sending data (contents hidden)"); + logger.debug("{} Sending data (contents hidden)", label); } else { - logger.debug("Sending data: {}", data); + logger.debug("{} Sending data: {}", label, data); } conn.send(data); } diff --git a/src/main/java/com/google/firebase/database/connection/PersistentConnectionImpl.java b/src/main/java/com/google/firebase/database/connection/PersistentConnectionImpl.java index ae5aa708e..dc86bf03f 100644 --- a/src/main/java/com/google/firebase/database/connection/PersistentConnectionImpl.java +++ b/src/main/java/com/google/firebase/database/connection/PersistentConnectionImpl.java @@ -32,6 +32,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PersistentConnectionImpl implements Connection.Delegate, PersistentConnection { @@ -91,6 +92,9 @@ public class PersistentConnectionImpl implements Connection.Delegate, Persistent private static final String SERVER_KILL_INTERRUPT_REASON = "server_kill"; private static final String IDLE_INTERRUPT_REASON = "connection_idle"; private static final String TOKEN_REFRESH_INTERRUPT_REASON = "token_refresh"; + + private static final Logger logger = LoggerFactory.getLogger(PersistentConnection.class); + private static long connectionIds = 0; private final Delegate delegate; @@ -99,8 +103,8 @@ public class PersistentConnectionImpl implements Connection.Delegate, Persistent private final ConnectionFactory connFactory; private final ConnectionAuthTokenProvider authTokenProvider; private final ScheduledExecutorService executorService; - private final PrefixedLogger logger; private final RetryHelper retryHelper; + private final String label; private String cachedHost; private HashSet interruptReasons = new HashSet<>(); @@ -151,8 +155,7 @@ public PersistentConnectionImpl(ConnectionContext context, HostInfo info, Delega .build(); long connId = connectionIds++; - this.logger = new PrefixedLogger(LoggerFactory.getLogger(PersistentConnection.class), - "[pc_" + connId + "]"); + this.label = "[pc_" + connId + "]"; this.lastSessionId = null; doIdleCheck(); } @@ -160,7 +163,7 @@ public PersistentConnectionImpl(ConnectionContext context, HostInfo info, Delega // Connection.Delegate methods @Override public void onReady(long timestamp, String sessionId) { - logger.debug("onReady"); + logger.debug("{} onReady", label); lastConnectionEstablishedTime = System.currentTimeMillis(); handleTimestamp(timestamp); @@ -187,12 +190,12 @@ public void listen( Long tag, RequestResultCallback listener) { ListenQuerySpec query = new ListenQuerySpec(path, queryParams); - logger.debug("Listening on {}", query); + logger.debug("{} Listening on {}", label, query); // TODO: Fix this somehow? //hardAssert(query.isDefault() || !query.loadsAllData(), "listen() called for non-default but " // + "complete query"); hardAssert(!listens.containsKey(query), "listen() called twice for same QuerySpec."); - logger.debug("Adding listen query: {}", query); + logger.debug("{} Adding listen query: {}", label, query); OutstandingListen outstandingListen = new OutstandingListen(listener, query, tag, currentHashFn); listens.put(query, outstandingListen); @@ -272,19 +275,19 @@ public void onDataMessage(Map message) { Map body = (Map) message.get(SERVER_ASYNC_PAYLOAD); onDataPush(action, body); } else { - logger.debug("Ignoring unknown message: {}", message); + logger.debug("{} Ignoring unknown message: {}", label, message); } } @Override public void onDisconnect(Connection.DisconnectReason reason) { - logger.debug("Got on disconnect due to {}", reason.name()); + logger.debug("{} Got on disconnect due to {}", label, reason.name()); this.connectionState = ConnectionState.Disconnected; this.realtime = null; this.hasOnDisconnects = false; requestCBHash.clear(); if (inactivityTimer != null) { - logger.debug("cancelling idle time checker"); + logger.debug("{} Cancelling idle time checker", label); inactivityTimer.cancel(false); inactivityTimer = null; } @@ -310,16 +313,18 @@ public void onDisconnect(Connection.DisconnectReason reason) { @Override public void onKill(String reason) { - logger.debug( - "Firebase Database connection was forcefully killed by the server. Will not attempt " - + "reconnect. Reason: {}", reason); + if (logger.isDebugEnabled()) { + logger.debug( + "{} Firebase Database connection was forcefully killed by the server. Will not attempt " + + "reconnect. Reason: {}", label, reason); + } interrupt(SERVER_KILL_INTERRUPT_REASON); } @Override public void unlisten(List path, Map queryParams) { ListenQuerySpec query = new ListenQuerySpec(path, queryParams); - logger.debug("unlistening on {}", query); + logger.debug("{} Unlistening on {}", label, query); // TODO: fix this by understanding query params? //Utilities.hardAssert(query.isDefault() || !query.loadsAllData(), @@ -381,7 +386,7 @@ public void onDisconnectCancel(List path, RequestResultCallback onComple @Override public void interrupt(String reason) { - logger.debug("Connection interrupted for: {}", reason); + logger.debug("{} Connection interrupted for: {}", label, reason); interruptReasons.add(reason); if (realtime != null) { @@ -398,7 +403,7 @@ public void interrupt(String reason) { @Override public void resume(String reason) { - logger.debug("Connection no longer interrupted for: {}", reason); + logger.debug("{} Connection no longer interrupted for: {}", label, reason); interruptReasons.remove(reason); if (shouldReconnect() && connectionState == ConnectionState.Disconnected) { @@ -425,7 +430,7 @@ public void refreshAuthToken() { // we close the connection to make sure any writes/listens are queued until the connection // is reauthed with the current token after reconnecting. Note that this will trigger // onDisconnects which isn't ideal. - logger.debug("Auth token refresh requested"); + logger.debug("{} Auth token refresh requested", label); // By using interrupt instead of closing the connection we make sure there are no race // conditions with other fetch token attempts (interrupt/resume is expected to handle those @@ -436,7 +441,7 @@ public void refreshAuthToken() { @Override public void refreshAuthToken(String token) { - logger.debug("Auth token refreshed."); + logger.debug("{} Auth token refreshed.", label); this.authToken = token; if (connected()) { if (token != null) { @@ -454,13 +459,13 @@ private void tryScheduleReconnect() { "Not in disconnected state: %s", this.connectionState); final boolean forceRefresh = this.forceAuthTokenRefresh; - logger.debug("Scheduling connection attempt"); + logger.debug("{} Scheduling connection attempt", label); this.forceAuthTokenRefresh = false; retryHelper.retry( new Runnable() { @Override public void run() { - logger.debug("Trying to fetch auth token"); + logger.debug("{} Trying to fetch auth token", label); hardAssert( connectionState == ConnectionState.Disconnected, "Not in disconnected state: %s", @@ -477,7 +482,7 @@ public void onSuccess(String token) { // Someone could have interrupted us while fetching the token, // marking the connection as Disconnected if (connectionState == ConnectionState.GettingToken) { - logger.debug("Successfully fetched token, opening connection"); + logger.debug("{} Successfully fetched token, opening connection", label); openNetworkConnection(token); } else { hardAssert( @@ -485,13 +490,13 @@ public void onSuccess(String token) { "Expected connection state disconnected, but was %s", connectionState); logger.debug( - "Not opening connection after token refresh, " - + "because connection was set to disconnected"); + "{} Not opening connection after token refresh, because connection " + + "was set to disconnected", label); } } else { logger.debug( - "Ignoring getToken result, because this was not the " - + "latest attempt."); + "{} Ignoring getToken result, because this was not the " + + "latest attempt.", label); } } @@ -499,12 +504,12 @@ public void onSuccess(String token) { public void onError(String error) { if (thisGetTokenAttempt == currentGetTokenAttempt) { connectionState = ConnectionState.Disconnected; - logger.debug("Error fetching token: " + error); + logger.debug("{} Error fetching token: {}", label, error); tryScheduleReconnect(); } else { logger.debug( - "Ignoring getToken error, because this was not the " - + "latest attempt."); + "{} Ignoring getToken error, because this was not the " + + "latest attempt.", label); } } }); @@ -593,7 +598,7 @@ private OutstandingListen removeListen(ListenQuerySpec query) { logger.debug("removing query {}", query); if (!listens.containsKey(query)) { logger.debug( - "Trying to remove listener for QuerySpec {} but no listener exists.", query); + "{} Trying to remove listener for QuerySpec {} but no listener exists.", label, query); return null; } else { OutstandingListen oldListen = listens.get(query); @@ -604,7 +609,7 @@ private OutstandingListen removeListen(ListenQuerySpec query) { } private Collection removeListens(List path) { - logger.debug("removing all listens at path {}", path); + logger.debug("{} Removing all listens at path {}", label, path); List removedListens = new ArrayList<>(); for (Map.Entry entry : listens.entrySet()) { ListenQuerySpec query = entry.getKey(); @@ -624,7 +629,7 @@ private Collection removeListens(List path) { } private void onDataPush(String action, Map body) { - logger.debug("handleServerMessage: {} {}", action, body); + logger.debug("{} handleServerMessage: {} {}", label, action, body); if (action.equals(SERVER_ASYNC_DATA_UPDATE) || action.equals(SERVER_ASYNC_DATA_MERGE)) { boolean isMerge = action.equals(SERVER_ASYNC_DATA_MERGE); @@ -633,7 +638,7 @@ private void onDataPush(String action, Map body) { Long tagNumber = ConnectionUtils.longFromObject(body.get(SERVER_DATA_TAG)); // ignore empty merges if (isMerge && (payloadData instanceof Map) && ((Map) payloadData).size() == 0) { - logger.debug("ignoring empty merge for path {}", pathString); + logger.debug("{} Ignoring empty merge for path {}", label, pathString); } else { List path = ConnectionUtils.stringToPath(pathString); delegate.onDataUpdate(path, payloadData, isMerge, tagNumber); @@ -655,7 +660,7 @@ private void onDataPush(String action, Map body) { rangeMerges.add(new RangeMerge(start, end, update)); } if (rangeMerges.isEmpty()) { - logger.debug("Ignoring empty range merge for path {}", pathString); + logger.debug("{} Ignoring empty range merge for path {}", label, pathString); } else { this.delegate.onRangeMergeUpdate(path, rangeMerges, tag); } @@ -670,7 +675,7 @@ private void onDataPush(String action, Map body) { } else if (action.equals(SERVER_ASYNC_SECURITY_DEBUG)) { onSecurityDebugPacket(body); } else { - logger.debug("Unrecognized action from server: {}", action); + logger.debug("{} Unrecognized action from server: {}", label, action); } } @@ -690,7 +695,7 @@ private void onAuthRevoked(String errorCode, String errorMessage) { // This might be for an earlier token than we just recently sent. But since we need to close // the connection anyways, we can set it to null here and we will refresh the token later // on reconnect. - logger.debug("Auth token revoked: " + errorCode + " (" + errorMessage + ")"); + logger.debug("{} Auth token revoked: {} ({})", label, errorCode, errorMessage); this.authToken = null; this.forceAuthTokenRefresh = true; this.delegate.onAuthStatus(false); @@ -700,7 +705,7 @@ private void onAuthRevoked(String errorCode, String errorMessage) { private void onSecurityDebugPacket(Map message) { // TODO: implement on iOS too - logger.info((String) message.get("msg")); + logger.info("{} {}", label, message.get("msg")); } private void upgradeAuth() { @@ -733,7 +738,7 @@ public void onResponse(Map response) { forceAuthTokenRefresh = true; delegate.onAuthStatus(false); String reason = (String) response.get(SERVER_RESPONSE_DATA); - logger.debug("Authentication failed: " + status + " (" + reason + ")"); + logger.debug("{} Authentication failed: {} ({})", label, status, reason); realtime.close(); if (status.equals("invalid_token") || status.equals("permission_denied")) { @@ -745,11 +750,11 @@ public void onResponse(Map response) { // Set a long reconnect delay because recovery is unlikely. retryHelper.setMaxDelay(); logger.warn( - "Provided authentication credentials are invalid. This " + "{} Provided authentication credentials are invalid. This " + "usually indicates your FirebaseApp instance was not initialized " + "correctly. Make sure your database URL is correct and that your " + "service account is for the correct project and is authorized to " - + "access it."); + + "access it.", label); } } } @@ -781,7 +786,7 @@ private void sendUnauth() { } private void restoreAuth() { - logger.debug("calling restore state"); + logger.debug("{} Calling restore state", label); hardAssert( this.connectionState == ConnectionState.Connecting, @@ -789,11 +794,11 @@ private void restoreAuth() { this.connectionState); if (authToken == null) { - logger.debug("Not restoring auth because token is null."); + logger.debug("{} Not restoring auth because token is null.", label); this.connectionState = ConnectionState.Connected; restoreState(); } else { - logger.debug("Restoring auth."); + logger.debug("{} Restoring auth.", label); this.connectionState = ConnectionState.Authenticating; sendAuthAndRestoreState(); } @@ -806,13 +811,13 @@ private void restoreState() { this.connectionState); // Restore listens - logger.debug("Restoring outstanding listens"); + logger.debug("{} Restoring outstanding listens", label); for (OutstandingListen listen : listens.values()) { - logger.debug("Restoring listen {}", listen.getQuery()); + logger.debug("{} Restoring listen {}", label, listen.getQuery()); sendListen(listen); } - logger.debug("Restoring writes."); + logger.debug("{} Restoring writes.", label); // Restore puts ArrayList outstanding = new ArrayList<>(outstandingPuts.keySet()); // Make sure puts are restored in order @@ -833,7 +838,7 @@ private void restoreState() { } private void handleTimestamp(long timestamp) { - logger.debug("handling timestamp"); + logger.debug("{} Handling timestamp", label); long timestampDelta = timestamp - System.currentTimeMillis(); Map updates = new HashMap<>(); updates.put(Constants.DOT_INFO_SERVERTIME_OFFSET, timestampDelta); @@ -883,7 +888,7 @@ assert canSendWrites() new ConnectionRequestCallback() { @Override public void onResponse(Map response) { - logger.debug("{} response: {}", action, response); + logger.debug("{} {} response: {}", label, action, response); OutstandingPut currentPut = outstandingPuts.get(putId); if (currentPut == put) { @@ -899,8 +904,8 @@ public void onResponse(Map response) { } } } else { - logger.debug( - "Ignoring on complete for put {} because it was removed already.", putId); + logger.debug("{} Ignoring on complete for put {} because it was removed already.", + label, putId); } doIdleCheck(); } @@ -982,12 +987,12 @@ public void onResponse(Map response) { if (!status.equals("ok")) { String errorMessage = (String) response.get(SERVER_DATA_UPDATE_BODY); logger.debug( - "Failed to send stats: {} (message: {})", stats, errorMessage); + "{} Failed to send stats: {} (message: {})", label, stats, errorMessage); } } }); } else { - logger.debug("Not sending stats because stats are empty"); + logger.debug("{} Not sending stats because stats are empty", label); } } @@ -996,11 +1001,9 @@ private void warnOnListenerWarnings(List warnings, ListenQuerySpec query if (warnings.contains("no_index")) { String indexSpec = "\".indexOn\": \"" + query.queryParams.get("i") + '\"'; logger.warn( - "Using an unspecified index. Consider adding '" - + indexSpec - + "' at " - + ConnectionUtils.pathToString(query.path) - + " to your security and Firebase Database rules for better performance"); + "{} Using an unspecified index. Consider adding '{}' at {} to your security and " + + "Firebase Database rules for better performance", + label, indexSpec, ConnectionUtils.pathToString(query.path)); } } @@ -1009,7 +1012,7 @@ private void sendConnectStats() { assert !this.context.isPersistenceEnabled() : "Stats for persistence on JVM missing (persistence not yet supported)"; stats.put("sdk.admin_java." + context.getClientSdkVersion().replace('.', '-'), 1); - logger.debug("Sending first connection stats"); + logger.debug("{} Sending first connection stats", label); sendStats(stats); } diff --git a/src/main/java/com/google/firebase/database/connection/PrefixedLogger.java b/src/main/java/com/google/firebase/database/connection/PrefixedLogger.java deleted file mode 100644 index 293c22d32..000000000 --- a/src/main/java/com/google/firebase/database/connection/PrefixedLogger.java +++ /dev/null @@ -1,58 +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.connection; - -import org.slf4j.Logger; - -public class PrefixedLogger { - - private final Logger logger; - private final String prefix; - - public PrefixedLogger(Logger logger, String prefix) { - this.logger = logger; - this.prefix = prefix; - } - - public void debug(String msg, Object... args) { - if (logger.isDebugEnabled()) { - logger.debug(addPrefix(msg), args); - } - } - - public void debug(String msg, Throwable t) { - if (logger.isDebugEnabled()) { - logger.debug(addPrefix(msg), t); - } - } - - public void error(String msg, Throwable t) { - logger.error(addPrefix(msg), t); - } - - public void warn(String msg) { - logger.warn(addPrefix(msg)); - } - - public void info(String msg) { - logger.info(addPrefix(msg)); - } - - private String addPrefix(String msg) { - return prefix + " " + msg; - } -} diff --git a/src/main/java/com/google/firebase/database/connection/WebsocketConnection.java b/src/main/java/com/google/firebase/database/connection/WebsocketConnection.java index 512f81023..44335b038 100644 --- a/src/main/java/com/google/firebase/database/connection/WebsocketConnection.java +++ b/src/main/java/com/google/firebase/database/connection/WebsocketConnection.java @@ -48,11 +48,12 @@ class WebsocketConnection { private static final long CONNECT_TIMEOUT_MS = 30 * 1000; // 30 seconds private static final int MAX_FRAME_SIZE = 16384; private static final AtomicLong CONN_ID = new AtomicLong(0); + private static final Logger logger = LoggerFactory.getLogger(WebsocketConnection.class); private final ScheduledExecutorService executorService; - private final PrefixedLogger logger; private final WSClient conn; private final Delegate delegate; + private final String label; private StringList buffer; private boolean everConnected = false; @@ -76,8 +77,7 @@ class WebsocketConnection { WSClientFactory clientFactory) { this.executorService = connectionContext.getExecutorService(); this.delegate = delegate; - this.logger = new PrefixedLogger(LoggerFactory.getLogger(WebsocketConnection.class), - "[ws_" + CONN_ID.getAndIncrement() + "]"); + this.label = "[ws_" + CONN_ID.getAndIncrement() + "]"; this.conn = clientFactory.newClient(new WSClientHandlerImpl()); } @@ -100,7 +100,7 @@ void start() { } void close() { - logger.debug("websocket is being closed"); + logger.debug("{} Websocket is being closed", label); isClosed = true; conn.close(); @@ -129,7 +129,7 @@ void send(Map message) { conn.send(seg); } } catch (IOException e) { - logger.error("Failed to serialize message: " + message.toString(), e); + logger.error("{} Failed to serialize message: {}", label, message, e); closeAndNotify(); } } @@ -149,7 +149,7 @@ private List splitIntoFrames(String src, int maxFrameSize) { } private void handleNewFrameCount(int numFrames) { - logger.debug("HandleNewFrameCount: {}", numFrames); + logger.debug("{} Handle new frame count: {}", label, numFrames); buffer = new StringList(numFrames); } @@ -162,13 +162,13 @@ private void appendFrame(String message) { String combined = buffer.combine(); try { Map decoded = JsonMapper.parseJson(combined); - logger.debug("handleIncomingFrame complete frame: {}", decoded); + logger.debug("{} Parsed complete frame: {}", label, decoded); delegate.onMessage(decoded); } catch (IOException e) { - logger.error("Error parsing frame: " + combined, e); + logger.error("{} Error parsing frame: {}", label, combined, e); closeAndNotify(); } catch (ClassCastException e) { - logger.error("Error parsing frame (cast error): " + combined, e); + logger.error("{} Error parsing frame (cast error): {}", label, combined, e); closeAndNotify(); } } @@ -213,9 +213,10 @@ private void resetKeepAlive() { } if (keepAlive != null) { keepAlive.cancel(false); - logger.debug("Reset keepAlive. Remaining: {}", keepAlive.getDelay(TimeUnit.MILLISECONDS)); + logger.debug("{} Reset keepAlive. Remaining: {}", label, + keepAlive.getDelay(TimeUnit.MILLISECONDS)); } else { - logger.debug("Reset keepAlive"); + logger.debug("{} Reset keepAlive", label); } keepAlive = executorService.schedule(nop(), KEEP_ALIVE_TIMEOUT_MS, TimeUnit.MILLISECONDS); } @@ -244,14 +245,14 @@ private void closeAndNotify() { private void onClosed() { if (!isClosed) { - logger.debug("closing itself"); + logger.debug("{} Closing itself", label); closeAndNotify(); } } private void closeIfNeverConnected() { if (!everConnected && !isClosed) { - logger.debug("timed out on connect"); + logger.debug("{} Timed out on connect", label); closeAndNotify(); } } @@ -265,7 +266,7 @@ private class WSClientHandlerImpl implements WSClientEventHandler { @Override public void onOpen() { - logger.debug("websocket opened"); + logger.debug("{} Websocket opened", label); executorService.execute(new Runnable() { @Override public void run() { @@ -278,7 +279,7 @@ public void run() { @Override public void onMessage(final String message) { - logger.debug("ws message: {}", message); + logger.debug("{} WS message: {}", label, message); executorService.execute(new Runnable() { @Override public void run() { @@ -289,7 +290,7 @@ public void run() { @Override public void onClose() { - logger.debug("closed"); + logger.debug("{} Closed", label); if (!isClosed) { // If the connection tear down was initiated by the higher-layer, isClosed will already // be true. Nothing more to do in that case. @@ -306,9 +307,9 @@ public void run() { @Override public void onError(final Throwable e) { if (e.getCause() != null && e.getCause() instanceof EOFException) { - logger.error("WebSocket reached EOF", e); + logger.error("{} WebSocket reached EOF", label, e); } else { - logger.error("WebSocket error", e); + logger.error("{} WebSocket error", label, e); } executorService.execute( new Runnable() { diff --git a/src/test/java/com/google/firebase/Main.java b/src/test/java/com/google/firebase/Main.java deleted file mode 100644 index 684ad7e2f..000000000 --- a/src/test/java/com/google/firebase/Main.java +++ /dev/null @@ -1,50 +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; - -import com.google.firebase.database.DataSnapshot; -import com.google.firebase.database.DatabaseError; -import com.google.firebase.database.DatabaseReference; -import com.google.firebase.database.FirebaseDatabase; -import com.google.firebase.database.ValueEventListener; -import com.google.firebase.testing.IntegrationTestUtils; - -public class Main { - - public static void main(String[] args) throws Exception { - IntegrationTestUtils.ensureDefaultApp(); - DatabaseReference foo = FirebaseDatabase.getInstance().getReference("foo"); - foo.addValueEventListener( - new ValueEventListener() { - @Override - public void onDataChange(DataSnapshot snapshot) { - System.out.println(snapshot.getValue()); - } - - @Override - public void onCancelled(DatabaseError error) { - System.out.println(error); - } - }); - for (int i = 0; i < 10; i++) { - Thread.sleep(1000); - foo.setValueAsync(System.currentTimeMillis()).get(); - } - Thread.sleep(60000); - } - -} diff --git a/src/test/java/com/google/firebase/database/TestHelpers.java b/src/test/java/com/google/firebase/database/TestHelpers.java index 6f077b8b2..cfd15034d 100644 --- a/src/test/java/com/google/firebase/database/TestHelpers.java +++ b/src/test/java/com/google/firebase/database/TestHelpers.java @@ -89,6 +89,7 @@ public static ScheduledExecutorService getExecutorService(DatabaseConfig config) DefaultRunLoop runLoop = (DefaultRunLoop) config.getRunLoop(); return runLoop.getExecutorService(); } + public static void waitFor(Semaphore semaphore) throws InterruptedException { waitFor(semaphore, 1); } diff --git a/src/test/java/com/google/firebase/database/core/SyncPointTest.java b/src/test/java/com/google/firebase/database/core/SyncPointTest.java index 9b9e47207..91be0cce7 100644 --- a/src/test/java/com/google/firebase/database/core/SyncPointTest.java +++ b/src/test/java/com/google/firebase/database/core/SyncPointTest.java @@ -96,7 +96,7 @@ public void startListening( @Override public void stopListening(QuerySpec query, Tag tag) { Path path = query.getPath(); - logger.debug("Listening at " + path + " for Tag " + tag); + logger.debug("Listening at {} for Tag {}", path, tag); checkState(this.listens.contains(query), "Stopped listening for query already"); this.listens.remove(query); @@ -314,7 +314,7 @@ private static Map parseMergePaths(Map merges) { @SuppressWarnings("unchecked") private static void runTest(Map testSpec, String basePath) { - logger.info("Running \"" + testSpec.get("name") + '"'); + logger.debug("Running \"{}\"", testSpec.get("name")); SyncTree.ListenProvider listenProvider = getNewListenProvider(); SyncTree syncTree = new SyncTree(new NoopPersistenceManager(), listenProvider); diff --git a/src/test/java/com/google/firebase/database/core/persistence/RandomPersistenceTest.java b/src/test/java/com/google/firebase/database/core/persistence/RandomPersistenceTest.java index c4f3cd371..58358a8ff 100644 --- a/src/test/java/com/google/firebase/database/core/persistence/RandomPersistenceTest.java +++ b/src/test/java/com/google/firebase/database/core/persistence/RandomPersistenceTest.java @@ -184,7 +184,7 @@ public void randomOperations() { new DefaultPersistenceManager(storageEngine, CachePolicy.NONE); final HashMap tagMap = new HashMap<>(); SyncTree syncTree = - new SyncTree( manager, new SyncTree.ListenProvider() { + new SyncTree(manager, new SyncTree.ListenProvider() { @Override public void startListening( QuerySpec query, From 27f63b08bf65c09d27780f1a264d31237f433d35 Mon Sep 17 00:00:00 2001 From: hiranya911 Date: Thu, 15 Mar 2018 17:26:10 -0700 Subject: [PATCH 10/12] Removing test config file --- logging.properties | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 logging.properties diff --git a/logging.properties b/logging.properties deleted file mode 100644 index e69de29bb..000000000 From fa2b7eeb77c4a888fd7b3802944fb6176d0ecfe9 Mon Sep 17 00:00:00 2001 From: hiranya911 Date: Tue, 20 Mar 2018 12:50:09 -0700 Subject: [PATCH 11/12] Updated CHANGELOG --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f91d8e00a..eb5743a57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ - [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. +- [changed] Removed the deprecated `FirebaseDatabase.setLogLevel()` API + and the related logging utilities. Developers should use SLF4J to + configure logging directly. # v5.9.0 From 82ddecea7606fb51ca3c9b57b5b194eae4f49ae0 Mon Sep 17 00:00:00 2001 From: hiranya911 Date: Thu, 22 Mar 2018 17:05:00 -0700 Subject: [PATCH 12/12] Minor clean ups pointed out in the code review --- .../connection/PersistentConnectionImpl.java | 10 +++---- .../google/firebase/database/core/Repo.java | 30 +++++++++---------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/google/firebase/database/connection/PersistentConnectionImpl.java b/src/main/java/com/google/firebase/database/connection/PersistentConnectionImpl.java index dc86bf03f..fc79b4cfe 100644 --- a/src/main/java/com/google/firebase/database/connection/PersistentConnectionImpl.java +++ b/src/main/java/com/google/firebase/database/connection/PersistentConnectionImpl.java @@ -313,11 +313,9 @@ public void onDisconnect(Connection.DisconnectReason reason) { @Override public void onKill(String reason) { - if (logger.isDebugEnabled()) { - logger.debug( - "{} Firebase Database connection was forcefully killed by the server. Will not attempt " - + "reconnect. Reason: {}", label, reason); - } + logger.debug( + "{} Firebase Database connection was forcefully killed by the server. Will not attempt " + + "reconnect. Reason: {}", label, reason); interrupt(SERVER_KILL_INTERRUPT_REASON); } @@ -595,7 +593,7 @@ private void sendUnlisten(OutstandingListen listen) { } private OutstandingListen removeListen(ListenQuerySpec query) { - logger.debug("removing query {}", query); + logger.debug("{} removing query {}", label, query); if (!listens.containsKey(query)) { logger.debug( "{} Trying to remove listener for QuerySpec {} but no listener exists.", label, query); diff --git a/src/main/java/com/google/firebase/database/core/Repo.java b/src/main/java/com/google/firebase/database/core/Repo.java index 0668506a9..98777d04d 100644 --- a/src/main/java/com/google/firebase/database/core/Repo.java +++ b/src/main/java/com/google/firebase/database/core/Repo.java @@ -69,7 +69,7 @@ public class Repo implements PersistentConnection.Delegate { private static final String TRANSACTION_TOO_MANY_RETRIES = "maxretries"; private static final String TRANSACTION_OVERRIDE_BY_SET = "overriddenBySet"; - private static final Logger operationLogger = LoggerFactory.getLogger(Repo.class); + private static final Logger logger = LoggerFactory.getLogger(Repo.class); private final RepoInfo repoInfo; private final OffsetClock serverClock = new OffsetClock(new DefaultClock(), 0); @@ -130,7 +130,7 @@ private void deferredInitialization() { new AuthTokenProvider.TokenChangeListener() { @Override public void onTokenChange(String token) { - operationLogger.debug("Auth token changed, triggering auth token refresh"); + logger.debug("Auth token changed, triggering auth token refresh"); connection.refreshAuthToken(token); } }); @@ -259,7 +259,7 @@ boolean hasListeners() { public void onDataUpdate( List pathSegments, Object message, boolean isMerge, Long optTag) { Path path = new Path(pathSegments); - operationLogger.debug("onDataUpdate: {} {}", path, message); + logger.debug("onDataUpdate: {} {}", path, message); List events; @@ -298,7 +298,7 @@ public void onDataUpdate( postEvents(events); } catch (DatabaseException e) { - operationLogger.error("FIREBASE INTERNAL ERROR", e); + logger.error("Firebase internal error", e); } } @@ -308,7 +308,7 @@ public void onRangeMergeUpdate( List merges, Long tagNumber) { Path path = new Path(pathSegments); - operationLogger.debug("onRangeMergeUpdate: {} {}", path, merges); + logger.debug("onRangeMergeUpdate: {} {}", path, merges); List parsedMerges = new ArrayList<>(merges.size()); for (com.google.firebase.database.connection.RangeMerge merge : merges) { @@ -370,7 +370,7 @@ public void setValue( final Path path, Node newValueUnresolved, final DatabaseReference.CompletionListener onComplete) { - operationLogger.debug("set: {} {}", path, newValueUnresolved); + logger.debug("set: {} {}", path, newValueUnresolved); Map serverValues = ServerValues.generateServerValues(serverClock); Node newValue = ServerValues.resolveDeferredValueSnapshot(newValueUnresolved, serverValues); @@ -403,9 +403,9 @@ public void updateChildren( CompoundWrite updates, final DatabaseReference.CompletionListener onComplete, Map unParsedUpdates) { - operationLogger.debug("update: {} {}", path, unParsedUpdates); + logger.debug("update: {} {}", path, unParsedUpdates); if (updates.isEmpty()) { - operationLogger.debug("update called with no changes. No-op"); + logger.debug("update called with no changes. No-op"); // dispatch on complete callOnComplete(onComplete, null, path); return; @@ -443,7 +443,7 @@ public void onRequestResult(String optErrorCode, String optErrorMessage) { } public void purgeOutstandingWrites() { - operationLogger.debug("Purging writes"); + logger.debug("Purging writes"); List events = serverSyncTree.removeAllWrites(); postEvents(events); // Abort any transactions @@ -592,7 +592,7 @@ private void updateInfo(ChildKey childKey, Object value) { List events = this.infoSyncTree.applyServerOverwrite(path, node); this.postEvents(events); } catch (DatabaseException e) { - operationLogger.error("Failed to parse info update", e); + logger.error("Failed to parse info update", e); } } @@ -625,16 +625,16 @@ private void warnIfWriteFailed(String writeType, Path path, DatabaseError error) if (error != null && !(error.getCode() == DatabaseError.DATA_STALE || error.getCode() == DatabaseError.WRITE_CANCELED)) { - operationLogger.warn(writeType + " at " + path.toString() + " failed: " + error.toString()); + logger.warn(writeType + " at " + path.toString() + " failed: " + error.toString()); } } public void startTransaction(Path path, final Transaction.Handler handler, boolean applyLocally) { - operationLogger.debug("transaction: {}", path); + logger.debug("transaction: {}", path); if (this.ctx.isPersistenceEnabled() && !loggedTransactionPersistenceWarning) { loggedTransactionPersistenceWarning = true; - operationLogger.info( + logger.info( "runTransaction() usage detected while persistence is enabled. Please be aware that " + "transactions *will not* be persisted across database restarts. See " + "https://www.firebase.com/docs/android/guide/offline-capabilities.html" @@ -1110,7 +1110,7 @@ public void visitTree(Tree> tree) { private Path abortTransactions(Path path, final int reason) { Path affectedPath = getAncestorTransactionNode(path).getPath(); - operationLogger.debug("Aborting transactions for path: {}. Affected: {}", path, affectedPath); + logger.debug("Aborting transactions for path: {}. Affected: {}", path, affectedPath); Tree> transactionNode = transactionQueueTree.subTree(path); transactionNode.forEachAncestor( @@ -1211,7 +1211,7 @@ private void runTransactionOnComplete(Transaction.Handler handler, DatabaseError try { handler.onComplete(error, committed, snapshot); } catch (Exception e) { - operationLogger.error("Exception in transaction onComplete callback", e); + logger.error("Exception in transaction onComplete callback", e); } }