View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions copyright [year] [name of copyright owner]".
13   *
14   * Copyright 2015-2016 ForgeRock AS.
15   */
16  
17  package org.forgerock.json.resource;
18  
19  import static org.forgerock.http.routing.RouteMatchers.resourceApiVersionMatcher;
20  import static org.forgerock.http.routing.RouteMatchers.uriMatcher;
21  
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import org.forgerock.http.routing.ResourceApiVersionBehaviourManager;
26  import org.forgerock.http.routing.RoutingMode;
27  import org.forgerock.http.routing.Version;
28  import org.forgerock.http.ApiProducer;
29  import org.forgerock.services.context.Context;
30  import org.forgerock.services.routing.IncomparableRouteMatchException;
31  import org.forgerock.services.routing.RouteMatch;
32  import org.forgerock.services.routing.RouteMatcher;
33  
34  /**
35   * A utility class that contains methods for creating route matchers.
36   */
37  public final class RouteMatchers {
38  
39      private static final SelfApiMatcher SELF_API_MATCHER = new SelfApiMatcher();
40  
41      private RouteMatchers() {
42      }
43  
44      /**
45       * Creates a {@code RouteMatcher} instance that matches {@code Request}s
46       * with the provided {@literal mode} and {@literal template}.
47       *
48       * @param mode The routing mode.
49       * @param template The uri template.
50       * @return A {@code RouteMatcher} instance.
51       */
52      public static RouteMatcher<Request> requestUriMatcher(RoutingMode mode, String template) {
53          return new RequestUriRouteMatcher(uriMatcher(mode, template));
54      }
55  
56      /**
57       * Creates a new {@code ResourceApiVersionBehaviourManager} which is responsibly
58       * for managing whether warning headers are returned and the default
59       * version behaviour when the {@literal Accept-API-Version} header is not
60       * present on the request.
61       *
62       * @return A new {@code ResourceApiVersionBehaviourManager}.
63       */
64      public static ResourceApiVersionBehaviourManager newResourceApiVersionBehaviourManager() {
65          return org.forgerock.http.routing.RouteMatchers.newResourceApiVersionBehaviourManager();
66      }
67  
68      /**
69       * Creates a {@code Filter} which MUST be placed, in the route, before any
70       * API Version routing takes place.
71       *
72       * <p>The filter will add the required {@code Context}s, default version
73       * behaviour and response headers.</p>
74       *
75       * @param behaviourManager A {@code ResourceApiVersionBehaviourManager} instance.
76       * @return A {@code Filter} instance.
77       */
78      public static Filter resourceApiVersionContextFilter(ResourceApiVersionBehaviourManager behaviourManager) {
79          return new ResourceApiVersionRoutingFilter(behaviourManager);
80      }
81  
82      /**
83       * Creates a {@code RouteMatcher} instance that matches the request
84       * resource API version with the provided {@literal version}.
85       *
86       * @param version The API version of the resource.
87       * @return A {@code RouteMatcher} instance.
88       */
89      public static RouteMatcher<Request> requestResourceApiVersionMatcher(Version version) {
90          return new RequestApiVersionRouteMatcher(resourceApiVersionMatcher(version));
91      }
92  
93      /**
94       * A matcher to check if the request is for all versions of the API descriptor of the current path.
95       *
96       * @return A {@code RouteMatcher} instance.
97       */
98      static RouteMatcher<Request> selfApiMatcher() {
99          return SELF_API_MATCHER;
100     }
101 
102     /**
103      * A CREST specific {@code RouteMatcher} which extracts the requests
104      * resource name from a {@code Request} and passes it as a
105      * {@code ResourcePath} to the common {@code ResourcePath} route predicate.
106      */
107     private static final class RequestUriRouteMatcher extends RouteMatcher<Request> {
108 
109         private final RouteMatcher<List<String>> delegate;
110 
111         private RequestUriRouteMatcher(RouteMatcher<List<String>> delegate) {
112             this.delegate = delegate;
113         }
114 
115         @Override
116         public RouteMatch evaluate(Context context, Request request) {
117             final List<String> pathElements = new ArrayList<>(request.getResourcePathObject().size());
118             for (String pathElement : request.getResourcePathObject()) {
119                 pathElements.add(pathElement);
120             }
121             return delegate.evaluate(context, pathElements);
122         }
123 
124         @Override
125         public String toString() {
126             return delegate.toString();
127         }
128 
129         @Override
130         public String idFragment() {
131             return delegate.idFragment();
132         }
133 
134         @Override
135         public boolean equals(Object o) {
136             if (this == o) {
137                 return true;
138             }
139             if (!(o instanceof RequestUriRouteMatcher)) {
140                 return false;
141             }
142             RequestUriRouteMatcher that = (RequestUriRouteMatcher) o;
143             return delegate.equals(that.delegate);
144         }
145 
146         @Override
147         public <T> T transformApi(T t, ApiProducer<T> apiProducer) {
148             return delegate.transformApi(t, apiProducer);
149         }
150 
151         @Override
152         public int hashCode() {
153             return delegate.hashCode();
154         }
155     }
156 
157     /**
158      * A CREST specific {@code RouteMatcher} which extracts the resource API
159      * version from a {@code Request} and passes it to the common
160      * {@code Version} route matcher.
161      */
162     private static final class RequestApiVersionRouteMatcher extends RouteMatcher<Request> {
163 
164         private final RouteMatcher<Version> delegate;
165 
166         private RequestApiVersionRouteMatcher(RouteMatcher<Version> delegate) {
167             this.delegate = delegate;
168         }
169 
170         @Override
171         public RouteMatch evaluate(Context context, Request request) {
172             return delegate.evaluate(context, request.getResourceVersion());
173         }
174 
175         @Override
176         public String toString() {
177             return delegate.toString();
178         }
179 
180         @Override
181         public String idFragment() {
182             return delegate.idFragment();
183         }
184 
185         @Override
186         public boolean equals(Object o) {
187             if (this == o) {
188                 return true;
189             }
190             if (!(o instanceof RequestApiVersionRouteMatcher)) {
191                 return false;
192             }
193             RequestApiVersionRouteMatcher that = (RequestApiVersionRouteMatcher) o;
194             return delegate.equals(that.delegate);
195         }
196 
197         @Override
198         public <T> T transformApi(T t, ApiProducer<T> apiProducer) {
199             return delegate.transformApi(t, apiProducer);
200         }
201 
202         @Override
203         public int hashCode() {
204             return delegate.hashCode();
205         }
206     }
207 
208     private static class SelfApiMatcher extends RouteMatcher<Request> {
209 
210         @Override
211         public RouteMatch evaluate(Context context, final Request request) {
212             return new RouteMatch() {
213                 @Override
214                 public boolean isBetterMatchThan(RouteMatch result) throws IncomparableRouteMatchException {
215                     return request.getRequestType().equals(RequestType.API)
216                             && request.getResourceVersion() == null
217                             && request.getResourcePathObject().equals(ResourcePath.empty());
218                 }
219 
220                 @Override
221                 public Context decorateContext(Context context) {
222                     return context;
223                 }
224             };
225         }
226 
227         @Override
228         public String toString() {
229             return "API Request";
230         }
231 
232         @Override
233         public int hashCode() {
234             return 0;
235         }
236 
237         @Override
238         public boolean equals(Object o) {
239             return this == o;
240         }
241 
242         @Override
243         public String idFragment() {
244             throw new UnsupportedOperationException();
245         }
246 
247         @Override
248         public <D> D transformApi(D descriptor, ApiProducer<D> producer) {
249             throw new UnsupportedOperationException();
250         }
251     }
252 }