001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.rest;
014
015import static javax.servlet.http.HttpServletResponse.*;
016
017import java.util.*;
018
019import javax.servlet.http.*;
020
021/**
022 * Represents a group of CallMethods on a REST resource that handle the same HTTP Method name but with different
023 * paths/matchers/guards/etc...
024 *
025 * <p>
026 * Incoming requests for a particular HTTP method type (e.g. <js>"GET"</js>) are handed off to this class and then
027 * dispatched to the appropriate RestJavaMethod.
028 */
029public class RestCallRouter {
030   private final RestJavaMethod[] restJavaMethods;
031
032   RestCallRouter(RestJavaMethod[] callMethods) {
033      this.restJavaMethods = callMethods;
034   }
035
036   /**
037    * Builder class.
038    */
039   static final class Builder {
040      private List<RestJavaMethod> childMethods = new ArrayList<>();
041      private Set<String> collisions = new HashSet<>();
042      private String httpMethodName;
043
044      Builder(String httpMethodName) {
045         this.httpMethodName = httpMethodName;
046      }
047
048      String getHttpMethodName() {
049         return httpMethodName;
050      }
051
052      Builder add(RestJavaMethod m) throws RestServletException {
053         if (! m.hasGuardsOrMatchers()) {
054            String p = m.getHttpMethod() + ":" + m.getPathPattern();
055            if (collisions.contains(p))
056               throw new RestServletException("Duplicate Java methods assigned to the same method/pattern:  ''{0}''", p);
057            collisions.add(p);
058         }
059         childMethods.add(m);
060         return this;
061      }
062
063      RestCallRouter build() {
064         Collections.sort(childMethods);
065         return new RestCallRouter(childMethods.toArray(new RestJavaMethod[childMethods.size()]));
066      }
067   }
068
069   /**
070    * Workhorse method.
071    *
072    * <p>
073    * Routes this request to one of the CallMethods.
074    *
075    * @param pathInfo The value of {@link HttpServletRequest#getPathInfo()} (sorta)
076    * @return The HTTP response code.
077    */
078   int invoke(String pathInfo, RestRequest req, RestResponse res) throws Throwable {
079      if (restJavaMethods.length == 1)
080         return restJavaMethods[0].invoke(pathInfo, req, res);
081
082      int maxRc = 0;
083      for (RestJavaMethod m : restJavaMethods) {
084         int rc = m.invoke(pathInfo, req, res);
085         if (rc == SC_OK)
086            return SC_OK;
087         maxRc = Math.max(maxRc, rc);
088      }
089      return maxRc;
090   }
091
092   @Override /* Object */
093   public String toString() {
094      StringBuilder sb = new StringBuilder("RestCallRouter: [\n");
095      for (RestJavaMethod sm : restJavaMethods)
096         sb.append("\t" + sm + "\n");
097      sb.append("]");
098      return sb.toString();
099   }
100}