blob: 8311ae4fcec38f93a02b296a3549262624c379fe [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.extensions.scripting.jsf.adapters.handlers;
/**
* @author Werner Punz (latest modification by $Author$)
* @version $Revision$ $Date$
*
*/
import org.apache.myfaces.config.RuntimeConfig;
import org.apache.myfaces.config.annotation.LifecycleProvider;
import org.apache.myfaces.config.annotation.LifecycleProviderFactory;
import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
import org.apache.myfaces.extensions.scripting.core.common.util.ReflectUtil;
import org.apache.myfaces.extensions.scripting.core.monitor.ClassResource;
import javax.faces.context.FacesContext;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Bean handler implementation
* which encapsulates the myfaces specific parts
* of the bean processing
*/
public class MyFacesBeanHandler
{
static final Logger _logger = Logger.getLogger(MyFacesBeanHandler.class.getName());
/**
* constructor
*/
public MyFacesBeanHandler()
{
}
/**
* Refreshes all managed beans
* session, and personal scoped ones
* <p/>
* personal scoped beans are beans which
* have either
* <li> session scope </li>
* <li> page scope </li>
* <li> custom scope </li>
*/
public void refreshAllManagedBeans()
{
if (FacesContext.getCurrentInstance() == null)
{
return;//no npe allowed
}
Collection<ClassResource> tainted = WeavingContext.getInstance().getTaintedClasses();
Set<String> taints = new HashSet<String>();
for (ClassResource taintedClass : tainted)
{
if(taintedClass.getAClass() != null) {
taints.add(taintedClass.getAClass().getName());
}
}
//scanElDependencies();
if (taints.size() > 0)
{
//We now have to check if the tainted classes belong to the managed beans
Set<String> managedBeanClasses = new HashSet<String>();
Map mbeans = RuntimeConfig.getCurrentInstance(FacesContext.getCurrentInstance().getExternalContext()).getManagedBeans();
Map mbeansSnapshotView;
//synchronized (RefreshContext.BEAN_SYNC_MONITOR) {
mbeansSnapshotView = makeSnapshot(mbeans);
//}
for (Object entry : mbeansSnapshotView.entrySet())
{
Object bean = (Object) ((Map.Entry) entry).getValue();
managedBeanClasses.add((String) ReflectUtil.executeMethod(bean, "getManagedBeanClassName"));//bean.getManagedBeanClassName());
}
boolean managedBeanTainted = isAnyManagedBeanTainted(taints, managedBeanClasses);
markPersonalScopeRefreshRecommended();
getLog().info("[EXT-SCRIPTING] Tainting all beans to avoid classcast exceptions");
if (managedBeanTainted)
{
globalManagedBeanRefresh(mbeansSnapshotView);
//personalScopeRefresh();
}
}
}
/**
* removes all bean references which have been tainted
* (note for now we remove all dynamic references until we
* get a more sophisticated handling of managed beans)
*
* @param workCopy the managed beam snapshot view
*/
private void globalManagedBeanRefresh(Map workCopy)
{
Collection<ClassResource> tainted = WeavingContext.getInstance().getTaintedClasses();
Set<String> taints = new HashSet<String>();
for (ClassResource taintedClass : tainted)
{
if(taintedClass.getAClass() != null)
taints.add(taintedClass.getAClass().getName());
}
for (Object entry : workCopy.entrySet())
{
Object bean = ((Map.Entry) entry).getValue();
Class managedBeanClass = (Class) ReflectUtil.executeMethod(bean, "getManagedBeanClass");
if (hasToBeRefreshed(taints, managedBeanClass))
{
//managed bean class found we drop the class from our session
removeBeanReferences(bean);
}
}
}
/**
* determines whether any bean in our managed bean list
* is tainted or not
*
* @param tainted a list of classes which are tainted in this iteration
* @param managedBeanClasses a ist of classes which are our managed beans
* @return true if one of the beans is tainted
*/
private boolean isAnyManagedBeanTainted(Set<String> tainted, Set<String> managedBeanClasses)
{
boolean managedBeanTainted = false;
for (String taintedClass : tainted)
{
if (managedBeanClasses.contains(taintedClass))
{
managedBeanTainted = true;
break;
}
}
return managedBeanTainted;
}
/**
* removes the references from out static scope
* for jsf2 we probably have some kind of notification mechanism
* which notifies custom scopes
*
* @param bean the managed bean which all references have to be removed from
*/
private void removeBeanReferences(Object bean)
{
String managedBeanName = (String) ReflectUtil.executeMethod(bean, "getManagedBeanName");
if (getLog().isLoggable(Level.FINE))
{
getLog().log(Level.FINE, "[EXT-SCRIPTING] JavaScriptingWeaver.removeBeanReferences({0})", managedBeanName);
}
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().remove(managedBeanName);
FacesContext.getCurrentInstance().getExternalContext().getApplicationMap().remove(managedBeanName);
removeCustomScopedBean(bean);
}
/**
* @return the log for this class
*/
protected Logger getLog()
{
return Logger.getLogger(this.getClass().getName());
}
/**
* jsf2 helper to remove custom scoped beans
*
* @param bean the managed bean which has to be removed from the custom scope from
*/
private void removeCustomScopedBean(Object bean)
{
Object scopeImpl = FacesContext.getCurrentInstance().getExternalContext().getApplicationMap().get(ReflectUtil.executeMethod(bean, "getManagedBeanScope"));
if (scopeImpl == null) return; //scope not implemented
//we now have to revert to introspection here because scopes are a pure jsf2 construct
//so we use a messaging pattern here to cope with it
Object beanInstance = ReflectUtil.executeMethod(scopeImpl, "get", ReflectUtil.executeMethod(bean, "getManagedBeanName"));
LifecycleProvider lifecycleProvider =
LifecycleProviderFactory.getLifecycleProviderFactory().getLifecycleProvider(FacesContext.getCurrentInstance().getExternalContext());
try
{
lifecycleProvider.destroyInstance(beanInstance);
}
catch (IllegalAccessException e)
{
_logger.log(Level.WARNING, "removeCustomScopedBean():", e);
}
catch (InvocationTargetException e)
{
_logger.log(Level.WARNING, "removeCustomScopedBean():", e);
}
}
/**
* MyFaces 2.0 keeps an immutable map over the session
* and request scoped beans
* if we alter that during our loop we get a concurrent modification exception
* taking a snapshot in time fixes that
*
* @param mbeans the internal managed bean map which has to be investigated
* @return a map with the class name as key and the managed bean info
* as value of the current state of the internal runtime config bean map
*/
private Map makeSnapshot(Map mbeans)
{
Map workCopy;
workCopy = new HashMap(mbeans.size());
for (Object elem : mbeans.entrySet())
{
Map.Entry entry = (Map.Entry) elem;
workCopy.put(entry.getKey(), entry.getValue());
}
return workCopy;
}
/**
* sets the internal timer for other processes
* to update their beans as well
*/
private void markPersonalScopeRefreshRecommended()
{
long sessionRefreshTime = System.currentTimeMillis();
// WeavingContext.getRefreshContext().setPersonalScopedBeanRefresh(sessionRefreshTime);
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(ScriptingConst.SESS_BEAN_REFRESH_TIMER, sessionRefreshTime);
}
/**
* important, this method determines whether a managed bean class
* has to be refreshed or not
*
* @param tainted set of tainted classes
* @param managedBeanClass the class to be checked for refresh criteria
* @return true if the current bean class fulfills our refresh criteria
*/
protected boolean hasToBeRefreshed(Set<String> tainted, Class managedBeanClass)
{
return WeavingContext.getInstance().isDynamic(managedBeanClass) && tainted.contains(managedBeanClass.getName());
}
public void registerManagedBean(Class clazz, String beanName) {
//TODO move this over from the beanimplementationListener
}
public void removeManagedBean(String className) {
//TODO move this over from the beanimplementationlistener
}
}