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.util.ArrayDeque; 023import java.util.ArrayList; 024import java.util.Deque; 025import java.util.List; 026import java.util.Map; 027 028import org.forgerock.http.Handler; 029import org.forgerock.http.protocol.Request; 030import org.forgerock.http.protocol.Response; 031import org.forgerock.json.JsonValue; 032import org.forgerock.openig.el.Bindings; 033import org.forgerock.openig.el.Expression; 034import org.forgerock.openig.heap.GenericHeapObject; 035import org.forgerock.openig.heap.GenericHeaplet; 036import org.forgerock.openig.heap.HeapException; 037import org.forgerock.services.context.Context; 038import org.forgerock.util.promise.NeverThrowsException; 039import org.forgerock.util.promise.Promise; 040import org.forgerock.util.promise.PromiseImpl; 041import org.forgerock.util.promise.ResultHandler; 042 043/** 044 * Processes a request through a sequence of handlers. This allows multi-request processing such as retrieving a form, 045 * extracting form content (e.g. nonce) and submitting in a subsequent request. 046 */ 047public class SequenceHandler extends GenericHeapObject implements Handler { 048 049 /** Handlers and associated sequence processing postconditions. */ 050 private final List<Binding> bindings = new ArrayList<>(); 051 052 /** 053 * Binds sequenced handlers with sequence processing postconditions. 054 * 055 * @param handler 056 * The name of the handler heap object to dispatch to if the associated condition yields true. 057 * @param postcondition 058 * evaluated to determine if sequence continues (default: {@code null} a.k.a. unconditional) 059 * @return The current dispatch handler. 060 */ 061 public SequenceHandler addBinding(final Handler handler, final Expression<Boolean> postcondition) { 062 bindings.add(new Binding(handler, postcondition)); 063 return this; 064 } 065 066 @Override 067 public Promise<Response, NeverThrowsException> handle(final Context context, final Request request) { 068 069 final PromiseImpl<Response, NeverThrowsException> composite = PromiseImpl.create(); 070 071 final Deque<Binding> theBindings = new ArrayDeque<>(bindings); 072 073 Binding binding = theBindings.peekFirst(); 074 Promise<Response, NeverThrowsException> promise = binding.handler.handle(context, request); 075 promise.thenOnResult(new ResultHandler<Response>() { 076 077 @Override 078 public void handleResult(final Response result) { 079 Binding binding = theBindings.removeFirst(); 080 Bindings scope = Bindings.bindings(context, request, result); 081 if ((binding.postcondition != null && !Boolean.TRUE.equals(binding.postcondition.eval(scope))) 082 || theBindings.isEmpty()) { 083 // Do not continue 084 composite.handleResult(result); 085 } else { 086 // Next promise 087 final Binding next = theBindings.peekFirst(); 088 next.handler.handle(context, request) 089 .thenOnResult(this); 090 091 } 092 } 093 }); 094 095 return composite; 096 } 097 098 /** Binds sequenced handlers with sequence processing postconditions. */ 099 private static class Binding { 100 101 private final Handler handler; 102 103 private final Expression<Boolean> postcondition; 104 105 /** 106 * Default constructor. 107 * 108 * @param handler 109 * Handler to dispatch request to. 110 * @param postcondition 111 * Postcondition evaluated to determine if sequence continues (default: {@code null} a.k.a. 112 * unconditional). 113 */ 114 Binding(Handler handler, Expression<Boolean> postcondition) { 115 this.handler = handler; 116 this.postcondition = postcondition; 117 } 118 } 119 120 /** Creates and initializes a sequence handler in a heap environment. */ 121 public static class Heaplet extends GenericHeaplet { 122 @Override 123 public Object create() throws HeapException { 124 final SequenceHandler sequenceHandler = new SequenceHandler(); 125 for (final JsonValue jv : config.get("bindings").required().expect(List.class)) { 126 jv.required().expect(Map.class); 127 final Handler handler = heap.resolve(jv.get("handler"), Handler.class); 128 final Expression<Boolean> postcondition = asExpression(jv.get("postcondition"), Boolean.class); 129 sequenceHandler.addBinding(handler, postcondition); 130 } 131 return sequenceHandler; 132 } 133 } 134}