/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2014 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.sun.faces.mock; import java.lang.reflect.Constructor; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.logging.Level; import java.util.logging.Logger; import javax.el.ELContextListener; import javax.el.ELException; import javax.el.ELResolver; import javax.el.ExpressionFactory; import javax.el.ValueExpression; import javax.faces.FacesException; import javax.faces.application.*; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import javax.faces.el.MethodBinding; import javax.faces.el.PropertyResolver; import javax.faces.el.ValueBinding; import javax.faces.el.VariableResolver; import javax.faces.event.AbortProcessingException; import javax.faces.event.ActionEvent; import javax.faces.event.ActionListener; import javax.faces.event.SystemEvent; import javax.faces.event.SystemEventListener; import javax.faces.event.SystemEventListenerHolder; import javax.faces.validator.Validator; import javax.servlet.ServletContext; import com.sun.el.ExpressionFactoryImpl; import javax.faces.component.search.SearchExpressionHandler; import javax.faces.component.search.SearchKeywordResolver; public class MockApplication extends Application { private static final Logger LOGGER = Logger.getLogger("MockApplication"); private final SystemEventHelper systemEventHelper = new SystemEventHelper(); private final ComponentSystemEventHelper compSysEventHelper = new ComponentSystemEventHelper(); public MockApplication() { addComponent("TestNamingContainer", "javax.faces.webapp.TestNamingContainer"); addComponent("TestComponent", "javax.faces.webapp.TestComponent"); addComponent("TestInput", "javax.faces.component.UIInput"); addComponent("TestOutput", "javax.faces.component.UIOutput"); addConverter("Integer", "javax.faces.convert.IntegerConverter"); addConverter("javax.faces.Number", "javax.faces.convert.NumberConverter"); addConverter("javax.faces.Long", "javax.faces.convert.LongConverter"); addValidator("Length", "javax.faces.validator.LengthValidator"); servletContext = new MockServletContext(); } private ServletContext servletContext = null; private ActionListener actionListener = null; private static boolean processActionCalled = false; public ActionListener getActionListener() { if (null == actionListener) { actionListener = new ActionListener() { public void processAction(ActionEvent e) { processActionCalled = true; } // see if the other object is the same as our // anonymous inner class implementation. public boolean equals(Object otherObj) { if (!(otherObj instanceof ActionListener)) { return false; } ActionListener other = (ActionListener) otherObj; processActionCalled = false; other.processAction(null); boolean result = processActionCalled; processActionCalled = false; return result; } }; } return (this.actionListener); } public void setActionListener(ActionListener actionListener) { this.actionListener = actionListener; } private NavigationHandler navigationHandler = null; public NavigationHandler getNavigationHandler() { return (this.navigationHandler); } public void setNavigationHandler(NavigationHandler navigationHandler) { this.navigationHandler = navigationHandler; } private ResourceHandler resourceHandler = new MockResourceHandler(); @Override public ResourceHandler getResourceHandler() { return resourceHandler; } @Override public void setResourceHandler(ResourceHandler resourceHandler) { this.resourceHandler = resourceHandler; } private PropertyResolver propertyResolver = null; public PropertyResolver getPropertyResolver() { if (propertyResolver == null) { propertyResolver = new MockPropertyResolver(); } return (this.propertyResolver); } public void setPropertyResolver(PropertyResolver propertyResolver) { this.propertyResolver = propertyResolver; } public MethodBinding createMethodBinding(String ref, Class params[]) { if (ref == null) { throw new NullPointerException(); } else { return (new MockMethodBinding(this, ref, params)); } } public ValueBinding createValueBinding(String ref) { if (ref == null) { throw new NullPointerException(); } else { return (new MockValueBinding(this, ref)); } } // PENDING(edburns): implement public void addELResolver(ELResolver resolver) { } // PENDING(edburns): implement public ELResolver getELResolver() { return null; } private ExpressionFactory expressionFactory = null; public ExpressionFactory getExpressionFactory() { if (null == expressionFactory) { expressionFactory = new ExpressionFactoryImpl(); } return expressionFactory; } public Object evaluateExpressionGet(FacesContext context, String expression, Class expectedType) throws ELException { ValueExpression ve = getExpressionFactory().createValueExpression(context.getELContext(), expression, expectedType); return ve.getValue(context.getELContext()); } private VariableResolver variableResolver = null; public VariableResolver getVariableResolver() { if (variableResolver == null) { variableResolver = new MockVariableResolver(); } return (this.variableResolver); } public void setVariableResolver(VariableResolver variableResolver) { this.variableResolver = variableResolver; } private ViewHandler viewHandler = null; public ViewHandler getViewHandler() { if (null == viewHandler) { viewHandler = new MockViewHandler(); } return (this.viewHandler); } public void setViewHandler(ViewHandler viewHandler) { this.viewHandler = viewHandler; } private StateManager stateManager = null; public StateManager getStateManager() { if (null == stateManager) { stateManager = new MockStateManager(); } return (this.stateManager); } public void setStateManager(StateManager stateManager) { this.stateManager = stateManager; } private Map components = new HashMap(); public void addComponent(String componentType, String componentClass) { components.put(componentType, componentClass); } public UIComponent createComponent(String componentType) { String componentClass = (String) components.get(componentType); try { Class clazz = Class.forName(componentClass); return ((UIComponent) clazz.newInstance()); } catch (Exception e) { throw new FacesException(e); } } public UIComponent createComponent(ValueBinding componentBinding, FacesContext context, String componentType) throws FacesException { throw new FacesException(new UnsupportedOperationException()); } public UIComponent createComponent(ValueExpression componentExpression, FacesContext context, String componentType) throws FacesException { throw new FacesException(new UnsupportedOperationException()); } public Iterator getComponentTypes() { return (components.keySet().iterator()); } private Map converters = new HashMap(); public void addConverter(String converterId, String converterClass) { converters.put(converterId, converterClass); } public void addConverter(Class targetClass, String converterClass) { throw new UnsupportedOperationException(); } public Converter createConverter(String converterId) { String converterClass = (String) converters.get(converterId); try { Class clazz = Class.forName(converterClass); return ((Converter) clazz.newInstance()); } catch (Exception e) { throw new FacesException(e); } } public Converter createConverter(Class targetClass) { throw new UnsupportedOperationException(); } public Iterator getConverterIds() { return (converters.keySet().iterator()); } public Iterator getConverterTypes() { throw new UnsupportedOperationException(); } private String messageBundle = null; public void setMessageBundle(String messageBundle) { this.messageBundle = messageBundle; } public String getMessageBundle() { return messageBundle; } private Map validators = new HashMap(); public void addValidator(String validatorId, String validatorClass) { validators.put(validatorId, validatorClass); } public Validator createValidator(String validatorId) { String validatorClass = (String) validators.get(validatorId); try { Class clazz = Class.forName(validatorClass); return ((Validator) clazz.newInstance()); } catch (Exception e) { throw new FacesException(e); } } public Iterator getValidatorIds() { return (validators.keySet().iterator()); } public Iterator getSupportedLocales() { return Collections.EMPTY_LIST.iterator(); } public void setSupportedLocales(Collection newLocales) { } public void addELContextListener(ELContextListener listener) { // PENDING(edburns): maybe implement } public void removeELContextListener(ELContextListener listener) { // PENDING(edburns): maybe implement } public ELContextListener[] getELContextListeners() { // PENDING(edburns): maybe implement return (ELContextListener[]) java.lang.reflect.Array.newInstance(ELContextListener.class, 0); } public Locale getDefaultLocale() { return Locale.getDefault(); } public void setDefaultLocale(Locale newLocale) { } public String getDefaultRenderKitId() { return null; } public void setDefaultRenderKitId(String renderKitId) { } public ResourceBundle getResourceBundle(FacesContext ctx, String name) { return null; } /** * <p class="changed_added_2_0">If there are one or more listeners for * events of the type represented by <code>systemEventClass</code>, call * those listeners, passing <code>source</code> as the source of the event. * The implementation should be as fast as possible in determining whether * or not a listener for the given <code>systemEventClass</code> and * <code>source</code> has been installed, and should return immediately * once such a determination has been made. The implementation of * <code>publishEvent</code> must honor the requirements stated in * {@link #subscribeToEvent} regarding the storage and retrieval of listener * instances.</p> * * <div class="changed_added_2_0"> * * <p> * The default implementation must implement an algorithm semantically * equivalent to the following to locate listener instances and to invoke * them.</p> * * <ul> * * <li><p> * If the <code>source</code> argument implements {@link * javax.faces.event.SystemEventListenerHolder}, call {@link * javax.faces.event.SystemEventListenerHolder#getListenersForEventClass} on * it, passing the <code>systemEventClass</code> argument. If the list is * not empty, perform algorithm * <em>traverseListenerList</em> on the list.</p></li> * * <li><p> * If any <code>Application</code> level listeners have been installed by * previous calls to {@link * #subscribeToEvent(Class, Class, * javax.faces.event.SystemEventListener)}, perform algorithm * <em>traverseListenerList</em> on the list.</p></li> * * <li><p> * If any <code>Application</code> level listeners have been installed by * previous calls to {@link * #subscribeToEvent(Class, javax.faces.event.SystemEventListener)}, perform * algorithm <em>traverseListenerList</em> on the list.</p></li> * * </ul> * * <p> * If the act of invoking the <code>processListener</code> method causes an * {@link javax.faces.event.AbortProcessingException} to be thrown, * processing of the listeners must be aborted.</p> * * RELEASE_PENDING (edburns,rogerk) it may be prudent to specify how the * abortprocessingexception should be handled. Logged or thrown? * * <p> * Algorithm <em>traverseListenerList</em>: For each listener in the * list,</p> * * <ul> * * <li><p> * Call {@link * javax.faces.event.SystemEventListener#isListenerForSource}, passing the * <code>source</code> argument. If this returns <code>false</code>, take no * action on the listener.</p></li> * * <li><p> * Otherwise, if the event to be passed to the listener instances has not * yet been constructed, construct the event, passing <code>source</code> as * the argument to the one-argument constructor that takes an * <code>Object</code>. This same event instance must be passed to all * listener instances.</p></li> * * <li><p> * Call {@link javax.faces.event.SystemEvent#isAppropriateListener}, passing * the listener instance as the argument. If this returns * <code>false</code>, take no action on the listener.</p></li> * * <li><p> * Call {@link javax.faces.event.SystemEvent#processListener}, passing the * listener instance. </p></li> * * </ul> * </div> * * @param systemEventClass The <code>Class</code> of event that is being * published. * @param source The source for the event of type * <code>systemEventClass</code>. * * @throws NullPointerException if either <code>systemEventClass</code> or * <code>source</code> is <code>null</code> * * @since 2.0 */ public void publishEvent(FacesContext context, Class<? extends SystemEvent> systemEventClass, Object source) { if (systemEventClass == null) { throw new NullPointerException("systemEventClass"); } if (source == null) { throw new NullPointerException("source"); } 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 SystemEvent event; // Look for and invoke any listeners stored on the source instance. event = invokeComponentListenersFor(systemEventClass, source); // look for and invoke any listeners stored on the application // using source type. event = invokeListenersFor(systemEventClass, event, source, true); // look for and invoke any listeners not specific to the source class invokeListenersFor(systemEventClass, event, source, false); } catch (AbortProcessingException ape) { if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, ape.getMessage(), ape); } } } /** * <p class="changed_added_2_0">Install the listener instance referenced by * argument <code>listener</code> into the application as a listener for * events of type <code>systemEventClass</code> that originate from objects * of type <code>sourceClass</code>.</p> * * <div class="changed_added_2_0"> * * <p> * If argument <code>sourceClass</code> is non-<code>null</code>, * <code>sourceClass</code> and <code>systemEventClass</code> must be used * to store the argument <code>listener</code> in the application in such a * way that the <code>listener</code> can be quickly looked up by the * implementation of * {@link javax.faces.application.Application#publishEvent} given * <code>systemEventClass</code> and an instance of the <code>Class</code> * referenced by <code>sourceClass</code>. If argument * <code>sourceClass</code> is <code>null</code>, the <code>listener</code> * must be discoverable by the implementation of * {@link javax.faces.application.Application#publishEvent} given only * <code>systemEventClass</code>. * </p> * * </div> * * @param systemEventClass the <code>Class</code> of event for which * <code>listener</code> must be fired. * * @param sourceClass the <code>Class</code> of the instance which causes * events of type <code>systemEventClass</code> to be fired. May be * <code>null</code>. * * @param listener the implementation of {@link * javax.faces.event.SystemEventListener} whose {@link * javax.faces.event.SystemEventListener#processEvent} method must be called * when events of type <code>systemEventClass</code> are fired. * * @throws <code>NullPointerException</code> if any combination of * <code>systemEventClass</code>, or <code>listener</code> are * <code>null</code>. * * @since 2.0 */ public void subscribeToEvent(Class<? extends SystemEvent> systemEventClass, Class<?> sourceClass, SystemEventListener listener) { if (systemEventClass == null) { throw new NullPointerException("systemEventClass"); } if (listener == null) { throw new NullPointerException("listener"); } Set<SystemEventListener> listeners = getListeners(systemEventClass, sourceClass); listeners.add(listener); } /** * <p class="changed_added_2_0">Install the listener instance referenced by * argument <code>listener</code> into application as a listener for events * of type <code>systemEventClass</code>. The default implementation simply * calls through to * {@link #subscribeToEvent(Class, Class, javax.faces.event.SystemEventListener)} * passing <code>null</code> as the <code>sourceClass</code> argument</p> * * @param systemEventClass the <code>Class</code> of event for which * <code>listener</code> must be fired. * * @param listener the implementation of {@link * javax.faces.event.SystemEventListener} whose {@link * javax.faces.event.SystemEventListener#processEvent} method must be called * when events of type <code>systemEventClass</code> are fired. * * @throws <code>NullPointerException</code> if any combination of * <code>systemEventClass</code>, or <code>listener</code> are * <code>null</code>. * * @since 2.0 */ public void subscribeToEvent(Class<? extends SystemEvent> systemEventClass, SystemEventListener listener) { subscribeToEvent(systemEventClass, null, listener); } /** * <p class="changed_added_2_0">Remove the listener instance referenced by * argument <code>listener</code> from the application as a listener for * events of type <code>systemEventClass</code> that originate from objects * of type <code>sourceClass</code>. See {@link * #subscribeToEvent(Class, Class, * javax.faces.event.SystemEventListener)} for the specification of how the * listener is stored, and therefore, how it must be removed.</p> * * @param systemEventClass the <code>Class</code> of event for which * <code>listener</code> must be fired. * * @param sourceClass the <code>Class</code> of the instance which causes * events of type <code>systemEventClass</code> to be fired. May be * <code>null</code>. * * @param listener the implementation of {@link * javax.faces.event.SystemEventListener} to remove from the internal data * structure. * * @throws <code>NullPointerException</code> if any combination of * <code>context</code>, <code>systemEventClass</code>, or * <code>listener</code> are <code>null</code>. */ public void unsubscribeFromEvent(Class<? extends SystemEvent> systemEventClass, Class<?> sourceClass, SystemEventListener listener) { if (systemEventClass == null) { throw new NullPointerException("systemEventClass"); } if (listener == null) { throw new NullPointerException("listener"); } Set<SystemEventListener> listeners = getListeners(systemEventClass, sourceClass); if (listeners != null) { listeners.remove(listener); } } /** * <p class="changed_added_2_0">Remove the listener instance referenced by * argument <code>listener</code> from the application as a listener for * events of type <code>systemEventClass</code>. The default implementation * simply calls through to * {@link #unsubscribeFromEvent(Class, javax.faces.event.SystemEventListener)} * passing <code>null</code> as the <code>sourceClass</code> argument</p> * * @param systemEventClass the <code>Class</code> of event for which * <code>listener</code> must be fired. * * @param listener the implementation of {@link * javax.faces.event.SystemEventListener} to remove from the internal data * structure. * * @throws <code>NullPointerException</code> if any combination of * <code>context</code>, <code>systemEventClass</code>, or * <code>listener</code> are <code>null</code>. */ public void unsubscribeFromEvent(Class<? extends SystemEvent> systemEventClass, SystemEventListener listener) { unsubscribeFromEvent(systemEventClass, null, listener); } /** * @return the SystemEventListeners that should be used for the provided * combination of SystemEvent and source. */ private Set<SystemEventListener> getListeners(Class<? extends SystemEvent> systemEvent, Class<?> sourceClass) { Set<SystemEventListener> listeners = null; EventInfo sourceInfo = systemEventHelper.getEventInfo(systemEvent, sourceClass); if (sourceInfo != null) { listeners = sourceInfo.getListeners(); } return listeners; } /** * @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<? extends SystemEvent> systemEventClass, Object source) { if (source instanceof SystemEventListenerHolder) { EventInfo eventInfo = compSysEventHelper.getEventInfo(systemEventClass, source.getClass()); return processListeners(((SystemEventListenerHolder) source).getListenersForEventClass(systemEventClass), null, source, eventInfo); } return null; } /** * Traverse the <code>List</code> of listeners and invoke any that are * relevent for the specified source. * * @throws javax.faces.event.AbortProcessingException propagated from the * listener invocation */ private SystemEvent invokeListenersFor(Class<? extends SystemEvent> systemEventClass, SystemEvent event, Object source, boolean useSourceLookup) throws AbortProcessingException { EventInfo eventInfo = systemEventHelper.getEventInfo(systemEventClass, source, useSourceLookup); if (eventInfo != null) { Set<SystemEventListener> listeners = eventInfo.getListeners(); event = processListeners(listeners, event, source, eventInfo); } return event; } /** * Iterate through and invoke the listeners. If the passed event was * <code>null</code>, create the event, and return it. */ private SystemEvent processListeners(Collection<SystemEventListener> listeners, SystemEvent event, Object source, EventInfo eventInfo) { if (listeners != null && !listeners.isEmpty()) { for (SystemEventListener curListener : listeners) { if (curListener.isListenerForSource(source)) { if (event == null) { event = eventInfo.createSystemEvent(source); } assert (event != null); if (event.isAppropriateListener(curListener)) { event.processListener(curListener); } } } } return event; } /** * Utility class for dealing with application events. */ private static class SystemEventHelper { private final Cache<Class<? extends SystemEvent>, SystemEventInfo> systemEventInfoCache; // -------------------------------------------------------- Constructors public SystemEventHelper() { systemEventInfoCache = new Cache<Class<? extends SystemEvent>, SystemEventInfo>( new Factory<Class<? extends SystemEvent>, SystemEventInfo>() { public SystemEventInfo newInstance(final Class<? extends SystemEvent> arg) throws InterruptedException { return new SystemEventInfo(arg); } } ); } // ------------------------------------------------------ Public Methods public EventInfo getEventInfo(Class<? extends SystemEvent> systemEventClass, Class<?> sourceClass) { EventInfo info = null; SystemEventInfo systemEventInfo = systemEventInfoCache.get(systemEventClass); if (systemEventInfo != null) { info = systemEventInfo.getEventInfo(sourceClass); } return info; } public EventInfo getEventInfo(Class<? extends SystemEvent> systemEventClass, Object source, boolean useSourceForLookup) { Class<?> sourceClass = ((useSourceForLookup) ? source.getClass() : Void.class); return getEventInfo(systemEventClass, sourceClass); } } // END SystemEventHelper /** * Utility class for dealing with {@link javax.faces.component.UIComponent} * events. */ private static class ComponentSystemEventHelper { private Cache<Class<?>, Cache<Class<? extends SystemEvent>, EventInfo>> sourceCache; // -------------------------------------------------------- Constructors public ComponentSystemEventHelper() { // Initialize the 'sources' cache for, ahem, readability... // ~generics++ Factory<Class<?>, Cache<Class<? extends SystemEvent>, EventInfo>> eventCacheFactory = new Factory<Class<?>, Cache<Class<? extends SystemEvent>, EventInfo>>() { public Cache<Class<? extends SystemEvent>, EventInfo> newInstance( final Class<?> sourceClass) throws InterruptedException { Factory<Class<? extends SystemEvent>, EventInfo> eventInfoFactory = new Factory<Class<? extends SystemEvent>, EventInfo>() { public EventInfo newInstance(final Class<? extends SystemEvent> systemEventClass) throws InterruptedException { return new EventInfo(systemEventClass, sourceClass); } }; return new Cache<Class<? extends SystemEvent>, EventInfo>(eventInfoFactory); } }; sourceCache = new Cache<Class<?>, Cache<Class<? extends SystemEvent>, EventInfo>>(eventCacheFactory); } // ------------------------------------------------------ Public Methods public EventInfo getEventInfo(Class<? extends SystemEvent> systemEvent, Class<?> sourceClass) { Cache<Class<? extends SystemEvent>, EventInfo> eventsCache = sourceCache.get(sourceClass); return eventsCache.get(systemEvent); } } // END ComponentSystemEventHelper /** * Simple wrapper class for application level SystemEvents. It provides the * structure to map a single SystemEvent to multiple sources which are * represented by <code>SourceInfo</code> instances. */ private static class SystemEventInfo { private Cache<Class<?>, EventInfo> cache = new Cache<Class<?>, EventInfo>( new Factory<Class<?>, EventInfo>() { public EventInfo newInstance(Class<?> arg) throws InterruptedException { return new EventInfo(systemEvent, arg); } } ); private Class<? extends SystemEvent> systemEvent; // -------------------------------------------------------- Constructors private SystemEventInfo(Class<? extends SystemEvent> systemEvent) { this.systemEvent = systemEvent; } // ------------------------------------------------------ Public Methods public EventInfo getEventInfo(Class<?> source) { Class<?> sourceClass = ((source == null) ? Void.class : source); return cache.get(sourceClass); } } // END SystemEventInfo /** * Represent a logical association between a SystemEvent and a Source. This * call will contain the Listeners specific to this association as well as * provide a method to construct new SystemEvents as required. */ private static class EventInfo { private Class<? extends SystemEvent> systemEvent; private Class<?> sourceClass; private Set<SystemEventListener> listeners; private Constructor eventConstructor; private Map<Class<?>, Constructor> constructorMap; // -------------------------------------------------------- Constructors public EventInfo(Class<? extends SystemEvent> systemEvent, Class<?> sourceClass) { this.systemEvent = systemEvent; this.sourceClass = sourceClass; this.listeners = new CopyOnWriteArraySet<SystemEventListener>(); this.constructorMap = new HashMap<Class<?>, Constructor>(); if (!sourceClass.equals(Void.class)) { eventConstructor = getEventConstructor(sourceClass); } } // ------------------------------------------------------ Public Methods public Set<SystemEventListener> getListeners() { return listeners; } public SystemEvent createSystemEvent(Object source) { Constructor toInvoke = getCachedConstructor(source.getClass()); if (toInvoke != null) { try { return (SystemEvent) toInvoke.newInstance(source); } catch (Exception e) { throw new FacesException(e); } } return null; } // ----------------------------------------------------- Private Methods private Constructor getCachedConstructor(Class<?> source) { if (eventConstructor != null) { return eventConstructor; } else { Constructor c = constructorMap.get(source); if (c == null) { c = getEventConstructor(source); if (c != null) { constructorMap.put(source, c); } } return c; } } private Constructor getEventConstructor(Class<?> source) { Constructor ctor = null; try { return systemEvent.getDeclaredConstructor(source); } catch (NoSuchMethodException ignored) { Constructor[] ctors = systemEvent.getConstructors(); if (ctors != null) { for (Constructor c : ctors) { Class<?>[] params = c.getParameterTypes(); if (params.length != 1) { continue; } if (params[0].isAssignableFrom(source)) { return c; } } } if (eventConstructor == null) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Unable to find Constructor within {0} that accepts {1} instances.", new Object[]{systemEvent.getName(), sourceClass.getName()}); } } } return ctor; } } // END SourceInfo /** * Factory interface for creating various cacheable objects. */ private interface Factory<K, V> { V newInstance(final K arg) throws InterruptedException; } // END Factory /** * A concurrent caching mechanism. */ private static final class Cache<K, V> { private ConcurrentMap<K, Future<V>> cache = new ConcurrentHashMap<K, Future<V>>(); private Factory<K, V> factory; // -------------------------------------------------------- Constructors /** * Constructs this cache using the specified <code>Factory</code>. * * @param factory */ public Cache(Factory<K, V> factory) { this.factory = factory; } // ------------------------------------------------------ Public Methods /** * If a value isn't associated with the specified key, a new * {@link java.util.concurrent.Callable} will be created wrapping the * <code>Factory</code> specified via the constructor and passed to a * {@link java.util.concurrent.FutureTask}. This task will be passed to * the backing ConcurrentMap. When * {@link java.util.concurrent.FutureTask#get()} is invoked, the Factory * will return the new Value which will be cached by the * {@link java.util.concurrent.FutureTask}. * * @param key the key the value is associated with * @return the value for the specified key, if any */ public V get(final K key) { while (true) { Future<V> f = cache.get(key); if (f == null) { Callable<V> callable = new Callable<V>() { public V call() throws Exception { return factory.newInstance(key); } }; FutureTask<V> ft = new FutureTask<V>(callable); // here is the real beauty of the concurrent utilities. // 1. putIfAbsent() is atomic // 2. putIfAbsent() will return the value already associated // with the specified key // So, if multiple threads make it to this point // they will all be calling f.get() on the same // FutureTask instance, so this guarantees that the instances // that the invoked Callable will return will be created once f = cache.putIfAbsent(key, ft); if (f == null) { f = ft; ft.run(); } } try { return f.get(); } catch (CancellationException ce) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.log(Level.FINEST, ce.toString(), ce); } cache.remove(key); } catch (InterruptedException ie) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.log(Level.FINEST, ie.toString(), ie); } cache.remove(key); } catch (ExecutionException ee) { throw new FacesException(ee); } } } } // END Cache private SearchExpressionHandler searchExpressionHandler; private SearchKeywordResolver searchKeywordResolver; @Override public SearchExpressionHandler getSearchExpressionHandler() { return searchExpressionHandler; } @Override public void setSearchExpressionHandler(SearchExpressionHandler searchExpressionHandler) { this.searchExpressionHandler = searchExpressionHandler; } @Override public SearchKeywordResolver getSearchKeywordResolver() { return searchKeywordResolver; } public void setSearchKeywordResolver(SearchKeywordResolver searchKeywordResolver) { this.searchKeywordResolver = searchKeywordResolver; } }