/* * Copyright 2004 Original mockejb authors. * Copyright 2007 Nuxeo SAS. * * This file is derived from mockejb-0.6-beta2 * * 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.nuxeo.common.jndi; import java.util.ArrayList; import java.util.Collection; import java.util.Hashtable; import java.util.List; import javax.naming.Binding; import javax.naming.Context; import javax.naming.ContextNotEmptyException; import javax.naming.InitialContext; import javax.naming.Name; import javax.naming.NameAlreadyBoundException; import javax.naming.NameNotFoundException; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.NoPermissionException; import javax.naming.Reference; import javax.naming.spi.ObjectFactory; import org.nuxeo.common.jndi.NamingContext.NamingContextNameParser; import junit.framework.TestCase; /** * Tests all basic methods of NamingContext. * Test for remote context lookup is not included. * * @author Dimitar Gospodinov */ public class TestNamingContext extends TestCase { private Context initialCtx; private Context compCtx; private Context envCtx; private Context ejbCtx; @Override protected void setUp() throws Exception { NamingContextFactory.setAsInitial(); // Empty initial Context initialCtx = new InitialContext(); initialCtx.bind("java:comp/env/ejb/Dummy", null); compCtx = (Context) initialCtx.lookup("java:comp"); envCtx = (Context) compCtx.lookup("env"); ejbCtx = (Context) envCtx.lookup("ejb"); } @Override protected void tearDown() throws Exception { clearContext(initialCtx); ejbCtx = null; envCtx = null; compCtx = null; initialCtx = null; NamingContextFactory.revertSetAsInitial(); } /** * Removes all entries from the specified context, including subcontexts. * * @param context the context to clear */ private static void clearContext(Context context) throws NamingException { for (NamingEnumeration<Binding> e = context.listBindings(""); e.hasMoreElements(); ) { Binding binding = e.nextElement(); if (binding.getObject() instanceof Context) { clearContext((Context) binding.getObject()); } context.unbind(binding.getName()); } } /** * Tests inability to create duplicate subcontexts. * * @throws NamingException */ public void testSubcontextCreationOfDuplicates() throws NamingException { // Try to create duplicate subcontext try { initialCtx.createSubcontext("java:comp"); fail(); } catch (NameAlreadyBoundException ex) { } // Try to create duplicate subcontext using multi-component name try { compCtx.createSubcontext("env/ejb"); fail(); } catch (NameAlreadyBoundException ex) { } } /** * Tests inability to destroy non-empty subcontexts. * * @throws NamingException */ public void testSubcontextNonEmptyDestruction() throws NamingException { // Bind some object in ejb subcontext ejbCtx.bind("EmptyTest", "EmptyTest Object"); // Attempt to destroy any subcontext try { initialCtx.destroySubcontext("java:comp"); fail(); } catch (ContextNotEmptyException ex) { } try { initialCtx.destroySubcontext("java:comp/env/ejb"); fail(); } catch (ContextNotEmptyException ex) { } try { envCtx.destroySubcontext("ejb"); fail(); } catch (ContextNotEmptyException ex) { } } /** * Tests ability to destroy empty subcontexts. * * @throws NamingException */ public void testSubcontextDestruction() throws NamingException { // Create three new subcontexts ejbCtx.createSubcontext("sub1"); ejbCtx.createSubcontext("sub2"); envCtx.createSubcontext("sub3"); // Destroy initialCtx.destroySubcontext("java:comp/env/ejb/sub1"); ejbCtx.destroySubcontext("sub2"); envCtx.destroySubcontext("sub3"); // Perform lookup try { ejbCtx.lookup("sub1"); fail(); } catch (NameNotFoundException ex) { } try { envCtx.lookup("ejb/sub2"); fail(); } catch (NameNotFoundException ex) { } try { initialCtx.lookup("java:comp/sub3"); fail(); } catch (NameNotFoundException ex) { } } /** * Tests inability to invoke methods on destroyed subcontexts. * * @throws NamingException */ public void testSubcontextInvokingMethodsOnDestroyedContext() throws NamingException { //Create subcontext and destroy it. Context sub = ejbCtx.createSubcontext("subXX"); initialCtx.destroySubcontext("java:comp/env/ejb/subXX"); /* * At this point sub is destroyed. Any method invocation should fail. * Try to bind some object, create subcontext and perform list. * Because the Context was empty, and non-empty Context can not be * destroyed, a lookup will fail and in general may not be tested. * We will test it for completeness. The same applies for unbind and destroyContext methods. */ try { sub.bind("SomeName", "SomeObject"); fail(); } catch (NoPermissionException ex) { } try { sub.unbind("SomeName"); fail(); } catch (NoPermissionException ex) { } try { sub.createSubcontext("SomeSubcontext"); fail(); } catch (NoPermissionException ex) { } try { sub.destroySubcontext("DummyName"); fail(); } catch (NoPermissionException ex) { } try { sub.list(""); fail(); } catch (NoPermissionException ex) { } try { sub.lookup("DummyName"); fail(); } catch (NoPermissionException ex) { } try { sub.composeName("name", "prefix"); fail(); } catch (NoPermissionException ex) { } try { NamingContextNameParser parser = new NamingContextNameParser(); sub.composeName(parser.parse("a"), parser.parse("b")); fail(); } catch (NoPermissionException ex) { } } /** * Tests ability to bind name to object and inability to bind * duplicate names. * TODO Duplicate names can not be tested at this time because * we treat bind as re-bind. * * @throws NamingException */ public void testBindLookup() throws NamingException { /* * Add four binding - two for null reference and two for an object, * using atomic and compound names. */ Object o1 = "Test object for atomic binding"; Object o2 = "Test object for compound binding"; Object o3 = "Test object for complex compound binding"; ejbCtx.bind("AtomicNull", null); ejbCtx.bind("AtomicObject", o1); initialCtx.bind("java:comp/env/CompoundNull", null); initialCtx.bind("java:comp/env/CompoundObject", o2); // Bind to subcontexts that do not exist initialCtx.bind("java:comp/env/ejb/subToCreate1/subToCreate2/oo", o3); // Try to lookup assertNull(ejbCtx.lookup("AtomicNull")); assertSame(ejbCtx.lookup("AtomicObject"), o1); assertNull(compCtx.lookup("env/CompoundNull")); assertSame(initialCtx.lookup("java:comp/env/CompoundObject"), o2); assertSame(ejbCtx.lookup("subToCreate1/subToCreate2/oo"), o3); } /** * Tests ability to unbind names. * * @throws NamingException */ public void testUnbind() throws NamingException { envCtx.bind("testUnbindName1", null); compCtx.bind("env/ejb/testUnbindName2", "Test unbind object"); // Unbind initialCtx.unbind("java:comp/env/testUnbindName1"); ejbCtx.unbind("testUnbindName2"); try { envCtx.lookup("testUnbindName1"); fail(); } catch (NameNotFoundException ex) { } try { initialCtx.lookup("java:comp/env/ejb/testUnbindName2"); fail(); } catch (NameNotFoundException ex) { } // Unbind non-existing name try { ejbCtx.unbind("This name does not exist in the context"); } catch (Exception ex) { fail(); } // Unbind non-existing name, when subcontext does not exists try { compCtx.unbind("env/ejb/ejb1/somename"); fail(); } catch (NameNotFoundException ex) { } } /** * Tests ability to list bindings for a context - specified by * name through object reference. * * @throws NamingException */ public void testListBindings() throws NamingException { // Add three bindings Object o1 = "Test list bindings 1"; Object o2 = "Test list bindings 2"; compCtx.bind("env/ejb/testListBindings1", o1); envCtx.bind("testListBindings2", o2); ejbCtx.bind("testListBindings3", null); // Verify bindings for context specified by reference verifyListBindingsResult(envCtx, "", o1, o2); // Verify bindings for context specified by name verifyListBindingsResult(initialCtx, "java:comp/env", o1, o2); } private void verifyListBindingsResult(Context c, String name, Object o1, Object o2) throws NamingException { boolean ejbFoundFlg = false; boolean o2FoundFlg = false; boolean ejbO1FoundFlg = false; boolean ejbNullFoundFlg = false; // List bindings for the specified context for (NamingEnumeration<Binding> en = c.listBindings(name); en.hasMore();) { Binding b = en.next(); if (b.getName().equals("ejb")) { assertEquals(b.getObject(), ejbCtx); ejbFoundFlg = true; Context nextCon = (Context) b.getObject(); for (NamingEnumeration<Binding> en1 = nextCon.listBindings(""); en1.hasMore();) { Binding b1 = en1.next(); if (b1.getName().equals("testListBindings1")) { assertEquals(b1.getObject(), o1); ejbO1FoundFlg = true; } else if (b1.getName().equals("testListBindings3")) { assertNull(b1.getObject()); ejbNullFoundFlg = true; } } } else if (b.getName().equals("testListBindings2")) { assertEquals(b.getObject(), o2); o2FoundFlg = true; } } if (!(ejbFoundFlg && o2FoundFlg && ejbO1FoundFlg && ejbNullFoundFlg)) { fail(); } } public void testCompositeNameWithLeadingTrailingEmptyComponents() throws NamingException { NamingContext c = new NamingContext(null); Object o = new Object(); c.rebind("/a/b/c/", o); assertEquals(c.lookup("a/b/c"), o); assertEquals(c.lookup("///a/b/c///"), o); } public void testLookup() throws NamingException { NamingContext namingCtx = new NamingContext(null); Object obj = new Object(); namingCtx.rebind("a/b/c/d", obj); assertEquals(obj, namingCtx.lookup("a/b/c/d")); namingCtx.bind("a", obj); assertEquals(obj, namingCtx.lookup("a")); } public void testGetCompositeName() throws NamingException { NamingContext namingCtx = new NamingContext(null); namingCtx.rebind("a/b/c/d", new Object()); NamingContext subCtx; subCtx = (NamingContext) namingCtx.lookup("a"); assertEquals("a", subCtx.getCompoundStringName()); subCtx = (NamingContext) namingCtx.lookup("a/b/c"); assertEquals("a/b/c", subCtx.getCompoundStringName()); } /** * Tests that delegate context is * invoked when NamingContext does not find the name. */ public void testDelegateContext() throws NamingException { List<String> recordedLookups = new ArrayList<String>(); Context ctx = new NamingContext(new RecordingNamingContext(recordedLookups)); // Test simple name String wrongName = "mockejb"; ctx.lookup(wrongName); assertEquals(1, recordedLookups.size()); assertEquals(wrongName, recordedLookups.get(0)); // Test composite name recordedLookups.clear(); wrongName = "mockejb/a"; ctx.lookup(wrongName); assertEquals(1, recordedLookups.size()); assertEquals(wrongName, recordedLookups.get(0)); // Test the situation when root context is bound already in NamingContext recordedLookups.clear(); ctx.rebind("mockejb/dummy", new Object()); wrongName = "mockejb/a"; ctx.lookup(wrongName); assertEquals(1, recordedLookups.size()); assertEquals(wrongName, recordedLookups.get(0)); } /** * Tests substitution of '.' with '/' when parsing string names. * * @throws NamingException */ public void testTwoSeparatorNames() throws NamingException { NamingContext ctx = new NamingContext(null); Object obj = new Object(); ctx.bind("a/b.c.d/e", obj); assertEquals(ctx.lookup("a/b/c/d/e"), obj); assertEquals(ctx.lookup("a.b/c.d.e"), obj); assertTrue(ctx.lookup("a.b.c.d") instanceof Context); } public void testResourceFactory() throws NamingException { Reference ref = new Reference(String.class.getName(), StringFactory.class.getName(), null); initialCtx.bind("java:comp/env/astring", ref); assertEquals("someString", initialCtx.lookup("java:comp/env/astring")); } /** * Class to simulate remote context. * Always returns the dummy object from lookup * and stores the lookup call info. */ static class RecordingNamingContext extends NamingContext { private final Collection<String> recordedLookups; RecordingNamingContext(Collection<String> recordedNames) { super(null); recordedLookups = recordedNames; } @Override public Object lookup(Name name) throws NamingException { recordedLookups.add(name.toString()); return new Object(); } @Override public Object lookup(String name) throws NamingException { recordedLookups.add(name); return new Object(); } } public static class StringFactory implements ObjectFactory { public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> env) throws Exception { return "someString"; } } }