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.openig.http;
018
019import static java.lang.String.format;
020
021import java.util.concurrent.CountDownLatch;
022
023import org.forgerock.http.Handler;
024import org.forgerock.http.protocol.Request;
025import org.forgerock.http.protocol.Response;
026import org.forgerock.http.protocol.Status;
027import org.forgerock.services.context.Context;
028import org.forgerock.util.promise.ResultHandler;
029
030/**
031 * Provide out-of-the-box, pre-configured {@link Response} objects.
032 */
033public final class Responses {
034
035    /**
036     * Empty private constructor for utility.
037     */
038    private Responses() { }
039
040    /**
041     * Generates an empty {@literal Internal Server Error} response ({@literal 500}).
042     *
043     * @return an empty {@literal Internal Server Error} response ({@literal 500}).
044     */
045    public static Response newInternalServerError() {
046        return new Response(Status.INTERNAL_SERVER_ERROR);
047    }
048
049    /**
050     * Generates an {@literal Internal Server Error} response ({@literal 500}) whose content is set to the given {@code
051     * exception}'s message.
052     *
053     * @param exception
054     *         wrapped exception
055     * @return a configured {@literal Internal Server Error} response ({@literal 500}).
056     */
057    public static Response newInternalServerError(Exception exception) {
058        return newInternalServerError(exception.getMessage())
059                .setCause(exception);
060    }
061
062    /**
063     * Generates an {@literal Internal Server Error} response ({@literal 500}) whose content is set to a concatenation
064     * of the given {@code message} and {@code exception}'s message.
065     *
066     * @param message
067     *         first part of the response's content
068     * @param exception
069     *         wrapped exception
070     * @return a configured {@literal Internal Server Error} response ({@literal 500}).
071     */
072    public static Response newInternalServerError(String message, Exception exception) {
073        return newInternalServerError(format("%s: %s", message, exception.getMessage())).setCause(exception);
074    }
075
076    /**
077     * Generates an {@literal Internal Server Error} response ({@literal 500}) whose content is set to the given {@code
078     * message}.
079     *
080     * @param message
081     *         response's content
082     * @return a configured {@literal Internal Server Error} response ({@literal 500}).
083     */
084    public static Response newInternalServerError(String message) {
085        return newInternalServerError().setEntity(message);
086    }
087
088    /**
089     * Generates an empty {@literal Not Found} response ({@literal 404}).
090     *
091     * @return an empty {@literal Not Found} response ({@literal 404}).
092     */
093    public static Response newNotFound() {
094        return new Response(Status.NOT_FOUND);
095    }
096
097    /**
098     * Generates a {@literal Not Found} response ({@literal 404}) whose content is set to the given {@code message}.
099     *
100     * @param message
101     *         response's content
102     * @return a configured {@literal Not Found} response ({@literal 404}).
103     */
104    public static Response newNotFound(String message) {
105        return newNotFound().setEntity(message);
106    }
107
108    /**
109     * Executes a blocking call with the given {@code handler}, {@code context} and {@code request}, returning
110     * the {@link Response} when fully available.
111     *
112     * <p>This function is here to fix a concurrency issue where a caller thread is blocking a promise and is
113     * resumed before all of the ResultHandlers and Function of the blocked promise have been invoked.
114     * That may lead to concurrent consumption of {@link org.forgerock.http.io.BranchingInputStream} that is a
115     * not thread safe object.
116     *
117     * @param handler Handler for handling the given request
118     * @param context Context to be used for the invocation
119     * @param request request to be executed
120     * @return a ready to used {@link Response}
121     * @throws InterruptedException if either {@link org.forgerock.util.promise.Promise#getOrThrow()} or
122     *         {@link CountDownLatch#await()} is interrupted.
123     */
124    public static Response blockingCall(final Handler handler, final Context context, final Request request)
125            throws InterruptedException {
126
127        final CountDownLatch latch = new CountDownLatch(1);
128        Response response = handler.handle(context, request)
129                                   // Decrement the latch at the very end of the listener's sequence
130                                   .thenOnResult(new ResultHandler<Response>() {
131                                       @Override
132                                       public void handleResult(Response result) {
133                                           latch.countDown();
134                                       }
135                                   })
136                                   // Block the promise, waiting for the response
137                                   .getOrThrow();
138
139        // Wait for the latch to be released so we can make sure that all of the Promise's ResultHandlers and Functions
140        // have been invoked
141        latch.await();
142
143        return response;
144    }
145}