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 java.io.IOException;
021import java.util.ArrayList;
022import java.util.List;
023
024import org.forgerock.json.fluent.JsonValue;
025import org.forgerock.openig.handler.GenericHandler;
026import org.forgerock.openig.handler.Handler;
027import org.forgerock.openig.handler.HandlerException;
028import org.forgerock.openig.heap.GenericHeaplet;
029import org.forgerock.openig.heap.HeapException;
030import org.forgerock.openig.http.Exchange;
031
032/**
033 * A chain of exchange zero or more filters and one handler. The chain is responsible for
034 * dispatching the exchange to each filter in the chain, and finally the handler.
035 * <p>
036 * When a chain dispatches an exchange to a filter, it creates a "subchain" (a subset of this
037 * chain, which contains the remaining downstream filters and handler), and passes it as a
038 * parameter to the filter. For this reason, a filter should make no assumptions or
039 * correlations using the chain it is supplied with when invoked.
040 * <p>
041 * A filter may elect to terminate dispatching of the exchange to the rest of the chain by not
042 * calling {@code chain.handle(exchange)} and generate its own response or dispatch to a
043 * completely different handler.
044 *
045 * @see Filter
046 */
047public class Chain extends GenericHandler {
048
049    /** A list of filters, in the order they are to be dispatched by the chain. */
050    private final List<Filter> filters = new ArrayList<Filter>();
051
052    /** The handler dispatch the exchange to; terminus of the chain. */
053    private final Handler handler;
054
055    /**
056     * Builds a chain of filters that will finally dispatch to the given handler.
057     * List of Filters is empty by default.
058     * @param handler terminus of the chain
059     */
060    public Chain(final Handler handler) {
061        this.handler = handler;
062    }
063
064    /**
065     * Returns the list of filters, in the order they are to be dispatched by the chain.
066     * @return the list of filters, in the order they are to be dispatched by the chain.
067     */
068    public List<Filter> getFilters() {
069        return filters;
070    }
071
072    @Override
073    public void handle(Exchange exchange) throws HandlerException, IOException {
074        new Handler() {
075            private int cursor = 0;
076
077            @Override
078            public void handle(Exchange exchange) throws HandlerException, IOException {
079                // save position to restore after the call
080                int saved = cursor;
081                try {
082                    if (cursor < filters.size()) {
083                        filters.get(cursor++).filter(exchange, this);
084                    } else {
085                        handler.handle(exchange);
086                    }
087                } finally {
088                    cursor = saved;
089                }
090            }
091        } .handle(exchange);
092    }
093
094    /** Creates and initializes a filter chain in a heap environment. */
095    public static class Heaplet extends GenericHeaplet {
096        @Override
097        public Object create() throws HeapException {
098            Chain chain = new Chain(heap.resolve(config.get("handler"), Handler.class));
099            for (JsonValue filter : config.get("filters").required().expect(List.class)) {
100                chain.filters.add(heap.resolve(filter, Filter.class));
101            }
102            return chain;
103        }
104    }
105}