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 java.util.concurrent.atomic.*; 020 021import org.apache.juneau.rest.server.*; 022 023/** 024 * Optional base class for <c>@Rest</c> mixin classes — the third sibling of {@link RestServlet} 025 * (servlet flavor) and {@link RestResource} (child-resource flavor), completing the 026 * servlet / resource / mixin naming triad. 027 * 028 * <p> 029 * A <i>mixin</i> is a plain {@link Rest @Rest}-annotated POJO whose {@code @RestOp} methods are pulled 030 * into a host servlet/resource context when the host declares 031 * {@link Rest#mixins() @Rest(mixins=ThatMixin.class)}. Mixins compose through the existing sub-context 032 * model; {@code RestMixin} does <b>not</b> change that composition mechanism. 033 * 034 * <h5 class='section'>Opt-in, not mandatory:</h5> 035 * 036 * <p> 037 * Extending {@code RestMixin} is <b>optional</b>. A bare {@code @Rest} POJO with no base class remains a 038 * fully-supported mixin (annotation-only configuration), exactly as before — the framework reads 039 * such mixins reflectively. Capability mixins that ship without state (e.g. the api-docs pack) stay 040 * base-less; the new single-responsibility op-mixins extend this base to make the triad membership 041 * explicit. 042 * 043 * <h5 class='section'>Builder support:</h5> 044 * 045 * <p> 046 * The fluent programmatic-builder surface ({@link RestBuilder}/ {@link Builder}) lets a mixin be configured 047 * programmatically rather than (or in addition to) by annotation — builder-supplied values take precedence 048 * over {@link Rest @Rest} annotation values. Use {@link #builder(Class)} for the common case, or subclass 049 * {@link Builder} for capability mixins that add their own setters. 050 * 051 * <h5 class='section'>Reaching the host resource:</h5> 052 * 053 * <p> 054 * Because a mixin's {@code @RestOp} methods are bound to a per-mixin {@link RestContext} sub-context (the 055 * sub-context has no children of its own), host-level introspection performed against the mixin's <i>own</i> 056 * context comes back empty. {@link #getHostContext()} bridges that gap: it returns the {@link RestContext} of 057 * the host (mixed-into) resource so a mixin op can enumerate the host's child resources, swagger, stats, etc. 058 * 059 * <p class='bjava'> 060 * <jc>// Render the host's child-resource navigation list from a mixin op.</jc> 061 * <ja>@RestGet</ja>(path=<js>"/"</js>) 062 * <jk>public</jk> ChildResourceDescriptions getChildren(RestRequest <jv>req</jv>) { 063 * <jk>return new</jk> ChildResourceDescriptions(getHostContext(), <jv>req</jv>); 064 * } 065 * </p> 066 * 067 * <h5 class='section'>See Also:</h5><ul> 068 * <li class='jc'>{@link RestServlet} 069 * <li class='jc'>{@link RestResource} 070 * <li class='jm'>{@link #getHostContext()} 071 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerComposition">REST Server — Mixins and Multi-Mount Paths</a> 072 * </ul> 073 * 074 * @since 10.0.0 075 */ 076public abstract class RestMixin { 077 078 /** 079 * The per-mixin {@link RestContext} sub-context this mixin instance is bound to, captured via 080 * {@link #setContext(RestContext)} when the host composes the mixin. 081 * 082 * <p> 083 * Remains {@code null} when a {@code RestMixin} subclass is instantiated directly (not composed via 084 * {@link Rest#mixins() @Rest(mixins=...)}), in which case {@link #getHostContext()} returns {@code null}. 085 */ 086 private final AtomicReference<RestContext> context = new AtomicReference<>(); 087 088 /** 089 * The programmatic configuration builder stashed on this instance, or <jk>null</jk> when the 090 * mixin was constructed without a builder. Mutable so it can be written by either the 091 * {@link #RestMixin(RestBuilder)} constructor or {@link Builder#build()}. Read non-reflectively by 092 * {@link RestContext} during mixin sub-context construction so builder-supplied values take precedence over 093 * {@link Rest @Rest} annotation values. 094 */ 095 RestBuilder<?> restBuilder; 096 097 /** 098 * Default constructor. 099 */ 100 protected RestMixin() {} 101 102 /** 103 * Builder-injection constructor. 104 * 105 * @param builder The programmatic configuration builder. May be <jk>null</jk>. 106 */ 107 protected RestMixin(RestBuilder<?> builder) { 108 this.restBuilder = builder; 109 } 110 111 /** 112 * Returns the programmatic configuration builder stashed on this mixin, or <jk>null</jk> if none. 113 * 114 * @return The stashed builder, or <jk>null</jk> if none was injected. 115 */ 116 @SuppressWarnings({ 117 "java:S1452" // RestBuilder<?> wildcard return intentional; concrete builder type varies by REST resource class 118 }) 119 public RestBuilder<?> getRestBuilder() { 120 return restBuilder; 121 } 122 123 /** 124 * Creates a new fluent builder for programmatically configuring an instance of the specified mixin type. 125 * 126 * @param <R> The mixin type. 127 * @param type The mixin type to build. Must not be <jk>null</jk>. 128 * @return A new builder. 129 */ 130 public static <R extends RestMixin> DefaultBuilder<R> builder(Class<R> type) { 131 return new DefaultBuilder<>(type); 132 } 133 134 /** 135 * Captures the per-mixin {@link RestContext} sub-context this mixin instance is bound to. 136 * 137 * <p> 138 * Invoked reflectively by the host's {@code RestContext.buildMixinContext(...)} while composing the mixin 139 * (mirroring the {@code setContext(RestContext)} contract honored by {@link RestServlet} and 140 * {@link RestResource} for child resources). The supplied context is the mixin sub-context whose 141 * {@link RestContext#getParentContext() parent} is the host — that linkage is what {@link #getHostContext()} 142 * reads. 143 * 144 * @param value The mixin sub-context. Must not be <jk>null</jk>. 145 */ 146 protected void setContext(RestContext value) { 147 context.set(value); 148 } 149 150 /** 151 * Returns the {@link RestContext} of the host (mixed-into) resource this mixin is composed into. 152 * 153 * <p> 154 * A mixin's {@code @RestOp} methods are bound to a per-mixin sub-context that has no children of its own; 155 * this accessor returns the host context so a mixin op (or config-time code) can introspect the host 156 * — most commonly its child resources via {@link RestContext#getRestChildren()} for the navigation 157 * page. It is backed by the mixin sub-context's already-populated 158 * {@link RestContext#getParentContext() parent linkage}, so it is usable at config time and does not depend 159 * on an in-flight request. 160 * 161 * <h5 class='section'>Edge cases:</h5><ul> 162 * <li class='note'><b>Standalone / no host</b> — when a {@code RestMixin} subclass is instantiated 163 * directly rather than composed via {@link Rest#mixins() @Rest(mixins=...)}, this returns {@code null}, 164 * mirroring {@link RestContext#getParentContext()}'s top-level contract. Callers needing host-only 165 * behavior should null-check. 166 * <li class='note'><b>Nested mixins</b> — under nested {@code @Rest(mixins=...)} the flat-inheritance 167 * rule collects every mixin as a mixin of the single top-level host, so this returns that top-level 168 * host (never an intermediate mixin). 169 * </ul> 170 * 171 * @return The host resource's {@link RestContext}, or {@code null} when this mixin is not composed into a host. 172 * @since 10.0.0 173 */ 174 public RestContext getHostContext() { 175 var c = context.get(); 176 return c == null ? null : c.getParentContext(); 177 } 178 179 /** 180 * Fluent builder for programmatically configuring a {@link RestMixin} subclass. 181 * 182 * <p> 183 * Subclassable, self-typed (CRTP) flavor builder. Capability mixins (e.g. 184 * {@code FaviconMixin}) extend this and add their own worker-config setters, which chain with true covariant 185 * returns alongside the inherited {@link RestBuilder}surface. For the common (non-subclassed) case use 186 * {@link RestMixin#builder(Class)} which returns the concrete {@link DefaultBuilder} leaf. 187 * 188 * @param <R> The mixin type produced by {@link #build()}. 189 * @param <SELF> The concrete builder type (self type). 190 */ 191 @SuppressWarnings({ 192 "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name. 193 }) 194 public static class Builder<R extends RestMixin,SELF extends Builder<R,SELF>> extends AbstractRestBuilder<R,SELF> { 195 196 /** 197 * Constructor. 198 * 199 * @param type The mixin type produced by {@link #build()}. Must not be <jk>null</jk>. 200 */ 201 protected Builder(Class<R> type) { 202 super(type); 203 } 204 205 @Override /* AbstractRestBuilder */ 206 public R build() { 207 var r = createResource(); 208 r.restBuilder = this; 209 return r; 210 } 211 } 212 213 /** 214 * Concrete default leaf builder returned by {@link RestMixin#builder(Class)} for the common (non-subclassed) 215 * case. 216 * 217 * @param <R> The mixin type produced by {@link #build()}. 218 */ 219 public static final class DefaultBuilder<R extends RestMixin> extends Builder<R,DefaultBuilder<R>> { 220 DefaultBuilder(Class<R> type) { 221 super(type); 222 } 223 } 224}