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