blob: bcfb8339446a4149a342c9e5e9c14aaaa3bd509a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.myfaces.webapp;
import org.apache.myfaces.util.lang.ClassUtils;
import javax.faces.FactoryFinder;
import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ServiceLoader;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.myfaces.config.MyfacesConfig;
/**
* Initialise the MyFaces system.
* <p>
* This context listener is registered by the JSP TLD file for the standard JSF "f" components. Normally, servlet
* containers will automatically load and process .tld files at startup time, and therefore register and run this class
* automatically.
* </p><p>
* Some very old servlet containers do not do this correctly, so in those cases this listener may be registered manually
* in web.xml. Registering it twice (ie in both .tld and web.xml) will result in a harmless warning message being
* generated. Very old versions of MyFaces Core do not register the listener in the .tld file, so those also need a
* manual entry in web.xml. However all versions since at least 1.1.2 have this entry in the tld.
* </p><p>
* </p>
*
* @author Manfred Geiler (latest modification by $Author$)
* @version $Revision$ $Date$
*/
public class StartupServletContextListener implements ServletContextListener
{
static final String FACES_INIT_DONE = "org.apache.myfaces.webapp.StartupServletContextListener.FACES_INIT_DONE";
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;
private static final Logger log = Logger.getLogger(StartupServletContextListener.class.getName());
private FacesInitializer _facesInitializer;
private ServletContext _servletContext;
@Override
public void contextInitialized(ServletContextEvent event)
{
if (_servletContext != null)
{
throw new IllegalStateException("context is already initialized");
}
_servletContext = event.getServletContext();
Boolean b = (Boolean) _servletContext.getAttribute(FACES_INIT_DONE);
if (b == null || b == false)
{
long start = System.currentTimeMillis();
if (_facesInitializer == null)
{
_facesInitializer = FacesInitializerFactory.getFacesInitializer(_servletContext);
}
// Create startup FacesContext before initializing
FacesContext facesContext = _facesInitializer.initStartupFacesContext(_servletContext);
dispatchInitializationEvent(event, FACES_INIT_PHASE_PREINIT);
_facesInitializer.initFaces(_servletContext);
dispatchInitializationEvent(event, FACES_INIT_PHASE_POSTINIT);
_servletContext.setAttribute(FACES_INIT_DONE, Boolean.TRUE);
//Destroy startup FacesContext
_facesInitializer.destroyStartupFacesContext(facesContext);
log.log(Level.INFO, "MyFaces Core has started, it took ["
+ (System.currentTimeMillis() - start)
+ "] ms.");
}
else
{
log.finest("MyFaces already initialized");
}
}
@Override
public void contextDestroyed(ServletContextEvent event)
{
if (_facesInitializer != null && _servletContext != null)
{
// Create startup FacesContext before start undeploy
FacesContext facesContext = _facesInitializer.initShutdownFacesContext(_servletContext);
dispatchInitializationEvent(event, FACES_INIT_PHASE_PREDESTROY);
_facesInitializer.destroyFaces(_servletContext);
// Destroy startup FacesContext, but note we do before publish postdestroy event on
// plugins and before release factories.
if (facesContext != null)
{
_facesInitializer.destroyShutdownFacesContext(facesContext);
}
FactoryFinder.releaseFactories();
//DiscoverSingleton.release(); //clears EnvironmentCache and prevents leaking classloader references
dispatchInitializationEvent(event, FACES_INIT_PHASE_POSTDESTROY);
}
_servletContext = null;
}
/**
* configure the faces initializer
*
* @param facesInitializer
*/
public void setFacesInitializer(FacesInitializer facesInitializer) // TODO who uses this method?
{
if (_facesInitializer != null && _facesInitializer != facesInitializer && _servletContext != null)
{
_facesInitializer.destroyFaces(_servletContext);
}
_facesInitializer = facesInitializer;
if (_servletContext != null)
{
facesInitializer.initFaces(_servletContext);
}
}
/**
* Loads the faces init plugins per Service loader.
*
* @return false if there are not plugins defined via ServiceLoader.
*/
private boolean loadFacesInitPluginsViaServiceLoader()
{
ServiceLoader<StartupListener> loader = ServiceLoader.load(StartupListener.class,
ClassUtils.getContextClassLoader());
Iterator<StartupListener> it = (Iterator<StartupListener>) loader.iterator();
if (!it.hasNext())
{
return false;
}
List<StartupListener> listeners = new LinkedList<>();
while (it.hasNext())
{
listeners.add(it.next());
}
_servletContext.setAttribute(MyfacesConfig.FACES_INIT_PLUGINS, listeners);
return true;
}
/**
* loads the faces init plugins per reflection from the context param.
*/
private void loadFacesInitViaContextParam()
{
String plugins = (String) _servletContext.getInitParameter(MyfacesConfig.FACES_INIT_PLUGINS);
if (plugins == null)
{
return;
}
log.info("MyFaces Plugins found");
String[] pluginEntries = plugins.split(",");
List<StartupListener> listeners = new ArrayList<>(pluginEntries.length);
for (String pluginEntry : pluginEntries)
{
try
{
Class pluginClass = ClassUtils.getContextClassLoader().loadClass(pluginEntry);
if (pluginClass == null)
{
pluginClass = this.getClass().getClassLoader().loadClass(pluginEntry);
}
listeners.add((StartupListener) pluginClass.newInstance());
}
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e)
{
log.log(Level.SEVERE, e.getMessage(), e);
}
}
_servletContext.setAttribute(MyfacesConfig.FACES_INIT_PLUGINS, listeners);
}
/**
* the central initialisation event dispatcher which calls
* our listeners
*
* @param event
* @param operation
*/
private void dispatchInitializationEvent(ServletContextEvent event, int operation)
{
if (operation == FACES_INIT_PHASE_PREINIT)
{
if (!loadFacesInitPluginsViaServiceLoader())
{
loadFacesInitViaContextParam();
}
}
List<StartupListener> pluginEntries = (List<StartupListener>)
_servletContext.getAttribute(MyfacesConfig.FACES_INIT_PLUGINS);
if (pluginEntries == null)
{
return;
}
//now we process the plugins
for (StartupListener initializer : pluginEntries)
{
log.info("Processing plugin");
//for now the initializers have to be stateless to
//so that we do not have to enforce that the initializer
//must be serializable
switch (operation)
{
case FACES_INIT_PHASE_PREINIT:
initializer.preInit(event);
break;
case FACES_INIT_PHASE_POSTINIT:
initializer.postInit(event);
break;
case FACES_INIT_PHASE_PREDESTROY:
initializer.preDestroy(event);
break;
default:
initializer.postDestroy(event);
break;
}
}
log.info("Processing MyFaces plugins done");
}
}