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