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 org.forgerock.services.context.Context;
020import org.forgerock.http.routing.ResourceApiVersionBehaviourManager;
021import org.forgerock.http.routing.ApiVersionRouterContext;
022import org.forgerock.http.routing.Version;
023import org.forgerock.util.promise.ExceptionHandler;
024import org.forgerock.util.promise.Promise;
025import org.forgerock.util.promise.ResultHandler;
026
027/**
028 * API Version routing filter which creates a {@link ApiVersionRouterContext}
029 * which contains the default routing behaviour when the
030 * {@literal Accept-API-Version} header is set on the request. In addition also
031 * sets the {@literal Warning} and {@literal Content-API-Version} headers on
032 * the response.
033 */
034public class ResourceApiVersionRoutingFilter extends org.forgerock.http.routing.ResourceApiVersionRoutingFilter
035        implements Filter {
036
037    ResourceApiVersionRoutingFilter(ResourceApiVersionBehaviourManager behaviourManager) {
038        super(behaviourManager);
039    }
040
041    @Override
042    public Promise<ActionResponse, ResourceException> filterAction(Context context, ActionRequest request,
043            RequestHandler next) {
044        final ApiVersionRouterContext apiVersionRouterContext = createApiVersionRouterContext(context);
045        return wrapWithApiVersionInfo(apiVersionRouterContext, request,
046                next.handleAction(apiVersionRouterContext, request));
047    }
048
049    @Override
050    public Promise<ResourceResponse, ResourceException> filterCreate(Context context, CreateRequest request,
051            RequestHandler next) {
052        final ApiVersionRouterContext apiVersionRouterContext = createApiVersionRouterContext(context);
053        return wrapWithApiVersionInfo(apiVersionRouterContext, request,
054                next.handleCreate(apiVersionRouterContext, request));
055    }
056
057    @Override
058    public Promise<ResourceResponse, ResourceException> filterDelete(Context context, DeleteRequest request,
059            RequestHandler next) {
060        final ApiVersionRouterContext apiVersionRouterContext = createApiVersionRouterContext(context);
061        return wrapWithApiVersionInfo(apiVersionRouterContext, request,
062                next.handleDelete(apiVersionRouterContext, request));
063    }
064
065    @Override
066    public Promise<ResourceResponse, ResourceException> filterPatch(Context context, PatchRequest request,
067            RequestHandler next) {
068        final ApiVersionRouterContext apiVersionRouterContext = createApiVersionRouterContext(context);
069        return wrapWithApiVersionInfo(apiVersionRouterContext, request,
070                next.handlePatch(apiVersionRouterContext, request));
071    }
072
073    @Override
074    public Promise<QueryResponse, ResourceException> filterQuery(Context context, QueryRequest request,
075            QueryResourceHandler handler, RequestHandler next) {
076        final ApiVersionRouterContext apiVersionRouterContext = createApiVersionRouterContext(context);
077        return wrapWithApiVersionInfo(apiVersionRouterContext, request,
078                next.handleQuery(apiVersionRouterContext, request, handler));
079    }
080
081    @Override
082    public Promise<ResourceResponse, ResourceException> filterRead(Context context, ReadRequest request,
083            RequestHandler next) {
084        final ApiVersionRouterContext apiVersionRouterContext = createApiVersionRouterContext(context);
085        return wrapWithApiVersionInfo(apiVersionRouterContext, request,
086                next.handleRead(apiVersionRouterContext, request));
087    }
088
089    @Override
090    public Promise<ResourceResponse, ResourceException> filterUpdate(Context context, UpdateRequest request,
091            RequestHandler next) {
092        final ApiVersionRouterContext apiVersionRouterContext = createApiVersionRouterContext(context);
093        return wrapWithApiVersionInfo(apiVersionRouterContext, request,
094                next.handleUpdate(apiVersionRouterContext, request));
095    }
096
097    private <V extends Response> Promise<V, ResourceException> wrapWithApiVersionInfo(
098            final ApiVersionRouterContext apiVersionRouterContext, final Request request,
099            Promise<V, ResourceException> promise) {
100        return promise
101                .thenOnResult(new ResultHandler<V>() {
102                    @Override
103                    public void handleResult(V response) {
104                        setApiVersionInfo(apiVersionRouterContext, request, response);
105                    }
106                })
107                .thenOnException(new ExceptionHandler<ResourceException>() {
108                    @Override
109                    public void handleException(ResourceException e) {
110                        setApiVersionInfo(apiVersionRouterContext, request, e);
111                    }
112                });
113    }
114
115    static void setApiVersionInfo(ApiVersionRouterContext apiVersionRouterContext, Request request, Response response) {
116        Version resourceVersion = apiVersionRouterContext.getResourceVersion();
117        if (resourceVersion != null) {
118            response.setResourceApiVersion(resourceVersion);
119        }
120        if (apiVersionRouterContext.isWarningEnabled() && request.getResourceVersion() == null) {
121            AdviceContext adviceContext = apiVersionRouterContext.asContext(AdviceContext.class);
122            if (!adviceContext.getAdvices().containsKey("crest")) {
123                adviceContext.putAdvice("Warning", AdviceWarning.getNotPresent("CREST", "Accept-API-Version")
124                        .toString());
125            }
126        }
127    }
128}