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 org.apache.juneau.commons.utils.Shorts.*; 020 021import java.util.concurrent.atomic.*; 022import java.util.function.*; 023import java.util.logging.*; 024 025import org.apache.juneau.commons.logging.RichLogger; 026import org.apache.juneau.http.response.*; 027import org.apache.juneau.rest.server.*; 028 029import jakarta.servlet.*; 030import jakarta.servlet.http.*; 031 032/** 033 * Identical to {@link RestServlet} but doesn't extend from {@link HttpServlet}. 034 * 035 * <p> 036 * This is particularly useful in Spring Boot environments that auto-detect servlets to deploy in servlet containers, 037 * but you want this resource to be deployed as a child instead. 038 * 039 * <h5 class='section'>See Also:</h5><ul> 040 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestAnnotatedClasses">@Rest-Annotated Class</a> 041 * </ul> 042 */ 043public abstract class RestResource { 044 045 private final AtomicReference<RestContext> context = new AtomicReference<>(); 046 047 /** 048 * The programmatic configuration builder stashed on this instance, or <jk>null</jk> when the 049 * resource was constructed without a builder. Mutable so it can be written by either the 050 * {@link #RestResource(RestBuilder)} constructor or {@link Builder#build()}. Read non-reflectively by 051 * {@link RestContext} during construction so builder-supplied values take precedence over {@code @Rest} 052 * annotation values. 053 */ 054 RestBuilder<?> restBuilder; 055 056 /** 057 * Default constructor. 058 */ 059 protected RestResource() {} 060 061 /** 062 * Builder-injection constructor. 063 * 064 * @param builder The programmatic configuration builder. May be <jk>null</jk>. 065 */ 066 protected RestResource(RestBuilder<?> builder) { 067 this.restBuilder = builder; 068 } 069 070 /** 071 * Returns the programmatic configuration builder stashed on this resource, or <jk>null</jk> if none. 072 * 073 * @return The stashed builder, or <jk>null</jk> if none was injected. 074 */ 075 @SuppressWarnings({ 076 "java:S1452" // RestBuilder<?> wildcard return intentional; concrete builder type varies by REST resource class 077 }) 078 public RestBuilder<?> getRestBuilder() { 079 return restBuilder; 080 } 081 082 /** 083 * Creates a new fluent builder for programmatically configuring an instance of the specified resource type. 084 * 085 * @param <R> The resource type. 086 * @param type The resource type to build. Must not be <jk>null</jk>. 087 * @return A new builder. 088 */ 089 public static <R extends RestResource> DefaultBuilder<R> builder(Class<R> type) { 090 return new DefaultBuilder<>(type); 091 } 092 093 /** 094 * Returns the current thread-local HTTP request. 095 * 096 * @return The current thread-local HTTP request, or <jk>null</jk> if it wasn't created. 097 */ 098 public synchronized RestRequest getRequest() { return getContext().getLocalSession().getOpSession().getRequest(); } 099 100 /** 101 * Returns the current thread-local HTTP response. 102 * 103 * @return The current thread-local HTTP response, or <jk>null</jk> if it wasn't created. 104 */ 105 public synchronized RestResponse getResponse() { return getContext().getLocalSession().getOpSession().getResponse(); } 106 107 /** 108 * Log a message. 109 * 110 * <p> 111 * Subclasses can intercept the handling of these messages by overriding {@link #doLog(Level, Throwable, Supplier)}. 112 * 113 * @param level The log level. 114 * @param msg The message to log. 115 * @param args Optional {@link String#format(String, Object...) String.format}-style arguments. 116 */ 117 public void log(Level level, String msg, Object...args) { 118 doLog(level, null, fs(msg, args)); 119 } 120 121 /** 122 * Log a message. 123 * 124 * <p> 125 * Subclasses can intercept the handling of these messages by overriding {@link #doLog(Level, Throwable, Supplier)}. 126 * 127 * @param level The log level. 128 * @param cause The cause. 129 * @param msg The message to log. 130 * @param args Optional {@link String#format(String, Object...) String.format}-style arguments. 131 */ 132 public void log(Level level, Throwable cause, String msg, Object...args) { 133 doLog(level, cause, fs(msg, args)); 134 } 135 136 /** 137 * Main logger method. 138 * 139 * <p> 140 * The default behavior logs a message to the Java logger of the class name. 141 * 142 * <p> 143 * Subclasses can override this method to implement their own logger handling. 144 * 145 * @param level The log level. 146 * @param cause Optional throwable. 147 * @param msg The message to log. 148 */ 149 protected void doLog(Level level, Throwable cause, Supplier<String> msg) { 150 var c = context.get(); 151 var logger = c == null ? null : c.getLogger(); 152 if (logger == null) 153 logger = RichLogger.getLogger(cn(this)); 154 logger.log(level, cause, msg); 155 } 156 157 /** 158 * Returns the read-only context object that contains all the configuration information about this resource. 159 * 160 * @return The context information on this servlet. 161 */ 162 protected RestContext getContext() { 163 if (context.get() == null) 164 throw new InternalServerError("RestContext object not set on resource."); 165 return context.get(); 166 } 167 168 /** 169 * Returns the runtime-overridden top-level mount paths for this resource. 170 * 171 * <p> 172 * Subclasses can override this method to substitute the {@code @Rest(paths)} annotation defaults at 173 * construction time. This is the "getter" rung in the runtime-override resolution chain 174 * documented on {@link RestContext#getPaths()}: 175 * <ol> 176 * <li>Programmatic ({@code RestContext.Builder.paths(String...)}) — highest precedence. 177 * <li>This {@code getPaths()} getter (when non-{@code null}). 178 * <li>{@code @Rest(paths={...})} annotation default (SVL-resolved per element, comma-split) — 179 * lowest precedence. 180 * </ol> 181 * 182 * <h5 class='section'>Accepted return shapes:</h5><ul> 183 * <li>{@code null} — no override; resolution falls through to the {@code @Rest(paths=...)} 184 * annotation default. 185 * <li>{@link String} — a single path or a comma-delimited list, e.g. 186 * {@code "/healthz,/readyz"}. 187 * <li>{@code String[]} — each element may itself be a comma-delimited list. 188 * <li>{@link java.util.Collection Collection}, {@link java.util.List List}, 189 * {@link java.util.Set Set}, {@link Iterable}, or {@link java.util.stream.Stream Stream} 190 * of any of the above (nested mixes are recursively flattened). 191 * <li>Primitive arrays (boxed during flattening). 192 * </ul> 193 * 194 * <p> 195 * Non-{@link String} leaves are coerced via {@link String#valueOf(Object)}. Each leaf string then runs 196 * through the same per-element pipeline used for {@code @Rest(paths=...)} annotation elements: SVL is 197 * applied via the bean-store-backed {@link org.apache.juneau.commons.svl.VarResolverSession VarResolverSession} 198 * (so {@code $C{key}} consults the live {@link org.apache.juneau.config.Config Config}, {@code $E{NAME,default}} 199 * and {@code $S{prop,default}} resolve through the bootstrap variable catalog), and the post-SVL value is 200 * comma-split with each piece trimmed and empty pieces dropped. 201 * 202 * <h5 class='section'>Notes:</h5><ul> 203 * <li class='note'> 204 * Returning {@code null} (the default) means "inherit annotation" — the next rung resolves. 205 * <li class='note'> 206 * Returning {@code new String[0]}, an empty collection, an empty string, or a blank string means 207 * "explicitly clear" — no top-level mounts. 208 * </ul> 209 * 210 * @return The runtime-overridden mount paths in any accepted shape, or {@code null} to inherit the 211 * annotation default. 212 * @since 10.0.0 213 */ 214 public Object getPaths() { return null; } 215 216 /** 217 * Sets the context object for this servlet. 218 * 219 * @param context Sets the context object on this servlet. 220 * @throws ServletException If error occurred during post-initialiation. 221 */ 222 protected void setContext(RestContext context) throws ServletException { 223 this.context.set(context); 224 } 225 226 /** 227 * Fluent builder for programmatically configuring a {@link RestResource} subclass. 228 * 229 * <p> 230 * Subclassable, self-typed (CRTP) flavor builder. For the common (non-subclassed) case 231 * use {@link RestResource#builder(Class)} which returns the concrete {@link DefaultBuilder} leaf. 232 * 233 * @param <R> The resource type produced by {@link #build()}. 234 * @param <SELF> The concrete builder type (self type). 235 */ 236 @SuppressWarnings({ 237 "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name. 238 }) 239 public static class Builder<R extends RestResource,SELF extends Builder<R,SELF>> extends AbstractRestBuilder<R,SELF> { 240 241 /** 242 * Constructor. 243 * 244 * @param type The resource type produced by {@link #build()}. Must not be <jk>null</jk>. 245 */ 246 protected Builder(Class<R> type) { 247 super(type); 248 } 249 250 @Override /* AbstractRestBuilder */ 251 public R build() { 252 var r = createResource(); 253 r.restBuilder = this; 254 return r; 255 } 256 } 257 258 /** 259 * Concrete default leaf builder returned by {@link RestResource#builder(Class)} for the common (non-subclassed) 260 * case. 261 * 262 * @param <R> The resource type produced by {@link #build()}. 263 */ 264 public static final class DefaultBuilder<R extends RestResource> extends Builder<R,DefaultBuilder<R>> { 265 DefaultBuilder(Class<R> type) { 266 super(type); 267 } 268 } 269}