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