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;
018
019import static org.apache.juneau.common.utils.Utils.*;
020import static org.apache.juneau.internal.CollectionUtils.*;
021import static org.apache.juneau.internal.CollectionUtils.map;
022
023import java.util.*;
024
025import org.apache.juneau.*;
026import org.apache.juneau.common.utils.*;
027import org.apache.juneau.cp.*;
028import org.apache.juneau.internal.*;
029import org.apache.juneau.rest.annotation.*;
030import org.apache.juneau.rest.util.*;
031
032import jakarta.servlet.*;
033
034/**
035 * Implements the child resources of a {@link Rest}-annotated class.
036 *
037 * <h5 class='section'>See Also:</h5><ul>
038 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestAnnotatedClassBasics">@Rest-Annotated Class Basics</a>
039 * </ul>
040 */
041public class RestChildren {
042
043   //-----------------------------------------------------------------------------------------------------------------
044   // Static
045   //-----------------------------------------------------------------------------------------------------------------
046
047   /**
048    * Represents a null value for the {@link Rest#restChildrenClass()} annotation.
049    */
050   @SuppressWarnings("javadoc")
051   public final class Void extends RestChildren {
052      public Void(Builder builder) throws Exception {
053         super(builder);
054      }
055   }
056
057   /**
058    * Static creator.
059    *
060    * @param beanStore The bean store to use for creating beans.
061    * @return A new builder for this object.
062    */
063   public static Builder create(BeanStore beanStore) {
064      return new Builder(beanStore);
065   }
066
067   //-----------------------------------------------------------------------------------------------------------------
068   // Builder
069   //-----------------------------------------------------------------------------------------------------------------
070
071   /**
072    * Builder class.
073    */
074   public static class Builder extends BeanBuilder<RestChildren> {
075
076      final List<RestContext> list;
077
078      /**
079       * Constructor.
080       *
081       * @param beanStore The bean store to use for creating beans.
082       */
083      protected Builder(BeanStore beanStore) {
084         super(RestChildren.class, beanStore);
085         list = Utils.list();
086      }
087
088      @Override /* BeanBuilder */
089      protected RestChildren buildDefault() {
090         return new RestChildren(this);
091      }
092
093      //-------------------------------------------------------------------------------------------------------------
094      // Properties
095      //-------------------------------------------------------------------------------------------------------------
096
097      /**
098       * Adds a child resource to this builder.
099       *
100       * @param value The REST context of the child resource.
101       * @return This object.
102       */
103      public Builder add(RestContext value) {
104         list.add(value);
105         return this;
106      }
107      @Override /* Overridden from BeanBuilder */
108      public Builder impl(Object value) {
109         super.impl(value);
110         return this;
111      }
112
113      @Override /* Overridden from BeanBuilder */
114      public Builder type(Class<?> value) {
115         super.type(value);
116         return this;
117      }
118   }
119
120   //-----------------------------------------------------------------------------------------------------------------
121   // Instance
122   //-----------------------------------------------------------------------------------------------------------------
123
124   private final Map<String,RestContext> children = synced(map());
125
126   /**
127    * Constructor.
128    *
129    * @param builder The builder containing the settings for this object.
130    */
131   public RestChildren(Builder builder) {
132      for (RestContext rc : builder.list)
133         children.put(rc.getPath(), rc);
134   }
135
136   /**
137    * Looks through the registered children of this object and returns the best match.
138    *
139    * @param builder The HTTP call builder.
140    * @return The child that best matches the call, or an empty {@link Optional} if a match could not be made.
141    */
142   public Optional<RestChildMatch> findMatch(RestSession.Builder builder) {
143      String pi = builder.getPathInfoUndecoded();
144      if ((! children.isEmpty()) && pi != null && ! pi.equals("/")) {
145         for (RestContext rc : children.values()) {
146            UrlPathMatcher upp = rc.getPathMatcher();
147            UrlPathMatch uppm = upp.match(builder.getUrlPath());
148            if (uppm != null) {
149               return Utils.opt(RestChildMatch.create(uppm, rc));
150            }
151         }
152      }
153      return opte();
154   }
155
156   /**
157    * Returns the children in this object as a map.
158    *
159    * <p>
160    * The keys are the {@link RestContext#getPath() paths} of the child contexts.
161    *
162    * @return The children as an unmodifiable map.
163    */
164   public Map<String,RestContext> asMap() {
165      return u(children);
166   }
167
168   //-----------------------------------------------------------------------------------------------------------------
169   // Lifecycle methods.
170   //-----------------------------------------------------------------------------------------------------------------
171
172   /**
173    * Called during servlet initialization on all children to invoke all {@link RestPostInit} child-last methods.
174    *
175    * @throws ServletException Error occurred.
176    */
177   public void postInit() throws ServletException {
178      for (RestContext childContext : children.values())
179         childContext.postInit();
180   }
181
182   /**
183    * Called during servlet initialization on all children to invoke all {@link RestPostInit} child-first methods.
184    *
185    * @throws ServletException Error occurred.
186    */
187   public void postInitChildFirst() throws ServletException {
188      for (RestContext childContext : children.values())
189         childContext.postInitChildFirst();
190   }
191
192   /**
193    * Called during servlet destruction on all children to invoke all {@link RestDestroy} and {@link Servlet#destroy()} methods.
194    */
195   public void destroy() {
196      for (RestContext r : children.values()) {
197         r.destroy();
198         if (r.getResource() instanceof Servlet)
199            ((Servlet)r.getResource()).destroy();
200      }
201   }
202}