001    /*
002     * The contents of this file are subject to the terms
003     * of the Common Development and Distribution License
004     * (the "License").  You may not use this file except
005     * in compliance with the License.
006     *
007     * You can obtain a copy of the license at
008     * http://www.opensource.org/licenses/cddl1.php
009     * See the License for the specific language governing
010     * permissions and limitations under the License.
011     */
012    
013    /*
014     * Response.java
015     *
016     * Created on April 18, 2007, 9:00 AM
017     *
018     */
019    
020    package javax.ws.rs.core;
021    
022    import java.net.URI;
023    import java.util.Date;
024    import java.util.List;
025    import java.util.Locale;
026    import javax.ws.rs.ext.RuntimeDelegate;
027    
028    /**
029     * Defines the contract between a returned instance and the runtime when
030     * an application needs to provide metadata to the runtime. An application
031     * class can extend this class directly or can use one of the static 
032     * methods to create an instance using a ResponseBuilder.
033     * 
034     * Several methods have parameters of type URI, {@link UriBuilder} provides
035     * convenient methods to create such values as does 
036     * {@link <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/net/URI.html#create(java.lang.String)">URI.create()</a>}.
037     * 
038     * @see Response.ResponseBuilder
039     */
040    public abstract class Response {
041        
042        /**
043         * Protected constructor, use one of the static methods to obtain a 
044         * {@link ResponseBuilder} instance and obtain a Response from that.
045         */
046        protected Response() {}
047        
048        
049        /**
050         * Return the response entity. The response will be serialized using a
051         * MessageBodyWriter for either the class of the entity or, in the case of
052         * {@link GenericEntity}, the value of {@link GenericEntity#getRawType()}.
053         * @return an object instance or null if there is no entity
054         * @see javax.ws.rs.ext.MessageBodyWriter
055         */
056        public abstract Object getEntity();
057        
058        /**
059         * Get the status code associated with the response.
060         * @return the response status code or -1 if the status was not set.  
061         */
062        public abstract int getStatus();
063    
064        /**
065         * Get metadata associated with the response as a map. The returned map
066         * may be subsequently modified by the JAX-RS runtime. Values will be
067         * serialized using a {@link javax.ws.rs.ext.RuntimeDelegate.HeaderDelegate}
068         * if one is available via
069         * {@link javax.ws.rs.ext.RuntimeDelegate#createHeaderDelegate(java.lang.Class)}
070         * for the class of the value or using the values {@code toString} method if a 
071         * header delegate is not available.
072         * @return response metadata as a map
073         */
074        public abstract MultivaluedMap<String, Object> getMetadata();
075        
076        /**
077         * Create a new ResponseBuilder by performing a shallow copy of an
078         * existing Response. The returned builder has its own metadata map but
079         * entries are simply references to the keys and values contained in the
080         * supplied Response metadata map.
081         * @param response a Response from which the status code, entity and metadata
082         * will be copied
083         * @return a new ReponseBuilder
084         */
085        public static ResponseBuilder fromResponse(Response response) {
086            ResponseBuilder b = status(response.getStatus());
087            b.entity(response.getEntity());
088            for (String headerName: response.getMetadata().keySet()) {
089                List<Object> headerValues = response.getMetadata().get(headerName);
090                for (Object headerValue: headerValues) {
091                    b.header(headerName, headerValue);
092                }
093            }
094            return b;
095        }
096        
097        /**
098         * Create a new ResponseBuilder with the supplied status.
099         * @param status the response status
100         * @return a new ResponseBuilder
101         * @throws IllegalArgumentException if status is null
102         */
103        public static ResponseBuilder status(StatusType status) {
104            ResponseBuilder b = ResponseBuilder.newInstance();
105            b.status(status);
106            return b;
107        }
108    
109        /**
110         * Create a new ResponseBuilder with the supplied status.
111         * @param status the response status
112         * @return a new ResponseBuilder
113         * @throws IllegalArgumentException if status is null
114         */
115        public static ResponseBuilder status(Status status) {
116            return status((StatusType)status);
117        }
118    
119        /**
120         * Create a new ResponseBuilder with the supplied status.
121         * @param status the response status
122         * @return a new ResponseBuilder
123         * @throws IllegalArgumentException if status is less than 100 or greater
124         * than 599.
125         */
126        public static ResponseBuilder status(int status) {
127            ResponseBuilder b = ResponseBuilder.newInstance();
128            b.status(status);
129            return b;
130        }
131    
132        /**
133         * Create a new ResponseBuilder with an OK status.
134         * 
135         * @return a new ResponseBuilder
136         */
137        public static ResponseBuilder ok() {
138            ResponseBuilder b = status(Status.OK);
139            return b;
140        }
141    
142        /**
143         * Create a new ResponseBuilder that contains a representation. It is the
144         * callers responsibility to wrap the actual entity with
145         * {@link GenericEntity} if preservation of its generic type is required.
146         * 
147         * @param entity the representation entity data
148         * @return a new ResponseBuilder
149         */
150        public static ResponseBuilder ok(Object entity) {
151            ResponseBuilder b = ok();
152            b.entity(entity);
153            return b;
154        }
155    
156        /**
157         * Create a new ResponseBuilder that contains a representation. It is the
158         * callers responsibility to wrap the actual entity with
159         * {@link GenericEntity} if preservation of its generic type is required.
160         * 
161         * @param entity the representation entity data
162         * @param type the media type of the entity
163         * @return a new ResponseBuilder
164         */
165        public static ResponseBuilder ok(Object entity, MediaType type) {
166            ResponseBuilder b = ok();
167            b.entity(entity);
168            b.type(type);
169            return b;
170        }
171    
172        /**
173         * Create a new ResponseBuilder that contains a representation. It is the
174         * callers responsibility to wrap the actual entity with
175         * {@link GenericEntity} if preservation of its generic type is required.
176         * 
177         * @param entity the representation entity data
178         * @param type the media type of the entity
179         * @return a new ResponseBuilder
180         */
181        public static ResponseBuilder ok(Object entity, String type) {
182            ResponseBuilder b = ok();
183            b.entity(entity);
184            b.type(type);
185            return b;
186        }
187    
188        /**
189         * Create a new ResponseBuilder that contains a representation. It is the
190         * callers responsibility to wrap the actual entity with
191         * {@link GenericEntity} if preservation of its generic type is required.
192         * 
193         * @param entity the representation entity data
194         * @param variant representation metadata
195         * @return a new ResponseBuilder
196         */
197        public static ResponseBuilder ok(Object entity, Variant variant) {
198            ResponseBuilder b = ok();
199            b.entity(entity);
200            b.variant(variant);
201            return b;
202        }
203    
204        /**
205         * Create a new ResponseBuilder with an server error status.
206         * 
207         * @return a new ResponseBuilder
208         */
209        public static ResponseBuilder serverError() {
210            ResponseBuilder b = status(Status.INTERNAL_SERVER_ERROR);
211            return b;
212        }
213    
214        /**
215         * Create a new ResponseBuilder for a created resource, set the location
216         * header using the supplied value.
217         * 
218         * @param location the URI of the new resource. If a relative URI is 
219         * supplied it will be converted into an absolute URI by resolving it
220         * relative to the request URI (see {@link UriInfo#getRequestUri}).
221         * @return a new ResponseBuilder
222         * @throws java.lang.IllegalArgumentException if location is null
223         */
224        public static ResponseBuilder created(URI location) {
225            ResponseBuilder b = status(Status.CREATED).location(location);
226            return b;
227        }
228    
229        /**
230         * Create a new ResponseBuilder for an empty response.
231         * 
232         * @return a new ResponseBuilder
233         */
234        public static ResponseBuilder noContent() {
235            ResponseBuilder b = status(Status.NO_CONTENT);
236            return b;
237        }
238    
239        /**
240         * Create a new ResponseBuilder with a not-modified status.
241         * 
242         * @return a new ResponseBuilder
243         */
244        public static ResponseBuilder notModified() {
245            ResponseBuilder b = status(Status.NOT_MODIFIED);
246            return b;
247        }
248    
249        /**
250         * Create a new ResponseBuilder with a not-modified status.
251         * 
252         * @param tag a tag for the unmodified entity
253         * @return a new ResponseBuilder
254         * @throws java.lang.IllegalArgumentException if tag is null
255         */
256        public static ResponseBuilder notModified(EntityTag tag) {
257            ResponseBuilder b = notModified();
258            b.tag(tag);
259            return b;
260        }
261    
262        /**
263         * Create a new ResponseBuilder with a not-modified status
264         * and a strong entity tag. This is a shortcut
265         * for <code>notModified(new EntityTag(<i>value</i>))</code>.
266         * 
267         * @param tag the string content of a strong entity tag. The JAX-RS
268         * runtime will quote the supplied value when creating the header.
269         * @return a new ResponseBuilder
270         * @throws java.lang.IllegalArgumentException if tag is null
271         */
272        public static ResponseBuilder notModified(String tag) {
273            ResponseBuilder b = notModified();
274            b.tag(tag);
275            return b;
276        }
277    
278        /**
279         * Create a new ResponseBuilder for a redirection. Used in the
280         * redirect-after-POST (aka POST/redirect/GET) pattern.
281         * 
282         * @param location the redirection URI. If a relative URI is 
283         * supplied it will be converted into an absolute URI by resolving it
284         * relative to the base URI of the application (see 
285         * {@link UriInfo#getBaseUri}).
286         * @return a new ResponseBuilder
287         * @throws java.lang.IllegalArgumentException if location is null
288         */
289        public static ResponseBuilder seeOther(URI location) {
290            ResponseBuilder b = status(Status.SEE_OTHER).location(location);
291            return b;
292        }
293    
294        /**
295         * Create a new ResponseBuilder for a temporary redirection.
296         * 
297         * @param location the redirection URI. If a relative URI is 
298         * supplied it will be converted into an absolute URI by resolving it
299         * relative to the base URI of the application (see 
300         * {@link UriInfo#getBaseUri}).
301         * @return a new ResponseBuilder
302         * @throws java.lang.IllegalArgumentException if location is null
303         */
304        public static ResponseBuilder temporaryRedirect(URI location) {
305            ResponseBuilder b = status(Status.TEMPORARY_REDIRECT).location(location);
306            return b;
307        }
308    
309        /**
310         * Create a new ResponseBuilder for a not acceptable response.
311         * 
312         * @param variants list of variants that were available, a null value is
313         * equivalent to an empty list.
314         * @return a new ResponseBuilder
315         */
316        public static ResponseBuilder notAcceptable(List<Variant> variants) {
317            ResponseBuilder b = status(Status.NOT_ACCEPTABLE).variants(variants);
318            return b;
319        }
320            
321        /**
322         * A class used to build Response instances that contain metadata instead 
323         * of or in addition to an entity. An initial instance may be obtained via
324         * static methods of the Response class, instance methods provide the
325         * ability to set metadata. E.g. to create a response that indicates the 
326         * creation of a new resource:
327         * <pre>&#64;POST
328         * Response addWidget(...) {
329         *   Widget w = ...
330         *   URI widgetId = UriBuilder.fromResource(Widget.class)...
331         *   return Response.created(widgetId).build();
332         * }</pre>
333         * 
334         * <p>Several methods have parameters of type URI, {@link UriBuilder} provides
335         * convenient methods to create such values as does <code>URI.create()</code>.</p>
336         * 
337         * <p>Where multiple variants of the same method are provided, the type of
338         * the supplied parameter is retained in the metadata of the built 
339         * {@code Response}.</p>  
340         * 
341         */
342        public static abstract class ResponseBuilder {
343    
344            /**
345             * Protected constructor, use one of the static methods of
346             * <code>Response</code> to obtain an instance.
347             */
348            protected ResponseBuilder() {}
349            
350            /**
351             * Create a new builder instance.
352             * 
353             * @return a new ResponseBuilder
354             */
355            protected static ResponseBuilder newInstance() {
356                ResponseBuilder b = RuntimeDelegate.getInstance().createResponseBuilder();
357                return b;
358            }
359            
360            /**
361             * Create a Response instance from the current ResponseBuilder. The builder
362             * is reset to a blank state equivalent to calling the ok method.
363             * 
364             * @return a Response instance
365             */
366            public abstract Response build();
367            
368            /**
369             * Create a copy of the ResponseBuilder preserving its state.
370             * @return a copy of the ResponseBuilder
371             */
372            @Override
373            public abstract ResponseBuilder clone();
374    
375            /**
376             * Set the status on the ResponseBuilder.
377             * 
378             * @param status the response status
379             * @return the updated ResponseBuilder
380             * @throws IllegalArgumentException if status is less than 100 or greater
381             * than 599.
382             */
383            public abstract ResponseBuilder status(int status);
384            
385            /**
386             * Set the status on the ResponseBuilder.
387             * 
388             * @param status the response status
389             * @return the updated ResponseBuilder
390             * @throws IllegalArgumentException if status is null
391             */
392            public ResponseBuilder status(StatusType status) {
393                if (status == null)
394                    throw new IllegalArgumentException();
395                return status(status.getStatusCode());
396            };
397            
398            /**
399             * Set the status on the ResponseBuilder.
400             *
401             * @param status the response status
402             * @return the updated ResponseBuilder
403             * @throws IllegalArgumentException if status is null
404             */
405            public ResponseBuilder status(Status status) {
406                return status((StatusType)status);
407            };
408    
409            /**
410             * Set the entity on the ResponseBuilder. It is the
411             * callers responsibility to wrap the actual entity with
412             * {@link GenericEntity} if preservation of its generic type is required.
413             * 
414             * @param entity the response entity
415             * @return the updated ResponseBuilder
416             */
417            public abstract ResponseBuilder entity(Object entity);
418            
419            /**
420             * Set the response media type on the ResponseBuilder.
421             * 
422             * @param type the media type of the response entity, if null any 
423             * existing value for type will be removed
424             * @return the updated ResponseBuilder
425             */
426            public abstract ResponseBuilder type(MediaType type);
427            
428            /**
429             * Set the response media type on the ResponseBuilder.
430             * 
431             * @param type the media type of the response entity, if null any 
432             * existing value for type will be removed
433             * @return the updated ResponseBuilder
434             * @throws IllegalArgumentException if type cannot be parsed
435             */
436            public abstract ResponseBuilder type(String type);
437            
438            /**
439             * Set representation metadata on the ResponseBuilder. Equivalent to
440             * setting the values of content type, content language, and content
441             * encoding separately using the values of the variant properties.
442             * 
443             * @param variant metadata of the response entity, a null value is
444             * equivalent to a variant with all null properties.
445             * @return the updated ResponseBuilder
446             */
447            public abstract ResponseBuilder variant(Variant variant);
448            
449            /**
450             * Add a Vary header that lists the available variants.
451             * 
452             * @param variants a list of available representation variants, a null
453             * value will remove an existing value for vary.
454             * @return the updated ResponseBuilder
455             */
456            public abstract ResponseBuilder variants(List<Variant> variants);
457    
458            /**
459             * Set the language on the ResponseBuilder.
460             * 
461             * @param language the language of the response entity, if null any 
462             * existing value for language will be removed
463             * @return the updated ResponseBuilder
464             */
465            public abstract ResponseBuilder language(String language);
466            
467            /**
468             * Set the language on the ResponseBuilder.
469             * 
470             * 
471             * @param language the language of the response entity, if null any 
472             * existing value for type will be removed
473             * @return the updated ResponseBuilder
474             */
475            public abstract ResponseBuilder language(Locale language);
476            
477            /**
478             * Set the location on the ResponseBuilder.
479             * 
480             * @param location the location. If a relative URI is 
481             * supplied it will be converted into an absolute URI by resolving it
482             * relative to the base URI of the application (see 
483             * {@link UriInfo#getBaseUri}). If null any 
484             * existing value for location will be removed.
485             * @return the updated ResponseBuilder
486             */
487            public abstract ResponseBuilder location(URI location);
488            
489            /**
490             * Set the content location on the ResponseBuilder.
491             * 
492             * @param location the content location. Relative or absolute URIs
493             * may be used for the value of content location. If null any 
494             * existing value for content location will be removed.
495             * @return the updated ResponseBuilder
496             */
497            public abstract ResponseBuilder contentLocation(URI location);
498            
499            /**
500             * Set an entity tag on the ResponseBuilder.
501             * 
502             * @param tag the entity tag, if null any 
503             * existing entity tag value will be removed.
504             * @return the updated ResponseBuilder
505             */
506            public abstract ResponseBuilder tag(EntityTag tag);
507            
508            /**
509             * Set a strong entity tag on the ResponseBuilder. This is a shortcut
510             * for <code>tag(new EntityTag(<i>value</i>))</code>.
511             * 
512             * @param tag the string content of a strong entity tag. The JAX-RS
513             * runtime will quote the supplied value when creating the header. If
514             * null any existing entity tag value will be removed.
515             * @return the updated ResponseBuilder
516             */
517            public abstract ResponseBuilder tag(String tag);
518            
519            /**
520             * Set the last modified date on the ResponseBuilder.
521             * 
522             * @param lastModified the last modified date, if null any existing
523             * last modified value will be removed.
524             * @return the updated ResponseBuilder
525             */
526            public abstract ResponseBuilder lastModified(Date lastModified);
527            
528            /**
529             * Set the cache control data on the ResponseBuilder.
530             * 
531             * @param cacheControl the cache control directives, if null removes any
532             * existing cache control directives.
533             * @return the updated ResponseBuilder
534             */
535            public abstract ResponseBuilder cacheControl(CacheControl cacheControl);
536            
537            /**
538             * Set the expires date on the ResponseBuilder.
539             * 
540             * @param expires the expiration date, if null removes any existing
541             * expires value.
542             * @return the updated ResponseBuilder
543             */
544            public abstract ResponseBuilder expires(Date expires);
545            
546            /**
547             * Add a header to the ResponseBuilder.
548             * 
549             * @param name the name of the header
550             * @param value the value of the header, the header will be serialized
551             * using a {@link javax.ws.rs.ext.RuntimeDelegate.HeaderDelegate} if
552             * one is available via
553             * {@link javax.ws.rs.ext.RuntimeDelegate#createHeaderDelegate(java.lang.Class)}
554             * for the class of {@code value} or using its {@code toString} method if a 
555             * header delegate is not available. If {@code value} is null then all 
556             * current headers of the same name will be removed.
557             * @return the updated ResponseBuilder
558             */
559            public abstract ResponseBuilder header(String name, Object value);
560            
561            /**
562             * Add cookies to the ResponseBuilder.
563             * 
564             * @param cookies new cookies that will accompany the response. A null
565             * value will remove all cookies, including those added via the 
566             * {@link #header(java.lang.String, java.lang.Object)} method.
567             * @return the updated ResponseBuilder
568             */
569            public abstract ResponseBuilder cookie(NewCookie... cookies);
570        }
571        
572        /**
573         * Base interface for statuses used in responses.
574         */
575        public interface StatusType {
576            /**
577             * Get the associated status code
578             * @return the status code
579             */
580            public int getStatusCode();
581    
582            /**
583             * Get the class of status code
584             * @return the class of status code
585             */
586            public Status.Family getFamily();
587    
588            /**
589             * Get the reason phrase
590             * @return the reason phrase
591             */
592            public String getReasonPhrase();
593        }
594        
595        /**
596         * Commonly used status codes defined by HTTP, see 
597         * {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10">HTTP/1.1 documentation</a>}
598         * for the complete list. Additional status codes can be added by applications
599         * by creating an implementation of {@link StatusType}.
600         */
601        public enum Status implements StatusType {
602            /**
603             * 200 OK, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">HTTP/1.1 documentation</a>}.
604             */
605            OK(200, "OK"),
606            /**
607             * 201 Created, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.2">HTTP/1.1 documentation</a>}.
608             */
609            CREATED(201, "Created"),
610            /**
611             * 202 Accepted, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.3">HTTP/1.1 documentation</a>}.
612             */
613            ACCEPTED(202, "Accepted"),
614            /**
615             * 204 No Content, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.5">HTTP/1.1 documentation</a>}.
616             */
617            NO_CONTENT(204, "No Content"),
618            /**
619             * 301 Moved Permanently, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.2">HTTP/1.1 documentation</a>}.
620             */
621            MOVED_PERMANENTLY(301, "Moved Permanently"),
622            /**
623             * 303 See Other, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4">HTTP/1.1 documentation</a>}.
624             */
625            SEE_OTHER(303, "See Other"),
626            /**
627             * 304 Not Modified, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5">HTTP/1.1 documentation</a>}.
628             */
629            NOT_MODIFIED(304, "Not Modified"),
630            /**
631             * 307 Temporary Redirect, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.8">HTTP/1.1 documentation</a>}.
632             */
633            TEMPORARY_REDIRECT(307, "Temporary Redirect"),
634            /**
635             * 400 Bad Request, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">HTTP/1.1 documentation</a>}.
636             */
637            BAD_REQUEST(400, "Bad Request"),
638            /**
639             * 401 Unauthorized, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">HTTP/1.1 documentation</a>}.
640             */
641            UNAUTHORIZED(401, "Unauthorized"),
642            /**
643             * 403 Forbidden, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4">HTTP/1.1 documentation</a>}.
644             */
645            FORBIDDEN(403, "Forbidden"),
646            /**
647             * 404 Not Found, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5">HTTP/1.1 documentation</a>}.
648             */
649            NOT_FOUND(404, "Not Found"),
650            /**
651             * 406 Not Acceptable, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7">HTTP/1.1 documentation</a>}.
652             */
653            NOT_ACCEPTABLE(406, "Not Acceptable"),
654            /**
655             * 409 Conflict, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10">HTTP/1.1 documentation</a>}.
656             */
657            CONFLICT(409, "Conflict"),
658            /**
659             * 410 Gone, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.11">HTTP/1.1 documentation</a>}.
660             */
661            GONE(410, "Gone"),
662            /**
663             * 412 Precondition Failed, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.13">HTTP/1.1 documentation</a>}.
664             */
665            PRECONDITION_FAILED(412, "Precondition Failed"),
666            /**
667             * 415 Unsupported Media Type, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.16">HTTP/1.1 documentation</a>}.
668             */
669            UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"),
670            /**
671             * 500 Internal Server Error, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1">HTTP/1.1 documentation</a>}.
672             */
673            INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
674            /**
675             * 503 Service Unavailable, see {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4">HTTP/1.1 documentation</a>}.
676             */
677            SERVICE_UNAVAILABLE(503, "Service Unavailable");
678            
679            private final int code;
680            private final String reason;
681            private Family family;
682            
683            /**
684             * An enumeration representing the class of status code. Family is used
685             * here since class is overloaded in Java.
686             */
687            public enum Family {INFORMATIONAL, SUCCESSFUL, REDIRECTION, CLIENT_ERROR, SERVER_ERROR, OTHER};
688    
689            Status(final int statusCode, final String reasonPhrase) {
690                this.code = statusCode;
691                this.reason = reasonPhrase;
692                switch(code/100) {
693                    case 1: this.family = Family.INFORMATIONAL; break;
694                    case 2: this.family = Family.SUCCESSFUL; break;
695                    case 3: this.family = Family.REDIRECTION; break;
696                    case 4: this.family = Family.CLIENT_ERROR; break;
697                    case 5: this.family = Family.SERVER_ERROR; break;
698                    default: this.family = Family.OTHER; break;
699                }
700            }
701            
702            /**
703             * Get the class of status code
704             * @return the class of status code
705             */
706            public Family getFamily() {
707                return family;
708            }
709            
710            /**
711             * Get the associated status code
712             * @return the status code
713             */
714            public int getStatusCode() {
715                return code;
716            }
717            
718            /**
719             * Get the reason phrase
720             * @return the reason phrase
721             */
722            public String getReasonPhrase() {
723                return toString();
724            }
725            
726            /**
727             * Get the reason phrase
728             * @return the reason phrase
729             */
730            @Override
731            public String toString() {
732                return reason;
733            }
734            
735            /**
736             * Convert a numerical status code into the corresponding Status
737             * @param statusCode the numerical status code
738             * @return the matching Status or null is no matching Status is defined
739             */
740            public static Status fromStatusCode(final int statusCode) {
741                for (Status s : Status.values()) {
742                    if (s.code == statusCode) {
743                        return s;
744                    }
745                }
746                return null;
747            }
748        }
749    }