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}