<commit_before>/*
 * Copyright (C) 2014-2016 Authlete, 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.authlete.jaxrs.api;


import static javax.ws.rs.core.HttpHeaders.AUTHORIZATION;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.ResponseProcessingException;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.StatusType;
import com.authlete.common.api.AuthleteApi;
import com.authlete.common.api.AuthleteApiException;
import com.authlete.common.conf.AuthleteConfiguration;
import com.authlete.common.dto.AuthorizationFailRequest;
import com.authlete.common.dto.AuthorizationFailResponse;
import com.authlete.common.dto.AuthorizationIssueRequest;
import com.authlete.common.dto.AuthorizationIssueResponse;
import com.authlete.common.dto.AuthorizationRequest;
import com.authlete.common.dto.AuthorizationResponse;
import com.authlete.common.dto.Client;
import com.authlete.common.dto.ClientListResponse;
import com.authlete.common.dto.IntrospectionRequest;
import com.authlete.common.dto.IntrospectionResponse;
import com.authlete.common.dto.RevocationRequest;
import com.authlete.common.dto.RevocationResponse;
import com.authlete.common.dto.Service;
import com.authlete.common.dto.ServiceListResponse;
import com.authlete.common.dto.TokenCreateRequest;
import com.authlete.common.dto.TokenCreateResponse;
import com.authlete.common.dto.TokenFailRequest;
import com.authlete.common.dto.TokenFailResponse;
import com.authlete.common.dto.TokenIssueRequest;
import com.authlete.common.dto.TokenIssueResponse;
import com.authlete.common.dto.TokenRequest;
import com.authlete.common.dto.TokenResponse;
import com.authlete.common.dto.TokenUpdateRequest;
import com.authlete.common.dto.TokenUpdateResponse;
import com.authlete.common.dto.UserInfoIssueRequest;
import com.authlete.common.dto.UserInfoIssueResponse;
import com.authlete.common.dto.UserInfoRequest;
import com.authlete.common.dto.UserInfoResponse;
import com.authlete.common.web.BasicCredentials;


/**
 * The implementation of {@link AuthleteApi} using JAX-RS 2.0 client API.
 *
 * @author <NAME>
 */
public class AuthleteApiImpl implements AuthleteApi
{
    private interface AuthleteApiCall<TResponse>
    {
        TResponse call();
    }


    private static final String AUTH_AUTHORIZATION_API_PATH        = "/api/auth/authorization";
    private static final String AUTH_AUTHORIZATION_FAIL_API_PATH   = "/api/auth/authorization/fail";
    private static final String AUTH_AUTHORIZATION_ISSUE_API_PATH  = "/api/auth/authorization/issue";
    private static final String AUTH_TOKEN_API_PATH                = "/api/auth/token";
    private static final String AUTH_TOKEN_CREATE_API_PATH         = "/api/auth/token/create";
    private static final String AUTH_TOKEN_FAIL_API_PATH           = "/api/auth/token/fail";
    private static final String AUTH_TOKEN_ISSUE_API_PATH          = "/api/auth/token/issue";
    private static final String AUTH_TOKEN_UPDATE_API_PATH         = "/api/auth/token/update";
    private static final String AUTH_REVOCATION_API_PATH           = "/api/auth/revocation";
    private static final String AUTH_USERINFO_API_PATH             = "/api/auth/userinfo";
    private static final String AUTH_USERINFO_ISSUE_API_PATH       = "/api/auth/userinfo/issue";
    private static final String AUTH_INTROSPECTION_API_PATH        = "/api/auth/introspection";
    private static final String SERVICE_CONFIGURATION_API_PATH     = "/api/service/configuration";
    private static final String SERVICE_CREATE_API_PATH            = "/api/service/create";
    private static final String SERVICE_DELETE_API_PATH            = "/api/service/delete/%d";
    private static final String SERVICE_GET_API_PATH               = "/api/service/get/%d";
    private static final String SERVICE_GET_LIST_API_PATH          = "/api/service/get/list";
    private static final String SERVICE_JWKS_GET_API_PATH          = "/api/service/jwks/get";
    private static final String SERVICE_UPDATE_API_PATH            = "/api/service/update/%d";
    private static final String CLIENT_CREATE_API_PATH             = "/api/client/create";
    private static final String CLIENT_DELETE_API_PATH             = "/api/client/delete/%d";
    private static final String CLIENT_GET_API_PATH                = "/api/client/get/%d";
    private static final String CLIENT_GET_LIST_API_PATH           = "/api/client/get/list";
    private static final String CLIENT_UPDATE_API_PATH             = "/api/client/update/%d";
    private static final String REQUESTABLE_SCOPES_DELETE_API_PATH = "/api/client/extension/requestable_scopes/delete/%d";
    private static final String REQUESTABLE_SCOPES_GET_API_PATH    = "/api/client/extension/requestable_scopes/get/%d";
    private static final String REQUESTABLE_SCOPES_UPDATE_API_PATH = "/api/client/extension/requestable_scopes/update/%d";


    private final WebTarget mTarget;
    private final String mServiceOwnerAuth;
    private final String mServiceAuth;


    /**
     * The constructor with an instance of {@link AuthleteConfiguration}.
     *
     * <p>
     * The existence of a constructor of this type is a required by
     * {@link com.authlete.common.api.AuthleteApiFactory AuthleteApiFactory}.
     * </p>
     *
     * @param configuration
     *         An instance of {@link AuthleteConfiguration}.
     */
    public AuthleteApiImpl(AuthleteConfiguration configuration)
    {
        if (configuration == null)
        {
            throw new IllegalArgumentException("configuration is null.");
        }

        mTarget           = ClientBuilder.newClient().target(configuration.getBaseUrl());
        mServiceOwnerAuth = createServiceOwnerCredentials(configuration).format();
        mServiceAuth      = createServiceCredentials(configuration).format();
    }


    /**
     * Create a {@link BasicCredentials} for the service owner.
     */
    private BasicCredentials createServiceOwnerCredentials(AuthleteConfiguration configuration)
    {
        String key    = configuration.getServiceOwnerApiKey();
        String secret = configuration.getServiceOwnerApiSecret();

        return new BasicCredentials(key, secret);
    }


    /**
     * Create a {@link BasicCredentials} for the service.
     */
    private BasicCredentials createServiceCredentials(AuthleteConfiguration configuration)
    {
        String key    = configuration.getServiceApiKey();
        String secret = configuration.getServiceApiSecret();

        return new BasicCredentials(key, secret);
    }


    /**
     * Execute an Authlete API call.
     */
    private <TResponse> TResponse executeApiCall(AuthleteApiCall<TResponse> apiCall) throws AuthleteApiException
    {
        try
        {
            // Call the Authlete API.
            return apiCall.call();
        }
        catch (WebApplicationException e)
        {
            // Throw an exception with HTTP response information.
            throw createApiException(e, e.getResponse());
        }
        catch (ResponseProcessingException e)
        {
            // Throw an exception with HTTP response information.
            throw createApiException(e, e.getResponse());
        }
        catch (Throwable t)
        {
            // Throw an exception without HTTP response information.
            throw createApiException(t, null);
        }
    }


    /**
     * Create an {@link AuthleteApiException} instance.
     */
    private AuthleteApiException createApiException(Throwable cause, Response response)
    {
        // Error message.
        String message = cause.getMessage();

        if (response == null)
        {
            // Create an exception without HTTP response information.
            return new AuthleteApiException(message, cause);
        }

        // Status code and status message.
        int statusCode = 0;
        String statusMessage = null;

        // Get the status information.
        StatusType type = response.getStatusInfo();
        if (type != null)
        {
            statusCode    = type.getStatusCode();
            statusMessage = type.getReasonPhrase();
        }

        // Response body.
        String responseBody = null;

        // If the response has response body.
        if (response.hasEntity())
        {
            // Get the response body.
            responseBody = extractResponseBody(response);
        }

        // Response headers.
        Map<String, List<String>> headers = response.getStringHeaders();

        // Create an exception with HTTP response information.
        return new AuthleteApiException(message, cause, statusCode, statusMessage, responseBody, headers);
    }


    private String extractResponseBody(Response response)
    {
        try
        {
            // Convert the entity body into a String.
            return response.readEntity(String.class);
        }
        catch (Exception e)
        {
            // Failed to convert the entity body into a String.
            e.printStackTrace();

            // Response body is not available.
            return null;
        }
    }


    private <TResponse> TResponse callGetApi(
            String auth, String path, Class<TResponse> responseClass, Map<String, Object[]> params)
    {
        WebTarget webTarget = mTarget.path(path);

        if (params != null)
        {
            for (Map.Entry<String, Object[]> param : params.entrySet())
            {
                webTarget = webTarget.queryParam(param.getKey(), param.getValue());
            }
        }

        return webTarget
                .request(APPLICATION_JSON_TYPE)
                .header(AUTHORIZATION, auth)
                .get(responseClass);
    }


    private <TResponse> TResponse callServiceOwnerGetApi(
            String path, Class<TResponse> responseClass, Map<String, Object[]> params)
    {
        return callGetApi(mServiceOwnerAuth, path, responseClass, params);
    }


    private <TResponse> TResponse callServiceGetApi(
            String path, Class<TResponse> responseClass, Map<String, Object[]> params)
    {
        return callGetApi(mServiceAuth, path, responseClass, params);
    }


    private Void callDeleteApi(String auth, String path)
    {
        mTarget
            .path(path)
            .request()
            .header(AUTHORIZATION, auth)
            .delete();

        return null;
    }


    private Void callServiceOwnerDeleteApi(String path)
    {
        return callDeleteApi(mServiceOwnerAuth, path);
    }


    private Void callServiceDeleteApi(String path)
    {
        return callDeleteApi(mServiceAuth, path);
    }


    private <TResponse> TResponse callPostApi(String auth, String path, Object request, Class<TResponse> responseClass)
    {
        return mTarget
                .path(path)
                .request(APPLICATION_JSON_TYPE)
                .header(AUTHORIZATION, auth)
                .post(Entity.entity(request, APPLICATION_JSON_TYPE), responseClass);
    }


    private <TResponse> TResponse callServiceOwnerPostApi(String path, Object request, Class<TResponse> responseClass)
    {
        return callPostApi(mServiceOwnerAuth, path, request, responseClass);
    }


    private <TResponse> TResponse callServicePostApi(String path, Object request, Class<TResponse> responseClass)
    {
        return callPostApi(mServiceAuth, path, request, responseClass);
    }


    private static abstract class ApiCaller<TResponse> implements AuthleteApiCall<TResponse>
    {
        protected final String mPath;
        protected final Object mRequest;
        protected final Class<TResponse> mResponseClass;
        protected final Map<String, Object[]> mParams = new LinkedHashMap<>();


        ApiCaller(Class<TResponse> responseClass, Object request, String path)<commit_msg>Changed "application/json" to "application/json;UTF-8" in callPostApi() defined in AuthleteApiImpl.<commit_after>/*
 * Copyright (C) 2014-2017 Authlete, 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.authlete.jaxrs.api;


import static javax.ws.rs.core.HttpHeaders.AUTHORIZATION;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.ResponseProcessingException;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.StatusType;
import com.authlete.common.api.AuthleteApi;
import com.authlete.common.api.AuthleteApiException;
import com.authlete.common.conf.AuthleteConfiguration;
import com.authlete.common.dto.AuthorizationFailRequest;
import com.authlete.common.dto.AuthorizationFailResponse;
import com.authlete.common.dto.AuthorizationIssueRequest;
import com.authlete.common.dto.AuthorizationIssueResponse;
import com.authlete.common.dto.AuthorizationRequest;
import com.authlete.common.dto.AuthorizationResponse;
import com.authlete.common.dto.Client;
import com.authlete.common.dto.ClientListResponse;
import com.authlete.common.dto.IntrospectionRequest;
import com.authlete.common.dto.IntrospectionResponse;
import com.authlete.common.dto.RevocationRequest;
import com.authlete.common.dto.RevocationResponse;
import com.authlete.common.dto.Service;
import com.authlete.common.dto.ServiceListResponse;
import com.authlete.common.dto.TokenCreateRequest;
import com.authlete.common.dto.TokenCreateResponse;
import com.authlete.common.dto.TokenFailRequest;
import com.authlete.common.dto.TokenFailResponse;
import com.authlete.common.dto.TokenIssueRequest;
import com.authlete.common.dto.TokenIssueResponse;
import com.authlete.common.dto.TokenRequest;
import com.authlete.common.dto.TokenResponse;
import com.authlete.common.dto.TokenUpdateRequest;
import com.authlete.common.dto.TokenUpdateResponse;
import com.authlete.common.dto.UserInfoIssueRequest;
import com.authlete.common.dto.UserInfoIssueResponse;
import com.authlete.common.dto.UserInfoRequest;
import com.authlete.common.dto.UserInfoResponse;
import com.authlete.common.web.BasicCredentials;


/**
 * The implementation of {@link AuthleteApi} using JAX-RS 2.0 client API.
 *
 * @author <NAME>
 */
public class AuthleteApiImpl implements AuthleteApi
{
    // "application/json;charset=UTF-8"
    private static final MediaType JSON_UTF8_TYPE = APPLICATION_JSON_TYPE.withCharset("UTF-8");


    private interface AuthleteApiCall<TResponse>
    {
        TResponse call();
    }


    private static final String AUTH_AUTHORIZATION_API_PATH        = "/api/auth/authorization";
    private static final String AUTH_AUTHORIZATION_FAIL_API_PATH   = "/api/auth/authorization/fail";
    private static final String AUTH_AUTHORIZATION_ISSUE_API_PATH  = "/api/auth/authorization/issue";
    private static final String AUTH_TOKEN_API_PATH                = "/api/auth/token";
    private static final String AUTH_TOKEN_CREATE_API_PATH         = "/api/auth/token/create";
    private static final String AUTH_TOKEN_FAIL_API_PATH           = "/api/auth/token/fail";
    private static final String AUTH_TOKEN_ISSUE_API_PATH          = "/api/auth/token/issue";
    private static final String AUTH_TOKEN_UPDATE_API_PATH         = "/api/auth/token/update";
    private static final String AUTH_REVOCATION_API_PATH           = "/api/auth/revocation";
    private static final String AUTH_USERINFO_API_PATH             = "/api/auth/userinfo";
    private static final String AUTH_USERINFO_ISSUE_API_PATH       = "/api/auth/userinfo/issue";
    private static final String AUTH_INTROSPECTION_API_PATH        = "/api/auth/introspection";
    private static final String SERVICE_CONFIGURATION_API_PATH     = "/api/service/configuration";
    private static final String SERVICE_CREATE_API_PATH            = "/api/service/create";
    private static final String SERVICE_DELETE_API_PATH            = "/api/service/delete/%d";
    private static final String SERVICE_GET_API_PATH               = "/api/service/get/%d";
    private static final String SERVICE_GET_LIST_API_PATH          = "/api/service/get/list";
    private static final String SERVICE_JWKS_GET_API_PATH          = "/api/service/jwks/get";
    private static final String SERVICE_UPDATE_API_PATH            = "/api/service/update/%d";
    private static final String CLIENT_CREATE_API_PATH             = "/api/client/create";
    private static final String CLIENT_DELETE_API_PATH             = "/api/client/delete/%d";
    private static final String CLIENT_GET_API_PATH                = "/api/client/get/%d";
    private static final String CLIENT_GET_LIST_API_PATH           = "/api/client/get/list";
    private static final String CLIENT_UPDATE_API_PATH             = "/api/client/update/%d";
    private static final String REQUESTABLE_SCOPES_DELETE_API_PATH = "/api/client/extension/requestable_scopes/delete/%d";
    private static final String REQUESTABLE_SCOPES_GET_API_PATH    = "/api/client/extension/requestable_scopes/get/%d";
    private static final String REQUESTABLE_SCOPES_UPDATE_API_PATH = "/api/client/extension/requestable_scopes/update/%d";


    private final WebTarget mTarget;
    private final String mServiceOwnerAuth;
    private final String mServiceAuth;


    /**
     * The constructor with an instance of {@link AuthleteConfiguration}.
     *
     * <p>
     * The existence of a constructor of this type is a required by
     * {@link com.authlete.common.api.AuthleteApiFactory AuthleteApiFactory}.
     * </p>
     *
     * @param configuration
     *         An instance of {@link AuthleteConfiguration}.
     */
    public AuthleteApiImpl(AuthleteConfiguration configuration)
    {
        if (configuration == null)
        {
            throw new IllegalArgumentException("configuration is null.");
        }

        mTarget           = ClientBuilder.newClient().target(configuration.getBaseUrl());
        mServiceOwnerAuth = createServiceOwnerCredentials(configuration).format();
        mServiceAuth      = createServiceCredentials(configuration).format();
    }


    /**
     * Create a {@link BasicCredentials} for the service owner.
     */
    private BasicCredentials createServiceOwnerCredentials(AuthleteConfiguration configuration)
    {
        String key    = configuration.getServiceOwnerApiKey();
        String secret = configuration.getServiceOwnerApiSecret();

        return new BasicCredentials(key, secret);
    }


    /**
     * Create a {@link BasicCredentials} for the service.
     */
    private BasicCredentials createServiceCredentials(AuthleteConfiguration configuration)
    {
        String key    = configuration.getServiceApiKey();
        String secret = configuration.getServiceApiSecret();

        return new BasicCredentials(key, secret);
    }


    /**
     * Execute an Authlete API call.
     */
    private <TResponse> TResponse executeApiCall(AuthleteApiCall<TResponse> apiCall) throws AuthleteApiException
    {
        try
        {
            // Call the Authlete API.
            return apiCall.call();
        }
        catch (WebApplicationException e)
        {
            // Throw an exception with HTTP response information.
            throw createApiException(e, e.getResponse());
        }
        catch (ResponseProcessingException e)
        {
            // Throw an exception with HTTP response information.
            throw createApiException(e, e.getResponse());
        }
        catch (Throwable t)
        {
            // Throw an exception without HTTP response information.
            throw createApiException(t, null);
        }
    }


    /**
     * Create an {@link AuthleteApiException} instance.
     */
    private AuthleteApiException createApiException(Throwable cause, Response response)
    {
        // Error message.
        String message = cause.getMessage();

        if (response == null)
        {
            // Create an exception without HTTP response information.
            return new AuthleteApiException(message, cause);
        }

        // Status code and status message.
        int statusCode = 0;
        String statusMessage = null;

        // Get the status information.
        StatusType type = response.getStatusInfo();
        if (type != null)
        {
            statusCode    = type.getStatusCode();
            statusMessage = type.getReasonPhrase();
        }

        // Response body.
        String responseBody = null;

        // If the response has response body.
        if (response.hasEntity())
        {
            // Get the response body.
            responseBody = extractResponseBody(response);
        }

        // Response headers.
        Map<String, List<String>> headers = response.getStringHeaders();

        // Create an exception with HTTP response information.
        return new AuthleteApiException(message, cause, statusCode, statusMessage, responseBody, headers);
    }


    private String extractResponseBody(Response response)
    {
        try
        {
            // Convert the entity body into a String.
            return response.readEntity(String.class);
        }
        catch (Exception e)
        {
            // Failed to convert the entity body into a String.
            e.printStackTrace();

            // Response body is not available.
            return null;
        }
    }


    private <TResponse> TResponse callGetApi(
            String auth, String path, Class<TResponse> responseClass, Map<String, Object[]> params)
    {
        WebTarget webTarget = mTarget.path(path);

        if (params != null)
        {
            for (Map.Entry<String, Object[]> param : params.entrySet())
            {
                webTarget = webTarget.queryParam(param.getKey(), param.getValue());
            }
        }

        return webTarget
                .request(APPLICATION_JSON_TYPE)
                .header(AUTHORIZATION, auth)
                .get(responseClass);
    }


    private <TResponse> TResponse callServiceOwnerGetApi(
            String path, Class<TResponse> responseClass, Map<String, Object[]> params)
    {
        return callGetApi(mServiceOwnerAuth, path, responseClass, params);
    }


    private <TResponse> TResponse callServiceGetApi(
            String path, Class<TResponse> responseClass, Map<String, Object[]> params)
    {
        return callGetApi(mServiceAuth, path, responseClass, params);
    }


    private Void callDeleteApi(String auth, String path)
    {
        mTarget
            .path(path)
            .request()
            .header(AUTHORIZATION, auth)
            .delete();

        return null;
    }


    private Void callServiceOwnerDeleteApi(String path)
    {
        return callDeleteApi(mServiceOwnerAuth, path);
    }


    private Void callServiceDeleteApi(String path)
    {
        return callDeleteApi(mServiceAuth, path);
    }


    private <TResponse> TResponse callPostApi(String auth, String path, Object request, Class<TResponse> responseClass)
    {
        return mTarget
                .path(path)
                .request(APPLICATION_JSON_TYPE)
                .header(AUTHORIZATION, auth)
                .post(Entity.entity(request, JSON_UTF8_TYPE), responseClass);
    }


    private <TResponse> TResponse callServiceOwnerPostApi(String path, Object request, Class<TResponse> responseClass)
    {
        return callPostApi(mServiceOwnerAuth, path, request, responseClass);
    }


    private <TResponse> TResponse callServicePostApi(String path, Object request, Class<TResponse> responseClass)
    {
        return callPostApi(mServiceAuth, path, request, responseClass);
    }


    private static abstract class ApiCaller<TResponse> implements AuthleteApiCall<TResponse>
    {
        protected final String mPath;
        protected final Object mRequest;
        protected final Class<TResponse> mResponseClass;
        protected final Map<String, Object[]> mParams = new LinkedHashMap<>();


        ApiCaller(Class<TResponse> responseClass, Object request, String path)
{'fraction_non_alphanumeric': 0.08702494295381226, 'fraction_numerical': 0.0013769769454717129, 'mean_word_length': 2.8772117144600364, 'pattern_counts': {'":': 0, '<': 63, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6813376', 'n_tokens_mistral': 6872, 'n_tokens_neox': 6717, 'n_words': 1664}