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 java.util.logging.Level.*;
016import static javax.servlet.http.HttpServletResponse.*;
017import static org.apache.juneau.internal.StringUtils.*;
018import static org.apache.juneau.rest.HttpRuntimeException.*;
019import static org.apache.juneau.rest.annotation.HookEvent.*;
020
021import java.io.*;
022import java.lang.reflect.Method;
023import java.text.*;
024import java.util.*;
025import java.util.function.*;
026import java.util.logging.*;
027
028import javax.servlet.*;
029import javax.servlet.http.*;
030
031import org.apache.juneau.internal.*;
032import org.apache.juneau.reflect.*;
033import org.apache.juneau.rest.annotation.*;
034import org.apache.juneau.cp.*;
035import org.apache.juneau.dto.swagger.*;
036import org.apache.juneau.http.exception.*;
037
038/**
039 * Servlet implementation of a REST resource.
040 *
041 * <ul class='seealso'>
042 *    <li class='link'>{@doc RestServlet}
043 * </ul>
044 */
045public abstract class RestServlet extends HttpServlet implements RestInfoProvider, RestCallLogger, RestResourceResolver, ResourceFinder {
046
047   private static final long serialVersionUID = 1L;
048
049   private RestContextBuilder builder;
050   private volatile RestContext context;
051   private volatile Exception initException;
052   private boolean isInitialized = false;  // Should not be volatile.
053   private volatile RestResourceResolver resourceResolver = new BasicRestResourceResolver();
054   private Logger logger = Logger.getLogger(getClass().getName());
055   private RestInfoProvider infoProvider;
056   private RestCallLogger callLogger;
057   private ResourceFinder resourceFinder;
058
059   @Override /* Servlet */
060   public final synchronized void init(ServletConfig servletConfig) throws ServletException {
061      try {
062         if (context != null)
063            return;
064         builder = RestContext.create(servletConfig, this.getClass(), null);
065         if (resourceResolver != null)
066            builder.resourceResolver(resourceResolver);
067         builder.init(this);
068         super.init(servletConfig);
069         builder.servletContext(this.getServletContext());
070         setContext(builder.build());
071         context.postInitChildFirst();
072      } catch (ServletException e) {
073         initException = e;
074         log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName());
075         throw e;
076      } catch (Throwable e) {
077         initException = toHttpException(e, InternalServerError.class);
078         log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName());
079      }
080   }
081
082   /*
083    * Bypasses the init(ServletConfig) method and just calls the super.init(ServletConfig) method directly.
084    * Used when subclasses of RestServlet are attached as child resources.
085    */
086   synchronized void innerInit(ServletConfig servletConfig) throws ServletException {
087      super.init(servletConfig);
088   }
089
090   /**
091    * Sets the context object for this servlet.
092    *
093    * @param context Sets the context object on this servlet.
094    * @throws ServletException If error occurred during post-initialiation.
095    */
096   public synchronized void setContext(RestContext context) throws ServletException {
097      this.builder = context.builder;
098      this.context = context;
099      isInitialized = true;
100      infoProvider = new BasicRestInfoProvider(context);
101      callLogger = new BasicRestCallLogger(context);
102      resourceFinder = new RecursiveResourceFinder();
103      context.postInit();
104   }
105
106   /**
107    * Returns <jk>true</jk> if this servlet has been initialized and {@link #getContext()} returns a value.
108    *
109    * @return <jk>true</jk> if this servlet has been initialized and {@link #getContext()} returns a value.
110    */
111   public synchronized boolean isInitialized() {
112      return isInitialized;
113   }
114
115   /**
116    * Sets the resource resolver to use for this servlet and all child servlets.
117    * <p>
118    * This method can be called immediately following object construction, but must be called before {@link #init(ServletConfig)} is called.
119    * Otherwise calling this method will have no effect.
120    *
121    * @param resourceResolver The resolver instance.  Can be <jk>null</jk>.
122    * @return This object (for method chaining).
123    */
124   public synchronized RestServlet setRestResourceResolver(RestResourceResolver resourceResolver) {
125      this.resourceResolver = resourceResolver;
126      return this;
127   }
128
129   /**
130    * Returns the path defined on this servlet if it's defined via {@link Rest#path()}.
131    *
132    * @return The path defined on this servlet, or an empty string if not specified.
133    */
134   @SuppressWarnings("deprecation")
135   public synchronized String getPath() {
136      if (context != null)
137         return context.getPath();
138      ClassInfo ci = ClassInfo.of(getClass());
139      String path = "";
140      for (Rest rr : ci.getAnnotations(Rest.class))
141         if (! rr.path().isEmpty())
142            path = trimSlashes(rr.path());
143      if (! path.isEmpty())
144         return path;
145      for (RestResource rr : ci.getAnnotations(RestResource.class))
146         if (! rr.path().isEmpty())
147            path = trimSlashes(rr.path());
148      return "";
149   }
150
151   @Override /* GenericServlet */
152   public synchronized RestContextBuilder getServletConfig() {
153      return builder;
154   }
155
156   //-----------------------------------------------------------------------------------------------------------------
157   // Context methods.
158   //-----------------------------------------------------------------------------------------------------------------
159
160   /**
161    * Returns the read-only context object that contains all the configuration information about this resource.
162    *
163    * <p>
164    * This object is <jk>null</jk> during the call to {@link #init(ServletConfig)} but is populated by the time
165    * {@link #init()} is called.
166    *
167    * <p>
168    * Resource classes that don't extend from {@link RestServlet} can add the following method to their class to get
169    * access to this context object:
170    * <p class='bcode w800'>
171    *    <jk>public void</jk> init(RestServletContext context) <jk>throws</jk> Exception;
172    * </p>
173    *
174    * @return The context information on this servlet.
175    */
176   public synchronized RestContext getContext() {
177      if (context == null)
178         throw new InternalServerError("RestContext object not set on resource.");
179      return context;
180   }
181
182   /**
183    * Convenience method for calling <c>getContext().getProperties();</c>
184    *
185    * @return The resource properties as an {@link RestContextProperties}.
186    * @see RestContext#getProperties()
187    */
188   public RestContextProperties getProperties() {
189      return getContext().getProperties();
190   }
191
192
193   //-----------------------------------------------------------------------------------------------------------------
194   // Convenience logger methods
195   //-----------------------------------------------------------------------------------------------------------------
196
197   /**
198    * Log a message at {@link Level#INFO} level.
199    *
200    * <p>
201    * Subclasses can intercept the handling of these messages by overriding {@link #doLog(Level, Throwable, Supplier)}.
202    *
203    * @param msg The message to log.
204    */
205   @Override /* GenericServlet */
206   public void log(String msg) {
207      doLog(Level.INFO, null, () -> msg);
208   }
209
210   /**
211    * Log a message.
212    *
213    * <p>
214    * Subclasses can intercept the handling of these messages by overriding {@link #doLog(Level, Throwable, Supplier)}.
215    *
216    * @param msg The message to log.
217    * @param cause The cause.
218    */
219   @Override /* GenericServlet */
220   public void log(String msg, Throwable cause) {
221      doLog(Level.INFO, null, () -> msg);
222   }
223
224   /**
225    * Log a message.
226    *
227    * <p>
228    * Subclasses can intercept the handling of these messages by overriding {@link #doLog(Level, Throwable, Supplier)}.
229    *
230    * @param level The log level.
231    * @param msg The message to log.
232    * @param args Optional {@link MessageFormat}-style arguments.
233    */
234   public void log(Level level, String msg, Object...args) {
235      doLog(level, null, () -> StringUtils.format(msg, args));
236   }
237
238   /**
239    * Log a message.
240    *
241    * <p>
242    * Subclasses can intercept the handling of these messages by overriding {@link #doLog(Level, Throwable, Supplier)}.
243    *
244    * @param level The log level.
245    * @param cause The cause.
246    * @param msg The message to log.
247    * @param args Optional {@link MessageFormat}-style arguments.
248    */
249   public void log(Level level, Throwable cause, String msg, Object...args) {
250      doLog(level, cause, () -> StringUtils.format(msg, args));
251   }
252
253   /**
254    * Main logger method.
255    *
256    * <p>
257    * The default behavior logs a message to the Java logger of the class name.
258    *
259    * <p>
260    * Subclasses can override this method to implement their own logger handling.
261    *
262    * @param level The log level.
263    * @param cause Optional throwable.
264    * @param msg The message to log.
265    */
266   protected void doLog(Level level, Throwable cause, Supplier<String> msg) {
267      logger.log(level, cause, msg);
268   }
269
270   //-----------------------------------------------------------------------------------------------------------------
271   // Lifecycle methods
272   //-----------------------------------------------------------------------------------------------------------------
273
274   /**
275    * The main service method.
276    *
277    * <p>
278    * Subclasses can optionally override this method if they want to tailor the behavior of requests.
279    */
280   @Override /* Servlet */
281   public void service(HttpServletRequest r1, HttpServletResponse r2) throws ServletException, InternalServerError, IOException {
282      try {
283         // To avoid checking the volatile field context on every call, use the non-volatile isInitialized field as a first-check check.
284         if (! isInitialized) {
285            if (initException != null)
286               throw initException;
287            if (context == null)
288               throw new InternalServerError("Servlet {0} not initialized.  init(ServletConfig) was not called.  This can occur if you've overridden this method but didn't call super.init(RestConfig).", getClass().getName());
289            isInitialized = true;
290         }
291
292         context.execute(r1, r2);
293
294      } catch (Throwable e) {
295         r2.sendError(SC_INTERNAL_SERVER_ERROR, e.getLocalizedMessage());
296      }
297   }
298
299   @Override /* GenericServlet */
300   public synchronized void destroy() {
301      if (context != null)
302         context.destroy();
303      super.destroy();
304   }
305
306   //-----------------------------------------------------------------------------------------------------------------
307   // Hook events
308   //-----------------------------------------------------------------------------------------------------------------
309
310   /**
311    * Method that gets called during servlet initialization.
312    *
313    * <p>
314    * This method is called from within the {@link Servlet#init(ServletConfig)} method after the {@link RestContextBuilder}
315    * object has been created and initialized with the annotations defined on the class, but before the
316    * {@link RestContext} object has been created.
317    *
318    * <p>
319    * An example of this is the <c>PetStoreResource</c> class that uses an init method to perform initialization
320    * of an internal data structure.
321    *
322    * <h5 class='figure'>Example:</h5>
323    * <p class='bcode w800'>
324    *    <ja>@Rest</ja>(...)
325    *    <jk>public class</jk> PetStoreResource <jk>extends</jk> ResourceJena {
326    *
327    *       <jc>// Our database.</jc>
328    *       <jk>private</jk> Map&lt;Integer,Pet&gt; <jf>petDB</jf>;
329    *
330    *       <ja>@Override</ja>
331    *       <jk>public void</jk> onInit(RestContextBuilder builder) <jk>throws</jk> Exception {
332    *          <jc>// Load our database from a local JSON file.</jc>
333    *          <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>);
334    *       }
335    *    }
336    * </p>
337    *
338    * <ul class='notes'>
339    *    <li>
340    *       The default implementation of this method is a no-op.
341    *    <li>
342    *       Multiple INIT methods can be defined on a class.
343    *       <br>INIT methods on parent classes are invoked before INIT methods on child classes.
344    *       <br>The order of INIT method invocations within a class is alphabetical, then by parameter count, then by parameter types.
345    *    <li>
346    *       The method can throw any exception causing initialization of the servlet to fail.
347    * </ul>
348    *
349    * @param builder Context builder which can be used to configure the servlet.
350    * @throws Exception Any exception thrown will cause servlet to fail startup.
351    */
352   @RestHook(INIT)
353   public void onInit(RestContextBuilder builder) throws Exception {}
354
355   /**
356    * Method that gets called immediately after servlet initialization.
357    *
358    * <p>
359    * This method is called from within the {@link Servlet#init(ServletConfig)} method after the {@link RestContext}
360    * object has been created.
361    *
362    * <ul class='notes'>
363    *    <li>
364    *       The default implementation of this method is a no-op.
365    *    <li>
366    *       Multiple POST_INIT methods can be defined on a class.
367    *       <br>POST_INIT methods on parent classes are invoked before POST_INIT methods on child classes.
368    *       <br>The order of POST_INIT method invocations within a class is alphabetical, then by parameter count, then by parameter types.
369    *    <li>
370    *       The method can throw any exception causing initialization of the servlet to fail.
371    * </ul>
372    *
373    * @param context The initialized context object.
374    * @throws Exception Any exception thrown will cause servlet to fail startup.
375    */
376   @RestHook(POST_INIT)
377   public void onPostInit(RestContext context) throws Exception {}
378
379   /**
380    * Identical to {@link #onPostInit(RestContext)} except the order of execution is child-resources first.
381    *
382    * <p>
383    * Use this method if you need to perform any kind of initialization on child resources before the parent resource.
384    *
385    * <p>
386    * This method is called from within the {@link Servlet#init(ServletConfig)} method after the {@link RestContext}
387    * object has been created and after the {@link HookEvent#POST_INIT} methods have been called.
388    *
389    * <p>
390    * The only valid parameter type for this method is {@link RestContext} which can be used to retrieve information
391    * about the servlet.
392    *
393    * <ul class='notes'>
394    *    <li>
395    *       The default implementation of this method is a no-op.
396    *    <li>
397    *       Multiple POST_INIT_CHILD_FIRST methods can be defined on a class.
398    *       <br>POST_INIT_CHILD_FIRST methods on parent classes are invoked before POST_INIT_CHILD_FIRST methods on child classes.
399    *       <br>The order of POST_INIT_CHILD_FIRST method invocations within a class is alphabetical, then by parameter count, then by parameter types.
400    *    <li>
401    *       The method can throw any exception causing initialization of the servlet to fail.
402    * </ul>
403    *
404    * @param context The initialized context object.
405    * @throws Exception Any exception thrown will cause servlet to fail startup.
406    */
407   @RestHook(POST_INIT_CHILD_FIRST)
408   public void onPostInitChildFirst(RestContext context) throws Exception {}
409
410   /**
411    * Method that gets called during servlet destroy.
412    *
413    * <p>
414    * This method is called from within the {@link Servlet#destroy()}.
415    *
416    * <h5 class='figure'>Example:</h5>
417    * <p class='bcode w800'>
418    *    <ja>@Rest</ja>(...)
419    *    <jk>public class</jk> PetStoreResource <jk>extends</jk> ResourceJena {
420    *
421    *       <jc>// Our database.</jc>
422    *       <jk>private</jk> Map&lt;Integer,Pet&gt; <jf>petDB</jf>;
423    *
424    *       <ja>@Override</ja>
425    *       <jk>public void</jk> onDestroy(RestContext context) {
426    *          <jf>petDB</jf> = <jk>null</jk>;
427    *       }
428    *    }
429    * </p>
430    *
431    * <ul class='notes'>
432    *    <li>
433    *       The default implementation of this method is a no-op.
434    *    <li>
435    *       Multiple DESTROY methods can be defined on a class.
436    *       <br>DESTROY methods on child classes are invoked before DESTROY methods on parent classes.
437    *       <br>The order of DESTROY method invocations within a class is alphabetical, then by parameter count, then by parameter types.
438    *    <li>
439    *       In general, destroy methods should not throw any exceptions, although if any are thrown, the stack trace will be
440    *       printed to <c>System.err</c>.
441    * </ul>
442    *
443    * @param context The initialized context object.
444    * @throws Exception Any exception thrown will cause stack trace to be printed to <c>System.err</c>.
445    */
446   @RestHook(DESTROY)
447   public void onDestroy(RestContext context) throws Exception {}
448
449   /**
450    * A method that is called immediately after the <c>HttpServlet.service(HttpServletRequest, HttpServletResponse)</c>
451    * method is called.
452    *
453    * <p>
454    * Note that you only have access to the raw request and response objects at this point.
455    *
456    * <h5 class='figure'>Example:</h5>
457    * <p class='bcode w800'>
458    *    <ja>@Rest</ja>(...)
459    *    <jk>public class</jk> MyResource <jk>extends</jk> BasicRestServlet {
460    *
461    *       <jc>// Add a request attribute to all incoming requests.</jc>
462    *       <ja>@Override</ja>
463    *       <jk>public void</jk> onStartCall(HttpServletRequest req, HttpServletResponse res) {
464    *          req.setAttribute(<js>"foobar"</js>, <jk>new</jk> FooBar());
465    *       }
466    *    }
467    * </p>
468    *
469    * <ul class='notes'>
470    *    <li>
471    *       The default implementation of this method is a no-op.
472    *    <li>
473    *       Multiple START_CALL methods can be defined on a class.
474    *       <br>START_CALL methods on parent classes are invoked before START_CALL methods on child classes.
475    *       <br>The order of START_CALL method invocations within a class is alphabetical, then by parameter count, then by parameter types.
476    *    <li>
477    *       The method can throw any exception.
478    *       <br>{@link HttpException HttpExceptions} can be thrown to cause a particular HTTP error status code.
479    *       <br>All other exceptions cause an HTTP 500 error status code.
480    * </ul>
481    *
482    * @param req The HTTP servlet request object.
483    * @param res The HTTP servlet response object.
484    * @throws Exception Any exception.
485    */
486   @RestHook(START_CALL)
487   public void onStartCall(HttpServletRequest req, HttpServletResponse res) throws Exception {}
488
489   /**
490    * Method that gets called immediately before the <ja>@RestMethod</ja> annotated method gets called.
491    *
492    * <p>
493    * At this point, the {@link RestRequest} object has been fully initialized, and all {@link RestGuard} and
494    * {@link RestMatcher} objects have been called.
495    *
496    * <ul class='notes'>
497    *    <li>
498    *       The default implementation of this method is a no-op.
499    *    <li>
500    *       Multiple PRE_CALL methods can be defined on a class.
501    *       <br>PRE_CALL methods on parent classes are invoked before PRE_CALL methods on child classes.
502    *       <br>The order of PRE_CALL method invocations within a class is alphabetical, then by parameter count, then by parameter types.
503    *    <li>
504    *       The method can throw any exception.
505    *       <br>{@link HttpException HttpExceptions} can be thrown to cause a particular HTTP error status code.
506    *       <br>All other exceptions cause an HTTP 500 error status code.
507    *    <li>
508    *       It's advisable not to mess around with the HTTP body itself since you may end up consuming the body
509    *       before the actual REST method has a chance to use it.
510    * </ul>
511    *
512    * @param req The request object.
513    * @param res The response object.
514    * @throws Exception Any exception.
515    */
516   @RestHook(PRE_CALL)
517   public void onPreCall(RestRequest req, RestResponse res) throws Exception {}
518
519   /**
520    * Method that gets called immediately after the <ja>@RestMethod</ja> annotated method gets called.
521    *
522    * <p>
523    * At this point, the output object returned by the method call has been set on the response, but
524    * {@link RestConverter RestConverters} have not yet been executed and the response has not yet been written.
525    *
526    * <ul class='notes'>
527    *    <li>
528    *       The default implementation of this method is a no-op.
529    *    <li>
530    *       Multiple POST_CALL methods can be defined on a class.
531    *       <br>POST_CALL methods on parent classes are invoked before POST_CALL methods on child classes.
532    *       <br>The order of POST_CALL method invocations within a class is alphabetical, then by parameter count, then by parameter types.
533    *    <li>
534    *       The method can throw any exception, although at this point it is too late to set an HTTP error status code.
535    * </ul>
536    *
537    * @param req The request object.
538    * @param res The response object.
539    * @throws Exception Any exception.
540    */
541   @RestHook(POST_CALL)
542   public void onPostCall(RestRequest req, RestResponse res) throws Exception {}
543
544   /**
545    * Method that gets called right before we exit the servlet service method.
546    *
547    * <p>
548    * At this point, the output has been written and flushed.
549    *
550    * <p>
551    * The following attributes are set on the {@link HttpServletRequest} object that can be useful for logging purposes:
552    * <ul>
553    *    <li><js>"Exception"</js> - Any exceptions thrown during the request.
554    *    <li><js>"ExecTime"</js> - Execution time of the request.
555    * </ul>
556    *
557    * <ul class='notes'>
558    *    <li>
559    *       The default implementation of this method is a no-op.
560    *    <li>
561    *       Multiple END_CALL methods can be defined on a class.
562    *       <br>END_CALL methods on parent classes are invoked before END_CALL methods on child classes.
563    *       <br>The order of END_CALL method invocations within a class is alphabetical, then by parameter count, then by parameter types.
564    *    <li>
565    *       The method can throw any exception, although at this point it is too late to set an HTTP error status code.
566    *    <li>
567    *       Note that if you override a parent method, you probably need to call <code><jk>super</jk>.parentMethod(...)</code>.
568    *       <br>The method is still considered part of the parent class for ordering purposes even though it's
569    *       overridden by the child class.
570    * </ul>
571    *
572    * @param req The HTTP servlet request object.
573    * @param res The HTTP servlet response object.
574    * @throws Exception Any exception.
575    */
576   @RestHook(END_CALL)
577   public void onEndCall(HttpServletRequest req, HttpServletResponse res) throws Exception {}
578
579   //-----------------------------------------------------------------------------------------------------------------
580   // Other methods.
581   //-----------------------------------------------------------------------------------------------------------------
582
583   /**
584    * Returns the current HTTP request.
585    *
586    * @return The current HTTP request, or <jk>null</jk> if it wasn't created.
587    */
588   public synchronized RestRequest getRequest() {
589      return getContext().getRequest();
590   }
591
592   /**
593    * Returns the current HTTP response.
594    *
595    * @return The current HTTP response, or <jk>null</jk> if it wasn't created.
596    */
597   public synchronized RestResponse getResponse() {
598      return getContext().getResponse();
599   }
600
601
602   //-----------------------------------------------------------------------------------------------------------------
603   // RestInfoProvider
604   //-----------------------------------------------------------------------------------------------------------------
605
606   @Override /* RestInfoProvider */
607   public Swagger getSwagger(RestRequest req) throws Exception {
608      return infoProvider.getSwagger(req);
609   }
610
611   @Override /* RestInfoProvider */
612   public String getSiteName(RestRequest req) throws Exception {
613      return infoProvider.getSiteName(req);
614   }
615
616   @Override /* RestInfoProvider */
617   public String getTitle(RestRequest req) throws Exception {
618      return infoProvider.getTitle(req);
619   }
620
621   @Override /* RestInfoProvider */
622   public String getDescription(RestRequest req) throws Exception {
623      return infoProvider.getDescription(req);
624   }
625
626   @Override /* RestInfoProvider */
627   public String getMethodSummary(Method method, RestRequest req) throws Exception {
628      return infoProvider.getMethodSummary(method, req);
629   }
630
631   @Override /* RestInfoProvider */
632   public String getMethodDescription(Method method, RestRequest req) throws Exception {
633      return infoProvider.getMethodDescription(method, req);
634   }
635
636   //-----------------------------------------------------------------------------------------------------------------
637   // RestCallLogger
638   //-----------------------------------------------------------------------------------------------------------------
639
640   @Override /* RestCallLogger */
641   public void log(RestCallLoggerConfig config, HttpServletRequest req, HttpServletResponse res) {
642      callLogger.log(config, req, res);
643   }
644
645   //-----------------------------------------------------------------------------------------------------------------
646   // ClasspathResourceFinder
647   //-----------------------------------------------------------------------------------------------------------------
648
649   @Override /* ClasspathResourceFinder */
650   public InputStream findResource(Class<?> baseClass, String name, Locale locale) throws IOException {
651      return resourceFinder.findResource(baseClass, name, locale);
652   }
653
654   //-----------------------------------------------------------------------------------------------------------------
655   // RestResourceResolver
656   //-----------------------------------------------------------------------------------------------------------------
657
658   @Override /* RestResourceResolver */
659   public <T> T resolve(Object parent, Class<T> c, Object... args) {
660      return resourceResolver.resolve(parent, c, args);
661   }
662
663   @Override /* RestResourceResolver */
664   public <T> T resolve(Object parent, Class<T> c, RestContextBuilder builder, Object... args) throws Exception {
665      return resourceResolver.resolve(parent, c, builder, args);
666   }
667}