com.tinspx.util.net.RequestBody.java Source code

Java tutorial

Introduction

Here is the source code for com.tinspx.util.net.RequestBody.java

Source

/* 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;
}