package org.jboss.seam.deployment; import java.io.File; import java.lang.reflect.Constructor; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.Collections; import java.util.Map; import java.util.Set; import javax.servlet.ServletContext; import org.jboss.seam.contexts.Contexts; import org.jboss.seam.util.Reflections; /** * A deployment strategy for hot deployable Java Seam components * * @author Pete Muir * */ public class HotDeploymentStrategy extends DeploymentStrategy { /** * The default path at which hot deployable Seam components are placed */ public static final String DEFAULT_HOT_DEPLOYMENT_DIRECTORY_PATH = "/WEB-INF/dev"; /** * The contextual variable name this deployment strategy is made available at * during Seam startup. */ public static final String NAME = "hotDeploymentStrategy"; /** * The key under which to list extra hot deployment directories * * This can be specified as a System property or in * /META-INF/seam-deployment.properties */ //public static final String HOT_DEPLOY_DIRECTORIES_KEY = "org.jboss.seam.deployment.hotDeploymentDirectories"; /** * The key under which to list extra deployment handlers. * * This can be specified as a System property or in * /META-INF/seam-deployment.properties */ public static final String HANDLERS_KEY = "org.jboss.seam.deployment.hotDeploymentHandlers"; private ClassLoader hotDeployClassLoader; private ComponentDeploymentHandler componentDeploymentHandler; private AnnotationDeploymentHandler annotationDeploymentHandler; private ClassLoader classLoader; private ServletContext servletContext; /** * @param classLoader The parent classloader of the hot deployment classloader * @param hotDeployDirectory The directory in which hot deployable Seam * components are placed */ public HotDeploymentStrategy(ClassLoader classLoader, File hotDeployDirectory, ServletContext servletContext, boolean enabled) { if (enabled) { this.servletContext=servletContext; this.classLoader = Thread.currentThread().getContextClassLoader(); if (hotDeployDirectory != null && hotDeployDirectory.exists()) { initHotDeployClassLoader(classLoader, hotDeployDirectory); componentDeploymentHandler = new ComponentDeploymentHandler(); getDeploymentHandlers().put(ComponentDeploymentHandler.NAME, componentDeploymentHandler); annotationDeploymentHandler = new AnnotationDeploymentHandler(new SeamDeploymentProperties(classLoader).getPropertyValues(AnnotationDeploymentHandler.ANNOTATIONS_KEY), classLoader); getDeploymentHandlers().put(AnnotationDeploymentHandler.NAME, annotationDeploymentHandler); } } } private void initHotDeployClassLoader(ClassLoader classLoader, File hotDeployDirectory) { try { URL url = hotDeployDirectory.toURL(); URL[] urls = { url }; hotDeployClassLoader = new URLClassLoader(urls, classLoader); getFiles().add(hotDeployDirectory); } catch (MalformedURLException mue) { throw new RuntimeException(mue); } } /** * It is both enabled and the classpath was detected. Admittedly, * this seems like a redundant confirmation. */ public boolean available() { return isEnabled() && isHotDeployClassLoaderEnabled(); } public boolean isEnabled() { return classLoader != null; } public boolean isHotDeployClassLoaderEnabled() { return hotDeployClassLoader != null; } @Override protected String getDeploymentHandlersKey() { return HANDLERS_KEY; } /** * Get all hot deployable paths */ public File[] getHotDeploymentPaths() { return getFiles().toArray(new File[0]); } /** * Return true if the component is from a hot deployment classloader */ public boolean isFromHotDeployClassLoader(Class componentClass) { return componentClass.getClassLoader() == hotDeployClassLoader; } /** * Dynamically instantiate a {@link HotDeploymentStrategy} * * Needed to prevent dependency on optional librarires * @param className The strategy to use * @param classLoader The classloader to use with this strategy * @param hotDeployDirectory The directory which contains hot deployable * Seam components */ public static HotDeploymentStrategy createInstance(String className, ClassLoader classLoader, File hotDeployDirectory, ServletContext servletContext, boolean enabled) { try { Class initializer = Reflections.classForName(className); Constructor ctr = initializer.getConstructor(ClassLoader.class, File.class, ServletContext.class, boolean.class); return (HotDeploymentStrategy) ctr.newInstance(classLoader, hotDeployDirectory, servletContext, enabled); } catch (Exception e) { throw new IllegalArgumentException("No such deployment strategy " + className, e); } } @Override public ClassLoader getClassLoader() { return hotDeployClassLoader != null ? hotDeployClassLoader : classLoader; } /** * Get all Components which the strategy has scanned and handled */ public Set<ClassDescriptor> getScannedComponentClasses() { return Collections.unmodifiableSet(componentDeploymentHandler.getClasses()); } public Map<String, Set<Class<?>>> getAnnotatedClasses() { return Collections.unmodifiableMap(annotationDeploymentHandler.getClassMap()); } @Override public void scan() { getScanner().scanDirectories(getFiles().toArray(new File[0])); postScan(); } public static HotDeploymentStrategy instance() { if (Contexts.getEventContext().isSet(NAME)) { return (HotDeploymentStrategy) Contexts.getEventContext().get(NAME); } return null; } @Override public ServletContext getServletContext() { return servletContext; } }