/**
 * Copyright (C) 2015 Zalando SE (http://tech.zalando.com)
 *
 * 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 org.zalando.stups.tokens;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

public class CloseableHttpProvider extends AbstractHttpProvider {

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    private final CloseableHttpClient client;
    private final HttpClientContext localContext;
    private UserCredentials userCredentials;
    private URI accessTokenUri;
    private final HttpHost host;
    private final RequestConfig requestConfig;

    public CloseableHttpProvider(ClientCredentials clientCredentials,
                                 UserCredentials userCredentials,
                                 URI accessTokenUri,
                                 HttpConfig httpConfig) {
        this.userCredentials = userCredentials;
        this.accessTokenUri = accessTokenUri;
        requestConfig = RequestConfig.custom()
                    	        .setSocketTimeout(httpConfig.getSocketTimeout())
                    	        .setConnectTimeout(httpConfig.getConnectTimeout())
                    	        .setConnectionRequestTimeout(httpConfig.getConnectionRequestTimeout())
                    	        .setStaleConnectionCheckEnabled(httpConfig.isStaleConnectionCheckEnabled())
                    	        .build();

        // prepare basic auth credentials
        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(new AuthScope(accessTokenUri.getHost(),
                        accessTokenUri.getPort()),
                new UsernamePasswordCredentials(clientCredentials.getId(), clientCredentials.getSecret()));

        client = HttpClients.custom().setDefaultCredentialsProvider(credentialsProvider)
                .build();

        host = new HttpHost(accessTokenUri.getHost(),
                accessTokenUri.getPort(), accessTokenUri.getScheme());

        // enable basic auth for the request
        final AuthCache authCache = new BasicAuthCache();
        final BasicScheme basicAuth = new BasicScheme();
        authCache.put(host, basicAuth);

        localContext = HttpClientContext.create();
        localContext.setAuthCache(authCache);
    }

    public AccessToken createToken(final AccessTokenConfiguration tokenConfig) throws UnsupportedEncodingException {
        final List<NameValuePair> values = buildParameterList(tokenConfig);

        final HttpPost request = new HttpPost(accessTokenUri);
        request.setEntity(new UrlEncodedFormEntity(values));
        request.setConfig(requestConfig);

        try (final CloseableHttpResponse response = client.execute(host, request, localContext)) {

            // success status code?
            final int status = response.getStatusLine().getStatusCode();
            if (status < 200 || status >= 300) {
                throw AccessTokenEndpointException.from(response);
            }

            // get json response
            final HttpEntity entity = response.getEntity();
            final AccessTokenResponse accessTokenResponse = OBJECT_MAPPER.readValue(EntityUtils.toByteArray(entity),
                    AccessTokenResponse.class);

            // create new access token object
            final Date validUntil = calculateValidUntil(accessTokenResponse);

            return new AccessToken(accessTokenResponse.getAccessToken(), accessTokenResponse.getTokenType(),
                    accessTokenResponse.getExpiresInSeconds(), validUntil);
        } catch (Throwable t) {
            throw new AccessTokenEndpointException(t.getMessage(), t);
        }
    }


    private List<NameValuePair> buildParameterList(final AccessTokenConfiguration tokenConfig) {
        Map<String, String> parameterList = buildParameterMap(tokenConfig, userCredentials);
        List<NameValuePair> nameValuePairs = new ArrayList<>();
        for (Map.Entry<String, String> entry : parameterList.entrySet()) {
            nameValuePairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
        }
        return nameValuePairs;
    }

    @Override
    public void close() throws IOException {
        client.close();
    }
}

@@ -3424,16 +3424,72 @@
 custom()
+%0A                .useSystemProperties()%0A                
 .setDefa

Use system properties when constructing the HttpClient, so that JVM Proxy settings will be used (if present)