From 67ea9ebc88057a53eb7e3ca1be0497979468c057 Mon Sep 17 00:00:00 2001 From: Paul Nicolucci Date: Mon, 29 Aug 2022 10:47:47 -0400 Subject: [PATCH] MYFACES-4442: avoid UnsupportedOperationException --- .../myfaces/webapp/FacesInitializerImpl.java | 77 +++++++--------- .../webapp/MyFacesContainerInitializer.java | 90 ++++++++++++------- 2 files changed, 90 insertions(+), 77 deletions(-) diff --git a/impl/src/main/java/org/apache/myfaces/webapp/FacesInitializerImpl.java b/impl/src/main/java/org/apache/myfaces/webapp/FacesInitializerImpl.java index 3752d2b345..db5a91f944 100644 --- a/impl/src/main/java/org/apache/myfaces/webapp/FacesInitializerImpl.java +++ b/impl/src/main/java/org/apache/myfaces/webapp/FacesInitializerImpl.java @@ -85,19 +85,19 @@ public class FacesInitializerImpl implements FacesInitializer private static final Logger log = Logger.getLogger(FacesInitializerImpl.class.getName()); public static final String CDI_BEAN_MANAGER_INSTANCE = "oam.cdi.BEAN_MANAGER_INSTANCE"; - + private static final String CDI_SERVLET_CONTEXT_BEAN_MANAGER_ATTRIBUTE = "jakarta.enterprise.inject.spi.BeanManager"; public static final String INJECTED_BEAN_STORAGE_KEY = "org.apache.myfaces.spi.BEAN_ENTRY_STORAGE"; public static final String INITIALIZED = "org.apache.myfaces.INITIALIZED"; - + private static final byte FACES_INIT_PHASE_PREINIT = 0; private static final byte FACES_INIT_PHASE_POSTINIT = 1; private static final byte FACES_INIT_PHASE_PREDESTROY = 2; private static final byte FACES_INIT_PHASE_POSTDESTROY = 3; - + /** * Performs all necessary initialization tasks like configuring this Faces * application. @@ -115,14 +115,14 @@ public void initFaces(ServletContext servletContext) } return; } - + try { if (log.isLoggable(Level.FINEST)) { log.finest("Initializing MyFaces"); } - + long start = System.currentTimeMillis(); // Some parts of the following configuration tasks have been implemented @@ -144,43 +144,34 @@ public void initFaces(ServletContext servletContext) { spf.initKnownServiceProviderMapInfo(externalContext, spfConfig); } - - // Parse and validate the web.xml configuration file - + if (!WebConfigParamUtils.getBooleanInitParameter(externalContext, MyfacesConfig.INITIALIZE_ALWAYS_STANDALONE, false)) { - FacesServletMappingUtils.ServletRegistrationInfo facesServletRegistration = - FacesServletMappingUtils.getFacesServletRegistration(facesContext, servletContext); - if (facesServletRegistration == null - || facesServletRegistration.getMappings() == null - || facesServletRegistration.getMappings().length == 0) + // check to see if the FacesServlet was found by MyFacesContainerInitializer + Boolean mappingAdded = (Boolean) servletContext.getAttribute( + MyFacesContainerInitializer.FACES_SERVLET_FOUND); + + if (mappingAdded == null || !mappingAdded) { - // check to see if the FacesServlet was found by MyFacesContainerInitializer - Boolean mappingAdded = (Boolean) servletContext.getAttribute( - MyFacesContainerInitializer.FACES_SERVLET_FOUND); + // check if the FacesServlet has been added dynamically + // in a Servlet 3.0 environment by MyFacesContainerInitializer + mappingAdded = (Boolean) servletContext.getAttribute( + MyFacesContainerInitializer.FACES_SERVLET_ADDED_ATTRIBUTE); if (mappingAdded == null || !mappingAdded) { - // check if the FacesServlet has been added dynamically - // in a Servlet 3.0 environment by MyFacesContainerInitializer - mappingAdded = (Boolean) servletContext.getAttribute( - MyFacesContainerInitializer.FACES_SERVLET_ADDED_ATTRIBUTE); - - if (mappingAdded == null || !mappingAdded) + if (log.isLoggable(Level.WARNING)) { - if (log.isLoggable(Level.WARNING)) - { - log.warning("No mappings of FacesServlet found. Abort initializing MyFaces."); - } - return; + log.warning("No mappings of FacesServlet found. Abort initializing MyFaces."); } + return; } } } - + initCDIIntegration(servletContext, externalContext); - + initContainerIntegration(servletContext, externalContext); // log environment integrations @@ -205,10 +196,10 @@ public void initFaces(ServletContext servletContext) initWebsocketIntegration(servletContext, externalContext); WebConfigParamsLogger.logWebContextParams(facesContext); - + //Start ViewPoolProcessor if necessary ViewPoolProcessor.initialize(facesContext); - + MyfacesConfig config = MyfacesConfig.getCurrentInstance(facesContext.getExternalContext()); if (config.isAutomaticExtensionlessMapping()) { @@ -222,7 +213,7 @@ public void initFaces(ServletContext servletContext) facesContext.getExternalContext().getApplicationMap().put( MyfacesConfig.RESOURCE_BUNDLE_CONTROL, resourceBundleControl); } - + // print out a very prominent log message if the project stage is != Production if (!facesContext.isProjectStage(ProjectStage.Production) && !facesContext.isProjectStage(ProjectStage.UnitTest)) @@ -476,33 +467,33 @@ public FacesContext initStartupFacesContext(ServletContext servletContext) // before Application and RenderKit factories, so we should use different object. return _createFacesContext(servletContext, true); } - + @Override public void destroyStartupFacesContext(FacesContext facesContext) { _releaseFacesContext(facesContext); } - + @Override public FacesContext initShutdownFacesContext(ServletContext servletContext) { return _createFacesContext(servletContext, false); } - - @Override + + @Override public void destroyShutdownFacesContext(FacesContext facesContext) { _releaseFacesContext(facesContext); } - + private FacesContext _createFacesContext(ServletContext servletContext, boolean startup) { ExternalContext externalContext = new StartupServletExternalContextImpl(servletContext, startup); ExceptionHandler exceptionHandler = new ExceptionHandlerImpl(); - FacesContext facesContext = new StartupFacesContextImpl(externalContext, + FacesContext facesContext = new StartupFacesContextImpl(externalContext, externalContext, exceptionHandler, startup); - - // If getViewRoot() is called during application startup or shutdown, + + // If getViewRoot() is called during application startup or shutdown, // it should return a new UIViewRoot with its locale set to Locale.getDefault(). UIViewRoot startupViewRoot = new UIViewRoot(); startupViewRoot.setLocale(Locale.getDefault()); @@ -510,7 +501,7 @@ private FacesContext _createFacesContext(ServletContext servletContext, boolean return facesContext; } - + private void _releaseFacesContext(FacesContext facesContext) { // make sure that the facesContext gets released. @@ -518,7 +509,7 @@ private void _releaseFacesContext(FacesContext facesContext) if (facesContext != null) { facesContext.release(); - } + } } /** @@ -819,7 +810,7 @@ private void dispatchInitializationEvent(ServletContext context, int operation) * @return false if there are not plugins defined via ServiceLoader. */ private boolean loadFacesInitPluginsViaServiceLoader(ServletContext servletContext) - { + { ServiceLoader loader = ServiceLoader.load(StartupListener.class, ClassUtils.getContextClassLoader()); diff --git a/impl/src/main/java/org/apache/myfaces/webapp/MyFacesContainerInitializer.java b/impl/src/main/java/org/apache/myfaces/webapp/MyFacesContainerInitializer.java index 5bbc8978a0..546e4c7bce 100644 --- a/impl/src/main/java/org/apache/myfaces/webapp/MyFacesContainerInitializer.java +++ b/impl/src/main/java/org/apache/myfaces/webapp/MyFacesContainerInitializer.java @@ -132,6 +132,9 @@ public void onStartup(Set> clazzes, ServletContext servletContext) thro return; } + // Check for a FacesServlet and store the result so it can be used by the FacesInitializer. + boolean isFacesServletPresent = checkForFacesServlet(servletContext); + // Check for one or more of this conditions: // 1. A faces-config.xml file is found in WEB-INF // 2. A faces-config.xml file is found in the META-INF directory of a jar in the application's classpath. @@ -142,45 +145,42 @@ public void onStartup(Set> clazzes, ServletContext servletContext) thro // implementation is not empty. if ((clazzes != null && !clazzes.isEmpty()) || isFacesConfigPresent(servletContext)) { - // look for the FacesServlet - Map servlets = servletContext.getServletRegistrations(); - for (Map.Entry servletEntry : servlets.entrySet()) + /* + * If we get into this code block the application contains some Faces artifacts, either a faces-config.xml + * or one ore more classes from @HandlesTypes. However if classes from @HandlesTypes or a faces-config.xml + * is available a FacesServlet definition might not be defined. + * + * If a FacesServet definition was not found then add it dynamically. + */ + if(!isFacesServletPresent) { - String className = servletEntry.getValue().getClassName(); - if (FACES_SERVLET_CLASS.getName().equals(className) || isDelegatedFacesServlet(className)) + // the FacesServlet is not installed yet - install it + ServletRegistration.Dynamic servlet = + servletContext.addServlet(FACES_SERVLET_NAME, FACES_SERVLET_CLASS); + + //try to add typical Faces mappings + String[] mappings = isAutomaticXhtmlMappingDisabled(servletContext) ? + FACES_SERVLET_MAPPINGS : FACES_SERVLET_FULL_MAPPINGS; + Set conflictMappings = servlet.addMapping(mappings); + if (conflictMappings != null && !conflictMappings.isEmpty()) { - // we found a FacesServlet; set an attribute for use during initialization - servletContext.setAttribute(FACES_SERVLET_FOUND, Boolean.TRUE); - return; + //at least one of the attempted mappings is in use, remove and try again + Set newMappings = new HashSet<>(Arrays.asList(mappings)); + newMappings.removeAll(conflictMappings); + mappings = newMappings.toArray(new String[newMappings.size()]); + servlet.addMapping(mappings); } - } - - // the FacesServlet is not installed yet - install it - ServletRegistration.Dynamic servlet = servletContext.addServlet(FACES_SERVLET_NAME, FACES_SERVLET_CLASS); - - //try to add typical Faces mappings - String[] mappings = isAutomaticXhtmlMappingDisabled(servletContext) ? - FACES_SERVLET_MAPPINGS : FACES_SERVLET_FULL_MAPPINGS; - Set conflictMappings = servlet.addMapping(mappings); - if (conflictMappings != null && !conflictMappings.isEmpty()) - { - //at least one of the attempted mappings is in use, remove and try again - Set newMappings = new HashSet<>(Arrays.asList(mappings)); - newMappings.removeAll(conflictMappings); - mappings = newMappings.toArray(new String[newMappings.size()]); - servlet.addMapping(mappings); - } - if (mappings != null && mappings.length > 0) - { - // at least one mapping was added - // now we have to set a field in the ServletContext to indicate that we have - // added the mapping dynamically, because MyFaces just parsed the web.xml to - // find mappings and thus it would abort initializing - servletContext.setAttribute(FACES_SERVLET_ADDED_ATTRIBUTE, Boolean.TRUE); + if (mappings != null && mappings.length > 0) + { + // at least one mapping was added + // now we have to set a field in the ServletContext to indicate that we have + // added the mapping dynamically. + servletContext.setAttribute(FACES_SERVLET_ADDED_ATTRIBUTE, Boolean.TRUE); - // add a log message - log.log(Level.INFO, "Added FacesServlet with mappings=" + Arrays.toString(mappings)); + // add a log message + log.log(Level.INFO, "Added FacesServlet with mappings=" + Arrays.toString(mappings)); + } } } } @@ -304,6 +304,28 @@ private boolean isFacesConfigPresent(ServletContext servletContext) } } + /* + * Checks if a FacesServlet has been mapped. + */ + private boolean checkForFacesServlet(ServletContext servletContext) + { + // look for the FacesServlet + Map servlets = servletContext.getServletRegistrations(); + boolean isFacesServletPresent = false; + + for (Map.Entry servletEntry : servlets.entrySet()) + { + String className = servletEntry.getValue().getClassName(); + if (FACES_SERVLET_CLASS.getName().equals(className) || isDelegatedFacesServlet(className)) + { + // we found a FacesServlet; set an attribute for use during initialization + servletContext.setAttribute(FACES_SERVLET_FOUND, Boolean.TRUE); + isFacesServletPresent = true; + } + } + return isFacesServletPresent; + } + /** * Checks if the class represented by className implements DelegatedFacesServlet. * @param className