com.threewks.thundr.http.service.gae.HttpServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.threewks.thundr.http.service.gae.HttpServiceImpl.java

Source

/*
 * This file is a component of thundr, a software library from 3wks.
 * Read more: http://3wks.github.io/thundr/
 * Copyright (C) 2014 3wks, <thundr@3wks.com.au>
 *
 * 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.threewks.thundr.http.service.gae;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpCookie;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;

import org.apache.http.HttpEntity;

import com.google.appengine.api.urlfetch.FetchOptions;
import com.google.appengine.api.urlfetch.HTTPHeader;
import com.google.appengine.api.urlfetch.HTTPMethod;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.threewks.thundr.http.Authorization;
import com.threewks.thundr.http.ContentType;
import com.threewks.thundr.http.Header;
import com.threewks.thundr.http.service.BaseHttpService;
import com.threewks.thundr.http.service.HttpException;
import com.threewks.thundr.http.service.HttpRequestException;
import com.threewks.thundr.http.service.HttpService;
import com.threewks.thundr.transformer.TransformerManager;
import com.threewks.thundr.util.Streams;

import jodd.util.StringPool;
import jodd.util.StringUtil;

public class HttpServiceImpl extends BaseHttpService<HttpServiceImpl, HttpRequestImpl, HttpResponseImpl>
        implements HttpService {
    private URLFetchService fetchService;

    public HttpServiceImpl(URLFetchService fetchService, TransformerManager transformerManager) {
        super(transformerManager);
        this.fetchService = fetchService;
    }

    public HttpRequestImpl request(String url) {
        return new HttpRequestImpl(this, url);
    }

    @Override
    protected HttpResponseImpl head(HttpRequestImpl request) throws HttpException {
        return headGetDelete(request, HTTPMethod.HEAD);
    }

    @Override
    protected HttpResponseImpl get(HttpRequestImpl request) throws HttpException {
        return headGetDelete(request, HTTPMethod.GET);
    }

    @Override
    protected HttpResponseImpl post(HttpRequestImpl request) throws HttpException {
        return postOrPut(request, HTTPMethod.POST);
    }

    @Override
    protected HttpResponseImpl put(HttpRequestImpl request) throws HttpException {
        return postOrPut(request, HTTPMethod.PUT);
    }

    @Override
    protected HttpResponseImpl delete(HttpRequestImpl request) throws HttpException {
        return headGetDelete(request, HTTPMethod.DELETE);
    }

    protected HttpResponseImpl headGetDelete(HttpRequestImpl request, HTTPMethod headGetDelete) {
        FetchOptions fetchOptions = createFetchOptions(request);
        URL requestUrl = buildGetRequestUrl(request);
        HTTPRequest fetchRequest = new HTTPRequest(requestUrl, headGetDelete, fetchOptions);
        setContentTypeIfNotPresent(request, fetchRequest, ContentType.TextPlain);
        addAuthorization(request, fetchRequest);
        addHeaders(request, fetchRequest);
        addCookies(request, fetchRequest);
        addBody(request, fetchRequest);

        return fetch(fetchRequest, headGetDelete);
    }

    protected HttpResponseImpl postOrPut(HttpRequestImpl request, HTTPMethod postOrPut) {
        FetchOptions fetchOptions = createFetchOptions(request);
        URL requestUrl = buildPostRequestUrl(request);
        HTTPRequest fetchRequest = new HTTPRequest(requestUrl, postOrPut, fetchOptions);
        setContentTypeIfNotPresent(request, fetchRequest, ContentType.ApplicationFormUrlEncoded);

        addAuthorization(request, fetchRequest);
        addHeaders(request, fetchRequest);
        addCookies(request, fetchRequest);
        addBody(request, fetchRequest);

        return fetch(fetchRequest, postOrPut);
    }

    protected HttpResponseImpl fetch(HTTPRequest request, HTTPMethod headGetDelete) {
        try {
            Future<HTTPResponse> fetchAsync = fetchService.fetchAsync(request);
            return new HttpResponseImpl(fetchAsync, this, request.getURL());
        } catch (Exception e) {
            throw new HttpRequestException(e, "Failed to create a %s request: %s", headGetDelete, e.getMessage());
        }
    }

    private FetchOptions createFetchOptions(HttpRequestImpl request) {
        FetchOptions fetchOptions = FetchOptions.Builder.withDefaults();
        fetchOptions = fetchOptions.setDeadline((double) request.getTimeout() / (double) 1000);
        return fetchOptions = request.isFollowRedirects() ? fetchOptions.followRedirects()
                : fetchOptions.doNotFollowRedirects();
    }

    private void addCookies(HttpRequestImpl request, HTTPRequest fetchRequest) {
        for (HttpCookie cookie : request.getCookies()) {
            fetchRequest.addHeader(new HTTPHeader("Cookie", cookie.toString()));
        }
    }

    private void addHeaders(HttpRequestImpl request, HTTPRequest fetchRequest) {
        for (Map.Entry<String, String> header : request.getHeaders().entrySet()) {
            fetchRequest.addHeader(new HTTPHeader(header.getKey(), StringUtil.toString(header.getValue())));
        }
    }

    private void addAuthorization(HttpRequestImpl request, HTTPRequest fetchRequest) {
        String username = request.getUsername();
        String password = request.getPassword();
        String scheme = request.getScheme();
        if (username != null && password != null) {
            if (Authorization.Basic.equalsIgnoreCase(scheme)) {
                fetchRequest.addHeader(
                        new HTTPHeader(Header.Authorization, Authorization.createBasicHeader(username, password)));
            } else {
                throw new HttpRequestException("%s only currently supports %s authorization",
                        HttpServiceImpl.class.getSimpleName(), Authorization.Basic);
            }
        }
    }

    private void addBody(HttpRequestImpl request, HTTPRequest fetchRequest) {
        try {
            Object body = request.getBody();
            byte[] data = null;
            if (isMultipart(request)) {
                HttpEntity multipart = prepareMultipart(request);
                ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
                multipart.writeTo(baos);
                data = baos.toByteArray();
                fetchRequest.setHeader(new HTTPHeader(Header.ContentType, multipart.getContentType().getValue()));
            } else if (body == null) {
                Map<String, Object> parameters = request.getParameters();
                List<String> parameterPairs = new ArrayList<String>(parameters.size());
                for (Map.Entry<String, Object> parameter : parameters.entrySet()) {
                    String pair = String.format("%s=%s", encodeParameter(parameter.getKey()),
                            encodeParameter(StringUtil.toString(parameter.getValue())));
                    parameterPairs.add(pair);
                }
                String form = createFormPostBody(request);
                InputStream is = convertOutgoing(form);
                data = Streams.readBytes(is);
            } else {
                InputStream is = convertOutgoing(body);
                data = Streams.readBytes(is);
            }
            fetchRequest.setPayload(data);
        } catch (Exception e) {
            throw new HttpException(e, "Failed to generate request body: %s", e.getMessage());
        }
    }

    private void setContentTypeIfNotPresent(HttpRequestImpl request, HTTPRequest fetchRequest,
            ContentType contentType) {
        if (!request.getHeaders().containsKey(Header.ContentType)) {
            fetchRequest.setHeader(new HTTPHeader(Header.ContentType, contentType.value()));
        }
    }

    private URL buildGetRequestUrl(HttpRequestImpl request) {
        String requestUrlString = request.getUrl();
        try {
            String queryString = createQueryString(request);
            if (StringUtil.isNotBlank(queryString)) {
                requestUrlString = String.format("%s%s%s", requestUrlString,
                        requestUrlString.contains("?") ? "&" : "?", queryString);
            }
            return new URL(requestUrlString);
        } catch (MalformedURLException e) {
            throw new HttpRequestException(e, "Failed to create destination url - generated '%s': %s",
                    requestUrlString, e.getMessage());
        }
    }

    private URL buildPostRequestUrl(HttpRequestImpl request) {
        String requestUrlString = request.getUrl();
        try {
            return new URL(requestUrlString);
        } catch (MalformedURLException e) {
            throw new HttpRequestException(e, "Failed to create destination url - generated '%s': %s",
                    requestUrlString, e.getMessage());
        }
    }

    protected String createQueryString(HttpRequestImpl request) {
        Map<String, Object> parameters = request.getParameters();
        StringBuilder sb = new StringBuilder();
        for (String key : parameters.keySet()) {
            if (sb.length() > 0) {
                sb.append("&");
            }
            Object value = parameters.get(key);

            if (value != null) {
                if (value instanceof Collection<?> || value.getClass().isArray()) {
                    Collection<?> values = value.getClass().isArray() ? Arrays.asList((Object[]) value)
                            : (Collection<?>) value;
                    boolean first = true;
                    for (Object v : values) {
                        if (!first) {
                            sb.append("&");
                        }
                        first = false;
                        sb.append(encodeParameter(key)).append("=").append(encodeParameter(v.toString()));
                    }
                } else {
                    sb.append(encodeParameter(key)).append("=")
                            .append(encodeParameter(parameters.get(key).toString()));
                }
            }
        }
        return sb.toString();
    }

    protected String encodeParameter(String value) {
        try {
            return URLEncoder.encode(value, StringPool.UTF_8);
        } catch (UnsupportedEncodingException e) {
            throw new HttpRequestException(e, "Unable to format the parameter using %s: %s", StringPool.UTF_8,
                    e.getMessage());
        }
    }
}