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; 025import java.util.Map; 026 027import org.forgerock.json.fluent.JsonValue; 028import org.forgerock.openig.el.Expression; 029import org.forgerock.openig.handler.Handler; 030import org.forgerock.openig.handler.HandlerException; 031import org.forgerock.openig.heap.GenericHeaplet; 032import org.forgerock.openig.heap.HeapException; 033import org.forgerock.openig.http.Exchange; 034 035/** 036 * Conditionally diverts the exchange to another handler. Before and after the exchange is 037 * handled, associated conditions are evaluated. If a condition evaluates to {@code true}, then 038 * the exchange flow is diverted to the associated handler. If no condition evaluates to 039 * {@code true}, then the exchange flows normally through the filter. 040 */ 041public class SwitchFilter extends GenericFilter { 042 043 /** Associates a condition with a handler to divert to if the condition yields {@code true}. */ 044 private static class Case { 045 /** Condition to evaluate if exchange should be diverted to handler. */ 046 private final Expression condition; 047 048 /** Handler to divert to if condition yields {@code true}. */ 049 private final Handler handler; 050 051 /** 052 * Build a switch case from a condition and the handler to execute if condition yields. 053 * @param condition expression to evaluate 054 * @param handler handler to be executed if the condition yields 055 */ 056 public Case(final Expression condition, final Handler handler) { 057 this.condition = condition; 058 this.handler = handler; 059 } 060 } 061 062 /** Switch cases to test before the exchange is handled. */ 063 private final List<Case> requestCases = new ArrayList<Case>(); 064 065 /** Switch cases to test after the exchange is handled. */ 066 private final List<Case> responseCases = new ArrayList<Case>(); 067 068 /** 069 * Add a request switch case with a condition and the handler to execute if condition yields. 070 * @param condition expression to evaluate 071 * @param handler handler to be executed if the condition yields 072 * @return this filter for fluent invocation. 073 */ 074 public SwitchFilter addRequestCase(final Expression condition, final Handler handler) { 075 requestCases.add(new Case(condition, handler)); 076 return this; 077 } 078 079 /** 080 * Add a response switch case with a condition and the handler to execute if condition yields. 081 * @param condition expression to evaluate 082 * @param handler handler to be executed if the condition yields 083 * @return this filter for fluent invocation. 084 */ 085 public SwitchFilter addResponseCase(final Expression condition, final Handler handler) { 086 responseCases.add(new Case(condition, handler)); 087 return this; 088 } 089 090 @Override 091 public void filter(Exchange exchange, Handler next) throws HandlerException, IOException { 092 if (!doSwitch(exchange, requestCases)) { 093 // not intercepted 094 next.handle(exchange); 095 doSwitch(exchange, responseCases); 096 } 097 } 098 099 private boolean doSwitch(Exchange exchange, List<Case> cases) throws HandlerException, IOException { 100 for (Case c : cases) { 101 Object o = (c.condition != null ? c.condition.eval(exchange) : Boolean.TRUE); 102 if (o instanceof Boolean && ((Boolean) o)) { 103 c.handler.handle(exchange); 104 // switched flow 105 return true; 106 } 107 } 108 // no interception 109 return false; 110 } 111 112 /** 113 * Creates and initializes an expect filter in a heap environment. 114 */ 115 public static class Heaplet extends GenericHeaplet { 116 @Override 117 public Object create() throws HeapException { 118 SwitchFilter result = new SwitchFilter(); 119 result.requestCases.addAll(asCases("onRequest")); 120 result.responseCases.addAll(asCases("onResponse")); 121 return result; 122 } 123 124 private List<Case> asCases(String name) throws HeapException { 125 ArrayList<Case> result = new ArrayList<Case>(); 126 JsonValue cases = config.get(name).expect(List.class); 127 for (JsonValue value : cases) { 128 result.add(asCase(value.required().expect(Map.class))); 129 } 130 return result; 131 } 132 133 private Case asCase(JsonValue value) throws HeapException { 134 return new Case(asExpression(value.get("condition")), 135 heap.resolve(value.get("handler"), Handler.class)); 136 } 137 } 138}