View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.core5.http.io.entity;
29  
30  import java.io.IOException;
31  import java.io.InputStream;
32  import java.io.OutputStream;
33  import java.util.Collections;
34  import java.util.List;
35  import java.util.Objects;
36  import java.util.Set;
37  
38  import org.apache.hc.core5.function.Supplier;
39  import org.apache.hc.core5.http.ContentType;
40  import org.apache.hc.core5.http.Header;
41  import org.apache.hc.core5.http.HttpEntity;
42  import org.apache.hc.core5.util.Args;
43  
44  /**
45   * Abstract base class for mutable entities. Provides the commonly used attributes for streamed and
46   * self-contained implementations.
47   * <p>
48   * This class contains immutable attributes but subclasses may contain additional immutable or mutable attributes.
49   * </p>
50   *
51   * @since 4.0
52   */
53  public abstract class AbstractHttpEntity implements HttpEntity {
54  
55      static final int OUTPUT_BUFFER_SIZE = 4096;
56  
57      private final String contentType;
58      private final String contentEncoding;
59      private final boolean chunked;
60  
61      /**
62       * Constructs a new instance with the given attributes kept as immutable.
63       *
64       * @param contentType     The content-type string, may be null.
65       * @param contentEncoding The content encoding string, may be null.
66       * @param chunked         Whether this entity should be chunked.
67       */
68      protected AbstractHttpEntity(final String contentType, final String contentEncoding, final boolean chunked) {
69          this.contentType = contentType;
70          this.contentEncoding = contentEncoding;
71          this.chunked = chunked;
72      }
73  
74      /**
75       * Constructs a new instance with the given attributes kept as immutable.
76       *
77       * @param contentType     The content-type, may be null.
78       * @param contentEncoding The content encoding string, may be null.
79       * @param chunked         Whether this entity should be chunked.
80       */
81      protected AbstractHttpEntity(final ContentType contentType, final String contentEncoding, final boolean chunked) {
82          this.contentType = Objects.toString(contentType, null);
83          this.contentEncoding = contentEncoding;
84          this.chunked = chunked;
85      }
86  
87      /**
88       * Constructs a new instance with the given attributes kept as immutable.
89       * <p>
90       * The new instance:
91       * </p>
92       * <ul>
93       * <li>is not chunked.</li>
94       * </ul>
95       *
96       * @param contentType     The content-type string, may be null.
97       * @param contentEncoding The content encoding string, may be null.
98       */
99      protected AbstractHttpEntity(final String contentType, final String contentEncoding) {
100         this(contentType, contentEncoding, false);
101     }
102 
103     /**
104      * Constructs a new instance with the given attributes kept as immutable.
105      * <p>
106      * The new instance:
107      * </p>
108      * <ul>
109      * <li>is not chunked.</li>
110      * </ul>
111      *
112      * @param contentType     The content-type, may be null.
113      * @param contentEncoding The content encoding string, may be null.
114      */
115     protected AbstractHttpEntity(final ContentType contentType, final String contentEncoding) {
116         this(contentType, contentEncoding, false);
117     }
118 
119     /**
120      * Writes an entity to an OutputStream.
121      *
122      * @param entity    The entity to write, never null.
123      * @param outStream Where to write the entity, never null.
124      * @throws IOException                   if the entity cannot generate its content stream; also thrown if the output stream is closed.
125      * @throws UnsupportedOperationException if entity content cannot be represented as {@link java.io.InputStream}.
126      */
127     public static void writeTo(final HttpEntity entity, final OutputStream outStream) throws IOException {
128         Args.notNull(entity, "Entity");
129         Args.notNull(outStream, "Output stream");
130         try (final InputStream inStream = entity.getContent()) {
131             if (inStream != null) {
132                 int count;
133                 final byte[] tmp = new byte[OUTPUT_BUFFER_SIZE];
134                 while ((count = inStream.read(tmp)) != -1) {
135                     outStream.write(tmp, 0, count);
136                 }
137             }
138         }
139     }
140 
141     /**
142      * Writes this entity to an OutputStream.
143      *
144      * @param outStream Where to write the entity, never null.
145      * @throws IOException                   if the entity cannot generate its content stream; also thrown if the output stream is closed.
146      * @throws UnsupportedOperationException if entity content cannot be represented as {@link java.io.InputStream}.
147      */
148     @Override
149     public void writeTo(final OutputStream outStream) throws IOException {
150         writeTo(this, outStream);
151     }
152 
153     @Override
154     public final String getContentType() {
155         return contentType;
156     }
157 
158     @Override
159     public final String getContentEncoding() {
160         return contentEncoding;
161     }
162 
163     /**
164      * {@inheritDoc}
165      * <p>
166      * This implementation always returns {@code false}.
167      * </p>
168      */
169     @Override
170     public final boolean isChunked() {
171         return chunked;
172     }
173 
174     /**
175      * {@inheritDoc}
176      * <p>
177      * This implementation always returns {@code false}.
178      * </p>
179      */
180     @Override
181     public boolean isRepeatable() {
182         return false;
183     }
184 
185     /**
186      * {@inheritDoc}
187      * <p>
188      * This implementation always returns {@code null}.
189      * </p>
190      */
191     @Override
192     public Supplier<List<? extends Header>> getTrailers() {
193         return null;
194     }
195 
196     /**
197      * {@inheritDoc}
198      * <p>
199      * This implementation always returns an immutable empty set.
200      * </p>
201      */
202     @Override
203     public Set<String> getTrailerNames() {
204         return Collections.emptySet();
205     }
206 
207     @Override
208     public String toString() {
209         final StringBuilder sb = new StringBuilder();
210         sb.append("[Entity-Class: ");
211         sb.append(getClass().getSimpleName());
212         sb.append(", Content-Type: ");
213         sb.append(contentType);
214         sb.append(", Content-Encoding: ");
215         sb.append(contentEncoding);
216         sb.append(", chunked: ");
217         sb.append(chunked);
218         sb.append(']');
219         return sb.toString();
220     }
221 
222 }