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*/
016package org.forgerock.openig.filter;
017
018
019import static org.forgerock.openig.http.Responses.newInternalServerError;
020
021import org.forgerock.http.Filter;
022import org.forgerock.http.Handler;
023import org.forgerock.http.protocol.Request;
024import org.forgerock.http.protocol.Response;
025import org.forgerock.services.context.Context;
026import org.forgerock.util.promise.NeverThrowsException;
027import org.forgerock.util.promise.Promise;
028import org.forgerock.util.promise.PromiseImpl;
029import org.forgerock.util.promise.ResultHandler;
030import org.forgerock.util.promise.RuntimeExceptionHandler;
031
032/**
033 * This filter aims to guarantee the caller that it will always get a Response to process, even if the {@literal next}
034 * returns a promise completed with a {@link RuntimeException}, or even if a {@link RuntimeException} is thrown.
035 */
036public class RuntimeExceptionFilter implements Filter {
037
038    @Override
039    public Promise<Response, NeverThrowsException> filter(Context context, Request request, Handler next) {
040        // Wraps the result's promise into another promise so we can ensure that in every case we return a Response.
041        final PromiseImpl<Response, NeverThrowsException> promise = PromiseImpl.create();
042
043        try {
044            next.handle(context, request)
045                    .thenOnResult(new ResultHandler<Response>() {
046                        @Override
047                        public void handleResult(Response result) {
048                            promise.handleResult(result);
049                        }
050                    })
051                    .thenOnRuntimeException(onRuntimeException(promise));
052            // Note : it's not possible to instantiate a NeverThrowsException so there's no need to add an
053            // ExceptionHandler<NeverThrowsException>
054        } catch (RuntimeException exception) {
055            // next.handle can throw such exceptions
056            onRuntimeException(promise).handleRuntimeException(exception);
057        }
058        return promise;
059    }
060
061    private RuntimeExceptionHandler onRuntimeException(final PromiseImpl<Response, NeverThrowsException> promise) {
062        return new RuntimeExceptionHandler() {
063            @Override
064            public void handleRuntimeException(RuntimeException exception) {
065                promise.handleResult(errorResponse(exception));
066            }
067        };
068    }
069
070    private static Response errorResponse(Exception exception) {
071        return newInternalServerError().setCause(exception);
072    }
073
074}