001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2015-2016 ForgeRock AS. 015 */ 016 017package org.forgerock.json.resource; 018 019import static org.forgerock.http.routing.RouteMatchers.resourceApiVersionMatcher; 020import static org.forgerock.http.routing.RouteMatchers.uriMatcher; 021 022import java.util.ArrayList; 023import java.util.List; 024 025import org.forgerock.http.routing.ResourceApiVersionBehaviourManager; 026import org.forgerock.http.routing.RoutingMode; 027import org.forgerock.http.routing.Version; 028import org.forgerock.http.ApiProducer; 029import org.forgerock.services.context.Context; 030import org.forgerock.services.routing.IncomparableRouteMatchException; 031import org.forgerock.services.routing.RouteMatch; 032import org.forgerock.services.routing.RouteMatcher; 033 034/** 035 * A utility class that contains methods for creating route matchers. 036 */ 037public final class RouteMatchers { 038 039 private static final SelfApiMatcher SELF_API_MATCHER = new SelfApiMatcher(); 040 041 private RouteMatchers() { 042 } 043 044 /** 045 * Creates a {@code RouteMatcher} instance that matches {@code Request}s 046 * with the provided {@literal mode} and {@literal template}. 047 * 048 * @param mode The routing mode. 049 * @param template The uri template. 050 * @return A {@code RouteMatcher} instance. 051 */ 052 public static RouteMatcher<Request> requestUriMatcher(RoutingMode mode, String template) { 053 return new RequestUriRouteMatcher(uriMatcher(mode, template)); 054 } 055 056 /** 057 * Creates a new {@code ResourceApiVersionBehaviourManager} which is responsibly 058 * for managing whether warning headers are returned and the default 059 * version behaviour when the {@literal Accept-API-Version} header is not 060 * present on the request. 061 * 062 * @return A new {@code ResourceApiVersionBehaviourManager}. 063 */ 064 public static ResourceApiVersionBehaviourManager newResourceApiVersionBehaviourManager() { 065 return org.forgerock.http.routing.RouteMatchers.newResourceApiVersionBehaviourManager(); 066 } 067 068 /** 069 * Creates a {@code Filter} which MUST be placed, in the route, before any 070 * API Version routing takes place. 071 * 072 * <p>The filter will add the required {@code Context}s, default version 073 * behaviour and response headers.</p> 074 * 075 * @param behaviourManager A {@code ResourceApiVersionBehaviourManager} instance. 076 * @return A {@code Filter} instance. 077 */ 078 public static Filter resourceApiVersionContextFilter(ResourceApiVersionBehaviourManager behaviourManager) { 079 return new ResourceApiVersionRoutingFilter(behaviourManager); 080 } 081 082 /** 083 * Creates a {@code RouteMatcher} instance that matches the request 084 * resource API version with the provided {@literal version}. 085 * 086 * @param version The API version of the resource. 087 * @return A {@code RouteMatcher} instance. 088 */ 089 public static RouteMatcher<Request> requestResourceApiVersionMatcher(Version version) { 090 return new RequestApiVersionRouteMatcher(resourceApiVersionMatcher(version)); 091 } 092 093 /** 094 * A matcher to check if the request is for all versions of the API descriptor of the current path. 095 * 096 * @return A {@code RouteMatcher} instance. 097 */ 098 static RouteMatcher<Request> selfApiMatcher() { 099 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}