001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.http.classic.entity;
018
019import static org.apache.juneau.commons.utils.IoUtils.*;
020import static org.apache.juneau.commons.utils.Shorts.*;
021
022import java.io.*;
023import java.nio.charset.*;
024import java.util.function.*;
025
026import org.apache.http.*;
027import org.apache.juneau.commons.bean.*;
028import org.apache.juneau.http.UnmodifiableBean;
029import org.apache.juneau.http.classic.header.*;
030import org.apache.juneau.bct.assertions.*;
031
032/**
033 * A basic {@link org.apache.http.HttpEntity} implementation with additional features.
034 *
035 * Provides the following features:
036 * <ul class='spaced-list'>
037 *    <li>
038 *       Caching.
039 *    <li>
040 *       Fluent setters.
041 *    <li>
042 *       Fluent assertions.
043 *    <li>
044 *       Externally-supplied/dynamic content.
045 * </ul>
046 *
047 * <p>
048 * Immutability is expressed with the "funnel + nested {@code Unmodifiable} snapshot" paradigm: every fluent setter
049 * routes through the single protected {@link #modify(Runnable)} choke-point, and each concrete leaf gets a nested
050 * {@code X.Unmodifiable} whose only behavioral override is a throwing {@code modify(...)}.  A leaf's
051 * {@link #unmodifiable()} factory returns a point-in-time snapshot of type {@code X.Unmodifiable}; any attempt to set a
052 * property on it throws an {@link UnsupportedOperationException}.
053 *
054 * <p>
055 * This is a self-typed (CRTP) root: a leaf declares <c>class StringEntity <jk>extends</jk> BasicHttpEntity&lt;StringEntity&gt;</c>
056 * and inherits leaf-typed fluent setters with no covariant overrides.
057 *
058 * <h5 class='section'>Notes:</h5><ul>
059 *    <li class='warn'>Beans are not thread safe unless they're marked as unmodifiable.
060 * </ul>
061 *
062 * <h5 class='section'>See Also:</h5><ul>
063 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestCommon">juneau-rest-common</a>
064 * </ul>
065 *
066 * @param <SELF> The self type for fluent setters.
067 */
068@BeanIgnore
069
070@SuppressWarnings({
071   "java:S119", // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name.
072   "resource", // Content may be streams; only reference equality is used for the content field.
073   "unchecked" // contentOrElse() casts the stored content to the caller's T, and self() is the standard CRTP cast.
074})
075public abstract class BasicHttpEntity<SELF extends BasicHttpEntity<SELF>> implements HttpEntity {
076
077   /**
078    * An empty HttpEntity.
079    */
080   @SuppressWarnings({
081      "java:S2390" // Convenience constant, not a static-init cycle: ByteArrayEntity()/unmodifiable() touch no static state of either class (only per-instance fields), so nothing here observes a not-yet-initialized value.
082   })
083   public static final BasicHttpEntity<?> EMPTY = new ByteArrayEntity().unmodifiable();
084
085   private boolean cached;
086   private boolean chunked;
087   private Object content;
088   private Supplier<?> contentSupplier;
089   private ContentType contentType;
090   private ContentEncoding contentEncoding;
091   private Charset charset;
092   private long contentLength = -1;
093   private int maxLength = -1;
094
095   /**
096    * Constructor.
097    */
098   protected BasicHttpEntity() {}
099
100   /**
101    * Copy constructor.
102    *
103    * @param copyFrom The bean being copied.  Must not be <jk>null</jk>.
104    */
105   protected BasicHttpEntity(BasicHttpEntity<?> copyFrom) {
106      this.cached = copyFrom.cached;
107      this.chunked = copyFrom.chunked;
108      this.content = copyFrom.content;
109      this.contentSupplier = copyFrom.contentSupplier;
110      this.contentType = copyFrom.contentType;
111      this.contentEncoding = copyFrom.contentEncoding;
112      this.contentLength = copyFrom.contentLength;
113      this.charset = copyFrom.charset;
114      this.maxLength = copyFrom.maxLength;
115   }
116
117   /**
118    * Constructor.
119    *
120    * @param contentType The entity content type.  Can be <jk>null</jk> to omit an explicit <c>Content-Type</c> header.
121    * @param content The entity content.  Can be <jk>null</jk> (the effect depends on the concrete leaf type: e.g. treated as empty for {@link StringEntity}/{@link ByteArrayEntity}, or a {@link NullPointerException} on read for {@link StreamEntity}/{@link ReaderEntity}/{@link FileEntity}).
122    */
123   protected BasicHttpEntity(ContentType contentType, Object content) {
124      this.contentType = contentType;
125      this.content = content;
126   }
127
128   /**
129    * Converts the contents of this entity as a byte array.
130    *
131    * <p>
132    * Note that this may exhaust the content on non-repeatable, non-cached entities.
133    *
134    * @return The contents of this entity as a byte array.
135    * @throws IOException If a problem occurred while trying to read the content.
136    */
137   public byte[] asBytes() throws IOException {
138      return readBytes(getContent());
139   }
140
141   /**
142    * Returns an assertion on the contents of this entity.
143    *
144    * <p>
145    * Note that this may exhaust the content on non-repeatable, non-cached entities.
146    *
147    * @return A new fluent assertion.
148    * @throws IOException If a problem occurred while trying to read the byte array.
149    */
150   public FluentByteArrayAssertion<BasicHttpEntity<?>> assertBytes() throws IOException {
151      return new FluentByteArrayAssertion<>(asBytes(), this);
152   }
153
154   /**
155    * Returns an assertion on the contents of this entity.
156    *
157    * <p>
158    * Note that this may exhaust the content on non-repeatable, non-cached entities.
159    *
160    * @return A new fluent assertion.
161    * @throws IOException If a problem occurred while trying to read the byte array.
162    */
163   public FluentStringAssertion<BasicHttpEntity<?>> assertString() throws IOException {
164      return new FluentStringAssertion<>(asString(), this);
165   }
166
167   /**
168    * Converts the contents of this entity as a string.
169    *
170    * <p>
171    * Note that this may exhaust the content on non-repeatable, non-cached entities.
172    *
173    * @return The contents of this entity as a string.
174    * @throws IOException If a problem occurred while trying to read the content.
175    */
176   public String asString() throws IOException {
177      return read(getContent());
178   }
179
180   @Override /* Overridden from HttpEntity */
181   public void consumeContent() throws IOException {
182      // No-op: Intentional empty implementation for optional interface method
183   }
184
185   /**
186    * Creates a builder for this class initialized with the contents of this bean.
187    *
188    * <p>
189    * Allows you to create a modifiable copy of this bean.
190    *
191    * @return A new builder bean.
192    */
193   public abstract SELF copy();
194
195   /**
196    * Returns the charset to use when converting to and from stream-based resources.
197    *
198    * @return The charset to use when converting to and from stream-based resources.
199    */
200   public Charset getCharset() { return charset == null ? UTF8 : charset; }
201
202   @Override /* Overridden from HttpEntity */
203   public InputStream getContent() throws IOException, UnsupportedOperationException { return EMPTY_INPUT_STREAM; }
204
205   @Override /* Overridden from HttpEntity */
206   public Header getContentEncoding() { return contentEncoding; }
207
208   @Override /* Overridden from HttpEntity */
209   public long getContentLength() { return contentLength; }
210
211   @Override /* Overridden from HttpEntity */
212   public Header getContentType() { return contentType; }
213
214   /**
215    * Returns the maximum number of bytes to read or write to and from stream-based resources.
216    *
217    * @return The maximum number of bytes to read or write to and from stream-based resources.
218    */
219   public int getMaxLength() { return maxLength; }
220
221   /**
222    * Returns <jk>true</jk> if this entity is cached in-memory.
223    *
224    * @return <jk>true</jk> if this entity is cached in-memory.
225    */
226   public boolean isCached() { return cached; }
227
228   @Override /* Overridden from HttpEntity */
229   public boolean isChunked() { return chunked; }
230
231   @Override /* Overridden from HttpEntity */
232   public boolean isRepeatable() { return false; }
233
234   @Override /* Overridden from HttpEntity */
235   public boolean isStreaming() { return false; }
236
237   /**
238    * Returns <jk>true</jk> if this bean is unmodifiable.
239    *
240    * @return <jk>true</jk> if this bean is an {@link UnmodifiableBean} snapshot.
241    */
242   public boolean isUnmodifiable() { return this instanceof UnmodifiableBean; }
243
244   /**
245    * Specifies that the contents of this resource should be cached into an internal byte array so that it can
246    * be read multiple times.
247    *
248    * @return This object.
249    * @throws IOException If entity could not be read into memory.
250    */
251   public SELF setCached() throws IOException {
252      return modify(() -> cached = true);
253   }
254
255   /**
256    * Specifies the charset to use when converting to and from stream-based resources.
257    *
258    * @param value The new value.  If <jk>null</jk>, <c>UTF-8</c> is assumed.
259    * @return This object.
260    */
261   public SELF setCharset(Charset value) {
262      return modify(() -> charset = value);
263   }
264
265   /**
266    * Sets the 'chunked' flag value to <jk>true</jk>.
267    *
268    * <h5 class='section'>Notes:</h5><ul>
269    *    <li class='note'>If the {@link HttpEntity#getContentLength()} method returns a negative value, the HttpClient code will always
270    *       use chunked encoding.
271    * </ul>
272    *
273    * @return This object.
274    */
275   public SELF setChunked() {
276      return setChunked(true);
277   }
278
279   /**
280    * Sets the 'chunked' flag value.
281    *
282    * <h5 class='section'>Notes:</h5><ul>
283    *    <li class='note'>If the {@link HttpEntity#getContentLength()} method returns a negative value, the HttpClient code will always
284    *       use chunked encoding.
285    * </ul>
286    *
287    * @param value The new value for this flag.
288    * @return This object.
289    */
290   public SELF setChunked(boolean value) {
291      return modify(() -> chunked = value);
292   }
293
294   /**
295    * Sets the content on this entity bean.
296    *
297    * @param value The entity content.  Can be <jk>null</jk> (the effect depends on the concrete leaf type: e.g. treated as empty for {@link StringEntity}/{@link ByteArrayEntity}, or a {@link NullPointerException} on read for {@link StreamEntity}/{@link ReaderEntity}/{@link FileEntity}).
298    * @return This object.
299    */
300   public SELF setContent(Object value) {
301      return modify(() -> content = value);
302   }
303
304   /**
305    * Sets the content on this entity bean from a supplier.
306    *
307    * <p>
308    * Repeatable entities such as {@link StringEntity} use this to allow the entity content to be resolved at
309    * serialization time.
310    *
311    * @param value The entity content, can be <jk>null</jk> (treated as a supplier that always returns <jk>null</jk>).
312    * @return This object.
313    */
314   public SELF setContent(Supplier<?> value) {
315      return modify(() -> contentSupplier = value == null ? () -> null : value);
316   }
317
318   /**
319    * Sets the content encoding header on this entity bean.
320    *
321    * @param value The new <c>Content-Encoding</c> header, or <jk>null</jk> to unset.
322    * @return This object.
323    */
324   public SELF setContentEncoding(ContentEncoding value) {
325      return modify(() -> contentEncoding = value);
326   }
327
328   /**
329    * Sets the content encoding header on this entity bean.
330    *
331    * @param value The new <c>Content-Encoding</c> header, or <jk>null</jk> to unset.
332    * @return This object.
333    */
334   public SELF setContentEncoding(String value) {
335      return setContentEncoding(ContentEncoding.of(value));
336   }
337
338   /**
339    * Sets the content length on this entity bean.
340    *
341    * @param value The new <c>Content-Length</c> header value, or <c>-1</c> to unset.
342    * @return This object.
343    */
344   public SELF setContentLength(long value) {
345      return modify(() -> contentLength = value);
346   }
347
348   /**
349    * Sets the content type on this entity bean.
350    *
351    * @param value The new <c>Content-Type</c> header, or <jk>null</jk> to unset.
352    * @return This object.
353    */
354   public SELF setContentType(ContentType value) {
355      return modify(() -> contentType = value);
356   }
357
358   /**
359    * Sets the content type on this entity bean.
360    *
361    * @param value The new <c>Content-Type</c> header, or <jk>null</jk> to unset.
362    * @return This object.
363    */
364   public SELF setContentType(String value) {
365      return setContentType(ContentType.of(value));
366   }
367
368   /**
369    * Specifies the maximum number of bytes to read or write to and from stream-based resources.
370    *
371    * <p>
372    * Implementation is not universal.
373    *
374    * @param value The new value.  The default is <c>-1</c> which means read everything.
375    * @return This object.
376    */
377   public SELF setMaxLength(int value) {
378      return modify(() -> maxLength = value);
379   }
380
381   /**
382    * Returns an unmodifiable snapshot of this bean.
383    *
384    * <p>
385    * The returned instance is a point-in-time copy of the leaf's nested {@code Unmodifiable} type; any attempt to set a
386    * property on it throws an {@link UnsupportedOperationException}.  This method is idempotent: if this bean is already
387    * unmodifiable it returns itself rather than taking another snapshot.  The receiver is left unchanged (and still
388    * modifiable unless it was already unmodifiable).
389    *
390    * @return An unmodifiable snapshot of this bean, or this bean if it is already unmodifiable.
391    */
392   public abstract SELF unmodifiable();
393
394   @Override /* Overridden from HttpEntity */
395   public void writeTo(OutputStream outStream) throws IOException {
396      // No-op: Intentional empty implementation for optional interface method
397   }
398
399   /**
400    * Compares the shared base content of two entities.
401    *
402    * <p>
403    * This base implementation compares only the fields declared on {@link BasicHttpEntity} and deliberately accepts any
404    * {@code BasicHttpEntity} subtype so that concrete leaves can delegate to it via {@code super.equals(...)}.  It does
405    * <b>not</b> gate on the concrete leaf type; each concrete leaf overrides {@code equals} to first apply an
406    * {@code instanceof <ThatLeaf>} gate (so two unrelated leaves with equal base content are not equal) and then calls
407    * {@code super.equals(...)} to compare the inherited content.  Using {@code instanceof <ThatLeaf>} (rather than
408    * {@code getClass() ==}) is what preserves the immutability invariant that {@code bean.equals(bean.unmodifiable())}
409    * holds, since each {@code X.Unmodifiable} snapshot is a subclass of its leaf {@code X}.
410    */
411   @Override /* Overridden from Object */
412   public boolean equals(Object o) {
413      return o instanceof BasicHttpEntity<?> other && eq(this, other, (x, y) ->
414         eq(x.cached, y.cached) && eq(x.chunked, y.chunked) && eq(x.content, y.content)
415         && eq(x.contentSupplier, y.contentSupplier) && eq(x.contentType, y.contentType)
416         && eq(x.contentEncoding, y.contentEncoding) && eq(x.charset, y.charset)
417         && eq(x.contentLength, y.contentLength) && eq(x.maxLength, y.maxLength));
418   }
419
420   @Override /* Overridden from Object */
421   public int hashCode() {
422      return h(cached, chunked, content, contentSupplier, contentType, contentEncoding, charset, contentLength, maxLength);
423   }
424
425   /**
426    * Same as {@link #asBytes()} but wraps {@link IOException IOExceptions} inside a {@link RuntimeException}.
427    *
428    * @return The contents of this entity as a byte array.
429    */
430   protected byte[] asSafeBytes() {
431      try {
432         return asBytes();
433      } catch (IOException e) {
434         throw new UncheckedIOException(e);
435      }
436   }
437
438   /**
439    * Returns the content of this entity.
440    *
441    * @param <T> The value type.
442    * @param def The default value if <jk>null</jk>.
443    * @return The content object.
444    */
445   protected <T> T contentOrElse(T def) {
446      Object o = content;
447      if (o == null && nn(contentSupplier))
448         o = contentSupplier.get();
449      return (o == null ? def : (T)o);
450   }
451
452   /**
453    * Returns <jk>true</jk> if the contents of this entity are provided through an external supplier.
454    *
455    * <p>
456    * Externally supplied content generally means the content length cannot be reliably determined
457    * based on the content.
458    *
459    * @return <jk>true</jk> if the contents of this entity are provided through an external supplier.
460    */
461   protected boolean isSupplied() { return nn(contentSupplier); }
462
463   /**
464    * Returns this object cast to the self type.
465    *
466    * @return This object.
467    */
468   protected final SELF self() { return (SELF) this; }
469
470   /**
471    * Single mutation funnel — the only choke-point through which all state changes on this bean flow.
472    *
473    * <p>
474    * Every fluent setter routes through this method.  The nested {@code Unmodifiable} snapshot overrides only this
475    * method to throw, which freezes the entire mutation surface with a single override.
476    *
477    * @param mutation The state change to apply.
478    * @return This object.
479    */
480   protected SELF modify(Runnable mutation) {
481      mutation.run();
482      return self();
483   }
484
485   /**
486    * Deep-freeze hook invoked from the nested {@code Unmodifiable} constructor after the snapshot copy completes.
487    *
488    * <p>
489    * Entities hold no mutable sub-beans (the content-type / content-encoding headers are immutable value objects, and
490    * the content buffer follows the copy-constructor's reference-copy behavior), so this is a documented no-op — the
491    * single throwing {@link #modify(Runnable)} override on the nested {@code Unmodifiable} already freezes the entire
492    * mutation surface.  A leaf that adds its own mutable sub-bean would override this method (calling
493    * {@code super.freeze()} first).
494    */
495   protected void freeze() {
496      // No-op: entities have no mutable sub-beans to deep-freeze.
497   }
498}