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

com.sun.faces.mock.MockApplication Maven / Gradle / Ivy

The newest version!
/*
 * $Id: MockApplication.java,v 1.1 2005/10/18 17:47:51 edburns Exp $
 */

/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the License). You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the License at
 * https://javaserverfaces.dev.java.net/CDDL.html or
 * legal/CDDLv1.0.txt. 
 * See the License for the specific language governing
 * permission and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at legal/CDDLv1.0.txt.    
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * [Name of File] [ver.__] [Date]
 * 
 * Copyright 2005 Sun Microsystems Inc. All Rights Reserved
 */

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;


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;
    }

    /**
     * 

If there are one or more listeners * for events of the type represented by * systemEventClass, call those listeners, passing * source as the source of the event. The * implementation should be as fast as possible in determining * whether or not a listener for the given * systemEventClass and source has been * installed, and should return immediately once such a * determination has been made. The implementation of * publishEvent must honor the requirements stated in * {@link #subscribeToEvent} regarding the storage and retrieval of * listener instances.

* *
* *

The default implementation must implement an algorithm * semantically equivalent to the following to locate listener * instances and to invoke them.

* *
    * *
  • If the source argument implements {@link * javax.faces.event.SystemEventListenerHolder}, call {@link * javax.faces.event.SystemEventListenerHolder#getListenersForEventClass} * on it, passing the systemEventClass argument. If * the list is not empty, perform algorithm * traverseListenerList on the list.

  • * *
  • If any Application level listeners have * been installed by previous calls to {@link * #subscribeToEvent(Class, Class, * javax.faces.event.SystemEventListener)}, perform algorithm * traverseListenerList on the list.

  • * *
  • If any Application level listeners have * been installed by previous calls to {@link * #subscribeToEvent(Class, javax.faces.event.SystemEventListener)}, * perform algorithm traverseListenerList on the * list.

  • * *
* *

If the act of invoking the processListener method * causes an {@link javax.faces.event.AbortProcessingException} to * be thrown, processing of the listeners must be aborted.

* * RELEASE_PENDING (edburns,rogerk) it may be prudent to specify how the * abortprocessingexception should be handled. Logged or thrown? * *

Algorithm traverseListenerList: For each listener in * the list,

* *
    * *
  • Call {@link * javax.faces.event.SystemEventListener#isListenerForSource}, passing the * source argument. If this returns * false, take no action on the listener.

  • * *
  • Otherwise, if the event to be passed to the listener * instances has not yet been constructed, construct the event, * passing source as the argument to the * one-argument constructor that takes an Object. * This same event instance must be passed to all listener * instances.

  • * *
  • Call {@link javax.faces.event.SystemEvent#isAppropriateListener}, * passing the listener instance as the argument. If this * returns false, take no action on the * listener.

  • * *
  • Call {@link javax.faces.event.SystemEvent#processListener}, * passing the listener instance.

  • * *
*
* * @param systemEventClass The Class of event that is * being published. * @param source The source for the event of type * systemEventClass. * * @throws NullPointerException if either systemEventClass or * source is null * * @since 2.0 */ public void publishEvent(FacesContext context, Class 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); } } } /** *

Install the listener instance * referenced by argument listener into the * application as a listener for events of type * systemEventClass that originate from objects of type * sourceClass.

* *
* *

If argument sourceClass is non-null, * sourceClass and systemEventClass must be * used to store the argument listener in the application in * such a way that the listener can be quickly looked * up by the implementation of {@link javax.faces.application.Application#publishEvent} given * systemEventClass and an instance of the * Class referenced by sourceClass. If * argument sourceClass is null, the * listener must be discoverable by the implementation * of {@link javax.faces.application.Application#publishEvent} given only systemEventClass. *

* *
* * @param systemEventClass the Class of event for which * listener must be fired. * * @param sourceClass the Class of the instance which * causes events of type systemEventClass to be fired. * May be null. * * @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 systemEventClass are fired. * * @throws NullPointerException if any combination of * systemEventClass, or listener are * null. * * @since 2.0 */ public void subscribeToEvent(Class systemEventClass, Class sourceClass, SystemEventListener listener) { if (systemEventClass == null) { throw new NullPointerException("systemEventClass"); } if (listener == null) { throw new NullPointerException("listener"); } Set listeners = getListeners(systemEventClass, sourceClass); listeners.add(listener); } /** *

Install the listener instance * referenced by argument listener into application * as a listener for events of type * systemEventClass. The default implementation simply calls * through to {@link #subscribeToEvent(Class, Class, javax.faces.event.SystemEventListener)} passing null as the sourceClass argument

* * @param systemEventClass the Class of event for which * listener 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 systemEventClass are fired. * * @throws NullPointerException if any combination of * systemEventClass, or listener are * null. * * @since 2.0 */ public void subscribeToEvent(Class systemEventClass, SystemEventListener listener) { subscribeToEvent(systemEventClass, null, listener); } /** *

Remove the listener instance * referenced by argument listener from the application * as a listener for events of type * systemEventClass that originate from objects of type * sourceClass. 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.

* * @param systemEventClass the Class of event for which * listener must be fired. * * @param sourceClass the Class of the instance which * causes events of type systemEventClass to be fired. * May be null. * * @param listener the implementation of {@link * javax.faces.event.SystemEventListener} to remove from the internal data * structure. * * @throws NullPointerException if any combination of * context, * systemEventClass, or listener are * null. */ public void unsubscribeFromEvent(Class systemEventClass, Class sourceClass, SystemEventListener listener) { if (systemEventClass == null) { throw new NullPointerException("systemEventClass"); } if (listener == null) { throw new NullPointerException("listener"); } Set listeners = getListeners(systemEventClass, sourceClass); if (listeners != null) { listeners.remove(listener); } } /** *

Remove the listener instance * referenced by argument listener from the application * as a listener for events of type systemEventClass. The * default implementation simply calls through to {@link #unsubscribeFromEvent(Class, javax.faces.event.SystemEventListener)} passing null as the sourceClass argument

* * @param systemEventClass the Class of event for which * listener must be fired. * * @param listener the implementation of {@link * javax.faces.event.SystemEventListener} to remove from the internal data * structure. * * @throws NullPointerException if any combination of * context, systemEventClass, or * listener are * null. */ public void unsubscribeFromEvent(Class systemEventClass, SystemEventListener listener) { unsubscribeFromEvent(systemEventClass, null, 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; } /** * @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) { EventInfo eventInfo = compSysEventHelper.getEventInfo(systemEventClass, source.getClass()); return processListeners(((SystemEventListenerHolder) source).getListenersForEventClass(systemEventClass), null, source, eventInfo); } return null; } /** * Traverse the List 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 systemEventClass, SystemEvent event, Object source, boolean useSourceLookup) throws AbortProcessingException { EventInfo eventInfo = systemEventHelper.getEventInfo(systemEventClass, source, 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()) { 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, SystemEventInfo> systemEventInfoCache; // -------------------------------------------------------- Constructors public SystemEventHelper() { systemEventInfoCache = new Cache, SystemEventInfo>( new Factory, SystemEventInfo>() { public SystemEventInfo newInstance(final Class arg) throws InterruptedException { return new SystemEventInfo(arg); } } ); } // ------------------------------------------------------ Public Methods public EventInfo getEventInfo(Class systemEventClass, Class sourceClass) { EventInfo info = null; SystemEventInfo systemEventInfo = systemEventInfoCache.get(systemEventClass); if (systemEventInfo != null) { info = systemEventInfo.getEventInfo(sourceClass); } return info; } public EventInfo getEventInfo(Class 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,Cache,EventInfo>> sourceCache; // -------------------------------------------------------- Constructors public ComponentSystemEventHelper() { // Initialize the 'sources' cache for, ahem, readability... // ~generics++ Factory, Cache, EventInfo>> eventCacheFactory = new Factory, Cache, EventInfo>>() { public Cache, EventInfo> newInstance( final Class sourceClass) throws InterruptedException { Factory, EventInfo> eventInfoFactory = new Factory, EventInfo>() { public EventInfo newInstance(final Class systemEventClass) throws InterruptedException { return new EventInfo(systemEventClass, sourceClass); } }; return new Cache, EventInfo>(eventInfoFactory); } }; sourceCache = new Cache,Cache,EventInfo>>(eventCacheFactory); } // ------------------------------------------------------ Public Methods public EventInfo getEventInfo(Class systemEvent, Class sourceClass) { Cache, 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 SourceInfo instances. */ private static class SystemEventInfo { private Cache,EventInfo> cache = new Cache,EventInfo>( new Factory, EventInfo>() { public EventInfo newInstance(Class arg) throws InterruptedException { return new EventInfo(systemEvent, arg); } } ); private Class systemEvent; // -------------------------------------------------------- Constructors private SystemEventInfo(Class 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 systemEvent; private Class sourceClass; private Set listeners; private Constructor eventConstructor; private Map,Constructor> constructorMap; // -------------------------------------------------------- Constructors public EventInfo(Class systemEvent, Class sourceClass) { this.systemEvent = systemEvent; this.sourceClass = sourceClass; this.listeners = new CopyOnWriteArraySet(); this.constructorMap = new HashMap,Constructor>(); if (!sourceClass.equals(Void.class)) { eventConstructor = getEventConstructor(sourceClass); } } // ------------------------------------------------------ Public Methods public Set 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 { V newInstance(final K arg) throws InterruptedException; } // END Factory /** * A concurrent caching mechanism. */ private static final class Cache { private ConcurrentMap> cache = new ConcurrentHashMap>(); private Factory factory; // -------------------------------------------------------- Constructors /** * Constructs this cache using the specified Factory. * @param factory */ public Cache(Factory 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 Factory * 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 f = cache.get(key); if (f == null) { Callable callable = new Callable() { public V call() throws Exception { return factory.newInstance(key); } }; FutureTask ft = new FutureTask(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 }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy