001/*
002 * ====================================================================
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *   http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied.  See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 * ====================================================================
020 *
021 * This software consists of voluntary contributions made by many
022 * individuals on behalf of the Apache Software Foundation.  For more
023 * information on the Apache Software Foundation, please see
024 * <http://www.apache.org/>.
025 *
026 */
027package org.apache.http.nio.protocol;
028
029import java.io.IOException;
030
031import org.apache.http.HttpEntity;
032import org.apache.http.HttpEntityEnclosingRequest;
033import org.apache.http.HttpHost;
034import org.apache.http.HttpRequest;
035import org.apache.http.nio.ContentEncoder;
036import org.apache.http.nio.IOControl;
037import org.apache.http.nio.entity.EntityAsyncContentProducer;
038import org.apache.http.nio.entity.HttpAsyncContentProducer;
039import org.apache.http.protocol.HttpContext;
040import org.apache.http.util.Args;
041
042/**
043 * Basic implementation of {@link HttpAsyncRequestProducer}. The producer
044 * can make use of the {@link HttpAsyncContentProducer} interface to
045 * efficiently stream out message content to the underlying non-blocking HTTP
046 * connection, if it is implemented by the enclosed {@link HttpEntity}.
047 *
048 * @see HttpAsyncContentProducer
049 *
050 * @since 4.2
051 */
052public class BasicAsyncRequestProducer implements HttpAsyncRequestProducer {
053
054    private final HttpHost target;
055    private final HttpRequest request;
056    private final HttpAsyncContentProducer producer;
057
058    /**
059     * Creates a producer that can be used to transmit the given request
060     * message. The given content producer will be used to stream out message
061     * content. Please note that the request message is expected to enclose
062     * an {@link HttpEntity} whose properties are consistent with the behavior
063     * of the content producer.
064     *
065     * @param target target host.
066     * @param request request message.
067     * @param producer request content producer.
068     */
069    protected BasicAsyncRequestProducer(
070            final HttpHost target,
071            final HttpEntityEnclosingRequest request,
072            final HttpAsyncContentProducer producer) {
073        super();
074        Args.notNull(target, "HTTP host");
075        Args.notNull(request, "HTTP request");
076        Args.notNull(producer, "HTTP content producer");
077        this.target = target;
078        this.request = request;
079        this.producer = producer;
080    }
081
082    /**
083     * Creates a producer that can be used to transmit the given request
084     * message. If the request message encloses an {@link HttpEntity}
085     * it is also expected to implement {@link HttpAsyncContentProducer}.
086     *
087     * @param target target host.
088     * @param request request message.
089     */
090    public BasicAsyncRequestProducer(final HttpHost target, final HttpRequest request) {
091        Args.notNull(target, "HTTP host");
092        Args.notNull(request, "HTTP request");
093        this.target = target;
094        this.request = request;
095        if (request instanceof HttpEntityEnclosingRequest) {
096            final HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
097            if (entity != null) {
098                if (entity instanceof HttpAsyncContentProducer) {
099                    this.producer = (HttpAsyncContentProducer) entity;
100                } else {
101                    this.producer = new EntityAsyncContentProducer(entity);
102                }
103            } else {
104                this.producer = null;
105            }
106        } else {
107            this.producer = null;
108        }
109    }
110
111    @Override
112    public HttpRequest generateRequest() {
113        return this.request;
114    }
115
116    @Override
117    public HttpHost getTarget() {
118        return this.target;
119    }
120
121    @Override
122    public void produceContent(
123            final ContentEncoder encoder, final IOControl ioctrl) throws IOException {
124        if (this.producer != null) {
125            this.producer.produceContent(encoder, ioctrl);
126            if (encoder.isCompleted()) {
127                this.producer.close();
128            }
129        }
130    }
131
132    @Override
133    public void requestCompleted(final HttpContext context) {
134    }
135
136    @Override
137    public void failed(final Exception ex) {
138    }
139
140    @Override
141    public boolean isRepeatable() {
142        return this.producer == null || this.producer.isRepeatable();
143    }
144
145    @Override
146    public void resetRequest() throws IOException {
147        if (this.producer != null) {
148            this.producer.close();
149        }
150    }
151
152    @Override
153    public void close() throws IOException {
154        if (this.producer != null) {
155            this.producer.close();
156        }
157    }
158
159    @Override
160    public String toString() {
161        final StringBuilder sb = new StringBuilder();
162        sb.append(this.target);
163        sb.append(' ');
164        sb.append(this.request);
165        if (this.producer != null) {
166            sb.append(' ');
167            sb.append(this.producer);
168        }
169        return sb.toString();
170    }
171
172}