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

com.sun.faces.application.ApplicationAssociate Maven / Gradle / Ivy

Go to download

Jakarta Faces defines an MVC framework for building user interfaces for web applications, including UI components, state management, event handing, input validation, page navigation, and support for internationalization and accessibility.

There is a newer version: 4.1.2
Show newest version
/*
 * Copyright (c) 2021 Contributors to Eclipse Foundation.
 * 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;

import static com.sun.faces.RIConstants.FACES_CONFIG_VERSION;
import static com.sun.faces.RIConstants.FACES_PREFIX;
import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.AutomaticExtensionlessMapping;
import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.FaceletsSkipComments;
import static com.sun.faces.config.WebConfiguration.WebContextInitParameter.FaceletsDecorators;
import static com.sun.faces.config.WebConfiguration.WebContextInitParameter.FaceletsDefaultRefreshPeriod;
import static com.sun.faces.el.ELUtils.buildFacesResolver;
import static com.sun.faces.el.FacesCompositeELResolver.ELResolverChainType.Faces;
import static com.sun.faces.facelets.util.ReflectionUtil.forName;
import static com.sun.faces.util.MessageUtils.APPLICATION_ASSOCIATE_EXISTS_ID;
import static com.sun.faces.util.MessageUtils.getExceptionMessageString;
import static com.sun.faces.util.Util.getFacesConfigXmlVersion;
import static com.sun.faces.util.Util.getFacesServletRegistration;
import static com.sun.faces.util.Util.split;
import static jakarta.faces.FactoryFinder.FACELET_CACHE_FACTORY;
import static jakarta.faces.FactoryFinder.FLOW_HANDLER_FACTORY;
import static jakarta.faces.application.ProjectStage.Development;
import static jakarta.faces.application.ProjectStage.Production;
import static jakarta.faces.application.ViewVisitOption.RETURN_AS_MINIMAL_IMPLICIT_OUTCOME;
import static java.lang.Long.parseLong;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.SEVERE;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

import com.sun.faces.RIConstants;
import com.sun.faces.application.annotation.AnnotationManager;
import com.sun.faces.application.annotation.FacesComponentUsage;
import com.sun.faces.application.resource.ResourceCache;
import com.sun.faces.application.resource.ResourceManager;
import com.sun.faces.component.search.SearchExpressionHandlerImpl;
import com.sun.faces.config.ConfigManager;
import com.sun.faces.config.WebConfiguration;
import com.sun.faces.el.DemuxCompositeELResolver;
import com.sun.faces.facelets.compiler.Compiler;
import com.sun.faces.facelets.compiler.SAXCompiler;
import com.sun.faces.facelets.impl.DefaultFaceletFactory;
import com.sun.faces.facelets.impl.DefaultResourceResolver;
import com.sun.faces.facelets.tag.composite.CompositeLibrary;
import com.sun.faces.facelets.tag.faces.PassThroughAttributeLibrary;
import com.sun.faces.facelets.tag.faces.PassThroughElementLibrary;
import com.sun.faces.facelets.tag.faces.core.CoreLibrary;
import com.sun.faces.facelets.tag.faces.html.HtmlLibrary;
import com.sun.faces.facelets.tag.jstl.core.JstlCoreLibrary;
import com.sun.faces.facelets.tag.jstl.fn.JstlFunction;
import com.sun.faces.facelets.tag.ui.UILibrary;
import com.sun.faces.facelets.util.DevTools;
import com.sun.faces.facelets.util.FunctionLibrary;
import com.sun.faces.spi.InjectionProvider;
import com.sun.faces.util.FacesLogger;

import jakarta.el.CompositeELResolver;
import jakarta.el.ELResolver;
import jakarta.el.ExpressionFactory;
import jakarta.faces.FacesException;
import jakarta.faces.FactoryFinder;
import jakarta.faces.application.Application;
import jakarta.faces.application.NavigationCase;
import jakarta.faces.application.ViewHandler;
import jakarta.faces.component.UIViewRoot;
import jakarta.faces.component.search.SearchExpressionHandler;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.context.FacesContext;
import jakarta.faces.event.PostConstructApplicationEvent;
import jakarta.faces.event.SystemEvent;
import jakarta.faces.event.SystemEventListener;
import jakarta.faces.flow.FlowHandler;
import jakarta.faces.flow.FlowHandlerFactory;
import jakarta.faces.view.facelets.FaceletCache;
import jakarta.faces.view.facelets.FaceletCacheFactory;
import jakarta.faces.view.facelets.TagDecorator;
import jakarta.servlet.ServletContext;

/**
 * 

* Break out the things that are associated with the Application, but need to be present even when the user has replaced * the Application instance. *

* *

* For example: the user replaces ApplicationFactory, and wants to intercept calls to createValueExpression() and * createMethodExpression() for certain kinds of expressions, but allow the existing application to handle the rest. *

*/ public class ApplicationAssociate { private static final Logger LOGGER = FacesLogger.APPLICATION.getLogger(); private final static String FacesComponentJcpNamespace = "http://xmlns.jcp.org/jsf/component"; private final ApplicationImpl applicationImpl; /** * Overall Map containing from-view-id key and Set of NavigationCase objects for * that key; The from-view-id strings in this map will be stored as specified in the configuration file - * some of them will have a trailing asterisk "*" signifying wild card, and some may be specified as an asterisk "*". */ private final Map> navigationMap; /* * The FacesComponentTagLibrary uses the information in this map to help it fabricate tag handlers for components * annotated with FacesComponent. Key: namespace */ private Map> facesComponentsByNamespace; // Flag indicating that a response has been rendered. private boolean responseRendered; private static final String ASSOCIATE_KEY = RIConstants.FACES_PREFIX + "ApplicationAssociate"; private static final ThreadLocal instance = ThreadLocal.withInitial(() -> null); private List elResolversFromFacesConfig; private ExpressionFactory expressionFactory; private final InjectionProvider injectionProvider; private ResourceCache resourceCache; private String contextName; private boolean requestServiced; private boolean errorPagePresent; private final AnnotationManager annotationManager; private final boolean devModeEnabled; private Compiler compiler; private DefaultFaceletFactory faceletFactory; private ResourceManager resourceManager; private final ApplicationStateInfo applicationStateInfo; private final PropertyEditorHelper propertyEditorHelper; private final NamedEventManager namedEventManager; private final WebConfiguration webConfig; private FlowHandler flowHandler; private SearchExpressionHandler searchExpressionHandler; private final Map definingDocumentIdsToTruncatedJarUrls; private final long timeOfInstantiation; private Map> resourceLibraryContracts; Map resourceBundles = new HashMap<>(); public static void setCurrentInstance(ApplicationAssociate associate) { if (associate == null) { instance.remove(); } else { instance.set(associate); } } public static ApplicationAssociate getCurrentInstance() { ApplicationAssociate associate = instance.get(); if (associate == null) { // Fallback to ExternalContext lookup return getInstance(); } return associate; } public static ApplicationAssociate getInstance() { return getInstance(FacesContext.getCurrentInstance()); } public static ApplicationAssociate getInstance(FacesContext facesContext) { if (facesContext == null) { return null; } return ApplicationAssociate.getInstance(facesContext.getExternalContext()); } public static ApplicationAssociate getInstance(ExternalContext externalContext) { if (externalContext == null) { return null; } return (ApplicationAssociate) externalContext.getApplicationMap().get(ASSOCIATE_KEY); } public static ApplicationAssociate getInstance(ServletContext context) { if (context == null) { return null; } return (ApplicationAssociate) context.getAttribute(ASSOCIATE_KEY); } public ApplicationAssociate(ApplicationImpl appImpl) { applicationImpl = appImpl; propertyEditorHelper = new PropertyEditorHelper(appImpl); FacesContext facesContext = FacesContext.getCurrentInstance(); if (facesContext == null) { throw new IllegalStateException("ApplicationAssociate ctor not called in same callstack as ConfigureListener.contextInitialized()"); } ExternalContext externalContext = facesContext.getExternalContext(); if (externalContext.getApplicationMap().get(ASSOCIATE_KEY) != null) { throw new IllegalStateException(getExceptionMessageString(APPLICATION_ASSOCIATE_EXISTS_ID)); } Map applicationMap = externalContext.getApplicationMap(); applicationMap.put(ASSOCIATE_KEY, this); navigationMap = new ConcurrentHashMap<>(); injectionProvider = (InjectionProvider) facesContext.getAttributes().get(ConfigManager.INJECTION_PROVIDER_KEY); webConfig = WebConfiguration.getInstance(externalContext); annotationManager = new AnnotationManager(); devModeEnabled = appImpl.getProjectStage() == Development; if (!devModeEnabled) { resourceCache = new ResourceCache(); } resourceManager = new ResourceManager(applicationMap, resourceCache); namedEventManager = new NamedEventManager(); applicationStateInfo = new ApplicationStateInfo(); appImpl.subscribeToEvent(PostConstructApplicationEvent.class, Application.class, new PostConstructApplicationListener()); definingDocumentIdsToTruncatedJarUrls = new ConcurrentHashMap<>(); timeOfInstantiation = System.currentTimeMillis(); } public Application getApplication() { return applicationImpl; } public void setResourceLibraryContracts(Map> map) { resourceLibraryContracts = map; } private class PostConstructApplicationListener implements SystemEventListener { @Override public boolean isListenerForSource(Object source) { return source instanceof Application; } @Override public void processEvent(SystemEvent event) { initializeFacelets(); if (flowHandler == null) { FlowHandlerFactory flowHandlerFactory = (FlowHandlerFactory) FactoryFinder.getFactory(FLOW_HANDLER_FACTORY); flowHandler = flowHandlerFactory.createFlowHandler(FacesContext.getCurrentInstance()); } if (searchExpressionHandler == null) { searchExpressionHandler = new SearchExpressionHandlerImpl(); } FacesContext context = FacesContext.getCurrentInstance(); try { new JavaFlowLoaderHelper().loadFlows(context, flowHandler); } catch (IOException ex) { LOGGER.log(SEVERE, null, ex); } // cause the Facelet VDL to be instantiated eagerly, so it can // become aware of the resource library contracts ViewHandler viewHandler = context.getApplication().getViewHandler(); // FindBugs: ignore the return value, this is just to get the // ctor called at this time. viewHandler.getViewDeclarationLanguage(context, FACES_PREFIX + "xhtml"); String facesConfigVersion = getFacesConfigXmlVersion(context); context.getExternalContext().getApplicationMap().put(FACES_CONFIG_VERSION, facesConfigVersion); if (webConfig.isOptionEnabled(AutomaticExtensionlessMapping)) { getFacesServletRegistration(context) .ifPresent(registration -> viewHandler.getViews(context, "/", RETURN_AS_MINIMAL_IMPLICIT_OUTCOME) .forEach(view -> registration.addMapping(view))); } } } public void initializeFacelets() { if (compiler != null) { return; } FacesContext ctx = FacesContext.getCurrentInstance(); Map appMap = ctx.getExternalContext().getApplicationMap(); compiler = createCompiler(appMap, webConfig); faceletFactory = createFaceletFactory(ctx, compiler, webConfig); } public long getTimeOfInstantiation() { return timeOfInstantiation; } public ApplicationStateInfo getApplicationStateInfo() { return applicationStateInfo; } public ResourceManager getResourceManager() { return resourceManager; } // Return the resource library contracts and mappings from the // application configuration resources public Map> getResourceLibraryContracts() { return resourceLibraryContracts; } public void setResourceManager(ResourceManager resourceManager) { this.resourceManager = resourceManager; } public ResourceCache getResourceCache() { return resourceCache; } public AnnotationManager getAnnotationManager() { return annotationManager; } public Compiler getCompiler() { if (compiler == null) { initializeFacelets(); } return compiler; } public boolean isErrorPagePresent() { return errorPagePresent; } public void setErrorPagePresent(boolean errorPagePresent) { this.errorPagePresent = errorPagePresent; } public DefaultFaceletFactory getFaceletFactory() { return faceletFactory; } public static void clearInstance(ExternalContext externalContext) { Map applicationMap = externalContext.getApplicationMap(); ApplicationAssociate me = (ApplicationAssociate) applicationMap.get(ASSOCIATE_KEY); if (me != null && me.resourceBundles != null) { me.resourceBundles.clear(); } applicationMap.remove(ASSOCIATE_KEY); } public static void clearInstance(ServletContext servletContext) { ApplicationAssociate me = (ApplicationAssociate) servletContext.getAttribute(ASSOCIATE_KEY); if (me != null && me.resourceBundles != null) { me.resourceBundles.clear(); } servletContext.removeAttribute(ASSOCIATE_KEY); } public void initializeELResolverChains() { // 1. initialize the chains with default values if (applicationImpl.getCompositeELResolver() == null) { applicationImpl.setCompositeELResolver(new DemuxCompositeELResolver(Faces)); buildFacesResolver(applicationImpl.getCompositeELResolver(), this); } } public boolean isDevModeEnabled() { return devModeEnabled; } /** * Obtain the PropertyEditorHelper instance for this app. * * @return The PropertyEditorHeler instance for this app. */ public PropertyEditorHelper getPropertyEditorHelper() { return propertyEditorHelper; } public FlowHandler getFlowHandler() { return flowHandler; } public void setFlowHandler(FlowHandler flowHandler) { this.flowHandler = flowHandler; } public SearchExpressionHandler getSearchExpressionHandler() { return searchExpressionHandler; } public void setSearchExpressionHandler(SearchExpressionHandler searchExpressionHandler) { this.searchExpressionHandler = searchExpressionHandler; } public void setELResolversFromFacesConfig(List resolvers) { elResolversFromFacesConfig = resolvers; } public List getELResolversFromFacesConfig() { return elResolversFromFacesConfig; } public void setExpressionFactory(ExpressionFactory expressionFactory) { this.expressionFactory = expressionFactory; } public ExpressionFactory getExpressionFactory() { return expressionFactory; } public CompositeELResolver getApplicationELResolvers() { return applicationImpl.getApplicationELResolvers(); } public InjectionProvider getInjectionProvider() { return injectionProvider; } public void setContextName(String contextName) { this.contextName = contextName; } public String getContextName() { return contextName; } /** * Called by application code to indicate we've processed the first request to the application. */ public void setRequestServiced() { requestServiced = true; } /** * @return true if we've processed a request, otherwise false */ public boolean hasRequestBeenServiced() { return requestServiced; } public void addFacesComponent(FacesComponentUsage facesComponentUsage) { if (facesComponentsByNamespace == null) { facesComponentsByNamespace = new HashMap<>(); } facesComponentsByNamespace.computeIfAbsent(facesComponentUsage.getAnnotation().namespace(), k -> new ArrayList<>()).add(facesComponentUsage); facesComponentsByNamespace.computeIfAbsent(FacesComponentJcpNamespace, k -> new ArrayList<>()).add(facesComponentUsage); } public List getComponentsForNamespace(String namespace) { if (facesComponentsByNamespace != null && facesComponentsByNamespace.containsKey(namespace)) { return facesComponentsByNamespace.get(namespace); } return emptyList(); } /** * Add a navigation case to the internal case set. If a case set does not already exist in the case list map containing * this case (identified by from-view-id), start a new list, add the case to it, and store the set in the * case set map. If a case set already exists, overwrite the previous case. * * @param navigationCase the navigation case containing navigation mapping information from the configuration file. */ public void addNavigationCase(NavigationCase navigationCase) { // If there already is a case existing for the fromviewid/fromaction.fromoutcome // combination, // replace it ... (last one wins). navigationMap.computeIfAbsent(navigationCase.getFromViewId(), k -> new LinkedHashSet<>()).add(navigationCase); } public NamedEventManager getNamedEventManager() { return namedEventManager; } /** * Return a Map of navigation mappings loaded from the configuration system. The key for the returned * Map is from-view-id, and the value is a List of navigation cases. * * @return Map the map of navigation mappings. */ public Map> getNavigationCaseListMappings() { if (navigationMap == null) { return emptyMap(); } return navigationMap; } public ResourceBundle getResourceBundle(FacesContext context, String var) { ApplicationResourceBundle bundle = resourceBundles.get(var); if (bundle == null) { return null; } // Start out with the default locale Locale defaultLocale = Locale.getDefault(); Locale locale = defaultLocale; // See if this FacesContext has a ViewRoot UIViewRoot root = context.getViewRoot(); if (root != null) { locale = root.getLocale(); if (locale == null) { // If the ViewRoot has no Locale, fall back to the default. locale = defaultLocale; } } return bundle.getResourceBundle(locale); } /** * keys: element from faces-config *

* * values: ResourceBundleBean instances. * * @param var the variable name * @param bundle the application resource bundle */ public void addResourceBundle(String var, ApplicationResourceBundle bundle) { resourceBundles.put(var, bundle); } public Map getResourceBundles() { return resourceBundles; } // This is called by ViewHandlerImpl.renderView(). public void responseRendered() { responseRendered = true; } public boolean isResponseRendered() { return responseRendered; } public boolean urlIsRelatedToDefiningDocumentInJar(URL candidateUrl, String definingDocumentId) { boolean result = false; String match = definingDocumentIdsToTruncatedJarUrls.get(definingDocumentId); if (match != null) { String candidate = candidateUrl.toExternalForm(); if (candidate != null) { int i = candidate.lastIndexOf("/META-INF"); if (i == -1) { throw new FacesException("Invalid url for application configuration resources file with respect to faces flows"); } candidate = candidate.substring(0, i); result = candidate.equals(match); } } return result; } public void relateUrlToDefiningDocumentInJar(URL url, String definingDocumentId) { String candidate = url.toExternalForm(); int i = candidate.lastIndexOf("/META-INF"); if (i == -1) { return; } candidate = candidate.substring(0, i); definingDocumentIdsToTruncatedJarUrls.put(definingDocumentId, candidate); } protected DefaultFaceletFactory createFaceletFactory(FacesContext context, Compiler compiler, WebConfiguration webConfig) { // refresh period boolean isProduction = applicationImpl.getProjectStage() == Production; String refreshPeriod; if (webConfig.isSet(FaceletsDefaultRefreshPeriod)) { refreshPeriod = webConfig.getOptionValue(FaceletsDefaultRefreshPeriod); } else if (isProduction) { refreshPeriod = "-1"; } else { refreshPeriod = FaceletsDefaultRefreshPeriod.getDefaultValue(); } long period = parseLong(refreshPeriod); // resource resolver DefaultResourceResolver resolver = new DefaultResourceResolver(applicationImpl.getResourceHandler()); FaceletCacheFactory cacheFactory = (FaceletCacheFactory) FactoryFinder.getFactory(FACELET_CACHE_FACTORY); FaceletCache cache = cacheFactory.getFaceletCache(); DefaultFaceletFactory toReturn = new DefaultFaceletFactory(); toReturn.init(context, compiler, resolver, period, cache); return toReturn; } protected Compiler createCompiler(Map appMap, WebConfiguration webConfig) { Compiler newCompiler = new SAXCompiler(); loadDecorators(appMap, newCompiler); // Skip params? newCompiler.setTrimmingComments(webConfig.isOptionEnabled(FaceletsSkipComments)); addTagLibraries(newCompiler); return newCompiler; } protected void loadDecorators(Map appMap, Compiler newCompiler) { String decoratorsParamValue = webConfig.getOptionValue(FaceletsDecorators); if (decoratorsParamValue != null) { for (String decorator : split(appMap, decoratorsParamValue.trim(), ";")) { try { newCompiler .addTagDecorator((TagDecorator) forName(decorator).getDeclaredConstructor().newInstance()); if (LOGGER.isLoggable(FINE)) { LOGGER.log(FINE, "Successfully Loaded Decorator: {0}", decorator); } } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) { if (LOGGER.isLoggable(SEVERE)) { LOGGER.log(SEVERE, "Error Loading Decorator: " + decorator, e); } } } } } protected void addTagLibraries(Compiler newCompiler) { CoreLibrary.NAMESPACES.forEach(namespace -> newCompiler.addTagLibrary(new CoreLibrary(namespace))); HtmlLibrary.NAMESPACES.forEach(namespace -> newCompiler.addTagLibrary(new HtmlLibrary(namespace))); UILibrary.NAMESPACES.forEach(namespace -> newCompiler.addTagLibrary(new UILibrary(namespace))); JstlCoreLibrary.NAMESPACES.forEach(namespace -> newCompiler.addTagLibrary(new JstlCoreLibrary(namespace))); PassThroughAttributeLibrary.NAMESPACES.forEach(namespace -> newCompiler.addTagLibrary(new PassThroughAttributeLibrary(namespace))); PassThroughElementLibrary.NAMESPACES.forEach(namespace -> newCompiler.addTagLibrary(new PassThroughElementLibrary(namespace))); FunctionLibrary.NAMESPACES.forEach(namespace -> newCompiler.addTagLibrary(new FunctionLibrary(JstlFunction.class, namespace))); if (isDevModeEnabled()) { DevTools.NAMESPACES.forEach(namespace -> newCompiler.addTagLibrary(new FunctionLibrary(DevTools.class, namespace))); } CompositeLibrary.NAMESPACES.forEach(namespace -> newCompiler.addTagLibrary(new CompositeLibrary(namespace))); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy