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.rest.client;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020
021import java.io.*;
022
023import org.apache.juneau.http.*;
024import org.apache.juneau.marshall.serializer.*;
025
026/**
027 * A repeatable {@link HttpBody} that serializes a POJO straight to the transport output stream.
028 *
029 * <p>
030 * The next-generation analog of the classic {@code SerializedEntity}: rather than pre-serializing the POJO to an
031 * in-memory {@link String}/{@code byte[]} and wrapping that, {@link #writeTo(OutputStream)} runs the supplied
032 * {@link Serializer} directly against the wire output stream, so large payloads stream out without full in-memory
033 * materialization.
034 *
035 * <p>
036 * The body is {@linkplain #isRepeatable() repeatable}: each {@link #writeTo(OutputStream)} re-serializes the value, so
037 * it can be safely resent (e.g. for a future auto-retry).  {@link #getContentLength()} returns {@code -1} (the
038 * transport uses chunked transfer encoding).
039 *
040 * <h5 class='section'>See Also:</h5><ul>
041 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/NextGenRestClient">Next-Generation REST Client</a>
042 * </ul>
043 *
044 * @since 9.2.1
045 */
046public final class SerializerBody implements HttpBody {
047
048   private final Serializer serializer;
049   private final Object value;
050   private final String contentType;
051
052   private SerializerBody(Serializer serializer, Object value, String contentType) {
053      this.serializer = reqnn("serializer", serializer);
054      this.value = value;
055      this.contentType = contentType;
056   }
057
058   /**
059    * Creates a {@link SerializerBody} using the serializer's own {@linkplain Serializer#getResponseContentType() media type}.
060    *
061    * @param serializer The serializer that writes the POJO. Must not be <jk>null</jk>.
062    * @param value The POJO to serialize. May be <jk>null</jk>.
063    * @return A new instance. Never <jk>null</jk>.
064    */
065   public static SerializerBody of(Serializer serializer, Object value) {
066      reqnn("serializer", serializer);
067      var mt = serializer.getResponseContentType();
068      return new SerializerBody(serializer, value, mt == null ? null : mt.toString());
069   }
070
071   /**
072    * Creates a {@link SerializerBody} with an explicit content type.
073    *
074    * @param serializer The serializer that writes the POJO. Must not be <jk>null</jk>.
075    * @param value The POJO to serialize. May be <jk>null</jk>.
076    * @param contentType The MIME content type. May be <jk>null</jk>.
077    * @return A new instance. Never <jk>null</jk>.
078    */
079   public static SerializerBody of(Serializer serializer, Object value, String contentType) {
080      return new SerializerBody(serializer, value, contentType);
081   }
082
083   @Override /* HttpBody */
084   public String getContentType() {
085      return contentType;
086   }
087
088   @Override /* HttpBody */
089   public long getContentLength() {
090      return -1;
091   }
092
093   @Override /* HttpBody */
094   public void writeTo(OutputStream out) throws IOException {
095      try {
096         serializer.write(value, out);
097      } catch (SerializeException e) {
098         throw ioex(e);
099      }
100   }
101
102   @Override /* HttpBody */
103   public boolean isRepeatable() {
104      return true;
105   }
106}