Spring Security
  1. Spring Security
  2. SEC-1455

SecurityNamespaceHandler problems in OSGi environment, need to import spring-security-web packages

    Details

    • Type: Defect Defect
    • Status: Closed
    • Priority: Minor Minor
    • Resolution: Fixed
    • Affects Version/s: 3.0.0
    • Fix Version/s: 3.0.3, 3.1.0.M1
    • Component/s: Namespace
    • Labels:
      None
    • Environment:
      DM Server 2.0.1, Spring Framework 3.0.0

      Description

      Imagine the following situation: there is an OSGi (in my case DM Server) environment, which contains some bundles. One of them uses the security namespace to protect some methods. This bundle has nothing to do with the web, this is the responsibility of the web bundle, which contains some web pages and expose some of the services from that bundle. . Within the web bundle, a filter-chain-map is used to protect some URLs.

      The manifest of the first bundle imports some Spring Security related packages, but not org.springframework.security.web, this package is only imported by the web bundle. The SecurityNamespaceHandler seems to be a singleton, and when the org.springframework.security.web.FilterChainProxy is not available on the classpath when the SecurityNamespaceHandler is loaded for the first time (in my case, when the first bundle is started), an exception will be thrown when another bundle is using the security:http and security:filter-chain-map elements.

      Workaround:
      Import the org.springframework.security.web package in all bundles which do something with the security namespace.

      Stacktrace:
      org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: spring-security-web classes are not available. You need these to use <filter-chain-map>
      Offending resource: URL [bundleentry://121.fwk7527493/WEB-INF/applicationContext.xml]
      at org.springframework.beans.factory.parsing.FailFastProblemReporter.fatal(FailFastProblemReporter.java:59)
      at org.springframework.beans.factory.parsing.ReaderContext.fatal(ReaderContext.java:68)
      at org.springframework.beans.factory.parsing.ReaderContext.fatal(ReaderContext.java:55)
      at org.springframework.security.config.SecurityNamespaceHandler.reportMissingWebClasses(SecurityNamespaceHandler.java:91)
      at org.springframework.security.config.SecurityNamespaceHandler.decorate(SecurityNamespaceHandler.java:72)
      at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.decorateIfRequired(BeanDefinitionParserDelegate.java:1372)
      at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.decorateBeanDefinitionIfRequired(BeanDefinitionParserDelegate.java:1359)
      at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.decorateBeanDefinitionIfRequired(BeanDefinitionParserDelegate.java:1339)
      at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.processBeanDefinition(DefaultBeanDefinitionDocumentReader.java:261)
      at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseDefaultElement(DefaultBeanDefinitionDocumentReader.java:154)
      at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:133)
      at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:93)
      at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:493)
      at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:390)
      at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:334)
      at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302)
      at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:143)
      at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:178)
      at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:149)
      at org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext.loadBeanDefinitions(OsgiBundleXmlApplicationContext.java:164)
      at org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext.loadBeanDefinitions(OsgiBundleXmlApplicationContext.java:136)
      at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130)
      at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:458)
      at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext.access$800(AbstractDelegatedExecutionApplicationContext.java:69)
      at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext$3.run(AbstractDelegatedExecutionApplicationContext.java:269)
      at org.springframework.osgi.util.internal.PrivilegedUtils.executeWithCustomTCCL(PrivilegedUtils.java:85)
      at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext.startRefresh(AbstractDelegatedExecutionApplicationContext.java:247)
      at org.springframework.osgi.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor.stageOne(DependencyWaiterApplicationContextExecutor.java:214)
      at org.springframework.osgi.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor.refresh(DependencyWaiterApplicationContextExecutor.java:169)
      at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext.refresh(AbstractDelegatedExecutionApplicationContext.java:175)
      at org.springframework.osgi.extender.internal.activator.ContextLoaderListener$2.run(ContextLoaderListener.java:716)
      at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:48)
      at org.springframework.osgi.extender.internal.activator.ContextLoaderListener.maybeCreateApplicationContextFor(ContextLoaderListener.java:781)
      at org.springframework.osgi.extender.internal.activator.ContextLoaderListener$ContextBundleListener.handleEvent(ContextLoaderListener.java:229)
      at org.springframework.osgi.extender.internal.activator.ContextLoaderListener$BaseListener.bundleChanged(ContextLoaderListener.java:172)
      at org.eclipse.osgi.framework.internal.core.BundleContextImpl.dispatchEvent(BundleContextImpl.java:919)
      at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:227)
      at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:149)
      at org.eclipse.osgi.framework.internal.core.Framework.publishBundleEventPrivileged(Framework.java:1350)
      at org.eclipse.osgi.framework.internal.core.Framework.publishBundleEvent(Framework.java:1301)
      at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:362)
      at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:280)
      at com.springsource.kernel.core.internal.StandardBundleStarter.start(StandardBundleStarter.java:68)
      at com.springsource.kernel.core.internal.StandardBundleStarter.start(StandardBundleStarter.java:56)
      at sun.reflect.GeneratedMethodAccessor72.invoke(Unknown Source)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:597)
      at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307)
      at org.springframework.osgi.service.importer.support.internal.aop.ServiceInvoker.doInvoke(ServiceInvoker.java:58)
      at org.springframework.osgi.service.importer.support.internal.aop.ServiceInvoker.invoke(ServiceInvoker.java:62)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
      at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:131)
      at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:119)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
      at org.springframework.osgi.service.importer.support.LocalBundleContextAdvice.invoke(LocalBundleContextAdvice.java:59)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
      at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:131)
      at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:119)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
      at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
      at $Proxy60.start(Unknown Source)
      at com.springsource.kernel.install.artifact.internal.bundle.StandardBundleDriver.startBundle(StandardBundleDriver.java:177)
      at com.springsource.kernel.install.artifact.internal.bundle.StandardBundleDriver.start(StandardBundleDriver.java:158)
      at com.springsource.kernel.install.artifact.internal.bundle.StandardBundleInstallArtifact.doStart(StandardBundleInstallArtifact.java:262)
      at com.springsource.kernel.install.artifact.internal.AbstractInstallArtifact.driveDoStart(AbstractInstallArtifact.java:211)
      at com.springsource.kernel.install.artifact.internal.bundle.StandardBundleInstallArtifact.start(StandardBundleInstallArtifact.java:251)
      at com.springsource.kernel.deployer.core.internal.PipelinedApplicationDeployer.start(PipelinedApplicationDeployer.java:291)
      at com.springsource.kernel.deployer.core.internal.PipelinedApplicationDeployer.deploy(PipelinedApplicationDeployer.java:201)
      at com.springsource.kernel.deployer.management.StandardDeployer.deploy(StandardDeployer.java:62)
      at sun.reflect.GeneratedMethodAccessor88.invoke(Unknown Source)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:597)
      at com.sun.jmx.mbeanserver.ConvertingMethod.invokeWithOpenReturn(ConvertingMethod.java:167)
      at com.sun.jmx.mbeanserver.MXBeanIntrospector.invokeM2(MXBeanIntrospector.java:96)
      at com.sun.jmx.mbeanserver.MXBeanIntrospector.invokeM2(MXBeanIntrospector.java:33)
      at com.sun.jmx.mbeanserver.MBeanIntrospector.invokeM(MBeanIntrospector.java:208)
      at com.sun.jmx.mbeanserver.PerInterface.invoke(PerInterface.java:120)
      at com.sun.jmx.mbeanserver.MBeanSupport.invoke(MBeanSupport.java:262)
      at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:836)
      at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:761)
      at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1426)
      at javax.management.remote.rmi.RMIConnectionImpl.access$200(RMIConnectionImpl.java:72)
      at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1264)
      at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1359)
      at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:788)
      at sun.reflect.GeneratedMethodAccessor87.invoke(Unknown Source)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:597)
      at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:305)
      at sun.rmi.transport.Transport$1.run(Transport.java:159)
      at java.security.AccessController.doPrivileged(Native Method)
      at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
      at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535)
      at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
      at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
      at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
      at java.lang.Thread.run(Thread.java:619)

        Activity

        Hide
        Luke Taylor added a comment -

        Could you clarify the version you are using, please. You say SFW 3.0.0. Do you mean Spring Security? If so, could you check with the latest version? If there are still could you possibly supply a test app which reproduces the issue?

        Show
        Luke Taylor added a comment - Could you clarify the version you are using, please. You say SFW 3.0.0. Do you mean Spring Security? If so, could you check with the latest version? If there are still could you possibly supply a test app which reproduces the issue?
        Hide
        Daniël van 't Ooster added a comment -

        Hi Luke,

        see the attached jars or eclipse projects.

        They are tested on dm server 2.0.1 with spring security 3.0.0. First start bundle1 which will create an instance of the security namespace handler. After that, start bundle2, it will give the problem as described in the case. Imho the problematic code is in the SecurityNamespaceHandler:

        // Web-namespace stuff
        if (ClassUtils.isPresent("org.springframework.security.web.FilterChainProxy", ClassUtils.getDefaultClassLoader()))

        { parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser()); parsers.put(Elements.FILTER_INVOCATION_DEFINITION_SOURCE, new FilterInvocationSecurityMetadataSourceParser()); parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser()); filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator(); //registerBeanDefinitionDecorator(Elements.FILTER_CHAIN_MAP, new FilterChainMapBeanDefinitionDecorator()); }

        }

        Because this is a singleton and the org.springframework.security.web class it not imported in bundle1 (imaging bundle1 has nothing to do with the web), the handler cannot handle the http related elements.

        Grtz,
        Daniël

        Show
        Daniël van 't Ooster added a comment - Hi Luke, see the attached jars or eclipse projects. They are tested on dm server 2.0.1 with spring security 3.0.0. First start bundle1 which will create an instance of the security namespace handler. After that, start bundle2, it will give the problem as described in the case. Imho the problematic code is in the SecurityNamespaceHandler: // Web-namespace stuff if (ClassUtils.isPresent("org.springframework.security.web.FilterChainProxy", ClassUtils.getDefaultClassLoader())) { parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser()); parsers.put(Elements.FILTER_INVOCATION_DEFINITION_SOURCE, new FilterInvocationSecurityMetadataSourceParser()); parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser()); filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator(); //registerBeanDefinitionDecorator(Elements.FILTER_CHAIN_MAP, new FilterChainMapBeanDefinitionDecorator()); } } Because this is a singleton and the org.springframework.security.web class it not imported in bundle1 (imaging bundle1 has nothing to do with the web), the handler cannot handle the http related elements. Grtz, Daniël
        Hide
        Christopher Frost added a comment -

        Daniel,

        Luke has asked me to look at this as I work on dm Server and will have a little more insight from that point of view, I haven't managed to get to it today but I understand the issue. I need to recreate it and have a look myself, will do so next week.
        I am tracking this from my own project with https://issuetracker.springsource.com/browse/DMS-2551.

        Chris

        Show
        Christopher Frost added a comment - Daniel, Luke has asked me to look at this as I work on dm Server and will have a little more insight from that point of view, I haven't managed to get to it today but I understand the issue. I need to recreate it and have a look myself, will do so next week. I am tracking this from my own project with https://issuetracker.springsource.com/browse/DMS-2551 . Chris
        Hide
        Christopher Frost added a comment -

        Hi,

        I've looked in to this and tried deploying you test bundles with different headers. Your diagnoses is correct, because the SecurityNamespaceHandler is a singleton and the way it loads the web stuff if you want to use the web elements anywhere in an instance of the dm Server then the first bundle loaded that uses security must import the security web bundle, bundles after that don't matter. This should not impact what you need to have deployed as the security.web bundle is required by the security.config bundle anyway. This is far from ideal though, having to have this unintuitive import but from OSGi's point of view it is working as intended. This isn't even a dm Server issue, it's fundamental OSGi. A better solution to get around the singleton issue would be to make the code in the security.config bundle a little smarter about OSGi, as it imports the security.web bundle anyway I would want this 'if statement' to always return true in an OSGi environment, i.e. be aware of the OSGi classloader system.

        Luke, please ping me if you want to discuss...

        Chris.

        P.S. Thanks for the well cut-down test bundles, made recreating it a breeze.

        Show
        Christopher Frost added a comment - Hi, I've looked in to this and tried deploying you test bundles with different headers. Your diagnoses is correct, because the SecurityNamespaceHandler is a singleton and the way it loads the web stuff if you want to use the web elements anywhere in an instance of the dm Server then the first bundle loaded that uses security must import the security web bundle, bundles after that don't matter. This should not impact what you need to have deployed as the security.web bundle is required by the security.config bundle anyway. This is far from ideal though, having to have this unintuitive import but from OSGi's point of view it is working as intended. This isn't even a dm Server issue, it's fundamental OSGi. A better solution to get around the singleton issue would be to make the code in the security.config bundle a little smarter about OSGi, as it imports the security.web bundle anyway I would want this 'if statement' to always return true in an OSGi environment, i.e. be aware of the OSGi classloader system. Luke, please ping me if you want to discuss... Chris. P.S. Thanks for the well cut-down test bundles, made recreating it a breeze.
        Hide
        Luke Taylor added a comment -

        Apparently dmServer has some specialized internal behaviour related to classloading and namespace-handling (bundles which contain spring.schemas files), so this may account for why the web classes are not visible, even though they are declared in the config bundle. I have altered the code to attempt to load the parsers when they are required, rather than just when the init() method is called (which presumably only happens the first time the class is registered). This appears to solve the problem, though the second test bundle fails to deploy because it doesn't import the package that contains BasicAuthenticationFilter. Adding the import solves that problem too.

        Show
        Luke Taylor added a comment - Apparently dmServer has some specialized internal behaviour related to classloading and namespace-handling (bundles which contain spring.schemas files), so this may account for why the web classes are not visible, even though they are declared in the config bundle. I have altered the code to attempt to load the parsers when they are required, rather than just when the init() method is called (which presumably only happens the first time the class is registered). This appears to solve the problem, though the second test bundle fails to deploy because it doesn't import the package that contains BasicAuthenticationFilter. Adding the import solves that problem too.
        Hide
        Luke Taylor added a comment -

        Resolving, as the above change seems to fix the problem.

        Show
        Luke Taylor added a comment - Resolving, as the above change seems to fix the problem.

          People

          • Assignee:
            Luke Taylor
            Reporter:
            Daniël van 't Ooster
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: