Java tutorial
/* * 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()); } } }