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