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 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.services.context.Context;
029import org.forgerock.services.routing.RouteMatch;
030import org.forgerock.services.routing.RouteMatcher;
031
032/**
033 * A utility class that contains methods for creating route matchers.
034 */
035public final class RouteMatchers {
036
037    private RouteMatchers() {
038    }
039
040    /**
041     * Creates a {@code RouteMatcher} instance that matches {@code Request}s
042     * with the provided {@literal mode} and {@literal template}.
043     *
044     * @param mode The routing mode.
045     * @param template The uri template.
046     * @return A {@code RouteMatcher} instance.
047     */
048    public static RouteMatcher<Request> requestUriMatcher(RoutingMode mode, String template) {
049        return new RequestUriRouteMatcher(uriMatcher(mode, template));
050    }
051
052    /**
053     * Creates a new {@code ResourceApiVersionBehaviourManager} which is responsibly
054     * for managing whether warning headers are returned and the default
055     * version behaviour when the {@literal Accept-API-Version} header is not
056     * present on the request.
057     *
058     * @return A new {@code ResourceApiVersionBehaviourManager}.
059     */
060    public static ResourceApiVersionBehaviourManager newResourceApiVersionBehaviourManager() {
061        return org.forgerock.http.routing.RouteMatchers.newResourceApiVersionBehaviourManager();
062    }
063
064    /**
065     * Creates a {@code Filter} which MUST be placed, in the route, before any
066     * API Version routing takes place.
067     *
068     * <p>The filter will add the required {@code Context}s, default version
069     * behaviour and response headers.</p>
070     *
071     * @param behaviourManager A {@code ResourceApiVersionBehaviourManager} instance.
072     * @return A {@code Filter} instance.
073     */
074    public static Filter resourceApiVersionContextFilter(ResourceApiVersionBehaviourManager behaviourManager) {
075        return new ResourceApiVersionRoutingFilter(behaviourManager);
076    }
077
078    /**
079     * Creates a {@code RouteMatcher} instance that matches the request
080     * resource API version with the provided {@literal version}.
081     *
082     * @param version The API version of the resource.
083     * @return A {@code RouteMatcher} instance.
084     */
085    public static RouteMatcher<Request> requestResourceApiVersionMatcher(Version version) {
086        return new RequestApiVersionRouteMatcher(resourceApiVersionMatcher(version));
087    }
088
089    /**
090     * A CREST specific {@code RouteMatcher} which extracts the requests
091     * resource name from a {@code Request} and passes it as a
092     * {@code ResourcePath} to the common {@code ResourcePath} route predicate.
093     */
094    private static final class RequestUriRouteMatcher extends RouteMatcher<Request> {
095
096        private final RouteMatcher<List<String>> delegate;
097
098        private RequestUriRouteMatcher(RouteMatcher<List<String>> delegate) {
099            this.delegate = delegate;
100        }
101
102        @Override
103        public RouteMatch evaluate(Context context, Request request) {
104            final List<String> pathElements = new ArrayList<>(request.getResourcePathObject().size());
105            for (String pathElement : request.getResourcePathObject()) {
106                pathElements.add(pathElement);
107            }
108            return delegate.evaluate(context, pathElements);
109        }
110
111        @Override
112        public String toString() {
113            return delegate.toString();
114        }
115
116        @Override
117        public boolean equals(Object o) {
118            if (this == o) {
119                return true;
120            }
121            if (!(o instanceof RequestUriRouteMatcher)) {
122                return false;
123            }
124            RequestUriRouteMatcher that = (RequestUriRouteMatcher) o;
125            return delegate.equals(that.delegate);
126        }
127
128        @Override
129        public int hashCode() {
130            return delegate.hashCode();
131        }
132    }
133
134    /**
135     * A CREST specific {@code RouteMatcher} which extracts the resource API
136     * version from a {@code Request} and passes it to the common
137     * {@code Version} route matcher.
138     */
139    private static final class RequestApiVersionRouteMatcher extends RouteMatcher<Request> {
140
141        private final RouteMatcher<Version> delegate;
142
143        private RequestApiVersionRouteMatcher(RouteMatcher<Version> delegate) {
144            this.delegate = delegate;
145        }
146
147        @Override
148        public RouteMatch evaluate(Context context, Request request) {
149            return delegate.evaluate(context, request.getResourceVersion());
150        }
151
152        @Override
153        public String toString() {
154            return delegate.toString();
155        }
156
157        @Override
158        public boolean equals(Object o) {
159            if (this == o) {
160                return true;
161            }
162            if (!(o instanceof RequestApiVersionRouteMatcher)) {
163                return false;
164            }
165            RequestApiVersionRouteMatcher that = (RequestApiVersionRouteMatcher) o;
166            return delegate.equals(that.delegate);
167        }
168
169        @Override
170        public int hashCode() {
171            return delegate.hashCode();
172        }
173    }
174}