package net.sf.springofeclipse;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import net.sf.springofeclipse.internal.BundleClassLoader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.Platform;
import org.osgi.framework.Bundle;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.support.AbstractBeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
/**
* This class is an implementation of the interface {@link BeanDefinitionReader}
* and it loads the bean definitions via the extension-point
* <code>net.sf.springofeclipse.imports</code>.
*
* @see BeanDefinitionReader
* @see ApplicationContextFactory
*
* @author Zsolt Farkas
*
* @since 0.0.1
*/
public class ImportExtensionPointBeanDefinitionReader extends
AbstractBeanDefinitionReader {
/**
* The logger of the class.
*/
private static Log log = LogFactory
.getLog(ImportExtensionPointBeanDefinitionReader.class);
private class ImportConfiguration {
private URL[] resources;
private Class<BeanDefinitionReader> readerClass;
private ClassLoader classLoader;
}
/**
* Constructs the bean definition reader.
*
* @param registry
*/
public ImportExtensionPointBeanDefinitionReader(
BeanDefinitionRegistry registry) {
super(registry);
}
/**
* <p>
* <i>Note: this method is not supported.</i>
* </p>
*
* @see org.springframework.beans.factory.support.BeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource)
*
* @since 0.0.1
*/
@Override
public int loadBeanDefinitions(Resource resource)
throws BeanDefinitionStoreException {
throw new UnsupportedOperationException("operation is not supported");
}
/**
* Loads the beans, that are defined via the extension-point
* <code>net.sf.springofeclispe.imports</code>.
*
* @see org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String)
*
* @since 0.0.1
*/
@SuppressWarnings("unchecked")
public int loadBeanDefinitions(String contextId) {
if (log.isDebugEnabled()) {
log
.debug("loading imports for the context: [id=" + contextId
+ "]");
}
int count = 0;
ImportConfiguration[] importConfigurations = loadImportConfigurations(contextId);
for (int i = 0; i < importConfigurations.length; i++) {
ImportConfiguration importConfiguration = importConfigurations[i];
BeanDefinitionReader reader = createBeanDefinitionReader(
importConfiguration.readerClass, getRegistry());
if (reader == null) {
if (log.isDebugEnabled()) {
log.debug("creating default reader: [class="
+ XmlBeanDefinitionReader.class.getName() + "]");
}
reader = new XmlBeanDefinitionReader(getRegistry());
}
initReader(reader, importConfiguration.classLoader);
URL[] resourceURLs = importConfiguration.resources;
List resources = new ArrayList();
for (int j = 0; j < resourceURLs.length; j++) {
resources.add(new UrlResource(resourceURLs[j]));
}
count += reader.loadBeanDefinitions((Resource[]) resources
.toArray(new Resource[resources.size()]));
}
if (log.isDebugEnabled()) {
log.debug("loaded beans count: [" + count + "]");
}
return count;
}
/**
* Initializes the <code>reader</code> with the <code>classLoader</code>.
*
* @param reader
* @param classLoader
*/
protected void initReader(BeanDefinitionReader reader,
ClassLoader classLoader) {
if (log.isDebugEnabled()) {
log.debug("initializing bean definition reader: [reader=" + reader
+ "; classLoader=" + classLoader + "]");
}
try {
Method method = reader.getClass().getMethod("setBeanClassLoader",
ClassLoader.class);
method.invoke(reader, classLoader);
} catch (Throwable e) {
if (log.isWarnEnabled()) {
log.warn("initialization of the reader failed: " + e);
}
}
}
/**
* Creates the bean definition reader for the bean definition registry.
*
* @param readerClass
* @param registry
* @return the created bean definition reader
*/
@SuppressWarnings("unchecked")
protected BeanDefinitionReader createBeanDefinitionReader(
Class<BeanDefinitionReader> readerClass,
BeanDefinitionRegistry registry) {
if (log.isDebugEnabled()) {
log.debug("creating bean definition reader [readerClass="
+ readerClass + "; registry=" + registry + "]");
}
try {
Constructor constructor = readerClass
.getConstructor(BeanDefinitionRegistry.class);
return (BeanDefinitionReader) constructor.newInstance(registry);
} catch (Throwable e) {
if (log.isWarnEnabled()) {
log.warn("bean definition reader could not be created: " + e);
}
}
return null;
}
/**
* Loads the extensions for the <code>contextId</code>.
*
* @param contextId
* @return the configuration objects
*/
@SuppressWarnings("unchecked")
protected ImportConfiguration[] loadImportConfigurations(String contextId) {
List configurationList = new ArrayList();
IExtensionPoint ep = Platform.getExtensionRegistry().getExtensionPoint(
"net.sf.springofeclipse.imports");
IExtension[] extensions = ep.getExtensions();
for (int i = 0; i < extensions.length; i++) {
IExtension extension = extensions[i];
IConfigurationElement[] elements = extension
.getConfigurationElements();
for (int j = 0; j < elements.length; j++) {
IConfigurationElement configurationElement = elements[j];
String contextIdTemp = configurationElement
.getAttribute("contextId");
if (contextId.equals(contextIdTemp)) {
if (log.isDebugEnabled()) {
log.debug("loading imports for context: [id="
+ contextId + "]");
}
ImportConfiguration configuration = new ImportConfiguration();
Bundle bundle = Platform.getBundle(extension
.getNamespaceIdentifier());
configuration.classLoader = new BundleClassLoader(bundle);
String readerClassName = configurationElement
.getAttribute("beanDefinitionReader");
try {
if (readerClassName != null) {
Class readerClass = bundle
.loadClass(readerClassName);
if (readerClass
.isAssignableFrom(BeanDefinitionReader.class)) {
configuration.readerClass = readerClass;
} else if (log.isWarnEnabled()) {
log
.warn("bean definition reader must implement the interface ["
+ BeanDefinitionReader.class
.getName()
+ "]; defined reader will be ignored: ["
+ readerClassName + "]");
}
}
} catch (ClassNotFoundException e) {
if (log.isWarnEnabled()) {
log.warn("bean definition reader not found [class="
+ readerClassName + "]: " + e);
}
}
IConfigurationElement[] resourceElements = configurationElement
.getChildren("import");
List resourceURLs = new ArrayList();
for (int k = 0; k < resourceElements.length; k++) {
IConfigurationElement resourceElement = resourceElements[k];
String resource = resourceElement
.getAttribute("resource");
if (log.isDebugEnabled()) {
log.debug("found resource: [" + resource + "]");
}
URL resourceURL = bundle.getResource(resource);
if (log.isDebugEnabled()) {
log.debug("resource url: [" + resourceURL + "]");
}
resourceURLs.add(resourceURL);
}
configuration.resources = (URL[]) resourceURLs
.toArray(new URL[resourceURLs.size()]);
configurationList.add(configuration);
}
}
}
return (ImportConfiguration[]) configurationList
.toArray(new ImportConfiguration[configurationList.size()]);
}
}