-
1. Re: AS7/JNDI integration
thomas.diesler Jul 31, 2012 5:00 AM (in response to 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 Sep 4, 2012 7:48 AM (in response to thomas.diesler)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.