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.entity;
018
019import static org.apache.juneau.commons.utils.IoUtils.*;
020import static org.apache.juneau.commons.utils.ThrowableUtils.*;
021import static org.apache.juneau.commons.utils.Utils.*;
022import static org.apache.juneau.http.HttpHeaders.*;
023
024import java.io.*;
025import java.nio.charset.*;
026import java.util.function.*;
027
028import org.apache.http.*;
029import org.apache.juneau.commons.io.*;
030import org.apache.juneau.http.header.*;
031import org.apache.juneau.httppart.*;
032import org.apache.juneau.serializer.*;
033
034/**
035 * HttpEntity for serializing POJOs as the body of HTTP requests.
036 *
037 * <h5 class='section'>See Also:</h5><ul>
038 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestCommonBasics">juneau-rest-common Basics</a>
039
040 * </ul>
041 */
042public class SerializedEntity extends BasicHttpEntity {
043   Serializer serializer;
044   HttpPartSchema schema;
045
046   /**
047    * Constructor.
048    */
049   public SerializedEntity() {}
050
051   /**
052    * Constructor.
053    *
054    * @param contentType The entity content type.
055    * @param content The entity content.
056    * @param serializer The entity serializer.
057    * @param schema The entity schema.  Can be <jk>null</jk>.
058    */
059   public SerializedEntity(ContentType contentType, Object content, Serializer serializer, HttpPartSchema schema) {
060      super(contentType, content);
061      this.serializer = serializer;
062      this.schema = schema;
063   }
064
065   /**
066    * Copy constructor.
067    *
068    * @param copyFrom The bean being copied.
069    */
070   public SerializedEntity(SerializedEntity copyFrom) {
071      super(copyFrom);
072      this.serializer = copyFrom.serializer;
073      this.schema = copyFrom.schema;
074   }
075
076   /**
077    * Creates a builder for this class initialized with the contents of this bean.
078    *
079    * <p>
080    * Allows you to create a modifiable copy of this bean.
081    *
082    * @return A new builder bean.
083    */
084   @Override
085   public SerializedEntity copy() {
086      return new SerializedEntity(this);
087   }
088
089   /**
090    * Copies this bean and sets the serializer and schema on it.
091    *
092    * @param serializer The new serializer for the bean.  Can be <jk>null</jk>.
093    * @param schema The new schema for the bean.  Can be <jk>null</jk>.
094    * @return Either a new bean with the serializer set, or this bean if
095    *    both values are <jk>null</jk> or the serializer and schema were already set.
096    */
097   public SerializedEntity copyWith(Serializer serializer, HttpPartSchema schema) {
098      if ((this.serializer == null && nn(serializer)) || (this.schema == null && nn(schema))) {
099         SerializedEntity h = copy();
100         if (nn(serializer))
101            h.setSerializer(serializer);
102         if (nn(schema))
103            h.setSchema(schema);
104         return h;
105      }
106      return this;
107   }
108
109   //-----------------------------------------------------------------------------------------------------------------
110
111   @Override /* Overridden from BasicHttpEntity */
112   public InputStream getContent() {
113      var baos = new ByteArrayOutputStream();
114      try {
115         writeTo(baos);
116         return new ByteArrayInputStream(baos.toByteArray());
117      } catch (IOException e) {
118         throw toRex(e);
119      }
120   }
121
122   @Override /* Overridden from BasicHttpEntity */
123   public long getContentLength() { return -1; }
124
125   @Override
126   public Header getContentType() {
127      Header x = super.getContentType();
128      if (x == null && nn(serializer))
129         x = contentType(serializer.getPrimaryMediaType());
130      return x;
131   }
132
133   @Override /* Overridden from BasicHttpEntity */
134   public boolean isRepeatable() { return true; }
135
136   @Override /* Overridden from BasicHttpEntity */
137   public SerializedEntity setCached() throws IOException {
138      super.setCached();
139      return this;
140   }
141
142   @Override /* Overridden from BasicHttpEntity */
143   public SerializedEntity setCharset(Charset value) {
144      super.setCharset(value);
145      return this;
146   }
147
148   @Override /* Overridden from BasicHttpEntity */
149   public SerializedEntity setChunked() {
150      super.setChunked();
151      return this;
152   }
153
154   @Override /* Overridden from BasicHttpEntity */
155   public SerializedEntity setChunked(boolean value) {
156      super.setChunked(value);
157      return this;
158   }
159
160   @Override /* Overridden from BasicHttpEntity */
161   public SerializedEntity setContent(Object value) {
162      super.setContent(value);
163      return this;
164   }
165
166   @Override /* Overridden from BasicHttpEntity */
167   public SerializedEntity setContent(Supplier<?> value) {
168      super.setContent(value);
169      return this;
170   }
171
172   @Override /* Overridden from BasicHttpEntity */
173   public SerializedEntity setContentEncoding(ContentEncoding value) {
174      super.setContentEncoding(value);
175      return this;
176   }
177
178   @Override /* Overridden from BasicHttpEntity */
179   public SerializedEntity setContentEncoding(String value) {
180      super.setContentEncoding(value);
181      return this;
182   }
183
184   @Override /* Overridden from BasicHttpEntity */
185   public SerializedEntity setContentLength(long value) {
186      super.setContentLength(value);
187      return this;
188   }
189
190   @Override /* Overridden from BasicHttpEntity */
191   public SerializedEntity setContentType(ContentType value) {
192      super.setContentType(value);
193      return this;
194   }
195
196   @Override /* Overridden from BasicHttpEntity */
197   public SerializedEntity setContentType(String value) {
198      super.setContentType(value);
199      return this;
200   }
201
202   @Override /* Overridden from BasicHttpEntity */
203   public SerializedEntity setMaxLength(int value) {
204      super.setMaxLength(value);
205      return this;
206   }
207
208   /**
209    * Sets the schema on this entity bean.
210    *
211    * <p>
212    * Used to provide instructions to the serializer on how to serialize this object.
213    *
214    * @param value The entity schema, can be <jk>null</jk>.
215    * @return This object.
216    */
217   public SerializedEntity setSchema(HttpPartSchema value) {
218      assertModifiable();
219      schema = value;
220      return this;
221   }
222
223   /**
224    * Sets the serializer on this entity bean.
225    *
226    * @param value The entity serializer, can be <jk>null</jk>.
227    * @return This object.
228    */
229   public SerializedEntity setSerializer(Serializer value) {
230      assertModifiable();
231      serializer = value;
232      return this;
233   }
234
235   @Override /* Overridden from BasicHttpEntity */
236   public SerializedEntity setUnmodifiable() {
237      super.setUnmodifiable();
238      return this;
239   }
240
241   @Override /* Overridden from HttpEntity */
242   public void writeTo(OutputStream os) throws IOException {
243      try {
244         os = new NoCloseOutputStream(os);
245         Object o = contentOrElse(null);
246         if (serializer == null) {
247            try (Writer w = new OutputStreamWriter(os, UTF8)) {
248               w.write(o.toString());
249            }
250         } else {
251            SerializerSession session = serializer.createSession().schema(schema).build();
252            try (Closeable c = session.isWriterSerializer() ? new OutputStreamWriter(os, UTF8) : os) {
253               session.serialize(o, c);
254            }
255         }
256      } catch (SerializeException e) {
257         throw rex(e, "Serialization error on request body.");
258      }
259   }
260}