/*
* JBoss, Home of Professional Open Source.
* Copyright 2013, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file 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.jboss.portletbridge.listener;
import com.sun.faces.application.ApplicationAssociate;
import com.sun.faces.config.ConfigManager;
import com.sun.faces.mgbean.BeanManager;
import com.sun.faces.spi.InjectionProvider;
import org.jboss.portletbridge.bridge.context.BridgeContext;
import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Enumeration;
/**
* @author <a href="http://community.jboss.org/people/kenfinni">Ken Finnigan</a>
*/
public class PortletBridgeListener implements ServletContextListener, HttpSessionListener {
private static final String MOJARRA_VIEW_SCOPE_MANAGER = "com.sun.faces.application.view.viewScopeManager";
private static final String MOJARRA_ACTIVE_VIEW_MAPS = "com.sun.faces.application.view.activeViewMaps";
private static PortletBridgeListener INSTANCE;
private ServletContext servletContext;
private ApplicationAssociate applicationAssociate;
private InjectionProvider injectionProvider;
public PortletBridgeListener() {
INSTANCE = this;
}
@Override
public void contextInitialized(ServletContextEvent sce) {
if (null == this.servletContext) {
this.servletContext = sce.getServletContext();
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
for (Enumeration e = servletContext.getAttributeNames(); e.hasMoreElements(); ) {
String beanName = (String)e.nextElement();
handleAttributeEvent(beanName,
servletContext.getAttribute(beanName));
}
this.servletContext = null;
this.applicationAssociate = null;
INSTANCE = null;
}
@Override
public void sessionCreated(HttpSessionEvent se) {
// We can only retrieve this once JSF has initialized, which is after the ServletContext contextInitialized() call.
if (null == this.injectionProvider) {
getInjectionProvider();
}
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
HttpSession session = se.getSession();
for (Enumeration<String> attrs = session.getAttributeNames(); attrs.hasMoreElements(); ) {
String attribute = attrs.nextElement();
if (null != attribute && attribute.startsWith("javax.portlet.p.")) {
// Attribute is namespaced to a portlet and was added with PortletSession.setAttribute()
int pos = attribute.indexOf('?');
if (pos > 0) {
String jsfAttribute = attribute.substring(pos + 1);
Object value = session.getAttribute(attribute);
session.removeAttribute(attribute);
if (null != value) {
boolean destroyCalled = handleAttributeEvent(jsfAttribute, value);
if (!destroyCalled) {
String valueClass = value.getClass().getName();
if (null != valueClass && MOJARRA_ACTIVE_VIEW_MAPS.equals(jsfAttribute)) {
session.setAttribute(jsfAttribute, value);
// Clean up View Scoped beans
HttpSessionListener viewScopeManager =
(HttpSessionListener) servletContext.getAttribute(MOJARRA_VIEW_SCOPE_MANAGER);
if (null != viewScopeManager) {
viewScopeManager.sessionDestroyed(se);
}
}
}
}
}
}
}
}
public boolean handleAttributeEvent(String beanName,
Object bean) {
boolean destroyCalled = false;
ApplicationAssociate associate = getAssociate();
try {
if (null != associate) {
BeanManager beanManager = associate.getBeanManager();
if (null != beanManager && beanManager.isManaged(beanName)) {
// Check whether class has annotations present to determine whether JSF BeanBuilder
// will call invokePreDestroy() on InjectionProvider. If it won't, we do directly.
if (scanForAnnotations(bean.getClass())) {
beanManager.destroy(beanName, bean);
} else {
getInjectionProvider().invokePreDestroy(bean);
}
destroyCalled = true;
}
}
} catch (Exception e) {
String className = e.getClass().getName();
BridgeContext.log("Error calling predestroy on instance of: " + className, e);
}
return destroyCalled;
}
private ApplicationAssociate getAssociate() {
if (null == applicationAssociate) {
applicationAssociate = ApplicationAssociate.getInstance(servletContext);
}
return applicationAssociate;
}
private InjectionProvider getInjectionProvider() {
if (null == injectionProvider) {
FacesContext context = FacesContext.getCurrentInstance();
if (null != context) {
injectionProvider = (InjectionProvider) context.getAttributes().get(ConfigManager.INJECTION_PROVIDER_KEY);
}
}
return injectionProvider;
}
private boolean scanForAnnotations(Class<?> clazz) {
if (clazz != null) {
while (clazz != Object.class) {
Field[] fields = clazz.getDeclaredFields();
if (fields != null) {
for (Field field : fields) {
if (field.getAnnotations().length > 0) {
return true;
}
}
}
Method[] methods = clazz.getDeclaredMethods();
if (methods != null) {
for (Method method : methods) {
if (method.getDeclaredAnnotations().length > 0) {
return true;
}
}
}
clazz = clazz.getSuperclass();
}
}
return false;
}
public static PortletBridgeListener getCurrentInstance() {
return INSTANCE;
}
}