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 static org.apache.juneau.rest.annotation.HookEvent.*;
016
017import java.io.*;
018import java.lang.reflect.Method;
019import java.text.*;
020import java.util.*;
021import java.util.function.*;
022import java.util.logging.*;
023
024import javax.servlet.*;
025import javax.servlet.http.*;
026
027import org.apache.juneau.cp.*;
028import org.apache.juneau.cp.ResourceFinder;
029import org.apache.juneau.dto.swagger.*;
030import org.apache.juneau.html.annotation.*;
031import org.apache.juneau.internal.*;
032import org.apache.juneau.rest.annotation.*;
033import org.apache.juneau.rest.config.*;
034import org.apache.juneau.http.exception.*;
035
036/**
037 * Identical to {@link BasicRestServlet} but doesn't extend from {@link HttpServlet}.
038 *
039 * <p>
040 * This is particularly useful in Spring Boot environments that auto-detect servlets to deploy in servlet containers,
041 * but you want this resource to be deployed as a child instead.
042 *
043 * <ul class='seealso'>
044 *    <li class='link'>{@doc BasicRest}
045 * </ul>
046 */
047@Rest(
048   // Allow OPTIONS requests to be simulated using ?method=OPTIONS query parameter.
049   allowedMethodParams="OPTIONS"
050)
051@HtmlDocConfig(
052   // Basic page navigation links.
053   navlinks={
054      "up: request:/..",
055      "options: servlet:/?method=OPTIONS",
056      "stats: servlet:/stats"
057   }
058)
059public abstract class BasicRest implements BasicUniversalRest, BasicRestMethods, RestInfoProvider, RestCallLogger, RestResourceResolver, ResourceFinder {
060
061   private Logger logger = Logger.getLogger(getClass().getName());
062   private volatile RestContext context;
063   private RestInfoProvider infoProvider;
064   private RestCallLogger callLogger;
065   private ResourceFinder resourceFinder;
066   private RestResourceResolver resourceResolver = new BasicRestResourceResolver();
067
068   /**
069    * [OPTIONS /*] - Show resource options.
070    *
071    * @param req The HTTP request.
072    * @return A bean containing the contents for the OPTIONS page.
073    */
074   @Override /* BasicRestConfig */
075   public Swagger getOptions(RestRequest req) {
076      // Localized Swagger for this resource is available through the RestRequest object.
077      return req.getSwagger();
078   }
079
080   /**
081    * [GET /options] - Show resource options.
082    *
083    * @param req The HTTP request.
084    * @return A bean containing the contents for the OPTIONS page.
085    */
086   @Override /* BasicRestConfig */
087   public Swagger getOptions2(RestRequest req) {
088      // Localized Swagger for this resource is available through the RestRequest object.
089      return req.getSwagger();
090   }
091
092   /**
093    * [* /error] - Error occurred.
094    *
095    * <p>
096    * Servlet chains will often automatically redirect to <js>"/error"</js> when any sort of error condition occurs
097    * (such as failed authentication) and will set appropriate response parameters (such as an <c>WWW-Authenticate</c>
098    * response header).
099    *
100    * <p>
101    * These responses should be left as-is without any additional processing.
102    */
103   @Override /* BasicRestConfig */
104   public void error() {}
105
106   /**
107    * [GET /stats] - Timing statistics.
108    *
109    * <p>
110    * Timing statistics for method invocations on this resource.
111    *
112    * @return A collection of timing statistics for each annotated method on this resource.
113    */
114   @Override /* BasicRestConfig */
115   public RestContextStats getStats(RestRequest req) {
116      return req.getContext().getStats();
117   }
118
119   //-----------------------------------------------------------------------------------------------------------------
120   // Context methods.
121   //-----------------------------------------------------------------------------------------------------------------
122
123   /**
124    * Returns the read-only context object that contains all the configuration information about this resource.
125    *
126    * @return The context information on this servlet.
127    */
128   protected synchronized RestContext getContext() {
129      if (context == null)
130         throw new InternalServerError("RestContext object not set on resource.");
131      return context;
132   }
133
134   //-----------------------------------------------------------------------------------------------------------------
135   // Convenience logger methods
136   //-----------------------------------------------------------------------------------------------------------------
137
138   /**
139    * Log a message at {@link Level#INFO} level.
140    *
141    * <p>
142    * Subclasses can intercept the handling of these messages by overriding {@link #doLog(Level, Throwable, Supplier)}.
143    *
144    * @param msg The message to log.
145    */
146   public void log(String msg) {
147      doLog(Level.INFO, null, () -> msg);
148   }
149
150   /**
151    * Log a message.
152    *
153    * <p>
154    * Subclasses can intercept the handling of these messages by overriding {@link #doLog(Level, Throwable, Supplier)}.
155    *
156    * @param msg The message to log.
157    * @param cause The cause.
158    */
159   public void log(String msg, Throwable cause) {
160      doLog(Level.INFO, null, () -> msg);
161   }
162
163   /**
164    * Log a message.
165    *
166    * <p>
167    * Subclasses can intercept the handling of these messages by overriding {@link #doLog(Level, Throwable, Supplier)}.
168    *
169    * @param level The log level.
170    * @param msg The message to log.
171    * @param args Optional {@link MessageFormat}-style arguments.
172    */
173   public void log(Level level, String msg, Object...args) {
174      doLog(level, null, () -> StringUtils.format(msg, args));
175   }
176
177   /**
178    * Log a message.
179    *
180    * <p>
181    * Subclasses can intercept the handling of these messages by overriding {@link #doLog(Level, Throwable, Supplier)}.
182    *
183    * @param level The log level.
184    * @param cause The cause.
185    * @param msg The message to log.
186    * @param args Optional {@link MessageFormat}-style arguments.
187    */
188   public void log(Level level, Throwable cause, String msg, Object...args) {
189      doLog(level, cause, () -> StringUtils.format(msg, args));
190   }
191
192   /**
193    * Main logger method.
194    *
195    * <p>
196    * The default behavior logs a message to the Java logger of the class name.
197    *
198    * <p>
199    * Subclasses can override this method to implement their own logger handling.
200    *
201    * @param level The log level.
202    * @param cause Optional throwable.
203    * @param msg The message to log.
204    */
205   protected void doLog(Level level, Throwable cause, Supplier<String> msg) {
206      logger.log(level, cause, msg);
207   }
208
209   //-----------------------------------------------------------------------------------------------------------------
210   // Hook events
211   //-----------------------------------------------------------------------------------------------------------------
212
213   /**
214    * Method that gets called during servlet initialization.
215    *
216    * <p>
217    * This method is called from within the {@link Servlet#init(ServletConfig)} method after the {@link RestContextBuilder}
218    * object has been created and initialized with the annotations defined on the class, but before the
219    * {@link RestContext} object has been created.
220    *
221    * <p>
222    * An example of this is the <c>PetStoreResource</c> class that uses an init method to perform initialization
223    * of an internal data structure.
224    *
225    * <h5 class='figure'>Example:</h5>
226    * <p class='bcode w800'>
227    *    <ja>@Rest</ja>(...)
228    *    <jk>public class</jk> PetStoreResource <jk>extends</jk> ResourceJena {
229    *
230    *       <jc>// Our database.</jc>
231    *       <jk>private</jk> Map&lt;Integer,Pet&gt; <jf>petDB</jf>;
232    *
233    *       <ja>@Override</ja>
234    *       <jk>public void</jk> onInit(RestContextBuilder builder) <jk>throws</jk> Exception {
235    *          <jc>// Load our database from a local JSON file.</jc>
236    *          <jf>petDB</jf> = JsonParser.<jsf>DEFAULT</jsf>.parse(getClass().getResourceAsStream(<js>"PetStore.json"</js>), LinkedHashMap.<jk>class</jk>, Integer.<jk>class</jk>, Pet.<jk>class</jk>);
237    *       }
238    *    }
239    * </p>
240    *
241    * <ul class='notes'>
242    *    <li>
243    *       The default implementation of this method is a no-op.
244    *    <li>
245    *       Multiple INIT methods can be defined on a class.
246    *       <br>INIT methods on parent classes are invoked before INIT methods on child classes.
247    *       <br>The order of INIT method invocations within a class is alphabetical, then by parameter count, then by parameter types.
248    *    <li>
249    *       The method can throw any exception causing initialization of the servlet to fail.
250    * </ul>
251    *
252    * @param builder Context builder which can be used to configure the servlet.
253    * @throws Exception Any exception thrown will cause servlet to fail startup.
254    */
255   @RestHook(INIT)
256   public void onInit(RestContextBuilder builder) throws Exception {}
257
258   /**
259    * Method that gets called immediately after servlet initialization.
260    *
261    * <p>
262    * This method is called from within the {@link Servlet#init(ServletConfig)} method after the {@link RestContext}
263    * object has been created.
264    *
265    * <ul class='notes'>
266    *    <li>
267    *       The default implementation of this method is a no-op.
268    *    <li>
269    *       Multiple POST_INIT methods can be defined on a class.
270    *       <br>POST_INIT methods on parent classes are invoked before POST_INIT methods on child classes.
271    *       <br>The order of POST_INIT method invocations within a class is alphabetical, then by parameter count, then by parameter types.
272    *    <li>
273    *       The method can throw any exception causing initialization of the servlet to fail.
274    * </ul>
275    *
276    * @param context The initialized context object.
277    * @throws Exception Any exception thrown will cause servlet to fail startup.
278    */
279   @RestHook(POST_INIT)
280   public void onPostInit(RestContext context) throws Exception {
281      this.context = context;
282      this.infoProvider = new BasicRestInfoProvider(context);
283      this.callLogger = new BasicRestCallLogger(context);
284      this.resourceFinder = new BasicResourceFinder();
285   }
286
287   /**
288    * Identical to {@link #onPostInit(RestContext)} except the order of execution is child-resources first.
289    *
290    * <p>
291    * Use this method if you need to perform any kind of initialization on child resources before the parent resource.
292    *
293    * <p>
294    * This method is called from within the {@link Servlet#init(ServletConfig)} method after the {@link RestContext}
295    * object has been created and after the {@link HookEvent#POST_INIT} methods have been called.
296    *
297    * <p>
298    * The only valid parameter type for this method is {@link RestContext} which can be used to retrieve information
299    * about the servlet.
300    *
301    * <ul class='notes'>
302    *    <li>
303    *       The default implementation of this method is a no-op.
304    *    <li>
305    *       Multiple POST_INIT_CHILD_FIRST methods can be defined on a class.
306    *       <br>POST_INIT_CHILD_FIRST methods on parent classes are invoked before POST_INIT_CHILD_FIRST methods on child classes.
307    *       <br>The order of POST_INIT_CHILD_FIRST method invocations within a class is alphabetical, then by parameter count, then by parameter types.
308    *    <li>
309    *       The method can throw any exception causing initialization of the servlet to fail.
310    * </ul>
311    *
312    * @param context The initialized context object.
313    * @throws Exception Any exception thrown will cause servlet to fail startup.
314    */
315   @RestHook(POST_INIT_CHILD_FIRST)
316   public void onPostInitChildFirst(RestContext context) throws Exception {}
317
318   /**
319    * Method that gets called during servlet destroy.
320    *
321    * <p>
322    * This method is called from within the {@link Servlet#destroy()}.
323    *
324    * <h5 class='figure'>Example:</h5>
325    * <p class='bcode w800'>
326    *    <ja>@Rest</ja>(...)
327    *    <jk>public class</jk> PetStoreResource <jk>extends</jk> ResourceJena {
328    *
329    *       <jc>// Our database.</jc>
330    *       <jk>private</jk> Map&lt;Integer,Pet&gt; <jf>petDB</jf>;
331    *
332    *       <ja>@Override</ja>
333    *       <jk>public void</jk> onDestroy(RestContext context) {
334    *          <jf>petDB</jf> = <jk>null</jk>;
335    *       }
336    *    }
337    * </p>
338    *
339    * <ul class='notes'>
340    *    <li>
341    *       The default implementation of this method is a no-op.
342    *    <li>
343    *       Multiple DESTROY methods can be defined on a class.
344    *       <br>DESTROY methods on child classes are invoked before DESTROY methods on parent classes.
345    *       <br>The order of DESTROY method invocations within a class is alphabetical, then by parameter count, then by parameter types.
346    *    <li>
347    *       In general, destroy methods should not throw any exceptions, although if any are thrown, the stack trace will be
348    *       printed to <c>System.err</c>.
349    * </ul>
350    *
351    * @param context The initialized context object.
352    * @throws Exception Any exception thrown will cause stack trace to be printed to <c>System.err</c>.
353    */
354   @RestHook(DESTROY)
355   public void onDestroy(RestContext context) throws Exception {}
356
357   /**
358    * A method that is called immediately after the <c>HttpServlet.service(HttpServletRequest, HttpServletResponse)</c>
359    * method is called.
360    *
361    * <p>
362    * Note that you only have access to the raw request and response objects at this point.
363    *
364    * <h5 class='figure'>Example:</h5>
365    * <p class='bcode w800'>
366    *    <ja>@Rest</ja>(...)
367    *    <jk>public class</jk> MyResource <jk>extends</jk> BasicRestServlet {
368    *
369    *       <jc>// Add a request attribute to all incoming requests.</jc>
370    *       <ja>@Override</ja>
371    *       <jk>public void</jk> onStartCall(HttpServletRequest req, HttpServletResponse res) {
372    *          req.setAttribute(<js>"foobar"</js>, <jk>new</jk> FooBar());
373    *       }
374    *    }
375    * </p>
376    *
377    * <ul class='notes'>
378    *    <li>
379    *       The default implementation of this method is a no-op.
380    *    <li>
381    *       Multiple START_CALL methods can be defined on a class.
382    *       <br>START_CALL methods on parent classes are invoked before START_CALL methods on child classes.
383    *       <br>The order of START_CALL method invocations within a class is alphabetical, then by parameter count, then by parameter types.
384    *    <li>
385    *       The method can throw any exception.
386    *       <br>{@link HttpException HttpExceptions} can be thrown to cause a particular HTTP error status code.
387    *       <br>All other exceptions cause an HTTP 500 error status code.
388    * </ul>
389    *
390    * @param req The HTTP servlet request object.
391    * @param res The HTTP servlet response object.
392    * @throws Exception Any exception.
393    */
394   @RestHook(START_CALL)
395   public void onStartCall(HttpServletRequest req, HttpServletResponse res) throws Exception {}
396
397   /**
398    * Method that gets called immediately before the <ja>@RestMethod</ja> annotated method gets called.
399    *
400    * <p>
401    * At this point, the {@link RestRequest} object has been fully initialized, and all {@link RestGuard} and
402    * {@link RestMatcher} objects have been called.
403    *
404    * <ul class='notes'>
405    *    <li>
406    *       The default implementation of this method is a no-op.
407    *    <li>
408    *       Multiple PRE_CALL methods can be defined on a class.
409    *       <br>PRE_CALL methods on parent classes are invoked before PRE_CALL methods on child classes.
410    *       <br>The order of PRE_CALL method invocations within a class is alphabetical, then by parameter count, then by parameter types.
411    *    <li>
412    *       The method can throw any exception.
413    *       <br>{@link HttpException HttpExceptions} can be thrown to cause a particular HTTP error status code.
414    *       <br>All other exceptions cause an HTTP 500 error status code.
415    *    <li>
416    *       It's advisable not to mess around with the HTTP body itself since you may end up consuming the body
417    *       before the actual REST method has a chance to use it.
418    * </ul>
419    *
420    * @param req The request object.
421    * @param res The response object.
422    * @throws Exception Any exception.
423    */
424   @RestHook(PRE_CALL)
425   public void onPreCall(RestRequest req, RestResponse res) throws Exception {}
426
427   /**
428    * Method that gets called immediately after the <ja>@RestMethod</ja> annotated method gets called.
429    *
430    * <p>
431    * At this point, the output object returned by the method call has been set on the response, but
432    * {@link RestConverter RestConverters} have not yet been executed and the response has not yet been written.
433    *
434    * <ul class='notes'>
435    *    <li>
436    *       The default implementation of this method is a no-op.
437    *    <li>
438    *       Multiple POST_CALL methods can be defined on a class.
439    *       <br>POST_CALL methods on parent classes are invoked before POST_CALL methods on child classes.
440    *       <br>The order of POST_CALL method invocations within a class is alphabetical, then by parameter count, then by parameter types.
441    *    <li>
442    *       The method can throw any exception, although at this point it is too late to set an HTTP error status code.
443    * </ul>
444    *
445    * @param req The request object.
446    * @param res The response object.
447    * @throws Exception Any exception.
448    */
449   @RestHook(POST_CALL)
450   public void onPostCall(RestRequest req, RestResponse res) throws Exception {}
451
452   /**
453    * Method that gets called right before we exit the servlet service method.
454    *
455    * <p>
456    * At this point, the output has been written and flushed.
457    *
458    * <p>
459    * The following attributes are set on the {@link HttpServletRequest} object that can be useful for logging purposes:
460    * <ul>
461    *    <li><js>"Exception"</js> - Any exceptions thrown during the request.
462    *    <li><js>"ExecTime"</js> - Execution time of the request.
463    * </ul>
464    *
465    * <ul class='notes'>
466    *    <li>
467    *       The default implementation of this method is a no-op.
468    *    <li>
469    *       Multiple END_CALL methods can be defined on a class.
470    *       <br>END_CALL methods on parent classes are invoked before END_CALL methods on child classes.
471    *       <br>The order of END_CALL method invocations within a class is alphabetical, then by parameter count, then by parameter types.
472    *    <li>
473    *       The method can throw any exception, although at this point it is too late to set an HTTP error status code.
474    *    <li>
475    *       Note that if you override a parent method, you probably need to call <code><jk>super</jk>.parentMethod(...)</code>.
476    *       <br>The method is still considered part of the parent class for ordering purposes even though it's
477    *       overridden by the child class.
478    * </ul>
479    *
480    * @param req The HTTP servlet request object.
481    * @param res The HTTP servlet response object.
482    * @throws Exception Any exception.
483    */
484   @RestHook(END_CALL)
485   public void onEndCall(HttpServletRequest req, HttpServletResponse res) throws Exception {}
486
487   //-----------------------------------------------------------------------------------------------------------------
488   // Other methods.
489   //-----------------------------------------------------------------------------------------------------------------
490
491   /**
492    * Returns the current HTTP request.
493    *
494    * @return The current HTTP request, or <jk>null</jk> if it wasn't created.
495    */
496   public synchronized RestRequest getRequest() {
497      return getContext().getRequest();
498   }
499
500   /**
501    * Returns the current HTTP response.
502    *
503    * @return The current HTTP response, or <jk>null</jk> if it wasn't created.
504    */
505   public synchronized RestResponse getResponse() {
506      return getContext().getResponse();
507   }
508
509   //-----------------------------------------------------------------------------------------------------------------
510   // RestInfoProvider
511   //-----------------------------------------------------------------------------------------------------------------
512
513   @Override /* RestInfoProvider */
514   public Swagger getSwagger(RestRequest req) throws Exception {
515      return infoProvider.getSwagger(req);
516   }
517
518   @Override /* RestInfoProvider */
519   public String getSiteName(RestRequest req) throws Exception {
520      return infoProvider.getSiteName(req);
521   }
522
523   @Override /* RestInfoProvider */
524   public String getTitle(RestRequest req) throws Exception {
525      return infoProvider.getTitle(req);
526   }
527
528   @Override /* RestInfoProvider */
529   public String getDescription(RestRequest req) throws Exception {
530      return infoProvider.getDescription(req);
531   }
532
533   @Override /* RestInfoProvider */
534   public String getMethodSummary(Method method, RestRequest req) throws Exception {
535      return infoProvider.getMethodSummary(method, req);
536   }
537
538   @Override /* RestInfoProvider */
539   public String getMethodDescription(Method method, RestRequest req) throws Exception {
540      return infoProvider.getMethodDescription(method, req);
541   }
542
543   //-----------------------------------------------------------------------------------------------------------------
544   // RestCallLogger
545   //-----------------------------------------------------------------------------------------------------------------
546
547   @Override /* RestCallLogger */
548   public void log(RestCallLoggerConfig config, HttpServletRequest req, HttpServletResponse res) {
549      callLogger.log(config, req, res);
550   }
551
552   //-----------------------------------------------------------------------------------------------------------------
553   // ClasspathResourceFinder
554   //-----------------------------------------------------------------------------------------------------------------
555
556   @Override /* ClasspathResourceFinder */
557   public InputStream findResource(Class<?> baseClass, String name, Locale locale) throws IOException {
558      return resourceFinder.findResource(baseClass, name, locale);
559   }
560
561   //-----------------------------------------------------------------------------------------------------------------
562   // RestResourceResolver
563   //-----------------------------------------------------------------------------------------------------------------
564
565   @Override /* RestResourceResolver */
566   public <T> T resolve(Object parent, Class<T> c, Object... args) {
567      return resourceResolver.resolve(parent, c, args);
568   }
569
570   @Override /* RestResourceResolver */
571   public <T> T resolve(Object parent, Class<T> c, RestContextBuilder builder, Object... args) throws Exception {
572      return resourceResolver.resolve(parent, c, builder, args);
573   }
574}