From 3e009cb10063457115db198b8faea1b395365b89 Mon Sep 17 00:00:00 2001
From: Arnaud Simon In all the implementations in our code base
+ * when we create a Reference we pass in In our construction of the Reference the last param. is null,
+ * we could put a url to a jar that contains our {@link ObjectFactory} so that
+ * any of our objects stored in JNDI can be recreated without even having
+ * the classes locally. As it is the The preferred way to assign a JMS client's client identifier is for
* it to be configured in a client-specific In AMQP it is not possible to change the client ID. If one is not specified
+ * In Qpid it is not possible to change the client ID. If one is not specified
* upon connection construction, an id is generated automatically. Therefore
* we can always throw an exception.
* TODO: Make sure that the client identifier can be set on the ConnectionFactoryImpl as the
+ * factory for creating the objects. This is the factory (or
+ * {@link ObjectFactory}) that is used to turn the description in to a real object.
+ * ConnectionFactoryImpl must be on the
+ * classpath when you do a lookup in a JNDI context.. else you'll get a
+ * ClassNotFoundEx.
+ */
public class ConnectionFactoryImpl implements ConnectionFactory, QueueConnectionFactory, TopicConnectionFactory,
XATopicConnectionFactory, XAQueueConnectionFactory, XAConnectionFactory,
- Referenceable
+ ObjectFactory, Referenceable
{
/**
* this ConnectionFactoryImpl's logger
@@ -42,16 +59,21 @@ public class ConnectionFactoryImpl implements ConnectionFactory, QueueConnection
/**
* The URL used to build this factory, (not yet supported)
*/
- private String _url;
+ private QpidURL _qpidURL;
// Undefined at the moment
- public ConnectionFactoryImpl(String url)
+ public ConnectionFactoryImpl(QpidURL url)
+ {
+ _qpidURL = url;
+ }
+
+ public ConnectionFactoryImpl(String url) throws URLSyntaxException
{
- _url = url;
+ // todo
}
/**
- * Create a connection.
+ * Create a connection Factory
*
* @param host The broker host name.
* @param port The port on which the broker is listening for connection.
@@ -409,10 +431,74 @@ public class ConnectionFactoryImpl implements ConnectionFactory, QueueConnection
// Support for JNDI
// ----------------------------------------
+ /**
+ * Creates an object using the location or reference information
+ * specified.
+ *
+ * @param obj The possibly null object containing location or reference
+ * information that can be used in creating an object.
+ * @param name The name of this object relative to nameCtx,
+ * or null if no name is specified.
+ * @param nameCtx The context relative to which the name
+ * parameter is specified, or null if name is
+ * relative to the default initial context.
+ * @param environment The possibly null environment that is used in
+ * creating the object.
+ * @return The object created; null if an object cannot be created.
+ * @throws Exception if this object factory encountered an exception
+ * while attempting to create an object, and no other object factories are
+ * to be tried.
+ */
+ public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception
+ {
+ if (obj instanceof Reference)
+ {
+ Reference ref = (Reference) obj;
+
+ if (ref.getClassName().equals(QueueImpl.class.getName()))
+ {
+ RefAddr addr = ref.get(QueueImpl.class.getName());
+
+ if (addr != null)
+ {
+ return new QueueImpl(new BindingURLImpl((String) addr.getContent()));
+ }
+ }
+
+ if (ref.getClassName().equals(TopicImpl.class.getName()))
+ {
+ RefAddr addr = ref.get(TopicImpl.class.getName());
+
+ if (addr != null)
+ {
+ return new TopicImpl(new BindingURLImpl((String) addr.getContent()));
+ }
+ }
+
+ if (ref.getClassName().equals(ConnectionFactoryImpl.class.getName()))
+ {
+ RefAddr addr = ref.get(ConnectionFactoryImpl.class.getName());
+ if (addr != null)
+ {
+ return new ConnectionFactoryImpl(new QpidURLImpl((String) addr.getContent()));
+ }
+ }
+
+ }
+ return null;
+ }
+
+ //-- interface Reference
+ /**
+ * Retrieves the Reference of this object.
+ *
+ * @return The non-null Reference of this object.
+ * @throws NamingException If a naming exception was encountered while retrieving the reference.
+ */
public Reference getReference() throws NamingException
{
return new Reference(ConnectionFactoryImpl.class.getName(),
- new StringRefAddr(ConnectionFactoryImpl.class.getName(), _url));
+ new StringRefAddr(ConnectionFactoryImpl.class.getName(), _qpidURL.getURL()),
+ ConnectionFactoryImpl.class.getName(), null);
}
-
}
diff --git a/java/client/src/main/java/org/apache/qpidity/jms/ConnectionImpl.java b/java/client/src/main/java/org/apache/qpidity/jms/ConnectionImpl.java
index 60dc126dcf..1a7d5f7402 100644
--- a/java/client/src/main/java/org/apache/qpidity/jms/ConnectionImpl.java
+++ b/java/client/src/main/java/org/apache/qpidity/jms/ConnectionImpl.java
@@ -27,19 +27,14 @@ import javax.jms.ExceptionListener;
import javax.jms.IllegalStateException;
import javax.jms.JMSException;
import javax.jms.Queue;
-import javax.jms.QueueConnection;
import javax.jms.QueueSession;
import javax.jms.ServerSessionPool;
import javax.jms.Session;
import javax.jms.Topic;
-import javax.jms.TopicConnection;
import javax.jms.TopicSession;
-import javax.naming.NamingException;
-import javax.naming.Reference;
-import javax.naming.Referenceable;
-import javax.naming.StringRefAddr;
import org.apache.qpidity.QpidException;
+import org.apache.qpidity.url.QpidURL;
import org.apache.qpidity.client.Client;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -48,7 +43,7 @@ import org.slf4j.LoggerFactory;
/**
* Implements javax.jms.Connection, javax.jms.QueueConnection and javax.jms.TopicConnection
*/
-public class ConnectionImpl implements Connection, Referenceable
+public class ConnectionImpl implements Connection
{
/**
* This class's logger
@@ -65,16 +60,6 @@ public class ConnectionImpl implements Connection, Referenceable
*/
private String _clientID;
- /**
- * The user name to use for authentication
- */
- private String _username;
-
- /**
- * The password to use for authentication
- */
- private String _password;
-
/**
* The Exception listenr get informed when a serious problem is detected
*/
@@ -128,8 +113,19 @@ public class ConnectionImpl implements Connection, Referenceable
_qpidConnection.connect(host, port, virtualHost, username, password);
}
- //---- Interface javax.jms.Connection ---//
+ /**
+ * Create a connection from a QpidURL
+ *
+ * @param qpidURL The url used to create this connection
+ * @throws QpidException If creating a connection fails due to some internal error.
+ */
+ protected ConnectionImpl(QpidURL qpidURL) throws QpidException
+ {
+ _qpidConnection = Client.createConnection();
+ _qpidConnection.connect(qpidURL);
+ }
+ //---- Interface javax.jms.Connection ---//
/**
* Creates a Session
*
@@ -142,7 +138,7 @@ public class ConnectionImpl implements Connection, Referenceable
public synchronized Session createSession(boolean transacted, int acknowledgeMode) throws JMSException
{
checkNotClosed();
- SessionImpl session = null;
+ SessionImpl session;
try
{
session = new SessionImpl(this, transacted, acknowledgeMode, false);
@@ -174,12 +170,11 @@ public class ConnectionImpl implements Connection, Referenceable
/**
* Sets the client identifier for this connection.
- * ConnectionFactory
* object and transparently assigned to the Connection object
* it creates.
- * ConnectionFactory
@@ -396,7 +391,7 @@ public class ConnectionImpl implements Connection, Referenceable
public synchronized QueueSession createQueueSession(boolean transacted, int acknowledgeMode) throws JMSException
{
checkNotClosed();
- QueueSessionImpl queueSession = null;
+ QueueSessionImpl queueSession;
try
{
queueSession = new QueueSessionImpl(this, transacted, acknowledgeMode);
@@ -437,12 +432,11 @@ public class ConnectionImpl implements Connection, Referenceable
* Session.DUPS_OK_ACKNOWLEDGE.
* @return a newly created topic session
* @throws JMSException If creating the session fails due to some internal error.
- * @throws QpidException
*/
public synchronized TopicSession createTopicSession(boolean transacted, int acknowledgeMode) throws JMSException
{
checkNotClosed();
- TopicSessionImpl session = null;
+ TopicSessionImpl session;
try
{
session = new TopicSessionImpl(this, transacted, acknowledgeMode);
@@ -476,7 +470,6 @@ public class ConnectionImpl implements Connection, Referenceable
}
//-------------- protected and private methods
-
/**
* Validate that the Connection is not closed.
*
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:
+ *
+ * Context componentContext = (Context)new InitialContext().lookup("java:comp");
+ * String envEntry = (String) componentContext.lookup("env/myEntry");
+ * String envEntry2 = (String) componentContext.lookup("env/myEntry2");
+ *
+ */
+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 ReadOnlyContext.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 ReadOnlyContext.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 ReadOnlyContext.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 ReadOnlyContext.LocalNamingEnumeration
+ {
+ public Object next() throws NamingException
+ {
+ return nextElement();
+ }
+
+ public Object nextElement()
+ {
+ Map.Entry entry = getNext();
+
+ return new Binding((String) entry.getKey(), entry.getValue());
+ }
+ }
+}
diff --git a/java/client/src/main/java/org/apache/qpidity/naming/jndi.properties b/java/client/src/main/java/org/apache/qpidity/naming/jndi.properties
new file mode 100644
index 0000000000..c379ab7cfd
--- /dev/null
+++ b/java/client/src/main/java/org/apache/qpidity/naming/jndi.properties
@@ -0,0 +1,39 @@
+#
+# 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.qpidity.naming.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
--
cgit v1.2.1