/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license. See terms of license at gnu.org.
*/
package org.jboss.seam.init;
import static org.jboss.seam.util.Resources.getRealFile;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
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.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.Lifecycle;
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.DeploymentStrategy;
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.TimestampCheckForwardingDeploymentStrategy;
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 warClassesDirectory;
private File warLibDirectory;
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.warClassesDirectory = getRealFile(servletContext, "/WEB-INF/classes");
this.warLibDirectory = getRealFile(servletContext, "/WEB-INF/lib");
this.hotDeployDirectory = getRealFile(servletContext, HotDeploymentStrategy.DEFAULT_HOT_DEPLOYMENT_DIRECTORY_PATH);
}
public Initialization create()
{
standardDeploymentStrategy = new StandardDeploymentStrategy(Thread.currentThread().getContextClassLoader(),servletContext);
standardDeploymentStrategy.scan();
addNamespaces();
initComponentsFromXmlDocument("/WEB-INF/components.xml");
initComponentsFromXmlDocument("/WEB-INF/events.xml"); //deprecated
initComponentsFromXmlDocuments();
initPropertiesFromServletContext();
initPropertiesFromResource();
initJndiProperties();
initPropertiesFromSystem();
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 {
Class<Object> clazz = null;
Name nameAnnotation = null;
if (className != null) {
//get the class implied by the namespaced XML element name
clazz = Reflections.classForName(className);
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,e);
}
//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 = Boolean.parseBoolean(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 || Boolean.parseBoolean(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 : Boolean.parseBoolean(autocreateAttribute);
String startupAttribute = component.attributeValue("startup");
Boolean startup = startupAttribute==null ? null : Boolean.parseBoolean(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;
}
}
// for the same component name and the same class in classpath don't throw exception,
// use the first in classpath as standard order - JBSEAM-3996
Class clazz = descriptor.getComponentClass();
if (!clazz.getName().equals(other.getComponentClass().getName()))
{
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.debug("initializing Seam");
if (standardDeploymentStrategy == null)
{
throw new IllegalStateException("No deployment strategy!");
}
ServletLifecycle.beginInitialization();
Contexts.getApplicationContext().set(Component.PROPERTIES, properties);
addComponent( new ComponentDescriptor(Init.class), Contexts.getApplicationContext());
Init init = (Init) Component.getInstance(Init.class, ScopeType.APPLICATION);
// Make the deployment strategies available in the contexts. This gives
// access to custom deployment handlers for processing custom annotations
Contexts.getEventContext().set(StandardDeploymentStrategy.NAME, standardDeploymentStrategy);
scanForComponents();
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,servletContext, new File[] { warClassesDirectory, warLibDirectory, hotDeployDirectory });
Contexts.getEventContext().set(WarRootDeploymentStrategy.NAME, warRootDeploymentStrategy);
warRootDeploymentStrategy.scan();
init.setWarTimestamp(System.currentTimeMillis());
hotDeploymentStrategy = createHotDeployment(Thread.currentThread().getContextClassLoader(), isHotDeployEnabled(init));
Contexts.getEventContext().set(HotDeploymentStrategy.NAME, hotDeploymentStrategy);
init.setHotDeployPaths( hotDeploymentStrategy.getHotDeploymentPaths() );
if (hotDeploymentStrategy.available())
{
hotDeploymentStrategy.scan();
installHotDeployableComponents();
}
installComponents(init);
for (String globalImport: globalImports)
{
init.importNamespace(globalImport);
}
ServletLifecycle.endInitialization();
log.debug("done initializing Seam");
return this;
}
public void redeploy(HttpServletRequest request) throws InterruptedException
{
redeploy(request, (Init) servletContext.getAttribute( Seam.getComponentName(Init.class) ));
}
public void redeploy(HttpServletRequest request, Init init) throws InterruptedException
{
// It's possible to have the HotDeployFilter installed but disabled
if (!isHotDeployEnabled(init))
{
return;
}
hotDeploymentStrategy = createHotDeployment(Thread.currentThread().getContextClassLoader(), isHotDeployEnabled(init));
boolean changed = new TimestampCheckForwardingDeploymentStrategy()
{
@Override
protected DeploymentStrategy delegate()
{
return hotDeploymentStrategy;
}
}.changedSince(init.getTimestamp());
if (hotDeploymentStrategy.available() && changed)
{
ServletLifecycle.beginReinitialization(request);
Contexts.getEventContext().set(HotDeploymentStrategy.NAME, hotDeploymentStrategy);
hotDeploymentStrategy.scan();
if (hotDeploymentStrategy.getTimestamp() > init.getTimestamp())
{
log.debug("redeploying components");
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);
}
init.getHotDeployableComponents().clear();
installHotDeployableComponents();
installComponents(init);
log.debug("done redeploying components");
}
// update the timestamp outside of the second timestamp check to be
// sure we don't cause an unnecessary scan
// the second scan checks annotations (the slow part) which might
// happen to exclude the most recent file
init.setTimestamp(System.currentTimeMillis());
ServletLifecycle.endReinitialization();
}
final WarRootDeploymentStrategy warRootDeploymentStrategy = new WarRootDeploymentStrategy(Thread.currentThread().getContextClassLoader(), warRoot, servletContext, new File[] { warClassesDirectory, warLibDirectory, hotDeployDirectory });
changed = new TimestampCheckForwardingDeploymentStrategy()
{
@Override
protected DeploymentStrategy delegate()
{
return warRootDeploymentStrategy;
}
}.changedSince(init.getWarTimestamp());
if (changed)
{
warRootDeploymentStrategy.scan();
if (warRootDeploymentStrategy.getTimestamp() > init.getWarTimestamp())
{
log.debug("redeploying page descriptors...");
Pages pages = (Pages) ServletLifecycle.getServletContext().getAttribute(Seam.getComponentName(Pages.class));
if (pages != null)
{
// application context is needed for creating expressions
Lifecycle.setupApplication();
pages.initialize(warRootDeploymentStrategy.getDotPageDotXmlFileNames());
Lifecycle.cleanupApplication();
}
ServletLifecycle.getServletContext().removeAttribute(Seam.getComponentName(Exceptions.class));
init.setWarTimestamp(warRootDeploymentStrategy.getTimestamp());
log.debug("done redeploying page descriptors");
}
}
}
private void installHotDeployableComponents()
{
for (ClassDescriptor classDescriptor: hotDeploymentStrategy.getScannedComponentClasses() )
{
Class<?> scannedClass = classDescriptor.getClazz();
installScannedComponentAndRoles(scannedClass);
}
}
private HotDeploymentStrategy createHotDeployment(ClassLoader classLoader, boolean hotDeployEnabled)
{
if (isGroovyPresent())
{
log.debug("Using Java + Groovy hot deploy");
return HotDeploymentStrategy.createInstance("org.jboss.seam.deployment.GroovyHotDeploymentStrategy", classLoader, hotDeployDirectory, servletContext, hotDeployEnabled);
}
else
{
log.debug("Using Java hot deploy");
return new HotDeploymentStrategy(classLoader, hotDeployDirectory, servletContext, hotDeployEnabled);
}
}
private boolean isHotDeployEnabled(Init init)
{
return init.isDebug();
}
private 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.debug("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 initPropertiesFromSystem()
{
Properties filtered = new Properties();
String prefix = Component.PROPERTIES + ".";
for (Map.Entry me : System.getProperties().entrySet())
{
if (((String)me.getKey()).startsWith(prefix))
{
filtered.put(((String)me.getKey()).substring(prefix.length()),me.getValue());
}
}
initPropertiesFromMap(filtered);
}
private void initPropertiesFromResource()
{
initPropertiesFromMap(loadFromResource("/seam.properties"));
}
private void initPropertiesFromMap(Properties props)
{
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.debug("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 != null && 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 + ')';
}
}
}