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 org.apache.juneau.rest.server.*;
020import org.apache.juneau.rest.server.config.*;
021import org.apache.juneau.rest.server.ops.*;
022
023import jakarta.servlet.*;
024import jakarta.servlet.http.*;
025
026/**
027 * Identical to {@link BasicRestServletGroup} but doesn't extend from {@link HttpServlet}.
028 *
029 * <p>
030 * Meant as a base class for child REST resources in servlet containers or Spring Boot environments.
031 *
032 * <p>
033 * Provides support for JSON, XML, HTML, URL-Encoding, UON, XML, OpenAPI, and MessagePack.  See {@link BasicUniversalConfig}
034 * for details.
035 *
036 * <p>
037 * Adds the group-navigation endpoint ({@code GET /}) by composing {@link NavigationMixin} via
038 * {@link Rest#mixins() @Rest(mixins=...)} on top of the residual op-mixins inherited from
039 * {@link BasicRestResource}. The navigation op lives on the {@code NavigationMixin} sub-context (backed by
040 * {@link RestMixin#getHostContext()}) but renders with the host's
041 * {@link org.apache.juneau.marshall.html.HtmlDocConfig @HtmlDocConfig} page decoration &mdash; mixin
042 * sub-contexts inherit the host's class-level {@code @HtmlDocConfig} (and other class-level config), so the
043 * navigation page matches the host's other endpoints.
044 *
045 * <p>
046 * Children are attached to this resource using the {@link Rest#children() @Rest(children)} annotation.
047 *
048 * <h5 class='section'>Dynamic children:</h5>
049 *
050 * <p>
051 * Additional child resources can be registered or unregistered at runtime via {@link #addChild(Class)} /
052 * {@link #removeChild(String)} (and overloads). This is particularly useful in test fixtures where a single
053 * group resource is mounted once and individual tests plug in their own {@code @Rest} resources without restarting
054 * the container.
055 *
056 * <h5 class='section'>See Also:</h5><ul>
057 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestAnnotatedClasses">@Rest-Annotated Class</a>
058 * </ul>
059 */
060@Rest(mixins=NavigationMixin.class)
061public abstract class BasicRestResourceGroup extends BasicRestResource {
062
063   /**
064    * Returns the {@link RestChildren} registry backing this group resource.
065    *
066    * <p>
067    * Use this for full access to the dynamic add/remove API, including the {@code replace} overloads and the
068    * unmodifiable {@link RestChildren#asMap() asMap()} snapshot.
069    *
070    * @return The {@link RestChildren} registry for this resource's {@link RestContext}.
071    */
072   public RestChildren getChildResources() {
073      return getContext().getRestChildren();
074   }
075
076   /**
077    * Dynamically registers a child REST resource by class.
078    *
079    * <p>
080    * Convenience pass-through to {@link RestChildren#addChild(Class)} on {@link #getChildResources()}.
081    *
082    * @param resourceClass The {@code @Rest}-annotated resource class.
083    * @return The newly-built child {@link RestContext}.
084    * @throws ServletException If construction or lifecycle initialization of the child fails.
085    */
086   public RestContext addChild(Class<?> resourceClass) throws ServletException {
087      return getChildResources().addChild(resourceClass);
088   }
089
090   /**
091    * Dynamically registers a pre-instantiated child REST resource.
092    *
093    * <p>
094    * Convenience pass-through to {@link RestChildren#addChild(Object)} on {@link #getChildResources()}.
095    *
096    * @param resource The {@code @Rest}-annotated resource instance.
097    * @return The newly-built child {@link RestContext}.
098    * @throws ServletException If construction or lifecycle initialization of the child fails.
099    */
100   public RestContext addChild(Object resource) throws ServletException {
101      return getChildResources().addChild(resource);
102   }
103
104   /**
105    * Dynamically registers a pre-instantiated child REST resource at an explicit path.
106    *
107    * <p>
108    * Convenience pass-through to {@link RestChildren#addChild(String, Object)} on {@link #getChildResources()}.
109    *
110    * @param path The path segment under this group at which to mount the child.
111    * @param resource The {@code @Rest}-annotated resource instance.
112    * @return The newly-built child {@link RestContext}.
113    * @throws ServletException If construction or lifecycle initialization of the child fails.
114    */
115   public RestContext addChild(String path, Object resource) throws ServletException {
116      return getChildResources().addChild(path, resource);
117   }
118
119   /**
120    * Removes the child registered at the given composed path.
121    *
122    * <p>
123    * Convenience pass-through to {@link RestChildren#removeChild(String)} on {@link #getChildResources()}.
124    *
125    * @param path The composed path key (as returned by {@link RestContext#getPath()}).
126    * @return The removed and destroyed {@link RestContext}, or {@code null} if no child was registered there.
127    */
128   public RestContext removeChild(String path) {
129      return getChildResources().removeChild(path);
130   }
131
132   /**
133    * Removes the first child whose resource class matches the given type.
134    *
135    * <p>
136    * Convenience pass-through to {@link RestChildren#removeChild(Class)} on {@link #getChildResources()}.
137    *
138    * @param resourceClass The resource class to match.
139    * @return The removed and destroyed {@link RestContext}, or {@code null} if no matching child was found.
140    */
141   public RestContext removeChild(Class<?> resourceClass) {
142      return getChildResources().removeChild(resourceClass);
143   }
144}