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
021import org.apache.juneau.rest.util.*;
022
023/**
024 * Represents a group of CallMethods on a REST resource that handle the same HTTP Method name but with different
025 * paths/matchers/guards/etc...
026 *
027 * <p>
028 * Incoming requests for a particular HTTP method type (e.g. <js>"GET"</js>) are handed off to this class and then
029 * dispatched to the appropriate RestJavaMethod.
030 */
031public class RestCallRouter {
032   private final RestMethodContext[] restJavaMethods;
033
034   RestCallRouter(RestMethodContext[] callMethods) {
035      this.restJavaMethods = callMethods;
036   }
037
038   /**
039    * Builder class.
040    */
041   static final class Builder {
042      private List<RestMethodContext> childMethods = new ArrayList<>();
043      private Set<String> collisions = new HashSet<>();
044      private String httpMethodName;
045
046      Builder(String httpMethodName) {
047         this.httpMethodName = httpMethodName;
048      }
049
050      String getHttpMethodName() {
051         return httpMethodName;
052      }
053
054      Builder add(RestMethodContext m) throws RestServletException {
055         if (! m.hasGuardsOrMatchers()) {
056            String p = m.getHttpMethod() + ":" + m.getPathPattern();
057            if (collisions.contains(p))
058               throw new RestServletException("Duplicate Java methods assigned to the same method/pattern:  ''{0}''", p);
059            collisions.add(p);
060         }
061         childMethods.add(m);
062         return this;
063      }
064
065      RestCallRouter build() {
066         Collections.sort(childMethods);
067         return new RestCallRouter(childMethods.toArray(new RestMethodContext[childMethods.size()]));
068      }
069   }
070
071   boolean matches(UrlPathInfo pathInfo) {
072      for (RestMethodContext m : restJavaMethods)
073         if (m.matches(pathInfo))
074            return true;
075      return false;
076   }
077
078   /**
079    * Workhorse method.
080    *
081    * <p>
082    * Routes this request to one of the CallMethods.
083    *
084    * @param pathInfo The value of {@link HttpServletRequest#getPathInfo()} (sorta)
085    * @return The HTTP response code.
086    */
087   int invoke(UrlPathInfo pathInfo, RestRequest req, RestResponse res) throws Throwable {
088      if (restJavaMethods.length == 1)
089         return restJavaMethods[0].invoke(pathInfo, req, res);
090
091      int maxRc = 0;
092      for (RestMethodContext m : restJavaMethods) {
093         int rc = m.invoke(pathInfo, req, res);
094         if (rc == SC_OK)
095            return SC_OK;
096         maxRc = Math.max(maxRc, rc);
097      }
098      return maxRc;
099   }
100
101   @Override /* Object */
102   public String toString() {
103      StringBuilder sb = new StringBuilder("RestCallRouter: [\n");
104      for (RestMethodContext sm : restJavaMethods)
105         sb.append("\t" + sm + "\n");
106      sb.append("]");
107      return sb.toString();
108   }
109}