org.jboss.seam.init.Initialization.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.seam.init.Initialization.java

Source

/*
 * JBoss, Home of Professional Open Source
 * 
 * Distributable under LGPL license. See terms of license at gnu.org.
 */
package org.jboss.seam.init;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import org.dom4j.Attribute;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.Seam;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Namespace;
import org.jboss.seam.annotations.Role;
import org.jboss.seam.annotations.Roles;
import org.jboss.seam.bpm.Jbpm;
import org.jboss.seam.contexts.Context;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.contexts.ServletLifecycle;
import org.jboss.seam.core.Expressions;
import org.jboss.seam.core.Init;
import org.jboss.seam.deployment.ClassDescriptor;
import org.jboss.seam.deployment.FileDescriptor;
import org.jboss.seam.deployment.HotDeploymentStrategy;
import org.jboss.seam.deployment.SeamDeploymentProperties;
import org.jboss.seam.deployment.StandardDeploymentStrategy;
import org.jboss.seam.deployment.WarRootDeploymentStrategy;
import org.jboss.seam.exception.Exceptions;
import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
import org.jboss.seam.navigation.Pages;
import org.jboss.seam.util.Conversions;
import org.jboss.seam.util.Naming;
import org.jboss.seam.util.Reflections;
import org.jboss.seam.util.Resources;
import org.jboss.seam.util.Strings;
import org.jboss.seam.util.XML;

/**
 * Builds configuration metadata when Seam first initialized.
 * 
 * @author Gavin King
 * @author <a href="mailto:theute@jboss.org">Thomas Heute</a>
 * @author Pete Muir
 */
public class Initialization {
    public static final String COMPONENT_NAMESPACE = "http://jboss.com/products/seam/components";
    public static final String COMPONENT_SUFFIX = ".component";
    public static final String DUPLICATE_JARS_PATTERNS = "org.jboss.seam.init.duplicateJarsPatterns";

    private static final LogProvider log = Logging.getLogProvider(Initialization.class);

    private ServletContext servletContext;
    private Map<String, Conversions.PropertyValue> properties = new HashMap<String, Conversions.PropertyValue>();
    private Map<String, Set<ComponentDescriptor>> componentDescriptors = new HashMap<String, Set<ComponentDescriptor>>();
    private List<FactoryDescriptor> factoryDescriptors = new ArrayList<FactoryDescriptor>();
    private Set<Class> installedComponentClasses = new HashSet<Class>();
    //private Set<String> importedPackages = new HashSet<String>();
    private Map<String, NamespaceDescriptor> namespaceMap = new HashMap<String, NamespaceDescriptor>();
    private NamespacePackageResolver namespacePackageResolver = new NamespacePackageResolver();
    private Map<String, EventListenerDescriptor> eventListenerDescriptors = new HashMap<String, EventListenerDescriptor>();
    private Collection<String> globalImports = new ArrayList<String>();

    private StandardDeploymentStrategy standardDeploymentStrategy;
    private HotDeploymentStrategy hotDeploymentStrategy;
    private WarRootDeploymentStrategy warRootDeploymentStrategy;

    private File hotDeployDirectory;
    private File warRoot;

    private Set<String> nonPropertyAttributes = new HashSet<String>();

    {
        nonPropertyAttributes.add("name");
        nonPropertyAttributes.add("installed");
        nonPropertyAttributes.add("scope");
        nonPropertyAttributes.add("startup");
        nonPropertyAttributes.add("startupDepends");
        nonPropertyAttributes.add("class");
        nonPropertyAttributes.add("jndi-name");
        nonPropertyAttributes.add("precedence");
        nonPropertyAttributes.add("auto-create");
    }

    public Initialization(ServletContext servletContext) {
        this.servletContext = servletContext;
        this.warRoot = getRealFile(servletContext, "/");
        this.hotDeployDirectory = getRealFile(servletContext,
                HotDeploymentStrategy.DEFAULT_HOT_DEPLOYMENT_DIRECTORY_PATH);
    }

    public Initialization create() {
        standardDeploymentStrategy = new StandardDeploymentStrategy(Thread.currentThread().getContextClassLoader());
        standardDeploymentStrategy.scan();
        addNamespaces();
        initComponentsFromXmlDocument("/WEB-INF/components.xml");
        initComponentsFromXmlDocument("/WEB-INF/events.xml"); //deprecated
        initComponentsFromXmlDocuments();
        initPropertiesFromServletContext();
        initPropertiesFromResource();
        initJndiProperties();
        return this;
    }

    private void initComponentsFromXmlDocuments() {
        Enumeration<URL> resources;
        try {
            resources = Thread.currentThread().getContextClassLoader().getResources("META-INF/components.xml");
        } catch (IOException ioe) {
            throw new RuntimeException("error scanning META-INF/components.xml files", ioe);
        }

        List<String> seenDocuments = new ArrayList<String>();

        Properties replacements = getReplacements();
        Set<Pattern> duplicateJarPatterns = new HashSet<Pattern>();
        for (String duplicateJarRegex : new HashSet<String>(
                new SeamDeploymentProperties(Thread.currentThread().getContextClassLoader())
                        .getPropertyValues(DUPLICATE_JARS_PATTERNS))) {
            duplicateJarPatterns.add(Pattern.compile(duplicateJarRegex));
        }
        while (resources.hasMoreElements()) {
            URL url = resources.nextElement();
            boolean skip = false;
            String path = url.getPath();
            String prefixPattern = "^(\\S*/)([\\S^/]*.jar)(\\S*)$";
            Pattern pattern = Pattern.compile(prefixPattern);
            Matcher matcher = pattern.matcher(path);
            String jarName;
            String fileName;
            if (matcher.matches()) {
                jarName = matcher.group(2);
                fileName = matcher.group(3);
                Set<String> documentNames = new HashSet<String>();
                for (Pattern duplicateJarPattern : duplicateJarPatterns) {
                    Matcher duplicateMatcher = duplicateJarPattern.matcher(jarName);

                    if (duplicateMatcher.matches()) {
                        jarName = duplicateMatcher.group(1);
                    }

                    documentNames.add(jarName + fileName);
                }
                for (String documentName : documentNames) {
                    if (seenDocuments.contains(documentName)) {
                        skip = true;
                    }
                }
                seenDocuments.addAll(documentNames);
            }

            if (!skip) {
                try {
                    InputStream stream = url.openStream();

                    log.debug("reading " + url);
                    try {
                        installComponentsFromXmlElements(XML.getRootElement(stream), replacements);
                    } finally {
                        Resources.closeStream(stream);
                    }
                } catch (Exception e) {
                    throw new RuntimeException("error while reading " + url, e);
                }
            } else {
                log.trace("skipping read of duplicate components.xml " + url);
            }
        }

    }

    private void initComponentsFromXmlDocument(String resource) {
        InputStream stream = Resources.getResourceAsStream(resource, servletContext);
        if (stream != null) {
            log.info("reading " + resource);
            try {
                installComponentsFromXmlElements(XML.getRootElement(stream), getReplacements());
            } catch (Exception e) {
                throw new RuntimeException("error while reading /WEB-INF/components.xml", e);
            } finally {
                Resources.closeStream(stream);
            }
        }
    }

    private Properties getReplacements() {
        InputStream replaceStream = null;
        try {
            Properties replacements = new Properties();
            replaceStream = Resources.getResourceAsStream("/components.properties", servletContext);
            if (replaceStream != null) {
                replacements.load(replaceStream);
            }
            return replacements;
        } catch (IOException ioe) {
            throw new RuntimeException("error reading components.properties", ioe);
        } finally {
            Resources.closeStream(replaceStream);
        }
    }

    private List<Element> elements(Element rootElement, String name) {
        return rootElement.elements(name);
    }

    @SuppressWarnings("unchecked")
    private void installComponentsFromXmlElements(Element rootElement, Properties replacements)
            throws DocumentException, ClassNotFoundException {
        /*List<Element> importJavaElements = rootElement.elements("import-java-package");
          for (Element importElement : importJavaElements)
          {
        String pkgName = importElement.getTextTrim();
        importedPackages.add(pkgName);
        addNamespace( Package.getPackage(pkgName) );
          }*/

        for (Element importElement : elements(rootElement, "import")) {
            globalImports.add(importElement.getTextTrim());
        }

        for (Element component : elements(rootElement, "component")) {
            installComponentFromXmlElement(component, component.attributeValue("name"),
                    component.attributeValue("class"), replacements);
        }

        for (Element factory : elements(rootElement, "factory")) {
            installFactoryFromXmlElement(factory);
        }

        for (Element event : elements(rootElement, "event")) {
            installEventListenerFromXmlElement(event);
        }

        for (Element elem : (List<Element>) rootElement.elements()) {
            String ns = elem.getNamespace().getURI();
            NamespaceDescriptor nsInfo = resolveNamespace(ns);
            if (nsInfo == null) {
                if (!ns.equals(COMPONENT_NAMESPACE)) {
                    log.warn("namespace declared in components.xml does not resolve to a package: " + ns);
                }
            } else {
                String name = elem.attributeValue("name");
                String elemName = toCamelCase(elem.getName(), true);

                String className = elem.attributeValue("class");
                if (className == null) {
                    for (String packageName : nsInfo.getPackageNames()) {
                        try {
                            // Try each of the packages in the namespace descriptor for a matching class
                            className = packageName + '.' + elemName;
                            Reflections.classForName(className);
                            break;
                        } catch (ClassNotFoundException ex) {
                            className = null;
                        }
                    }
                }

                try {
                    //get the class implied by the namespaced XML element name
                    Class<Object> clazz = Reflections.classForName(className);
                    Name nameAnnotation = clazz.getAnnotation(Name.class);

                    //if the name attribute is not explicitly specified in the XML,
                    //imply the name from the @Name annotation on the class implied
                    //by the XML element name
                    if (name == null && nameAnnotation != null) {
                        name = nameAnnotation.value();
                    }

                    //if this class already has the @Name annotation, the XML element 
                    //is just adding configuration to the existing component, don't
                    //add another ComponentDescriptor (this is super-important to
                    //allow overriding!)
                    if (nameAnnotation != null && nameAnnotation.value().equals(name)) {
                        Install install = clazz.getAnnotation(Install.class);
                        if (install == null || install.value()) {
                            className = null;
                        }
                    }
                } catch (ClassNotFoundException cnfe) {
                    //there is no class implied by the XML element name so the
                    //component must be defined some other way, assume that we are
                    //just adding configuration, don't add a ComponentDescriptor 
                    //TODO: this is problematic, it results in elements getting 
                    //      ignored when mis-spelled or given the wrong namespace!!
                    className = null;
                } catch (Exception e) {
                    throw new RuntimeException("Error loading element " + elemName + " with component name " + name
                            + " and component class " + className);
                }

                //finally, if we could not get the name from the XML name attribute,
                //or from an @Name annotation on the class, imply it
                if (name == null) {
                    String prefix = nsInfo.getComponentPrefix();
                    String componentName = toCamelCase(elem.getName(), false);
                    name = Strings.isEmpty(prefix) ? componentName : prefix + '.' + componentName;
                }

                installComponentFromXmlElement(elem, name, className, replacements);
            }
        }
    }

    private NamespaceDescriptor resolveNamespace(String namespace) {
        if (Strings.isEmpty(namespace) || namespace.equals(COMPONENT_NAMESPACE)) {
            return null;
        }

        NamespaceDescriptor descriptor = namespaceMap.get(namespace);
        if (descriptor == null) {
            try {
                String packageName = namespacePackageResolver.resolve(namespace);
                descriptor = new NamespaceDescriptor(namespace, packageName);
                namespaceMap.put(namespace, descriptor);
            } catch (Exception e) {
                log.warn("Could not determine java package for namespace: " + namespace, e);
            }
        }

        return descriptor;
    }

    @SuppressWarnings("unchecked")
    private void installEventListenerFromXmlElement(Element event) {
        String type = event.attributeValue("type");
        if (type == null) {
            throw new IllegalArgumentException("must specify type for <event/> declaration");
        }
        EventListenerDescriptor eventListener = eventListenerDescriptors.get(type);
        if (eventListener == null) {
            eventListener = new EventListenerDescriptor(type);
            eventListenerDescriptors.put(type, eventListener);
        }

        List<Element> actions = event.elements("action");
        for (Element action : actions) {
            String execute = action.attributeValue("execute");
            if (execute == null) {
                String actionExpression = action.attributeValue("expression");
                if (actionExpression != null) {
                    log.warn("<action expression=\"" + actionExpression
                            + "\" /> has been deprecated, use <action execute=\"" + actionExpression
                            + "\" /> instead");
                    execute = actionExpression;
                } else {
                    throw new IllegalArgumentException("must specify execute for <action/> declaration");
                }
            }
            eventListener.getListenerMethodBindings().add(execute);
        }
    }

    private void installFactoryFromXmlElement(Element factory) {
        String scopeName = factory.attributeValue("scope");
        String name = factory.attributeValue("name");
        if (name == null) {
            throw new IllegalArgumentException("must specify name in <factory/> declaration");
        }
        String method = factory.attributeValue("method");
        String value = factory.attributeValue("value");
        if (method == null && value == null) {
            throw new IllegalArgumentException(
                    "must specify either method or value in <factory/> declaration for variable: " + name);
        }
        ScopeType scope = scopeName == null ? ScopeType.UNSPECIFIED : ScopeType.valueOf(scopeName.toUpperCase());
        boolean autoCreate = "true".equals(factory.attributeValue("auto-create"));
        factoryDescriptors.add(new FactoryDescriptor(name, scope, method, value, autoCreate));
    }

    private String replace(String value, Properties replacements) {
        if (value.startsWith("@")) {
            value = replacements.getProperty(value.substring(1, value.length() - 1));
        }
        return value;
    }

    @SuppressWarnings("unchecked")
    private void installComponentFromXmlElement(Element component, String name, String className,
            Properties replacements) throws ClassNotFoundException {
        String installText = component.attributeValue("installed");
        boolean installed = false;
        if (installText == null || "true".equals(replace(installText, replacements))) {
            installed = true;
        }

        String scopeName = component.attributeValue("scope");
        String jndiName = component.attributeValue("jndi-name");
        String precedenceString = component.attributeValue("precedence");
        int precedence = precedenceString == null ? Install.APPLICATION : Integer.valueOf(precedenceString);
        ScopeType scope = scopeName == null ? null : ScopeType.valueOf(scopeName.toUpperCase());
        String autocreateAttribute = component.attributeValue("auto-create");
        Boolean autoCreate = autocreateAttribute == null ? null : "true".equals(autocreateAttribute);
        String startupAttribute = component.attributeValue("startup");
        Boolean startup = startupAttribute == null ? null : "true".equals(startupAttribute);
        String startupDependsAttribute = component.attributeValue("startupDepends");
        String[] startupDepends = startupDependsAttribute == null ? new String[0]
                : startupDependsAttribute.split(" ");

        if (className != null) {
            Class<?> clazz = getClassUsingImports(className);

            if (name == null) {
                if (!clazz.isAnnotationPresent(Name.class)) {
                    throw new IllegalArgumentException(
                            "Component class must have @Name annotation or name must be specified in components.xml: "
                                    + clazz.getName());
                }

                name = clazz.getAnnotation(Name.class).value();
            }

            ComponentDescriptor descriptor = new ComponentDescriptor(name, clazz, scope, autoCreate, startup,
                    startupDepends, jndiName, installed, precedence);
            addComponentDescriptor(descriptor);
            installedComponentClasses.add(clazz);
        } else if (name == null) {
            throw new IllegalArgumentException("must specify either class or name in <component/> declaration");
        }

        for (Element prop : (List<Element>) component.elements()) {
            String propName = prop.attributeValue("name");
            if (propName == null) {
                propName = prop.getQName().getName();
            }
            String qualifiedPropName = name + '.' + toCamelCase(propName, false);
            properties.put(qualifiedPropName, getPropertyValue(prop, qualifiedPropName, replacements));
        }

        for (Attribute prop : (List<Attribute>) component.attributes()) {
            String attributeName = prop.getName();
            if (isProperty(prop.getNamespaceURI(), attributeName)) {
                String qualifiedPropName = name + '.' + toCamelCase(prop.getQName().getName(), false);
                Conversions.PropertyValue propValue = null;
                try {
                    propValue = getPropertyValue(prop, replacements);
                    properties.put(qualifiedPropName, propValue);
                } catch (Exception ex) {
                    throw new IllegalArgumentException(String.format(
                            "Exception setting property %s on component %s.  Expression %s evaluated to %s.",
                            qualifiedPropName, name, prop.getValue(), propValue), ex);

                }
            }
        }
    }

    /**
     * component properties are non-namespaced and not in the reserved attribute list
     */
    private boolean isProperty(String namespaceURI, String attributeName) {
        return (namespaceURI == null || namespaceURI.length() == 0)
                && !nonPropertyAttributes.contains(attributeName);
    }

    private Class<?> getClassUsingImports(String className) throws ClassNotFoundException {
        Class<?> clazz = null;
        /*try
        {*/
        clazz = Reflections.classForName(className);
        /*}
        catch (ClassNotFoundException cnfe)
        {
           for (String pkg : importedPackages)
           {
        try
        {
           clazz = Reflections.classForName(pkg + '.' + className);
           break;
        }
        catch (Exception e)
        {
        }
           }
           if (clazz == null) throw cnfe;
        }*/
        return clazz;
    }

    private void addComponentDescriptor(ComponentDescriptor descriptor) {
        String name = descriptor.getName();

        Set<ComponentDescriptor> set = componentDescriptors.get(name);
        if (set == null) {
            set = new TreeSet<ComponentDescriptor>(new ComponentDescriptor.PrecedenceComparator());
            componentDescriptors.put(name, set);
        }
        if (!set.isEmpty()) {
            log.info("two components with same name, higher precedence wins: " + name);
        }
        if (!set.add(descriptor)) {
            ComponentDescriptor other = null;
            for (ComponentDescriptor d : set) {
                if (descriptor.compareTo(d) == 0) {
                    other = d;
                    break;
                }
            }

            throw new IllegalStateException("Two components with the same name and precedence - "
                    + "component name: " + name + ", component classes: " + descriptor.getComponentClass().getName()
                    + ", " + (other != null ? other.getComponentClass().getName() : "<unknown>"));
        }
    }

    private Conversions.PropertyValue getPropertyValue(Attribute prop, Properties replacements) {
        return new Conversions.FlatPropertyValue(trimmedText(prop, replacements));
    }

    @SuppressWarnings("unchecked")
    private Conversions.PropertyValue getPropertyValue(Element prop, String propName, Properties replacements) {
        String typeName = prop.attributeValue("type");
        Class type = null;
        try {
            if (typeName != null) {
                type = Class.forName(typeName);
            }
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Cannot find class " + typeName + " when setting up property " + propName);
        }

        List<Element> keyElements = prop.elements("key");
        List<Element> valueElements = prop.elements("value");

        if (valueElements.isEmpty() && keyElements.isEmpty()) {
            return new Conversions.FlatPropertyValue(trimmedText(prop, propName, replacements));
        } else if (keyElements.isEmpty()) {
            // a list-like structure
            int len = valueElements.size();
            String[] values = new String[len];
            for (int i = 0; i < len; i++) {
                values[i] = trimmedText(valueElements.get(i), propName, replacements);
            }
            return new Conversions.MultiPropertyValue(values, type);
        } else {
            // a map-like structure
            if (valueElements.size() != keyElements.size()) {
                throw new IllegalArgumentException("value elements must match key elements: " + propName);
            }
            Map<String, String> keyedValues = new LinkedHashMap<String, String>();
            for (int i = 0; i < keyElements.size(); i++) {
                String key = trimmedText(keyElements.get(i), propName, replacements);
                String value = trimmedText(valueElements.get(i), propName, replacements);
                keyedValues.put(key, value);
            }
            return new Conversions.AssociativePropertyValue(keyedValues, type);
        }
    }

    private String trimmedText(Element element, String propName, Properties replacements) {
        String text = element.getTextTrim();
        if (text == null) {
            throw new IllegalArgumentException("property value must be specified in element body: " + propName);
        }
        return replace(text, replacements);
    }

    private String trimmedText(Attribute attribute, Properties replacements) {
        return replace(attribute.getText(), replacements);
    }

    public Initialization setProperty(String name, Conversions.PropertyValue value) {
        properties.put(name, value);
        return this;
    }

    public Initialization init() {
        log.info("initializing Seam");
        if (standardDeploymentStrategy == null) {
            throw new IllegalStateException("No deployment strategy!");
        }
        ServletLifecycle.beginInitialization();
        Contexts.getApplicationContext().set(Component.PROPERTIES, properties);
        hotDeploymentStrategy = createHotDeployment(Thread.currentThread().getContextClassLoader());
        scanForComponents();
        addComponent(new ComponentDescriptor(Init.class), Contexts.getApplicationContext());
        Init init = (Init) Component.getInstance(Init.class, ScopeType.APPLICATION);
        init.setHotDeployPaths(hotDeploymentStrategy.getHotDeploymentPaths());
        ComponentDescriptor desc = findDescriptor(Jbpm.class);
        if (desc != null && desc.isInstalled()) {
            init.setJbpmInstalled(true);
        }
        init.checkDefaultInterceptors();
        init.setTimestamp(System.currentTimeMillis());
        addSpecialComponents(init);

        // Add the war root deployment
        warRootDeploymentStrategy = new WarRootDeploymentStrategy(Thread.currentThread().getContextClassLoader(),
                warRoot);
        warRootDeploymentStrategy.scan();

        // Make the deployment strategies available in the contexts. This gives 
        // access to custom deployment handlers for processing custom annotations
        // etc.
        Contexts.getEventContext().set(StandardDeploymentStrategy.NAME, standardDeploymentStrategy);
        Contexts.getEventContext().set(HotDeploymentStrategy.NAME, hotDeploymentStrategy);
        Contexts.getEventContext().set(WarRootDeploymentStrategy.NAME, warRootDeploymentStrategy);

        if (hotDeploymentStrategy.isEnabled()) {
            hotDeploymentStrategy.scan();
            if (hotDeploymentStrategy.isHotDeployClassLoaderEnabled()) {
                installHotDeployableComponents();
            }
        }

        installComponents(init);

        for (String globalImport : globalImports) {
            init.importNamespace(globalImport);
        }

        ServletLifecycle.endInitialization();
        log.info("done initializing Seam");
        return this;
    }

    public void redeploy(HttpServletRequest request) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        if (lock.tryLock(500, TimeUnit.MILLISECONDS)) {
            try {
                hotDeploymentStrategy = createHotDeployment(Thread.currentThread().getContextClassLoader());
                if (hotDeploymentStrategy.isEnabled()) {
                    hotDeploymentStrategy.scan();
                    Init init = (Init) ServletLifecycle.getServletContext()
                            .getAttribute(Seam.getComponentName(Init.class));

                    if (init.getTimestamp() < hotDeploymentStrategy.getTimestamp()) {
                        log.info("redeploying");
                        ServletLifecycle.beginReinitialization(request);
                        Seam.clearComponentNameCache();
                        for (String name : init.getHotDeployableComponents()) {
                            Component component = Component.forName(name);
                            if (component != null) {
                                ScopeType scope = component.getScope();
                                if (scope != ScopeType.STATELESS && scope.isContextActive()) {
                                    scope.getContext().remove(name);
                                }
                                init.removeObserverMethods(component);
                            }
                            Contexts.getApplicationContext().remove(name + COMPONENT_SUFFIX);
                        }

                        if (hotDeploymentStrategy.isHotDeployClassLoaderEnabled()) {
                            installHotDeployableComponents();
                        }
                        Contexts.getEventContext().set(HotDeploymentStrategy.NAME, hotDeploymentStrategy);
                        init.setTimestamp(System.currentTimeMillis());
                        installComponents(init);
                        ServletLifecycle.endReinitialization();
                        log.info("done redeploying");
                    }

                    WarRootDeploymentStrategy warRootDeploymentStrategy = new WarRootDeploymentStrategy(
                            Thread.currentThread().getContextClassLoader(), warRoot);
                    warRootDeploymentStrategy.scan();
                    Pages pages = (Pages) ServletLifecycle.getServletContext()
                            .getAttribute(Seam.getComponentName(Pages.class));
                    if (pages != null) {
                        pages.initialize(warRootDeploymentStrategy.getDotPageDotXmlFileNames());
                    }

                    ServletLifecycle.getServletContext().removeAttribute(Seam.getComponentName(Exceptions.class));
                }
            } finally {
                lock.unlock();
            }
        }
    }

    private void installHotDeployableComponents() {
        for (ClassDescriptor classDescriptor : hotDeploymentStrategy.getScannedComponentClasses()) {
            Class<?> scannedClass = classDescriptor.getClazz();
            installScannedComponentAndRoles(scannedClass);
        }
    }

    private HotDeploymentStrategy createHotDeployment(ClassLoader classLoader) {
        if (isGroovyPresent()) {
            log.debug("Using Java + Groovy hot deploy");
            return HotDeploymentStrategy.createInstance("org.jboss.seam.deployment.GroovyHotDeploymentStrategy",
                    classLoader, hotDeployDirectory, isDebugEnabled());
        } else {
            log.debug("Using Java hot deploy");
            return new HotDeploymentStrategy(classLoader, hotDeployDirectory, isDebugEnabled());
        }
    }

    private static File getRealFile(ServletContext servletContext, String path) {
        String realPath = servletContext.getRealPath(path);
        if (realPath == null) //WebLogic!
        {
            try {
                URL resourcePath = servletContext.getResource(path);
                if ((resourcePath != null) && (resourcePath.getProtocol().equals("file"))) {
                    realPath = resourcePath.getPath();
                } else {
                    log.warn("Unable to determine real path from servlet context for \"" + path
                            + "\" path does not exist.");
                }
            } catch (MalformedURLException e) {
                log.warn("Unable to determine real path from servlet context for : " + path);
                log.debug("Caused by MalformedURLException", e);
            }

        }

        if (realPath != null) {
            File file = new File(realPath);
            if (file.exists()) {
                return file;
            }
        }
        return null;
    }

    private static boolean isDebugEnabled() {
        return Resources.getResource("META-INF/debug.xhtml", null) != null;
    }

    private static boolean isGroovyPresent() {
        try {
            Reflections.classForName("groovy.lang.GroovyObject");
            return true;
        } catch (ClassNotFoundException e) {
            //groovy is not there
            return false;
        }
    }

    private void scanForComponents() {
        for (ClassDescriptor classDescriptor : standardDeploymentStrategy.getAnnotatedComponents()) {
            Class<?> scannedClass = classDescriptor.getClazz();
            installScannedComponentAndRoles(scannedClass);
        }

        for (FileDescriptor fileDescriptor : standardDeploymentStrategy.getXmlComponents()) {
            installComponentsFromDescriptor(fileDescriptor, standardDeploymentStrategy.getClassLoader());
        }
    }

    private static String classFilenameFromDescriptor(String descriptor) {
        int pos = descriptor.lastIndexOf(".component.xml");
        if (pos == -1) {
            return null;
        }

        return descriptor.substring(0, pos).replace('/', '.').replace('\\', '.');
    }

    private void installComponentsFromDescriptor(FileDescriptor fileDescriptor, ClassLoader loader) {
        //note: this is correct, we do not need to scan other classloaders!
        InputStream stream = null;
        try {
            stream = fileDescriptor.getUrl().openStream();
        } catch (IOException e) {
            // No-op
        }
        if (stream != null) {
            try {
                Properties replacements = getReplacements();
                Element root = XML.getRootElement(stream);
                if (root.getName().equals("components")) {
                    installComponentsFromXmlElements(root, replacements);
                } else {
                    installComponentFromXmlElement(root, root.attributeValue("name"),
                            classFilenameFromDescriptor(fileDescriptor.getName()), replacements);
                }
            } catch (Exception e) {
                throw new RuntimeException("error while reading " + fileDescriptor.getName(), e);
            } finally {
                Resources.closeStream(stream);
            }
        }
    }

    private void installScannedComponentAndRoles(Class<?> scannedClass) {
        try {
            if (scannedClass.isAnnotationPresent(Name.class)) {
                addComponentDescriptor(new ComponentDescriptor(scannedClass));
            }
            if (scannedClass.isAnnotationPresent(Role.class)) {
                installRole(scannedClass, scannedClass.getAnnotation(Role.class));
            }
            if (scannedClass.isAnnotationPresent(Roles.class)) {
                Role[] roles = scannedClass.getAnnotation(Roles.class).value();
                for (Role role : roles) {
                    installRole(scannedClass, role);
                }
            }
        } catch (TypeNotPresentException e) {
            log.info("Failed to install " + scannedClass.getName() + ": " + e.getMessage());
        }
    }

    private void installRole(Class<?> scannedClass, Role role) {
        ScopeType scope = Seam.getComponentRoleScope(scannedClass, role);
        addComponentDescriptor(new ComponentDescriptor(role.name(), scannedClass, scope));
    }

    private void addNamespace(Package pkg) {
        if (pkg != null) {
            Namespace ns = pkg.getAnnotation(Namespace.class);
            if (ns != null) {
                log.info("Namespace: " + ns.value() + ", package: " + pkg.getName() + ", prefix: " + ns.prefix());

                NamespaceDescriptor descriptor = namespaceMap.get(ns.value());
                if (descriptor != null) {
                    descriptor.addPackageName(pkg.getName());
                } else {
                    namespaceMap.put(ns.value(), new NamespaceDescriptor(ns, pkg));
                }
            }
        }
    }

    private void addNamespaces() {
        for (Package pkg : standardDeploymentStrategy.getScannedNamespaces()) {
            addNamespace(pkg);
        }
    }

    private void initPropertiesFromServletContext() {
        Enumeration params = servletContext.getInitParameterNames();
        while (params.hasMoreElements()) {
            String name = (String) params.nextElement();
            properties.put(name, new Conversions.FlatPropertyValue(servletContext.getInitParameter(name)));
        }
    }

    private void initPropertiesFromResource() {
        Properties props = loadFromResource("/seam.properties");
        for (Map.Entry me : props.entrySet()) {
            properties.put((String) me.getKey(), new Conversions.FlatPropertyValue((String) me.getValue()));
        }
    }

    private void initJndiProperties() {
        Properties jndiProperties = new Properties();
        jndiProperties.putAll(loadFromResource("/jndi.properties"));
        jndiProperties.putAll(loadFromResource("/seam-jndi.properties"));
        Naming.setInitialContextProperties(jndiProperties);
    }

    private Properties loadFromResource(String resource) {
        Properties props = new Properties();
        InputStream stream = Resources.getResourceAsStream(resource, servletContext);
        if (stream != null) {
            try {
                log.info("reading properties from: " + resource);
                try {
                    props.load(stream);
                } catch (IOException ioe) {
                    log.error("could not read " + resource, ioe);
                }
            } finally {
                Resources.closeStream(stream);
            }
        } else {
            log.debug("not found: " + resource);
        }
        return props;
    }

    protected ComponentDescriptor findDescriptor(Class<?> componentClass) {
        for (Set<ComponentDescriptor> components : componentDescriptors.values()) {
            for (ComponentDescriptor component : components) {
                if (component.getComponentClass().equals(componentClass)) {
                    return component;
                }
            }
        }
        return null;
    }

    private void addSpecialComponents(Init init) {
    }

    private void installComponents(Init init) {
        log.info("Installing components...");
        Context context = Contexts.getApplicationContext();

        DependencyManager manager = new DependencyManager(componentDescriptors);

        Set<ComponentDescriptor> installable = manager.installedSet();
        for (ComponentDescriptor componentDescriptor : installable) {
            String compName = componentDescriptor.getName() + COMPONENT_SUFFIX;

            if (!context.isSet(compName)) {
                addComponent(componentDescriptor, context);

                if (componentDescriptor.isAutoCreate()) {
                    init.addAutocreateVariable(componentDescriptor.getName());
                }

                if (componentDescriptor.isFilter()) {
                    init.addInstalledFilter(componentDescriptor.getName());
                }

                if (componentDescriptor.isResourceProvider()) {
                    if (!componentDescriptor.getScope().equals(ScopeType.APPLICATION)) {
                        throw new RuntimeException("Resource providers must be application-scoped components");
                    }

                    init.addResourceProvider(componentDescriptor.getName());
                }

                if (componentDescriptor.isPermissionResolver()) {
                    init.addPermissionResolver(componentDescriptor.getName());
                }
            }
        }

        for (FactoryDescriptor factoryDescriptor : factoryDescriptors) {
            if (factoryDescriptor.isValueBinding()) {
                init.addFactoryValueExpression(factoryDescriptor.getName(), factoryDescriptor.getValue(),
                        factoryDescriptor.getScope());
            } else {
                init.addFactoryMethodExpression(factoryDescriptor.getName(), factoryDescriptor.getMethod(),
                        factoryDescriptor.getScope());
            }
            if (factoryDescriptor.isAutoCreate()) {
                init.addAutocreateVariable(factoryDescriptor.getName());
            }
        }

        for (EventListenerDescriptor listenerDescriptor : eventListenerDescriptors.values()) {
            for (String expression : listenerDescriptor.getListenerMethodBindings()) {
                init.addObserverMethodExpression(listenerDescriptor.getType(),
                        Expressions.instance().createMethodExpression(expression));
            }
        }
    }

    /**
     * This actually creates a real Component and should only be called when
     * we want to install a component
     */
    protected void addComponent(ComponentDescriptor descriptor, Context context) {
        String name = descriptor.getName();
        String componentName = name + COMPONENT_SUFFIX;
        try {
            Component component = new Component(descriptor.getComponentClass(), name, descriptor.getScope(),
                    descriptor.isStartup(), descriptor.getStartupDependencies(), descriptor.getJndiName());
            context.set(componentName, component);
            if (hotDeploymentStrategy.isEnabled()
                    && hotDeploymentStrategy.isFromHotDeployClassLoader(descriptor.getComponentClass())) {
                Init.instance().addHotDeployableComponent(component.getName());
            }
        } catch (Throwable e) {
            throw new RuntimeException("Could not create Component: " + name, e);
        }
    }

    private static String toCamelCase(String hyphenated, boolean initialUpper) {
        StringTokenizer tokens = new StringTokenizer(hyphenated, "-");
        StringBuilder result = new StringBuilder(hyphenated.length());
        String firstToken = tokens.nextToken();
        if (initialUpper) {
            result.append(Character.toUpperCase(firstToken.charAt(0))).append(firstToken.substring(1));
        } else {
            result.append(firstToken);
        }
        while (tokens.hasMoreTokens()) {
            String token = tokens.nextToken();
            result.append(Character.toUpperCase(token.charAt(0))).append(token.substring(1));
        }
        return result.toString();
    }

    private static class EventListenerDescriptor {
        private String type;
        private List<String> listenerMethodBindings = new ArrayList<String>();

        EventListenerDescriptor(String type) {
            this.type = type;
        }

        public String getType() {
            return type;
        }

        public List<String> getListenerMethodBindings() {
            return listenerMethodBindings;
        }

        @Override
        public String toString() {
            return "EventListenerDescriptor(" + type + ')';
        }
    }

}