001/**
002 * Copyright 2014 Tampere University of Technology, Pori Department
003 * 
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * 
008 *   http://www.apache.org/licenses/LICENSE-2.0
009 * 
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package core.tut.pori.http;
017
018import java.io.IOException;
019
020import javax.servlet.http.HttpServletResponse;
021import javax.xml.bind.annotation.XmlAccessType;
022import javax.xml.bind.annotation.XmlAccessorType;
023import javax.xml.bind.annotation.XmlAttribute;
024import javax.xml.bind.annotation.XmlElement;
025import javax.xml.bind.annotation.XmlElementRef;
026import javax.xml.bind.annotation.XmlEnum;
027import javax.xml.bind.annotation.XmlRootElement;
028
029import org.apache.log4j.Logger;
030
031import core.tut.pori.utils.XMLFormatter;
032
033
034/**
035 * Basic HTTP response, which prints the given ResponseData as an xml.
036 * 
037 * By default, the status of the response is 200 OK
038 */
039@XmlRootElement(name=Definitions.ELEMENT_RESPONSE)
040@XmlAccessorType(XmlAccessType.NONE)
041public class Response {
042  private static final Logger LOGGER = Logger.getLogger(Response.class);
043  @XmlElementRef
044  private ResponseData _responseData = null;
045  @XmlAttribute(name=Definitions.ATTRIBUTE_METHOD)
046  private String _method = null;
047  @XmlElement(name=Definitions.ELEMENT_MESSAGE)
048  private String _message = null;
049  @XmlAttribute(name=Definitions.ATTRIBUTE_SERVICE)
050  private String _service = null; 
051  @XmlElement(name=Definitions.ELEMENT_STATUS)
052  private Status _status = Status.OK;
053
054  
055  /**
056   * HTTP Status code enumerations.
057   * 
058   * As defined by http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
059   */
060  @XmlEnum
061  public enum Status{
062    /** 200 OK response */
063    OK(200),
064    /** 204 No Content */
065    NO_CONTENT(204),
066    /** 400 Bad Request response */
067    BAD_REQUEST(400),
068    /** 401 Unauthorized response */
069    UNAUTHORIZED(401),
070    /** 403 Forbidden response */
071    FORBIDDEN(403),
072    /** 404 Not Found response */
073    NOT_FOUND(404),
074    /** 440 Login Timeout response, Microsoft/Outlook extension. In this case used more liberally for all login timeout cases. */
075    LOGIN_TIMEOUT(440),
076    /** 500 Internal Server Error response */
077    INTERNAL_SERVER_ERROR(500),
078    /** 503 Service Unavailable response */
079    SERVICE_UNAVAILABLE(503);
080    
081    private int _code;
082    
083    /**
084     * 
085     * @param code
086     */
087    private Status(int code){
088      _code = code;
089    }
090    
091    /**
092     * convert to HTTP status code
093     * @return the status as HTTP status code
094     */
095    public int toStatusCode(){
096      return _code;
097    }
098    
099    /**
100     * 
101     * @param code
102     * @return status code from the given integer code, returns INTERNAL_SERVER_ERROR on unknown error code
103     */
104    public static Status fromStatusCode(int code){
105      for(Status s : Status.values()){
106        if(s._code == code){
107          return s;
108        }
109      }
110      return INTERNAL_SERVER_ERROR;
111    }
112  } // enum Status
113  
114  /**
115   * for serialization
116   */
117  public Response(){
118    // nothing needed
119  }
120  
121  /**
122   * 
123   * @param status
124   */
125  public Response(Status status){
126    _status = status;
127  }
128  
129  /**
130   * 
131   * @param status
132   * @param message
133   */
134  public Response(Status status, String message){
135    _status = status;
136    _message = message;
137  }
138  
139  /**
140   * 
141   * @param data
142   */
143  public Response(ResponseData data){
144    _responseData = data;
145  }
146  
147  /**
148   * Write this response to the given response object.
149   * 
150   * This method can be overridden to provide custom responses.
151   * 
152   * By default this method writes this object to the stream as XML based on JAXB annotations
153   * and sets the encoding ({@value core.tut.pori.http.Definitions#ENCODING_UTF8}) and content type ({@value core.tut.pori.http.Definitions#CONTENT_TYPE_XML}) to appropriate values.
154   * These to parameters should be changed for the response if format is changed in the overriding method.
155   * 
156   * Additionally this method sets the HTTP basic authentication header if the status is set {@link core.tut.pori.http.Response.Status#UNAUTHORIZED}.
157   * 
158   * @param response
159   * @see #setDefaultAuthenticationHeader(HttpServletResponse)
160   * @see #setStatus(core.tut.pori.http.Response.Status)
161   */
162  public void writeTo(HttpServletResponse response){
163    try {
164      response.setContentType(Definitions.CONTENT_TYPE_XML);
165      response.setCharacterEncoding(Definitions.ENCODING_UTF8);
166      response.getWriter().write((new XMLFormatter()).toString(this));
167    } catch (IOException ex) {
168      LOGGER.error(ex, ex);
169      _status = Status.INTERNAL_SERVER_ERROR;
170    }
171    
172    if(_status != Status.OK){ // don't change defaults if there is OK status
173      response.setStatus(_status.toStatusCode());
174      if(_status == Status.UNAUTHORIZED){
175        setDefaultAuthenticationHeader(response);
176      }
177    }
178  }
179  
180  /**
181   * Set the default HTTP authentication header in the response.
182   * 
183   * <a href="http://tools.ietf.org/html/rfc2617#section-2">HTTP Basic Authentication</a>
184   * 
185   * @param response
186   */
187  public static void setDefaultAuthenticationHeader(HttpServletResponse response){
188    response.setHeader(Definitions.HEADER_AUTHENTICATE, Definitions.HEADER_AUTHENTICATE_VALUE);
189  }
190
191  /**
192   * @return the status
193   */
194  public Status getStatus() {
195    return _status;
196  }
197
198  /**
199   * @param status the status to set
200   */
201  public void setStatus(Status status) {
202    _status = status;
203  }
204
205  /**
206   * @return the method
207   */
208  public String getMethod() {
209    return _method;
210  }
211
212  /**
213   * @param method the method to set
214   */
215  public void setMethod(String method) {
216    _method = method;
217  }
218
219  /**
220   * @return the service
221   */
222  public String getService() {
223    return _service;
224  }
225
226  /**
227   * @param service the service to set
228   */
229  public void setService(String service) {
230    _service = service;
231  }
232
233  /**
234   * @return the data
235   */
236  public ResponseData getResponseData() {
237    return _responseData;
238  }
239
240  /**
241   * @param data the data to set
242   */
243  public void setResponseData(ResponseData data) {
244    _responseData = data;
245  }
246
247  /**
248   * 
249   * @return response message
250   */
251  public String getMessage() {
252    return _message;
253  }
254
255  /**
256   * Set an optional message to be shown with the response
257   * 
258   * @param message
259   */
260  public void setMessage(String message) {
261    _message = message;
262  }
263}