/*
* Copyright 2004 The Apache Software Foundation.
*
* Licensed 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.
*
* $Header:$
*/
package org.apache.beehive.netui.pageflow.internal;
import org.apache.beehive.netui.pageflow.AutoRegisterActionServlet;
import org.apache.beehive.netui.pageflow.ServletContainerAdapter;
import org.apache.beehive.netui.pageflow.RequestContext;
import org.apache.beehive.netui.pageflow.handler.ReloadableClassHandler;
import org.apache.beehive.netui.util.internal.BouncyClassLoader;
import org.apache.beehive.netui.util.internal.cache.ClassLevelCache;
import org.apache.beehive.netui.util.logging.Logger;
import org.apache.beehive.netui.util.internal.DiscoveryUtils;
import javax.servlet.http.HttpSession;
import javax.servlet.ServletContext;
import java.util.Enumeration;
import java.util.Map;
import org.apache.beehive.netui.util.internal.concurrent.InternalConcurrentHashMap;
import java.io.File;
import org.apache.struts.action.ActionServlet;
public class DefaultReloadableClassHandler
extends DefaultHandler
implements ReloadableClassHandler
{
private static final Logger _log = Logger.getInstance( DefaultReloadableClassHandler.class );
private BouncyClassLoader _pageFlowClassLoader = null;
public DefaultReloadableClassHandler( ServletContext servletContext )
{
init( null, null, servletContext );
// This feature is disabled for now.
if ( false )
{
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
ServletContainerAdapter servletContainerAdapter = AdapterManager.getServletContainerAdapter( servletContext );
File[] classDirs = null;
// TODO: make this configurable in netui-config.xml. You should be able to specify absolute files
// and also context-relative paths.
{
String path = servletContext.getRealPath( "/WEB-INF/classes" );
if ( path != null )
{
File file = new File( path );
if ( file.isDirectory() ) classDirs = new File[]{ file };
}
}
if ( classDirs != null && ! servletContainerAdapter.isInProductionMode() )
{
_pageFlowClassLoader = new BouncyClassLoader( classDirs, contextClassLoader );
}
}
}
public Object newInstance( String className )
throws ClassNotFoundException, InstantiationException, IllegalAccessException
{
return getRegisteredReloadableClassHandler().loadClass( className ).newInstance();
}
private static Map/*< String, Class >*/ _loadedClasses = new InternalConcurrentHashMap/*< String, Class >*/();
private static class Null {}
private static Class NULL_CLASS = Null.class;
public Class loadCachedClass( String className )
{
Class clazz = ( Class ) _loadedClasses.get( className );
if ( clazz != null )
{
return clazz != NULL_CLASS ? clazz : null;
}
else
{
try
{
clazz = getRegisteredReloadableClassHandler().loadClass( className );
_loadedClasses.put( className, clazz );
return clazz;
}
catch ( ClassNotFoundException e )
{
_loadedClasses.put( className, NULL_CLASS );
return null;
}
}
}
public Class loadClass( String className )
throws ClassNotFoundException
{
if ( _pageFlowClassLoader != null )
{
synchronized ( this )
{
return _pageFlowClassLoader.loadClass( className );
}
}
else
{
return DiscoveryUtils.getClassLoader().loadClass( className );
}
}
public void reloadClasses( RequestContext context )
{
if ( _pageFlowClassLoader == null )
{
return;
}
synchronized ( this )
{
if ( _pageFlowClassLoader.isStale() )
{
_log.debug( "Classes modified; bouncing classloader." );
//
// First go through the session and remove any attributes whose classes were loaded by the stale
// classloader.
//
HttpSession session = InternalUtils.getHttpSession( context.getRequest(), false );
if ( session != null )
{
for ( Enumeration e = session.getAttributeNames(); e.hasMoreElements(); )
{
String attrName = ( String ) e.nextElement();
Object attr = session.getAttribute( attrName );
if ( attr.getClass().getClassLoader() == _pageFlowClassLoader )
{
if ( _log.isDebugEnabled() )
{
_log.debug( "Removing session attribute " + attrName + " (" + attr
+ ") because its ClassLoader is being bounced." );
}
session.removeAttribute( attrName );
}
}
}
//
// Clear all caches of methods, etc.
//
ClassLevelCache.clearAll();
//
// Clear out all registered modules from the ActionServlet.
//
ActionServlet actionServlet = InternalUtils.getActionServlet( getServletContext() );
if ( actionServlet instanceof AutoRegisterActionServlet )
{
( ( AutoRegisterActionServlet ) actionServlet ).clearRegisteredModules();
}
//
// Bounce the classloader.
//
init( getConfig(), getPreviousHandler(), getServletContext() );
}
}
}
public ClassLoader getClassLoader()
{
if ( _pageFlowClassLoader != null )
{
synchronized ( this )
{
return _pageFlowClassLoader;
}
}
return _pageFlowClassLoader;
}
public boolean isReloadEnabled()
{
return _pageFlowClassLoader != null;
}
public ReloadableClassHandler getRegisteredReloadableClassHandler()
{
return ( ReloadableClassHandler ) super.getRegisteredHandler();
}
}