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.rest;
014
015import java.io.*;
016import java.util.*;
017
018import javax.servlet.http.*;
019
020import org.apache.juneau.httppart.bean.*;
021import org.apache.juneau.rest.util.*;
022
023/**
024 * A wrapper around a single HttpServletRequest/HttpServletResponse pair.
025 */
026public class RestCall {
027
028   private HttpServletRequest req;
029   private HttpServletResponse res;
030   private RestRequest rreq;
031   private RestResponse rres;
032   private UrlPathInfo urlPathInfo;
033   private String pathInfoUndecoded;
034   private long startTime = System.currentTimeMillis();
035   private RestCallLogger logger;
036   private RestCallLoggerConfig loggerConfig;
037
038   /**
039    * Constructor.
040    *
041    * @param req The incoming HTTP servlet request object.
042    * @param res The incoming HTTP servlet response object.
043    */
044   public RestCall(HttpServletRequest req, HttpServletResponse res) {
045      request(req).response(res);
046   }
047
048   //------------------------------------------------------------------------------------------------------------------
049   // Request/response objects.
050   //------------------------------------------------------------------------------------------------------------------
051
052   /**
053    * Overrides the request object on the REST call.
054    *
055    * @param req The new HTTP servlet request.
056    * @return This object (for method chaining).
057    */
058   public RestCall request(HttpServletRequest req) {
059      this.req = req;
060      this.urlPathInfo = null;
061      this.pathInfoUndecoded = null;
062      return this;
063   }
064
065   /**
066    * Overrides the response object on the REST call.
067    *
068    * @param res The new HTTP servlet response.
069    * @return This object (for method chaining).
070    */
071   public RestCall response(HttpServletResponse res) {
072      this.res = res;
073      return this;
074   }
075
076   /**
077    * Set the {@link RestRequest} object on this REST call.
078    *
079    * @param rreq The {@link RestRequest} object on this REST call.
080    * @return This object (for method chaining).
081    */
082   public RestCall restRequest(RestRequest rreq) {
083      request(rreq);
084      this.rreq = rreq;
085      return this;
086   }
087
088   /**
089    * Set the {@link RestResponse} object on this REST call.
090    *
091    * @param rres The {@link RestResponse} object on this REST call.
092    * @return This object (for method chaining).
093    */
094   public RestCall restResponse(RestResponse rres) {
095      response(rres);
096      this.rres = rres;
097      this.rreq.setResponse(rres);
098      return this;
099   }
100
101   /**
102    * Returns the HTTP servlet request of this REST call.
103    *
104    * @return the HTTP servlet request of this REST call.
105    */
106   public HttpServletRequest getRequest() {
107      return req;
108   }
109
110   /**
111    * Returns the HTTP servlet response of this REST call.
112    *
113    * @return the HTTP servlet response of this REST call.
114    */
115   public HttpServletResponse getResponse() {
116      return res;
117   }
118
119   /**
120    * Returns the REST request of this REST call.
121    *
122    * @return the REST request of this REST call.
123    */
124   public RestRequest getRestRequest() {
125      return rreq;
126   }
127
128   /**
129    * Returns the REST response of this REST call.
130    *
131    * @return the REST response of this REST call.
132    */
133   public RestResponse getRestResponse() {
134      return rres;
135   }
136
137   //------------------------------------------------------------------------------------------------------------------
138   // Setters.
139   //------------------------------------------------------------------------------------------------------------------
140
141   /**
142    * Sets the logger to use when logging this call.
143    *
144    * @param logger The logger to use when logging this call.
145    * @return This object (for method chaining).
146    */
147   public RestCall logger(RestCallLogger logger) {
148      this.logger = logger;
149      return this;
150   }
151
152   /**
153    * Sets the logging configuration to use when logging this call.
154    *
155    * @param config The logging configuration to use when logging this call.
156    * @return This object (for method chaining).
157    */
158   public RestCall loggerConfig(RestCallLoggerConfig config) {
159      this.loggerConfig = config;
160      return this;
161   }
162
163   /**
164    * Enables or disabled debug mode on this call.
165    *
166    * @param b The debug flag value.
167    * @return This object (for method chaining).
168    * @throws IOException Occurs if request body could not be cached into memory.
169    */
170   public RestCall debug(boolean b) throws IOException {
171      if (b) {
172         req = CachingHttpServletRequest.wrap(req);
173         res = CachingHttpServletResponse.wrap(res);
174      }
175      req.setAttribute("Debug", b);
176      return this;
177   }
178
179   /**
180    * Sets the HTTP status on this call.
181    *
182    * @param code The status code.
183    * @return This object (for method chaining).
184    */
185   public RestCall status(int code) {
186      res.setStatus(code);
187      return this;
188   }
189
190   /**
191    * Identifies that an exception occurred during this call.
192    *
193    * @param e The thrown exception.
194    * @return This object (for method chaining).
195    */
196   public RestCall exception(Throwable e) {
197      req.setAttribute("Exception", e);
198      return this;
199   }
200
201   /**
202    * Sets metadata about the response.
203    *
204    * @param meta The metadata about the response.
205    * @return This object (for method chaining).
206    */
207   public RestCall responseMeta(ResponseBeanMeta meta) {
208      if (rres != null)
209         rres.setResponseMeta(meta);
210      return this;
211   }
212
213   /**
214    * Sets the output object to serialize as the response of this call.
215    *
216    * @param output The response output POJO.
217    * @return This object (for method chaining).
218    */
219   public RestCall output(Object output) {
220      if (rres != null)
221         rres.setOutput(output);
222      return this;
223   }
224
225   //------------------------------------------------------------------------------------------------------------------
226   // Lifecycle methods.
227   //------------------------------------------------------------------------------------------------------------------
228
229   /**
230    * Called at the end of a call to finish any remaining tasks such as flushing buffers and logging the response.
231    *
232    * @return This object (for method chaining).
233    */
234   public RestCall finish() {
235      try {
236         res.flushBuffer();
237         req.setAttribute("ExecTime", System.currentTimeMillis() - startTime);
238         if (rreq != null)
239            rreq.close();
240      } catch (Exception e) {
241         exception(e);
242      }
243      if (logger != null)
244         logger.log(loggerConfig, req, res);
245      return this;
246   }
247
248
249   //------------------------------------------------------------------------------------------------------------------
250   // Pass-through convenience methods.
251   //------------------------------------------------------------------------------------------------------------------
252
253   /**
254    * Shortcut for calling <c>getRequest().getServletPath()</c>.
255    *
256    * @return The request servlet path.
257    */
258   public String getServletPath() {
259      return req.getServletPath();
260   }
261
262   /**
263    * Returns the request path info as a {@link UrlPathInfo} bean.
264    *
265    * @return The request path info as a {@link UrlPathInfo} bean.
266    */
267   public UrlPathInfo getUrlPathInfo() {
268      if (urlPathInfo == null)
269         urlPathInfo = new UrlPathInfo(getPathInfoUndecoded());
270      return urlPathInfo;
271   }
272
273   /**
274    * Shortcut for calling <c>getRequest().getPathInfo()</c>.
275    *
276    * @return The request servlet path info.
277    */
278   public String getPathInfo() {
279      return req.getPathInfo();
280   }
281
282   /**
283    * Same as {@link #getPathInfo()} but doesn't decode encoded characters.
284    *
285    * @return The undecoded request servlet path info.
286    */
287   public String getPathInfoUndecoded() {
288      if (pathInfoUndecoded == null)
289         pathInfoUndecoded = RestUtils.getPathInfoUndecoded(req);
290      return pathInfoUndecoded;
291   }
292
293   /**
294    * Returns the HTTP method name.
295    *
296    * @return The HTTP method name, always uppercased.
297    */
298   public String getMethod() {
299      if (rreq != null)
300         return rreq.getMethod().toUpperCase(Locale.ENGLISH);
301      return req.getMethod().toUpperCase(Locale.ENGLISH);
302   }
303
304   /**
305    * Shortcut for calling <c>getRequest().getStatus()</c>.
306    *
307    * @return The response status code.
308    */
309   public int getStatus() {
310      return res.getStatus();
311   }
312
313   /**
314    * Shortcut for calling <c>getRestResponse().hasOutput()</c>.
315    *
316    * @return <jk>true</jk> if response has output.
317    */
318   public boolean hasOutput() {
319      if (rres != null)
320         return rres.hasOutput();
321      return false;
322   }
323
324   /**
325    * Shortcut for calling <c>getRestResponse().getOutput()</c>.
326    *
327    * @return The response output.
328    */
329   public Object getOutput() {
330      if (rres != null)
331         return rres.getOutput();
332      return null;
333   }
334}