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.*;
021import static org.forgerock.util.Utils.*;
022
023import java.io.IOException;
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 * Processes an exchange through a sequence of handlers. This allows multi-request processing such as retrieving a form,
036 * extracting form content (e.g. nonce) and submitting in a subsequent request.
037 */
038public class SequenceHandler extends GenericHandler {
039
040    /** Handlers and associated sequence processing postconditions. */
041    private final List<Binding> bindings = new ArrayList<Binding>();
042
043    /**
044     * Binds sequenced handlers with sequence processing postconditions.
045     *
046     * @param handler
047     *            The name of the handler heap object to dispatch to if the associated condition yields true.
048     * @param postcondition
049     *            evaluated to determine if sequence continues (default: {@code null} a.k.a. unconditional)
050     * @return The current dispatch handler.
051     */
052    public SequenceHandler addBinding(final Handler handler, final Expression postcondition) {
053        bindings.add(new Binding(handler, postcondition));
054        return this;
055    }
056
057    /** Binds sequenced handlers with sequence processing postconditions. */
058    private static class Binding {
059
060        private final Handler handler;
061
062        private final Expression postcondition;
063
064        /**
065         * Default constructor.
066         *
067         * @param handler
068         *            Handler to dispatch exchange to.
069         * @param postcondition
070         *            Postcondition evaluated to determine if sequence continues (default: {@code null} a.k.a.
071         *            unconditional).
072         */
073        Binding(Handler handler, Expression postcondition) {
074            this.handler = handler;
075            this.postcondition = postcondition;
076        }
077    }
078
079    @Override
080    public void handle(Exchange exchange) throws HandlerException, IOException {
081        for (Binding binding : bindings) {
082            // avoid downstream filters/handlers inadvertently using response
083            closeSilently(exchange.response);
084            exchange.response = null;
085            binding.handler.handle(exchange);
086            if (binding.postcondition != null && !Boolean.TRUE.equals(binding.postcondition.eval(exchange))) {
087                break;
088            }
089        }
090    }
091
092    /** Creates and initializes a sequence handler in a heap environment. */
093    public static class Heaplet extends GenericHeaplet {
094        @Override
095        public Object create() throws HeapException {
096            final SequenceHandler sequenceHandler = new SequenceHandler();
097            for (final JsonValue jv : config.get("bindings").required().expect(List.class)) {
098                jv.required().expect(Map.class);
099                final Handler handler = heap.resolve(jv.get("handler"), Handler.class);
100                final Expression postcondition = asExpression(jv.get("postcondition"));
101                sequenceHandler.addBinding(handler, postcondition);
102            }
103            return sequenceHandler;
104        }
105    }
106}