class AccessTokenService {

    public AccessToken refreshToken(OAuth2Request request) throws InvalidClientException, InvalidRequestException,
            BadRequestException, ServerException, ExpiredTokenException, InvalidGrantException,
            InvalidScopeException, NotFoundException {

        Reject.ifTrue(isEmpty(request.<String>getParameter(REFRESH_TOKEN)), "Missing parameter, 'refresh_token'");

        final OAuth2ProviderSettings providerSettings = providerSettingsFactory.get(request);
        final OAuth2Uris uris = urisFactory.get(request);
        final ClientRegistration clientRegistration = clientAuthenticator.authenticate(request,
                uris.getTokenEndpoint());

        final String tokenId = request.getParameter(REFRESH_TOKEN);
        final RefreshToken refreshToken = tokenStore.readRefreshToken(request, tokenId);

        if (refreshToken == null) {
            logger.error("Refresh token does not exist for id: " + tokenId);
            throw new InvalidRequestException("RefreshToken does not exist");
        }

        AuditRequestContext.putProperty(OAUTH2_GRANT.toString(), refreshToken.getAuditTrackingId());

        if (!refreshToken.getClientId().equalsIgnoreCase(clientRegistration.getClientId())) {
            logger.error("Refresh Token was issued to a different client id: " + clientRegistration.getClientId());
            throw new InvalidRequestException("Token was issued to a different client");
        }

        if (refreshToken.isExpired()) {
            logger.warn("Refresh Token is expired for id: " + refreshToken.getTokenId());
            throw new InvalidGrantException("grant is invalid");
        }

        AMIdentity id = null;
        try {
            id = identityManager.getResourceOwnerIdentity(refreshToken.getResourceOwnerId(),
                    refreshToken.getRealm());
        } catch (UnauthorizedClientException ex) {
            // The detailed debug log has been output in IdentityManager.
            logger.debug("Error in validating the resource owner of this refresh token.", ex);
        }
        if (id == null) { // it includes the case that the resource owner is inactive.
            logger.warn("Resource owner({}) of this refresh token is not valid any more",
                    refreshToken.getResourceOwnerId());
            throw new InvalidRequestException("Resource owner of this refresh token is not valid any more.");
        }

        final Set<String> scope = splitScope(request.<String>getParameter(SCOPE));
        final String grantType = request.getParameter(GRANT_TYPE);

        final Set<String> tokenScope;
        if (refreshToken.getScope() != null) {
            tokenScope = new TreeSet<String>(refreshToken.getScope());
        } else {
            tokenScope = new TreeSet<String>();
        }

        final Set<String> validatedScope = providerSettings.validateRefreshTokenScope(clientRegistration,
                Collections.unmodifiableSet(scope), Collections.unmodifiableSet(tokenScope),
                request);

        final String validatedClaims = providerSettings.validateRequestedClaims(refreshToken.getClaims());

        RefreshToken newRefreshToken = null;
        if (providerSettings.issueRefreshTokensOnRefreshingToken()) {
            newRefreshToken = tokenStore.createRefreshToken(grantType, clientRegistration.getClientId(),
                    refreshToken.getResourceOwnerId(), refreshToken.getRedirectUri(), refreshToken.getScope(), request,
                    validatedClaims, refreshToken.getAuthGrantId());

            tokenStore.deleteRefreshToken(request, refreshToken.toString());
        }

        final AccessToken accessToken = tokenStore.createAccessToken(grantType, OAuth2Constants.Bearer.BEARER, null,
                refreshToken.getResourceOwnerId(), clientRegistration.getClientId(), refreshToken.getRedirectUri(),
                validatedScope, newRefreshToken == null ? refreshToken : newRefreshToken,
                null, validatedClaims, request);

        if (newRefreshToken != null) {
            accessToken.addExtraData(REFRESH_TOKEN, newRefreshToken.toString());
        }

        providerSettings.additionalDataToReturnFromTokenEndpoint(accessToken, request);

        if (validatedScope != null && !validatedScope.isEmpty()) {
            accessToken.addExtraData(SCOPE, joinScope(validatedScope));
        }

        return accessToken;
    }

    @Inject public  AccessTokenService(Map<String, GrantTypeHandler> grantTypeHandlers,
            final ClientAuthenticator clientAuthenticator, final TokenStore tokenStore,
            final OAuth2ProviderSettingsFactory providerSettingsFactory, OAuth2UrisFactory urisFactory,
            IdentityManager identityManager);

}