Java tutorial
/* Copyright (C) 2013-2014 Ian Teune <ian.teune@gmail.com> * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.tinspx.util.net; import static com.google.common.base.Preconditions.*; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.common.io.ByteSource; import com.google.common.net.HttpHeaders; import com.tinspx.util.collect.CollectUtils; import com.tinspx.util.io.ChannelSource; import java.io.IOException; import java.util.Map; import javax.annotation.concurrent.NotThreadSafe; import lombok.ToString; import lombok.experimental.Delegate; /** * Represents a {@link Request} body/playload. * <p> * A {@code RequestBody} is not thread-safe unless documented otherwise. It is * generally unsafe to modify or use a {@code RequestBody} concurrently with * multiple requests. * <p> * All static {@code of(...)} factory methods return an immutable * {@code RequestBody}, which is thread-safe only if the backing * {@code ByteSource} is thread-safe. * * @see Request#body(RequestBody) * @author Ian */ @NotThreadSafe public abstract class RequestBody extends ChannelSource { private static RequestBody of(ByteSource source, Multimap<String, String> headers, boolean copy) { return new DelegateRequestBody(ChannelSource.of(source), headers, copy); } public static RequestBody of(ByteSource source, Multimap<String, String> headers) { return of(source, headers, true); } public static RequestBody of(ByteSource source) { return of(source, ImmutableMultimap.<String, String>of(), false); } public static RequestBody of(ByteSource source, String contentType) { return of(source, ImmutableMultimap.of(HttpHeaders.CONTENT_TYPE, contentType), false); } public static RequestBody ofMap(ByteSource source, Map<String, String> headers) { return headers.isEmpty() ? of(source) : of(source, copyOfMap(headers), false); } public static RequestBody of(ByteSource source, Map<String, ? extends Iterable<String>> headers) { return headers.isEmpty() ? of(source) : of(source, copyOf(headers), false); } public static RequestBody of(ByteSource source, String... headers) { checkArgument(headers.length % 2 == 0, "headers list is odd (%s)", headers.length); Multimap<String, String> map = LinkedListMultimap.create(headers.length / 2); for (int i = 0; i < headers.length; i += 2) { map.put(headers[i], headers[i + 1]); } return of(source, map, false); } static <K, V> LinkedListMultimap<K, V> copyOf(Map<? extends K, ? extends Iterable<? extends V>> headers) { LinkedListMultimap<K, V> map = LinkedListMultimap.create(headers.size()); for (Map.Entry<? extends K, ? extends Iterable<? extends V>> entry : headers.entrySet()) { map.putAll(entry.getKey(), entry.getValue()); } return map; } static <K, V> LinkedListMultimap<K, V> copyOfMap(Map<? extends K, ? extends V> headers) { LinkedListMultimap<K, V> map = LinkedListMultimap.create(headers.size()); for (Map.Entry<? extends K, ? extends V> entry : headers.entrySet()) { map.put(entry.getKey(), entry.getValue()); } return map; } @ToString static class DelegateRequestBody extends RequestBody { @Delegate(types = { ByteSource.class, ChannelSource.class }) final ChannelSource delegate; final Multimap<String, String> headers; public DelegateRequestBody(ChannelSource delegate, Multimap<String, String> headers, boolean copy) { this.delegate = checkNotNull(delegate); CollectUtils.checkAllNotNull(headers.keySet(), "null header name"); CollectUtils.checkAllNotNull(headers.values(), "null header value"); //ImmutableListMultimap is not used because iteration order must //be preserved if (copy) { headers = LinkedListMultimap.create(headers); } this.headers = Multimaps.unmodifiableMultimap(headers); } @Override public Multimap<String, String> headers() { return headers; } @Override public DelegateRequestBody duplicate() { return this; } } /** * Returns all required headers for this {@code RequestBody}. Including the * {@code Content-Length} header is not required, but allowed. A * {@link RequestContext} or any consumer of this {@code RequestBody} is * responsible for ensuring the {@code Content-Length} header is correctly * set to the result of {@link #size() size()}. Typically, these headers * should include at least the {@code Content-Type} header. * <p> * The returned {@code Multimap} <i>may</i> be modifiable; therefore, it is * generally unsafe to modify this {@code Multimap} unless documented * otherwise. */ public abstract Multimap<String, String> headers() throws IOException; /** * Returns a {@code RequestBody} that is equivalent to this instance, but * subsequent changes made to the returned {@code RequestBody} will have no * effect on this instance, and vice versa. If this {@code RequestBody} is * immutable, this instance may be returned. */ public abstract RequestBody duplicate() throws IOException; }