/* * Copyright 2002-2008 the original author or authors. * * 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 com.revolsys.jndi; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import javax.naming.Binding; import javax.naming.Context; import javax.naming.Name; import javax.naming.NameClassPair; import javax.naming.NameNotFoundException; import javax.naming.NameParser; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.OperationNotSupportedException; import org.springframework.util.StringUtils; import com.revolsys.logging.Logs; /** * Simple implementation of a JNDI naming context. Only supports binding plain * Objects to String names. Mainly for test environments, but also usable for * standalone applications. * <p> * This class is not intended for direct usage by applications, although it can * be used for example to override JndiTemplate's * <code>createInitialContext</code> method in unit tests. Typically, * SimpleNamingContextBuilder will be used to set up a JVM-level JNDI * environment. * * @author Rod Johnson * @author Juergen Hoeller * @see org.springframework.mock.jndi.SimpleNamingContextBuilder * @see org.springframework.jndi.JndiTemplate#createInitialContext */ public class SimpleNamingContext implements Context { private static abstract class AbstractNamingEnumeration<T> implements NamingEnumeration<T> { private final Iterator<T> iterator; private AbstractNamingEnumeration(final SimpleNamingContext context, String proot) throws NamingException { if (!"".equals(proot) && !proot.endsWith("/")) { proot = proot + "/"; } final String root = context.root + proot; final Map<String, T> contents = new HashMap<>(); for (final String boundName : context.boundObjects.keySet()) { if (boundName.startsWith(root)) { final int startIndex = root.length(); final int endIndex = boundName.indexOf('/', startIndex); final String strippedName = endIndex != -1 ? boundName.substring(startIndex, endIndex) : boundName.substring(startIndex); if (!contents.containsKey(strippedName)) { try { contents.put(strippedName, newObject(strippedName, context.lookup(proot + strippedName))); } catch (final NameNotFoundException ex) { // cannot happen } } } } if (contents.size() == 0) { throw new NamingException("Invalid root: [" + context.root + proot + "]"); } this.iterator = contents.values().iterator(); } @Override public void close() { } @Override public boolean hasMore() { return this.iterator.hasNext(); } @Override public boolean hasMoreElements() { return this.iterator.hasNext(); } protected abstract T newObject(String strippedName, Object obj); @Override public T next() { return this.iterator.next(); } @Override public T nextElement() { return this.iterator.next(); } } private static class BindingEnumeration extends AbstractNamingEnumeration<Binding> { private BindingEnumeration(final SimpleNamingContext context, final String root) throws NamingException { super(context, root); } @Override protected Binding newObject(final String strippedName, final Object obj) { return new Binding(strippedName, obj); } } private static class NameClassPairEnumeration extends AbstractNamingEnumeration<NameClassPair> { private NameClassPairEnumeration(final SimpleNamingContext context, final String root) throws NamingException { super(context, root); } @Override protected NameClassPair newObject(final String strippedName, final Object obj) { return new NameClassPair(strippedName, obj.getClass().getName()); } } private final Hashtable<String, Object> boundObjects; private final Hashtable<String, Object> environment = new Hashtable<>(); private final String root; // Actual implementations of Context methods follow /** * Construct a new new naming context. */ public SimpleNamingContext() { this(""); } /** * Construct a new new naming context with the given naming root. */ public SimpleNamingContext(final String root) { this.root = root; this.boundObjects = new Hashtable<>(); } /** * Construct a new new naming context with the given naming root, the given * name/object map, and the JNDI environment entries. */ public SimpleNamingContext(final String root, final Hashtable<String, Object> boundObjects, final Hashtable<String, Object> env) { this.root = root; this.boundObjects = boundObjects; if (env != null) { this.environment.putAll(env); } } @Override public Object addToEnvironment(final String propName, final Object propVal) { return this.environment.put(propName, propVal); } @Override public void bind(final Name name, final Object obj) throws NamingException { throw new OperationNotSupportedException( "SimpleNamingContext does not support [javax.naming.Name]"); } /** * Bind the given object to the given name. Note: Not intended for direct use * by applications if setting up a JVM-level JNDI environment. Use * SimpleNamingContextBuilder to set up JNDI bindings then. * * @see org.springframework.mock.jndi.SimpleNamingContextBuilder#bind */ @Override public void bind(final String name, final Object obj) { Logs.info(this, "Static JNDI binding: [" + this.root + name + "] = [" + obj + "]"); this.boundObjects.put(this.root + name, obj); } @Override public void close() { } @Override public Name composeName(final Name name, final Name prefix) throws NamingException { throw new OperationNotSupportedException( "SimpleNamingContext does not support [javax.naming.Name]"); } @Override public String composeName(final String name, final String prefix) { return prefix + name; } @Override public Context createSubcontext(final Name name) throws NamingException { throw new OperationNotSupportedException( "SimpleNamingContext does not support [javax.naming.Name]"); } @Override public Context createSubcontext(final String name) { String subcontextName = this.root + name; if (!subcontextName.endsWith("/")) { subcontextName += "/"; } final Context subcontext = new SimpleNamingContext(subcontextName, this.boundObjects, this.environment); bind(name, subcontext); return subcontext; } @Override public void destroySubcontext(final Name name) throws NamingException { throw new OperationNotSupportedException( "SimpleNamingContext does not support [javax.naming.Name]"); } @Override public void destroySubcontext(final String name) { unbind(name); } @Override public Hashtable<String, Object> getEnvironment() { return this.environment; } @Override public String getNameInNamespace() throws NamingException { throw new OperationNotSupportedException( "SimpleNamingContext does not support [javax.naming.Name]"); } // Unsupported methods follow: no support for javax.naming.Name @Override public NameParser getNameParser(final Name name) throws NamingException { throw new OperationNotSupportedException( "SimpleNamingContext does not support [javax.naming.Name]"); } @Override public NameParser getNameParser(final String name) throws NamingException { throw new OperationNotSupportedException( "SimpleNamingContext does not support [javax.naming.Name]"); } @Override public NamingEnumeration<NameClassPair> list(final Name name) throws NamingException { throw new OperationNotSupportedException( "SimpleNamingContext does not support [javax.naming.Name]"); } @Override public NamingEnumeration<NameClassPair> list(final String root) throws NamingException { Logs.debug(this, "Listing name/class pairs under [" + root + "]"); return new NameClassPairEnumeration(this, root); } @Override public NamingEnumeration<Binding> listBindings(final Name name) throws NamingException { throw new OperationNotSupportedException( "SimpleNamingContext does not support [javax.naming.Name]"); } @Override public NamingEnumeration<Binding> listBindings(final String root) throws NamingException { Logs.debug(this, "Listing bindings under [" + root + "]"); return new BindingEnumeration(this, root); } @Override public Object lookup(final Name name) throws NamingException { throw new OperationNotSupportedException( "SimpleNamingContext does not support [javax.naming.Name]"); } /** * Look up the object with the given name. * <p> * Note: Not intended for direct use by applications. Will be used by any * standard InitialContext JNDI lookups. * * @throws javax.naming.NameNotFoundException if the object could not be found */ @Override public Object lookup(final String lookupName) throws NameNotFoundException { String name = this.root + lookupName; Logs.debug(this, "Static JNDI lookup: [" + name + "]"); if ("".equals(name)) { return new SimpleNamingContext(this.root, this.boundObjects, this.environment); } final Object found = this.boundObjects.get(name); if (found == null) { if (!name.endsWith("/")) { name = name + "/"; } for (final String boundName : this.boundObjects.keySet()) { if (boundName.startsWith(name)) { return new SimpleNamingContext(name, this.boundObjects, this.environment); } } throw new NameNotFoundException("Column [" + this.root + lookupName + "] not bound; " + this.boundObjects.size() + " bindings: [" + StringUtils.collectionToDelimitedString(this.boundObjects.keySet(), ",") + "]"); } return found; } @Override public Object lookupLink(final Name name) throws NamingException { throw new OperationNotSupportedException( "SimpleNamingContext does not support [javax.naming.Name]"); } @Override public Object lookupLink(final String name) throws NameNotFoundException { return lookup(name); } @Override public void rebind(final Name name, final Object obj) throws NamingException { throw new OperationNotSupportedException( "SimpleNamingContext does not support [javax.naming.Name]"); } @Override public void rebind(final String name, final Object obj) { bind(name, obj); } @Override public Object removeFromEnvironment(final String propName) { return this.environment.remove(propName); } @Override public void rename(final Name oldName, final Name newName) throws NamingException { throw new OperationNotSupportedException( "SimpleNamingContext does not support [javax.naming.Name]"); } @Override public void rename(final String oldName, final String newName) throws NameNotFoundException { final Object obj = lookup(oldName); unbind(oldName); bind(newName, obj); } @Override public void unbind(final Name name) throws NamingException { throw new OperationNotSupportedException( "SimpleNamingContext does not support [javax.naming.Name]"); } @Override public void unbind(final String name) { Logs.info(this, "Static JNDI remove: [" + this.root + name + "]"); this.boundObjects.remove(this.root + name); } }