Announcement Announcement Module
Collapse

Spring Dynamic Modules forum decommissioned in favor of Eclipse Gemini Blueprint

With the official first release of Eclipse Gemini Blueprint shipped, the migration of the Spring Dynamic Modules code base to the Eclipse Foundation, as part of the Gemini project, has been completed.

As such, this forum has been decommissioned in favour of the Eclipse Gemini forums.
See more
See less
Framework-dependent Import-package when developing a bundle Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Framework-dependent Import-package when developing a bundle

    We are developing an application using Spring DM.
    We are creating bundles by maven's maven-bundle-plugin. It is declared to calculate Import-package clause for resulting bundle.

    But many frameworks (especially spring and DM) have very complicated logic which cannot be understood by bnd when it calculates Import-packages.
    So, our import-package in pom.xml looks like this:
    Code:
    <Import-Package>
    org.springframework.transaction.interceptor;resolution:=optional,
        org.springframework.jdbc.support;resolution:=optional,
        org.springframework.jdbc.datasource;resolution:=optional,
        org.springframework.beans.factory.config;resolution:=optional,
        org.springframework.beans.factory.xml;resolution:=optional,
        org.springframework.aop;resolution:=optional,
        org.springframework.aop.framework;resolution:=optional,
        org.springframework.aop.framework.autoproxy;resolution:=optional,
        org.springframework.context.annotation;resolution:=optional,
        org.springframework.dao.support;resolution:=optional,
        org.springframework.core.io.support;resolution:=optional,
        org.springframework.osgi.service.importer.support;resolution:=optional,
        org.springframework.osgi.service.exporter;resolution:=optional,
        org.springframework.osgi.service.exporter.support;resolution:=optional,
        org.springframework.orm.jpa;resolution:=optional,
        org.springframework.orm.jpa.support;resolution:=optional,
        org.springframework.jndi;resolution:=optional,
        org.springframework.util;resolution:=optional,
        org.aopalliance.aop;resolution:=optional,
        org.h2;resolution:=optional,
        javax.jdo.listener;resolution:=optional,
        javax.persistence;resolution:=optional,
        org.apache.xerces.impl.dv.dtd;resolution:=optional,
        org.apache.xerces.impl.dv.xs;resolution:=optional,
        *;resolution:=optional
    </Import-Package>
    All those packages are added one by one, when we debug an application, and see logging like this:
    *** Class 'org.springframework.util.Assert' was not found because bundle 15 does not import 'org.springframework.util' even though bundle 2 does export it. To resolve this issue, add an import for 'org.springframework.util' to bundle 15. ***
    As far as I understand from debugger, this concrete message is caused by spring DM's extender. When our bundle is started, extender (extender's Activator) is trying to verify some a condition by calling smth like:
    Code:
       Assert.isTrue()
    but instead of check extender has got an ClassNotFoundException. I do not know, can extender handle this situation, so I add
    Code:
       org.springframework.util;resolution:=optional,
    And I repeat this procedure until there are no ClassNotFound exceptions in log.

    But the given import is not for my code, like most other imports shown above - it is for framework code, and I can not be sure I've added all required imports - there are other execution paths in framework.
    I think it is not a maintainable approach to add every implicitly-required package to Import-package clause.
    1) I can not document why the concrete line is added.
    2) Those lines can not be refactored by IDE.
    3) There are too many of them.

    So, my question is - Is there a more smart approach for adding "implicitly required" packages?

  • #2
    BTW, we are using Spring DM v 1.1.1 and Spring 2.5.5

    Comment


    • #3
      Hi,

      As far as I understand from debugger, this concrete message is caused by spring DM's extender
      To be correct, this message can be caused by any type of classloading and, by the looks of your log, it's Felix that actually does the logging (other frameworks might just throw the exception).
      Spring and Spring-DM themselves are OSGi bundles out of the box so I'm not sure why you'd like to add imports for them since these are already present. Can you clarify?

      As for the usage of the bnd plugin, I recommend you read their page: http://felix.apache.org/site/maven-b...lugin-bnd.html.
      For cases as you described, using a wildcard should pick up the dependency and add the import - afterall, the reason of using the plugin is to automatically pick up the imports rather then request them.

      Comment


      • #4
        Hi Costin, after your explanations I've downloaded extender's sources and watched the stacktrace.
        The stacktrace is:
        Code:
        java.lang.ClassNotFoundException: *** Class 'org.springframework.util.Assert' was not found because bundle 15 does not import 'org.springframework.util' even though bundle 2 does export it. To resolve this issue, add an import for 'org.springframework.util' to bundle 15. ***
        	at org.apache.felix.framework.searchpolicy.R4SearchPolicyCore.findClass(R4SearchPolicyCore.java:195)
        	at org.apache.felix.framework.searchpolicy.R4SearchPolicy.findClass(R4SearchPolicy.java:45)
        	at org.apache.felix.moduleloader.ModuleImpl.getClass(ModuleImpl.java:152)
        	at org.apache.felix.framework.Felix.loadBundleClass(Felix.java:1476)
        	at org.apache.felix.framework.BundleImpl.loadClass(BundleImpl.java:341)
        	at org.springframework.osgi.extender.internal.activator.SpringTypeCompatibilityChecker.isTypeAvailable(SpringTypeCompatibilityChecker.java:107)
        	at org.springframework.osgi.extender.internal.activator.SpringTypeCompatibilityChecker.checkCompatibility(SpringTypeCompatibilityChecker.java:64)
        	at org.springframework.osgi.extender.internal.activator.ContextLoaderListener.maybeCreateApplicationContextFor(ContextLoaderListener.java:721)
        	at org.springframework.osgi.extender.internal.activator.ContextLoaderListener$ContextBundleListener.handleEvent(ContextLoaderListener.java:230)
        	at org.springframework.osgi.extender.internal.activator.ContextLoaderListener$BaseListener.bundleChanged(ContextLoaderListener.java:173)
        	at org.apache.felix.framework.util.EventDispatcher.invokeBundleListenerCallback(EventDispatcher.java:690)
        So, as far as I understand, this my example above is not a problem, but feature - extender tries to verify if spring classes are available. In this case I can do not react to this warning. There are a lot of such warnings, BTW. I can not guess which is just a feature and which is a problem. I must download sources and trace them. But it is a general Felix problem, I believe :-)

        But I can give other example. If I remove import:
        Code:
          org.springframework.orm.jpa.support;resolution:=optional,
        I get the exception:
        Code:
        org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'filterDao' defined in URL [bundle://17.0:1/org/test/dao/jpox/FilterDaoImpl.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [javax.jdo.PersistenceManagerFactory]: : Cannot find class [org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor] for bean with name 'org.springframework.context.annotation.internalPersistenceAnnotationProcessor' defined in null; nested exception is java.lang.ClassNotFoundException: org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor not found from bundle [org.zekarkass.zekarkass-runtime-ui]; nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor] for bean with name 'org.springframework.context.annotation.internalPersistenceAnnotationProcessor' defined in null; nested exception is java.lang.ClassNotFoundException: org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor not found from bundle [org.zekarkass.zekarkass-runtime-ui]
        	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:591)
        	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:193)
        	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:925)
        	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:835)
        	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:440)
        	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
        	at java.security.AccessController.doPrivileged(Native Method)
        	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
        	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
        	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:221)
        	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
        	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
        	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
        	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:429)
        	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:729)
        	at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext.completeRefresh(AbstractDelegatedExecutionApplicationContext.java:276)
        	at org.springframework.osgi.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor$CompleteRefreshTask.run(DependencyWaiterApplicationContextExecutor.java:145)
        	at java.lang.Thread.run(Thread.java:619)
        Caused by: org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor] for bean with name 'org.springframework.context.annotation.internalPersistenceAnnotationProcessor' defined in null; nested exception is java.lang.ClassNotFoundException: org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor not found from bundle [org.zekarkass.zekarkass-runtime-ui]
        	at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1138)
        	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:524)
        	at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1174)
        	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:222)
        	at org.springframework.beans.factory.BeanFactoryUtils.beanNamesForTypeIncludingAncestors(BeanFactoryUtils.java:187)
        	at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:652)
        	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:610)
        	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:622)
        	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:584)
        	... 17 common frames omitted
        Caused by: java.lang.ClassNotFoundException: org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor not found from bundle [org.zekarkass.zekarkass-runtime-ui]
        	at org.springframework.osgi.util.BundleDelegatingClassLoader.findClass(BundleDelegatingClassLoader.java:103)
        	at org.springframework.osgi.util.BundleDelegatingClassLoader.loadClass(BundleDelegatingClassLoader.java:156)
        	at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
        	at org.springframework.util.ClassUtils.forName(ClassUtils.java:242)
        	at org.springframework.beans.factory.support.AbstractBeanDefinition.resolveBeanClass(AbstractBeanDefinition.java:383)
        	at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1135)
        	... 25 common frames omitted
        Caused by: java.lang.ClassNotFoundException: org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor
        	at org.apache.felix.framework.Felix.loadBundleClass(Felix.java:1479)
        	at org.apache.felix.framework.BundleImpl.loadClass(BundleImpl.java:341)
        	at org.springframework.osgi.util.BundleDelegatingClassLoader.findClass(BundleDelegatingClassLoader.java:99)
        	... 30 common frames omitted
        The problem here is: Spring DM creates an app context, then Spring tries to create a post processor for an app context, but the class is not visible to my bundle. BND plugin can not help here, because is parses only .java classes but do not parse spring's .xml files and do not analyze spring's postprocessors.
        As a result, I must import framework-specific classes by hands.

        Comment


        • #5
          Dmitry, I'm still not sure what you are trying to do... The extender doesn't try to test Spring classes - it really uses them. The Assert for example is used internally throughout the code. Felix throws the warning since probably the extender bundle doesn't start properly - however it should since the distribution as well as the sources have a proper manifest.
          This leads me to believe there is something wrong with your environment.
          As for Spring and the post processor - what post processor are you talking about? In most cases these are already loaded so there is nothing for you to worry.
          However you are right that BND doesn't parse the XML files (I believe there is a plugin for this) so any class defined in there needs to visible (imported) as well in the bundle.

          Comment


          • #6
            Originally posted by Costin Leau View Post
            Dmitry, I'm still not sure what you are trying to do... The extender doesn't try to test Spring classes - it really uses them. The Assert for example is used internally throughout the code. Felix throws the warning since probably the extender bundle doesn't start properly - however it should since the distribution as well as the sources have a proper manifest.
            Talking about extender testing Spring classes I mean class SpringTypeCompatibilityChecker. Lines 61-74
            Code:
            	/** Spring class used for loading */
            	private static Class SPRING_TYPE = Assert.class;
            
            	boolean checkCompatibility(Bundle bundle) {
            		Assert.notNull(bundle);
            
            		Boolean typeComparison = isTypeAvailable(bundle, SPRING_TYPE);
            
            		if (typeComparison != null) {
            			return typeComparison.booleanValue();
            		}
            		// check the imported bundles
            		else {
            			ImportedBundle[] importedBundles = dependencyResolver.getImportedBundles(bundle);
            			return checkImportedBundles(importedBundles);
            		}
            	}
            Here extender ensures Spring classes are visible to our bundle. If our bundle does not import Spring package explicitly, they are not visible. By default we do not Import-package Spring's packages (after all Spring is not invasive, our bundles do not import any spring class), so Felix writes a warning message to the console when lines of code above are executed.
            When I see Felix's warning "ClassNotFound" I prefer to import the package in question, since I do not know, whether the caller is prepared to ClassNotFoundException. But now I see we must revise this conception - not all Felix's warning are really warnings, Spring DM is ready for ClassNotFoundException :-)
            So I agree, this case is not a problem.




            But we can look at another example. We have a bundle with Spring app context. The context contains a DAO. We must add a line to Import-package
            Code:
            <Import-Package>
                 org.springframework.jdbc.support;resolution:=optional,
            </Import-Package>
            If this line is absent, there is a message in the log:
            Code:
              ResourceNotFoundException org/springframework/jdbc/support/sql-error-codes.xml
            WARN  o.s.j.s.SQLErrorCodesFactory - Default sql-error-codes.xml not found (should be included in spring.jar)
            This is because Spring during the creation of database-access classes initializes a SQLErrorCodesFactory class.
            There is a code:
            Code:
            	/**
            	 * The name of default SQL error code files, loading from the class path.
            	 */
            	public static final String SQL_ERROR_CODE_DEFAULT_PATH = "org/springframework/jdbc/support/sql-error-codes.xml";
            
            	/**
            	 * Keep track of a single instance so we can return it to classes that request it.
            	 */
            	private static final SQLErrorCodesFactory instance = new SQLErrorCodesFactory();
            
            	protected SQLErrorCodesFactory() {
            		Map errorCodes = null;
            
            		try {
            			DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
            			XmlBeanDefinitionReader bdr = new XmlBeanDefinitionReader(lbf);
            
            			// Load default SQL error codes.
            			Resource resource = loadResource(SQL_ERROR_CODE_DEFAULT_PATH);
            			if (resource != null && resource.exists()) {
            				bdr.loadBeanDefinitions(resource);
            			}
            			else {
            				logger.warn("Default sql-error-codes.xml not found (should be included in spring.jar)");
            			}
                    .............skip..........
            		}
            		catch (BeansException ex) {
            			logger.warn("Error loading SQL error codes from config file", ex);
            			errorCodes = Collections.EMPTY_MAP;
            		}
            
            		this.errorCodesMap = errorCodes;
            	}
            The warning is caused by line 110:
            Code:
               Resource resource = loadResource(SQL_ERROR_CODE_DEFAULT_PATH);
            As we can see, Spring can not load some defaults. I think this is a real warning and must be corrected. So I correct it by adding framework-specific import to my bundle.
            And this a problem, I suppose.

            Comment


            • #7
              Originally posted by Costin Leau View Post
              As for Spring and the post processor - what post processor are you talking about? In most cases these are already loaded so there is nothing for you to worry.
              I mean Spring's class AnnotationConfigUtils:

              Code:
              	/**
              	 * The bean name of the internally managed JPA annotation processor.
              	 */
              	public static final String PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME =
              			"org.springframework.context.annotation.internalPersistenceAnnotationProcessor";
              	private static final String PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME =
              			"org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor";
              	private static final boolean jpaPresent =
              			ClassUtils.isPresent("javax.persistence.EntityManagerFactory", AnnotationConfigUtils.class.getClassLoader()) &&
              			ClassUtils.isPresent(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader());
              
              
              	public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
              			BeanDefinitionRegistry registry, Object source) {
              ...skip....
              		// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
              		if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
              			RootBeanDefinition def = new RootBeanDefinition();
              			def.setBeanClassName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME);
              			def.setSource(source);
              			def.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
              			beanDefinitions.add(registerBeanPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
              		}
              ...skip.....
              In our case, we use Spring's "<context:annotation-config/>" in bundle's application context, so the AnnotationConfigBeanDefinitionParser is created. And this parser calls (line 46)
              Code:
              		Set<BeanDefinitionHolder> processorDefinitions =
              				AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);
              AnnotationConfigUtils register a post processor for bean factory (see the code above), of class named "org.springframework.orm.jpa.support.PersistenceAn notationBeanPostProcessor". So this class must instantiated during app context init, and if our bundle does not import org.springframework.orm.jpa.support, the bundle can not be loaded.
              Again, our code does not refer to org.springframework.orm.jpa.support package in any way, so I must import it into the pom.xml by hands.

              Comment


              • #8
                Dmitry, you are right about the compatibility check - this is done so if a different version of Spring is used by your bundle then by the extender (rare case but can happen), a proper logging message is given.
                Note that there are cases where the bundle doesn't use Spring (valid case) which is just fine - in this case Felix through a warning but again, this is harmless.
                Regarding loading of resources, that requires an import (non-optional I would say) - however for your particular case this can be improved (see http://jira.springframework.org/browse/SPR-5118).

                Comment


                • #9
                  Thank you Costin.
                  By the way, I think the problem with org.springframework.orm.jpa.support.PersistenceAnn otationBeanPostProcessor is more serious than SQLErrorCodesFactory warning.

                  Comment


                  • #10
                    The processor issue has been raised internally but before addressing it, we have to see whether the changes don't create other issues as well. Currently this forces a, somewhat, implicit import which is acceptable in my opinion.

                    Comment

                    Working...
                    X