diff options
Diffstat (limited to 'qpid/java/client/src/main/java/org/apache/qpid/jndi')
4 files changed, 973 insertions, 0 deletions
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/jndi/Example.properties b/qpid/java/client/src/main/java/org/apache/qpid/jndi/Example.properties new file mode 100644 index 0000000000..def53d8494 --- /dev/null +++ b/qpid/java/client/src/main/java/org/apache/qpid/jndi/Example.properties @@ -0,0 +1,40 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# +java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialConextFactory + +# use the following property to configure the default connector +#java.naming.provider.url - ignored. + +# register some connection factories +# connectionfactory.[jndiname] = [ConnectionURL] +connectionfactory.local = amqp://guest:guest@clientid/testpath?brokerlist='vm://:1' + +# register some queues in JNDI using the form +# queue.[jndiName] = [physicalName] +queue.MyQueue = example.MyQueue + +# register some topics in JNDI using the form +# topic.[jndiName] = [physicalName] +topic.ibmStocks = stocks.nyse.ibm + +# Register an AMQP destination in JNDI +# NOTE: Qpid currently only supports direct,topics and headers +# destination.[jniName] = [BindingURL] +destination.direct = direct://amq.direct//directQueue +destination.directQueue = direct://amq.direct//message_queue?routingkey="routing_key" diff --git a/qpid/java/client/src/main/java/org/apache/qpid/jndi/NameParserImpl.java b/qpid/java/client/src/main/java/org/apache/qpid/jndi/NameParserImpl.java new file mode 100644 index 0000000000..a3174aec7a --- /dev/null +++ b/qpid/java/client/src/main/java/org/apache/qpid/jndi/NameParserImpl.java @@ -0,0 +1,37 @@ +/** + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.qpid.jndi; + +import javax.naming.CompositeName; +import javax.naming.Name; +import javax.naming.NameParser; +import javax.naming.NamingException; + +/** + * A default implementation of {@link NameParser} + * <p/> + * Based on class from ActiveMQ. + */ +public class NameParserImpl implements NameParser +{ + public Name parse(String name) throws NamingException + { + return new CompositeName(name); + } +} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java b/qpid/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java new file mode 100644 index 0000000000..fec5af55c1 --- /dev/null +++ b/qpid/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java @@ -0,0 +1,369 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.qpid.jndi; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.Queue; +import javax.jms.Topic; +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.spi.InitialContextFactory; + +import org.apache.qpid.client.AMQConnectionFactory; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQHeadersExchange; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.url.AMQBindingURL; +import org.apache.qpid.url.BindingURL; +import org.apache.qpid.url.URLSyntaxException; +import org.apache.qpid.util.Strings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PropertiesFileInitialContextFactory implements InitialContextFactory +{ + protected final Logger _logger = LoggerFactory.getLogger(PropertiesFileInitialContextFactory.class); + + private String CONNECTION_FACTORY_PREFIX = "connectionfactory."; + private String DESTINATION_PREFIX = "destination."; + private String QUEUE_PREFIX = "queue."; + private String TOPIC_PREFIX = "topic."; + + public Context getInitialContext(Hashtable environment) throws NamingException + { + Map data = new ConcurrentHashMap(); + + try + { + + String file = null; + if (environment.containsKey(Context.PROVIDER_URL)) + { + file = (String) environment.get(Context.PROVIDER_URL); + } + else + { + file = System.getProperty(Context.PROVIDER_URL); + } + + if (file != null) + { + _logger.info("Loading Properties from:" + file); + + // Load the properties specified + BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(file)); + Properties p = new Properties(); + try + { + p.load(inputStream); + } + finally + { + inputStream.close(); + } + + Strings.Resolver resolver = new Strings.ChainedResolver + (Strings.SYSTEM_RESOLVER, new Strings.PropertiesResolver(p)); + + for (Map.Entry me : p.entrySet()) + { + String key = (String) me.getKey(); + String value = (String) me.getValue(); + String expanded = Strings.expand(value, resolver); + environment.put(key, expanded); + if (System.getProperty(key) == null) + { + System.setProperty(key, expanded); + } + } + _logger.info("Loaded Context Properties:" + environment.toString()); + } + else + { + _logger.info("No Provider URL specified."); + } + } + catch (IOException ioe) + { + _logger.warn("Unable to load property file specified in Provider_URL:" + environment.get(Context.PROVIDER_URL) +"\n" + + "Due to:"+ioe.getMessage()); + } + + createConnectionFactories(data, environment); + + createDestinations(data, environment); + + createQueues(data, environment); + + createTopics(data, environment); + + return createContext(data, environment); + } + + // Implementation methods + // ------------------------------------------------------------------------- + protected ReadOnlyContext createContext(Map data, Hashtable environment) + { + return new ReadOnlyContext(environment, data); + } + + protected void createConnectionFactories(Map data, Hashtable environment) + { + for (Iterator iter = environment.entrySet().iterator(); iter.hasNext();) + { + Map.Entry entry = (Map.Entry) iter.next(); + String key = entry.getKey().toString(); + if (key.startsWith(CONNECTION_FACTORY_PREFIX)) + { + String jndiName = key.substring(CONNECTION_FACTORY_PREFIX.length()); + ConnectionFactory cf = createFactory(entry.getValue().toString().trim()); + if (cf != null) + { + data.put(jndiName, cf); + } + } + } + } + + protected void createDestinations(Map data, Hashtable environment) + { + for (Iterator iter = environment.entrySet().iterator(); iter.hasNext();) + { + Map.Entry entry = (Map.Entry) iter.next(); + String key = entry.getKey().toString(); + if (key.startsWith(DESTINATION_PREFIX)) + { + String jndiName = key.substring(DESTINATION_PREFIX.length()); + Destination dest = createDestination(entry.getValue().toString().trim()); + if (dest != null) + { + data.put(jndiName, dest); + } + } + } + } + + protected void createQueues(Map data, Hashtable environment) + { + for (Iterator iter = environment.entrySet().iterator(); iter.hasNext();) + { + Map.Entry entry = (Map.Entry) iter.next(); + String key = entry.getKey().toString(); + if (key.startsWith(QUEUE_PREFIX)) + { + String jndiName = key.substring(QUEUE_PREFIX.length()); + Queue q = createQueue(entry.getValue().toString().trim()); + if (q != null) + { + data.put(jndiName, q); + } + } + } + } + + protected void createTopics(Map data, Hashtable environment) + { + for (Iterator iter = environment.entrySet().iterator(); iter.hasNext();) + { + Map.Entry entry = (Map.Entry) iter.next(); + String key = entry.getKey().toString(); + if (key.startsWith(TOPIC_PREFIX)) + { + String jndiName = key.substring(TOPIC_PREFIX.length()); + Topic t = createTopic(entry.getValue().toString().trim()); + if (t != null) + { + if (_logger.isDebugEnabled()) + { + StringBuffer b = new StringBuffer(); + b.append("Creating the topic: " + jndiName + " with the following binding keys "); + for (AMQShortString binding:((AMQTopic)t).getBindingKeys()) + { + b.append(binding.toString()).append(","); + } + + _logger.debug(b.toString()); + } + data.put(jndiName, t); + } + } + } + } + + /** + * Factory method to create new Connection Factory instances + */ + protected ConnectionFactory createFactory(String url) + { + try + { + return new AMQConnectionFactory(url); + } + catch (URLSyntaxException urlse) + { + _logger.warn("Unable to createFactories:" + urlse); + } + + return null; + } + + /** + * Factory method to create new Destination instances from an AMQP BindingURL + */ + protected Destination createDestination(String str) + { + try + { + return AMQDestination.createDestination(str); + } + catch (Exception e) + { + _logger.warn("Unable to create destination:" + e, e); + + return null; + } + } + + /** + * Factory method to create new Queue instances + */ + protected Queue createQueue(Object value) + { + if (value instanceof AMQShortString) + { + return new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, (AMQShortString) value); + } + else if (value instanceof String) + { + return new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, new AMQShortString((String) value)); + } + else if (value instanceof BindingURL) + { + return new AMQQueue((BindingURL) value); + } + + return null; + } + + /** + * Factory method to create new Topic instances + */ + protected Topic createTopic(Object value) + { + if (value instanceof AMQShortString) + { + return new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, (AMQShortString) value); + } + else if (value instanceof String) + { + String[] keys = ((String)value).split(","); + AMQShortString[] bindings = new AMQShortString[keys.length]; + int i = 0; + for (String key:keys) + { + bindings[i] = new AMQShortString(key.trim()); + i++; + } + // The Destination has a dual nature. If this was used for a producer the key is used + // for the routing key. If it was used for the consumer it becomes the bindingKey + return new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME,bindings[0],null,bindings); + } + else if (value instanceof BindingURL) + { + return new AMQTopic((BindingURL) value); + } + + return null; + } + + /** + * Factory method to create new HeaderExcahnge instances + */ + protected Destination createHeaderExchange(Object value) + { + if (value instanceof String) + { + return new AMQHeadersExchange((String) value); + } + else if (value instanceof BindingURL) + { + return new AMQHeadersExchange((BindingURL) value); + } + + return null; + } + + // Properties + // ------------------------------------------------------------------------- + public String getConnectionPrefix() + { + return CONNECTION_FACTORY_PREFIX; + } + + public void setConnectionPrefix(String connectionPrefix) + { + this.CONNECTION_FACTORY_PREFIX = connectionPrefix; + } + + public String getDestinationPrefix() + { + return DESTINATION_PREFIX; + } + + public void setDestinationPrefix(String destinationPrefix) + { + this.DESTINATION_PREFIX = destinationPrefix; + } + + public String getQueuePrefix() + { + return QUEUE_PREFIX; + } + + public void setQueuePrefix(String queuePrefix) + { + this.QUEUE_PREFIX = queuePrefix; + } + + public String getTopicPrefix() + { + return TOPIC_PREFIX; + } + + public void setTopicPrefix(String topicPrefix) + { + this.TOPIC_PREFIX = topicPrefix; + } +} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/jndi/ReadOnlyContext.java b/qpid/java/client/src/main/java/org/apache/qpid/jndi/ReadOnlyContext.java new file mode 100644 index 0000000000..1719ea1219 --- /dev/null +++ b/qpid/java/client/src/main/java/org/apache/qpid/jndi/ReadOnlyContext.java @@ -0,0 +1,527 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.qpid.jndi; + +import java.io.Serializable; +import java.util.Collections; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; + +import javax.naming.Binding; +import javax.naming.CompositeName; +import javax.naming.Context; +import javax.naming.LinkRef; +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.NotContextException; +import javax.naming.OperationNotSupportedException; +import javax.naming.Reference; +import javax.naming.spi.NamingManager; + +/** + * Based on class from ActiveMQ. + * A read-only Context + * <p/> + * This version assumes it and all its subcontext are read-only and any attempt + * to modify (e.g. through bind) will result in an OperationNotSupportedException. + * Each Context in the tree builds a cache of the entries in all sub-contexts + * to optimise the performance of lookup. + * </p> + * <p>This implementation is intended to optimise the performance of lookup(String) + * to about the level of a HashMap get. It has been observed that the scheme + * resolution phase performed by the JVM takes considerably longer, so for + * optimum performance lookups should be coded like:</p> + * <code> + * Context componentContext = (Context)new InitialContext().lookup("java:comp"); + * String envEntry = (String) componentContext.lookup("env/myEntry"); + * String envEntry2 = (String) componentContext.lookup("env/myEntry2"); + * </code> + */ +public class ReadOnlyContext implements Context, Serializable +{ + private static final long serialVersionUID = -5754338187296859149L; + protected static final NameParser nameParser = new NameParserImpl(); + + protected final Hashtable environment; // environment for this context + protected final Map bindings; // bindings at my level + protected final Map treeBindings; // all bindings under me + + private boolean frozen = false; + private String nameInNamespace = ""; + public static final String SEPARATOR = "/"; + + public ReadOnlyContext() + { + environment = new Hashtable(); + bindings = new HashMap(); + treeBindings = new HashMap(); + } + + public ReadOnlyContext(Hashtable env) + { + if (env == null) + { + this.environment = new Hashtable(); + } + else + { + this.environment = new Hashtable(env); + } + + this.bindings = Collections.EMPTY_MAP; + this.treeBindings = Collections.EMPTY_MAP; + } + + public ReadOnlyContext(Hashtable environment, Map bindings) + { + if (environment == null) + { + this.environment = new Hashtable(); + } + else + { + this.environment = new Hashtable(environment); + } + + this.bindings = bindings; + treeBindings = new HashMap(); + frozen = true; + } + + public ReadOnlyContext(Hashtable environment, Map bindings, String nameInNamespace) + { + this(environment, bindings); + this.nameInNamespace = nameInNamespace; + } + + protected ReadOnlyContext(ReadOnlyContext clone, Hashtable env) + { + this.bindings = clone.bindings; + this.treeBindings = clone.treeBindings; + this.environment = new Hashtable(env); + } + + protected ReadOnlyContext(ReadOnlyContext clone, Hashtable env, String nameInNamespace) + { + this(clone, env); + this.nameInNamespace = nameInNamespace; + } + + public void freeze() + { + frozen = true; + } + + boolean isFrozen() + { + return frozen; + } + + /** + * internalBind is intended for use only during setup or possibly by suitably synchronized superclasses. + * It binds every possible lookup into a map in each context. To do this, each context + * strips off one name segment and if necessary creates a new context for it. Then it asks that context + * to bind the remaining name. It returns a map containing all the bindings from the next context, plus + * the context it just created (if it in fact created it). (the names are suitably extended by the segment + * originally lopped off). + * + * @param name + * @param value + * @return + * @throws javax.naming.NamingException + */ + protected Map internalBind(String name, Object value) throws NamingException + { + assert (name != null) && (name.length() > 0); + assert !frozen; + + Map newBindings = new HashMap(); + int pos = name.indexOf('/'); + if (pos == -1) + { + if (treeBindings.put(name, value) != null) + { + throw new NamingException("Something already bound at " + name); + } + + bindings.put(name, value); + newBindings.put(name, value); + } + else + { + String segment = name.substring(0, pos); + assert segment != null; + assert !segment.equals(""); + Object o = treeBindings.get(segment); + if (o == null) + { + o = newContext(); + treeBindings.put(segment, o); + bindings.put(segment, o); + newBindings.put(segment, o); + } + else if (!(o instanceof ReadOnlyContext)) + { + throw new NamingException("Something already bound where a subcontext should go"); + } + + ReadOnlyContext readOnlyContext = (ReadOnlyContext) o; + String remainder = name.substring(pos + 1); + Map subBindings = readOnlyContext.internalBind(remainder, value); + for (Iterator iterator = subBindings.entrySet().iterator(); iterator.hasNext();) + { + Map.Entry entry = (Map.Entry) iterator.next(); + String subName = segment + "/" + (String) entry.getKey(); + Object bound = entry.getValue(); + treeBindings.put(subName, bound); + newBindings.put(subName, bound); + } + } + + return newBindings; + } + + protected ReadOnlyContext newContext() + { + return new ReadOnlyContext(); + } + + public Object addToEnvironment(String propName, Object propVal) throws NamingException + { + return environment.put(propName, propVal); + } + + public Hashtable getEnvironment() throws NamingException + { + return (Hashtable) environment.clone(); + } + + public Object removeFromEnvironment(String propName) throws NamingException + { + return environment.remove(propName); + } + + public Object lookup(String name) throws NamingException + { + if (name.length() == 0) + { + return this; + } + + Object result = treeBindings.get(name); + if (result == null) + { + result = bindings.get(name); + } + + if (result == null) + { + int pos = name.indexOf(':'); + if (pos > 0) + { + String scheme = name.substring(0, pos); + Context ctx = NamingManager.getURLContext(scheme, environment); + if (ctx == null) + { + throw new NamingException("scheme " + scheme + " not recognized"); + } + + return ctx.lookup(name); + } + else + { + // Split out the first name of the path + // and look for it in the bindings map. + CompositeName path = new CompositeName(name); + + if (path.size() == 0) + { + return this; + } + else + { + String first = path.get(0); + Object obj = bindings.get(first); + if (obj == null) + { + throw new NameNotFoundException(name); + } + else if ((obj instanceof Context) && (path.size() > 1)) + { + Context subContext = (Context) obj; + obj = subContext.lookup(path.getSuffix(1)); + } + + return obj; + } + } + } + + if (result instanceof LinkRef) + { + LinkRef ref = (LinkRef) result; + result = lookup(ref.getLinkName()); + } + + if (result instanceof Reference) + { + try + { + result = NamingManager.getObjectInstance(result, null, null, this.environment); + } + catch (NamingException e) + { + throw e; + } + catch (Exception e) + { + throw (NamingException) new NamingException("could not look up : " + name).initCause(e); + } + } + + if (result instanceof ReadOnlyContext) + { + String prefix = getNameInNamespace(); + if (prefix.length() > 0) + { + prefix = prefix + SEPARATOR; + } + + result = new ReadOnlyContext((ReadOnlyContext) result, environment, prefix + name); + } + + return result; + } + + public Object lookup(Name name) throws NamingException + { + return lookup(name.toString()); + } + + public Object lookupLink(String name) throws NamingException + { + return lookup(name); + } + + public Name composeName(Name name, Name prefix) throws NamingException + { + Name result = (Name) prefix.clone(); + result.addAll(name); + + return result; + } + + public String composeName(String name, String prefix) throws NamingException + { + CompositeName result = new CompositeName(prefix); + result.addAll(new CompositeName(name)); + + return result.toString(); + } + + public NamingEnumeration list(String name) throws NamingException + { + Object o = lookup(name); + if (o == this) + { + return new ListEnumeration(); + } + else if (o instanceof Context) + { + return ((Context) o).list(""); + } + else + { + throw new NotContextException(); + } + } + + public NamingEnumeration listBindings(String name) throws NamingException + { + Object o = lookup(name); + if (o == this) + { + return new ListBindingEnumeration(); + } + else if (o instanceof Context) + { + return ((Context) o).listBindings(""); + } + else + { + throw new NotContextException(); + } + } + + public Object lookupLink(Name name) throws NamingException + { + return lookupLink(name.toString()); + } + + public NamingEnumeration list(Name name) throws NamingException + { + return list(name.toString()); + } + + public NamingEnumeration listBindings(Name name) throws NamingException + { + return listBindings(name.toString()); + } + + public void bind(Name name, Object obj) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void bind(String name, Object obj) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void close() throws NamingException + { + // ignore + } + + public Context createSubcontext(Name name) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public Context createSubcontext(String name) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void destroySubcontext(Name name) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void destroySubcontext(String name) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public String getNameInNamespace() throws NamingException + { + return nameInNamespace; + } + + public NameParser getNameParser(Name name) throws NamingException + { + return nameParser; + } + + public NameParser getNameParser(String name) throws NamingException + { + return nameParser; + } + + public void rebind(Name name, Object obj) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void rebind(String name, Object obj) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void rename(Name oldName, Name newName) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void rename(String oldName, String newName) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void unbind(Name name) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void unbind(String name) throws NamingException + { + throw new OperationNotSupportedException(); + } + + private abstract class LocalNamingEnumeration implements NamingEnumeration + { + private Iterator i = bindings.entrySet().iterator(); + + public boolean hasMore() throws NamingException + { + return i.hasNext(); + } + + public boolean hasMoreElements() + { + return i.hasNext(); + } + + protected Map.Entry getNext() + { + return (Map.Entry) i.next(); + } + + public void close() throws NamingException + { } + } + + private class ListEnumeration extends LocalNamingEnumeration + { + public Object next() throws NamingException + { + return nextElement(); + } + + public Object nextElement() + { + Map.Entry entry = getNext(); + + return new NameClassPair((String) entry.getKey(), entry.getValue().getClass().getName()); + } + } + + private class ListBindingEnumeration extends LocalNamingEnumeration + { + public Object next() throws NamingException + { + return nextElement(); + } + + public Object nextElement() + { + Map.Entry entry = getNext(); + + return new Binding((String) entry.getKey(), entry.getValue()); + } + } +} |