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 * §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 "getter" rung in the runtime-override resolution 209 * chain documented on {@link RestContext#getPaths()}: 210 * <ol> 211 * <li>Programmatic ({@code RestContext.Builder.paths(String...)}) — 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) — 214 * lowest precedence. 215 * </ol> 216 * 217 * <h5 class='section'>Accepted return shapes:</h5><ul> 218 * <li>{@code null} — no override; resolution falls through to the {@code @Rest(paths=...)} 219 * annotation default. 220 * <li>{@link String} — a single path or a comma-delimited list, e.g. 221 * {@code "/healthz,/readyz"}. 222 * <li>{@code String[]} — 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 "inherit annotation" — 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 * "explicitly clear" — no top-level mounts. 243 * <li class='note'> 244 * The scalar accessor {@link #getPath()} remains orthogonal to this method — 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}