/*
* JBoss, Home of Professional Open Source
* Copyright 2013, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.richfaces.application;
import static org.richfaces.application.configuration.ConfigurationServiceHelper.getBooleanConfigurationValue;
import static org.richfaces.application.CoreConfiguration.Items.executeAWTInitializer;
import static org.richfaces.application.CoreConfiguration.Items.pushInitializePushContextOnStartup;
import static org.richfaces.application.CoreConfiguration.Items.pushJMSEnabled;
import java.awt.Toolkit;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import javax.faces.FacesException;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.PostConstructApplicationEvent;
import javax.faces.event.PreDestroyApplicationEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import javax.xml.rpc.ServiceFactory;
import org.richfaces.VersionBean;
import org.richfaces.application.push.PushContextFactory;
import org.richfaces.log.Logger;
import org.richfaces.log.RichfacesLogger;
/**
* <p>Listens for application's {@link PostConstructApplicationEvent} and {@link PreDestroyApplicationEvent} events in order to initialize RichFaces services.</p>
*
* @author Nick Belaevski
* @since 4.0
*/
public class InitializationListener implements SystemEventListener {
private static final Logger LOGGER = RichfacesLogger.APPLICATION.getLogger();
/**
* Called on application startup
*/
protected void onStart() {
JsfVersionInspector enforcer = new JsfVersionInspector();
if (!enforcer.verifyJsfImplVersion()) {
throw new RuntimeException(String.format("The JSF implementation %s does not support the RichFaces ExtendedPartialViewContext. Please upgrade to at least Mojarra 2.1.28 or 2.2.6", enforcer.getVersionString()));
}
createFactory();
if (LOGGER.isInfoEnabled()) {
String versionString = VersionBean.VERSION.toString();
if (versionString != null && versionString.length() != 0) {
LOGGER.info(versionString);
}
}
if (getConfiguration(executeAWTInitializer)) {
initializeAWT();
}
boolean jmsEnabled = getConfiguration(pushJMSEnabled) != null && getConfiguration(pushJMSEnabled);
if (jmsEnabled || getConfiguration(pushInitializePushContextOnStartup)) {
initializePushContext();
}
if (!jmsEnabled) {
logWarningWhenConnectionFactoryPresent();
}
}
/**
* Called when application is being destroyed
*/
protected void onStop() {
ServiceTracker.release();
}
/**
* Creates {@link ServiceFactory} that holds references to RichFaces application singletons
*/
protected ServicesFactory createFactory() {
ServicesFactoryImpl injector = new ServicesFactoryImpl();
ServiceTracker.setFactory(injector);
ArrayList<Module> modules = new ArrayList<Module>();
addDefaultModules(modules);
try {
modules.addAll(ServiceLoader.loadServices(Module.class));
injector.init(modules);
} catch (ServiceException e) {
throw new FacesException(e);
}
return injector;
}
/**
* Adds default application modules
*/
protected void addDefaultModules(List<Module> modules) {
modules.add(new DefaultModule());
}
/**
* Initializes AWT for dynamic image rendering
*/
private void initializeAWT() {
try {
AWTInitializer.initialize();
} catch (NoClassDefFoundError e) {
LOGGER.warn(MessageFormat.format("There were problems loading class: {0} - AWTInitializer won't be run",
e.getMessage()));
}
}
/**
* Initializes {@link org.richfaces.application.push.PushContext} on startup as it is a time when {@link FacesContext} is available
*/
private void initializePushContext() {
try {
LOGGER.info("Startup initialization of PushContext");
ServiceTracker.getService(PushContextFactory.class).getPushContext();
} catch (Exception e) {
LOGGER.error(MessageFormat.format("There were problems initializing PushContext on startup: {0}", e.getMessage()));
}
}
/**
* Logs a warning that JMS API is available but Push/JMS integration is not enabled
*/
private void logWarningWhenConnectionFactoryPresent() {
try {
Class.forName("javax.jms.ConnectionFactory");
LOGGER.warn("JMS API was found on the classpath; if you want to enable RichFaces Push JMS integration, set context-param 'org.richfaces.push.jms.enabled' in web.xml");
} catch (ClassNotFoundException e) {
}
}
/*
* (non-Javadoc)
*
* @see javax.faces.event.SystemEventListener#processEvent(javax.faces.event.SystemEvent)
*/
@Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
if (event instanceof PostConstructApplicationEvent) {
onStart();
} else if (event instanceof PreDestroyApplicationEvent) {
onStop();
} else {
throw new IllegalArgumentException(MessageFormat.format("Event {0} is not supported!", event));
}
}
/*
* (non-Javadoc)
*
* @see javax.faces.event.SystemEventListener#isListenerForSource(java.lang.Object)
*/
@Override
public boolean isListenerForSource(Object source) {
return true;
}
private Boolean getConfiguration(Enum<?> configuration) {
FacesContext facesContext = FacesContext.getCurrentInstance();
return getBooleanConfigurationValue(facesContext, configuration);
}
private static final class AWTInitializer {
private AWTInitializer() {
}
private static boolean checkGetSystemClassLoaderAccess() {
try {
ClassLoader.getSystemClassLoader();
return true;
} catch (SecurityException e) {
return false;
}
}
public static void initialize() {
if (!checkGetSystemClassLoaderAccess()) {
LOGGER.warn("Access to system class loader restricted - AWTInitializer won't be run");
return;
}
Thread thread = Thread.currentThread();
ClassLoader initialTCCL = thread.getContextClassLoader();
ImageInputStream testStream = null;
try {
ClassLoader systemCL = ClassLoader.getSystemClassLoader();
thread.setContextClassLoader(systemCL);
// set in-memory caching ImageIO
ImageIO.setUseCache(false);
// force Disposer/AWT threads initialization
testStream = ImageIO.createImageInputStream(new ByteArrayInputStream(new byte[0]));
Toolkit.getDefaultToolkit().getSystemEventQueue();
} catch (IOException e) {
LOGGER.error(e.getMessage(), e);
} finally {
if (testStream != null) {
try {
testStream.close();
} catch (IOException e) {
LOGGER.error(e.getMessage(), e);
}
}
thread.setContextClassLoader(initialTCCL);
}
}
}
}