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 2014 ForgeRock AS.
015 */
016
017package org.forgerock.openig.audit;
018
019import static java.lang.Boolean.*;
020import static org.forgerock.openig.audit.AuditSystem.*;
021
022import org.forgerock.openig.el.Expression;
023import org.forgerock.openig.heap.GenericHeaplet;
024import org.forgerock.openig.heap.HeapException;
025import org.forgerock.openig.heap.Heaplet;
026import org.forgerock.openig.util.Json;
027
028/**
029 * A ConditionalAuditEventListener is conditionally invoked {@link AuditEventListener}.
030 * <p>
031 * It delegates to a given {@link AuditEventListener} if the configured condition evaluates to {@code true}.
032 * If the condition evaluates to anything else ({@code false}, {@code null}, ...), the result is considered
033 * as a {@code false}, and the delegate listener will not be invoked.
034 * <p>
035 * This class is not intended to be sub-classed, although its associated {@link Heaplet} is.
036 */
037public class ConditionalAuditEventListener implements AuditEventListener {
038
039    private final AuditEventListener delegate;
040    private final Expression condition;
041
042    /**
043     * Builds a new ConditionalAuditEventListener that will delegates to the given {@code delegate} under the given
044     * {@code condition}.
045     *
046     * @param delegate
047     *         conditionally invoked listener
048     * @param condition
049     *         condition to evaluate
050     */
051    public ConditionalAuditEventListener(final AuditEventListener delegate, final Expression condition) {
052        this.delegate = delegate;
053        this.condition = condition;
054    }
055
056    @Override
057    public void onAuditEvent(final AuditEvent event) {
058        // Only process selected events
059        if (TRUE.equals(condition.eval(event, Boolean.class))) {
060            delegate.onAuditEvent(event);
061        }
062    }
063
064    /**
065     * Creates and initializes a ConditionalListenerHeaplet in a heap environment.
066     * <p>
067     * Here is an example of an extending heap object declaration:
068     * <pre>
069     *     {@code
070     *     {
071     *         "name": "...",
072     *         "type": "MySubTypeExtendingConditionalListener",
073     *         "config": {
074     *             "condition": "${contains(tags, 'marker')}",
075     *             "any other": "configuration attributes"
076     *         }
077     *     }
078     *     }
079     * </pre>
080     * The {@literal condition} property declares the condition that needs to be evaluated to {@code true} in order
081     * to forward the event notification to the real listener. It defaults to {@code ${true}} (will always invoke the
082     * delegate).
083     */
084    public static abstract class ConditionalListenerHeaplet extends GenericHeaplet {
085
086        private AuditSystem auditSystem;
087        private ConditionalAuditEventListener conditional;
088
089        @Override
090        public Object create() throws HeapException {
091            Expression condition = Json.asExpression(config.get("condition").defaultTo("${true}"));
092            auditSystem = heap.get(AUDIT_SYSTEM_HEAP_KEY, AuditSystem.class);
093            AuditEventListener listener = createListener();
094            conditional = new ConditionalAuditEventListener(listener, condition);
095            auditSystem.registerListener(conditional);
096            return listener;
097        }
098
099        /**
100         * Creates a new {@link AuditEventListener} that will be invoked if condition yields.
101         *
102         * @return a new {@link AuditEventListener} that will be invoked if condition yields.
103         * @throws HeapException
104         *             if an exception occurred during creation of the heap object or any of its dependencies.
105         * @throws org.forgerock.json.fluent.JsonValueException
106         *             if the heaplet (or one of its dependencies) has a malformed configuration.
107         */
108        protected abstract AuditEventListener createListener() throws HeapException;
109
110        @Override
111        public void destroy() {
112            super.destroy();
113            auditSystem.unregisterListener(conditional);
114        }
115    }
116}