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 2010–2011 ApexIdentity Inc.
015 * Portions Copyright 2011-2014 ForgeRock AS.
016 */
017
018package org.forgerock.openig.handler;
019
020import static org.forgerock.openig.util.Json.*;
021import static org.forgerock.util.Utils.*;
022
023import java.io.IOException;
024import java.util.List;
025import java.util.Map;
026
027import org.forgerock.json.fluent.JsonValue;
028import org.forgerock.openig.el.Expression;
029import org.forgerock.openig.heap.GenericHeaplet;
030import org.forgerock.openig.heap.HeapException;
031import org.forgerock.openig.http.Exchange;
032import org.forgerock.openig.http.HttpUtil;
033import org.forgerock.openig.http.Response;
034import org.forgerock.openig.util.CaseInsensitiveMap;
035import org.forgerock.openig.util.MultiValueMap;
036
037/**
038 * Creates a static response in an HTTP exchange.
039 */
040public class StaticResponseHandler extends GenericHandler {
041
042    /** The response status code (e.g. 200). */
043    private final Integer status;
044
045    /** The response status reason (e.g. "OK"). */
046    private final String reason;
047
048    /** Protocol version (e.g. {@code "HTTP/1.1"}. */
049    private final String version;
050
051    /** Message header fields whose values are expressions that are evaluated. */
052    private final MultiValueMap<String, Expression> headers =
053            new MultiValueMap<String, Expression>(new CaseInsensitiveMap<List<Expression>>());
054
055    /** The message entity expression. */
056    private final Expression entity;
057
058    /**
059     * Constructor.
060     *
061     * @param status
062     *            The response status to set.
063     * @param reason
064     *            The response status reason to set.
065     */
066    public StaticResponseHandler(final Integer status, final String reason) {
067        this(status, reason, null, null);
068    }
069
070    /**
071     * Constructor.
072     *
073     * @param status
074     *            The response status to set.
075     * @param reason
076     *            The response status reason to set.
077     * @param version
078     *            The protocol version.
079     * @param entity
080     *            The message entity expression.
081     */
082    public StaticResponseHandler(final Integer status, final String reason, final String version,
083            final Expression entity) {
084        super();
085        this.status = status;
086        this.reason = reason;
087        this.version = version;
088        this.entity = entity;
089    }
090
091    /**
092     * Adds a pair key / expression to the header.
093     *
094     * @param key
095     *            The header key.
096     * @param expression
097     *            The expression to evaluate.
098     * @return The current static response handler.
099     */
100    public StaticResponseHandler addHeader(final String key, final Expression expression) {
101        headers.add(key, expression);
102        return this;
103    }
104
105    @Override
106    public void handle(Exchange exchange) throws HandlerException, IOException {
107        Response response = new Response();
108        response.setStatus(this.status);
109        response.setReason(this.reason);
110        if (response.getReason() == null) {
111            // not explicit, derive from status
112            response.setReason(HttpUtil.getReason(response.getStatus()));
113        }
114        if (response.getReason() == null) {
115            // couldn't derive from status; say something
116            response.setReason("Uncertain");
117        }
118        if (this.version != null) { // default in Message class
119            response.setVersion(this.version);
120        }
121        for (String key : this.headers.keySet()) {
122            for (Expression expression : this.headers.get(key)) {
123                String eval = expression.eval(exchange, String.class);
124                if (eval != null) {
125                    response.getHeaders().add(key, eval);
126                }
127            }
128        }
129        if (entity != null) {
130            // use content-type charset (or default)
131            response.setEntity(entity.eval(exchange, String.class));
132        }
133        // finally replace response in the exchange
134        closeSilently(exchange.response);
135        exchange.response = response;
136    }
137
138    /**
139     * Creates and initializes a static response handler in a heap environment.
140     */
141    public static class Heaplet extends GenericHeaplet {
142        @Override
143        public Object create() throws HeapException {
144            final int status = config.get("status").required().asInteger();
145            final String reason = config.get("reason").asString();
146            final String version = config.get("version").asString();
147            final JsonValue headers = config.get("headers").expect(Map.class);
148            final Expression entity = asExpression(config.get("entity"));
149            final StaticResponseHandler handler = new StaticResponseHandler(status, reason, version, entity);
150            if (headers != null) {
151                for (String key : headers.keys()) {
152                    for (JsonValue value : headers.get(key).expect(List.class)) {
153                        handler.addHeader(key, asExpression(value.required()));
154                    }
155                }
156            }
157            return handler;
158        }
159    }
160}