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.lang.reflect.*;
017import java.util.*;
018
019import javax.servlet.http.*;
020
021import org.apache.juneau.httppart.bean.*;
022import org.apache.juneau.rest.util.*;
023
024/**
025 * A wrapper around a single HttpServletRequest/HttpServletResponse pair.
026 */
027public class RestCall {
028
029   /**
030    * Request attribute name for passing path variables from parent to child.
031    */
032   private static final String REST_PATHVARS_ATTR = "juneau.pathVars";
033
034   private HttpServletRequest req;
035   private HttpServletResponse res;
036   private RestRequest rreq;
037   private RestResponse rres;
038   private RestContext context;
039   private RestMethodContext rmethod;
040   private UrlPathInfo urlPathInfo;
041   private String pathInfoUndecoded;
042   private long startTime = System.currentTimeMillis();
043   private RestCallLogger logger;
044   private RestCallLoggerConfig loggerConfig;
045
046   private UrlPathPatternMatch urlPathPatternMatch;
047
048   /**
049    * Constructor.
050    *
051    * @param context The REST context object.
052    * @param req The incoming HTTP servlet request object.
053    * @param res The incoming HTTP servlet response object.
054    */
055   public RestCall(RestContext context, HttpServletRequest req, HttpServletResponse res) {
056      context(context).request(req).response(res);
057   }
058
059   //------------------------------------------------------------------------------------------------------------------
060   // Request/response objects.
061   //------------------------------------------------------------------------------------------------------------------
062
063   /**
064    * Overrides the request object on the REST call.
065    *
066    * @param req The new HTTP servlet request.
067    * @return This object (for method chaining).
068    */
069   public RestCall request(HttpServletRequest req) {
070      this.req = req;
071      this.urlPathInfo = null;
072      this.pathInfoUndecoded = null;
073      return this;
074   }
075
076   /**
077    * Overrides the response object on the REST call.
078    *
079    * @param res The new HTTP servlet response.
080    * @return This object (for method chaining).
081    */
082   public RestCall response(HttpServletResponse res) {
083      this.res = res;
084      return this;
085   }
086
087   /**
088    * Overrides the context object on this call.
089    *
090    * @param context The context that's creating this call.
091    * @return This object (for method chaining).
092    */
093   public RestCall context(RestContext context) {
094      this.context = context;
095      return this;
096   }
097
098   /**
099    * Sets the method context on this call.
100    *
101    * Used for logging statistics on the method.
102    *
103    * @param value The new value.
104    * @return This object (for method chaining).
105    */
106   public RestCall restMethodContext(RestMethodContext value) {
107      this.rmethod = value;
108      return this;
109   }
110
111   /**
112    * Set the {@link RestRequest} object on this REST call.
113    *
114    * @param rreq The {@link RestRequest} object on this REST call.
115    * @return This object (for method chaining).
116    */
117   public RestCall restRequest(RestRequest rreq) {
118      request(rreq);
119      this.rreq = rreq;
120      return this;
121   }
122
123   /**
124    * Set the {@link RestResponse} object on this REST call.
125    *
126    * @param rres The {@link RestResponse} object on this REST call.
127    * @return This object (for method chaining).
128    */
129   public RestCall restResponse(RestResponse rres) {
130      response(rres);
131      this.rres = rres;
132      this.rreq.setResponse(rres);
133      return this;
134   }
135
136   /**
137    * Returns the HTTP servlet request of this REST call.
138    *
139    * @return the HTTP servlet request of this REST call.
140    */
141   public HttpServletRequest getRequest() {
142      return req;
143   }
144
145   /**
146    * Returns the HTTP servlet response of this REST call.
147    *
148    * @return the HTTP servlet response of this REST call.
149    */
150   public HttpServletResponse getResponse() {
151      return res;
152   }
153
154   /**
155    * Returns the REST request of this REST call.
156    *
157    * @return the REST request of this REST call.
158    */
159   public RestRequest getRestRequest() {
160      return rreq;
161   }
162
163   /**
164    * Returns the REST response of this REST call.
165    *
166    * @return the REST response of this REST call.
167    */
168   public RestResponse getRestResponse() {
169      return rres;
170   }
171
172   /**
173    * Returns the method context of this call.
174    *
175    * @return The method context of this call.
176    */
177   public RestMethodContext getRestMethodContext() {
178      return rmethod;
179   }
180
181   /**
182    * Returns the Java method of this call.
183    *
184    * @return The java method of this call, or <jk>null</jk> if it hasn't been determined yet.
185    */
186   public Method getJavaMethod() {
187      return rmethod == null ? null : rmethod.method;
188   }
189
190   /**
191    * Adds resolved <c><ja>@Resource</ja>(path)</c> variable values to this call.
192    *
193    * @param vars The variables to add to this call.
194    */
195   @SuppressWarnings("unchecked")
196   public void addPathVars(Map<String,String> vars) {
197      if (vars != null && ! vars.isEmpty()) {
198         Map<String,String> m = (Map<String,String>)req.getAttribute(REST_PATHVARS_ATTR);
199         if (m == null) {
200            m = new TreeMap<>();
201            req.setAttribute(REST_PATHVARS_ATTR, m);
202         }
203         m.putAll(vars);
204      }
205   }
206
207   /**
208    * Returns resolved <c><ja>@Resource</ja>(path)</c> variable values on this call.
209    *
210    * @return Resolved <c><ja>@Resource</ja>(path)</c> variable values on this call.
211    */
212   @SuppressWarnings("unchecked")
213   public Map<String,String> getPathVars() {
214      Map<String,String> m = (Map<String,String>)req.getAttribute(REST_PATHVARS_ATTR);
215      return m == null ? Collections.emptyMap() : m;
216   }
217
218   //------------------------------------------------------------------------------------------------------------------
219   // Setters.
220   //------------------------------------------------------------------------------------------------------------------
221
222   /**
223    * Sets the logger to use when logging this call.
224    *
225    * @param logger The logger to use when logging this call.
226    * @return This object (for method chaining).
227    */
228   public RestCall logger(RestCallLogger logger) {
229      this.logger = logger;
230      return this;
231   }
232
233   /**
234    * Sets the logging configuration to use when logging this call.
235    *
236    * @param config The logging configuration to use when logging this call.
237    * @return This object (for method chaining).
238    */
239   public RestCall loggerConfig(RestCallLoggerConfig config) {
240      this.loggerConfig = config;
241      return this;
242   }
243
244   /**
245    * Enables or disabled debug mode on this call.
246    *
247    * @param b The debug flag value.
248    * @return This object (for method chaining).
249    * @throws IOException Occurs if request body could not be cached into memory.
250    */
251   public RestCall debug(boolean b) throws IOException {
252      if (b) {
253         req = CachingHttpServletRequest.wrap(req);
254         res = CachingHttpServletResponse.wrap(res);
255         req.setAttribute("Debug", true);
256      } else {
257         req.removeAttribute("Debug");
258      }
259      return this;
260   }
261
262   /**
263    * Sets the HTTP status on this call.
264    *
265    * @param code The status code.
266    * @return This object (for method chaining).
267    */
268   public RestCall status(int code) {
269      res.setStatus(code);
270      return this;
271   }
272
273   /**
274    * Identifies that an exception occurred during this call.
275    *
276    * @param e The thrown exception.
277    * @return This object (for method chaining).
278    */
279   public RestCall exception(Throwable e) {
280      req.setAttribute("Exception", e);
281      return this;
282   }
283
284   /**
285    * Sets metadata about the response.
286    *
287    * @param meta The metadata about the response.
288    * @return This object (for method chaining).
289    */
290   public RestCall responseMeta(ResponseBeanMeta meta) {
291      if (rres != null)
292         rres.setResponseMeta(meta);
293      return this;
294   }
295
296   /**
297    * Sets the output object to serialize as the response of this call.
298    *
299    * @param output The response output POJO.
300    * @return This object (for method chaining).
301    */
302   public RestCall output(Object output) {
303      if (rres != null)
304         rres.setOutput(output);
305      return this;
306   }
307
308   /**
309    * Sets the URL path pattern match on this call.
310    *
311    * @param urlPathPatternMatch The match pattern.
312    * @return This object (for method chaining).
313    */
314   public RestCall urlPathPatternMatch(UrlPathPatternMatch urlPathPatternMatch) {
315      this.urlPathPatternMatch = urlPathPatternMatch;
316      return this;
317   }
318
319   /**
320    * Returns the URL path pattern match on this call.
321    *
322    * @return The URL path pattern match on this call.
323    */
324   public UrlPathPatternMatch getUrlPathPatternMatch() {
325      return urlPathPatternMatch;
326   }
327
328   //------------------------------------------------------------------------------------------------------------------
329   // Lifecycle methods.
330   //------------------------------------------------------------------------------------------------------------------
331
332   /**
333    * Called at the end of a call to finish any remaining tasks such as flushing buffers and logging the response.
334    *
335    * @return This object (for method chaining).
336    */
337   public RestCall finish() {
338      try {
339         res.flushBuffer();
340         req.setAttribute("ExecTime", System.currentTimeMillis() - startTime);
341         if (rreq != null)
342            rreq.close();
343      } catch (Exception e) {
344         exception(e);
345      }
346      if (logger != null)
347         logger.log(loggerConfig, req, res);
348      return this;
349   }
350
351
352   //------------------------------------------------------------------------------------------------------------------
353   // Pass-through convenience methods.
354   //------------------------------------------------------------------------------------------------------------------
355
356   /**
357    * Shortcut for calling <c>getRequest().getServletPath()</c>.
358    *
359    * @return The request servlet path.
360    */
361   public String getServletPath() {
362      return req.getServletPath();
363   }
364
365   /**
366    * Returns the request path info as a {@link UrlPathInfo} bean.
367    *
368    * @return The request path info as a {@link UrlPathInfo} bean.
369    */
370   public UrlPathInfo getUrlPathInfo() {
371      if (urlPathInfo == null)
372         urlPathInfo = new UrlPathInfo(getPathInfoUndecoded());
373      return urlPathInfo;
374   }
375
376   /**
377    * Shortcut for calling <c>getRequest().getPathInfo()</c>.
378    *
379    * @return The request servlet path info.
380    */
381   public String getPathInfo() {
382      return req.getPathInfo();
383   }
384
385   /**
386    * Same as {@link #getPathInfo()} but doesn't decode encoded characters.
387    *
388    * @return The undecoded request servlet path info.
389    */
390   public String getPathInfoUndecoded() {
391      if (pathInfoUndecoded == null)
392         pathInfoUndecoded = RestUtils.getPathInfoUndecoded(req);
393      return pathInfoUndecoded;
394   }
395
396   /**
397    * Returns the HTTP method name.
398    *
399    * @return The HTTP method name, always uppercased.
400    */
401   public String getMethod() {
402      if (rreq != null)
403         return rreq.getMethod().toUpperCase(Locale.ENGLISH);
404      return req.getMethod().toUpperCase(Locale.ENGLISH);
405   }
406
407   /**
408    * Shortcut for calling <c>getRequest().getStatus()</c>.
409    *
410    * @return The response status code.
411    */
412   public int getStatus() {
413      return res.getStatus();
414   }
415
416   /**
417    * Shortcut for calling <c>getRestResponse().hasOutput()</c>.
418    *
419    * @return <jk>true</jk> if response has output.
420    */
421   public boolean hasOutput() {
422      if (rres != null)
423         return rres.hasOutput();
424      return false;
425   }
426
427   /**
428    * Shortcut for calling <c>getRestResponse().getOutput()</c>.
429    *
430    * @return The response output.
431    */
432   public Object getOutput() {
433      if (rres != null)
434         return rres.getOutput();
435      return null;
436   }
437
438   /**
439    * Shortcut for calling <c>getRestRequest().isDebug()</c>.
440    *
441    * @return <jk>true</jk> if debug is enabled for this request.
442    */
443   public boolean isDebug() {
444      if (rreq != null)
445         return rreq.isDebug();
446      return false;
447   }
448
449   /**
450    * Returns the context that created this call.
451    *
452    * @return The context that created this call.
453    */
454   public RestContext getContext() {
455      return context;
456   }
457}