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 — 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}