All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.faces.application.applicationimpl.Events Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package com.sun.faces.application.applicationimpl;

import static com.sun.faces.util.Util.notNull;
import static jakarta.faces.application.ProjectStage.Development;
import static java.util.logging.Level.WARNING;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import com.sun.faces.application.applicationimpl.events.ComponentSystemEventHelper;
import com.sun.faces.application.applicationimpl.events.EventInfo;
import com.sun.faces.application.applicationimpl.events.ReentrantLisneterInvocationGuard;
import com.sun.faces.application.applicationimpl.events.SystemEventHelper;
import com.sun.faces.util.FacesLogger;

import jakarta.faces.application.ProjectStage;
import jakarta.faces.component.UIViewRoot;
import jakarta.faces.context.FacesContext;
import jakarta.faces.event.AbortProcessingException;
import jakarta.faces.event.ExceptionQueuedEvent;
import jakarta.faces.event.ExceptionQueuedEventContext;
import jakarta.faces.event.SystemEvent;
import jakarta.faces.event.SystemEventListener;
import jakarta.faces.event.SystemEventListenerHolder;

public class Events {

    private static final Logger LOGGER = FacesLogger.APPLICATION.getLogger();

    private static final String CONTEXT = "context";
    private static final String LISTENER = "listener";
    private static final String SOURCE = "source";
    private static final String SYSTEM_EVENT_CLASS = "systemEventClass";

    private final SystemEventHelper systemEventHelper = new SystemEventHelper();
    private final ComponentSystemEventHelper compSysEventHelper = new ComponentSystemEventHelper();

    /*
     * This class encapsulates the behavior to prevent infinite loops when the publishing of one event leads to the queueing
     * of another event of the same type. Special provision is made to allow the case where this guaring mechanims happens
     * on a per-FacesContext, per-SystemEvent.class type basis.
     */

    private final ReentrantLisneterInvocationGuard listenerInvocationGuard = new ReentrantLisneterInvocationGuard();

    /*
     * @see jakarta.faces.application.Application#publishEvent(FacesContext, Class, Class, Object)
     */
    public void publishEvent(FacesContext context, Class systemEventClass, Class sourceBaseType, Object source,
            ProjectStage projectStage) {

        notNull(CONTEXT, context);
        notNull(SYSTEM_EVENT_CLASS, systemEventClass);
        notNull(SOURCE, source);

        if (!needsProcessing(context, systemEventClass)) {
            return;
        }

        // Source is not compatible with the provided base type.
        // Log a warning that the types are incompatible and return.
        if (projectStage == Development && sourceBaseType != null && !sourceBaseType.isInstance(source)) {
            if (LOGGER.isLoggable(WARNING)) {
                LOGGER.log(WARNING, "faces.application.publish.event.base_type_mismatch", new Object[] { source.getClass().getName(), sourceBaseType.getName() });
            }
            return;
        }

        try {
            // The side-effect of calling invokeListenersFor
            // will create a SystemEvent object appropriate to event/source
            // combination. This event will be passed on subsequent invocations
            // of invokeListenersFor

            // Look for and invoke any listeners stored on the source instance.
            SystemEvent event = invokeComponentListenersFor(systemEventClass, source);

            // Look for and invoke any 'view' listeners
            event = invokeViewListenersFor(context, systemEventClass, event, source);

            // Look for and invoke any listeners stored on the application using source type.
            event = invokeListenersFor(systemEventClass, event, source, sourceBaseType, true);

            // Look for and invoke any listeners not specific to the source class
            invokeListenersFor(systemEventClass, event, source, null, false);
        } catch (AbortProcessingException ape) {
            context.getApplication().publishEvent(context, ExceptionQueuedEvent.class, new ExceptionQueuedEventContext(context, ape));
        }
    }

    /*
     * @see Application#subscribeToEvent(Class, jakarta.faces.event.SystemEventListener)
     */
    public void subscribeToEvent(Class systemEventClass, SystemEventListener listener) {
        subscribeToEvent(systemEventClass, null, listener);
    }

    /*
     * @see Application#subscribeToEvent(Class, Class, jakarta.faces.event.SystemEventListener)
     */
    public void subscribeToEvent(Class systemEventClass, Class sourceClass, SystemEventListener listener) {

        notNull(SYSTEM_EVENT_CLASS, systemEventClass);
        notNull(LISTENER, listener);

        getListeners(systemEventClass, sourceClass).add(listener);
    }

    /*
     * @see Application#unsubscribeFromEvent(Class, Class, jakarta.faces.event.SystemEventListener)
     */
    public void unsubscribeFromEvent(Class systemEventClass, Class sourceClass, SystemEventListener listener) {

        notNull(SYSTEM_EVENT_CLASS, systemEventClass);
        notNull(LISTENER, listener);

        Set listeners = getListeners(systemEventClass, sourceClass);
        if (listeners != null) {
            listeners.remove(listener);
        }
    }

    /**
     * @return the SystemEventListeners that should be used for the provided combination of SystemEvent and source.
     */
    private Set getListeners(Class systemEvent, Class sourceClass) {

        Set listeners = null;
        EventInfo sourceInfo = systemEventHelper.getEventInfo(systemEvent, sourceClass);
        if (sourceInfo != null) {
            listeners = sourceInfo.getListeners();
        }

        return listeners;

    }

    private boolean needsProcessing(FacesContext context, Class systemEventClass) {
        return context.isProcessingEvents() || ExceptionQueuedEvent.class.isAssignableFrom(systemEventClass);
    }

    /**
     * @return process any listeners for the specified SystemEventListenerHolder and return any SystemEvent that may have
     * been created as a side-effect of processing the listeners.
     */
    private SystemEvent invokeComponentListenersFor(Class systemEventClass, Object source) {

        if (source instanceof SystemEventListenerHolder) {

            List listeners = ((SystemEventListenerHolder) source).getListenersForEventClass(systemEventClass);
            if (null == listeners) {
                return null;
            }
            EventInfo eventInfo = compSysEventHelper.getEventInfo(systemEventClass, source.getClass());
            return processListeners(listeners, null, source, eventInfo);
        }
        return null;

    }

    private SystemEvent invokeViewListenersFor(FacesContext ctx, Class systemEventClass, SystemEvent event, Object source) {
        SystemEvent result = event;

        if (listenerInvocationGuard.isGuardSet(ctx, systemEventClass)) {
            return result;
        }
        listenerInvocationGuard.setGuard(ctx, systemEventClass);

        UIViewRoot root = ctx.getViewRoot();
        try {
            if (root != null) {
                List listeners = root.getViewListenersForEventClass(systemEventClass);
                if (null == listeners) {
                    return null;
                }

                EventInfo rootEventInfo = systemEventHelper.getEventInfo(systemEventClass, UIViewRoot.class);
                // process view listeners
                result = processListenersAccountingForAdds(listeners, event, source, rootEventInfo);
            }
        } finally {
            listenerInvocationGuard.clearGuard(ctx, systemEventClass);
        }
        return result;

    }

    /**
     * Traverse the List of listeners and invoke any that are relevent for the specified source.
     *
     * @throws jakarta.faces.event.AbortProcessingException propagated from the listener invocation
     */
    private SystemEvent invokeListenersFor(Class systemEventClass, SystemEvent event, Object source, Class sourceBaseType,
            boolean useSourceLookup) throws AbortProcessingException {

        EventInfo eventInfo = systemEventHelper.getEventInfo(systemEventClass, source, sourceBaseType, useSourceLookup);
        if (eventInfo != null) {
            Set listeners = eventInfo.getListeners();
            event = processListeners(listeners, event, source, eventInfo);
        }

        return event;
    }

    /**
     * Iterate through and invoke the listeners. If the passed event was null, create the event, and return it.
     */
    private SystemEvent processListeners(Collection listeners, SystemEvent event, Object source, EventInfo eventInfo) {

        if (listeners != null && !listeners.isEmpty()) {
            ArrayList list = new ArrayList<>(listeners);

            for (SystemEventListener curListener : list) {
                if (curListener != null && curListener.isListenerForSource(source)) {
                    if (event == null) {
                        event = eventInfo.createSystemEvent(source);
                    }
                    assert event != null;
                    if (event.isAppropriateListener(curListener)) {
                        event.processListener(curListener);
                    }
                }
            }
        }

        return event;

    }

    private SystemEvent processListenersAccountingForAdds(List listeners, SystemEvent event, Object source, EventInfo eventInfo) {

        if (listeners != null && !listeners.isEmpty()) {

            // copy listeners
            // go thru copy completely
            // compare copy to original
            // if original differs from copy, make a new copy.
            // The new copy consists of the original list - processed

            SystemEventListener[] listenersCopy = new SystemEventListener[listeners.size()];
            int i = 0;
            for (i = 0; i < listenersCopy.length; i++) {
                listenersCopy[i] = listeners.get(i);
            }

            Map processedListeners = new HashMap<>(listeners.size());
            boolean processedSomeEvents = false, originalDiffersFromCopy = false;

            do {
                i = 0;
                originalDiffersFromCopy = false;
                if (0 < listenersCopy.length) {
                    for (i = 0; i < listenersCopy.length; i++) {
                        SystemEventListener curListener = listenersCopy[i];
                        if (curListener != null && curListener.isListenerForSource(source)) {
                            if (event == null) {
                                event = eventInfo.createSystemEvent(source);
                            }
                            assert event != null;
                            if (!processedListeners.containsKey(curListener) && event.isAppropriateListener(curListener)) {
                                processedSomeEvents = true;
                                event.processListener(curListener);
                                processedListeners.put(curListener, Boolean.TRUE);
                            }
                        }
                    }
                    if (originalDiffersFromCopy(listeners, listenersCopy)) {
                        originalDiffersFromCopy = true;
                        listenersCopy = copyListWithExclusions(listeners, processedListeners);
                    }
                }
            } while (originalDiffersFromCopy && processedSomeEvents);
        }

        return event;

    }

    private boolean originalDiffersFromCopy(Collection original, SystemEventListener[] copy) {
        boolean foundDifference = false;
        int i = 0, originalLen = original.size(), copyLen = copy.length;

        if (originalLen == copyLen) {
            SystemEventListener originalItem, copyItem;
            Iterator iter = original.iterator();
            while (iter.hasNext() && !foundDifference) {
                originalItem = iter.next();
                copyItem = copy[i++];
                foundDifference = originalItem != copyItem;
            }
        } else {
            foundDifference = true;
        }

        return foundDifference;
    }

    private SystemEventListener[] copyListWithExclusions(Collection original, Map excludes) {
        SystemEventListener[] result = null, temp = new SystemEventListener[original.size()];
        int i = 0;
        for (SystemEventListener cur : original) {
            if (!excludes.containsKey(cur)) {
                temp[i++] = cur;
            }
        }
        result = new SystemEventListener[i];
        System.arraycopy(temp, 0, result, 0, i);

        return result;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy