diff --git a/annotation/pom.xml b/annotation/pom.xml index bfd909b4d..7721830d0 100644 --- a/annotation/pom.xml +++ b/annotation/pom.xml @@ -3,7 +3,7 @@ spring-osgi org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi @@ -19,6 +19,11 @@ ${basedir}/../docs/src/javadoc org.springframework.osgi.extensions.annotation ${symName.prefix}.extensions.annotations + + org.springframework.osgi*;version=${spring.osgi.import.version}, + org.springframework*;version=${spring.base.version}, + * + diff --git a/bundle-archetype/pom.xml b/bundle-archetype/pom.xml index 52a29599b..f9a5e26fd 100644 --- a/bundle-archetype/pom.xml +++ b/bundle-archetype/pom.xml @@ -1,26 +1,24 @@ - + spring-osgi org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi spring-osgi-bundle-archetype maven-plugin Spring OSGi Archetype + Spring OSGi Maven2 Archetype http://www.springframework.org/osgi - - + - false src/main/resources - \ No newline at end of file diff --git a/bundle-archetype/src/main/resources/META-INF/archetype.xml b/bundle-archetype/src/main/resources/META-INF/archetype.xml index b4962e566..cbd68a6a3 100644 --- a/bundle-archetype/src/main/resources/META-INF/archetype.xml +++ b/bundle-archetype/src/main/resources/META-INF/archetype.xml @@ -1,16 +1,15 @@ - spring-osgi-bundle-artifact + spring-osgi-bundle-archetype src/main/java/Bean.java src/main/java/impl/BeanImpl.java - + META-INF/readme.txt readme.txt src/main/resources/META-INF/spring/bundle-context.xml src/main/resources/META-INF/spring/bundle-context-osgi.xml + src/main/resources/log4j.properties .classpath .project build.properties @@ -18,8 +17,9 @@ src/test/java/impl/BeanImplTest.java src/test/java/impl/BeanIntegrationTest.java + src/test/java/impl/BeanOsgiIntegrationTest.java - src/test/resources/readme.txt + src/test/resources/log4j.properties \ No newline at end of file diff --git a/bundle-archetype/src/main/resources/archetype-resources/.classpath b/bundle-archetype/src/main/resources/archetype-resources/.classpath index 6bee5dcd4..867585907 100644 --- a/bundle-archetype/src/main/resources/archetype-resources/.classpath +++ b/bundle-archetype/src/main/resources/archetype-resources/.classpath @@ -2,9 +2,8 @@ - - - + + @@ -13,5 +12,15 @@ + + + + + + + + + + diff --git a/bundle-archetype/src/main/resources/archetype-resources/META-INF/readme.txt b/bundle-archetype/src/main/resources/archetype-resources/META-INF/readme.txt new file mode 100644 index 000000000..7405efdc1 --- /dev/null +++ b/bundle-archetype/src/main/resources/archetype-resources/META-INF/readme.txt @@ -0,0 +1,6 @@ +To facilitate OSGi bundle manifest generation, the archetype uses Felix maven plugin. +The manifest will be generated when building the project. To trigger this, run: + +mvn package + +Please see http://felix.apache.org/site/maven-bundle-plugin-bnd.html for more information. \ No newline at end of file diff --git a/bundle-archetype/src/main/resources/archetype-resources/pom.xml b/bundle-archetype/src/main/resources/archetype-resources/pom.xml index 672574941..e1a34ea69 100644 --- a/bundle-archetype/src/main/resources/archetype-resources/pom.xml +++ b/bundle-archetype/src/main/resources/archetype-resources/pom.xml @@ -1,5 +1,6 @@ - - + + 4.0.0 ${groupId} ${artifactId} @@ -11,21 +12,12 @@ 1.4.3 2.5.1 + 1.0.3 + 3.2.2 - - org.springframework.osgi - junit.osgi - 3.8.2-SNAPSHOT - test - - - org.springframework - spring-test - ${spring.maven.artifact.version} - test - + org.springframework spring-core @@ -59,18 +51,86 @@ slf4j-log4j12 ${slf4j.version} + org.osgi org.osgi.core 4.0 + + + + org.springframework.osgi + junit.osgi + 3.8.2-SNAPSHOT + test + + + org.springframework + spring-test + ${spring.maven.artifact.version} + test + + + + org.springframework.osgi + spring-osgi-test + ${spring.osgi.version} + test + + + + org.springframework.osgi + spring-osgi-annotation + ${spring.osgi.version} + test + + + + org.springframework.osgi + aopalliance.osgi + 1.0-SNAPSHOT + test + + + + + org.eclipse.osgi + org.eclipse.osgi + ${equinox.ver} + jar + test + + + + org.springframework.osgi + log4j.osgi + 1.2.15-SNAPSHOT + test + + + + + + src/main/resources + + + + . + + plugin.xml + META-INF/* + + + + org.apache.felix @@ -78,13 +138,33 @@ true 1.0.0 + META-INF - META-INF - ${package}* + !${package}.impl,${package}* * + + src/main/resources + + + + org.apache.felix + maven-bundle-plugin + + + bundle-manifest + process-classes + + manifest + + + + diff --git a/bundle-archetype/src/main/resources/archetype-resources/src/main/resources/log4j.properties b/bundle-archetype/src/main/resources/archetype-resources/src/main/resources/log4j.properties new file mode 100644 index 000000000..18cfaa3f1 --- /dev/null +++ b/bundle-archetype/src/main/resources/archetype-resources/src/main/resources/log4j.properties @@ -0,0 +1,8 @@ +log4j.rootCategory=INFO, stdout +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout.ConversionPattern=%t %p [%c] - %m%n +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.threshold=TRACE + +#log4j.logger.org.springframework.osgi=DEBUG +#log4j.logger.org.springframework=DEBUG \ No newline at end of file diff --git a/bundle-archetype/src/main/resources/archetype-resources/src/test/java/impl/BeanIntegrationTest.java b/bundle-archetype/src/main/resources/archetype-resources/src/test/java/impl/BeanIntegrationTest.java index dc2cf56f7..d27f13928 100644 --- a/bundle-archetype/src/main/resources/archetype-resources/src/test/java/impl/BeanIntegrationTest.java +++ b/bundle-archetype/src/main/resources/archetype-resources/src/test/java/impl/BeanIntegrationTest.java @@ -4,9 +4,8 @@ import org.springframework.test.AbstractDependencyInjectionSpringContextTests; /** - * Integration test the bundle locally (outside of OSGi). - * Use AbstractOsgiTests and a separate integration test project - * for testing inside of OSGi. + * Local integration test (outside of OSGi). + * @see BeanOsgiIntegrationTest for integration test inside OSGi. */ public class BeanIntegrationTest extends AbstractDependencyInjectionSpringContextTests { diff --git a/bundle-archetype/src/main/resources/archetype-resources/src/test/java/impl/BeanOsgiIntegrationTest.java b/bundle-archetype/src/main/resources/archetype-resources/src/test/java/impl/BeanOsgiIntegrationTest.java new file mode 100644 index 000000000..40350fcc5 --- /dev/null +++ b/bundle-archetype/src/main/resources/archetype-resources/src/test/java/impl/BeanOsgiIntegrationTest.java @@ -0,0 +1,19 @@ +package ${package}.impl; + +import org.springframework.osgi.test.AbstractConfigurableBundleCreatorTests; + +/** + * OSGi integration test (inside OSGi). + * @see AbstractConfigurableBundleCreatorTests + */ +public class BeanOsgiIntegrationTest extends AbstractConfigurableBundleCreatorTests { + + protected String[] getConfigLocations() { + return new String[] {"META-INF/spring/*.xml"}; + } + + public void testOsgiBundleContext() { + assertNotNull(bundleContext); + } + +} \ No newline at end of file diff --git a/bundle-archetype/src/main/resources/archetype-resources/src/test/resources/log4j.properties b/bundle-archetype/src/main/resources/archetype-resources/src/test/resources/log4j.properties new file mode 100644 index 000000000..18cfaa3f1 --- /dev/null +++ b/bundle-archetype/src/main/resources/archetype-resources/src/test/resources/log4j.properties @@ -0,0 +1,8 @@ +log4j.rootCategory=INFO, stdout +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout.ConversionPattern=%t %p [%c] - %m%n +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.threshold=TRACE + +#log4j.logger.org.springframework.osgi=DEBUG +#log4j.logger.org.springframework=DEBUG \ No newline at end of file diff --git a/bundle-archetype/src/main/resources/archetype-resources/src/test/resources/readme.txt b/bundle-archetype/src/main/resources/archetype-resources/src/test/resources/readme.txt deleted file mode 100644 index e67bfe9b8..000000000 --- a/bundle-archetype/src/main/resources/archetype-resources/src/test/resources/readme.txt +++ /dev/null @@ -1,4 +0,0 @@ -This file is only here because maven archetypes cannot create empty directories.Without this directory present, eclipse will indicate a classpath error -because src/test/resources is on the project's classpath. - -You can safely delete this file. diff --git a/changelog.txt b/changelog.txt index c261b18af..483d6b232 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,7 +2,67 @@ SPRING DYNAMIC MODULES FRAMEWORK CHANGELOG ========================================== http://www.springframework.org/osgi -Changes in version 1.0 final (2008-01-22) +Changes in version 1.0.3 (2008-07-03) +------------------------------------- + +Package org.springframework.osgi.context +* AbstractDelegatedExecutionApplicationContext rethrows all exceptions occuring during refresh + +Package org.springframework.osgi.extender +* fixed reading of system property for enabling global annotation scanning + +Package org.springframework.osgi.util +* fixed parsing exception in DebugUtils when dealing with version ranges + + +Changes in version 1.0.2 (2008-03-25) +------------------------------------- + +General +* adjusted the manifests so internal packages are not exported and the versions for both spring and spring-osgi are + present + +Package org.springframework.osgi.context +* AbstractOsgiBundleApplicationContext registers the application context as an OSGi service using just the interfaces + +Package org.springframework.osgi.extender +* raised service dependency waiting logging level to INFO (from DEBUG) + +Package org.springframework.osgi.util +* BundleDelegatingClassLoader relies on default hashCode/equals methods to prevent class caching on bundle updates + +Package org.springframework.osgi.test +* Installed bundles have the location set to the backing resource URL if possible, falling back to the description + + +Changes in version 1.0.1 (2008-02-29) +------------------------------------- + +General +* fixed nested Eclipse classpath entry in spring-osgi bundle archetype +* added log4j.properties to spring-osgi bundle archetype +* added OSGi integration test to the spring-osgi bundle archetype + +Package org.springframework.context.support +* the namespace resolver mechanism first checks the local bundle classpath before using the extender namespace service + +Package org.springframework.osgi.context +* fixed AbstractDelegatedExecutionApplicationContext to log errors that occur during the bean factory startup + +Package org.springframework.osgi.io +* improved OsgiBundleResource handling of jars with missing archive entries for folders +* improved OsgiBundleResourcePatternResolver performance when checking for jar resources +* improved detection of relative OSGi URLs + +Package org.springframework.osgi.test +* AbstractOsgiTest provides better error messages for bundle that fail to start +* custom artifact locators can be used with AbstractDependencyManagerTests +* removed all internal usage of system properties by the testing infrastructure +* removed the use of serialization inside the testing framework +* improved automatic manifest generation by parsing only the test parents that are available in the test bundle + + +Changes in version 1.0 final (2008-01-25) ----------------------------------------- Package org.springframework.osgi.extensions.annotation @@ -27,6 +87,7 @@ Package org.springframework.osgi.util * BundleDelegatingClassLoader uses a degradable logger to properly report error messages when the logging implementation is misconfigured or the logging classes are not loaded yet + Changes in version 1.0 RC2 (2008-01-11) --------------------------------------- diff --git a/core/.classpath b/core/.classpath index 6704625f6..6ef88ef59 100644 --- a/core/.classpath +++ b/core/.classpath @@ -7,7 +7,7 @@ - + diff --git a/core/pom.xml b/core/pom.xml index 7184ea63a..7aee368bd 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -3,7 +3,7 @@ spring-osgi org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi @@ -20,9 +20,11 @@ ${basedir}/../docs/src/javadoc - - !org.springframework.osgi.io*, !org.springframework.osgi.io.internal, org.springframework.osgi* - + + !org.springframework.osgi.io*, + !org.springframework.osgi*internal*, + org.springframework.osgi*;version=${spring.osgi.export.version} + org.apache.commons.logging*, @@ -31,9 +33,15 @@ org.osgi.service.startlevel;version=1.0;resolution:=optional, org.w3c.dom;resolution:=optional, org.xml.sax;resolution:=optional, + org.springframework.osgi*;version=${spring.osgi.import.version}, * + + !org.springframework.osgi.io*, + org.springframework.osgi*internal* + + ${symName.prefix}.core diff --git a/core/src/main/java/org/springframework/osgi/context/support/AbstractDelegatedExecutionApplicationContext.java b/core/src/main/java/org/springframework/osgi/context/support/AbstractDelegatedExecutionApplicationContext.java index edceeb2fd..25193668f 100644 --- a/core/src/main/java/org/springframework/osgi/context/support/AbstractDelegatedExecutionApplicationContext.java +++ b/core/src/main/java/org/springframework/osgi/context/support/AbstractDelegatedExecutionApplicationContext.java @@ -116,7 +116,8 @@ boolean isAvailable() { * continuation-like behavior or completion of the refresh method on several * threads, in a asynch manner. * - * By default, the refresh method in executed in one go (normal behavior). + * By default, the refresh method in executed in one go (normal + * behavior). * * {@inheritDoc} */ @@ -209,11 +210,15 @@ public void startRefresh() { // Destroy already created singletons to avoid dangling // resources. beanFactory.destroySingletons(); - logger.error("Post refresh error", ex); + // rethrow exception to the caller throw ex; } } } + catch (RuntimeException ex) { + logger.error("Pre refresh error", ex); + throw ex; + } finally { thread.setContextClassLoader(oldTCCL); } @@ -255,6 +260,7 @@ public void completeRefresh() { // resources. getBeanFactory().destroySingletons(); logger.error("Post refresh error", ex); + // rethrow exception to the caller throw ex; } } diff --git a/core/src/main/java/org/springframework/osgi/context/support/AbstractOsgiBundleApplicationContext.java b/core/src/main/java/org/springframework/osgi/context/support/AbstractOsgiBundleApplicationContext.java index a0a652ba8..ac8fe287b 100644 --- a/core/src/main/java/org/springframework/osgi/context/support/AbstractOsgiBundleApplicationContext.java +++ b/core/src/main/java/org/springframework/osgi/context/support/AbstractOsgiBundleApplicationContext.java @@ -186,9 +186,14 @@ public String[] getConfigLocations() { */ protected void doClose() { if (!OsgiServiceUtils.unregisterService(serviceRegistration)) { - logger.info("the application context service has been already unregistered"); + logger.info("Unpublishing application context with properties (" + + APPLICATION_CONTEXT_SERVICE_PROPERTY_NAME + "=" + getBundleSymbolicName() + ")"); serviceRegistration = null; } + else { + if (publishContextAsService) + logger.info("Application Context service already unpublished"); + } // call super class super.doClose(); @@ -230,11 +235,11 @@ protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactor // add bundleContext bean if (!beanFactory.containsLocalBean(BUNDLE_CONTEXT_BEAN_NAME)) { - logger.debug("registering BundleContext as a bean named " + BUNDLE_CONTEXT_BEAN_NAME); + logger.debug("Registering BundleContext as a bean named " + BUNDLE_CONTEXT_BEAN_NAME); beanFactory.registerSingleton(BUNDLE_CONTEXT_BEAN_NAME, this.bundleContext); } else { - logger.warn("a bean named " + BUNDLE_CONTEXT_BEAN_NAME + logger.warn("A bean named " + BUNDLE_CONTEXT_BEAN_NAME + " already exists; the bundleContext will not be registered as a bean"); } @@ -258,7 +263,7 @@ private void cleanOsgiBundleScope(ConfigurableListableBeanFactory beanFactory) { Scope scope = beanFactory.getRegisteredScope(OsgiBundleScope.SCOPE_NAME); if (scope != null && scope instanceof OsgiBundleScope) { if (logger.isDebugEnabled()) - logger.debug("destroying existing bundle scope beans..."); + logger.debug("Destroying existing bundle scope beans..."); ((OsgiBundleScope) scope).destroy(); } } @@ -279,7 +284,7 @@ void publishContextAsOsgiServiceIfNecessary() { } Class[] classes = org.springframework.osgi.util.internal.ClassUtils.getClassHierarchy(getClass(), - org.springframework.osgi.util.internal.ClassUtils.INCLUDE_ALL_CLASSES); + org.springframework.osgi.util.internal.ClassUtils.INCLUDE_INTERFACES); // filter classes based on visibility Class[] filterClasses = org.springframework.osgi.util.internal.ClassUtils.getVisibleClasses(classes, @@ -288,7 +293,7 @@ void publishContextAsOsgiServiceIfNecessary() { String[] serviceNames = org.springframework.osgi.util.internal.ClassUtils.toStringArray(filterClasses); if (logger.isDebugEnabled()) - logger.debug("publishing service under classes " + ObjectUtils.nullSafeToString(serviceNames)); + logger.debug("Publishing service under classes " + ObjectUtils.nullSafeToString(serviceNames)); // Publish under all the significant interfaces we see this.serviceRegistration = getBundleContext().registerService(serviceNames, this, serviceProperties); diff --git a/core/src/main/java/org/springframework/osgi/context/support/DelegatedEntityResolver.java b/core/src/main/java/org/springframework/osgi/context/support/DelegatedEntityResolver.java new file mode 100644 index 000000000..f6f7e6306 --- /dev/null +++ b/core/src/main/java/org/springframework/osgi/context/support/DelegatedEntityResolver.java @@ -0,0 +1,72 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.osgi.context.support; + +import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.util.Assert; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * Delegated XML entity resolver. + * + * @author Costin Leau + * + */ +class DelegatedEntityResolver implements EntityResolver { + + /** logger */ + private static final Log log = LogFactory.getLog(DelegatedEntityResolver.class); + + private final Map resolvers = new LinkedHashMap(2); + + + public void addEntityResolver(EntityResolver resolver, String resolverToString) { + Assert.notNull(resolver); + resolvers.put(resolver, resolverToString); + } + + public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { + boolean trace = log.isTraceEnabled(); + + for (Iterator iterator = resolvers.entrySet().iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + EntityResolver entityResolver = (EntityResolver) entry.getKey(); + if (trace) + log.trace("Trying to resolve entity [" + publicId + "|" + systemId + "] through resolver " + + entry.getValue()); + InputSource entity = entityResolver.resolveEntity(publicId, systemId); + + String resolvedMsg = (entity != null ? "" : "not "); + if (trace) + log.trace("Entity [" + publicId + "|" + systemId + "] was " + resolvedMsg + + "resolved through entity resolver " + entry.getValue()); + + if (entity != null) { + return entity; + } + } + return null; + } +} diff --git a/core/src/main/java/org/springframework/osgi/context/support/DelegatedNamespaceHandlerResolver.java b/core/src/main/java/org/springframework/osgi/context/support/DelegatedNamespaceHandlerResolver.java new file mode 100644 index 000000000..f27445a2a --- /dev/null +++ b/core/src/main/java/org/springframework/osgi/context/support/DelegatedNamespaceHandlerResolver.java @@ -0,0 +1,70 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.osgi.context.support; + +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.xml.NamespaceHandler; +import org.springframework.beans.factory.xml.NamespaceHandlerResolver; +import org.springframework.util.Assert; + +/** + * Delegated XML namespace handler resolver. + * + * @author Costin Leau + * + */ +class DelegatedNamespaceHandlerResolver implements NamespaceHandlerResolver { + + /** logger */ + private static final Log log = LogFactory.getLog(DelegatedNamespaceHandlerResolver.class); + + private final Map resolvers = new LinkedHashMap(2); + + + public void addNamespaceHandler(NamespaceHandlerResolver resolver, String resolverToString) { + Assert.notNull(resolver); + resolvers.put(resolver, resolverToString); + } + + public NamespaceHandler resolve(String namespaceUri) { + boolean trace = log.isTraceEnabled(); + + for (Iterator iterator = resolvers.entrySet().iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + NamespaceHandlerResolver handlerResolver = (NamespaceHandlerResolver) entry.getKey(); + if (trace) + log.trace("Trying to resolve namespace [" + namespaceUri + "] through resolver " + entry.getValue()); + NamespaceHandler handler = handlerResolver.resolve(namespaceUri); + + String resolvedMsg = (handler != null ? "" : "not "); + if (trace) + log.trace("Namespace [" + namespaceUri + "] was " + resolvedMsg + "resolved through handler resolver " + + entry.getValue()); + + if (handler != null) { + return handler; + } + + } + return null; + } +} diff --git a/core/src/main/java/org/springframework/osgi/context/support/OsgiBundleXmlApplicationContext.java b/core/src/main/java/org/springframework/osgi/context/support/OsgiBundleXmlApplicationContext.java index f73e3c1f2..cf6e18c05 100644 --- a/core/src/main/java/org/springframework/osgi/context/support/OsgiBundleXmlApplicationContext.java +++ b/core/src/main/java/org/springframework/osgi/context/support/OsgiBundleXmlApplicationContext.java @@ -22,15 +22,16 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver; +import org.springframework.beans.factory.xml.DelegatingEntityResolver; import org.springframework.beans.factory.xml.NamespaceHandlerResolver; -import org.springframework.beans.factory.xml.ResourceEntityResolver; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.ApplicationContext; import org.springframework.osgi.io.OsgiBundleResource; +import org.springframework.osgi.util.OsgiStringUtils; import org.xml.sax.EntityResolver; /** - * Standalone XML application context, backed by an OSGi bundle. + * Stand-alone XML application context, backed by an OSGi bundle. * *

* The configuration location defaults can be overridden via @@ -117,8 +118,8 @@ protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throw // resource loading environment. beanDefinitionReader.setResourceLoader(this); - NamespaceHandlerResolver nsResolver = lookupNamespaceHandlerResolver(); - EntityResolver enResolver = lookupEntityResolver(); + NamespaceHandlerResolver nsResolver = createNamespaceHandlerResolver(); + EntityResolver enResolver = createEntityResolver(); beanDefinitionReader.setEntityResolver(enResolver); beanDefinitionReader.setNamespaceHandlerResolver(nsResolver); @@ -129,14 +130,61 @@ protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throw loadBeanDefinitions(beanDefinitionReader); } - private NamespaceHandlerResolver lookupNamespaceHandlerResolver() { + /** + * Creates a special OSGi namespace resolver that first uses the bundle + * class path falling back to the namespace service. This way embedded + * libraries that provide namespace handlers take priority over namespace + * provided by other bundles. + * + * @return + */ + private NamespaceHandlerResolver createNamespaceHandlerResolver() { + // create local namespace resolver + // we'll use the default resolver which uses the bundle local class-loader + NamespaceHandlerResolver localNamespaceResolver = new DefaultNamespaceHandlerResolver(getClassLoader()); + // hook in OSGi namespace resolver + NamespaceHandlerResolver osgiServiceNamespaceResolver = lookupNamespaceHandlerResolver(localNamespaceResolver); + + DelegatedNamespaceHandlerResolver delegate = new DelegatedNamespaceHandlerResolver(); + delegate.addNamespaceHandler(localNamespaceResolver, "LocalNamespaceResolver for bundle " + + OsgiStringUtils.nullSafeNameAndSymName(getBundle())); + delegate.addNamespaceHandler(osgiServiceNamespaceResolver, "OSGi Service resolver"); + + return delegate; + } + + /** + * Similar to {@link #createNamespaceHandlerResolver()}, this method + * creates a special OSGi entity resolver that considers the bundle class + * path first, falling back to the entity resolver service provided by the + * Spring DM extender. + * + * @return + */ + private EntityResolver createEntityResolver() { + // create local namespace resolver + EntityResolver localEntityResolver = new DelegatingEntityResolver(getClassLoader()); + // hook in OSGi namespace resolver + EntityResolver osgiServiceEntityResolver = lookupEntityResolver(localEntityResolver); + + DelegatedEntityResolver delegate = new DelegatedEntityResolver(); + delegate.addEntityResolver(localEntityResolver, "LocalEntityResolver for bundle " + + OsgiStringUtils.nullSafeNameAndSymName(getBundle())); + + // hook in OSGi namespace resolver + delegate.addEntityResolver(osgiServiceEntityResolver, "OSGi Service resolver"); + + return delegate; + } + + private NamespaceHandlerResolver lookupNamespaceHandlerResolver(Object fallbackObject) { return (NamespaceHandlerResolver) TrackingUtil.getService(new Class[] { NamespaceHandlerResolver.class }, null, - getClassLoader(), getBundleContext(), new DefaultNamespaceHandlerResolver(getClassLoader())); + getClassLoader(), getBundleContext(), fallbackObject); } - private EntityResolver lookupEntityResolver() { + private EntityResolver lookupEntityResolver(Object fallbackObject) { return (EntityResolver) TrackingUtil.getService(new Class[] { EntityResolver.class }, null, getClassLoader(), - getBundleContext(), new ResourceEntityResolver(this)); + getBundleContext(), fallbackObject); } /** diff --git a/core/src/main/java/org/springframework/osgi/context/support/OsgiPropertyEditorRegistrar.java b/core/src/main/java/org/springframework/osgi/context/support/OsgiPropertyEditorRegistrar.java index a8e83ab1d..83fe1e186 100644 --- a/core/src/main/java/org/springframework/osgi/context/support/OsgiPropertyEditorRegistrar.java +++ b/core/src/main/java/org/springframework/osgi/context/support/OsgiPropertyEditorRegistrar.java @@ -56,11 +56,11 @@ class OsgiPropertyEditorRegistrar implements PropertyEditorRegistrar { } catch (IOException ex) { throw (RuntimeException) new IllegalStateException( - "cannot load default propertiy editorsConfig configuration").initCause(ex); + "Cannot load default propertiy editorsConfig configuration").initCause(ex); } if (log.isTraceEnabled()) - log.trace("loaded property editors configuration " + editorsConfig); + log.trace("Loaded property editors configuration " + editorsConfig); editors = new LinkedHashMap(editorsConfig.size()); createEditors(classLoader, editorsConfig); @@ -87,7 +87,7 @@ private void createEditors(ClassLoader classLoader, Properties configuration) { Assert.isAssignable(PropertyEditor.class, editorClass); if (trace) - log.trace("adding property editor[" + editorClass + "] for type[" + key + "]"); + log.trace("Adding property editor[" + editorClass + "] for type[" + key + "]"); editors.put(key, editorClass); } } diff --git a/core/src/main/java/org/springframework/osgi/util/BundleDelegatingClassLoader.java b/core/src/main/java/org/springframework/osgi/util/BundleDelegatingClassLoader.java index 1590858f1..c38a895ce 100644 --- a/core/src/main/java/org/springframework/osgi/util/BundleDelegatingClassLoader.java +++ b/core/src/main/java/org/springframework/osgi/util/BundleDelegatingClassLoader.java @@ -26,7 +26,6 @@ import org.apache.commons.logging.Log; import org.osgi.framework.Bundle; import org.springframework.util.Assert; -import org.springframework.util.ObjectUtils; /** * ClassLoader backed by an OSGi bundle. Provides the ability to use a separate @@ -95,27 +94,6 @@ private BundleDelegatingClassLoader(Bundle bundle, ClassLoader bridgeLoader) { this.bridge = bridgeLoader; } - public boolean equals(Object o) { - if (this == o) - return true; - - if (!(o instanceof BundleDelegatingClassLoader)) - return false; - - final BundleDelegatingClassLoader bundleDelegatingClassLoader = (BundleDelegatingClassLoader) o; - - return (backingBundle.equals(bundleDelegatingClassLoader.backingBundle) && ObjectUtils.nullSafeEquals(bridge, - bundleDelegatingClassLoader.bridge)); - } - - public int hashCode() { - int hashCode = backingBundle.hashCode(); - if (bridge != null) - hashCode |= bridge.hashCode(); - - return hashCode; - } - protected Class findClass(String name) throws ClassNotFoundException { try { return this.backingBundle.loadClass(name); diff --git a/core/src/main/java/org/springframework/osgi/util/DebugUtils.java b/core/src/main/java/org/springframework/osgi/util/DebugUtils.java index eeedd7796..79e8b2368 100644 --- a/core/src/main/java/org/springframework/osgi/util/DebugUtils.java +++ b/core/src/main/java/org/springframework/osgi/util/DebugUtils.java @@ -18,8 +18,10 @@ import java.io.IOException; import java.net.URL; +import java.util.ArrayList; import java.util.Dictionary; import java.util.Enumeration; +import java.util.List; import java.util.StringTokenizer; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; @@ -48,6 +50,11 @@ */ public abstract class DebugUtils { + private static final String EQUALS = "="; + private static final String DOUBLE_QUOTE = "\""; + private static final String SEMI_COLON = ";"; + private static final String COMMA = ","; + /** use degradable logger */ private static final Log log = LogUtils.createLogger(DebugUtils.class); @@ -330,20 +337,53 @@ private static Version hasExport(Bundle bundle, String packageName) { */ private static Version getVersion(String stmt, String packageName) { if (stmt != null) { - for (StringTokenizer strok = new StringTokenizer(stmt, ","); strok.hasMoreTokens();) { - StringTokenizer parts = new StringTokenizer(strok.nextToken(), ";"); - String pkg = parts.nextToken().trim(); + String[] pkgs = splitIntoPackages(stmt); + + for (int packageIndex = 0; packageIndex < pkgs.length; packageIndex++) { + String pkgToken = pkgs[packageIndex].trim(); + String pkg = null; + Version version = null; + int firstDirectiveIndex = pkgToken.indexOf(SEMI_COLON); + if (firstDirectiveIndex > -1) { + pkg = pkgToken.substring(0, firstDirectiveIndex); + } + else { + pkg = pkgToken; + version = Version.emptyVersion; + } + + // check for version only if we have a match if (pkg.equals(packageName)) { - Version version = Version.emptyVersion; - for (; parts.hasMoreTokens();) { - String modifier = parts.nextToken().trim(); - if (modifier.startsWith("version")) { - String vstr = modifier.substring(modifier.indexOf("=") + 1).trim(); - if (vstr.startsWith("\"")) - vstr = vstr.substring(1); - if (vstr.endsWith("\"")) - vstr = vstr.substring(0, vstr.length() - 1); - version = Version.parseVersion(vstr); + // no version determined, find one + if (version == null) { + String[] directiveTokens = pkgToken.substring(firstDirectiveIndex + 1).split(SEMI_COLON); + for (int directiveTokenIndex = 0; directiveTokenIndex < directiveTokens.length; directiveTokenIndex++) { + String directive = directiveTokens[directiveTokenIndex].trim(); + // found it + if (directive.startsWith(Constants.VERSION_ATTRIBUTE)) { + String value = directive.substring(directive.indexOf(EQUALS) + 1).trim(); + + boolean lowEqualTo = value.startsWith("\"["); + boolean lowGreaterThen = value.startsWith("\"("); + if (lowEqualTo || lowGreaterThen) { + boolean highEqualTo = value.endsWith("]\""); + boolean highLessThen = value.endsWith(")\""); + + // remove brackets + value = value.substring(2, value.length() - 2); + int commaIndex = value.indexOf(COMMA); + + // TODO: currently, only the left side is considered + Version left = Version.parseVersion(value.substring(0, commaIndex)); + Version right = Version.parseVersion(value.substring(commaIndex + 1)); + + return left; + } + version = Version.parseVersion(value); + } + } + if (version == null) { + version = Version.emptyVersion; } } return version; @@ -352,4 +392,33 @@ private static Version getVersion(String stmt, String packageName) { } return null; } + + private static String[] splitIntoPackages(String stmt) { + // spit the statement into packages but consider " + List pkgs = new ArrayList(2); + + StringBuffer pkg = new StringBuffer(); + boolean ignoreComma = false; + for (int stringIndex = 0; stringIndex < stmt.length(); stringIndex++) { + char currentChar = stmt.charAt(stringIndex); + if (currentChar == ',') { + if (ignoreComma) { + pkg.append(currentChar); + } + else { + pkgs.add(pkg.toString()); + pkg = new StringBuffer(); + ignoreComma = false; + } + } + else { + if (currentChar == '\"') { + ignoreComma = !ignoreComma; + } + pkg.append(currentChar); + } + } + pkgs.add(pkg.toString()); + return (String[]) pkgs.toArray(new String[pkgs.size()]); + } } diff --git a/core/src/main/resources/org/springframework/osgi/compendium/config/spring-osgi-compendium-1.0.xsd b/core/src/main/resources/org/springframework/osgi/compendium/config/spring-osgi-compendium-1.0.xsd index e2b721882..3cb69cb90 100644 --- a/core/src/main/resources/org/springframework/osgi/compendium/config/spring-osgi-compendium-1.0.xsd +++ b/core/src/main/resources/org/springframework/osgi/compendium/config/spring-osgi-compendium-1.0.xsd @@ -14,7 +14,7 @@ diff --git a/core/src/main/resources/org/springframework/osgi/config/spring-osgi-1.0.xsd b/core/src/main/resources/org/springframework/osgi/config/spring-osgi-1.0.xsd index dd2841565..08fe902ab 100644 --- a/core/src/main/resources/org/springframework/osgi/config/spring-osgi-1.0.xsd +++ b/core/src/main/resources/org/springframework/osgi/config/spring-osgi-1.0.xsd @@ -13,7 +13,7 @@ diff --git a/core/src/test/java/org/springframework/osgi/TestUtils.java b/core/src/test/java/org/springframework/osgi/TestUtils.java index e0f10f474..1bef54890 100644 --- a/core/src/test/java/org/springframework/osgi/TestUtils.java +++ b/core/src/test/java/org/springframework/osgi/TestUtils.java @@ -13,10 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.osgi; import java.lang.reflect.Field; +import java.lang.reflect.Method; +import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils.FieldCallback; import org.springframework.util.ReflectionUtils.FieldFilter; @@ -32,6 +35,7 @@ public abstract class TestUtils { public static Object getFieldValue(final Object object, final String fieldName) { final Object[] fld = new Object[1]; ReflectionUtils.doWithFields(object.getClass(), new FieldCallback() { + public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { field.setAccessible(true); fld[0] = field.get(object); @@ -47,4 +51,29 @@ public boolean matches(Field field) { return fld[0]; } + + private static Object invokeMethod(final Object target, final Class targetClass, String methodName, Object[] args) { + Class[] types = null; + if (ObjectUtils.isEmpty(args)) { + types = new Class[0]; + } + else { + types = new Class[args.length]; + for (int objectIndex = 0; objectIndex < args.length; objectIndex++) { + types[objectIndex] = args[objectIndex].getClass(); + } + } + + Method method = ReflectionUtils.findMethod(targetClass, methodName, types); + ReflectionUtils.makeAccessible(method); + return ReflectionUtils.invokeMethod(method, target, args); + } + + public static Object invokeMethod(final Object target, String methodName, Object[] args) { + return invokeMethod(target, target.getClass(), methodName, args); + } + + public static Object invokeStaticMethod(final Class target, String methodName, Object[] args) { + return invokeMethod(null, target, methodName, args); + } } diff --git a/core/src/test/java/org/springframework/osgi/context/support/AbstractRefreshableOsgiBundleApplicationContextTest.java b/core/src/test/java/org/springframework/osgi/context/support/AbstractRefreshableOsgiBundleApplicationContextTest.java index 5cb738b06..2005c872b 100644 --- a/core/src/test/java/org/springframework/osgi/context/support/AbstractRefreshableOsgiBundleApplicationContextTest.java +++ b/core/src/test/java/org/springframework/osgi/context/support/AbstractRefreshableOsgiBundleApplicationContextTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.osgi.context.support; import java.io.IOException; @@ -24,7 +25,6 @@ import org.easymock.MockControl; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; -import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.core.io.Resource; @@ -39,6 +39,7 @@ public class AbstractRefreshableOsgiBundleApplicationContextTest extends TestCas private AbstractOsgiBundleApplicationContext context; + /* * (non-Javadoc) * @@ -67,7 +68,7 @@ protected void tearDown() throws Exception { } public void testBundleContext() throws Exception { - MockControl bundleCtxCtrl = MockControl.createStrictControl(BundleContext.class); + MockControl bundleCtxCtrl = MockControl.createNiceControl(BundleContext.class); BundleContext bundleCtx = (BundleContext) bundleCtxCtrl.getMock(); MockControl bundleCtrl = MockControl.createStrictControl(Bundle.class); @@ -90,8 +91,6 @@ public void testBundleContext() throws Exception { ClassLoader loader = context.getClassLoader(); assertTrue(loader instanceof BundleDelegatingClassLoader); - assertEquals( - BundleDelegatingClassLoader.createBundleClassLoaderFor(bundle, ProxyFactory.class.getClassLoader()), loader); // do some resource loading assertEquals(bundleResource, context.getResource(location)); diff --git a/core/src/test/java/org/springframework/osgi/util/BundleDelegatingClassLoaderTest.java b/core/src/test/java/org/springframework/osgi/util/BundleDelegatingClassLoaderTest.java index 329b8f32d..4f77418eb 100644 --- a/core/src/test/java/org/springframework/osgi/util/BundleDelegatingClassLoaderTest.java +++ b/core/src/test/java/org/springframework/osgi/util/BundleDelegatingClassLoaderTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.osgi.util; import java.net.URL; @@ -37,6 +38,7 @@ public class BundleDelegatingClassLoaderTest extends TestCase { private Bundle bundle; + protected void setUp() throws Exception { bundleCtrl = MockControl.createStrictControl(Bundle.class); bundle = (Bundle) bundleCtrl.getMock(); @@ -57,10 +59,9 @@ public void testEquals() { assertFalse(classLoader.equals(new Object())); assertEquals(classLoader, classLoader); - assertTrue(classLoader.equals(BundleDelegatingClassLoader.createBundleClassLoaderFor(bundle, + assertFalse(classLoader.equals(BundleDelegatingClassLoader.createBundleClassLoaderFor(bundle, ProxyFactory.class.getClassLoader()))); - // assertEquals(bundle.hashCode(), clientClassLoader.hashCode()); } public void testFindClass() throws Exception { diff --git a/core/src/test/java/org/springframework/osgi/util/DebugUtilsTest.java b/core/src/test/java/org/springframework/osgi/util/DebugUtilsTest.java new file mode 100644 index 000000000..5c9dec4b7 --- /dev/null +++ b/core/src/test/java/org/springframework/osgi/util/DebugUtilsTest.java @@ -0,0 +1,81 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.osgi.util; + +import junit.framework.TestCase; + +import org.osgi.framework.Version; +import org.springframework.osgi.TestUtils; +import org.springframework.util.ObjectUtils; + +/** + * Test for the critical logging path in debug utils. + * + * @author Costin Leau + * + */ +public class DebugUtilsTest extends TestCase { + + private Version getVersion(String statement, String pkg) { + return (Version) TestUtils.invokeStaticMethod(DebugUtils.class, "getVersion", new String[] { statement, pkg }); + } + + public void tstStringSplit() throws Exception { + String str = "gigel;version=\"[12,3.4)\";resolution:=optional,costel;resolution:=optional;version=\"1.2\",florel;resolution:=optional;version=1.2"; + String split = ",+?"; + String[] tokens = str.split(split); + System.out.println("tokens found " + tokens.length); + System.out.println("split is " + ObjectUtils.nullSafeToString(tokens)); + } + + public void testNoVersion() throws Exception { + String pkg = "foo"; + assertEquals(Version.emptyVersion, getVersion(pkg, pkg)); + } + + public void testSingleVersion() throws Exception { + String pkg = "foo"; + String version = "1.2"; + assertEquals(Version.parseVersion(version), getVersion(pkg + ";version=" + version, pkg)); + } + + public void testVersionRange() throws Exception { + String pkg = "foo"; + String version = "1.2.0.bla"; + assertEquals(Version.parseVersion(version), getVersion(pkg + ";version=\"[" + version + ",3.4\")", pkg)); + } + + public void testVersionRangePlusExtraDirective() throws Exception { + String pkg = "foo"; + String version = "1.2.0.bla"; + assertEquals(Version.parseVersion(version), getVersion(pkg + ";version=\"[" + version + + ",3.4\");resolution:=optional", pkg)); + } + + public void testNoVersionPlusExtraDirective() throws Exception { + String pkg = "foo"; + assertEquals(Version.emptyVersion, getVersion(pkg + ";resolution:=optional", pkg)); + } + + public void testSingleVersionPlusExtraDirective() throws Exception { + String pkg = "foo"; + String version = "1.2.0.bla"; + assertEquals(Version.parseVersion(version), getVersion(pkg + ";version=" + version + ";resolution:=optional", + pkg)); + } + +} diff --git a/docs/pom.xml b/docs/pom.xml index d279ce7d6..398654724 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -4,7 +4,7 @@ org.springframework.osgi spring-osgi pom - 1.0-SNAPSHOT + 1.0.3 Spring OSGi http://www.springframework.org/osgi @@ -26,6 +26,7 @@ single-page + pre-site generate-html generate-pdf @@ -58,10 +59,10 @@ - pre-site multi-page + pre-site generate-html @@ -84,7 +85,6 @@ - pre-site @@ -96,8 +96,7 @@ - index.xml,faq.xml,issues.xml - true + index.xml,faq.xml,issues.xml css/html.css true ${basedir}/src/docbkx diff --git a/docs/src/docbkx/appendix/appendix-archetype.xml b/docs/src/docbkx/appendix/appendix-archetype.xml new file mode 100644 index 000000000..26ffa2db1 --- /dev/null +++ b/docs/src/docbkx/appendix/appendix-archetype.xml @@ -0,0 +1,58 @@ + + + + Spring Dynamic Modules Maven Archetype + + As part of the distribution, Spring-DM provides a Maven archetype + which creates the basic structure of a Java project that uses Spring DM, especially useful to new users. To run the archetype (and create the new project), + simply run the following command line: + + \ +-DartifactId= \ +-Dversion= + ]]> + + The command above should be invoked as one line - the \ is used as a convenience to break the long line into smaller pieces + + The result of the command, is a Maven 2 project that defines a simple bean, configures it using + src/main/resources/META-INF/spring/bundle-context.xml and src/main/resources/META-INF/spring/bundle-context-osgi.xml + and provides unit and (out of the OSGi container) integration tests. The project is packaged as an OSGi bundle. + Notice that by default, the project does not contain a MANIFEST.MF for your project. The Maven infrastructure will generate it, through Apache + Felix bundle plug-in. To do that, run the following (from the project root): + + + + To avoid the confusion between the generated artifacts and maintained files, the manifest file resides under META-INF folder + while Spring configuration files under src/main/resources/META-INF directory. + + This will compile your project, pack it as a jar and create the OSGi manifest based on your classes under /META-INF folder (so + that users running Eclipse PDE can use it right away. To generate the manifest, without creating the OSGi bundle, simply run: + + + +

+ Generated Project Features at-a-glance + + + Packaged as an OSGi bundle + META-INF/MANIFEST.MF for OSGi bundle automatically generated + Simple bean interface and implementation defined. Interface and implementation types are in different packages, only the interface package + is exported by the bundle. + src/main/resources/META-INF/spring/bundle-context.xml is a Spring configuration file that defines the simple bean. + src/main/resources/META-INF/spring/bundle-context-osgi.xml is a spring configuration file ready for you to add bean + definitions from the osgi namespace (services, references etc.) + BeanImplTest case defined to unit test the simple bean + BeanIntegrationTest defined to fire up the non-osgi portions of the application context configuration and test + outside of OSGi + BeanOgiIntegrationTest defined to fire up the osgi portions of the application context configuration and test + inside of OSGi + .project, .classpath, and build.properties files created to enable use of this + project directly inside eclipse as a PDE plugin project + +
+ diff --git a/docs/src/docbkx/appendix/appendix-compendium.xml b/docs/src/docbkx/appendix/appendix-compendium.xml index eda546ef3..474aa346c 100644 --- a/docs/src/docbkx/appendix/appendix-compendium.xml +++ b/docs/src/docbkx/appendix/appendix-compendium.xml @@ -18,11 +18,11 @@ xmlns:osgix="http://www.springframework.org/schema/osgi-compendium" xmlns:beans="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/osgi - http://www.springframework.org/schema/osgi/spring-osgi-1.0.xsd + http://www.springframework.org/schema/osgi/spring-osgi.xsd http://www.springframework.org/schema/osgi-compendium - http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium-1.0.xsd + http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> + http://www.springframework.org/schema/beans/spring-beans.xsd"> Support for directly accessing configuration objects and their associated dictionaries, and for instantiating beans directly from configuration objects is on the Spring Dynamic Modules road map. See - for more information. + for more information. diff --git a/docs/src/docbkx/appendix/introduction.xml b/docs/src/docbkx/appendix/introduction.xml index 9d71f7301..af3113cff 100644 --- a/docs/src/docbkx/appendix/introduction.xml +++ b/docs/src/docbkx/appendix/introduction.xml @@ -19,6 +19,9 @@ describes how to integrate Spring-DM with Eclipse Plug-in Development Environment. + + describes the Spring-DM Maven 2 archetype + usage. describes some features that are included in the 1.0 distribution but are still considered early-access. The diff --git a/docs/src/docbkx/faq.xml b/docs/src/docbkx/faq.xml index e8d21cd5e..ccdd9ee80 100644 --- a/docs/src/docbkx/faq.xml +++ b/docs/src/docbkx/faq.xml @@ -96,7 +96,7 @@ JDK 1.4. Thus Spring Dynamic Modules cannot run on more constrained environments (such as the OSGi Minimum Execution Environment) unless Spring itself also runs in those environments. There are no current plans to make such a version of Spring. However as existing OSGi developers adopt Spring Dynamic Modules to simplify creation of OSGi applications and the user - base expands, the target audience can cover domains much broader than enterprise Java applications. + base expands, the target audience can cover domains much broader than enterprise Java applications. In time this could create a large enough demand to justify the investment needed to allow Spring and Spring-DM to run in restricted environments. @@ -246,6 +246,44 @@ url="http://radio.weblogs.com/0122027/2003/08/15.html">#1 #2 + + +
+ I have to use commons-logging, what can I do? + + If you have to use commons-logging (for example the jar is required by certain bundles) + then try using the most recent version commons-logging version (1.1+) as it provides more options + on the discovery process. Below are some settings that can be used to make commons-logging work + inside an OSGi environment: + + + + 1.0.x + + Unfortunately, commons-logging 1.0.x uses the thread context class loader (TCCL) always for loading + loggers implementations. Inside an OSGi environment, the TCCL is undefined and cannot be relied upon. + Since managing the TCCL is almost impossible as most loggers are defined as static fields that need to + resolved on class loading, using a different LogFactory is advised. One can use + the org.apache.commons.logging.LogFactory system property to specify a different + log factory however, the commons-logging bundle should be able to load this class. + + + 1.1.x + If using commons logging 1.1.x, one can turn off the tccl usage through use_tccl + property, part of the commons-logging.properties file. + . Additionally, + 1.1.x provides several system properties (such as org.apache.commons.logging.Log.allowFlawedContext, + org.apache.commons.logging.Log.allowFlawedDiscovery and + org.apache.commons.logging.Log.allowFlawedHierarchy) that can change the behavious of the discovery process. + See the LogFactoryImpl + javadoc for more details. + + + + In our tests, commons logging 1.1.x can be used with reasonable success inside OSGi. We haven't been able to find a generic + configuration for commons logging 1.0.x that works and that does not rely on fragile hacks dependent on the running environment. +
@@ -356,11 +394,24 @@ When using Spring-DM integration testing I get an exception about serialVersionUID. What is the problem? + This problem occurs on Spring DM versions up to 1.0 - consider upgrading to 1.0.1 or better. If you are stuck with 1.0 see below. When running an integration test, Spring-DM will duplicate the test instance and execute it inside OSGi. To avoid problems like this one, make sure you are using the same libraries (with the same version) as Spring-DM when running your test. This particular problem for example is caused by a JUnit 3.8.2 vs 3.8.x serialization compatibility. Make sure that you are using at least JUnit 3.8.2 for the execution of your tests. +
+ +
+ I'm using Eclipse PDE and I started getting some weird exceptions/behaviour. What's the matter? + + + Eclipse PDE uses Equinox OSGi platform underneath which (like other OSGi platforms) caches the bundles between re-runs. When + the cache is not properly updated, one can encounter strange behaviour (such as the new services/code being picked up) + or errors ranging from class versioning to linkage. Consider doing a complete clean build or, in case of Eclipse, + creating a new workspace or deleting the bundle folder (depends on each project settings but most users should find it at: + [workspace_dir]\.metadata\.plugins\org.eclipse.pde.core\OSGi\org.eclipse.osgi\bundles). +
\ No newline at end of file diff --git a/docs/src/docbkx/index.xml b/docs/src/docbkx/index.xml index dafdd93f5..cc3ac807a 100644 --- a/docs/src/docbkx/index.xml +++ b/docs/src/docbkx/index.xml @@ -66,6 +66,7 @@ + @@ -80,7 +81,7 @@ section. - --> + diff --git a/docs/src/docbkx/reference/bundle-app-context.xml b/docs/src/docbkx/reference/bundle-app-context.xml index 2f6bb35c8..6b5001bb5 100644 --- a/docs/src/docbkx/reference/bundle-app-context.xml +++ b/docs/src/docbkx/reference/bundle-app-context.xml @@ -33,7 +33,7 @@ The Spring Dynamic Modules Extender bundle Spring Dynamic Modules provides an OSGi bundle - org.springframework.osgi.bundles.extender. This + org.springframework.bundle.osgi.extender. This bundle is responsible for instantiating the Spring application contexts for your application bundles. It serves the same purpose as the ContextLoaderListener diff --git a/docs/src/docbkx/reference/bundles.xml b/docs/src/docbkx/reference/bundles.xml index c7db7bad2..a23cd138d 100644 --- a/docs/src/docbkx/reference/bundles.xml +++ b/docs/src/docbkx/reference/bundles.xml @@ -25,7 +25,7 @@ type Bundle. If the needed bundle is not installed, one can use location attribute - to indicate install or/and the action/dispose-action attributes + to indicate install or/and the action/destroy-action attributes provide declarative control over the bundle's lifecycle. The location attribute is used to specify a URL where the bundle jar file artifact can be found. The action attribute specifies the lifecycle operation to diff --git a/docs/src/docbkx/reference/deployment.xml b/docs/src/docbkx/reference/deployment.xml index f3c61eb9c..12aaf2a50 100644 --- a/docs/src/docbkx/reference/deployment.xml +++ b/docs/src/docbkx/reference/deployment.xml @@ -151,7 +151,7 @@ context ::= path ( ';' path ) * (';' directive) * For example: - Creates an application context synchronously, using all of the @@ -305,8 +305,28 @@ context ::= path ( ';' path ) * (';' directive) * + + +
+ Spring XML authoring support + + Spring 2.0 introduced (among other things) + easier XML configuration and + extensible XML authoring. The latter gives the ability of creating custom schemas that are discovered automatically (in non-OSGi environment) + by the Spring XML infrastructure by including them in the classpath. Spring-DM is aware of this process and supports it in OSGi environments so + that custom schemas are available to bundles that use them without any extra code or manifest declaration. + All bundles deployed in the OSGi space (whether they are Spring-powered or not) are scanned by Spring-DM for + custom Spring namespace declaration (by checking the bundle space forMETA-INF/spring.handlers and + META-INF/spring.schemas). If these are found, Spring-DM will make the schemas and the namespaces available through an OSGi + service that will be automatically used by Spring-powered bundles. This mean that if you deploy a bundle that uses a custom schema, all you have to do + is deploy the library that provides the namespace parser and the schema. + Bundles that embedded inside their classpath libraries that provide custom schemas will use these over those available in the OSGi space. However, + the namespaces of the embedded libraries will not shared with other bundles, that is, they will not be seen by any other bundle. + + In short, with using Spring-DM, custom Spring namespaces are supported transparently without any additional work. Embedded namespace providers will + have priority but will not be shared, as opposed to providers deployed as bundles which will be seen (and used) by others.
- +
Importing and Exporting packages @@ -378,7 +398,7 @@ context ::= path ( ';' path ) * (';' directive) * workarounds.
-
+
Diagnosing problems Your chosen OSGi platform implementation should be able to provide @@ -404,5 +424,6 @@ context ::= path ( ';' path ) * (';' directive) * the FAQ and Resources pages for more information on other logging libraries besides log4j. -
+
+ diff --git a/docs/src/docbkx/reference/service-registry.xml b/docs/src/docbkx/reference/service-registry.xml index 2aaf1a04e..fa8a74568 100644 --- a/docs/src/docbkx/reference/service-registry.xml +++ b/docs/src/docbkx/reference/service-registry.xml @@ -28,14 +28,14 @@ - ; + ; + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> @@ -80,9 +80,9 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/osgi - http://www.springframework.org/schema/osgi/spring-osgi-1.0.xsd + http://www.springframework.org/schema/osgi/spring-osgi.xsd http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> + http://www.springframework.org/schema/beans/spring-beans.xsd"> @@ -117,7 +117,7 @@ Using the OSGi namespace as a top-level namespace is particularly - convenient when following the recommendation of section 4.1 to use a + convenient when following the recommendation of to use a dedicated configuration file for all OSGi-related declarations.
@@ -318,13 +318,13 @@ public interface SubInterface extends SuperInterface {}]]> - + ]]> The Spring Dynamic Modules roadmap includes support for exporting properties registered in the OSGi Configuration Administration service as properties of the registered service. See - this appendix for more details. + for more details.
@@ -857,7 +857,7 @@ public interface SubInterface extends SuperInterface {}]]>
- Getting a hold of the managed service service reference + Getting a hold of the managed service reference Spring-DM can automatically convert a managed OSGi service to service reference. That is, if the property into which a reference bean @@ -1454,7 +1454,7 @@ public void anyMethodName(ServiceReference ref);]]> - + ]]> @@ -1478,7 +1478,7 @@ public void anyMethodName(ServiceReference ref);]]> The declaration above while valid, creates a dependecy between the listener and the importer it is defined upon. In order to create the importer, the listener has to be resolved and created but in order to do that, the importer called service needs to be retrieved (instantiated and configured). This cycle needs to be broken - down so that at least one bean can be fully created and configured. This scenario is supported is supported by Spring-DM + down so that at least one bean can be fully created and configured. This scenario is supported by Spring-DM for both exporter and importers however, if the listener is defined as a nested bean, the cycle cannot be resolved: @@ -1490,7 +1490,7 @@ public void anyMethodName(ServiceReference ref);]]> - + diff --git a/docs/src/docbkx/reference/testing.xml b/docs/src/docbkx/reference/testing.xml index 5dea7aadd..7a9376444 100644 --- a/docs/src/docbkx/reference/testing.xml +++ b/docs/src/docbkx/reference/testing.xml @@ -86,8 +86,9 @@ public void testComponent() throws Exception { problem that appears when building base JUnit classes, by decoupling the runner from the test through annotations, Spring-DM cannot use them since it has to support Java 1.4. - Running the tests on JDK 1.5 is not an option since the differences - between 1.4 and 1.5 are quite subtle especially when dealing with synchronization. + However, it is planned for the future to provide an optional, + JVM 5-based testing extension to integrate the existing testing framework + with the aforementioned libraries. @@ -195,6 +196,7 @@ spring-osgi-extender, etc... TestBundle-testOsgiPlatformStarts-com.your.package. As you can see, the testing framework installs the mandatory requirements required for running the test such as the Spring, Spring-DM, slf4j jars among others. +
@@ -274,16 +276,29 @@ Reason: Missing Constraint: Import-Package: org.apache.commons.lang.time; versio are advanced topics as they increase the complexity of your test infrastructure. -
- Customizing test manifest +
+ Customizing the test manifest There are cases where the auto-generated test manifest does not suite the needs of the test. - For example the manifest requires some different headers or a certain package needs to be an optional import. - To provide your own manifest, simply override getManifestLocations(): + For example the manifest requires some different headers or a certain package needs to be an optional import. + + For simple cases, one can work directly with the generated manifest - in the example below, the bundle class path + is being specified: + + + + + Another alternative is to provide your own manifest by overriding getManifestLocations(): + return "classpath:com/xyz/abc/test/MyTestTest.MF"; +}]]> - However, the manifest requires some the folowing entry: + However each manifest needs the following entry: Bundle-Activator: org.springframework.osgi.test.JUnitTestActivator since without it, the testing infrastructure cannot function properly. Also, one needs to import JUnit, Spring and Spring-DM specific packages used by the base test suite: @@ -362,6 +377,41 @@ Reason: Missing Constraint: Import-Package: org.apache.commons.lang.time; versio
+
+ Understanding the <code>MANIFEST.MF</code> generation + + A useful feature of the testing framework represents the automatic creation of the test manifest based on the test bundle content. The manifest + creator component uses byte-code analysis to determine the packages imported by the test classes so that it can generate the proper OSGi directives for them. + Since the generated bundle is used for running a test, the creator will use the following assumptions: + + + No packages will be exported. The on-the-fly bundle is used for running a test which + (usually) consumes OSGi packages for its execution. This behaviour can be changed by customizing + the manifest. + + + Split packages (i.e. classes from the same package can come from different bundles) are not supported. + + + This means that packages present in the test framework are considered complete and no Import-Package entry will be generated for them. + To avoid this problem, consider using sub-packages or moving the classes inside one bundle. Note that split packages are discouraged due to the + issues associated with them (see the OSGi Core spec, Chapter 3.13 - Required Bundles). + + + + The test bundle contains only test classes. + The byte-code parser will look only at the test classes hierarchy. Any other class included in the bundle, will not be considered so no imports + will be generated for it. Consider customizing the manifest yourself or attaching the extra code as inner classes to the test class. A future version + of Spring-DM might alleviate this problem by inspecting all classes included in the test bundle. + + + + + The reason behind the lack of such features is the byte-code parser is aimed to be simple and fast at creating test manifests - + it is not meant as a general-purpose tool for creating OSGi artifacts. + +
+
diff --git a/docs/src/docbkx/resources/xsl/fopdf.xsl b/docs/src/docbkx/resources/xsl/fopdf.xsl index ac2fff485..d9232dff2 100644 --- a/docs/src/docbkx/resources/xsl/fopdf.xsl +++ b/docs/src/docbkx/resources/xsl/fopdf.xsl @@ -439,6 +439,23 @@ + + + + + + + + + + + + + + diff --git a/extender/pom.xml b/extender/pom.xml index 3f2914277..50fa00ab9 100644 --- a/extender/pom.xml +++ b/extender/pom.xml @@ -3,7 +3,7 @@ spring-osgi org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi @@ -27,7 +27,9 @@ org.apache.commons.logging*, - org.springframework.beans.factory.xml, + org.springframework.osgi*;version=${spring.osgi.import.version}, + org.springframework.beans.factory.xml;version=${spring.base.version}, + org.springframework*;version=${spring.base.version}, org.xml.sax;resolution:=optional, * diff --git a/extender/src/main/java/org/springframework/osgi/extender/internal/ContextLoaderListener.java b/extender/src/main/java/org/springframework/osgi/extender/internal/ContextLoaderListener.java index 6bcb82f82..95bd9539d 100644 --- a/extender/src/main/java/org/springframework/osgi/extender/internal/ContextLoaderListener.java +++ b/extender/src/main/java/org/springframework/osgi/extender/internal/ContextLoaderListener.java @@ -112,7 +112,7 @@ public void bundleChanged(BundleEvent event) { synchronized (monitor) { if (isClosed) { if (trace) - log.trace("listener is closed; events are being ignored"); + log.trace("Listener is closed; events are being ignored"); return; } } @@ -125,7 +125,7 @@ public void bundleChanged(BundleEvent event) { } catch (Exception ex) { /* log exceptions before swallowing */ - log.warn("got exception while handling event " + event, ex); + log.warn("Got exception while handling event " + event, ex); } } @@ -472,7 +472,7 @@ public void run() { // eliminate context closedContexts.remove(context); if (log.isDebugEnabled()) - log.debug("closing appCtx " + context.getDisplayName()); + log.debug("Closing appCtx " + context.getDisplayName()); context.close(); } }); @@ -547,7 +547,7 @@ private void stopTaskExecutor() { } else - log.debug("all listeners closed"); + log.debug("All listeners closed"); } } } @@ -561,7 +561,7 @@ private void stopTaskExecutor() { private boolean handlerBundleMatchesExtenderVersion(Bundle bundle) { if (!ConfigUtils.matchExtenderVersionRange(bundle, extenderVersion)) { if (log.isDebugEnabled()) - log.debug("bundle [" + OsgiStringUtils.nullSafeNameAndSymName(bundle) + log.debug("Bundle [" + OsgiStringUtils.nullSafeNameAndSymName(bundle) + "] expects an extender w/ version[" + OsgiBundleUtils.getHeaderAsVersion(bundle, ConfigUtils.EXTENDER_VERSION) + "] which does not match current extender w/ version[" + extenderVersion @@ -594,7 +594,7 @@ protected void maybeCreateApplicationContextFor(Bundle bundle) { if (!ConfigUtils.matchExtenderVersionRange(bundle, extenderVersion)) { if (log.isDebugEnabled()) - log.debug("bundle [" + OsgiStringUtils.nullSafeNameAndSymName(bundle) + log.debug("Bundle [" + OsgiStringUtils.nullSafeNameAndSymName(bundle) + "] expects an extender w/ version[" + OsgiBundleUtils.getHeaderAsVersion(bundle, ConfigUtils.EXTENDER_VERSION) + "] which does not match current extender w/ version[" + extenderVersion @@ -604,7 +604,7 @@ protected void maybeCreateApplicationContextFor(Bundle bundle) { ApplicationContextConfiguration config = new ApplicationContextConfiguration(bundle); if (log.isDebugEnabled()) - log.debug("created config " + config); + log.debug("Created config " + config); if (!config.isSpringPoweredBundle()) { return; @@ -707,9 +707,9 @@ private void addAnnotationBPP(DelegatedExecutionOsgiBundleApplicationContext app Map config = getExternalConfiguration(); Object setting = config.get(AUTO_ANNOTATION_PROCESSING); - if (setting != null && setting instanceof String && Boolean.getBoolean((String) setting)) { + if (setting != null && setting instanceof String && Boolean.valueOf((String) setting).booleanValue()) { - log.info("enabled automatic Spring-DM annotation processing; [" + AUTO_ANNOTATION_PROCESSING + "=" + log.info("Enabled automatic Spring-DM annotation processing; [" + AUTO_ANNOTATION_PROCESSING + "=" + setting + "]"); // Try and load the annotation code if it exists @@ -729,13 +729,13 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) }); } catch (ClassNotFoundException exception) { - log.info("spring-dm annotation package cannot be found; automatic annotation processing is disabled"); + log.info("Spring-dm annotation package cannot be found; automatic annotation processing is disabled"); if (log.isDebugEnabled()) - log.debug("cannot load annotatoin bpp", exception); + log.debug("Cannot load annotatoin bpp", exception); } } else { - log.info("disabled automatic Spring-DM annotation processing; [ " + AUTO_ANNOTATION_PROCESSING + "=" + log.info("Disabled automatic Spring-DM annotation processing; [ " + AUTO_ANNOTATION_PROCESSING + "=" + setting + "]"); } diff --git a/extender/src/main/java/org/springframework/osgi/extender/internal/dependencies/startup/DependencyServiceManager.java b/extender/src/main/java/org/springframework/osgi/extender/internal/dependencies/startup/DependencyServiceManager.java index e05a63066..a8a114c80 100644 --- a/extender/src/main/java/org/springframework/osgi/extender/internal/dependencies/startup/DependencyServiceManager.java +++ b/extender/src/main/java/org/springframework/osgi/extender/internal/dependencies/startup/DependencyServiceManager.java @@ -97,10 +97,8 @@ public void serviceChanged(ServiceEvent serviceEvent) { if (unsatisfiedDependencies.isEmpty()) { deregister(); // context.listener = null; - if (debug) { - log.debug("No outstanding dependencies, completing initialization for " - + context.getDisplayName()); - } + log.info("No outstanding OSGi service dependencies, completing initialization for " + + context.getDisplayName()); // execute task to complete initialization // NOTE: the runnable should be able to delegate any long @@ -208,7 +206,7 @@ protected void findServiceDependencies() { dependencies.add(dependency); if (!dependency.isServicePresent()) { if (debug) - log.debug("adding dependency for importer " + beanName); + log.debug("adding OSGi service dependency for importer " + beanName); unsatisfiedDependencies.add(dependency); } } @@ -217,10 +215,8 @@ protected void findServiceDependencies() { currentThread.setContextClassLoader(oldTCCL); } - if (debug) { - log.debug(dependencies.size() + " dependencies, " + unsatisfiedDependencies.size() + " unsatisfied for " - + context.getDisplayName()); - } + log.info(dependencies.size() + " OSGi service dependencies, " + unsatisfiedDependencies.size() + + " unsatisfied for " + context.getDisplayName()); } diff --git a/extender/src/main/java/org/springframework/osgi/extender/internal/dependencies/startup/DependencyWaiterApplicationContextExecutor.java b/extender/src/main/java/org/springframework/osgi/extender/internal/dependencies/startup/DependencyWaiterApplicationContextExecutor.java index 4db3bcf17..66f231cf7 100644 --- a/extender/src/main/java/org/springframework/osgi/extender/internal/dependencies/startup/DependencyWaiterApplicationContextExecutor.java +++ b/extender/src/main/java/org/springframework/osgi/extender/internal/dependencies/startup/DependencyWaiterApplicationContextExecutor.java @@ -107,7 +107,7 @@ public void run() { } /** - * Crete the Runnable action which will complete the context creation + * Create the Runnable action which will complete the context creation * process. This process can be called synchronously or asynchronously, * depending on context configuration and availability of dependencies. * @@ -195,7 +195,7 @@ protected void stageOne() { try { if (debug) - log.debug("calling preRefresh on " + getDisplayName()); + log.debug("Calling preRefresh on " + getDisplayName()); synchronized (monitor) { @@ -213,7 +213,7 @@ protected void stageOne() { } if (debug) - log.debug("prerefresh completed; determining dependencies..."); + log.debug("Pre-refresh completed; determining dependencies..."); Runnable task = null; @@ -240,9 +240,8 @@ public void run() { // all dependencies are met, just go with stageTwo if (dl.isSatisfied()) { - if (debug) { - log.debug("No outstanding dependencies, completing initialization for " + getDisplayName()); - } + + log.info("No outstanding OSGi service dependencies, completing initialization for " + getDisplayName()); stageTwo(); } @@ -251,13 +250,13 @@ public void run() { // register a listener to look for them dependencyDetector = dl; if (debug) - log.debug("registering service dependency dependencyDetector for " + getDisplayName()); + log.debug("Registering service dependency dependencyDetector for " + getDisplayName()); dependencyDetector.register(); if (synchronousWait) { waitBarrier.increment(); if (debug) - log.debug("synchronous wait-for-dependencies; waiting..."); + log.debug("Synchronous wait-for-dependencies; waiting..."); // if waiting times out... if (waitBarrier.waitForZero(timeout)) { @@ -272,7 +271,7 @@ public void run() { startWatchDog(); if (debug) - log.debug("asynch wait-for-dependencies; ending method"); + log.debug("Asynch wait-for-dependencies; ending method"); } } @@ -287,15 +286,15 @@ protected void stageTwo() { boolean debug = log.isDebugEnabled(); if (debug) - log.debug("starting stage two for " + getDisplayName()); + log.debug("Starting stage two for " + getDisplayName()); synchronized (monitor) { -// if (state == ContextState.DEPENDENCIES_RESOLVED) { -// if (debug) -// log.debug("context [" + getDisplayName() + "] already in state (" + state + "); bailing out"); -// return; -// } + // if (state == ContextState.DEPENDENCIES_RESOLVED) { + // if (debug) + // log.debug("context [" + getDisplayName() + "] already in state (" + state + "); bailing out"); + // return; + // } if (state != ContextState.RESOLVING_DEPENDENCIES) { logWrongState(ContextState.RESOLVING_DEPENDENCIES); return; @@ -327,7 +326,7 @@ public void close() { } if (debug) - log.debug("closing appCtx for " + getDisplayName()); + log.debug("Closing appCtx for " + getDisplayName()); if (dependencyDetector != null) { dependencyDetector.deregister(); @@ -335,14 +334,14 @@ public void close() { if (state == ContextState.STARTED) { if (debug) - log.debug("shutting down normaly appCtx " + getDisplayName()); + log.debug("Shutting down normaly appCtx " + getDisplayName()); // close the context only if it was actually started state = ContextState.STOPPED; normalShutdown = true; } else { if (debug) - log.debug("no need to stop context (it hasn't been started yet)"); + log.debug("No need to stop context (it hasn't been started yet)"); state = ContextState.INTERRUPTED; } } @@ -353,7 +352,7 @@ public void close() { } } catch (Exception ex) { - log.fatal("could not succesfully close context " + delegateContext, ex); + log.fatal("Could not succesfully close context " + delegateContext, ex); } finally { monitorCounter.decrement(); @@ -412,7 +411,7 @@ private void timeout() { dependencyDetector.deregister(); } - log.warn("timeout occured before finding service dependencies for [" + delegateContext.getDisplayName() + log.warn("Timeout occured before finding service dependencies for [" + delegateContext.getDisplayName() + "]"); ApplicationContextException e = new ApplicationContextException("Application context initializition for '" @@ -498,7 +497,7 @@ public void setWatchdog(Timer watchdog) { * @param expected the expected value for the context state. */ private void logWrongState(ContextState expected) { - log.error("expecting state (" + expected + ") not (" + state + ") for context [" + getDisplayName() + log.error("Expecting state (" + expected + ") not (" + state + ") for context [" + getDisplayName() + "]; assuming an interruption and bailing out"); } diff --git a/extender/src/main/java/org/springframework/osgi/extender/internal/support/NamespacePlugins.java b/extender/src/main/java/org/springframework/osgi/extender/internal/support/NamespacePlugins.java index 8ca98eb7d..e70bc362b 100644 --- a/extender/src/main/java/org/springframework/osgi/extender/internal/support/NamespacePlugins.java +++ b/extender/src/main/java/org/springframework/osgi/extender/internal/support/NamespacePlugins.java @@ -60,19 +60,6 @@ private static class Plugin implements NamespaceHandlerResolver, EntityResolver private Plugin(Bundle bundle) { this.bundle = bundle; - -// ClassLoader springAopClassLoader = null; -// try { -// springAopClassLoader = BundleDelegatingClassLoader.class.getClassLoader().loadClass( -// "org.springframework.aop.framework.ProxyFactory").getClassLoader(); -// } -// catch (ClassNotFoundException ex) { -// throw new RuntimeException("cannot detect Spring AOP jar", ex); -// } -// - // the bundle classloader used for the namespace parser/resolver - // discovery and instantiation -// ClassLoader loader = BundleDelegatingClassLoader.createBundleClassLoaderFor(bundle, springAopClassLoader); ClassLoader loader = BundleDelegatingClassLoader.createBundleClassLoaderFor(bundle); entity = new DelegatingEntityResolver(loader); diff --git a/integration-tests/bundles/annotation.dependency.test.bundle/.classpath b/integration-tests/bundles/annotation.dependency.test.bundle/.classpath index d27e472c7..ea526626b 100644 --- a/integration-tests/bundles/annotation.dependency.test.bundle/.classpath +++ b/integration-tests/bundles/annotation.dependency.test.bundle/.classpath @@ -5,6 +5,6 @@ - + diff --git a/integration-tests/bundles/annotation.dependency.test.bundle/pom.xml b/integration-tests/bundles/annotation.dependency.test.bundle/pom.xml index 619de3223..3d782a2c4 100644 --- a/integration-tests/bundles/annotation.dependency.test.bundle/pom.xml +++ b/integration-tests/bundles/annotation.dependency.test.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/annotation.test.bundle/.classpath b/integration-tests/bundles/annotation.test.bundle/.classpath index 037aebc7c..60a0c65c4 100644 --- a/integration-tests/bundles/annotation.test.bundle/.classpath +++ b/integration-tests/bundles/annotation.test.bundle/.classpath @@ -5,6 +5,6 @@ - + diff --git a/integration-tests/bundles/annotation.test.bundle/pom.xml b/integration-tests/bundles/annotation.test.bundle/pom.xml index 2741ce4fd..1ab5062cc 100644 --- a/integration-tests/bundles/annotation.test.bundle/pom.xml +++ b/integration-tests/bundles/annotation.test.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/cardinality.0to1.bundle/pom.xml b/integration-tests/bundles/cardinality.0to1.bundle/pom.xml index 43335fe2e..ca60aa2ac 100644 --- a/integration-tests/bundles/cardinality.0to1.bundle/pom.xml +++ b/integration-tests/bundles/cardinality.0to1.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 diff --git a/integration-tests/bundles/config.bundle/async.nowait.bundle/pom.xml b/integration-tests/bundles/config.bundle/async.nowait.bundle/pom.xml index 518d74fcf..6051daa53 100644 --- a/integration-tests/bundles/config.bundle/async.nowait.bundle/pom.xml +++ b/integration-tests/bundles/config.bundle/async.nowait.bundle/pom.xml @@ -5,7 +5,7 @@ org.springframework.osgi.iandt.config.bundle org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 diff --git a/integration-tests/bundles/config.bundle/async.wait.bundle/pom.xml b/integration-tests/bundles/config.bundle/async.wait.bundle/pom.xml index 10be2512c..33e8e032e 100644 --- a/integration-tests/bundles/config.bundle/async.wait.bundle/pom.xml +++ b/integration-tests/bundles/config.bundle/async.wait.bundle/pom.xml @@ -5,7 +5,7 @@ org.springframework.osgi.iandt.config.bundle org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 diff --git a/integration-tests/bundles/config.bundle/no.publish.bundle/pom.xml b/integration-tests/bundles/config.bundle/no.publish.bundle/pom.xml index c22217f7a..1c6ce8fc7 100644 --- a/integration-tests/bundles/config.bundle/no.publish.bundle/pom.xml +++ b/integration-tests/bundles/config.bundle/no.publish.bundle/pom.xml @@ -5,7 +5,7 @@ org.springframework.osgi.iandt.config.bundle org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 diff --git a/integration-tests/bundles/config.bundle/pom.xml b/integration-tests/bundles/config.bundle/pom.xml index f51f3453d..34ddfea5a 100644 --- a/integration-tests/bundles/config.bundle/pom.xml +++ b/integration-tests/bundles/config.bundle/pom.xml @@ -3,7 +3,7 @@ org.springframework.osgi spring-osgi-integration-test-bundles - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/config.bundle/sync.nowait.bundle/pom.xml b/integration-tests/bundles/config.bundle/sync.nowait.bundle/pom.xml index cbe67c64a..d06c5db94 100644 --- a/integration-tests/bundles/config.bundle/sync.nowait.bundle/pom.xml +++ b/integration-tests/bundles/config.bundle/sync.nowait.bundle/pom.xml @@ -5,7 +5,7 @@ org.springframework.osgi.iandt.config.bundle org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 diff --git a/integration-tests/bundles/config.bundle/sync.tail.bundle/pom.xml b/integration-tests/bundles/config.bundle/sync.tail.bundle/pom.xml index 579fae651..817f9ad9a 100644 --- a/integration-tests/bundles/config.bundle/sync.tail.bundle/pom.xml +++ b/integration-tests/bundles/config.bundle/sync.tail.bundle/pom.xml @@ -5,7 +5,7 @@ org.springframework.osgi.iandt.config.bundle org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 diff --git a/integration-tests/bundles/config.bundle/sync.wait.bundle/pom.xml b/integration-tests/bundles/config.bundle/sync.wait.bundle/pom.xml index 5d257549f..6b2037fa5 100644 --- a/integration-tests/bundles/config.bundle/sync.wait.bundle/pom.xml +++ b/integration-tests/bundles/config.bundle/sync.wait.bundle/pom.xml @@ -5,7 +5,7 @@ org.springframework.osgi.iandt.config.bundle org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 diff --git a/integration-tests/bundles/config.file.with.dots.bundle/pom.xml b/integration-tests/bundles/config.file.with.dots.bundle/pom.xml index ab7b7754d..ac19e93d0 100644 --- a/integration-tests/bundles/config.file.with.dots.bundle/pom.xml +++ b/integration-tests/bundles/config.file.with.dots.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/configuration.test.bundle/pom.xml b/integration-tests/bundles/configuration.test.bundle/pom.xml index 4dab58f6e..43ad0e860 100644 --- a/integration-tests/bundles/configuration.test.bundle/pom.xml +++ b/integration-tests/bundles/configuration.test.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/deadlock.bundle/pom.xml b/integration-tests/bundles/deadlock.bundle/pom.xml index 013535422..8c5a1fcf8 100644 --- a/integration-tests/bundles/deadlock.bundle/pom.xml +++ b/integration-tests/bundles/deadlock.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/dependency.factory.bundle/pom.xml b/integration-tests/bundles/dependency.factory.bundle/pom.xml index f9aa8b36b..ff647c330 100644 --- a/integration-tests/bundles/dependency.factory.bundle/pom.xml +++ b/integration-tests/bundles/dependency.factory.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/dependency.test.bundle/pom.xml b/integration-tests/bundles/dependency.test.bundle/pom.xml index a7ae38d22..9693a524c 100644 --- a/integration-tests/bundles/dependency.test.bundle/pom.xml +++ b/integration-tests/bundles/dependency.test.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/error.bundle/pom.xml b/integration-tests/bundles/error.bundle/pom.xml index 5ef71b056..d041e157d 100644 --- a/integration-tests/bundles/error.bundle/pom.xml +++ b/integration-tests/bundles/error.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/extender.version.bundle/pom.xml b/integration-tests/bundles/extender.version.bundle/pom.xml index 1ac6bec40..14db27b9e 100644 --- a/integration-tests/bundles/extender.version.bundle/pom.xml +++ b/integration-tests/bundles/extender.version.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/io.fragment.1.bundle/pom.xml b/integration-tests/bundles/io.fragment.1.bundle/pom.xml index de0ef1ea6..cb0e492fd 100644 --- a/integration-tests/bundles/io.fragment.1.bundle/pom.xml +++ b/integration-tests/bundles/io.fragment.1.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/io.fragment.2.bundle/pom.xml b/integration-tests/bundles/io.fragment.2.bundle/pom.xml index 1e6b9dad4..fd0546288 100644 --- a/integration-tests/bundles/io.fragment.2.bundle/pom.xml +++ b/integration-tests/bundles/io.fragment.2.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/lifecycle.test.bundle/pom.xml b/integration-tests/bundles/lifecycle.test.bundle/pom.xml index bb233628f..56e62629d 100644 --- a/integration-tests/bundles/lifecycle.test.bundle/pom.xml +++ b/integration-tests/bundles/lifecycle.test.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/pom.xml b/integration-tests/bundles/pom.xml index 797dad1ee..edca970bf 100644 --- a/integration-tests/bundles/pom.xml +++ b/integration-tests/bundles/pom.xml @@ -3,7 +3,7 @@ spring-osgi org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi @@ -56,6 +56,7 @@ config.bundle tccl.bundle config.file.with.dots.bundle + proxy.creator.bundle diff --git a/integration-tests/bundles/proxy.creator.bundle/.classpath b/integration-tests/bundles/proxy.creator.bundle/.classpath new file mode 100644 index 000000000..4516b4fb0 --- /dev/null +++ b/integration-tests/bundles/proxy.creator.bundle/.classpath @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/integration-tests/bundles/proxy.creator.bundle/.project b/integration-tests/bundles/proxy.creator.bundle/.project new file mode 100644 index 000000000..28ae1f083 --- /dev/null +++ b/integration-tests/bundles/proxy.creator.bundle/.project @@ -0,0 +1,28 @@ + + + Proxy Creator + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/integration-tests/bundles/proxy.creator.bundle/pom.xml b/integration-tests/bundles/proxy.creator.bundle/pom.xml new file mode 100644 index 000000000..f8aab1fa6 --- /dev/null +++ b/integration-tests/bundles/proxy.creator.bundle/pom.xml @@ -0,0 +1,20 @@ + + + spring-osgi-integration-test-bundles + org.springframework.osgi + 1.0.3 + + + 4.0.0 + org.springframework.osgi + org.springframework.osgi.iandt.proxy.creator + jar + Spring OSGi Test Bundle: Spring Proxy Creator bundle + An OSGi bundle that creates proxies internally and is updated + http://www.springframework.org/osgi + + + diff --git a/integration-tests/bundles/proxy.creator.bundle/src/main/java/org/springframework/osgi/iandt/proxycreator/SomeImplementation.java b/integration-tests/bundles/proxy.creator.bundle/src/main/java/org/springframework/osgi/iandt/proxycreator/SomeImplementation.java new file mode 100644 index 000000000..cd3c7232d --- /dev/null +++ b/integration-tests/bundles/proxy.creator.bundle/src/main/java/org/springframework/osgi/iandt/proxycreator/SomeImplementation.java @@ -0,0 +1,37 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.osgi.iandt.proxycreator; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @author Costin Leau + * + */ +public class SomeImplementation implements SomeInterface { + + /** logger */ + private static final Log log = LogFactory.getLog(SomeImplementation.class); + + + public boolean doSmth(Object argument) { + log.info("received " + argument); + return true; + } + +} diff --git a/integration-tests/bundles/proxy.creator.bundle/src/main/java/org/springframework/osgi/iandt/proxycreator/SomeInterface.java b/integration-tests/bundles/proxy.creator.bundle/src/main/java/org/springframework/osgi/iandt/proxycreator/SomeInterface.java new file mode 100644 index 000000000..e3fe3588b --- /dev/null +++ b/integration-tests/bundles/proxy.creator.bundle/src/main/java/org/springframework/osgi/iandt/proxycreator/SomeInterface.java @@ -0,0 +1,29 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.osgi.iandt.proxycreator; + +/** + * Interface. We cannot used one from outside the bundle since we want to expose + * the CL problems. + * + * @author Costin Leau + * + */ +public interface SomeInterface { + + boolean doSmth(Object argument); +} diff --git a/integration-tests/bundles/proxy.creator.bundle/src/main/java/org/springframework/osgi/iandt/proxycreator/Test.java b/integration-tests/bundles/proxy.creator.bundle/src/main/java/org/springframework/osgi/iandt/proxycreator/Test.java new file mode 100644 index 000000000..9c178acc8 --- /dev/null +++ b/integration-tests/bundles/proxy.creator.bundle/src/main/java/org/springframework/osgi/iandt/proxycreator/Test.java @@ -0,0 +1,75 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.osgi.iandt.proxycreator; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Simple test that touches proxies created through spring. While this test + * doesn't do much by itself, it should always work even when the bundle it runs + * in, is updated. Failure to do so implies incorrect usage of the appropriate + * class loader. + * + * Thanks to Alexei Akimov for the proof of concept. + * + * @author Costin Leau + * + */ +public class Test { + + /** logger */ + private static final Log log = LogFactory.getLog(Test.class); + + private SomeInterface jdkProxy; + private SomeInterface cglibProxy; + + + /** + * Sets echo JDK proxy + * + * @param echoJdkProxy + */ + public void setJdkProxy(SomeInterface echoJdkProxy) { + this.jdkProxy = echoJdkProxy; + } + + /** + * Sets echo CGLIB proxy + * + * @param echoCglibProxy + */ + public void setCglibProxy(SomeInterface echoCglibProxy) { + this.cglibProxy = echoCglibProxy; + } + + public void test() { + testInterface(jdkProxy); + testInterface(cglibProxy); + } + + // interact with the proxy to make sure the weaving process is successful + private void testInterface(SomeInterface intfs) { + try { + log.info("testing proxy interception..."); + intfs.doSmth("hangar 18"); + } + catch (Throwable th) { + log.error("caught exception", th); + } + } +} diff --git a/integration-tests/bundles/proxy.creator.bundle/src/main/resources/META-INF/MANIFEST.MF b/integration-tests/bundles/proxy.creator.bundle/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 000000000..adf07797a --- /dev/null +++ b/integration-tests/bundles/proxy.creator.bundle/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,7 @@ +Bundle-Name: org-springframework-osgi-iandt-proxy-creator +Bundle-SymbolicName: org.springframework.osgi.iandt.proxy.creator +Bundle-ManifestVersion: 2 +Bundle-Version: 1.0 +Import-Package: org.apache.commons.logging +Export-Package: org.springframework.osgi.iandt.service.listener +Spring-Context: *; eagerly-init-importers:=true diff --git a/integration-tests/bundles/proxy.creator.bundle/src/main/resources/META-INF/spring/context.xml b/integration-tests/bundles/proxy.creator.bundle/src/main/resources/META-INF/spring/context.xml new file mode 100644 index 000000000..652dc77bb --- /dev/null +++ b/integration-tests/bundles/proxy.creator.bundle/src/main/resources/META-INF/spring/context.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + org.springframework.osgi.iandt.proxycreator.SomeInterface + + + loggerInterceptor + + + + + + + loggerInterceptor + + + + + + + + + diff --git a/integration-tests/bundles/reference.test.bundle/pom.xml b/integration-tests/bundles/reference.test.bundle/pom.xml index 63d74d6e4..1ffc41c30 100644 --- a/integration-tests/bundles/reference.test.bundle/pom.xml +++ b/integration-tests/bundles/reference.test.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/scoped.a.bundle/pom.xml b/integration-tests/bundles/scoped.a.bundle/pom.xml index 95476d997..101acd8e5 100644 --- a/integration-tests/bundles/scoped.a.bundle/pom.xml +++ b/integration-tests/bundles/scoped.a.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/scoped.b.bundle/pom.xml b/integration-tests/bundles/scoped.b.bundle/pom.xml index 04108dfe0..8f090bf67 100644 --- a/integration-tests/bundles/scoped.b.bundle/pom.xml +++ b/integration-tests/bundles/scoped.b.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/scoped.common.bundle/pom.xml b/integration-tests/bundles/scoped.common.bundle/pom.xml index 8f1df845b..60c71eadc 100644 --- a/integration-tests/bundles/scoped.common.bundle/pom.xml +++ b/integration-tests/bundles/scoped.common.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/service.listener.bundle/pom.xml b/integration-tests/bundles/service.listener.bundle/pom.xml index 1060ade62..e8916e9a3 100644 --- a/integration-tests/bundles/service.listener.bundle/pom.xml +++ b/integration-tests/bundles/service.listener.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/simple.service.2.bundle/pom.xml b/integration-tests/bundles/simple.service.2.bundle/pom.xml index 17b485de4..901d920d6 100644 --- a/integration-tests/bundles/simple.service.2.bundle/pom.xml +++ b/integration-tests/bundles/simple.service.2.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/simple.service.3.bundle/pom.xml b/integration-tests/bundles/simple.service.3.bundle/pom.xml index f63dd97d0..65788d54b 100644 --- a/integration-tests/bundles/simple.service.3.bundle/pom.xml +++ b/integration-tests/bundles/simple.service.3.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/simple.service.bundle.2.identical/pom.xml b/integration-tests/bundles/simple.service.bundle.2.identical/pom.xml index 41561fcf1..32bb5f5c7 100644 --- a/integration-tests/bundles/simple.service.bundle.2.identical/pom.xml +++ b/integration-tests/bundles/simple.service.bundle.2.identical/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/simple.service.bundle.identical/pom.xml b/integration-tests/bundles/simple.service.bundle.identical/pom.xml index 440c3e853..09caaed80 100644 --- a/integration-tests/bundles/simple.service.bundle.identical/pom.xml +++ b/integration-tests/bundles/simple.service.bundle.identical/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/simple.service.bundle/pom.xml b/integration-tests/bundles/simple.service.bundle/pom.xml index 45ded112e..6d10a7216 100644 --- a/integration-tests/bundles/simple.service.bundle/pom.xml +++ b/integration-tests/bundles/simple.service.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/tccl.bundle/pom.xml b/integration-tests/bundles/tccl.bundle/pom.xml index 19d71e20a..6c1723177 100644 --- a/integration-tests/bundles/tccl.bundle/pom.xml +++ b/integration-tests/bundles/tccl.bundle/pom.xml @@ -5,7 +5,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/trivial.bundle/pom.xml b/integration-tests/bundles/trivial.bundle/pom.xml index b01d0cc89..a6c37aa6b 100644 --- a/integration-tests/bundles/trivial.bundle/pom.xml +++ b/integration-tests/bundles/trivial.bundle/pom.xml @@ -2,7 +2,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/bundles/trivial.test.bundle/pom.xml b/integration-tests/bundles/trivial.test.bundle/pom.xml index 6bb37657a..28f867127 100644 --- a/integration-tests/bundles/trivial.test.bundle/pom.xml +++ b/integration-tests/bundles/trivial.test.bundle/pom.xml @@ -2,7 +2,7 @@ spring-osgi-integration-test-bundles org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index f048361b3..0ef0f4523 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -3,7 +3,7 @@ spring-osgi org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/tests-tiger/.classpath b/integration-tests/tests-tiger/.classpath index f18b2c7d6..bc93c21ae 100644 --- a/integration-tests/tests-tiger/.classpath +++ b/integration-tests/tests-tiger/.classpath @@ -5,7 +5,7 @@ - + @@ -17,9 +17,9 @@ - - - + + + diff --git a/integration-tests/tests-tiger/pom.xml b/integration-tests/tests-tiger/pom.xml index 268df093f..4e99075d6 100644 --- a/integration-tests/tests-tiger/pom.xml +++ b/integration-tests/tests-tiger/pom.xml @@ -3,7 +3,7 @@ spring-osgi org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/tests/.classpath b/integration-tests/tests/.classpath index 4539ecf30..8543d0cd6 100644 --- a/integration-tests/tests/.classpath +++ b/integration-tests/tests/.classpath @@ -11,16 +11,16 @@ - - - + + + - + - - + + @@ -31,8 +31,8 @@ - - - + + + diff --git a/integration-tests/tests/pom.xml b/integration-tests/tests/pom.xml index dfe3c240b..8cf513e6e 100644 --- a/integration-tests/tests/pom.xml +++ b/integration-tests/tests/pom.xml @@ -3,7 +3,7 @@ spring-osgi org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/integration-tests/tests/src/test/java/org/springframework/osgi/iandt/context/PublishedInterfacesTest.java b/integration-tests/tests/src/test/java/org/springframework/osgi/iandt/context/PublishedInterfacesTest.java new file mode 100644 index 000000000..a12f4b81f --- /dev/null +++ b/integration-tests/tests/src/test/java/org/springframework/osgi/iandt/context/PublishedInterfacesTest.java @@ -0,0 +1,58 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.osgi.iandt.context; + +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceReference; +import org.springframework.osgi.context.ConfigurableOsgiBundleApplicationContext; +import org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext; +import org.springframework.osgi.iandt.BaseIntegrationTest; + +/** + * Test checking the context published interfaces. + * + * @author Costin Leau + * + */ +public class PublishedInterfacesTest extends BaseIntegrationTest { + + public void testEmptyApplicationContext() throws Exception { + checkPublishedInterfaces(1); + } + + public void testXmlOsgiContext() throws Exception { + OsgiBundleXmlApplicationContext context = new OsgiBundleXmlApplicationContext( + new String[] { "/org/springframework/osgi/iandt/context/no-op-context.xml" }); + context.setBundleContext(bundleContext); + context.refresh(); + + checkPublishedInterfaces(2); + context.close(); + } + + private void checkPublishedInterfaces(int expectedContexts) throws Exception { + ServiceReference[] refs = bundleContext.getServiceReferences( + ConfigurableOsgiBundleApplicationContext.class.getName(), null); + assertEquals("different number of published contexts encountered", expectedContexts, refs.length); + + for (int i = 0; i < refs.length; i++) { + ServiceReference serviceReference = refs[i]; + String[] interfaces = (String[]) serviceReference.getProperty(Constants.OBJECTCLASS); + assertEquals("not enough interfaces published", 13, interfaces.length); + } + } +} diff --git a/integration-tests/tests/src/test/java/org/springframework/osgi/iandt/serviceProxyFactoryBean/ServiceRefAwareWithSingleServiceTest.java b/integration-tests/tests/src/test/java/org/springframework/osgi/iandt/serviceProxyFactoryBean/ServiceRefAwareWithSingleServiceTest.java index 64d312110..fb7d5740d 100644 --- a/integration-tests/tests/src/test/java/org/springframework/osgi/iandt/serviceProxyFactoryBean/ServiceRefAwareWithSingleServiceTest.java +++ b/integration-tests/tests/src/test/java/org/springframework/osgi/iandt/serviceProxyFactoryBean/ServiceRefAwareWithSingleServiceTest.java @@ -19,6 +19,7 @@ import java.util.Dictionary; import java.util.Enumeration; import java.util.Map; +import java.util.Properties; import org.osgi.framework.ServiceRegistration; import org.springframework.aop.SpringProxy; @@ -27,7 +28,6 @@ import org.springframework.osgi.service.importer.support.Cardinality; import org.springframework.osgi.service.importer.support.OsgiServiceProxyFactoryBean; import org.springframework.osgi.util.BundleDelegatingClassLoader; -import org.springframework.osgi.util.internal.MapBasedDictionary; /** * @author Costin Leau @@ -55,7 +55,7 @@ protected void onTearDown() throws Exception { public void tstProxyForUnaryCardinality() throws Exception { long time = 1234; Date date = new Date(time); - Dictionary dict = new MapBasedDictionary(); + Dictionary dict = new Properties(); ServiceRegistration reg = publishService(date); fb = new OsgiServiceProxyFactoryBean(); @@ -105,7 +105,7 @@ public void testServiceReferenceProperties() throws Exception { long time = 1234; Date date = new Date(time); - Dictionary dict = new MapBasedDictionary(); + Dictionary dict = new Properties(); dict.put("foo", "bar"); dict.put("george", "michael"); diff --git a/integration-tests/tests/src/test/resources/org/springframework/osgi/iandt/context/no-op-context.xml b/integration-tests/tests/src/test/resources/org/springframework/osgi/iandt/context/no-op-context.xml new file mode 100644 index 000000000..fa4b35535 --- /dev/null +++ b/integration-tests/tests/src/test/resources/org/springframework/osgi/iandt/context/no-op-context.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/io/.classpath b/io/.classpath index b331c360b..54a1b1074 100644 --- a/io/.classpath +++ b/io/.classpath @@ -5,7 +5,7 @@ - + diff --git a/io/pom.xml b/io/pom.xml index f73f34a83..668613bfd 100644 --- a/io/pom.xml +++ b/io/pom.xml @@ -3,7 +3,7 @@ spring-osgi org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/io/src/main/java/org/springframework/osgi/io/OsgiBundleResource.java b/io/src/main/java/org/springframework/osgi/io/OsgiBundleResource.java index 056b5293b..1bcfe67c4 100644 --- a/io/src/main/java/org/springframework/osgi/io/OsgiBundleResource.java +++ b/io/src/main/java/org/springframework/osgi/io/OsgiBundleResource.java @@ -408,17 +408,30 @@ URL[] getAllUrlsFromBundleSpace(String location) throws IOException { if (!StringUtils.hasText(location)) location = OsgiResourceUtils.FOLDER_DELIMITER; - // the root folder is requested (special case + // the root folder is requested (special case) if (OsgiResourceUtils.FOLDER_DELIMITER.equals(location)) { - Enumeration candidates = bundle.findEntries(location, null, false); + // there is no way to determine the URL to the root directly + // through findEntries so we'll have to use another way + // getEntry can't be used since it doesn't consider fragments + // so we have to rely on findEntries + + // we could ask for a known entry (such as META-INF) + // but not all jars have a dedicated entry for it + // so we'll just ask for whatever is present in the root + Enumeration candidates = bundle.findEntries("/", null, false); + + // since there can be multiple root paths (when fragments are present) + // iterate on all candidates while (candidates != null && candidates.hasMoreElements()) { - // extract the root URLs + URL url = (URL) candidates.nextElement(); - // if it's not a folder, consider it as a root - if (!url.getFile().endsWith(OsgiResourceUtils.FOLDER_DELIMITER)) - resources.add(new URL(url, ".")); + // determined the root path + // we'll have to parse the string since some implementations + // do not normalize the resulting URL resulting in mismatches + String rootPath = OsgiResourceUtils.findUpperFolder(url.toExternalForm()); + resources.add(new URL(rootPath)); } } else { diff --git a/io/src/main/java/org/springframework/osgi/io/OsgiBundleResourcePatternResolver.java b/io/src/main/java/org/springframework/osgi/io/OsgiBundleResourcePatternResolver.java index abb330ead..04b68e865 100644 --- a/io/src/main/java/org/springframework/osgi/io/OsgiBundleResourcePatternResolver.java +++ b/io/src/main/java/org/springframework/osgi/io/OsgiBundleResourcePatternResolver.java @@ -144,6 +144,26 @@ private Resource[] findPathMatchingResources(String locationPattern, int searchT return (Resource[]) result.toArray(new Resource[result.size()]); } + /** + * {@inheritDoc} + * + *

Overrides the default check up since computing the URL can be fairly + * expensive operation as there is no caching (due to the framework dynamic + * nature). + */ + protected boolean isJarResource(Resource resource) throws IOException { + if (resource instanceof OsgiBundleResource) { + // check the resource type + OsgiBundleResource bundleResource = (OsgiBundleResource) resource; + // if it's known, then it's not a jar + if (bundleResource.getSearchType() != OsgiResourceUtils.PREFIX_TYPE_UNKNOWN) { + return false; + } + // otherwise the normal parsing occur + } + return super.isJarResource(resource); + } + /** * Based on the search type, use the appropriate method * diff --git a/io/src/main/java/org/springframework/osgi/io/internal/OsgiResourceUtils.java b/io/src/main/java/org/springframework/osgi/io/internal/OsgiResourceUtils.java index ab35053c7..8ae7e6128 100644 --- a/io/src/main/java/org/springframework/osgi/io/internal/OsgiResourceUtils.java +++ b/io/src/main/java/org/springframework/osgi/io/internal/OsgiResourceUtils.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.osgi.io.internal; import java.net.URL; @@ -56,9 +57,10 @@ public abstract class OsgiResourceUtils { // classpath: public static final int PREFIX_TYPE_CLASS_SPACE = 2; - // osgibundlejar:: + // osgibundlejar: public static final int PREFIX_TYPE_BUNDLE_JAR = 4; + /** * Return the path prefix if there is any or {@link #EMPTY_PREFIX} * otherwise. @@ -111,6 +113,10 @@ public static String stripPrefix(String path) { } public static Resource[] convertURLArraytoResourceArray(URL[] urls) { + if (urls == null) { + return new Resource[0]; + } + // convert this into a resource array Resource[] res = new Resource[urls.length]; for (int i = 0; i < urls.length; i++) { @@ -120,10 +126,34 @@ public static Resource[] convertURLArraytoResourceArray(URL[] urls) { } public static Resource[] convertURLEnumerationToResourceArray(Enumeration enm) { - Set resources = new LinkedHashSet(5); + Set resources = new LinkedHashSet(4); while (enm != null && enm.hasMoreElements()) { - resources.add((URL) enm.nextElement()); + resources.add(new UrlResource((URL) enm.nextElement())); + } + return (Resource[]) resources.toArray(new Resource[resources.size()]); + } + + /** + * Similar to /path/path1/ -> /path/, /path/file -> /path/ + * + * @return + */ + public static String findUpperFolder(String path) { + if (path.length() < 2) + return path; + + String newPath = path; + // if it's a folder + if (path.endsWith(FOLDER_DELIMITER)) { + newPath = path.substring(0, path.length() - 1); } - return convertURLArraytoResourceArray((URL[]) resources.toArray(new URL[resources.size()])); + + int index = newPath.lastIndexOf(FOLDER_DELIMITER); + if (index > 0) + return newPath.substring(0, index + 1); + + else + // fallback to defaults + return path; } } diff --git a/mock/pom.xml b/mock/pom.xml index 9ca537663..361b54f94 100644 --- a/mock/pom.xml +++ b/mock/pom.xml @@ -3,7 +3,7 @@ spring-osgi org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi diff --git a/pom.xml b/pom.xml index 58008069c..ba9e2e813 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.springframework.osgi spring-osgi pom - 1.0-SNAPSHOT + 1.0.3 Spring Dynamic Modules http://www.springframework.org/osgi @@ -20,15 +20,18 @@ 2.5.1 + 2.5 - + + 1.0 + - 1.0.0 + 1.0.3 - 1.0.0 - 1.0.0 + 1.0.3 + 1.0.3 * @@ -277,6 +280,17 @@ limitations under the License. + + + + org.apache.maven.plugins + maven-plugin-plugin + 2.3 + + + + + diff --git a/readme.txt b/readme.txt index 2388af98d..81bc0a47b 100644 --- a/readme.txt +++ b/readme.txt @@ -66,31 +66,31 @@ The following distinct jar files are included in the distribution. This list spe third-party dependencies. Libraries in [brackets] are optional, i.e. just necessary for certain functionality. For an exact list of Spring-DM project dependencies see the respective Maven2 pom.xml files. -* spring-osgi-core-1.0.jar +* spring-osgi-core-${version}.jar - Contents: The Spring Dynamic Modules Core - Dependencies: slf4j, spring-aop, spring-beans, spring-core, spring-context, aop-alliance, spring-osgi-io [Log4J] -* spring-osgi-extender-1.0.jar +* spring-osgi-extender-${version}.jar - Contents: The Spring Dynamic Modules Extender - Dependencies: sl4fj, spring-osgi-core [Log4J] -* spring-osgi-io-1.0.jar +* spring-osgi-io-${version}.jar - Contents: The Spring Dynamic Modules IO library - Dependencies: sl4fj, spring-core [Log4J] -* spring-osgi-mock-1.0.jar +* spring-osgi-mock-${version}.jar - Contents: The Spring Dynamic Modules Mock library - Dependencies: OSGi API -* spring-osgi-test-1.0.jar +* spring-osgi-test-${version}.jar - Contents: The Spring Dynamic Modules Integration Testing framework - Dependencies: asm, junit, slf4j, spring-osgi-core, spring-osgi-extender [Equinox, Felix, Knopflerfish, Log4J] -* extensions / spring-osgi-annotation-1.0.jar +* extensions / spring-osgi-annotation-${version}.jar - Contents: The Spring Dynamic Modules Annotation Extension - Dependencies: slf4j, spring-beans, spring-core, spring-osgi-core [Log4J] diff --git a/release/pom.xml b/release/pom.xml index 8837399ad..1f728217e 100644 --- a/release/pom.xml +++ b/release/pom.xml @@ -1,21 +1,22 @@ 4.0.0 - org.springframework.osgi - spring-osgi-dist - Spring OSGi distribution + org.springframework.osgi.release + spring-osgi + Spring OSGi Distribution pom + org.springframework.osgi spring-osgi - 1.0-SNAPSHOT + 1.0.3 .. - + ../mock ../io @@ -23,36 +24,41 @@ ../extender ../test-support ../annotation - ../samples - + maven-assembly-plugin + 2.2-beta-2 + false + - zip-files + wo-dependencies package single - false - src/assembly/no-dependencies.xml + + + + + + + with-dependencies + package + + single + + + true + src/assembly/with-dependencies.xml diff --git a/release/src/assembly/dependencies.xml b/release/src/assembly/dependencies.xml new file mode 100644 index 000000000..b6d25beea --- /dev/null +++ b/release/src/assembly/dependencies.xml @@ -0,0 +1,71 @@ + + + + + + + + + ../target/libs + lib + + spring-osgi* + + + + + + + ../ + + + readme-building.txt + + dos + + + ../lib + lib + + readme.txt + + dos + + + + + diff --git a/release/src/assembly/docs.xml b/release/src/assembly/docs.xml index 3873e0a0b..2a61aa9a5 100644 --- a/release/src/assembly/docs.xml +++ b/release/src/assembly/docs.xml @@ -1,16 +1,28 @@ + + ../ + - ../changelog.txt - ../license.txt - ../notice.txt - ../notice.txt - ../readme.txt - ../readme-building.txt + changelog.txt + license.txt + notice.txt + readme.txt + dos + true - dos + + + + ../target/site/apidocs + docs/api + + + ../docs/target/site/reference + docs/reference + \ No newline at end of file diff --git a/release/src/assembly/no-dependencies.xml b/release/src/assembly/no-dependencies.xml index 2cb5a186f..67d5f450d 100644 --- a/release/src/assembly/no-dependencies.xml +++ b/release/src/assembly/no-dependencies.xml @@ -8,29 +8,18 @@ true + - - org.springframework.osgi:spring-osgi-mock - - - org.springframework.osgi:spring-osgi-io - - - org.springframework.osgi:spring-osgi-core - - - org.springframework.osgi:spring-osgi-extender - - - org.springframework.osgi:spring-osgi-test - - - org.springframework.osgi:spring-osgi-annotation - + org.springframework.osgi:spring-osgi-io + org.springframework.osgi:spring-osgi-mock + org.springframework.osgi:spring-osgi-core + org.springframework.osgi:spring-osgi-extender + org.springframework.osgi:spring-osgi-test + - false + false target @@ -42,29 +31,21 @@ + - - org.springframework.osgi.samples:samples - + org.springframework.osgi:spring-osgi-annotation + - - samples/${artifactId} - + false - - - pom.xml + + target + dist/extensions + + *.jar - - bin/** - target/** - **/clover.license - - diff --git a/release/src/assembly/parent.xml b/release/src/assembly/parent.xml deleted file mode 100644 index 7804588d4..000000000 --- a/release/src/assembly/parent.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - target - samples - - pom.xml - - - unix - - \ No newline at end of file diff --git a/release/src/assembly/with-dependencies.xml b/release/src/assembly/with-dependencies.xml index b3cbf42a4..62bad508e 100644 --- a/release/src/assembly/with-dependencies.xml +++ b/release/src/assembly/with-dependencies.xml @@ -1,3 +1,4 @@ + with-dependencies @@ -5,10 +6,11 @@ src/assembly/docs.xml - src/assembly/parent.xml + src/assembly/dependencies.xml true + org.springframework.osgi:spring-osgi-io @@ -16,96 +18,36 @@ org.springframework.osgi:spring-osgi-core org.springframework.osgi:spring-osgi-extender org.springframework.osgi:spring-osgi-test - org.springframework.osgi:spring-osgi-annotation - - false - dist/${artifactId}-${version}.${extension} - false - - - - - org.springframework.osgi:spring-osgi-io - org.springframework.osgi:spring-osgi-mock - org.springframework.osgi:spring-osgi-core - org.springframework.osgi:spring-osgi-extender - org.springframework.osgi:spring-osgi-test - org.springframework.osgi:spring-osgi-annotation - - - false - dist/${artifactId}-${version}-sources.${extension} - false - - target/${artifactId}-${version}-sources.${extension} - - - - sources/lib/${groupId}/${artifactId}-${version}.${extension} - runtime - - org.springframework.osgi:spring-osgi-io - org.springframework.osgi:spring-osgi-mock - org.springframework.osgi:spring-osgi-core - org.springframework.osgi:spring-osgi-extender - org.springframework.osgi:spring-osgi-test - org.springframework.osgi:spring-osgi-annotation - - - - sources/lib/${groupId}/${artifactId}-${version}.${extension} - provided - - - + - sources/${artifactId} + false - - bin/** - target/** - + target + dist + + *.jar + + - org.springframework.osgi.samples:samples + org.springframework.osgi:spring-osgi-annotation - - false - samples/${artifactId}-${version}.${extension} - false - - - samples/lib/${groupId}/${artifactId}-${version}.${extension} - runtime - - org.springframework.osgi:spring-osgi-io - org.springframework.osgi:spring-osgi-mock - org.springframework.osgi:spring-osgi-core - org.springframework.osgi:spring-osgi-extender - org.springframework.osgi:spring-osgi-test - org.springframework.osgi:spring-osgi-annotation - - - - sources/lib/${groupId}/${artifactId}-${version}.${extension} - provided - - - + - samples/${artifactId} + false - - bin/** - target/** - + target + dist/extensions + + *.jar + diff --git a/samples/petclinic/petclinic.sample.contract/pom.xml b/samples/petclinic/petclinic.sample.contract/pom.xml index 06a588e9d..c114bab7b 100644 --- a/samples/petclinic/petclinic.sample.contract/pom.xml +++ b/samples/petclinic/petclinic.sample.contract/pom.xml @@ -3,7 +3,7 @@ petclinic org.springframework.osgi.samples - 1.0-SNAPSHOT + 1.0.3 4.0.0 @@ -11,7 +11,7 @@ contract jar Spring OSGi Samples: Simple Petclinic Contract - 1.0-SNAPSHOT + 1.0.3 http://www.springframework.org/osgi diff --git a/samples/petclinic/petclinic.sample.hsqldb/pom.xml b/samples/petclinic/petclinic.sample.hsqldb/pom.xml index 7e623ff63..9e9f8dbad 100644 --- a/samples/petclinic/petclinic.sample.hsqldb/pom.xml +++ b/samples/petclinic/petclinic.sample.hsqldb/pom.xml @@ -3,7 +3,7 @@ petclinic org.springframework.osgi.samples - 1.0-SNAPSHOT + 1.0.3 4.0.0 @@ -11,7 +11,7 @@ hsqldb-impl jar Spring OSGi Samples: Simple Petclinic HSQLDB - 1.0-SNAPSHOT + 1.0.3 http://www.springframework.org/osgi diff --git a/samples/petclinic/petclinic.sample.jdbc/pom.xml b/samples/petclinic/petclinic.sample.jdbc/pom.xml index eca09ab24..5f6c4deb2 100644 --- a/samples/petclinic/petclinic.sample.jdbc/pom.xml +++ b/samples/petclinic/petclinic.sample.jdbc/pom.xml @@ -3,7 +3,7 @@ petclinic org.springframework.osgi.samples - 1.0-SNAPSHOT + 1.0.3 4.0.0 @@ -11,7 +11,7 @@ jdbc jar Spring OSGi Samples: Simple Petclinic JDBC - 1.0-SNAPSHOT + 1.0.3 http://www.springframework.org/osgi diff --git a/samples/petclinic/petclinic.sample.mysql/pom.xml b/samples/petclinic/petclinic.sample.mysql/pom.xml index a1cd3c899..ca36004ec 100644 --- a/samples/petclinic/petclinic.sample.mysql/pom.xml +++ b/samples/petclinic/petclinic.sample.mysql/pom.xml @@ -3,7 +3,7 @@ petclinic org.springframework.osgi.samples - 1.0-SNAPSHOT + 1.0.3 4.0.0 @@ -11,7 +11,7 @@ mysql-impl jar Spring OSGi Samples: Simple Petclinic MySQL - 1.0-SNAPSHOT + 1.0.3 http://www.springframework.org/osgi diff --git a/samples/petclinic/pom.xml b/samples/petclinic/pom.xml index 3e16be6a4..38350f9b3 100644 --- a/samples/petclinic/pom.xml +++ b/samples/petclinic/pom.xml @@ -3,7 +3,7 @@ spring-osgi org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi.samples diff --git a/samples/pom.xml b/samples/pom.xml index b290a6c6b..a46e765c3 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -3,7 +3,7 @@ spring-osgi org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi.samples diff --git a/samples/simple-service/pom.xml b/samples/simple-service/pom.xml index bc8cb91ec..14923b21c 100644 --- a/samples/simple-service/pom.xml +++ b/samples/simple-service/pom.xml @@ -3,7 +3,7 @@ samples org.springframework.osgi.samples - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi.samples diff --git a/samples/simple-service/simple-service-bundle/pom.xml b/samples/simple-service/simple-service-bundle/pom.xml index cfa2d952c..b95a0830f 100644 --- a/samples/simple-service/simple-service-bundle/pom.xml +++ b/samples/simple-service/simple-service-bundle/pom.xml @@ -2,7 +2,7 @@ simple-service org.springframework.osgi.samples - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi.samples diff --git a/samples/simple-service/simple-service-integration-test/pom.xml b/samples/simple-service/simple-service-integration-test/pom.xml index e6185ccc7..0176e51b4 100644 --- a/samples/simple-service/simple-service-integration-test/pom.xml +++ b/samples/simple-service/simple-service-integration-test/pom.xml @@ -3,7 +3,7 @@ simple-service org.springframework.osgi.samples - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi.samples diff --git a/samples/weather/pom.xml b/samples/weather/pom.xml index 1a43213d1..5b7d1401a 100644 --- a/samples/weather/pom.xml +++ b/samples/weather/pom.xml @@ -3,7 +3,7 @@ spring-osgi org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi.samples diff --git a/samples/weather/weather-dao/pom.xml b/samples/weather/weather-dao/pom.xml index 87cf66716..71e18ebb3 100755 --- a/samples/weather/weather-dao/pom.xml +++ b/samples/weather/weather-dao/pom.xml @@ -4,7 +4,7 @@ weather org.springframework.osgi.samples - 1.0-SNAPSHOT + 1.0.3 org.springframework.osgi.samples weather-dao diff --git a/samples/weather/weather-extension/pom.xml b/samples/weather/weather-extension/pom.xml index 6dc9258de..33d1a86e3 100644 --- a/samples/weather/weather-extension/pom.xml +++ b/samples/weather/weather-extension/pom.xml @@ -4,7 +4,7 @@ weather org.springframework.osgi.samples - 1.0-SNAPSHOT + 1.0.3 org.springframework.osgi.samples weather-extension diff --git a/samples/weather/weather-service-integration-test/.classpath b/samples/weather/weather-service-integration-test/.classpath index 2ceb13013..18af7169b 100644 --- a/samples/weather/weather-service-integration-test/.classpath +++ b/samples/weather/weather-service-integration-test/.classpath @@ -11,6 +11,6 @@ - + diff --git a/samples/weather/weather-service-integration-test/pom.xml b/samples/weather/weather-service-integration-test/pom.xml index da270688e..26610c106 100644 --- a/samples/weather/weather-service-integration-test/pom.xml +++ b/samples/weather/weather-service-integration-test/pom.xml @@ -3,7 +3,7 @@ weather org.springframework.osgi.samples - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi.samples diff --git a/samples/weather/weather-service-test/pom.xml b/samples/weather/weather-service-test/pom.xml index 9b01c9066..707e043ce 100755 --- a/samples/weather/weather-service-test/pom.xml +++ b/samples/weather/weather-service-test/pom.xml @@ -4,7 +4,7 @@ weather org.springframework.osgi.samples - 1.0-SNAPSHOT + 1.0.3 org.springframework.osgi.samples weather-service-test diff --git a/samples/weather/weather-service/pom.xml b/samples/weather/weather-service/pom.xml index 9ca708c46..6619ad7fe 100755 --- a/samples/weather/weather-service/pom.xml +++ b/samples/weather/weather-service/pom.xml @@ -4,7 +4,7 @@ weather org.springframework.osgi.samples - 1.0-SNAPSHOT + 1.0.3 org.springframework.osgi.samples weather-service diff --git a/samples/weather/wiring-bundle/.classpath b/samples/weather/wiring-bundle/.classpath index 15f9ff0cf..e01b88020 100644 --- a/samples/weather/wiring-bundle/.classpath +++ b/samples/weather/wiring-bundle/.classpath @@ -13,7 +13,7 @@ - - + + diff --git a/samples/weather/wiring-bundle/pom.xml b/samples/weather/wiring-bundle/pom.xml index 4534f3355..af59069e8 100755 --- a/samples/weather/wiring-bundle/pom.xml +++ b/samples/weather/wiring-bundle/pom.xml @@ -4,7 +4,7 @@ weather org.springframework.osgi.samples - 1.0-SNAPSHOT + 1.0.3 org.springframework.osgi.samples wiring-bundle diff --git a/test-support/.classpath b/test-support/.classpath index e12381f12..a0889533a 100644 --- a/test-support/.classpath +++ b/test-support/.classpath @@ -15,8 +15,8 @@ - - + + diff --git a/test-support/pom.xml b/test-support/pom.xml index b0a51a8b0..f90395878 100644 --- a/test-support/pom.xml +++ b/test-support/pom.xml @@ -2,7 +2,7 @@ spring-osgi org.springframework.osgi - 1.0-SNAPSHOT + 1.0.3 4.0.0 org.springframework.osgi @@ -30,6 +30,8 @@ org.eclipse.*;resolution:=optional, org.knopflerfish*;resolution:=optional, org.apache.felix.*;resolution:=optional, + org.springframework.osgi*;version=${spring.osgi.import.version}, + org.springframework*;version=${spring.base.version}, * ${symName.prefix}.test diff --git a/test-support/src/main/java/org/springframework/osgi/test/AbstractConfigurableOsgiTests.java b/test-support/src/main/java/org/springframework/osgi/test/AbstractConfigurableOsgiTests.java index d00012d55..75ead7efb 100644 --- a/test-support/src/main/java/org/springframework/osgi/test/AbstractConfigurableOsgiTests.java +++ b/test-support/src/main/java/org/springframework/osgi/test/AbstractConfigurableOsgiTests.java @@ -19,7 +19,9 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Properties; +import org.osgi.framework.Constants; import org.springframework.osgi.test.platform.EquinoxPlatform; import org.springframework.osgi.test.platform.OsgiPlatform; import org.springframework.osgi.test.platform.Platforms; @@ -60,12 +62,6 @@ public AbstractConfigurableOsgiTests(String name) { */ public static final String OSGI_FRAMEWORK_SELECTOR = "org.springframework.osgi.test.framework"; - /** - * - */ - private static final String ORG_OSGI_FRAMEWORK_BOOTDELEGATION = "org.osgi.framework.bootdelegation"; - - /** * {@inheritDoc} * @@ -88,7 +84,7 @@ protected OsgiPlatform createPlatform() { Class platformClass = ClassUtils.resolveClassName(platformClassName, currentCL); if (OsgiPlatform.class.isAssignableFrom(platformClass)) { if (trace) - logger.trace("instantiating platform wrapper..."); + logger.trace("Instantiating platform wrapper..."); try { platform = (OsgiPlatform) platformClass.newInstance(); } @@ -97,7 +93,7 @@ protected OsgiPlatform createPlatform() { } } else - logger.warn("class [" + platformClass + "] does not implement " + OsgiPlatform.class.getName() + logger.warn("Class [" + platformClass + "] does not implement " + OsgiPlatform.class.getName() + " interface; falling back to defaults"); } else { @@ -106,14 +102,15 @@ protected OsgiPlatform createPlatform() { } else - logger.trace("no platform specified; using default"); + logger.trace("No platform specified; using default"); // fall back if (platform == null) platform = new EquinoxPlatform(); + Properties config = platform.getConfigurationProperties(); // add boot delegation - platform.getConfigurationProperties().setProperty(ORG_OSGI_FRAMEWORK_BOOTDELEGATION, + config.setProperty(Constants.FRAMEWORK_BOOTDELEGATION, getBootDelegationPackageString()); return platform; @@ -132,7 +129,7 @@ protected OsgiPlatform createPlatform() { protected String getPlatformName() { String systemProperty = System.getProperty(OSGI_FRAMEWORK_SELECTOR); if (logger.isTraceEnabled()) - logger.trace("system property [" + OSGI_FRAMEWORK_SELECTOR + "] has value=" + systemProperty); + logger.trace("System property [" + OSGI_FRAMEWORK_SELECTOR + "] has value=" + systemProperty); return (systemProperty == null ? Platforms.EQUINOX : systemProperty); } @@ -165,11 +162,16 @@ private String getBootDelegationPackageString() { */ protected List getBootDelegationPackages() { List defaults = new ArrayList(); + // javax packages defaults.add("javax.*"); + // XML API available in JDK 1.4 defaults.add("org.w3c.*"); - defaults.add("sun.*"); defaults.add("org.xml.*"); + + // sun packages + defaults.add("sun.*"); defaults.add("com.sun.*"); + // FIXME: the JAXP package (for 1.4 VMs) should be discovered in an OSGi // manner defaults.add("org.apache.xerces.jaxp.*"); diff --git a/test-support/src/main/java/org/springframework/osgi/test/AbstractDependencyManagerTests.java b/test-support/src/main/java/org/springframework/osgi/test/AbstractDependencyManagerTests.java index a2f68624e..e926c18b2 100644 --- a/test-support/src/main/java/org/springframework/osgi/test/AbstractDependencyManagerTests.java +++ b/test-support/src/main/java/org/springframework/osgi/test/AbstractDependencyManagerTests.java @@ -164,7 +164,7 @@ protected String[] getTestFrameworkBundlesNames() { boolean trace = logger.isTraceEnabled(); if (trace) - logger.trace("loaded properties " + props); + logger.trace("Loaded properties " + props); // pass properties to test instance running inside OSGi space System.getProperties().put(SPRING_OSGI_VERSION_PROP_KEY, props.get(SPRING_OSGI_VERSION_PROP_KEY)); @@ -173,7 +173,7 @@ protected String[] getTestFrameworkBundlesNames() { Properties excluded = PropertiesUtil.filterKeysStartingWith(props, IGNORE); if (trace) { - logger.trace("excluded ignored properties " + excluded); + logger.trace("Excluded ignored properties " + excluded); } // filter bundles which are Tiger/JDK 1.5 specific @@ -191,7 +191,7 @@ protected String[] getTestFrameworkBundlesNames() { String[] bundles = (String[]) props.keySet().toArray(new String[props.size()]); if (logger.isDebugEnabled()) - logger.debug("default framework bundles :" + ObjectUtils.nullSafeToString(bundles)); + logger.debug("Default framework bundles :" + ObjectUtils.nullSafeToString(bundles)); return bundles; } @@ -287,8 +287,10 @@ protected Resource locateBundle(String bundleId) { artifactId[i] = StringUtils.trimWhitespace(artifactId[i]); } - return (artifactId.length == 3 ? locator.locateArtifact(artifactId[0], artifactId[1], artifactId[2]) - : locator.locateArtifact(artifactId[0], artifactId[1], artifactId[2], artifactId[3])); + ArtifactLocator aLocator = getLocator(); + + return (artifactId.length == 3 ? aLocator.locateArtifact(artifactId[0], artifactId[1], artifactId[2]) + : aLocator.locateArtifact(artifactId[0], artifactId[1], artifactId[2], artifactId[3])); } /** @@ -296,6 +298,11 @@ protected Resource locateBundle(String bundleId) { * override this method if the default locator (searching the local Maven2 * repository) is not enough. * + *

+ * Note: This method will be used each time a bundle has to be + * retrieved; it is highly recommended to return a cached instance instead + * of a new one each time. + * * @return artifact locator used by this test. */ protected ArtifactLocator getLocator() { diff --git a/test-support/src/main/java/org/springframework/osgi/test/AbstractOnTheFlyBundleCreatorTests.java b/test-support/src/main/java/org/springframework/osgi/test/AbstractOnTheFlyBundleCreatorTests.java index a36783981..bd58ec329 100644 --- a/test-support/src/main/java/org/springframework/osgi/test/AbstractOnTheFlyBundleCreatorTests.java +++ b/test-support/src/main/java/org/springframework/osgi/test/AbstractOnTheFlyBundleCreatorTests.java @@ -149,7 +149,7 @@ protected Manifest getManifest() { String manifestLocation = getManifestLocation(); if (StringUtils.hasText(manifestLocation)) { - logger.info("using Manifest from specified location=[" + getManifestLocation() + "]"); + logger.info("Using Manifest from specified location=[" + getManifestLocation() + "]"); DefaultResourceLoader loader = new DefaultResourceLoader(); manifest = createManifestFrom(loader.getResource(manifestLocation)); } @@ -167,14 +167,14 @@ protected Manifest getManifest() { for (Iterator iterator = jarEntries.entrySet().iterator(); iterator.hasNext();) { Map.Entry entry = (Map.Entry) iterator.next(); if (META_INF_JAR_LOCATION.equals(entry.getKey())) { - logger.info("using Manifest from the test bundle content=[/META-INF/MANIFEST.MF]"); + logger.info("Using Manifest from the test bundle content=[/META-INF/MANIFEST.MF]"); manifest = createManifestFrom((Resource) entry.getValue()); } } // fallback to default manifest creation if (manifest == null) { - logger.info("automatically creating Manifest for the test bundle"); + logger.info("Automatically creating Manifest for the test bundle"); manifest = createDefaultManifest(); } } @@ -219,7 +219,7 @@ protected Manifest createDefaultManifest() { addImportPackage(manifest); if (logger.isDebugEnabled()) - logger.debug("created manifest:" + manifest.getMainAttributes().entrySet()); + logger.debug("Created manifest:" + manifest.getMainAttributes().entrySet()); return manifest; } @@ -264,7 +264,7 @@ private Collection eliminateSpecialPackages(String[] rawImports) { } if (!eliminatedImports.isEmpty() && logger.isTraceEnabled()) - logger.trace("eliminated special packages " + eliminatedImports); + logger.trace("Eliminated special packages " + eliminatedImports); return filteredImports; } @@ -293,30 +293,58 @@ private Collection eliminatePackagesAvailableInTheJar(Collection imports) { filteredImports.add(pckg); } if (!eliminatedImports.isEmpty() && logger.isTraceEnabled()) - logger.trace("eliminated packages already present in the bundle " + eliminatedImports); + logger.trace("Eliminated packages already present in the bundle " + eliminatedImports); return filteredImports; } /** * Determine imports by walking a class hierarchy until the current package - * is found. + * is found. Currently, the parsers checks only the test class hierarchy + * available in the bundle. note that split packages are not supported. + * * * @return */ private String[] determineImports(Class clazz) { Assert.notNull(clazz, "a not-null class is required"); + + boolean trace = logger.isTraceEnabled(); + String endPackage = ClassUtils.classPackageAsResourcePath(AbstractOnTheFlyBundleCreatorTests.class).replace( '/', '.'); Set cumulatedPackages = new LinkedHashSet(); + // get contained packages to do matching on the test hierarchy + Collection containedPackages = jarCreator.getContainedPackages(); + + // make sure the collection package is valid + boolean validPackageCollection = !containedPackages.isEmpty(); + String clazzPackage; + // start parsing the test class hierarchy do { - cumulatedPackages.addAll(determineImportsForClass(clazz)); clazzPackage = ClassUtils.classPackageAsResourcePath(clazz).replace('/', '.'); + // check if the class package is inside this jar or not + // parse the class only for available packages otherwise + + // if we don't have the package, add it + if (validPackageCollection && !containedPackages.contains(clazzPackage)) { + logger.trace("Package [" + clazzPackage + "] is NOT part of the test archive; adding an import for it"); + cumulatedPackages.add(clazzPackage); + } + + // otherwise parse the class byte-code + else { + if (trace) + logger.trace("Package [" + clazzPackage + "] is part of the test archive; parsing " + clazz + + " bytecode to determine imports..."); + cumulatedPackages.addAll(determineImportsForClass(clazz)); + } clazz = clazz.getSuperclass(); + // work until the testing framework packages are reached } while (!endPackage.equals(clazzPackage)); String[] packages = (String[]) cumulatedPackages.toArray(new String[cumulatedPackages.size()]); @@ -348,7 +376,7 @@ private Set determineImportsForClass(Class clazz) { boolean trace = logger.isTraceEnabled(); if (trace) - logger.trace("discovered classes to analyze " + allClasses); + logger.trace("Discovered classes to analyze " + allClasses); ClassReader reader; @@ -356,7 +384,7 @@ private Set determineImportsForClass(Class clazz) { Class classToVisit = (Class) iterator.next(); try { if (trace) - logger.trace("visiting class " + classToVisit); + logger.trace("Visiting class " + classToVisit); reader = new ClassReader(clazz.getResourceAsStream(ClassUtils.getClassFileName(classToVisit))); } catch (Exception ex) { @@ -369,7 +397,7 @@ private Set determineImportsForClass(Class clazz) { } protected void postProcessBundleContext(BundleContext context) throws Exception { - logger.debug("post processing: creating test bundle"); + logger.debug("Post processing: creating test bundle"); Resource jar; @@ -415,10 +443,10 @@ private void installAndStartBundle(BundleContext context, Resource resource) thr boolean debug = logger.isDebugEnabled(); if (debug) - logger.debug("test bundle [" + bundleString + "] succesfully installed"); + logger.debug("Test bundle [" + bundleString + "] succesfully installed"); bundle.start(); if (debug) - logger.debug("test bundle [" + bundleString + "] succesfully started"); + logger.debug("Test bundle [" + bundleString + "] succesfully started"); } } diff --git a/test-support/src/main/java/org/springframework/osgi/test/AbstractOptionalDependencyInjectionTests.java b/test-support/src/main/java/org/springframework/osgi/test/AbstractOptionalDependencyInjectionTests.java index c7faaf1fc..8b364da27 100644 --- a/test-support/src/main/java/org/springframework/osgi/test/AbstractOptionalDependencyInjectionTests.java +++ b/test-support/src/main/java/org/springframework/osgi/test/AbstractOptionalDependencyInjectionTests.java @@ -20,6 +20,7 @@ import org.osgi.framework.BundleContext; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.osgi.context.ConfigurableOsgiBundleApplicationContext; @@ -52,7 +53,9 @@ public abstract class AbstractOptionalDependencyInjectionTests extends AbstractD * @author Costin Leau * */ - private static class EmptyOsgiApplicationContext extends AbstractDelegatedExecutionApplicationContext { + // the disposable interface is added just so that byte code detect the org.springframework.beans.factory package + private static class EmptyOsgiApplicationContext extends AbstractDelegatedExecutionApplicationContext implements + DisposableBean { protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException, BeansException { } diff --git a/test-support/src/main/java/org/springframework/osgi/test/AbstractOsgiTests.java b/test-support/src/main/java/org/springframework/osgi/test/AbstractOsgiTests.java index 66bfc0bce..a26c09c23 100644 --- a/test-support/src/main/java/org/springframework/osgi/test/AbstractOsgiTests.java +++ b/test-support/src/main/java/org/springframework/osgi/test/AbstractOsgiTests.java @@ -16,13 +16,9 @@ package org.springframework.osgi.test; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.Properties; +import java.net.URLDecoder; import junit.framework.Protectable; import junit.framework.TestCase; @@ -30,17 +26,17 @@ import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; import org.osgi.framework.ServiceReference; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.osgi.io.OsgiBundleResourceLoader; -import org.springframework.osgi.test.internal.OsgiJUnitTest; -import org.springframework.osgi.test.internal.util.ConfigurableByteArrayOutputStream; -import org.springframework.osgi.test.internal.util.IOUtils; +import org.springframework.osgi.test.internal.holder.OsgiTestInfoHolder; import org.springframework.osgi.test.internal.util.TestUtils; import org.springframework.osgi.test.platform.OsgiPlatform; import org.springframework.osgi.util.OsgiBundleUtils; import org.springframework.osgi.util.OsgiPlatformDetector; +import org.springframework.osgi.util.OsgiStringUtils; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; @@ -53,6 +49,8 @@ */ public abstract class AbstractOsgiTests extends AbstractOptionalDependencyInjectionTests { + private static final String UTF_8_CHARSET = "UTF-8"; + // JVM shutdown hook private static Thread shutdownHook; @@ -68,11 +66,6 @@ public abstract class AbstractOsgiTests extends AbstractOptionalDependencyInject // JUnitService trigger private static Method serviceTrigger; - // streams for communicating with the OSGi realm. - private ObjectInputStream inputStream; - - private ObjectOutputStream outputStream; - // the test results used by the triggering test runner private TestResult originalResult; @@ -169,7 +162,7 @@ protected void postProcessBundleContext(BundleContext platformBundleContext) thr } // - // JUnit overriden methods. + // JUnit overridden methods. // /** @@ -214,7 +207,7 @@ public void runBare() throws Throwable { readTestResult(); } finally { - completeTestExecution(); + // nothing to clean up } } } @@ -240,7 +233,7 @@ private void startup() throws Exception { osgiPlatform = createPlatform(); // start platform if (debug) - logger.debug("about to start " + osgiPlatform); + logger.debug("About to start " + osgiPlatform); osgiPlatform.start(); // platform context platformContext = osgiPlatform.getBundleContext(); @@ -262,18 +255,7 @@ private void startup() throws Exception { // start bundles for (int i = 0; i < bundles.length; i++) { - String info = null; - if (debug) - info = "bundle " + bundles[i].getBundleId() + "[" + bundles[i].getSymbolicName() + "]"; - - if (!OsgiBundleUtils.isFragment(bundles[i])) { - if (debug) - logger.debug("starting " + info); - bundles[i].start(); - } - - else if (debug) - logger.debug(info + " is a fragment; start not invoked"); + startBundle(bundles[i]); } // hook after the OSGi platform has been setup @@ -326,30 +308,66 @@ private Bundle installBundle(Resource location) throws Exception { Assert.notNull(platformContext); Assert.notNull(location); if (logger.isDebugEnabled()) - logger.debug("installing bundle from location " + location.getDescription()); - return platformContext.installBundle(location.getDescription(), location.getInputStream()); + logger.debug("Installing bundle from location " + location.getDescription()); + + String bundleLocation; + + try { + bundleLocation = URLDecoder.decode(location.getURL().toExternalForm(), UTF_8_CHARSET); + } + catch (Exception ex) { + // the URL cannot be created, fall back to the description + bundleLocation = location.getDescription(); + } + + return platformContext.installBundle(bundleLocation, location.getInputStream()); + } + + /** + * Starts a bundle and prints a nice logging message in case of failure. + * + * @param bundle + * @return + * @throws BundleException + */ + private void startBundle(Bundle bundle) throws BundleException { + boolean debug = logger.isDebugEnabled(); + String info = "[" + OsgiStringUtils.nullSafeNameAndSymName(bundle) + "|" + bundle.getLocation() + "]"; + + if (!OsgiBundleUtils.isFragment(bundle)) { + if (debug) + logger.debug("Starting " + info); + try { + bundle.start(); + } + catch (BundleException ex) { + logger.error("cannot start bundle " + info, ex); + throw ex; + } + } + + else if (debug) + logger.debug(info + " is a fragment; start not invoked"); } // // Delegation methods for OSGi execution and initialization // + // runs outside OSGi /** * Prepares test execution - the OSGi platform will be started (if needed) * and cached for the test suite execution. - * */ private void prepareTestExecution() throws Exception { if (getName() == null) throw new IllegalArgumentException("no test specified"); - // write the test name into the System properties - System.getProperties().put(OsgiJUnitTest.OSGI_TEST, osgiJUnitTest.getClass().getName()); - - // create streams first to avoid deadlocks in setting up the stream on - // the OSGi side - setupStreams(); + // clear test results + OsgiTestInfoHolder.INSTANCE.clearResults(); + // set test class + OsgiTestInfoHolder.INSTANCE.setTestClassName(osgiJUnitTest.getClass().getName()); // start OSGi platform (the caching is done inside the method). try { @@ -360,11 +378,12 @@ private void prepareTestExecution() throws Exception { throw e; } - logger.debug("writing test name [" + getName() + "] to OSGi"); - // write test name to OSGi - outputStream.writeUTF(getName()); - outputStream.flush(); + if (logger.isTraceEnabled()) + logger.trace("Writing test name [" + getName() + "] to OSGi"); + // write test name to OSGi + // set test method name + OsgiTestInfoHolder.INSTANCE.setTestMethodName(getName()); } /** @@ -386,17 +405,6 @@ private void invokeOSGiTestExecution() throws Exception { } } - /** - * Finishes the test execution - read back the result from the OSGi copy and - * closes up the streams. - * - * @throws Exception - */ - private void completeTestExecution() { - IOUtils.closeStream(inputStream); - IOUtils.closeStream(outputStream); - } - /** * Determines through reflection the methods used for invoking the * TestRunnerService. @@ -433,8 +441,8 @@ private void initializeServiceRunnerInvocationMethods() throws Exception { */ private BundleContext getRuntimeBundleContext() { - // read the system property - Long id = (Long) System.getProperties().get(OsgiJUnitTest.OSGI_TEST_BUNDLE_ID); + // read test bundle id property + Long id = OsgiTestInfoHolder.INSTANCE.getTestBundleId(); BundleContext ctx = null; if (id != null) @@ -448,15 +456,16 @@ private BundleContext getRuntimeBundleContext() { return (ctx == null ? platformContext : ctx); } + // runs outside OSGi private void readTestResult() { - // finish stream creation (to avoid circular dependencies) - createInStream(); - - logger.debug("reading OSGi results for test [" + getName() + "]"); + if (logger.isTraceEnabled()) + logger.trace("Reading OSGi results for test [" + getName() + "]"); - TestUtils.receiveTestResult(this.originalResult, osgiJUnitTest, inputStream); + // copy results from OSGi into existing test result + TestUtils.cloneTestResults(OsgiTestInfoHolder.INSTANCE, originalResult, osgiJUnitTest); - logger.debug("test[" + getName() + "]'s result read"); + if (logger.isTraceEnabled()) + logger.debug("Test[" + getName() + "]'s result read"); } /** @@ -479,64 +488,19 @@ public void run() { * Cleanup for the test suite. */ private void shutdownTest() { - IOUtils.closeStream(inputStream); - IOUtils.closeStream(outputStream); - - logger.info("shutting down OSGi platform"); + logger.info("Shutting down OSGi platform"); if (osgiPlatform != null) { try { osgiPlatform.stop(); } catch (Exception ex) { // swallow - logger.warn("shutdown procedure threw exception " + ex); + logger.warn("Shutdown procedure threw exception " + ex); } osgiPlatform = null; } } - /** - * Setup the piped streams to get the results out from the OSGi world. - * - * @throws IOException - */ - private void setupStreams() throws IOException { - - byte[] inArray = new byte[1024 * 2]; - // 16K seems to be enough - byte[] outArray = new byte[1024 * 16]; - - Properties systemProps = System.getProperties(); - - // put information for OSGi - systemProps.put(OsgiJUnitTest.FOR_OSGI, inArray); - - // get information from OSGi - systemProps.put(OsgiJUnitTest.FROM_OSGI, outArray); - - // setup output stream to prevent blocking - outputStream = new ObjectOutputStream(new ConfigurableByteArrayOutputStream(inArray)); - // flush header right away - outputStream.flush(); - - logger.debug("opened writer to OSGi"); - } - - /** - * Utility method for creating the input communication stream. - */ - private void createInStream() { - try { - byte[] inputSource = (byte[]) System.getProperties().get(OsgiJUnitTest.FROM_OSGI); - inputStream = new ObjectInputStream(new ByteArrayInputStream(inputSource)); - logger.debug("opened reader from OSGi"); - } - catch (IOException ex) { - throw new RuntimeException("cannot open streams; it's likely the osgi test execution failed;" + ex); - } - - } - // // OsgiJUnitTest execution hooks. Used by the test framework. // diff --git a/test-support/src/main/java/org/springframework/osgi/test/JUnitTestActivator.java b/test-support/src/main/java/org/springframework/osgi/test/JUnitTestActivator.java index 1db29cb5a..8d9b493b1 100644 --- a/test-support/src/main/java/org/springframework/osgi/test/JUnitTestActivator.java +++ b/test-support/src/main/java/org/springframework/osgi/test/JUnitTestActivator.java @@ -28,6 +28,8 @@ import org.osgi.framework.ServiceRegistration; import org.springframework.osgi.test.internal.OsgiJUnitTest; import org.springframework.osgi.test.internal.TestRunnerService; +import org.springframework.osgi.test.internal.holder.HolderLoader; +import org.springframework.osgi.test.internal.holder.OsgiTestInfoHolder; import org.springframework.osgi.test.internal.support.OsgiJUnitTestAdapter; import org.springframework.osgi.util.OsgiServiceUtils; @@ -77,9 +79,10 @@ void executeTest() { * @return */ private OsgiJUnitTest loadTest() { - String testClass = System.getProperty(OsgiJUnitTest.OSGI_TEST); + OsgiTestInfoHolder holder = HolderLoader.INSTANCE.getHolder(); + String testClass = holder.getTestClassName(); if (testClass == null) - throw new IllegalArgumentException("no test class specified under " + OsgiJUnitTest.OSGI_TEST); + throw new IllegalArgumentException("no test class specified"); try { // use bundle to load the classes diff --git a/test-support/src/main/java/org/springframework/osgi/test/internal/OsgiJUnitTest.java b/test-support/src/main/java/org/springframework/osgi/test/internal/OsgiJUnitTest.java index 3cf345f5a..8a26f56ab 100644 --- a/test-support/src/main/java/org/springframework/osgi/test/internal/OsgiJUnitTest.java +++ b/test-support/src/main/java/org/springframework/osgi/test/internal/OsgiJUnitTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.osgi.test.internal; import junit.framework.TestCase; @@ -29,27 +30,6 @@ */ public interface OsgiJUnitTest { - /** - * Lookup marker for "to-OSGi" communication channel. - */ - static final String FOR_OSGI = OsgiJUnitTest.class.getName() + "-osgi[in]"; - - /** - * Lookup marker for "from-OSGi" communication channel. - */ - static final String FROM_OSGI = OsgiJUnitTest.class.getName() + "-osgi[out]"; - - /** - * Lookup marker for the test suite that is executed inside the OSGi - * container. - */ - static final String OSGI_TEST = OsgiJUnitTest.class.getName() + "-test"; - - /** - * System property name used to pass around the spring-osgi-test bundle id. - */ - static final String OSGI_TEST_BUNDLE_ID = OsgiJUnitTest.class.getName() + "-bundle.id"; - /** * Replacement for the 'traditional' setUp. Called by TestRunnerService. * @@ -68,12 +48,14 @@ public interface OsgiJUnitTest { /** * Replacement for the 'traditional' runTest. Called by TestRunnerService. + * * @throws Throwable */ void osgiRunTest() throws Throwable; /** * Provides the OSGi bundle context to the test + * * @param bundleContext */ void injectBundleContext(BundleContext bundleContext); diff --git a/test-support/src/main/java/org/springframework/osgi/test/internal/holder/HolderLoader.java b/test-support/src/main/java/org/springframework/osgi/test/internal/holder/HolderLoader.java new file mode 100644 index 000000000..c879a4646 --- /dev/null +++ b/test-support/src/main/java/org/springframework/osgi/test/internal/holder/HolderLoader.java @@ -0,0 +1,79 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.osgi.test.internal.holder; + +import java.lang.reflect.Field; + +import org.osgi.framework.Bundle; +import org.springframework.util.ReflectionUtils; + +/** + * Specific OSGi loader for OsgiTestHolder. It's main usage is to load the + * holder using a class-loader outside the OSGi world and to store results + * there. + * + *

Boot delegation should work here but each platform has its own approach. + * Notably, Equinox uses by default the boot (not the app) classloader which + * means the classpath used for starting OSGi is not seen. To not interfere with + * the default configuration which might change in the future (it has been + * changed between 3.2 and 3.3) and to not impose restrictions on the test + * usage, the loader manually discovers the proper classloader and uses it to + * load the holder class. Inside OSGi, special care must be taken to make sure + * no CCE are generated. + * + * @author Costin Leau + * + */ +public class HolderLoader { + + private static final String INSTANCE_FIELD = "INSTANCE"; + + private static final String HOLDER_CLASS_NAME = "org.springframework.osgi.test.internal.holder.OsgiTestInfoHolder"; + + public static final HolderLoader INSTANCE = new HolderLoader(); + + private final OsgiTestInfoHolder holder; + + + public HolderLoader() { + // use the app ClassLoader + ClassLoader appCL = Bundle.class.getClassLoader(); + Class clazz; + try { + clazz = appCL.loadClass(HOLDER_CLASS_NAME); + } + catch (Exception ex) { + throw (RuntimeException) new IllegalStateException("cannot properly load class " + HOLDER_CLASS_NAME).initCause(ex); + } + // get the static instance + Field field = ReflectionUtils.findField(clazz, INSTANCE_FIELD, clazz); + Object instance; + try { + instance = field.get(null); + } + catch (Exception ex) { + throw (RuntimeException) new IllegalStateException("cannot read property " + INSTANCE_FIELD).initCause(ex); + } + // once the class is loaded return it wrapped through it's OSGi instance + holder = new ReflectionOsgiHolder(instance); + } + + public OsgiTestInfoHolder getHolder() { + return holder; + } + +} diff --git a/test-support/src/main/java/org/springframework/osgi/test/internal/holder/OsgiTestInfoHolder.java b/test-support/src/main/java/org/springframework/osgi/test/internal/holder/OsgiTestInfoHolder.java new file mode 100644 index 000000000..1f5be75bd --- /dev/null +++ b/test-support/src/main/java/org/springframework/osgi/test/internal/holder/OsgiTestInfoHolder.java @@ -0,0 +1,129 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.osgi.test.internal.holder; + +import java.util.ArrayList; +import java.util.List; + +/** + * Custom class used for storing JUnit test results. To work, this class should + * always be loaded through the same class loader, to transport + * information from OSGi to the outside world. + * + * @author Costin Leau + * + */ +public class OsgiTestInfoHolder { + + /** JUnit test problems */ + private List testFailures = new ArrayList(4); + private List testErrors = new ArrayList(4); + + /** test bundle id */ + private Long testBundleId; + /** test class name */ + private String testClassName; + /** test method name */ + private String testMethodName; + + /** static instance */ + public static final OsgiTestInfoHolder INSTANCE = new OsgiTestInfoHolder(); + + + /** + * + * Constructs a new OsgiTestInfoHolder instance. + */ + public OsgiTestInfoHolder() { + } + + /** + * Returns the testBundleId. + * + * @return Returns the testBundleId + */ + public Long getTestBundleId() { + return testBundleId; + } + + /** + * @param testBundleId The testBundleId to set. + */ + public void setTestBundleId(Long testBundleId) { + this.testBundleId = testBundleId; + } + + /** + * Returns the testClassName. + * + * @return Returns the testClassName + */ + public String getTestClassName() { + return testClassName; + } + + /** + * @param testClassName The testClassName to set. + */ + public void setTestClassName(String testClassName) { + this.testClassName = testClassName; + } + + /** + * @param testProblem The testResult to set. + */ + public void addTestFailure(Throwable testProblem) { + // TODO: unpack exception to prevent classloader leaks + testFailures.add(testProblem); + } + + public void addTestError(Throwable testProblem) { + testErrors.add(testProblem); + } + + /** + * Returns the testMethodName. + * + * @return Returns the testMethodName + */ + public String getTestMethodName() { + return testMethodName; + } + + /** + * @param testMethodName The testMethodName to set. + */ + public void setTestMethodName(String testMethodName) { + this.testMethodName = testMethodName; + } + + public List getTestFailures() { + return testFailures; + } + + public List getTestErrors() { + return testErrors; + } + + /** + * Clear all information. Used between test runs to clear results. + */ + public void clearResults() { + testFailures.clear(); + testErrors.clear(); + } +} diff --git a/test-support/src/main/java/org/springframework/osgi/test/internal/holder/ReflectionOsgiHolder.java b/test-support/src/main/java/org/springframework/osgi/test/internal/holder/ReflectionOsgiHolder.java new file mode 100644 index 000000000..6050941cf --- /dev/null +++ b/test-support/src/main/java/org/springframework/osgi/test/internal/holder/ReflectionOsgiHolder.java @@ -0,0 +1,79 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.osgi.test.internal.holder; + +import java.lang.reflect.Method; + +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; + +/** + * OSGi adapter for the information holder. Overrides the methods used inside + * OSGi to use reflection and avoid CCE. + * + * @author Costin Leau + * + */ +class ReflectionOsgiHolder extends OsgiTestInfoHolder { + + private final Object instance; + + private final Method GET_TEST_BUNDLE_ID, GET_TEST_CLASS_NAME, GET_TEST_METHOD_NAME, ADD_TEST_ERROR, + ADD_TEST_FAILURE; + + + /** + * Constructs a new OsgiTestInfoHolder instance wrapping the + * given object and accessing it through reflection. This constructor is + * used for accessing the instance loaded outside OSGi, from within OSGi. + * + * @param twinInstance + */ + ReflectionOsgiHolder(Object twinInstance) { + Assert.notNull(twinInstance); + this.instance = twinInstance; + Class clazz = instance.getClass(); + GET_TEST_BUNDLE_ID = ReflectionUtils.findMethod(clazz, "getTestBundleId"); + GET_TEST_CLASS_NAME = ReflectionUtils.findMethod(clazz, "getTestClassName"); + GET_TEST_METHOD_NAME = ReflectionUtils.findMethod(clazz, "getTestMethodName"); + + ADD_TEST_ERROR = ReflectionUtils.findMethod(clazz, "addTestError", new Class[] { Throwable.class }); + ADD_TEST_FAILURE = ReflectionUtils.findMethod(clazz, "addTestFailure", new Class[] { Throwable.class }); + + } + + public Long getTestBundleId() { + return (Long) ReflectionUtils.invokeMethod(GET_TEST_BUNDLE_ID, instance); + } + + public String getTestClassName() { + return (String) ReflectionUtils.invokeMethod(GET_TEST_CLASS_NAME, instance); + } + + public String getTestMethodName() { + return (String) ReflectionUtils.invokeMethod(GET_TEST_METHOD_NAME, instance); + } + + public void addTestError(Throwable testProblem) { + ReflectionUtils.invokeMethod(ADD_TEST_ERROR, instance, new Object[] { testProblem }); + } + + public void addTestFailure(Throwable testProblem) { + ReflectionUtils.invokeMethod(ADD_TEST_FAILURE, instance, new Object[] { testProblem }); + } + +} diff --git a/test-support/src/main/java/org/springframework/osgi/test/internal/support/Activator.java b/test-support/src/main/java/org/springframework/osgi/test/internal/support/Activator.java index 3ec65fb6f..413bf9de4 100644 --- a/test-support/src/main/java/org/springframework/osgi/test/internal/support/Activator.java +++ b/test-support/src/main/java/org/springframework/osgi/test/internal/support/Activator.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.osgi.test.internal.support; import java.util.Hashtable; @@ -20,12 +21,12 @@ import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; -import org.springframework.osgi.test.internal.OsgiJUnitTest; import org.springframework.osgi.test.internal.TestRunnerService; +import org.springframework.osgi.test.internal.holder.HolderLoader; /** - * Default activator for Spring/OSGi test support. This class can be seen as the 'server-side' of the framework, - * which register the OsgiJUnitTest executor. + * Default activator for Spring/OSGi test support. This class can be seen as the + * 'server-side' of the framework, which register the OsgiJUnitTest executor. * * @author Costin Leau * @@ -34,23 +35,16 @@ public class Activator implements BundleActivator { private ServiceRegistration registration; - /* - * (non-Javadoc) - * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) - */ + public void start(BundleContext context) throws Exception { - registration = context.registerService(TestRunnerService.class.getName(), new OsgiJUnitService(), new Hashtable()); - - // add also the bundle id so that AbstractOsgiTest can determine its BundleContext when used in an environment - // where the system bundle is treated as a special case (such as mBeddedServer). - System.getProperties().put(OsgiJUnitTest.OSGI_TEST_BUNDLE_ID, new Long(context.getBundle().getBundleId())); + registration = context.registerService(TestRunnerService.class.getName(), new OsgiJUnitService(), + new Hashtable()); + // add also the bundle id so that AbstractOsgiTest can determine its BundleContext when used in an environment + // where the system bundle is treated as a special case. + HolderLoader.INSTANCE.getHolder().setTestBundleId(new Long(context.getBundle().getBundleId())); } - /* - * (non-Javadoc) - * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) - */ public void stop(BundleContext context) throws Exception { // unregister the service even though the framework should do this automatically if (registration != null) diff --git a/test-support/src/main/java/org/springframework/osgi/test/internal/support/OsgiJUnitService.java b/test-support/src/main/java/org/springframework/osgi/test/internal/support/OsgiJUnitService.java index 4677a7ac4..feb6d8922 100644 --- a/test-support/src/main/java/org/springframework/osgi/test/internal/support/OsgiJUnitService.java +++ b/test-support/src/main/java/org/springframework/osgi/test/internal/support/OsgiJUnitService.java @@ -13,12 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.osgi.test.internal.support; -import java.io.ByteArrayInputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.Properties; +package org.springframework.osgi.test.internal.support; import junit.framework.Protectable; import junit.framework.Test; @@ -29,8 +25,8 @@ import org.apache.commons.logging.LogFactory; import org.springframework.osgi.test.internal.OsgiJUnitTest; import org.springframework.osgi.test.internal.TestRunnerService; -import org.springframework.osgi.test.internal.util.ConfigurableByteArrayOutputStream; -import org.springframework.osgi.test.internal.util.IOUtils; +import org.springframework.osgi.test.internal.holder.HolderLoader; +import org.springframework.osgi.test.internal.holder.OsgiTestInfoHolder; import org.springframework.osgi.test.internal.util.TestUtils; /** @@ -43,31 +39,6 @@ public class OsgiJUnitService implements TestRunnerService { private static final Log log = LogFactory.getLog(OsgiJUnitService.class); - /** - * Write the test result. - */ - private ObjectOutputStream outStream; - - /** - * Read the test name to execute. - */ - private ObjectInputStream inStream; - - protected void setupStreams() throws Exception { - Properties props = System.getProperties(); - - byte[] outSource = (byte[]) props.get(OsgiJUnitTest.FROM_OSGI); - this.outStream = new ObjectOutputStream(new ConfigurableByteArrayOutputStream(outSource)); - // write header - this.outStream.flush(); - - log.debug("opened writer to OSGi-outside"); - - byte[] inSource = (byte[]) props.get(OsgiJUnitTest.FOR_OSGI); - this.inStream = new ObjectInputStream(new ByteArrayInputStream(inSource)); - - log.debug("opened reader for OSGi"); - } public void runTest(OsgiJUnitTest test) { try { @@ -88,22 +59,21 @@ public void runTest(OsgiJUnitTest test) { * @throws Exception */ protected void executeTest(OsgiJUnitTest test) throws Exception { - try { - setupStreams(); - - // read the test to be executed - String testName = inStream.readUTF(); - // execute the test - TestResult result = runTest(test, testName); - - log.debug("sending test results from OSGi"); - // write result back to the stream - TestUtils.sendTestResult(result, outStream); - } - finally { - IOUtils.closeStream(outStream); - IOUtils.closeStream(inStream); - } + // create holder + // since we're inside OSGi, we have to use the special loading procedure + OsgiTestInfoHolder holder = HolderLoader.INSTANCE.getHolder(); + + // read the test to be executed + String testName = holder.getTestMethodName(); + if (log.isDebugEnabled()) + log.debug("Reading test [" + testName + "] for execution inside OSGi"); + // execute the test + TestResult result = runTest(test, testName); + + if (log.isDebugEnabled()) + log.debug("Sending test results from OSGi"); + // write result back to the outside world + TestUtils.unpackProblems(result, holder); } /** @@ -113,11 +83,11 @@ protected void executeTest(OsgiJUnitTest test) throws Exception { * @param testName */ protected TestResult runTest(final OsgiJUnitTest osgiTestExtensions, String testName) { - - log.debug("running test [" + testName + "] on testCase " + osgiTestExtensions); + if (log.isDebugEnabled()) + log.debug("Running test [" + testName + "] on testCase " + osgiTestExtensions); final TestResult result = new TestResult(); TestCase rawTest = osgiTestExtensions.getTestCase(); - + rawTest.setName(testName); try { diff --git a/test-support/src/main/java/org/springframework/osgi/test/internal/util/TestUtils.java b/test-support/src/main/java/org/springframework/osgi/test/internal/util/TestUtils.java index df2717891..449efc90c 100644 --- a/test-support/src/main/java/org/springframework/osgi/test/internal/util/TestUtils.java +++ b/test-support/src/main/java/org/springframework/osgi/test/internal/util/TestUtils.java @@ -13,21 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.osgi.test.internal.util; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; import java.util.List; +import java.util.Vector; import junit.framework.AssertionFailedError; import junit.framework.Test; import junit.framework.TestFailure; import junit.framework.TestResult; +import org.springframework.osgi.test.internal.holder.OsgiTestInfoHolder; +import org.springframework.util.ReflectionUtils; + /** * Utility class for running OSGi-JUnit tests. * @@ -39,6 +45,9 @@ public abstract class TestUtils { /** * Serialize the test result using the given OutputStream. * + * @deprecated will be removed in 1.1.0 w/o a replacement. the test + * framework relies on classloading instead of serialization. + * * @param result * @param stream */ @@ -71,6 +80,9 @@ public static void sendTestResult(TestResult result, ObjectOutputStream stream) * Deserialize testResult from the given ObjectInputStream. The loaded * failures/errors are added to the given testResult. * + * @deprecated will be removed in 1.1.0 w/o a replacement. the test + * framework relies on classloading instead of serialization. + * * @param testResult the test result to which the properties are added * @param stream the stream used to load the test result * @param test the test used for adding the TestResult errors/failures @@ -98,4 +110,71 @@ public static void receiveTestResult(TestResult testResult, Test test, ObjectInp testResult.addFailure(test, (AssertionFailedError) iter.next()); } } + + /** + * Clones the test result from a TestResult loaded through a different + * classloader. + * + * @param source test result loaded through a different classloader + * @param destination test result reported to the outside framework + * @param test initial test used for bootstrapping the integration framework + * @return cloned test result + */ + public static TestResult cloneTestResults(OsgiTestInfoHolder source, TestResult destination, Test test) { + // since we cannot cast, we have to use reflection + + // List errors; + // List failures; + // + // { + // Field errorsField = ReflectionUtils.findField(source.getClass(), "fErrors", Vector.class); + // ReflectionUtils.makeAccessible(errorsField); + // Field failuresField = ReflectionUtils.findField(source.getClass(), "fFailures", Vector.class); + // ReflectionUtils.makeAccessible(failuresField); + // try { + // errors = (List) errorsField.get(source); + // failures = (List) failuresField.get(source); + // } + // catch (IllegalAccessException iae) { + // throw (RuntimeException) new IllegalStateException("cannot access test results fields").initCause(iae); + // } + // } + + // get errors + for (Iterator iter = source.getTestErrors().iterator(); iter.hasNext();) { + destination.addError(test, (Throwable) iter.next()); + } + + // get failures + // since failures are a special JUnit error, we have to clone the stack + for (Iterator iter = source.getTestFailures().iterator(); iter.hasNext();) { + Throwable originalFailure = (Throwable) iter.next(); + AssertionFailedError clonedFailure = new AssertionFailedError(originalFailure.getMessage()); + clonedFailure.setStackTrace(originalFailure.getStackTrace()); + destination.addFailure(test, clonedFailure); + } + + return destination; + } + + /** + * Utility method which extracts the information from a TestResult and + * stores it as primordial classes. This avoids the use of reflection when + * reading the results outside OSGi. + * + * @param result + * @param holder + */ + public static void unpackProblems(TestResult result, OsgiTestInfoHolder holder) { + Enumeration errors = result.errors(); + while (errors.hasMoreElements()) { + TestFailure failure = (TestFailure) errors.nextElement(); + holder.addTestError(failure.thrownException()); + } + Enumeration failures = result.failures(); + while (failures.hasMoreElements()) { + TestFailure failure = (TestFailure) failures.nextElement(); + holder.addTestFailure(failure.thrownException()); + } + } } diff --git a/test-support/src/main/java/org/springframework/osgi/test/platform/EquinoxPlatform.java b/test-support/src/main/java/org/springframework/osgi/test/platform/EquinoxPlatform.java index 0bcfbc7d0..68d571eea 100644 --- a/test-support/src/main/java/org/springframework/osgi/test/platform/EquinoxPlatform.java +++ b/test-support/src/main/java/org/springframework/osgi/test/platform/EquinoxPlatform.java @@ -52,6 +52,8 @@ protected Properties getPlatformProperties() { props.setProperty("osgi.instance.area", "eclipse_config"); props.setProperty("osgi.user.area", "eclipse_config"); + // props.setProperty("osgi.java.profile.bootdelegation", "ignore"); + // props.setProperty("eclipse.consoleLog", "true"); // props.setProperty("osgi.debug", ""); diff --git a/test-support/src/main/java/org/springframework/osgi/test/platform/MBSProPlatform.jav b/test-support/src/main/java/org/springframework/osgi/test/platform/MBSProPlatform.jav deleted file mode 100644 index 729da23ab..000000000 --- a/test-support/src/main/java/org/springframework/osgi/test/platform/MBSProPlatform.jav +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2002-2007 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.osgi.test.platform; - -import java.io.File; -import java.util.Properties; - -import org.osgi.framework.BundleContext; -import org.osgi.framework.Constants; -import org.springframework.osgi.test.util.IOUtils; -import org.springframework.util.ObjectUtils; - -import com.prosyst.mbs.impl.framework.BundleContextImpl; -import com.prosyst.mbs.impl.framework.Framework; -import com.prosyst.mbs.impl.framework.Start; - -/** - * Prosyst mBedded Server Professional Edition 6.1.x - * - * @author Costin Leau - */ -public class MBSProPlatform extends AbstractOsgiPlatform { - - private BundleContext context; - - private File storageDir; - - public MBSProPlatform() { - toString = "mBedded Professional OSGi Platform"; - } - - protected Properties getPlatformProperties() { - Properties props = new Properties(); - - // default properties - props.setProperty("mbs.loader.r4", "true"); - props.setProperty("mbs.storage.delete", "true"); - - // this affects only the connector version but still - props.setProperty("mbs.storage.mm.type", "ram"); - props.setProperty("mbs.certificates", "false"); - - // props.setProperty("mbs.boot.disableProcessing", "true"); - - props.setProperty(Constants.SUPPORTS_FRAMEWORK_FRAGMENT, "true"); - props.setProperty(Constants.SUPPORTS_FRAMEWORK_REQUIREBUNDLE, "true"); - - props.setProperty("mbs.debug", "0"); - - props.putAll(getTemporaryStorage()); - - return props; - } - - protected Properties getTemporaryStorage() { - Properties props = new Properties(); - storageDir = createTempDir("mbs"); - - props.setProperty("mbs.storage.root", storageDir.getAbsolutePath()); - - if (log.isTraceEnabled()) - log.trace("mBedded temporary storage dir is " + storageDir.getAbsolutePath()); - return props; - } - - /* - * (non-Javadoc) - * - * @see org.springframework.osgi.test.OsgiPlatform#getBundleContext() - */ - public BundleContext getBundleContext() { - return context; - } - - /* - * (non-Javadoc) - * - * @see org.springframework.osgi.test.OsgiPlatform#start() - */ - public void start() throws Exception { - // copy configuration properties to sys properties - Properties props = getConfigurationProperties(); - System.getProperties().putAll(props); - Start.main(new String[0]); - - context = BundleContextImpl.nullCtx; - } - - /* - * (non-Javadoc) - * - * @see org.springframework.osgi.test.OsgiPlatform#stop() - */ - public void stop() throws Exception { - try { - Framework.systemBundle.stop(); - } - finally { - IOUtils.delete(storageDir); - } - } -} diff --git a/test-support/src/main/java/org/springframework/osgi/test/provisioning/internal/LocalFileSystemMavenRepository.java b/test-support/src/main/java/org/springframework/osgi/test/provisioning/internal/LocalFileSystemMavenRepository.java index 195a85ccf..720d85483 100644 --- a/test-support/src/main/java/org/springframework/osgi/test/provisioning/internal/LocalFileSystemMavenRepository.java +++ b/test-support/src/main/java/org/springframework/osgi/test/provisioning/internal/LocalFileSystemMavenRepository.java @@ -89,18 +89,18 @@ private void init() { Resource settingsFile = new FileSystemResource(new File(userHome, M2_SETTINGS)); localRepository = getMavenSettingsLocalRepository(settingsFile); if (trace) - log.trace("falling back to M2 settings.xml [" + settingsFile + "]; found value=" + localRepository); + log.trace("Falling back to M2 settings.xml [" + settingsFile + "]; found value=" + localRepository); if (localRepository == null) { // fall back to the default location localRepository = new File(userHome, DEFAULT_DIR).getAbsolutePath(); if (trace) - log.trace("no custom setting found; using default M2 local repository=" + localRepository); + log.trace("No custom setting found; using default M2 local repository=" + localRepository); } } repositoryHome = localRepository; - log.info("local Maven2 repository used: [" + repositoryHome + "]"); + log.info("Local Maven2 repository used: [" + repositoryHome + "]"); } /** diff --git a/test-support/src/main/resources/org/springframework/osgi/test/internal/boot-bundles.properties b/test-support/src/main/resources/org/springframework/osgi/test/internal/boot-bundles.properties index b3644fed0..d70bb7cba 100644 --- a/test-support/src/main/resources/org/springframework/osgi/test/internal/boot-bundles.properties +++ b/test-support/src/main/resources/org/springframework/osgi/test/internal/boot-bundles.properties @@ -26,7 +26,7 @@ ignore.junit.version=3.8.2-SNAPSHOT ignore.log4j.version=1.2.15-SNAPSHOT ignore.spring.version=2.5.1 -ignore.spring.osgi.version=1.0-SNAPSHOT +ignore.spring.osgi.version=1.0.3 ignore.slf4j.version=1.4.3 ignore.asm.version=2.2.3-SNAPSHOT diff --git a/test-support/src/test/java/org/springframework/osgi/test/JUnitTestActivatorTest.java b/test-support/src/test/java/org/springframework/osgi/test/JUnitTestActivatorTest.java index 4f52e7693..ec2d0aeab 100644 --- a/test-support/src/test/java/org/springframework/osgi/test/JUnitTestActivatorTest.java +++ b/test-support/src/test/java/org/springframework/osgi/test/JUnitTestActivatorTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.osgi.test; import java.lang.reflect.Field; @@ -28,17 +29,21 @@ import org.springframework.osgi.mock.MockServiceReference; import org.springframework.osgi.test.internal.OsgiJUnitTest; import org.springframework.osgi.test.internal.TestRunnerService; +import org.springframework.osgi.test.internal.holder.OsgiTestInfoHolder; public class JUnitTestActivatorTest extends TestCase { private JUnitTestActivator activator; + // private ServiceRegistration registration; // private ServiceReference reference; public static class TestExample extends TestCase implements OsgiJUnitTest { + private static BundleContext context; + public void osgiSetUp() throws Exception { } @@ -70,9 +75,10 @@ public Bundle findBundleBySymbolicName(String bundleSymbolicName) { public TestCase getTestCase() { return this; } - + } + protected void setUp() throws Exception { activator = new JUnitTestActivator(); // reference = new MockServiceReference(); @@ -149,7 +155,7 @@ public void testLoadTest() throws Exception { servCtrl.replay(); setActivatorField("context", ctx); - System.setProperty(OsgiJUnitTest.OSGI_TEST, TestExample.class.getName()); + OsgiTestInfoHolder.INSTANCE.setTestClassName(TestExample.class.getName()); activator.executeTest(); assertSame(ctx, TestExample.context); diff --git a/test-support/src/test/java/org/springframework/osgi/test/internal/StreamCorruption.java b/test-support/src/test/java/org/springframework/osgi/test/internal/StreamCorruption.java deleted file mode 100644 index cf8af0cf6..000000000 --- a/test-support/src/test/java/org/springframework/osgi/test/internal/StreamCorruption.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2006-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.osgi.test.internal; - -import java.io.ByteArrayInputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.lang.reflect.Method; - -import junit.framework.TestCase; - -import org.springframework.osgi.test.AbstractDependencyManagerTests; -import org.springframework.osgi.test.AbstractOsgiTests; -import org.springframework.osgi.test.internal.util.ConfigurableByteArrayOutputStream; - -/** - * Test JUnit stream corruption; basically test that the stream size is suitable - * enought. - * - * @author Costin Leau - * - */ -public class StreamCorruption extends TestCase { - - private ObjectInputStream in; - private ObjectOutputStream out; - private AbstractOsgiTests osgiTest; - - /* - * (non-Javadoc) - * - * @see junit.framework.TestCase#setUp() - */ - protected void setUp() throws Exception { - osgiTest = new AbstractDependencyManagerTests() { - }; - - // call AbstractOsgiTest to setup the streams - invokeOsgiTestMethod(osgiTest, "setupStreams"); - //osgiTest.setupStreams(); - } - - /* - * (non-Javadoc) - * - * @see junit.framework.TestCase#tearDown() - */ - protected void tearDown() throws Exception { - if (in != null) { - try { - in.close(); - } - catch (Exception ex) { - // ignore - } - } - - if (out != null) { - try { - out.close(); - } - catch (Exception ex) { - // ignore - } - } - - invokeOsgiTestMethod(osgiTest, "completeTestExecution"); - } - - public void testOutputStreamSize() throws Exception { - Throwable throwable; - - // build a huge stacktrace - try { - try { - try { - throw new IllegalArgumentException("root cause").fillInStackTrace(); - } - catch (Throwable ex1) { - throw new UnsupportedOperationException().initCause(ex1).fillInStackTrace(); - } - } - catch (Throwable ex2) { - throw new UnsupportedOperationException().initCause(ex2).fillInStackTrace(); - } - } - catch (Throwable ex3) { - new Exception("foo-bar", - new Exception(new Exception(new Exception(new Exception(new RuntimeException(new RuntimeException( - new ClassNotFoundException("boo", new RuntimeException(ex3)))))))).fillInStackTrace()); - - throwable = ex3; - } - - byte[] array = (byte[]) System.getProperties().get(OsgiJUnitTest.FROM_OSGI); - - out = new ObjectOutputStream(new ConfigurableByteArrayOutputStream(array)); - - // write the exception - out.writeObject(throwable); - out.flush(); - - in = new ObjectInputStream(new ByteArrayInputStream(array)); - Throwable read = (Throwable) in.readObject(); - assertEquals(throwable.getMessage(), read.getMessage()); - assertEquals(throwable.getClass(), read.getClass()); - } - - private void invokeOsgiTestMethod(Object obj, String name) throws Exception { - Method method = AbstractOsgiTests.class.getDeclaredMethod(name, null); - method.setAccessible(true); - method.invoke(obj, null); - } - -// private Object readField(Object obj, String name) throws Exception { -// Field field = obj.getClass().getDeclaredField(name); -// field.setAccessible(true); -// return field.get(obj); -// } -// -// private void writeField(Object obj, String name, Object value) throws Exception { -// Field field = obj.getClass().getDeclaredField(name); -// field.setAccessible(true); -// field.set(obj, value); -// } -} diff --git a/test-support/src/test/java/org/springframework/osgi/test/parsing/BaseTestCaseWithVisibleMethods.java b/test-support/src/test/java/org/springframework/osgi/test/parsing/BaseTestCaseWithVisibleMethods.java new file mode 100644 index 000000000..e0a4f49e5 --- /dev/null +++ b/test-support/src/test/java/org/springframework/osgi/test/parsing/BaseTestCaseWithVisibleMethods.java @@ -0,0 +1,47 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.osgi.test.parsing; + +import java.util.Properties; +import java.util.jar.Manifest; + +import org.springframework.osgi.test.AbstractConfigurableBundleCreatorTests; + +/** + * @author Costin Leau + * + */ +public class BaseTestCaseWithVisibleMethods extends AbstractConfigurableBundleCreatorTests { + + public String getRootPath() { + return super.getRootPath(); + } + + public Manifest getManifest() { + return super.getManifest(); + } + + public Properties getSettings() throws Exception { + return super.getSettings(); + } + + public String[] getBundleContentPattern() { + return super.getBundleContentPattern(); + } + + +} diff --git a/test-support/src/test/java/org/springframework/osgi/test/parsing/DifferentParentsInDifferentBundlesTest.java b/test-support/src/test/java/org/springframework/osgi/test/parsing/DifferentParentsInDifferentBundlesTest.java new file mode 100644 index 000000000..6cde16326 --- /dev/null +++ b/test-support/src/test/java/org/springframework/osgi/test/parsing/DifferentParentsInDifferentBundlesTest.java @@ -0,0 +1,97 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.osgi.test.parsing; + +import java.lang.reflect.Field; +import java.util.jar.Manifest; + +import junit.framework.TestCase; + +import org.osgi.framework.Constants; +import org.springframework.osgi.test.AbstractConfigurableBundleCreatorTests; +import org.springframework.osgi.test.parsing.packageA.BaseClassFromAnotherPackage; +import org.springframework.osgi.test.parsing.packageB.BaseClassFromAnotherPackageAndBundle; +import org.springframework.osgi.test.parsing.packageC.TestInDifferentPackageThenItsParents; +import org.springframework.osgi.test.parsing.packageZ.FinalTestClass; +import org.springframework.util.ObjectUtils; + +/** + * Integration that checks if the class hierarchy is properly parsed. Note this + * test doesn't run in OSGi, it just invokes the bytecode parsing. + * + * @author Costin Leau + * + */ +public class DifferentParentsInDifferentBundlesTest extends TestCase { + + public void testCheckBaseClassesHierarchy() throws Exception { + // create class + // make sure the packaging puts some of the tests parent in a different class + TestInDifferentPackageThenItsParents test = new TestInDifferentPackageThenItsParents() { + }; + + String importPackage = getImportPackage(test); + + // check parent package + // parent in a different bundle + assertTrue("missing parent package not considered", contains(importPackage, + BaseClassFromAnotherPackageAndBundle.class.getPackage().getName())); + // parent in a different package but the same bundle (i.e. no import) + assertFalse("contained parent not considered", contains(importPackage, + BaseClassFromAnotherPackage.class.getPackage().getName())); + // check present parent dependencies + assertTrue("contained parent dependencies not considered", contains(importPackage, "javax.imageio")); + } + + public void testSuperClassInterfacesConsidered() throws Exception { + FinalTestClass test = new FinalTestClass() { + }; + + String importPackage = getImportPackage(test); + // check test interface package + assertTrue("interface present on the test class ignored", contains(importPackage, "javax.swing.text")); + // check super class interface package + assertTrue("interface present on the test class ignored", contains(importPackage, + "javax.security.auth.callback")); + // check super class interface package + assertTrue("interface present on superclass ignored", contains(importPackage, "javax.print")); + } + + private Manifest getParsedManifestFor(BaseTestCaseWithVisibleMethods testCase) throws Exception { + + System.out.println(ObjectUtils.nullSafeToString(testCase.getBundleContentPattern())); + Field jarSettings = AbstractConfigurableBundleCreatorTests.class.getDeclaredField("jarSettings"); + // initialize settings + jarSettings.setAccessible(true); + jarSettings.set(null, testCase.getSettings()); + + Manifest mf = testCase.getManifest(); + + return mf; + } + + private String getImportPackage(BaseTestCaseWithVisibleMethods test) throws Exception { + Manifest mf = getParsedManifestFor(test); + String importPackage = mf.getMainAttributes().getValue(Constants.IMPORT_PACKAGE); + System.out.println("import package value is " + importPackage); + return importPackage; + } + + private boolean contains(String text, String item) { + return text.indexOf(item) > -1; + } +} diff --git a/test-support/src/test/java/org/springframework/osgi/test/parsing/packageA/BaseClassFromAnotherPackage.java b/test-support/src/test/java/org/springframework/osgi/test/parsing/packageA/BaseClassFromAnotherPackage.java new file mode 100644 index 000000000..3bf14e7b1 --- /dev/null +++ b/test-support/src/test/java/org/springframework/osgi/test/parsing/packageA/BaseClassFromAnotherPackage.java @@ -0,0 +1,37 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.osgi.test.parsing.packageA; + +import java.io.File; + +import javax.imageio.ImageIO; + +import org.springframework.osgi.test.parsing.BaseTestCaseWithVisibleMethods; + +/** + * Simple base class from a different class that will be included in the + * archive. + * + * @author Costin Leau + * + */ +public class BaseClassFromAnotherPackage extends BaseTestCaseWithVisibleMethods { + + // strange import that doesn't do anything + private static File file = ImageIO.getCacheDirectory(); + +} diff --git a/test-support/src/test/java/org/springframework/osgi/test/parsing/packageB/BaseClassFromAnotherPackageAndBundle.java b/test-support/src/test/java/org/springframework/osgi/test/parsing/packageB/BaseClassFromAnotherPackageAndBundle.java new file mode 100644 index 000000000..04485112d --- /dev/null +++ b/test-support/src/test/java/org/springframework/osgi/test/parsing/packageB/BaseClassFromAnotherPackageAndBundle.java @@ -0,0 +1,33 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.osgi.test.parsing.packageB; + +import javax.naming.CompositeName; +import javax.naming.Name; + +import org.springframework.osgi.test.parsing.packageA.BaseClassFromAnotherPackage; + +/** + * @author Costin Leau + * + */ +// add an interface just to check the behaviour +public class BaseClassFromAnotherPackageAndBundle extends BaseClassFromAnotherPackage { + + private Name context = new CompositeName(); + +} diff --git a/test-support/src/test/java/org/springframework/osgi/test/parsing/packageC/TestInDifferentPackageThenItsParents.java b/test-support/src/test/java/org/springframework/osgi/test/parsing/packageC/TestInDifferentPackageThenItsParents.java new file mode 100644 index 000000000..b1e68cc0f --- /dev/null +++ b/test-support/src/test/java/org/springframework/osgi/test/parsing/packageC/TestInDifferentPackageThenItsParents.java @@ -0,0 +1,44 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.osgi.test.parsing.packageC; + +import java.util.jar.Manifest; + +import org.springframework.osgi.test.parsing.packageA.BaseClassFromAnotherPackage; +import org.springframework.osgi.test.parsing.packageB.BaseClassFromAnotherPackageAndBundle; + +/** + * Abstract since we don't want to execute the test per se. + * + * @author Costin Leau + * + */ +// callback interface (no exception or custom method signature pulled in) +public abstract class TestInDifferentPackageThenItsParents extends BaseClassFromAnotherPackageAndBundle { + + public void testCheckBaseClassesHierarchy() throws Exception { + Manifest mf = getManifest(); + System.out.println(mf.getMainAttributes().entrySet()); + } + + public String[] getBundleContentPattern() { + String pkg = TestInDifferentPackageThenItsParents.class.getPackage().getName().replace('.', '/').concat("/"); + String[] patterns = new String[] { pkg + "**/*", + BaseClassFromAnotherPackage.class.getName().replace('.', '/').concat(".class") }; + return patterns; + } +} diff --git a/test-support/src/test/java/org/springframework/osgi/test/parsing/packageX/BaseClassInACertainPackage.java b/test-support/src/test/java/org/springframework/osgi/test/parsing/packageX/BaseClassInACertainPackage.java new file mode 100644 index 000000000..a6e148585 --- /dev/null +++ b/test-support/src/test/java/org/springframework/osgi/test/parsing/packageX/BaseClassInACertainPackage.java @@ -0,0 +1,44 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.osgi.test.parsing.packageX; + +import java.io.File; +import java.net.URI; + +import javax.imageio.ImageIO; +import javax.print.URIException; + +import org.springframework.osgi.test.parsing.BaseTestCaseWithVisibleMethods; + +/** + * @author Costin Leau + * + */ +public abstract class BaseClassInACertainPackage extends BaseTestCaseWithVisibleMethods implements URIException { + + // strange import that doesn't do anything + private static File file = ImageIO.getCacheDirectory(); + + + public int getReason() { + throw new UnsupportedOperationException(); + } + + public URI getUnsupportedURI() { + throw new UnsupportedOperationException(); + } +} diff --git a/test-support/src/test/java/org/springframework/osgi/test/parsing/packageY/ParentClassWithCertainInterfaceTest.java b/test-support/src/test/java/org/springframework/osgi/test/parsing/packageY/ParentClassWithCertainInterfaceTest.java new file mode 100644 index 000000000..ad476d318 --- /dev/null +++ b/test-support/src/test/java/org/springframework/osgi/test/parsing/packageY/ParentClassWithCertainInterfaceTest.java @@ -0,0 +1,31 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.osgi.test.parsing.packageY; + +import javax.security.auth.callback.Callback; + +import org.springframework.osgi.test.parsing.packageX.BaseClassInACertainPackage; + +/** + * Test class that implements an interface. + * + * @author Costin Leau + * + */ +public abstract class ParentClassWithCertainInterfaceTest extends BaseClassInACertainPackage implements Callback { + +} diff --git a/test-support/src/test/java/org/springframework/osgi/test/parsing/packageZ/FinalTestClass.java b/test-support/src/test/java/org/springframework/osgi/test/parsing/packageZ/FinalTestClass.java new file mode 100644 index 000000000..ca9448f63 --- /dev/null +++ b/test-support/src/test/java/org/springframework/osgi/test/parsing/packageZ/FinalTestClass.java @@ -0,0 +1,32 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.osgi.test.parsing.packageZ; + +import javax.swing.text.Position; + +import org.springframework.osgi.test.parsing.packageY.ParentClassWithCertainInterfaceTest; + +/** + * @author Costin Leau + * + */ +public abstract class FinalTestClass extends ParentClassWithCertainInterfaceTest implements Position { + + public int getOffset() { + throw new UnsupportedOperationException(); + } +} diff --git a/test-support/src/test/resources/log4j.properties b/test-support/src/test/resources/log4j.properties index 5736e2ec5..4306f7f03 100644 --- a/test-support/src/test/resources/log4j.properties +++ b/test-support/src/test/resources/log4j.properties @@ -3,4 +3,4 @@ log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout.ConversionPattern=%p [%c] - %m%n log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -#log4j.logger.org.springframework.osgi=DEBUG \ No newline at end of file +log4j.logger.org.springframework.osgi=TRACE \ No newline at end of file