001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest.server.servlet;
018
019import static jakarta.servlet.http.HttpServletResponse.*;
020import static java.util.logging.Level.*;
021import static org.apache.juneau.commons.utils.CollectionUtils.*;
022import static org.apache.juneau.commons.utils.Shorts.*;
023import static org.apache.juneau.commons.utils.ThrowableUtils.*;
024
025import java.io.*;
026import java.util.concurrent.atomic.*;
027import java.util.function.*;
028import java.util.logging.*;
029
030import org.apache.juneau.commons.inject.*;
031import org.apache.juneau.commons.logging.RichLogger;
032import org.apache.juneau.commons.reflect.*;
033import org.apache.juneau.commons.utils.*;
034import org.apache.juneau.http.response.*;
035import org.apache.juneau.rest.server.*;
036import org.apache.juneau.rest.server.auth.*;
037
038import jakarta.servlet.*;
039import jakarta.servlet.http.*;
040
041/**
042 * Servlet implementation of a REST resource.
043 *
044 * <p>
045 *    The {@link RestServlet} class is the entry point for your REST resources.
046 *    It extends directly from <l>HttpServlet</l> and is deployed like any other servlet.
047 * </p>
048 * <p>
049 *    When the servlet <l>init()</l> method is called, it triggers the code to find and process the <l>@Rest</l>
050 *    annotations on that class and all child classes.
051 *    These get constructed into a {@link RestContext} object that holds all the configuration
052 *    information about your resource in a read-only object.
053 * </p>
054 *
055 * <h5 class='section'>Notes:</h5><ul>
056 *    <li class='note'>
057 *       Users will typically extend from {@link BasicRestServlet} or {@link BasicRestServletGroup}
058 *       instead of this class directly.
059 * </ul>
060 *
061 * <h5 class='section'>See Also:</h5><ul>
062 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestAnnotatedClasses">@Rest-Annotated Class</a>
063 * </ul>
064 *
065 * @serial exclude
066 */
067@SuppressWarnings({
068   "java:S115", // Constants use UPPER_snakeCase convention (e.g., MSG_servletInitError)
069   "java:S1192", // Duplicated literals read more clearly inline than as constants.
070   "java:S2654", // destroy(), init() and setContext() are synchronized to guard one-time lifecycle transitions of the context/initException references, which are also read lock-free on the request path
071   "java:S2886" // Reads the AtomicReference 'context' field, which already guarantees atomicity and cross-thread visibility (same as a volatile read); synchronizing this single read with the setContext()/init() lock would add hot-path contention with no correctness benefit.
072})
073public abstract class RestServlet extends HttpServlet {
074
075   private static final long serialVersionUID = 1L;
076   private static final AnnotationProvider AP = AnnotationProvider.INSTANCE;
077
078   private final AtomicReference<RestContext> context = new AtomicReference<>();
079   private final AtomicReference<Exception> initException = new AtomicReference<>();
080
081   /**
082    * The programmatic configuration builder stashed on this instance, or <jk>null</jk> when the
083    * resource was constructed without a builder.  Mutable so it can be written by either the
084    * {@link #RestServlet(RestBuilder)} constructor or {@link Builder#build()} (the no-arg-only setter-stash path).
085    * Read non-reflectively by {@link RestContext} during construction so builder-supplied values take precedence
086    * over {@link Rest @Rest} annotation values.
087    */
088   @SuppressWarnings({
089      "java:S2226" // Write-once-before-service stash: set only by the (RestBuilder) constructor or Builder.build() (the no-arg + setter-stash path) before the servlet is placed in service, then read-only; it cannot be final because of the no-arg construction path.
090   })
091   transient RestBuilder<?> restBuilder;
092
093   /**
094    * Default constructor.
095    */
096   protected RestServlet() {}
097
098   /**
099    * Builder-injection constructor.
100    *
101    * @param builder The programmatic configuration builder.  May be <jk>null</jk>.
102    */
103   protected RestServlet(RestBuilder<?> builder) {
104      this.restBuilder = builder;
105   }
106
107   /**
108    * Returns the programmatic configuration builder stashed on this resource, or <jk>null</jk> if none.
109    *
110    * @return The stashed builder, or <jk>null</jk> if none was injected.
111    * @since 10.0.0
112    */
113   @SuppressWarnings({
114      "java:S1452" // RestBuilder<?> wildcard return intentional; concrete builder type varies by REST resource class
115   })
116   public RestBuilder<?> getRestBuilder() {
117      return restBuilder;
118   }
119
120   /**
121    * Creates a new fluent builder for programmatically configuring an instance of the specified resource type.
122    *
123    * <p>
124    * Builder-supplied values take precedence over the resource class's own {@link Rest @Rest} annotation values.
125    *
126    * @param <R> The resource type.
127    * @param type The resource type to build.  Must not be <jk>null</jk>.
128    * @return A new builder.
129    * @since 10.0.0
130    */
131   public static <R extends RestServlet> DefaultBuilder<R> builder(Class<R> type) {
132      return new DefaultBuilder<>(type);
133   }
134
135   /**
136    * Returns the resource-level authenticator for this servlet, or <jk>null</jk> to fall through to the
137    * {@code @Rest(authenticator=...)} annotation / registered bean / {@code @Bean} factory method.
138    *
139    * <p>
140    * This is the lowest-precedence rung in the {@link RestAuthenticator} resolution chain (see design spec
141    * &sect;5.3).  Subclasses can override it to supply an authenticator programmatically.
142    *
143    * @param beanStore The bean store for dependency resolution.
144    * @return The authenticator, or <jk>null</jk> (the default).
145    * @since 10.0.0
146    */
147   public RestAuthenticator createAuthenticator(BeanStore beanStore) {
148      return null;
149   }
150
151   @Override /* Overridden from GenericServlet */
152   public synchronized void destroy() {
153      var c = context.getAndSet(null);
154      if (nn(c))
155         c.destroy();
156      super.destroy();
157   }
158
159   /**
160    * Returns the read-only context object that contains all the configuration information about this resource.
161    *
162    * <p>
163    * This object is <jk>null</jk> during the call to {@link #init(ServletConfig)} but is populated by the time
164    * {@link #init()} is called.
165    *
166    * <p>
167    * Resource classes that don't extend from {@link RestServlet} can add the following method to their class to get
168    * access to this context object:
169    * <p class='bjava'>
170    *    <jk>public void</jk> init(RestServletContext <jv>context</jv>) <jk>throws</jk> Exception;
171    * </p>
172    *
173    * @return The context information on this servlet.
174    */
175   public RestContext getContext() {
176      var rc = context.get();
177      if (rc == null)
178         throw new InternalServerError("RestContext object not set on resource.");
179      return rc;
180   }
181
182   /**
183    * Returns the path for this resource as defined by the @Rest(path) annotation or RestContext.Builder.path(String) method
184    * concatenated with those on all parent classes.
185    *
186    * @return The path defined on this servlet, or an empty string if not specified.
187    */
188   public String getPath() {
189      var context2 = this.context.get();
190      if (nn(context2))
191         return context2.getFullPath();
192      var ci = ClassInfo.of(getClass());
193      // @formatter:off
194      return rstream(AP.find(Rest.class, ci))
195         .map(x -> x.inner().path())
196         .filter(Shorts::ine)
197         .map(StringUtils::trimSlashes)
198         .findFirst()
199         .orElse("");
200      // @formatter:on
201   }
202
203   /**
204    * Returns the runtime-overridden top-level mount paths for this resource.
205    *
206    * <p>
207    * Subclasses can override this method to substitute the {@link Rest#paths() @Rest(paths)} annotation
208    * defaults at construction time. This is the &quot;getter&quot; rung in the runtime-override resolution
209    * chain documented on {@link RestContext#getPaths()}:
210    * <ol>
211    *    <li>Programmatic ({@code RestContext.Builder.paths(String...)}) &mdash; highest precedence.
212    *    <li>This {@code getPaths()} getter (when non-{@code null}).
213    *    <li>{@code @Rest(paths={...})} annotation default (SVL-resolved per element, comma-split) &mdash;
214    *       lowest precedence.
215    * </ol>
216    *
217    * <h5 class='section'>Accepted return shapes:</h5><ul>
218    *    <li>{@code null} &mdash; no override; resolution falls through to the {@code @Rest(paths=...)}
219    *       annotation default.
220    *    <li>{@link String} &mdash; a single path or a comma-delimited list, e.g.
221    *       {@code "/healthz,/readyz"}.
222    *    <li>{@code String[]} &mdash; each element may itself be a comma-delimited list.
223    *    <li>{@link java.util.Collection Collection}, {@link java.util.List List},
224    *       {@link java.util.Set Set}, {@link Iterable}, or {@link java.util.stream.Stream Stream}
225    *       of any of the above (nested mixes are recursively flattened).
226    *    <li>Primitive arrays (boxed during flattening).
227    * </ul>
228    *
229    * <p>
230    * Non-{@link String} leaves are coerced via {@link String#valueOf(Object)}.  Each leaf string then runs
231    * through the same per-element pipeline used for {@code @Rest(paths=...)} annotation elements: SVL is
232    * applied via the bean-store-backed {@link org.apache.juneau.commons.svl.VarResolverSession VarResolverSession}
233    * (so {@code $C{key}} consults the live {@link org.apache.juneau.config.Config Config}, {@code $E{NAME,default}}
234    * and {@code $S{prop,default}} resolve through the bootstrap variable catalog), and the post-SVL value is
235    * comma-split with each piece trimmed and empty pieces dropped.
236    *
237    * <h5 class='section'>Notes:</h5><ul>
238    *    <li class='note'>
239    *       Returning {@code null} (the default) means &quot;inherit annotation&quot; &mdash; the next rung resolves.
240    *    <li class='note'>
241    *       Returning {@code new String[0]}, an empty collection, an empty string, or a blank string means
242    *       &quot;explicitly clear&quot; &mdash; no top-level mounts.
243    *    <li class='note'>
244    *       The scalar accessor {@link #getPath()} remains orthogonal to this method &mdash; the singular
245    *       {@code @Rest(path=...)} attribute has its own resolution chain and is unaffected.
246    * </ul>
247    *
248    * @return The runtime-overridden mount paths in any accepted shape, or {@code null} to inherit the
249    *    annotation default.
250    * @since 10.0.0
251    */
252   public Object getPaths() { return null; }
253
254   /**
255    * Returns the current thread-local HTTP request.
256    *
257    * @return The current thread-local HTTP request, or <jk>null</jk> if it wasn't created.
258    */
259   public RestRequest getRequest() { return getContext().getLocalSession().getOpSession().getRequest(); }
260
261   /**
262    * Returns the current thread-local HTTP response.
263    *
264    * @return The current thread-local HTTP response, or <jk>null</jk> if it wasn't created.
265    */
266   public RestResponse getResponse() { return getContext().getLocalSession().getOpSession().getResponse(); }
267
268   @Override /* Overridden from Servlet */
269   public synchronized void init(ServletConfig servletConfig) throws ServletException {
270      try {
271         if (nn(context.get()))
272            return;
273         super.init(servletConfig);
274         context.set(new RestContext(new RestContext.Args(this.getClass(), null, servletConfig, () -> this, "", null, null, null, RestContext.ContextKind.ROOT, restBuilder)));
275         context.get().postInit();
276         context.get().postInitChildFirst();
277      } catch (ServletException e) {
278         initException.set(e);
279         log(SEVERE, e, "Servlet init error on class '%s'", cn(this));
280         throw e;
281      } catch (BasicHttpException e) {
282         initException.set(e);
283         log(SEVERE, e, "Servlet init error on class '%s'", cn(this));
284      } catch (Exception e) {
285         initException.set(new InternalServerError(e));
286         log(SEVERE, e, "Servlet init error on class '%s'", cn(this));
287      }
288   }
289
290   /**
291    * Log a message.
292    *
293    * <p>
294    * Subclasses can intercept the handling of these messages by overriding {@link #doLog(Level, Throwable, Supplier)}.
295    *
296    * @param level The log level.
297    * @param msg The message to log.
298    * @param args Optional {@link String#format(String, Object...) String.format}-style arguments.
299    */
300   public void log(Level level, String msg, Object...args) {
301      doLog(level, null, fs(msg, args));
302   }
303
304   /**
305    * Log a message.
306    *
307    * <p>
308    * Subclasses can intercept the handling of these messages by overriding {@link #doLog(Level, Throwable, Supplier)}.
309    *
310    * @param level The log level.
311    * @param cause The cause.
312    * @param msg The message to log.
313    * @param args Optional {@link String#format(String, Object...) String.format}-style arguments.
314    */
315   public void log(Level level, Throwable cause, String msg, Object...args) {
316      doLog(level, cause, fs(msg, args));
317   }
318
319   /**
320    * The main service method.
321    *
322    * <p>
323    * Subclasses can optionally override this method if they want to tailor the behavior of requests.
324    */
325   @Override /* Overridden from Servlet */
326   public void service(HttpServletRequest r1, HttpServletResponse r2) throws ServletException, InternalServerError, IOException {
327      try {
328         if (nn(initException.get()))
329            throw initException.get();
330         if (context.get() == null)
331            throw new InternalServerError(
332               "Servlet %s not initialized.  init(ServletConfig) was not called.  This can occur if you've overridden this method but didn't call super.init(RestConfig).", cn(this));
333         getContext().execute(this, r1, r2);
334
335      } catch (Exception e) {
336         r2.sendError(SC_INTERNAL_SERVER_ERROR, localizedMessage(e));
337      }
338   }
339
340   /**
341    * Main logger method.
342    *
343    * <p>
344    * The default behavior logs a message to the Java logger of the class name.
345    *
346    * <p>
347    * Subclasses can override this method to implement their own logger handling.
348    *
349    * @param level The log level.
350    * @param cause Optional throwable.
351    * @param msg The message to log.
352    */
353   protected void doLog(Level level, Throwable cause, Supplier<String> msg) {
354      var c = context.get();
355      var logger = c == null ? null : c.getLogger();
356      if (logger == null)
357         logger = RichLogger.getLogger(cn(this));
358      logger.log(level, cause, msg);
359   }
360
361   /**
362    * Sets the context object for this servlet.
363    *
364    * <p>
365    * This method is effectively a no-op if {@link #init(ServletConfig)} has already been called.
366    *
367    * @param context Sets the context object on this servlet.  Must not be <jk>null</jk>.
368    * @throws ServletException If error occurred during initialization.
369    */
370   protected synchronized void setContext(RestContext context) throws ServletException {
371      if (this.context.get() == null) {
372         super.init(context.getBuilder());
373         this.context.set(context);
374      }
375   }
376
377   /**
378    * Fluent builder for programmatically configuring a {@link RestServlet} subclass.
379    *
380    * <p>
381    * This is the subclassable, self-typed (CRTP) flavor builder.  Its {@code SELF} type parameter is left open so
382    * a user subclass's bespoke setters chain with true covariant returns alongside the inherited
383    * {@link RestBuilder} surface.  For the common case where the builder is not subclassed,
384    * use {@link RestServlet#builder(Class)} which returns the concrete {@link DefaultBuilder} leaf.
385    *
386    * @param <R> The resource type produced by {@link #build()}.
387    * @param <SELF> The concrete builder type (self type).
388    * @since 10.0.0
389    */
390   @SuppressWarnings({
391      "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name.
392   })
393   public static class Builder<R extends RestServlet,SELF extends Builder<R,SELF>> extends AbstractRestBuilder<R,SELF> {
394
395      /**
396       * Constructor.
397       *
398       * @param type The resource type produced by {@link #build()}.  Must not be <jk>null</jk>.
399       */
400      protected Builder(Class<R> type) {
401         super(type);
402      }
403
404      @Override /* AbstractRestBuilder */
405      public R build() {
406         var r = createResource();
407         r.restBuilder = this;
408         return r;
409      }
410   }
411
412   /**
413    * Concrete default leaf builder returned by {@link RestServlet#builder(Class)} for the common (non-subclassed)
414    * case, so callers are not forced to spell the {@code SELF} type parameter.
415    *
416    * @param <R> The resource type produced by {@link #build()}.
417    * @since 10.0.0
418    */
419   public static final class DefaultBuilder<R extends RestServlet> extends Builder<R,DefaultBuilder<R>> {
420      DefaultBuilder(Class<R> type) {
421         super(type);
422      }
423   }
424}