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.filter; 019 020import static org.forgerock.openig.util.Json.*; 021 022import java.io.IOException; 023import java.util.ArrayList; 024import java.util.List; 025 026import org.forgerock.json.fluent.JsonValue; 027import org.forgerock.openig.el.Expression; 028import org.forgerock.openig.handler.Handler; 029import org.forgerock.openig.handler.HandlerException; 030import org.forgerock.openig.heap.GenericHeaplet; 031import org.forgerock.openig.heap.HeapException; 032import org.forgerock.openig.http.Exchange; 033 034/** 035 * Conditionally assigns values to expressions before and after the exchange is handled. 036 */ 037public class AssignmentFilter extends GenericFilter { 038 039 /** Defines assignment condition, target and value expressions. */ 040 private static final class Binding { 041 /** Condition to evaluate to determine if assignment should occur, or {@code null} if assignment is 042 * unconditional. */ 043 private Expression condition; 044 /** Expression that yields the target object whose value is to be set. */ 045 private Expression target; 046 /** Expression that yields the value to be set in the target. */ 047 private Expression value; 048 049 private Binding(final Expression condition, final Expression target, final Expression value) { 050 this.condition = condition; 051 this.target = target; 052 this.value = value; 053 } 054 } 055 056 /** Assignment bindings to apply before the request is handled. */ 057 private final List<Binding> onRequest = new ArrayList<Binding>(); 058 059 /** Assignment bindings to apply after the request is handled. */ 060 private final List<Binding> onResponse = new ArrayList<Binding>(); 061 062 /** 063 * Registers an unconditional (always executed) binding on the request flow. The value stored in the target will be 064 * {@literal null}. 065 * 066 * @param target 067 * Expression that yields the target object whose value is to be set 068 * @return this object for fluent usage 069 */ 070 public AssignmentFilter addRequestBinding(final Expression target) { 071 return this.addRequestBinding(target, null); 072 } 073 074 /** 075 * Registers an unconditional (always executed) binding on the request flow. The value stored in the target will be 076 * the result of the value {@link Expression}. 077 * 078 * @param target 079 * Expression that yields the target object whose value is to be set 080 * @param value 081 * Expression that yields the value to be set in the target (may be {@literal null}) 082 * @return this object for fluent usage 083 */ 084 public AssignmentFilter addRequestBinding(final Expression target, final Expression value) { 085 return this.addRequestBinding(null, target, value); 086 } 087 088 /** 089 * Registers a conditional binding on the request flow. If the condition is fulfilled, the value stored in the 090 * target will be the result of the value {@link Expression}. 091 * 092 * @param condition 093 * Condition to evaluate to determine if assignment should occur (may be {@literal null}, aka 094 * unconditional) 095 * @param target 096 * Expression that yields the target object whose value is to be set 097 * @param value 098 * Expression that yields the value to be set in the target (may be {@literal null}) 099 * @return this object for fluent usage 100 */ 101 public AssignmentFilter addRequestBinding(final Expression condition, 102 final Expression target, 103 final Expression value) { 104 this.onRequest.add(new Binding(condition, target, value)); 105 return this; 106 } 107 108 /** 109 * Registers an unconditional (always executed) binding on the response flow. The value stored in the target will be 110 * {@literal null}. 111 * 112 * @param target 113 * Expression that yields the target object whose value is to be set 114 * @return this object for fluent usage 115 */ 116 public AssignmentFilter addResponseBinding(final Expression target) { 117 return this.addResponseBinding(target, null); 118 } 119 120 /** 121 * Registers an unconditional (always executed) binding on the response flow. The value stored in the target will be 122 * the result of the value {@link Expression}. 123 * 124 * @param target 125 * Expression that yields the target object whose value is to be set 126 * @param value 127 * Expression that yields the value to be set in the target (may be {@literal null}) 128 * @return this object for fluent usage 129 */ 130 public AssignmentFilter addResponseBinding(final Expression target, final Expression value) { 131 return this.addResponseBinding(null, target, value); 132 } 133 134 /** 135 * Registers a conditional binding on the response flow. If the condition is fulfilled, the value stored in the 136 * target will be the result of the value {@link Expression}. 137 * 138 * @param condition 139 * Condition to evaluate to determine if assignment should occur (may be {@literal null}, aka 140 * unconditional) 141 * @param target 142 * Expression that yields the target object whose value is to be set 143 * @param value 144 * Expression that yields the value to be set in the target (may be {@literal null}) 145 * @return this object for fluent usage 146 */ 147 public AssignmentFilter addResponseBinding(final Expression condition, 148 final Expression target, 149 final Expression value) { 150 this.onResponse.add(new Binding(condition, target, value)); 151 return this; 152 } 153 154 @Override 155 public void filter(Exchange exchange, Handler next) throws HandlerException, IOException { 156 for (Binding binding : onRequest) { 157 eval(binding, exchange); 158 } 159 next.handle(exchange); 160 for (Binding binding : onResponse) { 161 eval(binding, exchange); 162 } 163 } 164 165 private void eval(Binding binding, Exchange exchange) { 166 if (binding.condition == null || Boolean.TRUE.equals(binding.condition.eval(exchange))) { 167 binding.target.set(exchange, binding.value != null ? binding.value.eval(exchange) : null); 168 } 169 } 170 171 /** Creates and initializes an assignment filter in a heap environment. */ 172 public static class Heaplet extends GenericHeaplet { 173 @Override 174 public Object create() throws HeapException { 175 AssignmentFilter result = new AssignmentFilter(); 176 addRequestBindings(result); 177 addResponseBindings(result); 178 return result; 179 } 180 181 private void addRequestBindings(final AssignmentFilter filter) { 182 // optional 183 JsonValue bindings = config.get("onRequest").expect(List.class); 184 for (JsonValue binding : bindings) { 185 Expression condition = asExpression(binding.get("condition")); 186 Expression target = asExpression(binding.get("target").required()); 187 Expression value = asExpression(binding.get("value")); 188 189 filter.addRequestBinding(condition, target, value); 190 } 191 } 192 193 private void addResponseBindings(final AssignmentFilter filter) { 194 // optional 195 JsonValue bindings = config.get("onResponse").expect(List.class); 196 for (JsonValue binding : bindings) { 197 Expression condition = asExpression(binding.get("condition")); 198 Expression target = asExpression(binding.get("target").required()); 199 Expression value = asExpression(binding.get("value")); 200 201 filter.addResponseBinding(condition, target, value); 202 } 203 } 204 } 205}