Java tutorial
// Copyright 2016 Yahoo Inc. // Licensed under the terms of the Apache license. Please see LICENSE.md file distributed with this work for terms. package com.yahoo.parsec.clients; import com.ning.http.client.*; import com.ning.http.client.cookie.Cookie; import com.ning.http.client.multipart.ByteArrayPart; import com.ning.http.client.multipart.FilePart; import com.ning.http.client.multipart.Part; import com.ning.http.client.multipart.StringPart; import com.ning.http.client.uri.Uri; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import javax.ws.rs.core.NewCookie; import javax.ws.rs.core.Response; import java.io.File; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; import java.util.*; /** * Request class representing HTTP request. * * @author sho */ public class ParsecAsyncHttpRequest { /** * Flag for cirtical get. */ private final boolean criticalGet; /** * Max retries. */ private final int maxRetries; /** * Response status codes tp retry. */ private final List<Integer> retryStatusCodes; /** * Cookies. */ private final List<NewCookie> cookies; /** * Form params. */ private final Map<String, List<String>> formParams; /** * Query params. */ private final Map<String, List<String>> queryParams; /** * Headers. */ private final Map<String, List<String>> headers; /** * Ning request. */ private final Request ningRequest; /** * Flag to append Accept-Encoding: deflate header. */ private final boolean acceptCompression; /** * Unused constructor. */ @SuppressWarnings("unused") ParsecAsyncHttpRequest() { throw new UnsupportedOperationException(); } /** * Private constructor. * @param builder builder */ private ParsecAsyncHttpRequest(final Builder builder) { criticalGet = builder.criticalGet; maxRetries = builder.maxRetries; retryStatusCodes = builder.retryStatusCodes; ningRequest = builder.ningRequestBuilder.build(); acceptCompression = builder.acceptCompression; headers = new FluentCaseInsensitiveStringsMap(ningRequest.getHeaders()); cookies = ParsecHttpUtil.getCookies(ningRequest.getCookies()); formParams = ParsecHttpUtil.getParamsMap(ningRequest.getFormParams()); queryParams = ParsecHttpUtil.getParamsMap(ningRequest.getQueryParams()); } @Override public boolean equals(Object object) { if (object == this) { return true; } if (!(object instanceof ParsecAsyncHttpRequest)) { return false; } // Compare local fields except Ning request if (!EqualsBuilder.reflectionEquals(this, object, new String[] { "ningRequest" })) { return false; } // Compare Ning request fields return ParsecEqualsUtil.ningRequestEquals(ningRequest, ((ParsecAsyncHttpRequest) object).ningRequest); } @Override public int hashCode() { // Calculate Ning request hash by custom algorithm int ningRequestHash = new HashCodeBuilder().append(ningRequest.getBodyEncoding()) .append(ningRequest.getContentLength()).append(ningRequest.getFollowRedirect()) .append(ningRequest.getMethod()).append(ningRequest.getRangeOffset()) .append(ningRequest.getRequestTimeout()).append(ningRequest.getStringData()) .append(ningRequest.getUrl()).append(ningRequest.getVirtualHost()).hashCode(); return HashCodeBuilder.reflectionHashCode(this, new String[] { "ningRequest" }) + ningRequestHash; } /** * Get body. * * @return Body */ public String getBody() { return ningRequest.getStringData(); } /** * Get body. * * @return Body */ public byte[] getBodyByte() { return ningRequest.getByteData(); } /** * Get body encoding. * * @return Body encoding */ public String getBodyEncoding() { return ningRequest.getBodyEncoding(); } /** * Get content length. * * @return Content length */ public long getContentLength() { return ningRequest.getContentLength(); } /** * Get cookies. * * @return Cookies */ public Collection<NewCookie> getCookies() { return Collections.unmodifiableCollection(cookies); } /** * Get form parameters. * * @return Form parameters */ public Map<String, List<String>> getFormParams() { return Collections.unmodifiableMap(formParams); } /** * Get header string. * * @param headerKey Header key to get * @return Header value */ public String getHeaderString(String headerKey) { if (headers.containsKey(headerKey)) { String arrayString = headers.get(headerKey).toString(); return arrayString.substring(1, arrayString.length() - 1); } return null; } /** * Get headers. * * @return Headers */ public Map<String, List<String>> getHeaders() { return Collections.unmodifiableMap(headers); } /** * Get max retries. * * @return Max retries */ public int getMaxRetries() { return maxRetries; } /** * Get method. * * @return Method */ public String getMethod() { return ningRequest.getMethod(); } /** * Get Ning {@link Request}. * * @return Ning {@link Request} */ Request getNingRequest() { return ningRequest; } /** * Get proxy server. * * @return Proxy server */ public String getProxyServer() { ProxyServer proxyServer = ningRequest.getProxyServer(); if (proxyServer == null) { return null; } return proxyServer.toString(); } /** * Get query parameters. * * @return Query parameters */ public Map<String, List<String>> getQueryParams() { return Collections.unmodifiableMap(queryParams); } /** * Get range offset. * * @return Range offset */ public long getRangeOffset() { return ningRequest.getRangeOffset(); } /** * Get request timeout. * * @return Request timeout */ public int getRequestTimeout() { return ningRequest.getRequestTimeout(); } /** * Get retry status codes. * * @return List of retry status codes */ public List<Integer> getRetryStatusCodes() { return Collections.unmodifiableList(retryStatusCodes); } /** * Get URI. * * @return {@link URI} * @throws URISyntaxException Uri syntax exception */ public URI getUri() throws URISyntaxException { return ningRequest.getUri().toJavaNetURI(); } /** * Get url. * * @return Url */ public String getUrl() { return ningRequest.getUrl(); } /** * Get virtual host. * * @return Virtual host */ public String getVirtualHost() { return ningRequest.getVirtualHost(); } /** * Is critical get. * * @return Whether request is critical get */ public boolean isCriticalGet() { return criticalGet; } /** * Is follow redirect. * * @return Whether request will follow redirect */ public boolean isFollowRedirect() { return ningRequest.getFollowRedirect(); } /** * Is accept compression. * * @return Whether request will accept compression */ public boolean isAcceptCompression() { return acceptCompression; } /** * Static Builder class for {@link ParsecAsyncHttpRequest}. * * @author sho */ public static class Builder { /** * Default max retries. */ private static final int DEFAULT_MAX_RETRIES = 3; /** * Accept encoding header. */ private static final String ACCEPT_ENCODING_HEADER = "Accept-Encoding"; /** * Encoding type. */ private static final String COMPRESSION_TYPE = "deflate"; /** * Flag for critical get. */ private boolean criticalGet; /** * Flag for follow redirect. */ private boolean followRedirect; /** * Headers. */ private FluentCaseInsensitiveStringsMap headers; /** * Content length. */ private int contentLength; /** * Max retries. */ private int maxRetries; /** * Request timeout. */ private int requestTimeout; /** * Cookies. */ private List<NewCookie> cookies; /** * Response status codes to retry. */ private List<Integer> retryStatusCodes; /** * Query params. */ private List<Param> queryParams; /** * Form params. */ private List<Param> formParams; /** * Range offset. */ private long rangeOffset; /** * Proxy server. */ private ProxyServer proxyServer; /** * Ning request builder. */ private RequestBuilder ningRequestBuilder; /** * Body. */ private String body; /** * Body for binary. */ private byte[] byteBody; /** * Body encoding. */ private String bodyEncoding; /** * Method. */ private String method; /** * Virtual host. */ private String virtualHost; /** * Uri. */ private URI uri; /** * Flag to append Accept-Encoding: deflate header. */ private boolean acceptCompression; /** * Body parts. */ private List<Part> bodyParts; /** * Constructor. */ public Builder() { // Init default values method = "GET"; maxRetries = DEFAULT_MAX_RETRIES; headers = new FluentCaseInsensitiveStringsMap(); retryStatusCodes = new ArrayList<>(); uri = URI.create("http://localhost"); acceptCompression = true; bodyParts = new ArrayList<>(); } /** * Add cookie. * * @param cookie Cookie * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder addCookie(NewCookie cookie) { if (cookies == null) { cookies = new ArrayList<>(1); } cookies.add(cookie); return this; } /** * Add form parameter. * * @param key Form key * @param value Form value * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder addFormParam(String key, String value) { if (formParams == null) { formParams = new ArrayList<>(1); } formParams.add(new Param(key, value)); body = null; return this; } /** * Add header. * * @param name Header name * @param value Header value * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder addHeader(String name, String value) { headers.add(name, value); return this; } /** * Add query parameter. * * @param name Query name * @param value Query value * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder addQueryParam(String name, String value) { if (queryParams == null) { queryParams = new ArrayList<>(1); } queryParams.add(new Param(name, value)); return this; } /** * Add retry status code. * * @param statusCode Retry status code to add * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder addRetryStatusCode(int statusCode) { if (Response.Status.fromStatusCode(statusCode) != Response.Status.OK && !retryStatusCodes.contains(statusCode)) { retryStatusCodes.add(statusCode); } return this; } /** * add body part for multipart/form-data upload. * @param name part name * @param value part value * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder addBodyPart(String name, String value) { Part part = new StringPart(name, value); bodyParts.add(part); return this; } /** * add body part for multipart/form-data upload. * @param name part name * @param value part value * @param contentType content type * @param charset content charset * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder addBodyPart(String name, String value, String contentType, Charset charset) { Part part = new StringPart(name, value, contentType, charset); bodyParts.add(part); return this; } /** * add body part for multipart/form-data upload. * @param name part name * @param file part file data * @param contentType content type * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder addBodyPart(String name, File file, String contentType) { Part part = new FilePart(name, file, contentType); bodyParts.add(part); return this; } /** * add body part for multipart/form-data upload. * @param name part name * @param bytes part file data * @param contentType content type * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder addBodyPart(String name, byte[] bytes, String contentType) { Part part = new ByteArrayPart(name, bytes, contentType); bodyParts.add(part); return this; } /** * Buile new {@link ParsecAsyncHttpRequest} instance. * * @return {@link ParsecAsyncHttpRequest} */ @SuppressWarnings("PMD.NPathComplexity") public ParsecAsyncHttpRequest build() { ningRequestBuilder = new RequestBuilder(method).setContentLength(contentLength) .setFollowRedirects(followRedirect).setHeaders(headers).setProxyServer(proxyServer) .setRangeOffset(rangeOffset).setRequestTimeout(requestTimeout).setVirtualHost(virtualHost) .setUri(new Uri(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), uri.getPath(), uri.getQuery())); if (body != null) { ningRequestBuilder.setBody(body); } else if (byteBody != null) { ningRequestBuilder.setBody(byteBody); } if (bodyEncoding != null) { ningRequestBuilder.setBodyEncoding(bodyEncoding); } if (cookies != null && !cookies.isEmpty()) { for (NewCookie cookie : cookies) { ningRequestBuilder .addCookie(new Cookie(cookie.getName(), cookie.getValue(), false, cookie.getDomain(), cookie.getPath(), cookie.getMaxAge(), cookie.isSecure(), cookie.isHttpOnly())); } } if (formParams != null) { ningRequestBuilder.setFormParams(formParams); } if (queryParams != null) { ningRequestBuilder.setQueryParams(queryParams); } if (acceptCompression && !headers.containsKey(ACCEPT_ENCODING_HEADER)) { ningRequestBuilder.addHeader(ACCEPT_ENCODING_HEADER, COMPRESSION_TYPE); } for (Part part : bodyParts) { ningRequestBuilder.addBodyPart(part); } return new ParsecAsyncHttpRequest(this); } /** * Get {@link Param} {@link List} from {@literal Map<String, List<String>> map}. * @param map Parameters * @return List<{@link Param}> */ private List<Param> mapToParamList(Map<String, List<String>> map) { if (map == null) { return null; } List<Param> params = new ArrayList<>(map.size()); map.entrySet().stream().forEach(entry -> entry.getValue().stream() .forEach(value -> params.add(new Param(entry.getKey(), value)))); return params; } /** * Remove retry status code. * * @param statusCode Retry status code to remove * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder removeRetryStatusCode(int statusCode) { retryStatusCodes.remove((Integer) statusCode); return this; } /** * Set body. * * @param body Body * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setBody(String body) { this.body = body; formParams = null; this.byteBody = null; return this; } /** * Set body. * * @param body Body * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setBody(byte[] body) { this.byteBody = Arrays.copyOf(body, body.length); formParams = null; this.body = null; return this; } /** * Set body encoding. * * @param bodyEncoding Body encoding * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setBodyEncoding(String bodyEncoding) { this.bodyEncoding = bodyEncoding; return this; } /** * Set content length. * * @param contentLength Content length * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setContentLength(int contentLength) { this.contentLength = contentLength; return this; } /** * Set cookies. * * @param cookies {@link NewCookie} {@link List} * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setCookies(List<NewCookie> cookies) { this.cookies = cookies; return this; } /** * Set critical get. * * @param criticalGet Critical get * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setCriticalGet(boolean criticalGet) { this.criticalGet = criticalGet; return this; } /** * Set follow redirects. * * @param followRedirects Follow redirects * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setFollowRedirects(boolean followRedirects) { this.followRedirect = followRedirects; return this; } /** * Set form paramemters. * * @param params Form parameters * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setFormParams(Map<String, List<String>> params) { formParams = mapToParamList(params); return this; } /** * Set header. * * @param name Header name * @param value Header value * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setHeader(String name, String value) { headers.replaceWith(name, value); return this; } /** * Set headers. * * @param headers Headers * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setHeaders(Map<String, Collection<String>> headers) { if (headers == null) { this.headers.clear(); } else { this.headers = new FluentCaseInsensitiveStringsMap(headers); } return this; } /** * Set max retries. * * @param maxRetries Max retries * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setMaxRetries(int maxRetries) { this.maxRetries = maxRetries; return this; } /** * Set method. * * @param method Method * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setMethod(String method) { this.method = method; return this; } /** * Set proxy server. * * @param protocol {@link ParsecNetProtocol} protocol * @param host Host * @param port port * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setProxyServer(ParsecNetProtocol protocol, String host, int port) { if (protocol == ParsecNetProtocol.HTTPS) { proxyServer = new ProxyServer(ProxyServer.Protocol.HTTPS, host, port); } else { proxyServer = new ProxyServer(ProxyServer.Protocol.HTTP, host, port); } return this; } /** * Set query parameters. * * @param params Query parameters * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setQueryParams(Map<String, List<String>> params) { queryParams = mapToParamList(params); return this; } /** * Set range offset. * * @param rangeOffset Range offset * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setRangeOffset(long rangeOffset) { this.rangeOffset = rangeOffset; return this; } /** * Set request timeout. * * @param requestTimeout Request timeout * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setRequestTimeout(int requestTimeout) { this.requestTimeout = requestTimeout; return this; } /** * Set URI. * * @param uri {@link URI} * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setUri(URI uri) { this.uri = uri; return this; } /** * Set url. * * @param url Url * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setUrl(String url) { this.uri = URI.create(url); return this; } /** * Set virtual host. * * @param virtualHost Virtual host * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setVirtualHost(String virtualHost) { this.virtualHost = virtualHost; return this; } /** * Set accept compression. * * @param acceptCompression enable accept compression * @return {@link ParsecAsyncHttpRequest.Builder} */ public Builder setAcceptCompression(boolean acceptCompression) { this.acceptCompression = acceptCompression; return this; } } }