Last active
September 8, 2020 15:23
-
-
Save nsivabalan/3f9a7e77bfe8ebb41e597e3d8f1eef2d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.ubercab.core.oauth_token_manager; | |
| import androidx.annotation.Nullable; | |
| import androidx.annotation.VisibleForTesting; | |
| import com.ubercab.experiment.DynamicExperiments; | |
| import com.ubercab.experiment.model.ExperimentUpdate; | |
| import com.ubercab.lumber.core.Lumber; | |
| import java.util.concurrent.ExecutorService; | |
| import java.util.concurrent.atomic.AtomicBoolean; | |
| import rx.Subscription; | |
| public class OAuthCore { | |
| private static final String TAG = OAuthCore.class.getName(); | |
| private static OAuthCore singleInstance; | |
| private AtomicBoolean asyncRetrievalInProgress = new AtomicBoolean(false); | |
| private AtomicBoolean logout = new AtomicBoolean(false); | |
| private OAuthTokenManager oAuthTokenManager; | |
| private ExecutorService executorService; | |
| @Nullable private Subscription experimentSubscription; | |
| @Nullable private DynamicExperiments dynamicExperiments; | |
| @VisibleForTesting | |
| @Nullable private ExperimentUpdate oauthEnabled; | |
| @VisibleForTesting @Nullable ExperimentUpdate oAuthLogoutStatusCodeKillSwitch; | |
| private OAuthCore(OAuthTokenManager oAuthTokenManager, ExecutorService executorService) { | |
| this.oAuthTokenManager = oAuthTokenManager; | |
| this.executorService = executorService; | |
| } | |
| /** | |
| * Singleton instantiation of {@link OAuthCore}. | |
| * | |
| * @param oAuthTokenManager instance of {@link OAuthTokenManager} to use. | |
| * @return the singleton instance of {@link OAuthCore}. | |
| */ | |
| public synchronized static OAuthCore getInstance(OAuthTokenManager oAuthTokenManager, | |
| ExecutorService executorService) { | |
| if (singleInstance == null) { | |
| singleInstance = new OAuthCore(oAuthTokenManager, executorService); | |
| } | |
| return singleInstance; | |
| } | |
| /** | |
| * Resets all tracking variables. | |
| */ | |
| public void reset() { | |
| asyncRetrievalInProgress.set(false); | |
| logout.set(false); | |
| } | |
| /* | |
| * @return true if logout is set, false otherwise. | |
| */ | |
| public boolean isLoggedout(){ | |
| return logout.get(); | |
| } | |
| public boolean isRefreshTokenIsNull() { | |
| return oAuthTokenManager.getRefreshToken() == null; | |
| } | |
| // Every new call should invoke this and only then should call other apis in this class. | |
| public synchronized boolean shouldSkipOAuth() { | |
| if (oAuthTokenManager.getRefreshToken() == null) { | |
| logout.set(false); | |
| if (experimentSubscription != null) { | |
| experimentSubscription.unsubscribe(); | |
| experimentSubscription = null; | |
| } | |
| return false; | |
| } | |
| if (experimentSubscription == null) { | |
| startSubscription(dynamicExperiments); | |
| } | |
| if (oauthEnabled == null || !oauthEnabled.isTreated()) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| /** | |
| * Fetches access token to refresh the expired one. Caller is expected to check if access token is | |
| * expired before calling this method, because this method will take a lock and wait for any | |
| * other threads that are executing. So, unless the token is expired, its is better to avoid | |
| * calling this method. There are guards in place to ensure only one network call is made, so | |
| * correctness will be guaranteed eitherways. | |
| * | |
| * @return the token thus refreshed. | |
| */ | |
| public synchronized String retrieveAccessToken() { | |
| if (!oAuthTokenManager.isAccessTokenExpired()) { // do we need to check for log out here. | |
| return oAuthTokenManager.getAccessToken(); | |
| } else if (!logout.get()) { | |
| // fetch access token in blocking manner with some configured number of retries. | |
| // after fetching if log out -> set log out. | |
| } | |
| // if log out set, what to return ? | |
| } | |
| /** | |
| * Check if the access token would expire in given amount of time. | |
| * | |
| * @param deltaMs The number of milliseconds to check if the token would expire | |
| * @return true if will expire. false otherwise. | |
| */ | |
| public boolean willExpireInDeltaTime(long deltaMs) { | |
| return oAuthTokenManager.willAccessTokenExpire(deltaMs); | |
| } | |
| /** | |
| * Refresh tokens asynchronously. Only one request will be in flux irrespective of number of calls | |
| * to this method. Rest are ignored. | |
| */ | |
| public void refreshTokenAsync() { | |
| if (!asyncRetrievalInProgress.get()) { | |
| // proceed only if async Retrieval not in progress. | |
| synchronized (this) { | |
| if (!asyncRetrievalInProgress.getAndSet(true)) { | |
| // retrieve token async via thread and update value of accessToken in oauthTokenManager | |
| executorService.execute(new RefreshTokenRunnable()); | |
| } | |
| } | |
| } | |
| } | |
| @SuppressWarnings("VisibleForTesting") | |
| void startSubscription(@Nullable DynamicExperiments dynamicExperiments) { | |
| if (dynamicExperiments == null) { | |
| return; | |
| } | |
| experimentSubscription = | |
| dynamicExperiments | |
| .observe(OAuthExperimentName.AUTH_ISSUE_OAUTH_TOKENS) | |
| .subscribe( | |
| experimentUpdate -> oauthEnabled = experimentUpdate, | |
| error -> Lumber.i(TAG, "Unable to get experiment update")); | |
| dynamicExperiments | |
| .observe(OAuthExperimentName.OAUTH_FORCE_LOGOUT_STATUS_CODE_KILL_SWITCH) | |
| .subscribe( | |
| experimentUpdate -> oAuthLogoutStatusCodeKillSwitch = experimentUpdate, | |
| error -> | |
| Lumber.i(TAG, "Unable to get OAUTH_FORCE_LOGOUT_STATUS_CODE_KILL_SWITCH update")); | |
| } | |
| /** | |
| * Runnable Class to refresh token asynchronously. | |
| */ | |
| class RefreshTokenRunnable implements Runnable { | |
| public void run() { | |
| // fetch token | |
| // oAuthTokenManager.setOAuthToken(oAuthTokens); // what is the expected behavior if this | |
| // results in UNAUTHORIZED. | |
| asyncRetrievalInProgress.set(false); | |
| } | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment