? sandbox/META-INF ? sandbox/src/sandbox.iml Index: sandbox/src/org/springframework/osgi/context/ContextLoaderListener.java =================================================================== RCS file: sandbox/src/org/springframework/osgi/context/ContextLoaderListener.java diff -N sandbox/src/org/springframework/osgi/context/ContextLoaderListener.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sandbox/src/org/springframework/osgi/context/ContextLoaderListener.java 22 Mar 2006 09:29:29 -0000 @@ -0,0 +1,196 @@ +package org.springframework.osgi.context; + +import java.lang.reflect.Method; +import java.net.URL; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.List; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.BundleListener; +import org.osgi.framework.ServiceReference; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.osgi.context.support.BundleDelegatingClassLoader; +import org.springframework.osgi.context.support.DefaultOsgiBundleXmlApplicationContextFactory; +import org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext; +import org.springframework.osgi.context.support.OsgiBundleXmlApplicationContextFactory; +import org.springframework.osgi.service.OsgiServiceUtils; +import org.springframework.util.StringUtils; + +/** + * A BundlerListener that transparently activates Spring inside a bundle. + * Activation is based on the presence of a configuration file named + * <bundle symbolic name>-context.xml + * + * The service properties used when publishing the service are + * determined by the OsgiServicePropertiesResolver. The default + * implementation uses + * + * + * @author OSGi Hackers + * @see ContextLoaderBundleActivator + * @since 2.0 + */ +public class ContextLoaderListener implements BundleActivator, BundleListener { + + private static final String CONTEXT_LOCATION_HEADER = "org.springframework.context"; + // FIXME: + private static final String PARENT_CONTEXT_SERVICE_NAME_HEADER = "SpringParentContext"; + //private static final String PARENT_CONTEXT_SERVICE_NAME_HEADER = "org.springframework.parent.context"; + private static final String CONTEXT_LOCATION_DELIMITERS = ", "; + private static final String DEFAULT_CONTEXT_PREFIX = "/META-INF/"; + private static final String DEFAULT_CONTEXT_POSTFIX = "-context.xml"; + + private OsgiBundleXmlApplicationContextFactory contextFactory = new DefaultOsgiBundleXmlApplicationContextFactory(); + private ConfigurableApplicationContext applicationContext; + private ServiceReference parentServiceReference; + + private HashMap managedBundles = new HashMap(); + + public void start(BundleContext context) throws Exception { + context.addBundleListener(this); + } + + public void stop(BundleContext context) throws Exception { + } + + public void bundleChanged(BundleEvent event) { + switch (event.getType()) { + case BundleEvent.STARTED: + startBundle(event.getBundle()); + break; + case BundleEvent.STOPPED: + stopBundle(event.getBundle()); + break; + } + } + + private void startBundle(Bundle bundle) { + String[] applicationContextLocations = getApplicationContextLocations(bundle); + BundleContext bundleContext = getBundleContext(bundle); + ApplicationContext parent = getParentApplicationContext(bundleContext); + + ClassLoader ccl = + new BundleDelegatingClassLoader(getClass().getClassLoader(), bundle); + ClassLoader savedCCL = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(ccl); + + try { + try { + OsgiBundleXmlApplicationContext ctx = + contextFactory.createApplicationContext(parent, bundleContext, + applicationContextLocations); + managedBundles.put(bundle, ctx); + } catch (Throwable thr) { + thr.printStackTrace(); + } + } finally { + Thread.currentThread().setContextClassLoader(savedCCL); + } + } + + private void stopBundle(Bundle bundle) { + OsgiBundleXmlApplicationContext ctx = managedBundles.remove(bundle); + if (ctx != null) { + ctx.close(); + } + } + + protected ApplicationContext getParentApplicationContext(BundleContext context) { + String parentContextServiceName = (String) context.getBundle().getHeaders().get(PARENT_CONTEXT_SERVICE_NAME_HEADER); + if (parentContextServiceName == null) { + return null; + } + else { + // try to find the service + String filter = "(" + OsgiBundleXmlApplicationContext.APPLICATION_CONTEXT_SERVICE_NAME_HEADER + + "=" + parentContextServiceName + ")"; + ServiceReference ref = OsgiServiceUtils.getService(context,ApplicationContext.class, filter); + ApplicationContext parent = (ApplicationContext) context.getService(ref); + // TODO: register as service listener..., probably in a proxy to the app context + // that we create here and return instead. + + return parent; + } + } + + /** + * Retrieves the org.springframework.context manifest header attribute + * and parses it to create a String[] of resource names for creating + * the application context. + * + * If the org.springframework.context header is not present, the + * default -context.xml file will be returned. + */ + protected String[] getApplicationContextLocations(Bundle bundle) { + Dictionary manifestHeaders = bundle.getHeaders(); + String contextLocationsHeader = (String) manifestHeaders.get(CONTEXT_LOCATION_HEADER); + if (contextLocationsHeader != null) { + String[] locs = + StringUtils.tokenizeToStringArray(contextLocationsHeader, CONTEXT_LOCATION_DELIMITERS); + List ret = new ArrayList(); + for (String s : locs) { + if (bundle.getEntry(s) != null) { + ret.add(s); + } + } + if (ret.isEmpty()) return null; + else return addBundlePrefixTo((String[])ret.toArray()); + } + else { + String defaultName = DEFAULT_CONTEXT_PREFIX + bundle.getSymbolicName() + DEFAULT_CONTEXT_POSTFIX; + if (bundle.getEntry(defaultName) != null) + return addBundlePrefixTo(new String[] {defaultName}); + else return null; + } + } + + /** + * add the "bundle:" prefix to the resource location paths in the given argument. + * This ensures that only this bundle will be searched for matching resources. + * + * Modifies the argument in place and returns it. + */ + private String[] addBundlePrefixTo(String[] resourcePaths) { + for (int i = 0; i < resourcePaths.length; i++) { + resourcePaths[i] = OsgiBundleXmlApplicationContext.BUNDLE_URL_PREFIX + resourcePaths[i]; + } + return resourcePaths; + } + + // for testing... + protected void setApplicationContext(ConfigurableApplicationContext context) { + this.applicationContext = context; + } + + // for testing... + protected void setParentServiceReference(ServiceReference ref) { + this.parentServiceReference = ref; + } + + // for testing... + protected void setApplicationContextFactory(OsgiBundleXmlApplicationContextFactory factory) { + this.contextFactory = factory; + } + + // FIXME: + private static BundleContext getBundleContext(Bundle bundle) { + try { + Method m = bundle.getClass().getDeclaredMethod("getContext"); + m.setAccessible(true); + return (BundleContext) m.invoke(bundle); + } catch (Exception exc) { + throw new AssertionError(exc); + } + } + +} Index: sandbox/src/org/springframework/osgi/context/MANIFEST.MF =================================================================== RCS file: sandbox/src/org/springframework/osgi/context/MANIFEST.MF diff -N sandbox/src/org/springframework/osgi/context/MANIFEST.MF --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sandbox/src/org/springframework/osgi/context/MANIFEST.MF 22 Mar 2006 09:29:30 -0000 @@ -0,0 +1,12 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: SpringBundle +Bundle-SymbolicName: SpringBundle +Bundle-Version: 1.0.0 +Bundle-Activator: org.springframework.osgi.context.ContextLoaderListener +Bundle-Localization: plugin +Import-Package: org.osgi.framework;version="1.3.0" +Bundle-ClassPath: bin/, + lib/spring.jar, + lib/commons-logging.jar, + lib/log4j-1.2.13.jar Index: sandbox/src/org/springframework/osgi/context/support/BundleDelegatingClassLoader.java =================================================================== RCS file: sandbox/src/org/springframework/osgi/context/support/BundleDelegatingClassLoader.java diff -N sandbox/src/org/springframework/osgi/context/support/BundleDelegatingClassLoader.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sandbox/src/org/springframework/osgi/context/support/BundleDelegatingClassLoader.java 22 Mar 2006 09:29:30 -0000 @@ -0,0 +1,26 @@ +package org.springframework.osgi.context.support; + +import org.osgi.framework.Bundle; + +public class BundleDelegatingClassLoader extends ClassLoader +{ + private final ClassLoader springBundleClassLoader; + private final Bundle targetBundle; + + + public BundleDelegatingClassLoader(ClassLoader springBundleClassLoader, + Bundle targetBundle) + { + this.springBundleClassLoader = springBundleClassLoader; + this.targetBundle = targetBundle; + } + + public Class loadClass(String name) throws ClassNotFoundException { + if (name.startsWith("org.springframework") || + name.startsWith("org.aopalliance")) + return springBundleClassLoader.loadClass(name); + else + return targetBundle.loadClass(name); + } + +} Index: sandbox/src/org/springframework/osgi/context/support/OsgiBundleXmlApplicationContext.java =================================================================== RCS file: /cvsroot/springframework/spring/sandbox/src/org/springframework/osgi/context/support/OsgiBundleXmlApplicationContext.java,v retrieving revision 1.2 diff -u -r1.2 OsgiBundleXmlApplicationContext.java --- sandbox/src/org/springframework/osgi/context/support/OsgiBundleXmlApplicationContext.java 21 Mar 2006 10:53:48 -0000 1.2 +++ sandbox/src/org/springframework/osgi/context/support/OsgiBundleXmlApplicationContext.java 22 Mar 2006 09:29:30 -0000 @@ -17,17 +17,19 @@ */ package org.springframework.osgi.context.support; -import java.io.IOException; -import java.net.URL; import java.util.Dictionary; -import java.util.Enumeration; import java.util.Properties; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanNotOfRequiredTypeException; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.AbstractXmlApplicationContext; import org.springframework.core.io.Resource; @@ -89,10 +91,9 @@ this.configLocations = configLocations; this.osgiBundleContext = aBundleContext; this.osgiBundle = this.osgiBundleContext.getBundle(); - this.setClassLoader(createBundleClassLoader(this.osgiBundle)); refresh(); - publishContextAsOsgiService(); + publishContextAsOsgiService(); } protected String[] getConfigLocations() { @@ -110,6 +111,7 @@ protected void publishContextAsOsgiService() { Dictionary serviceProperties = new Properties(); serviceProperties.put(APPLICATION_CONTEXT_SERVICE_NAME_HEADER, getServiceName()); + this.serviceRegistration = this.osgiBundleContext.registerService( new String[] {ApplicationContext.class.getName()}, @@ -118,9 +120,12 @@ } public void close() { - if (this.serviceRegistration != null) { - this.serviceRegistration.unregister(); - } + try { + if (this.serviceRegistration != null) { + this.serviceRegistration.unregister(); + } + } catch (IllegalStateException ignore) { + } super.close(); } @@ -195,29 +200,22 @@ beanFactory.addBeanPostProcessor(new BundleContextAwareProcessor(this.osgiBundleContext)); beanFactory.ignoreDependencyInterface(BundleContextAware.class); } - - private ClassLoader createBundleClassLoader(Bundle bundle) { - return new BundleClassLoader(bundle); - } - - private static class BundleClassLoader extends ClassLoader { - - private Bundle backingBundle; - - public BundleClassLoader(Bundle aBundle) { - this.backingBundle = aBundle; - } - - protected Class findClass(String name) throws ClassNotFoundException { - return this.backingBundle.loadClass(name); - } - - protected URL findResource(String name) { - return this.backingBundle.getResource(name); - } - - protected Enumeration findResources(String name) throws IOException { - return this.backingBundle.getResources(name); - } - } + + protected DefaultListableBeanFactory createBeanFactory() { + return new DefaultListableBeanFactory() { + public BeanDefinition getBeanDefinition(String beanName) + throws NoSuchBeanDefinitionException + { + try { + return super.getBeanDefinition(beanName); + } catch (NoSuchBeanDefinitionException nsbd) { + System.err.println("BEAN DEFINITION NOT FOUND2: " + nsbd); + + throw nsbd; + } + } + }; + } + + } Index: sandbox/src/org/springframework/osgi/service/BeanNameServicePropertiesResolver.java =================================================================== RCS file: /cvsroot/springframework/spring/sandbox/src/org/springframework/osgi/service/BeanNameServicePropertiesResolver.java,v retrieving revision 1.1 diff -u -r1.1 BeanNameServicePropertiesResolver.java --- sandbox/src/org/springframework/osgi/service/BeanNameServicePropertiesResolver.java 10 Mar 2006 16:40:11 -0000 1.1 +++ sandbox/src/org/springframework/osgi/service/BeanNameServicePropertiesResolver.java 22 Mar 2006 09:29:30 -0000 @@ -25,7 +25,7 @@ import org.springframework.osgi.context.BundleContextAware; /** - * OsgiServicePropertiesResolver that creats a service property set with + * OsgiServicePropertiesResolver that creates a service property set with * the following properties: *