001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.http.response;
014
015import static org.apache.juneau.assertions.Assertions.*;
016import static org.apache.juneau.common.internal.ArgUtils.*;
017import static org.apache.juneau.http.HttpEntities.*;
018
019import java.net.*;
020import java.util.*;
021
022import org.apache.http.*;
023import org.apache.http.impl.*;
024import org.apache.http.params.*;
025import org.apache.juneau.annotation.*;
026import org.apache.juneau.http.*;
027import org.apache.juneau.http.header.*;
028import org.apache.juneau.internal.*;
029
030/**
031 * Basic implementation of the {@link HttpResponse} interface.
032 *
033 * <p>
034 * Although this class implements the various setters defined on the {@link HttpResponse} interface, it's in general
035 * going to be more efficient to set the status/headers/content of this bean through the builder.
036 *
037 * <p>
038 * If the <c>unmodifiable</c> flag is set on this bean, calls to the setters will throw {@link UnsupportedOperationException} exceptions.
039 *
040 * <h5 class='section'>Notes:</h5><ul>
041 *    <li class='warn'>Beans are not thread safe unless they're marked as unmodifiable.
042 * </ul>
043 *
044 * <h5 class='section'>See Also:</h5><ul>
045 *    <li class='link'><a class="doclink" href="../../../../../index.html#juneau-rest-common">juneau-rest-common</a>
046 * </ul>
047 */
048@BeanIgnore /* Use toString() to serialize */
049@FluentSetters
050public class BasicHttpResponse implements HttpResponse {
051
052   //-----------------------------------------------------------------------------------------------------------------
053   // Instance
054   //-----------------------------------------------------------------------------------------------------------------
055
056   BasicStatusLine statusLine = new BasicStatusLine();
057   HeaderList headers = HeaderList.create();
058   HttpEntity content;
059   boolean unmodifiable;
060
061   /**
062    * Constructor.
063    *
064    * @param statusLine The HTTP status line.
065    */
066   public BasicHttpResponse(BasicStatusLine statusLine) {
067      setStatusLine(statusLine.copy());
068   }
069
070   /**
071    * Copy constructor.
072    *
073    * @param copyFrom The bean to copy from.
074    */
075   public BasicHttpResponse(BasicHttpResponse copyFrom) {
076      statusLine = copyFrom.statusLine.copy();
077      headers = copyFrom.headers.copy();
078      content = copyFrom.content;
079   }
080
081   /**
082    * Constructor.
083    *
084    * <p>
085    * This is the constructor used when parsing an HTTP response.
086    *
087    * @param response The HTTP response to copy from.  Must not be <jk>null</jk>.
088    */
089   public BasicHttpResponse(HttpResponse response) {
090      setHeaders(response.getAllHeaders());
091      setContent(response.getEntity());
092      setStatusLine(response.getStatusLine());
093   }
094
095   //-----------------------------------------------------------------------------------------------------------------
096   // Properties
097   //-----------------------------------------------------------------------------------------------------------------
098
099   /**
100    * Specifies whether this bean should be unmodifiable.
101    * <p>
102    * When enabled, attempting to set any properties on this bean will cause an {@link UnsupportedOperationException}.
103    *
104    * @return This object.
105    */
106   @FluentSetter
107   public BasicHttpResponse setUnmodifiable() {
108      unmodifiable = true;
109      return this;
110   }
111
112   /**
113    * Returns <jk>true</jk> if this bean is unmodifiable.
114    *
115    * @return <jk>true</jk> if this bean is unmodifiable.
116    */
117   public boolean isUnmodifiable() {
118      return unmodifiable;
119   }
120
121   /**
122    * Throws an {@link UnsupportedOperationException} if the unmodifiable flag is set on this bean.
123    */
124   protected final void assertModifiable() {
125      if (unmodifiable)
126         throw new UnsupportedOperationException("Bean is read-only");
127   }
128
129   //-----------------------------------------------------------------------------------------------------------------
130   // BasicStatusLine setters.
131   //-----------------------------------------------------------------------------------------------------------------
132
133   /**
134    * Sets the protocol version on the status line.
135    *
136    * <p>
137    * If not specified, <js>"HTTP/1.1"</js> will be used.
138    *
139    * @param value The new value.
140    * @return This object.
141    */
142   @FluentSetter
143   public BasicHttpResponse setStatusLine(BasicStatusLine value) {
144      assertModifiable();
145      statusLine = value.copy();
146      return this;
147   }
148
149   /**
150    * Sets the status code on the status line.
151    *
152    * <p>
153    * If not specified, <c>0</c> will be used.
154    *
155    * @param value The new value.
156    * @return This object.
157    */
158   @FluentSetter
159   public BasicHttpResponse setStatusCode2(int value) {
160      statusLine.setStatusCode(value);
161      return this;
162   }
163
164   /**
165    * Sets the protocol version on the status line.
166    *
167    * <p>
168    * If not specified, <js>"HTTP/1.1"</js> will be used.
169    *
170    * @param value The new value.
171    * @return This object.
172    */
173   @FluentSetter
174   public BasicHttpResponse setProtocolVersion(ProtocolVersion value) {
175      statusLine.setProtocolVersion(value);
176      return this;
177   }
178
179   /**
180    * Sets the reason phrase on the status line.
181    *
182    * <p>
183    * If not specified, the reason phrase will be retrieved from the reason phrase catalog
184    * using the locale on this builder.
185    *
186    * @param value The new value.
187    * @return This object.
188    */
189   @FluentSetter
190   public BasicHttpResponse setReasonPhrase2(String value) {
191      statusLine.setReasonPhrase(value);
192      return this;
193   }
194
195   /**
196    * Sets the reason phrase catalog used to retrieve reason phrases.
197    *
198    * <p>
199    * If not specified, uses {@link EnglishReasonPhraseCatalog}.
200    *
201    * @param value The new value.
202    * @return This object.
203    */
204   @FluentSetter
205   public BasicHttpResponse setReasonPhraseCatalog(ReasonPhraseCatalog value) {
206      statusLine.setReasonPhraseCatalog(value);
207      return this;
208   }
209
210   /**
211    * Sets the locale used to retrieve reason phrases.
212    *
213    * <p>
214    * If not specified, uses {@link Locale#getDefault()}.
215    *
216    * @param value The new value.
217    * @return This object.
218    */
219   @FluentSetter
220   public BasicHttpResponse setLocale2(Locale value) {
221      statusLine.setLocale(value);
222      return this;
223   }
224
225   //-----------------------------------------------------------------------------------------------------------------
226   // BasicHeaderGroup setters.
227   //-----------------------------------------------------------------------------------------------------------------
228
229   /**
230    * Returns access to the underlying builder for the headers.
231    *
232    * @return The underlying builder for the headers.
233    */
234   public HeaderList getHeaders() {
235      return headers;
236   }
237
238   /**
239    * Sets the specified headers on this response.
240    *
241    * @param value The new value.
242    * @return This object.
243    */
244   @FluentSetter
245   public BasicHttpResponse setHeaders(HeaderList value) {
246      assertModifiable();
247      headers = value.copy();
248      return this;
249   }
250
251   /**
252    * Sets the specified header to the end of the headers in this builder.
253    *
254    * @param value The header to add.  <jk>null</jk> values are ignored.
255    * @return This object.
256    */
257   @FluentSetter
258   public BasicHttpResponse setHeader2(Header value) {
259      headers.set(value);
260      return this;
261   }
262
263   /**
264    * Sets the specified header to the end of the headers in this builder.
265    *
266    * @param name The header name.
267    * @param value The header value.
268    * @return This object.
269    */
270   @FluentSetter
271   public BasicHttpResponse setHeader2(String name, String value) {
272      headers.set(name, value);
273      return this;
274   }
275
276   /**
277    * Sets the specified headers to the end of the headers in this builder.
278    *
279    * @param values The headers to add.  <jk>null</jk> values are ignored.
280    * @return This object.
281    */
282   @FluentSetter
283   public BasicHttpResponse setHeaders2(Header...values) {
284      headers.set(values);
285      return this;
286   }
287
288   /**
289    * Sets the specified headers to the end of the headers in this builder.
290    *
291    * @param values The headers to add.  <jk>null</jk> values are ignored.
292    * @return This object.
293    */
294   @FluentSetter
295   public BasicHttpResponse setHeaders(List<Header> values) {
296      headers.set(values);
297      return this;
298   }
299
300   /**
301    * Specifies the value for the <c>Location</c> header.
302    *
303    * @param value The new header location.
304    * @return This object.
305    */
306   @FluentSetter
307   public BasicHttpResponse setLocation(URI value) {
308      headers.set(Location.of(value));
309      return this;
310   }
311
312   /**
313    * Specifies the value for the <c>Location</c> header.
314    *
315    * @param value The new header location.
316    * @return This object.
317    */
318   @FluentSetter
319   public BasicHttpResponse setLocation(String value) {
320      headers.set(Location.of(value));
321      return this;
322   }
323
324   //-----------------------------------------------------------------------------------------------------------------
325   // Body setters.
326   //-----------------------------------------------------------------------------------------------------------------
327
328   /**
329    * Sets the body on this response.
330    *
331    * @param value The body on this response.
332    * @return This object.
333    */
334   @FluentSetter
335   public BasicHttpResponse setContent(String value) {
336      return setContent(stringEntity(value));
337   }
338
339   /**
340    * Sets the body on this response.
341    *
342    * @param value The body on this response.
343    * @return This object.
344    */
345   @FluentSetter
346   public BasicHttpResponse setContent(HttpEntity value) {
347      assertModifiable();
348      this.content = value;
349      return this;
350   }
351
352   /**
353    * Asserts that the specified HTTP response has the same status code as the one on the status line of this bean.
354    *
355    * @param response The HTTP response to check.  Must not be <jk>null</jk>.
356    * @throws AssertionError If status code is not what was expected.
357    */
358   protected void assertStatusCode(HttpResponse response) throws AssertionError {
359      assertArgNotNull("response", response);
360      int expected = getStatusLine().getStatusCode();
361      int actual = response.getStatusLine().getStatusCode();
362      assertInteger(actual).setMsg("Unexpected status code.  Expected:[{0}], Actual:[{1}]", expected, actual).is(expected);
363   }
364
365   @Override /* Object */
366   public String toString() {
367      StringBuilder sb = new StringBuilder().append(statusLine).append(' ').append(headers);
368      if (content != null)
369         sb.append(' ').append(content);
370      return sb.toString();
371   }
372
373   @Override /* HttpMessage */
374   public ProtocolVersion getProtocolVersion() {
375      return statusLine.getProtocolVersion();
376   }
377
378   @Override /* HttpMessage */
379   public boolean containsHeader(String name) {
380      return headers.contains(name);
381   }
382
383   @Override /* HttpMessage */
384   public Header[] getHeaders(String name) {
385      return headers.getAll(name);
386   }
387
388   @Override /* HttpMessage */
389   public Header getFirstHeader(String name) {
390      return headers.getFirst(name).orElse(null);
391   }
392
393   @Override /* HttpMessage */
394   public Header getLastHeader(String name) {
395      return headers.getLast(name).orElse(null);
396   }
397
398   @Override /* HttpMessage */
399   public Header[] getAllHeaders() {
400      return headers.getAll();
401   }
402
403   @Override /* HttpMessage */
404   public void addHeader(Header value) {
405      headers.append(value);
406   }
407
408   @Override /* HttpMessage */
409   public void addHeader(String name, String value) {
410      headers.append(name, value);
411   }
412
413   @Override /* HttpMessage */
414   public void setHeader(Header value) {
415      headers.set(value);
416   }
417
418   @Override /* HttpMessage */
419   public void setHeader(String name, String value) {
420      headers.set(name, value);
421   }
422
423   @Override /* HttpMessage */
424   public void setHeaders(Header[] values) {
425      headers.removeAll().append(values);
426   }
427
428   @Override /* HttpMessage */
429   public void removeHeader(Header value) {
430      headers.remove(value);
431   }
432
433   @Override /* HttpMessage */
434   public void removeHeaders(String name) {
435      headers.remove(name);
436   }
437
438   @Override /* HttpMessage */
439   public HeaderIterator headerIterator() {
440      return headers.headerIterator();
441   }
442
443   @Override /* HttpMessage */
444   public HeaderIterator headerIterator(String name) {
445      return headers.headerIterator(name);
446   }
447
448   @SuppressWarnings("deprecation")
449   @Override /* HttpMessage */
450   public HttpParams getParams() {
451      return null;
452   }
453
454   @SuppressWarnings("deprecation")
455   @Override /* HttpMessage */
456   public void setParams(HttpParams params) {
457   }
458
459   @Override /* HttpMessage */
460   public StatusLine getStatusLine() {
461      return statusLine;
462   }
463
464   @Override /* HttpMessage */
465   public void setStatusLine(StatusLine value) {
466      setStatusLine(value.getProtocolVersion(), value.getStatusCode(), value.getReasonPhrase());
467   }
468
469   @Override /* HttpMessage */
470   public void setStatusLine(ProtocolVersion ver, int code) {
471      statusLine.setProtocolVersion(ver).setStatusCode(code);
472   }
473
474   @Override /* HttpMessage */
475   public void setStatusLine(ProtocolVersion ver, int code, String reason) {
476      statusLine.setProtocolVersion(ver).setReasonPhrase(reason).setStatusCode(code);
477   }
478
479   @Override /* HttpMessage */
480   public void setStatusCode(int code) throws IllegalStateException {
481      statusLine.setStatusCode(code);
482   }
483
484   @Override /* HttpMessage */
485   public void setReasonPhrase(String reason) throws IllegalStateException {
486      statusLine.setReasonPhrase(reason);
487   }
488
489   @Override /* HttpMessage */
490   public HttpEntity getEntity() {
491      // Constructing a StringEntity is somewhat expensive, so don't create it unless it's needed.
492      if (content == null)
493         content = stringEntity(getStatusLine().getReasonPhrase());
494      return content;
495   }
496
497   @Override /* HttpMessage */
498   public void setEntity(HttpEntity entity) {
499      assertModifiable();
500      this.content = entity;
501   }
502
503   @Override /* HttpMessage */
504   public Locale getLocale() {
505      return statusLine.getLocale();
506   }
507
508   @Override /* HttpMessage */
509   public void setLocale(Locale loc) {
510      statusLine.setLocale(loc);
511   }
512
513   // <FluentSetters>
514
515   // </FluentSetters>
516}