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.*; 021 022import java.io.IOException; 023import java.net.URI; 024import java.util.ArrayList; 025import java.util.List; 026import java.util.Map; 027 028import org.forgerock.json.fluent.JsonValue; 029import org.forgerock.openig.el.Expression; 030import org.forgerock.openig.heap.GenericHeaplet; 031import org.forgerock.openig.heap.HeapException; 032import org.forgerock.openig.http.Exchange; 033 034/** 035 * Dispatches to one of a list of handlers. When an exchange is handled, each handler's 036 * condition is evaluated. If a condition expression yields {@code true}, then the exchange 037 * is dispatched to the associated handler with no further processing. 038 * <p> 039 * If no condition yields {@code true} then the handler will throw a {@link HandlerException}. 040 * Therefore, it's advisable to have a single "default" handler at the end of the list 041 * with no condition (unconditional) to handle otherwise un-dispatched requests. 042 */ 043public class DispatchHandler extends GenericHandler { 044 045 /** Expressions to evaluate against exchange, bound to handlers to dispatch to. */ 046 private final List<Binding> bindings = new ArrayList<Binding>(); 047 048 /** 049 * Binds an expression to the current handler to dispatch to. 050 * 051 * @param condition 052 * Condition to evaluate to determine if associated handler should be dispatched to. If omitted, then 053 * dispatch is unconditional. 054 * @param handler 055 * The name of the handler heap object to dispatch to if the associated condition yields true. 056 * @param baseURI 057 * Overrides the existing request URI, making requests relative to a new base URI. Only scheme, host and 058 * port are used in the supplied URI. Default: leave URI untouched. 059 * @return The current dispatch handler. 060 */ 061 public DispatchHandler addBinding(Expression condition, Handler handler, URI baseURI) { 062 bindings.add(new Binding(condition, handler, baseURI)); 063 return this; 064 } 065 066 /** 067 * Adds an unconditional bindings to the handler. 068 * 069 * @param handler 070 * The name of the handler heap object to dispatch to if the associated condition yields true. 071 * @param baseURI 072 * Overrides the existing request URI, making requests relative to a new base URI. Only scheme, host and 073 * port are used in the supplied URI. Default: leave URI untouched. 074 * @return The current dispatch handler. 075 */ 076 public DispatchHandler addUnconditionalBinding(Handler handler, URI baseURI) { 077 bindings.add(new Binding(null, handler, baseURI)); 078 return this; 079 } 080 081 @Override 082 public void handle(Exchange exchange) throws HandlerException, IOException { 083 for (Binding binding : bindings) { 084 if (binding.condition == null || Boolean.TRUE.equals(binding.condition.eval(exchange))) { 085 if (binding.baseURI != null) { 086 exchange.request.getUri().rebase(binding.baseURI); 087 } 088 binding.handler.handle(exchange); 089 return; 090 } 091 } 092 throw logger.debug(new HandlerException("no handler to dispatch to")); 093 } 094 095 /** Binds an expression with a handler to dispatch to. */ 096 private static class Binding { 097 098 /** Condition to dispatch to handler or {@code null} if unconditional. */ 099 private Expression condition; 100 101 /** Handler to dispatch to. */ 102 private Handler handler; 103 104 /** Overrides scheme/host/port of the request with a base URI. */ 105 private URI baseURI; 106 107 /** 108 * Constructor. 109 * 110 * @param condition 111 * Condition to evaluate to determine if associated handler should be dispatched to. If omitted, then 112 * dispatch is unconditional. 113 * @param handler 114 * The name of the handler heap object to dispatch to if the associated condition yields true. 115 * @param baseURI 116 * Overrides the existing request URI, making requests relative to a new base URI. Only scheme, host 117 * and port are used in the supplied URI. Default: leave URI untouched. 118 */ 119 public Binding(Expression condition, Handler handler, URI baseURI) { 120 super(); 121 this.condition = condition; 122 this.handler = handler; 123 this.baseURI = baseURI; 124 } 125 } 126 127 /** 128 * Creates and initializes a dispatch handler in a heap environment. 129 */ 130 public static class Heaplet extends GenericHeaplet { 131 @Override 132 public Object create() throws HeapException { 133 DispatchHandler dispatchHandler = new DispatchHandler(); 134 for (JsonValue jv : config.get("bindings").expect(List.class)) { 135 jv.required().expect(Map.class); 136 final Expression expression = asExpression(jv.get("condition")); 137 final Handler handler = heap.resolve(jv.get("handler"), Handler.class); 138 final URI uri = jv.get("baseURI").asURI(); 139 dispatchHandler.addBinding(expression, handler, uri); 140 } 141 return dispatchHandler; 142 } 143 } 144}