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(RestCall call) {
072      UrlPathInfo pi = call.getUrlPathInfo();
073      for (RestMethodContext m : restJavaMethods)
074         if (m.matches(pi))
075            return true;
076      return false;
077   }
078
079   /**
080    * Workhorse method.
081    *
082    * <p>
083    * Routes this request to one of the CallMethods.
084    *
085    * @param pathInfo The value of {@link HttpServletRequest#getPathInfo()} (sorta)
086    * @return The HTTP response code.
087    */
088   int invoke(RestCall call) throws Throwable {
089      if (restJavaMethods.length == 1)
090         return restJavaMethods[0].invoke(call);
091
092      int maxRc = 0;
093      for (RestMethodContext m : restJavaMethods) {
094         int rc = m.invoke(call);
095         if (rc == SC_OK)
096            return SC_OK;
097         maxRc = Math.max(maxRc, rc);
098      }
099      return maxRc;
100   }
101
102   @Override /* Object */
103   public String toString() {
104      StringBuilder sb = new StringBuilder("RestCallRouter: [\n");
105      for (RestMethodContext sm : restJavaMethods)
106         sb.append("\t" + sm + "\n");
107      sb.append("]");
108      return sb.toString();
109   }
110}