2 Replies Latest reply on Sep 4, 2012 7:48 AM by bosschaert

    AS7/JNDI integration

    thomas.diesler

      I took another look at the existing JNDI integration and was wondering how this is supposed to work.

       

      The NamingSubsystemOSGiService registers two services which essentially return a single instance of the respective type.

       

      InitialContext.class.newInstance()
      InitialContextFactoryBuilder.class.newInstance()
      

       

      According to the spec the InitialContext should not be registered as a service - I guess this is historic legacy.

       

      The second service registers an instance of org.jboss.as.naming.InitialContextFactoryBuilder

      which does


      public javax.naming.spi.InitialContextFactory createInitialContextFactory(Hashtable<?, ?> environment) throws NamingException {
              if (environment == null)
                  environment = new Hashtable<String, Object>();
      
              final String factoryClassName = (String)environment.get(Context.INITIAL_CONTEXT_FACTORY);
              if(factoryClassName == null || InitialContextFactory.class.getName().equals(factoryClassName)) {
                  return DEFAULT_FACTORY;
              }
              final ClassLoader classLoader = getContextClassLoader();
              try {
                  final Class<?> factoryClass = Class.forName(factoryClassName, true, classLoader);
                  return (javax.naming.spi.InitialContextFactory)factoryClass.newInstance();
              } catch (Exception e) {
                  throw MESSAGES.failedToInstantiate("InitialContextFactory", factoryClassName, classLoader);
              }
      }
      

       

      Because of the TCCL use, this does not seem right either.

       

      I'm further concerned about the NamingManager singletons that are being set by AS7, which means that the OSGi JNDI Implementation (e.g. Aries JNDI) cannot set them. What are the implications of this?

       

      Lets have a conversation on how this is supposed work.

        • 1. Re: AS7/JNDI integration
          thomas.diesler

          I extended our JNDI test coverage to get a deeper understanding of were we stand WRT our Enterprise JNDI integration.

           

          Here the use cases

           

          #1 Get the InitialContext from the JNDIContextManager and access the PackageAdmin service

           

          // Get the InitialContext via {@link JNDIContextManager}
          JNDIContextManager contextManager = NamingSupport.provideJNDIIntegration(context, bundle);
          Context initialContext = contextManager.newInitialContext();
          
          // Lookup the PackageAdmin OSGi service through JNDI
          PackageAdmin pa = (PackageAdmin) initialContext.lookup("osgi:service/" + PackageAdmin.class.getName());
          
          // Make an invocation on PackageAdmin
          ExportedPackage ep = pa.getExportedPackage(JNDITestService.class.getPackage().getName());
          Assert.assertEquals(bundle, ep.getExportingBundle());
          

           

          #2 Get the InitialContext from the JNDIContextManager and access a simple service both by FQN and alias

           

          // Lookup the {@link JNDITestService} service
          JNDITestService service = (JNDITestService) initialContext.lookup("osgi:service/" + JNDITestService.class.getName());
          Assert.assertEquals("jndi-value", service.getValue());
          
          // Lookup the {@link JNDITestService} service by name
          service = (JNDITestService) initialContext.lookup("osgi:service/foo");
          Assert.assertEquals("jndi-value", service.getValue());
          

           

          #3 Get the InitialContext from the JNDIContextManager and obtain the owner Bundle for that context

           

          // Get the context of the owner bundle
          BundleContext context = (BundleContext) initialContext.lookup("osgi:framework/bundleContext");
          Assert.assertEquals(bundle.getBundleContext(), context);
          

           

          #4 Register a InitialContextFactory provider service and bind a value

           

          // Get the InitialContext via {@link JNDIContextManager}
          JNDIContextManager contextManager = NamingSupport.provideJNDIIntegration(context, bundle);
          Hashtable<String, String> env = new Hashtable<String, String>();
          env.put(Context.INITIAL_CONTEXT_FACTORY, SimpleInitalContextFactory.class.getName());
          Context initialContext = contextManager.newInitialContext(env);
          
          // Bind a some value under some key
          initialContext.bind("test/foo", "bar");
          try {
              // Lookup the value
              Assert.assertEquals("bar", initialContext.lookup("test/foo"));
          } finally {
              initialContext.unbind("test/foo");
          }
          

           

          #5 Do the same as #4 with a Reference

           

          // Bind a some value reference under some key
          Reference ref = new StringReference("bar");
          initialContext.bind("test/foo", ref);
          try {
              // Lookup the value
              Assert.assertEquals("bar", initialContext.lookup("test/foo"));
          } finally {
              initialContext.unbind("test/foo");
          }
          

           

          #6 Access a simple service from another bundle that has a wire to the service's package

           

          // Access the service directly
          BundleContext contextB = bundleB.getBundleContext();
          ServiceReference sref = contextB.getServiceReference(JNDITestService.class.getName());
          JNDITestService service = (JNDITestService) contextB.getService(sref);
          Assert.assertEquals("jndi-value", service.getValue());
          
          // Get the InitialContext via {@link JNDIContextManager} for bundleB
          JNDIContextManager contextManager = NamingSupport.provideJNDIIntegration(context, bundleB);
          Context initialContext = contextManager.newInitialContext();
          
          // Lookup the {@link JNDITestService} service
          service = (JNDITestService) initialContext.lookup("osgi:service/" + JNDITestService.class.getName());
          Assert.assertEquals("jndi-value", service.getValue());
          
          // Lookup the {@link JNDITestService} service by name
          service = (JNDITestService) initialContext.lookup("osgi:service/foo");
          Assert.assertEquals("jndi-value", service.getValue());
          

           

          #7 Do the same as #6 for a bundle that does not have a wire

           

          // Access the service directly
          BundleContext contextC = bundleC.getBundleContext();
          ServiceReference sref = contextC.getServiceReference(JNDITestService.class.getName());
          Assert.assertNull("ServiceReference is null", sref);
          
          // Get the InitialContext via {@link JNDIContextManager} for bundleB
          JNDIContextManager contextManager = NamingSupport.provideJNDIIntegration(context, bundleC);
          Context initialContext = contextManager.newInitialContext();
          
          // Lookup the {@link JNDITestService} service
          try {
               initialContext.lookup("osgi:service/" + JNDITestService.class.getName());
               Assert.fail("NameNotFoundException expected");
          } catch (NameNotFoundException ex) {
               //expected
          }
          
          // Lookup the {@link JNDITestService} service by name
          try {
              initialContext.lookup("osgi:service/foo");
              Assert.fail("NameNotFoundException expected");
          } catch (NameNotFoundException ex) {
              //expected
          }
          

           

          #8-14 do all of the above using the tradinial JNDI API (i.e. new InitialContext())

           

          Everything was tested against aries-jndi-0.3.1.

          Embedded scenario

           

          #3 Get the InitialContext from the JNDIContextManager and obtain the owner Bundle for that context - fails also using the tradinial API.

           

          #7 Access a simple service from another bundle that has no wire to the service's package - fails with IllegalArgumentException also using the tradinial API.

           

          Everything else seems to be good.

          Integration scenario with AS7

           

          As expected because of the missing integration with the NamingManger singletons, all tests that go through NamingManager to obtain the InitialContextFactory or the ObjectFactory fail.

           

          Running org.jboss.test.osgi.example.jndi.JNDITestCase
          Tests run: 12, Failures: 1, Errors: 2, Skipped: 2, Time elapsed: 15.574 sec <<< FAILURE!
          
          Results :
          
          Failed tests:   testContextManagerReferenceBinding(org.jboss.test.osgi.example.jndi.JNDITestCase): expected:<bar> but was:<Reference Class Name: java.lang.String(..)
          
          Tests in error: 
            testTraditionalAPIValueBinding(org.jboss.test.osgi.example.jndi.JNDITestCase): JBAS011843: Failed instantiate InitialContextFactory org.jboss.test.osgi.example.jndi.bundle.JNDITestActivator$SimpleInitalContextFactory from classloader ModuleClassLoader for Module "deployment.arquillian-service:main" from Service Module Loader
            testTraditionalAPIReferenceBinding(org.jboss.test.osgi.example.jndi.JNDITestCase): JBAS011843: Failed instantiate InitialContextFactory org.jboss.test.osgi.example.jndi.bundle.JNDITestActivator$SimpleInitalContextFactory from classloader ModuleClassLoader for Module "deployment.arquillian-service:main" from Service Module Loader
          

           

          In reality it means that OSGi Bundles cannot use JNDI with object References and cannot expect the traditional JNDI API to work. These findings generally raise the question how a spec compliant OSGI JNDI Implementation is supposed to work in a container environment. Specifically, in an environment that has already set the NamingManager singletons.

           

          IMHO there is a layer of abstraction missing. Perhaps the container should register a NamingManger service that the JNDI Implementation can use to register its InitialContextFactoryBuilder and ObjectFactoryBuilder.

           

          Tracked by

           

          [AS7-5271] OSGI JNDI Implementation does not integrate with NamingManager

          • 2. Re: AS7/JNDI integration
            bosschaert

            Hi Thomas,

             

            Thomas Diesler wrote:

             

            The NamingSubsystemOSGiService registers two services which essentially return a single instance of the respective type.

             

            InitialContext.class.newInstance()
            InitialContextFactoryBuilder.class.newInstance()

             

            According to the spec the InitialContext should not be registered as a service - I guess this is historic legacy.

             

            Yes, we were registering the InitialContext before but it's outside of the spec. I think you have removed that since...

             

            Thomas Diesler wrote:

             

            The second service registers an instance of org.jboss.as.naming.InitialContextFactoryBuilder

            which does


            public javax.naming.spi.InitialContextFactory createInitialContextFactory(Hashtable<?, ?> environment) throws NamingException {
                    if (environment == null)
                        environment = new Hashtable<String, Object>();

                    final String factoryClassName = (String)environment.get(Context.INITIAL_CONTEXT_FACTORY);
                    if(factoryClassName == null || InitialContextFactory.class.getName().equals(factoryClassName)) {
                        return DEFAULT_FACTORY;
                    }
                    final ClassLoader classLoader = getContextClassLoader();
                    try {
                        final Class<?> factoryClass = Class.forName(factoryClassName, true, classLoader);
                        return (javax.naming.spi.InitialContextFactory)factoryClass.newInstance();
                    } catch (Exception e) {
                        throw MESSAGES.failedToInstantiate("InitialContextFactory", factoryClassName, classLoader);
                    }
            }

             

             

             

            Because of the TCCL use, this does not seem right either.

             

            Aries/JNDI uses the InitialContextFactoryBuilder OSGi service to create InitialContextFactory objects. OSGi/JNDI integration spec allows this. The TCCL piece only gets into play when you specify an alternative INITIAL_CONTEXT_FACTORY classname, I haven't seen a use-case for that yet...

            Otherwise the DEFAULT_FACTORY is simply returned.

             

             

            I'm further concerned about the NamingManager singletons that are being set by AS7, which means that the OSGi JNDI Implementation (e.g. Aries JNDI) cannot set them. What are the implications of this?

             

             

            Aries cannot set them, but Aries integrates with the JBoss Naming manager through javax.naming.spi.ObjectFactory services. The

            org.jboss.as.osgi.naming.JNDIServiceListener listens for these and registers them with the AS7 naming subsystem. So the fact that the NamingManager singletons cannot be set is handled through this integration layer.

             

            #3 Get the InitialContext from the JNDIContextManager and obtain the owner Bundle for that context - fails also using the tradinial API.

            I think the fact that osgi:framework/bundleContext doesn't work is a bug in Aries. I've filed ARIES-916 for that.

             

            As expected because of the missing integration with the NamingManger singletons, all tests that go through NamingManager to obtain the InitialContextFactory or the ObjectFactory fail.

             

             

            Not really, if you look at the following test, you'll see that it passes. It uses the Traditional API (new InitialContext()):

             

            {code}public void testTraditionalAPI() throws Exception {

             

                    // Make sure we have a valid BundleContext

                    bundle.start();

             

                    // Get the InitialContext via API

                    Context initialContext = new InitialContext();

             

                    // Lookup the PackageAdmin OSGi service through JNDI

                    PackageAdmin pa = (PackageAdmin) initialContext.lookup("osgi:service/" + PackageAdmin.class.getName());

             

                    // Make an invocation on PackageAdmin

                    ExportedPackage ep = pa.getExportedPackage(JNDITestService.class.getPackage().getName());

                    Assert.assertEquals(bundle, ep.getExportingBundle());

             

                    // Lookup the {@link JNDITestService} service

                    JNDITestService service = (JNDITestService) initialContext.lookup("osgi:service/" + JNDITestService.class.getName());

                    Assert.assertEquals("jndi-value", service.getValue());

             

                    // Lookup the {@link JNDITestService} service by name

                    service = (JNDITestService) initialContext.lookup("osgi:service/foo");

                    Assert.assertEquals("jndi-value", service.getValue());

                }{code}

             

             

            From what I can see now, the main two cases that fail are:

            • lookup of osgi:framework/BundleContext because of a bug in Aries
            • the tests that register an arbitrary value or reference in JNDI fail because the default naming context in AS7 is readonly

             

            Not sure why your #7 above fails. That needs some further investigation.