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-2015 ForgeRock AS. 016 */ 017 018package org.forgerock.openig.handler; 019 020import static org.forgerock.openig.el.Bindings.bindings; 021import static org.forgerock.openig.util.JsonValues.asExpression; 022 023import java.util.List; 024import java.util.Map; 025 026import org.forgerock.http.Handler; 027import org.forgerock.http.protocol.Request; 028import org.forgerock.http.protocol.Response; 029import org.forgerock.http.protocol.Status; 030import org.forgerock.http.util.CaseInsensitiveMap; 031import org.forgerock.http.util.MultiValueMap; 032import org.forgerock.json.JsonValue; 033import org.forgerock.openig.el.Bindings; 034import org.forgerock.openig.el.Expression; 035import org.forgerock.openig.heap.GenericHeapObject; 036import org.forgerock.openig.heap.GenericHeaplet; 037import org.forgerock.openig.heap.HeapException; 038import org.forgerock.services.context.Context; 039import org.forgerock.util.promise.NeverThrowsException; 040import org.forgerock.util.promise.Promise; 041import org.forgerock.util.promise.Promises; 042 043/** 044 * Creates a static HTTP response. 045 */ 046public class StaticResponseHandler extends GenericHeapObject implements Handler { 047 048 /** The status (code + reason). */ 049 private final Status status; 050 051 /** Protocol version (e.g. {@code "HTTP/1.1"}. */ 052 private final String version; 053 054 /** Message header fields whose values are expressions that are evaluated. */ 055 private final MultiValueMap<String, Expression<String>> headers = 056 new MultiValueMap<>(new CaseInsensitiveMap<List<Expression<String>>>()); 057 058 /** The message entity expression. */ 059 private final Expression<String> entity; 060 061 /** 062 * Constructor. 063 * 064 * @param status 065 * The response status to set. 066 */ 067 public StaticResponseHandler(final Status status) { 068 this(status, null, null); 069 } 070 071 /** 072 * Constructor. 073 * 074 * @param status 075 * The response status to set. 076 * @param version 077 * The protocol version. 078 * @param entity 079 * The message entity expression. 080 */ 081 public StaticResponseHandler(final Status status, 082 final String version, 083 final Expression<String> entity) { 084 this.status = status; 085 this.version = version; 086 this.entity = entity; 087 } 088 089 /** 090 * Adds a pair key / expression to the header. 091 * 092 * @param key 093 * The header key. 094 * @param expression 095 * The expression to evaluate. 096 * @return The current static response handler. 097 */ 098 public StaticResponseHandler addHeader(final String key, final Expression<String> expression) { 099 headers.add(key, expression); 100 return this; 101 } 102 103 @Override 104 public Promise<Response, NeverThrowsException> handle(final Context context, final Request request) { 105 Bindings bindings = bindings(context, request); 106 Response response = new Response(); 107 response.setStatus(this.status); 108 if (this.version != null) { // default in Message class 109 response.setVersion(this.version); 110 } 111 for (String key : this.headers.keySet()) { 112 for (Expression<String> expression : this.headers.get(key)) { 113 String eval = expression.eval(bindings); 114 if (eval != null) { 115 response.getHeaders().add(key, eval); 116 } 117 } 118 } 119 if (entity != null) { 120 // use content-type charset (or default) 121 response.setEntity(entity.eval(bindings)); 122 } 123 return Promises.newResultPromise(response); 124 } 125 126 /** 127 * Creates and initializes a static response handler in a heap environment. 128 */ 129 public static class Heaplet extends GenericHeaplet { 130 @Override 131 public Object create() throws HeapException { 132 final int code = config.get("status").required().asInteger(); 133 final String reason = config.get("reason").asString(); 134 Status status = Status.valueOf(code, reason); 135 final String version = config.get("version").asString(); 136 final JsonValue headers = config.get("headers").expect(Map.class); 137 final Expression<String> entity = asExpression(config.get("entity"), String.class); 138 final StaticResponseHandler handler = new StaticResponseHandler(status, version, entity); 139 if (headers != null) { 140 for (String key : headers.keys()) { 141 for (JsonValue value : headers.get(key).expect(List.class)) { 142 handler.addHeader(key, asExpression(value.required(), String.class)); 143 } 144 } 145 } 146 return handler; 147 } 148 } 149}