All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.here.account.util.RefreshableResponseProvider Maven / Gradle / Ivy

Go to download

here-oauth-client is for clients of the HERE AAA, and supports OAuth2.0 grant_types=client_credentials

There is a newer version: 0.4.9
Show newest version
/*
 * Copyright (c) 2016 HERE Europe B.V.
 * 
 * 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.here.account.util;

import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import com.here.account.util.RefreshableResponseProvider.ExpiringResponse;

/**
 * Use this class if you want to always have an unexpired view of an expiring response object 
 * (within reason we expect that each request calls {@link #getUnexpiredResponse()} which is 
 * intended to execute quickly).
 * Strategy is to use a dedicated thread-scheduled executor service, to retrieve and set your 
 * ExpiringResponse to be always up-to-date.
 * 
 * 

* Token provider which handles refreshing tokens on a configurable interval. * *

* Note that this implementation does not incur any synchronization; rather it is assumed the refresh interval * is less than the maximum time for token so that while the token is refreshing the current token * continues to be valid. * *

* Adapted from HERE DG class RefreshableTokenAuthenticationProvider. * * @author kmccrack * @author ramsden * @author Adam Stuenkel */ public class RefreshableResponseProvider { private static final Logger LOG = Logger.getLogger(RefreshableResponseProvider.class.getName()); /** * minimum number of seconds to schedule a refresh */ static final long MIN_REFRESH_SECONDS = 30; /** * number of seconds to remove from suggested token timeout */ static final long REFRESH_BACKOFF_SECONDS = 60; /** * number of seconds to wait before refreshing a response when the * attempt to refresh failed */ static final long RETRY_FAIL_SECONDS = 5; private final ResponseRefresher refreshResponseFunction; private final ScheduledExecutorService scheduledExecutorService; /** * If specified, overrides the normal semantics of scheduling the next refresh * close to the expires in from the refresh function, so that instead the * refresh is scheduled at this fixed interval in milliseconds. */ private final Long refreshIntervalMillis; private boolean started; private volatile T refreshResponse; //volatile so consistent across threads private Clock clock; /** * Create a RefreshableResponseProvider with optional refreshIntervalMillis, initialResponse, * and refreshResponseFunction. * * @param refreshIntervalMillis optional. only specify during tests, not in real code. * if you want to ignore the normal response * expires_in, * and instead refresh on a fixed interval not set by the HERE authorization server, * specify this value in milliseconds. * @param initialResponse the initial value of an active response * @param refreshResponseFunction the ability to refresh and get a new response prior to the * previous one expiring. */ public RefreshableResponseProvider( final Long refreshIntervalMillis, final T initialResponse, final ResponseRefresher refreshResponseFunction ) { this(Clock.SYSTEM, refreshIntervalMillis, initialResponse, refreshResponseFunction, getScheduledExecutorServiceSize1() ); } /** * Gets a here-auth-refresh ScheduledExecutorService with 1 core pool thread. * * @return the ScheduledExecutorService size 1 */ public static ScheduledExecutorService getScheduledExecutorServiceSize1() { return Executors.newScheduledThreadPool( 1, new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r, "here-auth-refresh-%s"); thread.setDaemon(true); return thread; } } ); } public RefreshableResponseProvider( final Clock clock, final Long refreshIntervalMillis, final T initialResponse, final ResponseRefresher refreshResponseFunction, final ScheduledExecutorService scheduledExecutorService ) { Objects.requireNonNull(clock, "clock cannot be null"); Objects.requireNonNull(initialResponse, "initialResponse cannot be null"); Objects.requireNonNull(refreshResponseFunction, "refreshResponseFunction cannot be null"); Objects.requireNonNull(scheduledExecutorService, "scheduledExecutorService cannot be null"); // expires_in cannot be null Objects.requireNonNull(initialResponse.getExpiresIn(), "initialResponse.getExpiresIn() cannot be null"); Objects.requireNonNull(initialResponse.getStartTimeMilliseconds(), "initialResponse.getStartTimeMilliseconds() cannot be null"); this.clock = clock; this.refreshIntervalMillis = refreshIntervalMillis; this.refreshResponse = initialResponse; this.refreshResponseFunction = refreshResponseFunction; this.scheduledExecutorService = scheduledExecutorService; this.started = true; scheduleTokenRefresh(nextRefreshInterval()); } /*---- public -------------------------------------------------------------*/ /** * You can call a response refresher to refresh a previous ExpiringResponse, * to keep it unexpired. * * @author kmccrack * * @param the response type that expires periodically */ public interface ResponseRefresher { /** * Invoked when on a specified interval to refresh the token. * * @param previous the previous token. Make sure your implementation * can handle the initial case where previous is null. * @return a new token */ T refresh(T previous); } /** * Various tokens and keys might be set up in the system to be expiring. * This interface allows visibility into the startTime when the object was received, * and the expiresIn the interval after startTime after which the object will be considered expired. * * @author kmccrack * */ public interface ExpiringResponse { /** * Seconds until expiration, at time of receipt of this object. * * @return the seconds until expiration */ Long getExpiresIn(); /** * Current time milliseconds UTC at the time of construction of this object. * In practice, this can generally be considered to be close to the time of receipt of * the object from the server. * * @return the start time in milliseconds UTC when this object was received. */ Long getStartTimeMilliseconds(); } /** * Shutdown the background threads */ public void shutdown() { if (started) { try { LOG.info("Shutting down refresh token thread"); scheduledExecutorService.shutdown(); } finally { started = false; } } } /*---- TokenAuthenticationProvider ----------------------------------------*/ /*@Override public void writeAuthentication(HttpRequest request) { TokenUtil.setBearerToken(request, refreshResponse.getAccessToken()); }*/ /** * Gets the current unexpired response. * Obviously you have to use the data within reason, such as within a few * seconds, on your request. * It is assumed that your code always comes back to this method, for every * API request. * * @return the unexpired response */ public T getUnexpiredResponse() { return refreshResponse; } /*---- private ------------------------------------------------------------*/ /** * Determine the interval the schedule the next refresh */ //@VisibleForTesting long nextRefreshInterval() { if (refreshIntervalMillis != null) { return refreshIntervalMillis; } //remove a few seconds to give time to refresh before token expires return TimeUnit.SECONDS.toMillis( Math.max(refreshResponse.getExpiresIn() - REFRESH_BACKOFF_SECONDS, MIN_REFRESH_SECONDS) ); } /** * Schedule the next refresh with the specified timeout duration * * @param millis time (msec) in the future to schedule the refresh */ protected void scheduleTokenRefresh(long millis) { if (!started) { LOG.info("Refresh token thread shutdown, not scheduling"); return; } LOG.info("Scheduling next token refresh in " + millis + " milliseconds"); clock.schedule(scheduledExecutorService, new Runnable() { @Override public void run() { refreshToken(); } }, millis); } /** * Execute the token refresh. */ private void refreshToken() { LOG.info( String.format( "Refreshing HERE auth token (last successful response %s seconds)", TimeUnit.SECONDS.convert(clock.currentTimeMillis() - refreshResponse.getStartTimeMilliseconds(), TimeUnit.MILLISECONDS) ) ); try { this.refreshResponse = refreshResponseFunction.refresh(refreshResponse); scheduleTokenRefresh(nextRefreshInterval()); } catch (Exception exp) { LOG.warning("Failed to refresh HERE token " + exp); scheduleTokenRefresh( //try again within time window if call failed Math.min(nextRefreshInterval(), TimeUnit.SECONDS.toMillis(RETRY_FAIL_SECONDS)) ); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy