Package com.box.boxjavalibv2.authorization

Source Code of com.box.boxjavalibv2.authorization.OAuthDataController

package com.box.boxjavalibv2.authorization;

import org.apache.http.HttpStatus;

import com.box.boxjavalibv2.BoxClient;
import com.box.boxjavalibv2.dao.BoxOAuthToken;
import com.box.boxjavalibv2.exceptions.AuthFatalFailureException;
import com.box.boxjavalibv2.exceptions.BoxServerException;
import com.box.restclientv2.exceptions.BoxRestException;

/**
* This is the data controller for OAuth, it handles token auto refresh.
*/
public class OAuthDataController implements IAuthDataController {

    public static enum OAuthTokenState {
        PRE_CREATION, AVAILABLE, REFRESHING, FAIL,
    }

    /**
     * Time to wait for lock.
     */
    private static final int WAIT = 200;

    /**
     * Default timeout waiting for lock
     */
    private static final int WAIT_TIME_OUT = 60000;

    private final BoxClient mClient;
    private final String mClientId;
    private final String mClientSecret;
    private String mDeviceId = null;
    private String mDeviceName = null;
    private volatile BoxOAuthToken mOAuthToken;
    private volatile OAuthTokenState mTokenState = OAuthTokenState.PRE_CREATION;
    private boolean mAutoRefresh;
    private int mWaitTimeOut = WAIT_TIME_OUT;
    private Exception refreshFailException;
    private volatile boolean locked = false;

    private OAuthRefreshListener refreshListener;

    public OAuthDataController(BoxClient boxClient, final String clientId, final String clientSecret, final boolean autoRefresh) {
        this.mClient = boxClient;
        this.mClientId = clientId;
        this.mClientSecret = clientSecret;
        this.mAutoRefresh = autoRefresh;
    }

    /**
     * Makes OAuth auto refresh itself when token expires. Note if autorefresh fails, it's not going to try refresh again.
     *
     * @param autoRefresh
     */
    public void setAutoRefreshOAuth(boolean autoRefresh) {
        mAutoRefresh = autoRefresh;
    }

    /**
     * Set the timeout for threads waiting for OAuth token refresh.
     *
     * @param timeout
     */
    public void setWaitTimeOut(int timeout) {
        this.mWaitTimeOut = timeout;
    }

    /**
     * @return the mScheme
     */
    public String getScheme() {
        return mClient.getConfig().getOAuthUrlScheme();
    }

    /**
     * @return the mAuthority
     */
    public String getAuthority() {
        return mClient.getConfig().getOAuthUrlAuthority();
    }

    /**
     * @return the OAuth url path.
     */
    public String getUrlPath() {
        return mClient.getConfig().getOAuthWebUrlPath();
    }

    /**
     * @return the mClientId
     */
    public String getClientId() {
        return mClientId;
    }

    /**
     * @return the mClientSecret
     */
    public String getClientSecret() {
        return mClientSecret;
    }

    public void setOAuthData(BoxOAuthToken token) {
        try {
            waitForLock(true, WAIT);

            mOAuthToken = token;
            if (mOAuthToken != null) {
                internalSetTokenState(OAuthTokenState.AVAILABLE);
            } else {
                internalSetTokenState(OAuthTokenState.PRE_CREATION);
            }
        } finally {
            unlock();
        }
    }

    /**
     * Set device id. This is optional.
     *
     * @param deviceId
     *            device id
     */
    public void setDeviceId(final String deviceId) {
        this.mDeviceId = deviceId;
    }

    /**
     * Set device name. Optional.
     *
     * @param deviceName
     *            device name
     */
    public void setDeviceName(final String deviceName) {
        this.mDeviceName = deviceName;
    }

    /**
     * @return the TokenState
     */
    public OAuthTokenState getTokenState() {
        return mTokenState;
    }

    /**
     * Reset token state to PRE_CREATION so it will be ready to refresh again.
     */
    public void resetTokenState() {
        try {
            waitForLock(true, WAIT);

            internalSetTokenState(OAuthTokenState.PRE_CREATION);
        } finally {
            unlock();
        }
    }

    /**
     * Setter of mTokenState. There's no locking mechanisms involved and should not be made public.
     *
     * @param tokenState
     *            the mTokenState to set
     */
    protected void internalSetTokenState(OAuthTokenState tokenState) {
        this.mTokenState = tokenState;
    }

    /**
     * @return the refreshFailException
     */
    public Exception getRefreshFailException() {
        return refreshFailException;
    }

    /**
     * @param refreshFailException
     *            the refreshFailException to set
     */
    public void setRefreshFail(Exception refreshFailException) {
        this.refreshFailException = refreshFailException;
        if (refreshFailException != null) {
            internalSetTokenState(OAuthTokenState.FAIL);
        }
    }

    /**
     * Initialize the controller.
     */
    public void initialize() {
        internalSetTokenState(OAuthTokenState.AVAILABLE);
        setRefreshFail(null);
        unlock();
    }

    /**
     * Get OAuthData, counting number of retries, in case of too many retries, throw. Note depending on the OAuth token state, there is no guarantee that the
     * OAuthData is valid. an example is that the token state is FAIL, which indicates the token is bad.
     *
     * @return OAuthData
     * @throws AuthFatalFailureException
     */
    @Override
    public BoxOAuthToken getAuthData() throws AuthFatalFailureException {
        long num = 0;
        while (num * WAIT <= mWaitTimeOut) {
            if (getAndSetLock(false)) {
                return mOAuthToken;
            } else {
                doWait(WAIT);
                num++;
            }
        }
        throw new AuthFatalFailureException(getRefreshFailException());
    }

    /**
     * Get OAuthData, in case of OAuthTokenState indicating refresh needed, do refresh. Note this method may involve network operation so do not call on UI
     * thread.
     */
    public BoxOAuthToken guaranteedGetAuthData() throws AuthFatalFailureException {
        long num = 0;
        while (num * WAIT <= mWaitTimeOut) {
            if (getAndSetLock(false)) {
                if (getTokenState() == OAuthTokenState.PRE_CREATION) {
                    if (!mAutoRefresh) {
                        throw new AuthFatalFailureException(getRefreshFailException());
                    } else {
                        refresh();
                        return guaranteedGetAuthData();
                    }
                } else if (getTokenState() == OAuthTokenState.FAIL) {
                    throw new AuthFatalFailureException(getRefreshFailException());
                } else {
                    return mOAuthToken;
                }
            } else {
                doWait(WAIT);
                num++;
            }
        }
        throw new AuthFatalFailureException(getRefreshFailException());
    }

    /**
     * Refresh the OAuth.
     *
     * @throws AuthFatalFailureException
     *             exception
     */
    @Override
    public void refresh() throws AuthFatalFailureException {
        if (!getAndSetLock(true)) {
            getAuthData();
        } else {
            try {
                if (getTokenState() == OAuthTokenState.FAIL || !mAutoRefresh) {
                    internalSetTokenState(OAuthTokenState.FAIL);
                    throw new AuthFatalFailureException(getRefreshFailException());
                } else {
                    doRefresh();
                }
            } finally {
                unlock();
            }
        }
    }

    @Deprecated
    /**
     * There is only one refresh listener, "add" doesn't make sense, use setOAuthRefreshListener instead.
     * @param listener
     */
    public void addOAuthRefreshListener(OAuthRefreshListener listener) {
        this.refreshListener = listener;
    }

    public void setOAuthRefreshListener(OAuthRefreshListener listener) {
        this.refreshListener = listener;
    }

    /**
     * Get the lock, optionally lock the lock after getting the lock.
     *
     * @param doLock
     *            whether want to lock after getting the lock.
     * @return
     */
    synchronized final protected boolean getAndSetLock(boolean doLock) {
        boolean lockRetrieved = false;
        if (doLock) {
            if (locked) {
                lockRetrieved = false;
            } else {
                locked = true;
                lockRetrieved = true;
            }
        } else {
            lockRetrieved = !locked;
        }
        return lockRetrieved;
    }

    /**
     * Unlock the OAuth lock.
     */
    protected void unlock() {
        locked = false;
    }

    /**
     * Refresh the OAuth.
     *
     * @throws AuthFatalFailureException
     *             exception
     */
    protected void doRefresh() throws AuthFatalFailureException {
        internalSetTokenState(OAuthTokenState.REFRESHING);

        if (mOAuthToken == null) {
            setRefreshFail(new BoxRestException("OAuthToken is null"));
            throw new AuthFatalFailureException(getRefreshFailException());
        }

        String refreshToken = mOAuthToken.getRefreshToken();
        try {
            mOAuthToken = mClient.getOAuthManager().refreshOAuth(refreshToken, mClientId, mClientSecret, mDeviceId, mDeviceName);
            internalSetTokenState(OAuthTokenState.AVAILABLE);
            setRefreshFail(null);
            if (refreshListener != null) {
                refreshListener.onRefresh(mOAuthToken);
            }
        } catch (BoxRestException e) {
            // A BoxRestException indicates a network error or a json parsing error. In this case, there is no reason to enter a failure state. Just throw an
            // exception and set the token state back to AVAILABLE so the app can retry if it wishes.
            internalSetTokenState(OAuthTokenState.AVAILABLE);
            throw new AuthFatalFailureException(e, refreshToken);
        } catch (BoxServerException e) {
            // A BoxServerException indicates an error from the server. This could be a 500, 403, 400, etc. The only case in which this is a permanent failure
            // is if we get a 400, which means the user's refresh token is invalid. In that case, we call setRefreshFail to enter a failure state until the user
            // re-logs-in. In the case of any other status, we set the token state back to AVAILABLE to allow the app to retry if it wishes.
            if (e.getStatusCode() == HttpStatus.SC_BAD_REQUEST) {
                setRefreshFail(e);
            } else {
                internalSetTokenState(OAuthTokenState.AVAILABLE);
            }
            throw new AuthFatalFailureException(e, refreshToken);
        }
    }

    /**
     * Convenient method for wait.
     */
    protected void doWait(long interval) {
        try {
            Thread.sleep(interval);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param doLock
     *            if true, this method will lock. DO call unlock() after your logic is done.
     * @param interval
     */
    protected void waitForLock(boolean doLock, long interval) {
        while (!getAndSetLock(doLock)) {
            doWait(interval);
        }
    }
}
TOP

Related Classes of com.box.boxjavalibv2.authorization.OAuthDataController

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.