summaryrefslogtreecommitdiff
path: root/qpid/java/client
diff options
context:
space:
mode:
authorArnaud Simon <arnaudsimon@apache.org>2007-09-19 11:36:23 +0000
committerArnaud Simon <arnaudsimon@apache.org>2007-09-19 11:36:23 +0000
commit67f46dac215559e7dfc714f4acccd0260889f08b (patch)
tree1f411a985d5b04cd31e529d1d962e06b72041501 /qpid/java/client
parentcf0c30f925429958c603838a07e0f0c9c4c4eb1b (diff)
downloadqpid-python-67f46dac215559e7dfc714f4acccd0260889f08b.tar.gz
renamed qpidity.jms to qpidity.njms and qpidity.client to qpidity.nclient
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@577253 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/java/client')
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/nclient/Client.java128
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/nclient/Connection.java86
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/nclient/DtxSession.java141
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/nclient/ExceptionListener.java37
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/nclient/JMSTestCase.java34
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/nclient/MessagePartListener.java64
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/nclient/Session.java614
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/nclient/impl/ClientSession.java119
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/nclient/impl/ClientSessionDelegate.java80
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/nclient/impl/DemoClient.java89
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/nclient/impl/LargeMsgDemoClient.java74
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/ByteBufferMessage.java150
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/FileMessage.java87
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/MessageListener.java34
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/MessagePartListenerAdapter.java59
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/ReadOnlyMessage.java38
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/StreamingMessage.java59
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/ConnectionFactoryImpl.java521
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/ConnectionImpl.java503
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/ConnectionMetaDataImpl.java165
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/CustomJMSXProperty.java47
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/DestinationImpl.java259
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/ExceptionHelper.java59
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/MessageActor.java176
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/MessageConsumerImpl.java662
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/MessageProducerImpl.java384
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/QpidBrowserListener.java70
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/QpidExceptionListenerImpl.java51
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueBrowserImpl.java256
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueConnectionImpl.java36
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueImpl.java136
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueReceiverImpl.java55
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueSenderImpl.java131
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueSessionImpl.java154
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/SessionImpl.java1324
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/TemporaryDestination.java41
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/TemporaryQueueImpl.java88
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/TemporaryTopicImpl.java71
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicConnectionImpl.java35
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicImpl.java129
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicPublisherImpl.java128
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicSessionImpl.java155
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicSubscriberImpl.java72
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/XAConnectionImpl.java71
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/XAQueueConnectionImpl.java72
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/XAQueueSessionImpl.java64
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/XAResourceImpl.java507
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/XASessionImpl.java126
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/XATopicConnectionImpl.java71
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/XATopicSessionImpl.java63
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/message/BytesMessageImpl.java863
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/message/MapMessageImpl.java628
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/message/MessageFactory.java75
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/message/MessageHelper.java456
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/message/MessageImpl.java1011
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/message/ObjectMessageImpl.java178
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/message/QpidMessage.java445
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/message/StreamMessageImpl.java1115
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpidity/njms/message/TextMessageImpl.java328
59 files changed, 13674 insertions, 0 deletions
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/nclient/Client.java b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/Client.java
new file mode 100644
index 0000000000..ff05e40f20
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/Client.java
@@ -0,0 +1,128 @@
+package org.apache.qpidity.nclient;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.qpidity.BrokerDetails;
+import org.apache.qpidity.ErrorCode;
+import org.apache.qpidity.QpidException;
+import org.apache.qpidity.nclient.impl.ClientSession;
+import org.apache.qpidity.nclient.impl.ClientSessionDelegate;
+import org.apache.qpidity.transport.Channel;
+import org.apache.qpidity.transport.Connection;
+import org.apache.qpidity.transport.ConnectionClose;
+import org.apache.qpidity.transport.ConnectionDelegate;
+import org.apache.qpidity.transport.ConnectionEvent;
+import org.apache.qpidity.transport.ProtocolHeader;
+import org.apache.qpidity.transport.SessionDelegate;
+import org.apache.qpidity.transport.network.mina.MinaHandler;
+import org.apache.qpidity.url.QpidURL;
+
+
+public class Client implements org.apache.qpidity.nclient.Connection
+{
+ private AtomicInteger _channelNo = new AtomicInteger();
+ private Connection _conn;
+ private ExceptionListener _exceptionListner;
+ private final Lock _lock = new ReentrantLock();
+
+ /**
+ *
+ * @return returns a new connection to the broker.
+ */
+ public static org.apache.qpidity.nclient.Connection createConnection()
+ {
+ return new Client();
+ }
+
+ public void connect(String host, int port,String virtualHost,String username, String password) throws QpidException
+ {
+ Condition negotiationComplete = _lock.newCondition();
+ _lock.lock();
+
+ ConnectionDelegate connectionDelegate = new ConnectionDelegate()
+ {
+ public SessionDelegate getSessionDelegate()
+ {
+ return new ClientSessionDelegate();
+ }
+
+ @Override public void connectionClose(Channel context, ConnectionClose connectionClose)
+ {
+ _exceptionListner.onException(
+ new QpidException("Server closed the connection: Reason " +
+ connectionClose.getReplyText(),
+ ErrorCode.get(connectionClose.getReplyCode()),
+ null));
+ }
+ };
+
+ connectionDelegate.setCondition(_lock,negotiationComplete);
+ connectionDelegate.setUsername(username);
+ connectionDelegate.setPassword(password);
+ connectionDelegate.setVirtualHost(virtualHost);
+
+ _conn = MinaHandler.connect(host, port,connectionDelegate);
+
+ // XXX: hardcoded version numbers
+ _conn.send(new ConnectionEvent(0, new ProtocolHeader(1, 0, 10)));
+
+ try
+ {
+ negotiationComplete.await();
+ }
+ catch (Exception e)
+ {
+ //
+ }
+ finally
+ {
+ _lock.unlock();
+ }
+ }
+
+ /*
+ * Until the dust settles with the URL disucssion
+ * I am not going to implement this.
+ */
+ public void connect(QpidURL url) throws QpidException
+ {
+ // temp impl to tests
+ BrokerDetails details = url.getAllBrokerDetails().get(0);
+ connect(details.getHost(),
+ details.getPort(),
+ details.getVirtualHost(),
+ details.getUserName(),
+ details.getPassword());
+ }
+
+ public void close() throws QpidException
+ {
+ Channel ch = _conn.getChannel(0);
+ ch.connectionClose(0, "client is closing", 0, 0);
+ //need to close the connection underneath as well
+ }
+
+ public Session createSession(long expiryInSeconds)
+ {
+ Channel ch = _conn.getChannel(_channelNo.incrementAndGet());
+ ClientSession ssn = new ClientSession();
+ ssn.attach(ch);
+ ssn.sessionOpen(expiryInSeconds);
+ return ssn;
+ }
+
+ public DtxSession createDTXSession(int expiryInSeconds)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setExceptionListener(ExceptionListener exceptionListner)
+ {
+ _exceptionListner = exceptionListner;
+ }
+
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/nclient/Connection.java b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/Connection.java
new file mode 100644
index 0000000000..c11c8a04e0
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/Connection.java
@@ -0,0 +1,86 @@
+/*
+ * 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.qpidity.nclient;
+
+import org.apache.qpidity.QpidException;
+import org.apache.qpidity.url.QpidURL;
+
+/**
+ * This represents a physical connection to a broker.
+ */
+public interface Connection
+{
+ /**
+ * Establish the connection using the given parameters
+ *
+ * @param host
+ * @param port
+ * @param username
+ * @param password
+ * @throws QpidException
+ */
+ public void connect(String host, int port,String virtualHost,String username, String password) throws QpidException;
+
+ /**
+ * Establish the connection with the broker identified by the provided URL.
+ *
+ * @param url The URL of the broker.
+ * @throws QpidException If the communication layer fails to connect with the broker.
+ */
+ public void connect(QpidURL url) throws QpidException;
+
+ /**
+ * Close this connection.
+ *
+ * @throws QpidException if the communication layer fails to close the connection.
+ */
+ public void close() throws QpidException;
+
+
+ /**
+ * Create a session for this connection.
+ * <p> The retuned session is suspended
+ * (i.e. this session is not attached with an underlying channel)
+ *
+ * @param expiryInSeconds Expiry time expressed in seconds, if the value is <= 0 then the session does not expire.
+ * @return A Newly created (suspended) session.
+ */
+ public Session createSession(long expiryInSeconds);
+
+ /**
+ * Create a DtxSession for this connection.
+ * <p> A Dtx Session must be used when resources have to be manipulated as
+ * part of a global transaction.
+ * <p> The retuned DtxSession is suspended
+ * (i.e. this session is not attached with an underlying channel)
+ *
+ * @param expiryInSeconds Expiry time expressed in seconds, if the value is <= 0 then the session does not expire.
+ * @return A Newly created (suspended) DtxSession.
+ */
+ public DtxSession createDTXSession(int expiryInSeconds);
+
+ /**
+ * If the communication layer detects a serious problem with a connection, it
+ * informs the connection's ExceptionListener
+ *
+ * @param exceptionListner The execptionListener
+ */
+
+ public void setExceptionListener(ExceptionListener exceptionListner);
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/nclient/DtxSession.java b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/DtxSession.java
new file mode 100644
index 0000000000..6fe019c3a1
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/DtxSession.java
@@ -0,0 +1,141 @@
+/*
+ * 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.qpidity.nclient;
+
+import org.apache.qpidity.transport.DtxCoordinationCommitResult;
+import org.apache.qpidity.transport.DtxCoordinationGetTimeoutResult;
+import org.apache.qpidity.transport.DtxCoordinationPrepareResult;
+import org.apache.qpidity.transport.DtxCoordinationRecoverResult;
+import org.apache.qpidity.transport.DtxCoordinationRollbackResult;
+import org.apache.qpidity.transport.DtxDemarcationEndResult;
+import org.apache.qpidity.transport.DtxDemarcationStartResult;
+import org.apache.qpidity.transport.Future;
+import org.apache.qpidity.transport.Option;
+
+/**
+ * This session�s resources are control under the scope of a distributed transaction.
+ */
+public interface DtxSession extends Session
+{
+
+ /**
+ * This method is called when messages should be produced and consumed on behalf a transaction
+ * branch identified by xid.
+ * possible options are:
+ * <ul>
+ * <li> {@link Option#JOIN}: Indicate that the start applies to joining a transaction previously seen.
+ * <li> {@link Option#RESUME}: Indicate that the start applies to resuming a suspended transaction branch specified.
+ * </ul>
+ *
+ * @param xid Specifies the xid of the transaction branch to be started.
+ * @param options Possible options are: {@link Option#JOIN} and {@link Option#RESUME}.
+ * @return Confirms to the client that the transaction branch is started or specify the error condition.
+ */
+ public Future<DtxDemarcationStartResult> dtxDemarcationStart(String xid, Option... options);
+
+ /**
+ * This method is called when the work done on behalf a transaction branch finishes or needs to
+ * be suspended.
+ * possible options are:
+ * <ul>
+ * <li> {@link Option#FAIL}: indicates that this portion of work has failed;
+ * otherwise this portion of work has
+ * completed successfully.
+ * <li> {@link Option#SUSPEND}: Indicates that the transaction branch is
+ * temporarily suspended in an incomplete state.
+ * </ul>
+ *
+ * @param xid Specifies the xid of the transaction branch to be ended.
+ * @param options Available options are: {@link Option#FAIL} and {@link Option#SUSPEND}.
+ * @return Confirms to the client that the transaction branch is ended or specify the error condition.
+ */
+ public Future<DtxDemarcationEndResult> dtxDemarcationEnd(String xid, Option... options);
+
+ /**
+ * Commit the work done on behalf a transaction branch. This method commits the work associated
+ * with xid. Any produced messages are made available and any consumed messages are discarded.
+ * possible option is:
+ * <ul>
+ * <li> {@link Option#ONE_PHASE}: When set then one-phase commit optimization is used.
+ * </ul>
+ *
+ * @param xid Specifies the xid of the transaction branch to be committed.
+ * @param options Available option is: {@link Option#ONE_PHASE}
+ * @return Confirms to the client that the transaction branch is committed or specify the error condition.
+ */
+ public Future<DtxCoordinationCommitResult> dtxCoordinationCommit(String xid, Option... options);
+
+ /**
+ * This method is called to forget about a heuristically completed transaction branch.
+ *
+ * @param xid Specifies the xid of the transaction branch to be forgotten.
+ */
+ public void dtxCoordinationForget(String xid);
+
+ /**
+ * This method obtains the current transaction timeout value in seconds. If set-timeout was not
+ * used prior to invoking this method, the return value is the default timeout; otherwise, the
+ * value used in the previous set-timeout call is returned.
+ *
+ * @param xid Specifies the xid of the transaction branch for getting the timeout.
+ * @return The current transaction timeout value in seconds.
+ */
+ public Future<DtxCoordinationGetTimeoutResult> dtxCoordinationGetTimeout(String xid);
+
+ /**
+ * This method prepares for commitment any message produced or consumed on behalf of xid.
+ *
+ * @param xid Specifies the xid of the transaction branch that can be prepared.
+ * @return The status of the prepare operation: can be one of those:
+ * xa-ok: Normal execution.
+ * <p/>
+ * xa-rdonly: The transaction branch was read-only and has been committed.
+ * <p/>
+ * xa-rbrollback: The broker marked the transaction branch rollback-only for an unspecified
+ * reason.
+ * <p/>
+ * xa-rbtimeout: The work represented by this transaction branch took too long.
+ */
+ public Future<DtxCoordinationPrepareResult> dtxCoordinationPrepare(String xid);
+
+ /**
+ * This method is called to obtain a list of transaction branches that are in a prepared or
+ * heuristically completed state.
+ * Todo The options ahould be removed once the xml is updated
+ * @return a array of xids to be recovered.
+ */
+ public Future<DtxCoordinationRecoverResult> dtxCoordinationRecover(Option... options);
+
+ /**
+ * This method rolls back the work associated with xid. Any produced messages are discarded and
+ * any consumed messages are re-enqueued.
+ *
+ * @param xid Specifies the xid of the transaction branch that can be rolled back.
+ * @return Confirms to the client that the transaction branch is rolled back or specify the error condition.
+ */
+ public Future<DtxCoordinationRollbackResult> dtxCoordinationRollback(String xid);
+
+ /**
+ * Sets the specified transaction branch timeout value in seconds.
+ *
+ * @param xid Specifies the xid of the transaction branch for setting the timeout.
+ * @param timeout The transaction timeout value in seconds.
+ */
+ public void dtxCoordinationSetTimeout(String xid, long timeout);
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/nclient/ExceptionListener.java b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/ExceptionListener.java
new file mode 100644
index 0000000000..60a5325c6f
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/ExceptionListener.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.qpidity.nclient;
+
+import org.apache.qpidity.QpidException;
+
+/**
+ * If the communication layer detects a serious problem with a <CODE>connection</CODE>, it
+ * informs the connection's ExceptionListener
+ */
+public interface ExceptionListener
+{
+ /**
+ * If the communication layer detects a serious problem with a connection, it
+ * informs the connection's ExceptionListener
+ *
+ * @param exception The exception comming from the communication layer.
+ * @see Connection
+ */
+ public void onException(QpidException exception);
+} \ No newline at end of file
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/nclient/JMSTestCase.java b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/JMSTestCase.java
new file mode 100644
index 0000000000..42dad7329e
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/JMSTestCase.java
@@ -0,0 +1,34 @@
+package org.apache.qpidity.nclient;
+
+import org.apache.qpidity.njms.ConnectionFactoryImpl;
+import org.apache.qpidity.njms.TopicImpl;
+
+public class JMSTestCase
+{
+ public static void main(String[] args)
+ {
+ try
+ {
+ javax.jms.Connection con = (new ConnectionFactoryImpl("localhost",5672, "test", "guest","guest")).createConnection();
+ con.start();
+
+ javax.jms.Session ssn = con.createSession(false, 1);
+
+ javax.jms.Destination dest = new TopicImpl("myTopic");
+ javax.jms.MessageProducer prod = ssn.createProducer(dest);
+ javax.jms.MessageConsumer cons = ssn.createConsumer(dest);
+
+ javax.jms.BytesMessage msg = ssn.createBytesMessage();
+ msg.writeInt(123);
+ prod.send(msg);
+
+ javax.jms.BytesMessage m = (javax.jms.BytesMessage)cons.receive();
+ System.out.println("Data : " + m.readInt());
+
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/nclient/MessagePartListener.java b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/MessagePartListener.java
new file mode 100644
index 0000000000..ecdd2d7952
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/MessagePartListener.java
@@ -0,0 +1,64 @@
+/* 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.qpidity.nclient;
+
+import java.nio.ByteBuffer;
+
+import org.apache.qpidity.transport.Header;
+
+/**
+ * Assembles message parts.
+ * <p> The sequence of event for transferring a message is as follows:
+ * <ul>
+ * <li> messageHeaders
+ * <li> n calls to addData
+ * <li> messageReceived
+ * </ul>
+ * This is up to the implementation to assembled the message when the different parts
+ * are transferred.
+ */
+public interface MessagePartListener
+{
+ /**
+ * Indicates the Message transfer has started.
+ *
+ * @param transferId
+ */
+ public void messageTransfer(long transferId);
+
+ /**
+ * Add the following headers ( {@link org.apache.qpidity.DeliveryProperties}
+ * or {@link org.apache.qpidity.ApplicationProperties} ) to the message being received.
+ *
+ * @param headers Either <code>DeliveryProperties</code> or <code>ApplicationProperties</code>
+ */
+ public void messageHeader(Header header);
+
+ /**
+ * Add the following byte array to the content of the message being received
+ *
+ * @param data Data to be added or streamed.
+ */
+ public void data(ByteBuffer src);
+
+ /**
+ * Indicates that the message has been fully received.
+ */
+ public void messageReceived();
+
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/nclient/Session.java b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/Session.java
new file mode 100644
index 0000000000..f2e28eb8e7
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/Session.java
@@ -0,0 +1,614 @@
+/*
+ * 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.qpidity.nclient;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Map;
+
+import org.apache.qpidity.transport.*;
+import org.apache.qpidity.api.Message;
+
+/**
+ * <p>A session is associated with a connection.
+ * When created a Session is not attached with an underlying channel.
+ * Session is single threaded </p>
+ * <p/>
+ * All the Session commands are asynchronous, synchronous invocation is achieved through invoking the sync method.
+ * That is to say that <code>command1</code> will be synchronously invoked using the following sequence:
+ * <ul>
+ * <li> <code>session.command1()</code>
+ * <li> <code>session.sync()</code>
+ * </ul>
+ */
+public interface Session
+{
+ public static final short TRANSFER_ACQUIRE_MODE_NO_ACQUIRE = 0;
+ public static final short TRANSFER_ACQUIRE_MODE_PRE_ACQUIRE = 1;
+ public static final short TRANSFER_CONFIRM_MODE_REQUIRED = 1;
+ public static final short TRANSFER_CONFIRM_MODE_NOT_REQUIRED = 0;
+ public static final short MESSAGE_FLOW_MODE_CREDIT = 0;
+ public static final short MESSAGE_FLOW_MODE_WINDOW = 1;
+ public static final short MESSAGE_FLOW_UNIT_MESSAGE = 0;
+ public static final short MESSAGE_FLOW_UNIT_BYTE = 1;
+ public static final short MESSAGE_REJECT_CODE_GENERIC = 0;
+ public static final short MESSAGE_REJECT_CODE_IMMEDIATE_DELIVERY_FAILED = 1;
+ public static final short MESSAGE_ACQUIRE_ANY_AVAILABLE_MESSAGE = 0;
+ public static final short MESSAGE_ACQUIRE_MESSAGES_IF_ALL_ARE_AVAILABLE = 1;
+
+ //------------------------------------------------------
+ // Session housekeeping methods
+ //------------------------------------------------------
+
+ /**
+ * Sync method will block until all outstanding commands
+ * are executed.
+ */
+ public void sync();
+
+ /**
+ * Close this session and any associated resources.
+ */
+ public void sessionClose();
+
+ /**
+ * Suspend this session resulting in interrupting the traffic with the broker.
+ * <p> The session timer will start to tick in suspend.
+ * <p> When a session is suspend any operation of this session and of the associated resources are unavailable.
+ */
+ public void sessionSuspend();
+
+ //------------------------------------------------------
+ // Messaging methods
+ // Producer
+ //------------------------------------------------------
+ /**
+ * Transfer the given message to a specified exchange.
+ * <p/>
+ * <p>This is a convinience method for providing a complete message
+ * using a single method which internaly is mapped to messageTransfer(), headers() followed
+ * by data() and an endData().
+ * <b><i>This method should only be used by small messages</b></i></p>
+ *
+ * @param destination The exchange the message is being sent.
+ * @param msg The Message to be sent
+ * @param confirmMode <ul> </li>off ({@link Session#TRANSFER_CONFIRM_MODE_NOT_REQUIRED}): confirmation
+ * is not required, once a message has been transferred in pre-acquire
+ * mode (or once acquire has been sent in no-acquire mode) the message is considered
+ * transferred
+ * <p/>
+ * <li> on ({@link Session#TRANSFER_CONFIRM_MODE_REQUIRED}): an acquired message
+ * (whether acquisition was implicit as in pre-acquire mode or
+ * explicit as in no-acquire mode) is not considered transferred until the original
+ * transfer is complete (signaled via execution.complete)
+ * </ul>
+ * @param acquireMode <ul>
+ * <li> no-acquire ({@link Session#TRANSFER_ACQUIRE_MODE_NO_ACQUIRE}): the message
+ * must be explicitly acquired
+ * <li> pre-acquire ({@link Session#TRANSFER_ACQUIRE_MODE_PRE_ACQUIRE}): the message is
+ * acquired when the transfer starts
+ * </ul>
+ * @throws java.io.IOException If transferring a message fails due to some internal communication error.
+ */
+ public void messageTransfer(String destination, Message msg, short confirmMode, short acquireMode)
+ throws IOException;
+
+ /**
+ * <p>This is a convinience method for streaming a message using pull semantics
+ * using a single method as opposed to doing a push using messageTransfer(), headers() followed
+ * by a series of data() and an endData().</p>
+ * <p>Internally data will be pulled from Message object(which wrap's a data stream) using it's read()
+ * and pushed using messageTransfer(), headers() followed by a series of data() and an endData().
+ * <br><b><i>This method should only be used by large messages</b></i><br>
+ * There are two convinience Message classes provided to facilitate this.
+ * <ul>
+ * <li> <code>{@link org.apache.qpidity.nclient.util.FileMessage}</code>
+ * <li> <code>{@link org.apache.qpidity.nclient.util.StreamingMessage}</code>
+ * </ul>
+ * You could also implement a the <code>Message</code> interface to and wrap any
+ * data stream you want.
+ * </p>
+ *
+ * @param destination The exchange the message is being sent.
+ * @param msg The Message to be sent
+ * @param confirmMode <ul> </li>off ({@link Session#TRANSFER_CONFIRM_MODE_NOT_REQUIRED}): confirmation
+ * is not required, once a message has been transferred in pre-acquire
+ * mode (or once acquire has been sent in no-acquire mode) the message is considered
+ * transferred
+ * <p/>
+ * <li> on ({@link Session#TRANSFER_CONFIRM_MODE_REQUIRED}): an acquired message
+ * (whether acquisition was implicit as in pre-acquire mode or
+ * explicit as in no-acquire mode) is not considered transferred until the original
+ * transfer is complete (signaled via execution.complete)
+ * </ul>
+ * @param acquireMode <ul>
+ * <li> no-acquire ({@link Session#TRANSFER_ACQUIRE_MODE_NO_ACQUIRE}): the message
+ * must be explicitly acquired
+ * <li> pre-acquire ({@link Session#TRANSFER_ACQUIRE_MODE_PRE_ACQUIRE}): the message
+ * is acquired when the transfer starts
+ * </ul>
+ * @throws java.io.IOException If transferring a message fails due to some internal communication error.
+ */
+ public void messageStream(String destination, Message msg, short confirmMode, short acquireMode) throws IOException;
+
+ /**
+ * Declare the beginning of a message transfer operation. This operation must
+ * be followed by {@link Session#header} then followed by any number of {@link Session#data}.
+ * The transfer is ended by {@link Session#endData}.
+ * <p> This way of transferring messages is useful when streaming large messages
+ * <p> In the interval [messageTransfer endData] any attempt to call a method other than
+ * {@link Session#header}, {@link Session#endData} ore {@link Session#sessionClose}
+ * will result in an exception being thrown.
+ *
+ * @param destination The exchange the message is being sent.
+ * @param confirmMode <ul> </li>off ({@link Session#TRANSFER_CONFIRM_MODE_NOT_REQUIRED}): confirmation
+ * is not required, once a message has been transferred in pre-acquire
+ * mode (or once acquire has been sent in no-acquire mode) the message is considered
+ * transferred
+ * <p/>
+ * <li> on ({@link Session#TRANSFER_CONFIRM_MODE_REQUIRED}): an acquired message
+ * (whether acquisition was implicit as in pre-acquire mode or
+ * explicit as in no-acquire mode) is not considered transferred until the original
+ * transfer is complete (signaled via execution.complete)
+ * </ul>
+ * @param acquireMode <ul>
+ * <li> no-acquire ({@link Session#TRANSFER_ACQUIRE_MODE_NO_ACQUIRE}): the message
+ * must be explicitly acquired
+ * <li> pre-acquire ({@link Session#TRANSFER_ACQUIRE_MODE_PRE_ACQUIRE}): the message
+ * is acquired when the transfer starts
+ * </ul>
+ */
+ public void messageTransfer(String destination, short confirmMode, short acquireMode);
+
+ /**
+ * Add a set of headers the following headers to the message being sent.
+ *
+ * @param headers Are either <code>{@link org.apache.qpidity.transport.DeliveryProperties}</code>
+ * or <code>{@link org.apache.qpidity.transport.MessageProperties}</code>
+ * @see org.apache.qpidity.transport.DeliveryProperties
+ * @see org.apache.qpidity.transport.MessageProperties
+ */
+ public void header(Struct... headers);
+
+ /**
+ * Add the following byte array to the content of the message being sent.
+ *
+ * @param data Data to be added.
+ */
+ public void data(byte[] data);
+
+ /**
+ * Add the following ByteBuffer to the content of the message being sent.
+ * <p> Note that only the data between the buffer current position and the
+ * buffer limit is added.
+ * It is therefore recommended to flip the buffer before adding it to the message,
+ *
+ * @param buf Data to be added.
+ */
+ public void data(ByteBuffer buf);
+
+ /**
+ * Add the following String to the content of the message being sent.
+ *
+ * @param str String to be added.
+ */
+ public void data(String str);
+
+ /**
+ * Signals the end of data for the message.
+ */
+ public void endData();
+
+ //------------------------------------------------------
+ // Messaging methods
+ // Consumer
+ //------------------------------------------------------
+
+ /**
+ * Associate a message listener with a destination.
+ * <p> The destination is bound to a queue and messages are filtered based
+ * on the provider filter map (message filtering is specific to the provider and may not be handled).
+ * <p> Following are valid options:
+ * <ul>
+ * <li>{@link Option#NO_LOCAL}: <p>If the no-local field is set the server will not send
+ * messages to the connection that
+ * published them.
+ * <li>{@link Option#EXCLUSIVE}: <p> Request exclusive subscription access, meaning only this
+ * ubscription can access the queue.
+ * <li>{@link Option#NO_OPTION}: <p> Has no effect as it represents an �empty� option.
+ * </ul>
+ *
+ * @param queue The queue this receiver is receiving messages from.
+ * @param destination The destination for the subscriber ,a.k.a the delivery tag.
+ * @param confirmMode <ul> </li>off ({@link Session#TRANSFER_CONFIRM_MODE_NOT_REQUIRED}): confirmation is not
+ * required, once a message has been transferred in pre-acquire
+ * mode (or once acquire has been sent in no-acquire mode) the message is considered
+ * transferred
+ * <p/>
+ * <li> on ({@link Session#TRANSFER_CONFIRM_MODE_REQUIRED}): an acquired message (whether
+ * acquisition was implicit as in pre-acquire mode or
+ * explicit as in no-acquire mode) is not considered transferred until the original
+ * transfer is complete (signaled via execution.complete)
+ * </ul>
+ * @param acquireMode <ul>
+ * <li> no-acquire ({@link Session#TRANSFER_ACQUIRE_MODE_NO_ACQUIRE}): the message must
+ * be explicitly acquired
+ * <li> pre-acquire ({@link Session#TRANSFER_ACQUIRE_MODE_PRE_ACQUIRE}): the message is
+ * acquired when the transfer starts
+ * </ul>
+ * @param listener The listener for this destination. When big message are transfered then
+ * it is recommended to use a {@link org.apache.qpidity.nclient.MessagePartListener}.
+ * @param options Set of Options (valid options are {@link Option#NO_LOCAL}, {@link Option#EXCLUSIVE}
+ * and {@link Option#NO_OPTION})
+ * @param filter A set of filters for the subscription. The syntax and semantics of these filters depends
+ * on the providers implementation.
+ */
+ public void messageSubscribe(String queue, String destination, short confirmMode, short acquireMode,
+ MessagePartListener listener, Map<String, ?> filter, Option... options);
+
+ /**
+ * This method cancels a consumer. This does not affect already delivered messages, but it does
+ * mean the server will not send any more messages for that destination. The client may receive an
+ * arbitrary number of messages in between sending the cancel method and receiving the
+ * notification of completion of the cancel command.
+ *
+ * @param destination The destination for the subscriber used at subscription
+ */
+ public void messageCancel(String destination);
+
+ /**
+ * Associate a message part listener with a destination.
+ * <p> Only one listerner per destination is allowed. This means
+ * that the previous message listener is replaced. This is done gracefully i.e. the message
+ * listener is replaced once it return from the processing of a message.
+ *
+ * @param destination The destination the listener is associated with.
+ * @param listener The new listener for this destination.
+ */
+ public void setMessageListener(String destination, MessagePartListener listener);
+
+ /**
+ * Sets the mode of flow control used for a given destination.
+ * <p> With credit based flow control, the broker continually maintains its current
+ * credit balance with the recipient. The credit balance consists of two values, a message
+ * count, and a byte count. Whenever message data is sent, both counts must be decremented.
+ * If either value reaches zero, the flow of message data must stop. Additional credit is
+ * received via the {@link Session#messageFlow} method.
+ * <p> Window based flow control is identical to credit based flow control, however message
+ * acknowledgment implicitly grants a single unit of message credit, and the size of the
+ * message in byte credits for each acknowledged message.
+ *
+ * @param destination The destination to set the flow mode on.
+ * @param mode <ul> <li>credit ({@link Session#MESSAGE_FLOW_MODE_CREDIT}): choose credit based flow control
+ * <li> window ({@link Session#MESSAGE_FLOW_MODE_WINDOW}): choose window based flow control</ul>
+ */
+ public void messageFlowMode(String destination, short mode);
+
+
+ /**
+ * This method controls the flow of message data to a given destination. It is used by the
+ * recipient of messages to dynamically match the incoming rate of message flow to its
+ * processing or forwarding capacity. Upon receipt of this method, the sender must add "value"
+ * number of the specified unit to the available credit balance for the specified destination.
+ * A value of 0 indicates an infinite amount of credit. This disables any limit for
+ * the given unit until the credit balance is zeroed with {@link Session#messageStop}
+ * or {@link Session#messageFlush}.
+ *
+ * @param destination The destination to set the flow.
+ * @param unit Specifies the unit of credit balance.
+ * <p/>
+ * One of: <ul>
+ * <li> message ({@link Session#MESSAGE_FLOW_UNIT_MESSAGE})
+ * <li> byte ({@link Session#MESSAGE_FLOW_UNIT_BYTE})
+ * </ul>
+ * @param value Number of credits, a value of 0 indicates an infinite amount of credit.
+ */
+ public void messageFlow(String destination, short unit, long value);
+
+ /**
+ * Forces the broker to exhaust its credit supply.
+ * <p> The broker's credit will always be zero when
+ * this method completes.
+ *
+ * @param destination The destination to call flush on.
+ */
+ public void messageFlush(String destination);
+
+ /**
+ * On receipt of this method, the brokers MUST set his credit to zero for the given
+ * destination. This obeys the generic semantics of command completion, i.e. when confirmation
+ * is issued credit MUST be zero and no further messages will be sent until such a time as
+ * further credit is received.
+ *
+ * @param destination The destination to stop.
+ */
+ public void messageStop(String destination);
+
+ /**
+ * Acknowledge the receipt of ranges of messages.
+ * <p>Message must have been previously acquired either by receiving them in
+ * pre-acquire mode or by explicitly acquiring them.
+ *
+ * @param ranges Range of acknowledged messages.
+ */
+ public void messageAcknowledge(RangeSet ranges);
+
+ /**
+ * Reject ranges of acquired messages.
+ * <p> The broker MUST deliver rejected messages to the
+ * alternate-exchange on the queue from which it was delivered. If no alternate-exchange is
+ * defined for that queue the broker MAY discard the message.
+ *
+ * @param ranges Range of rejected messages.
+ * @param code The reject code must be one of {@link Session#MESSAGE_REJECT_CODE_GENERIC} or
+ * {@link Session#MESSAGE_REJECT_CODE_IMMEDIATE_DELIVERY_FAILED} (immediate delivery was attempted but
+ * failed).
+ * @param text String describing the reason for a message transfer rejection.
+ */
+ public void messageReject(RangeSet ranges, int code, String text);
+
+ /**
+ * As it is possible that the broker does not manage to reject some messages, after completion of
+ * {@link Session#messageReject} this method will return the ranges of rejected messages.
+ * <p> Note that {@link Session#messageReject} and this methods are asynchronous therefore for accessing to the
+ * previously rejected messages this method must be invoked in conjunction with {@link Session#sync()}.
+ * <p> A recommended invocation sequence would be:
+ * <ul>
+ * <li> {@link Session#messageReject}
+ * <li> {@link Session#sync()}
+ * <li> {@link Session#getRejectedMessages()}
+ * </ul>
+ *
+ * @return The rejected message ranges
+ */
+ public RangeSet getRejectedMessages();
+
+ /**
+ * Try to acquire ranges of messages hence releasing them form the queue.
+ * This means that once acknowledged, a message will not be delivered to any other receiver.
+ * <p> As those messages may have been consumed by another receivers hence,
+ * message acquisition can fail.
+ * The outcome of the acquisition is returned as an array of ranges of qcquired messages.
+ * <p> This method should only be called on non-acquired messages.
+ *
+ * @param mode One of: <ul>
+ * <li> any ({@link Session#MESSAGE_ACQUIRE_ANY_AVAILABLE_MESSAGE}): acquire any available
+ * messages for consumption
+ * <li> all ({@link Session#MESSAGE_ACQUIRE_MESSAGES_IF_ALL_ARE_AVAILABLE}): only acquire messages
+ * if all are available for consumption
+ * </ul>
+ * @param ranges Ranges of messages to be acquired.
+ */
+ public void messageAcquire(RangeSet ranges, short mode);
+
+ /**
+ * As it is possible that the broker does not manage to acquire some messages, after completion of
+ * {@link Session#messageAcquire} this method will return the ranges of acquired messages.
+ * <p> Note that {@link Session#messageAcquire} and this methods are asynchronous therefore for accessing to the
+ * previously acquired messages this method must be invoked in conjunction with {@link Session#sync()}.
+ * <p> A recommended invocation sequence would be:
+ * <ul>
+ * <li> {@link Session#messageAcquire}
+ * <li> {@link Session#sync()}
+ * <li> {@link Session#getAccquiredMessages()}
+ * </ul>
+ *
+ * @return returns the message ranges marked by the broker as acquired.
+ */
+ public RangeSet getAccquiredMessages();
+
+ /**
+ * Give up responsibility for processing ranges of messages.
+ * <p> Released messages are re-enqueued.
+ *
+ * @param ranges Ranges of messages to be released.
+ */
+ public void messageRelease(RangeSet ranges);
+
+ // -----------------------------------------------
+ // Local transaction methods
+ // ----------------------------------------------
+ /**
+ * Selects the session for local transaction support.
+ */
+ public void txSelect();
+
+ /**
+ * Commit the receipt and the delivery of all messages exchanged by this session resources.
+ *
+ * @throws IllegalStateException If this session is not transacted.
+ */
+ public void txCommit() throws IllegalStateException;
+
+ /**
+ * Rollback the receipt and the delivery of all messages exchanged by this session resources.
+ *
+ * @throws IllegalStateException If this session is not transacted.
+ */
+ public void txRollback() throws IllegalStateException;
+
+ //---------------------------------------------
+ // Queue methods
+ //---------------------------------------------
+
+ /**
+ * Declare a queue with the given queueName
+ * <p> Following are the valid options:
+ * <ul>
+ * <li> {@link Option#AUTO_DELETE}: <p> If this field is set and the exclusive field is also set,
+ * then the queue is deleted when the connection closes.
+ * If this field is set and the exclusive field is not set the queue is deleted when all
+ * the consumers have finished using it.
+ * <li> {@link Option#DURABLE}: <p> If set when creating a new queue,
+ * the queue will be marked as durable. Durable queues
+ * remain active when a server restarts. Non-durable queues (transient queues) are purged
+ * if/when a server restarts. Note that durable queues do not necessarily hold persistent
+ * messages, although it does not make sense to send persistent messages to a transient
+ * queue.
+ * <li> {@link Option#EXCLUSIVE}: <p> Exclusive queues can only be used from one connection at a time.
+ * Once a connection declares an exclusive queue, that queue cannot be used by any other connections until the
+ * declaring connection closes.
+ * <li> {@link Option#PASSIVE}: <p> If set, the server will not create the queue.
+ * This field allows the client to assert the presence of a queue without modifying the server state.
+ * <li>{@link Option#NO_OPTION}: <p> Has no effect as it represents an �empty� option.
+ * </ul>
+ * <p>In the absence of a particular option, the defaul value is false for each option
+ *
+ * @param queueName The name of the delcared queue.
+ * @param alternateExchange If a message is rejected by a queue, then it is sent to the alternate-exchange. A message
+ * may be rejected by a queue for the following reasons:
+ * <oL> <li> The queue is deleted when it is not empty;
+ * <li> Immediate delivery of a message is requested, but there are no consumers connected to
+ * the queue. </ol>
+ * @param arguments Used for backward compatibility
+ * @param options Set of Options ( valide options are: {@link Option#AUTO_DELETE}, {@link Option#DURABLE},
+ * {@link Option#EXCLUSIVE}, {@link Option#PASSIVE} and {@link Option#NO_OPTION})
+ * @see Option
+ */
+ public void queueDeclare(String queueName, String alternateExchange, Map<String, ?> arguments, Option... options);
+
+ /**
+ * Bind a queue with an exchange.
+ *
+ * @param queueName Specifies the name of the queue to bind. If the queue name is empty, refers to the current
+ * queue for the session, which is the last declared queue.
+ * @param exchangeName The exchange name.
+ * @param routingKey Specifies the routing key for the binding. The routing key is used for routing messages
+ * depending on the exchange configuration. Not all exchanges use a routing key - refer to
+ * the specific exchange documentation. If the queue name is empty, the server uses the last
+ * queue declared on the session. If the routing key is also empty, the server uses this
+ * queue name for the routing key as well. If the queue name is provided but the routing key
+ * is empty, the server does the binding with that empty routing key. The meaning of empty
+ * routing keys depends on the exchange implementation.
+ * @param arguments Used for backward compatibility
+ */
+ public void queueBind(String queueName, String exchangeName, String routingKey, Map<String, ?> arguments);
+
+ /**
+ * Unbind a queue from an exchange.
+ *
+ * @param queueName Specifies the name of the queue to unbind.
+ * @param exchangeName The name of the exchange to unbind from.
+ * @param routingKey Specifies the routing key of the binding to unbind.
+ * @param arguments Used for backward compatibility
+ */
+ public void queueUnbind(String queueName, String exchangeName, String routingKey, Map<String, ?> arguments);
+
+ /**
+ * This method removes all messages from a queue. It does not cancel consumers. Purged messages
+ * are deleted without any formal "undo" mechanism.
+ *
+ * @param queueName Specifies the name of the queue to purge. If the queue name is empty, refers to the
+ * current queue for the session, which is the last declared queue.
+ */
+ public void queuePurge(String queueName);
+
+ /**
+ * This method deletes a queue. When a queue is deleted any pending messages are sent to a
+ * dead-letter queue if this is defined in the server configuration, and all consumers on the
+ * queue are cancelled.
+ * <p> Following are the valid options:
+ * <ul>
+ * <li> {@link Option#IF_EMPTY}: <p> If set, the server will only delete the queue if it has no messages.
+ * <li> {@link Option#IF_UNUSED}: <p> If set, the server will only delete the queue if it has no consumers.
+ * If the queue has consumers the server does does not delete it but raises a channel exception instead.
+ * <li>{@link Option#NO_OPTION}: <p> Has no effect as it represents an �empty� option.
+ * </ul>
+ * </p>
+ * <p/>
+ * <p>In the absence of a particular option, the defaul value is false for each option</p>
+ *
+ * @param queueName Specifies the name of the queue to delete. If the queue name is empty, refers to the
+ * current queue for the session, which is the last declared queue.
+ * @param options Set of options (Valid options are: {@link Option#IF_EMPTY}, {@link Option#IF_UNUSED}
+ * and {@link Option#NO_OPTION})
+ * @see Option
+ */
+ public void queueDelete(String queueName, Option... options);
+
+ // --------------------------------------
+ // exhcange methods
+ // --------------------------------------
+
+ /**
+ * This method creates an exchange if it does not already exist, and if the exchange exists,
+ * verifies that it is of the correct and expected class.
+ * <p> Following are the valid options:
+ * <ul>
+ * <li> {@link Option#AUTO_DELETE}: <p> If set, the exchange is deleted when all queues have finished using it.
+ * <li> {@link Option#DURABLE}: <p> If set when creating a new exchange, the exchange will
+ * be marked as durable. Durable exchanges remain active when a server restarts. Non-durable exchanges (transient
+ * exchanges) are purged if/when a server restarts.
+ * <li> {@link Option#PASSIVE}: <p> If set, the server will not create the exchange.
+ * The client can use this to check whether an exchange exists without modifying the server state.
+ * <li> {@link Option#NO_OPTION}: <p> Has no effect as it represents an �empty� option.
+ * </ul>
+ * <p>In the absence of a particular option, the defaul value is false for each option</p>
+ *
+ * @param exchangeName The exchange name.
+ * @param type Each exchange belongs to one of a set of exchange types implemented by the server. The
+ * exchange types define the functionality of the exchange - i.e. how messages are routed
+ * through it. It is not valid or meaningful to attempt to change the type of an existing
+ * exchange. Default exchange types are: direct, topic, headers and fanout.
+ * @param alternateExchange In the event that a message cannot be routed, this is the name of the exchange to which
+ * the message will be sent.
+ * @param options Set of options (valid options are: {@link Option#AUTO_DELETE}, {@link Option#DURABLE},
+ * {@link Option#PASSIVE}, {@link Option#NO_OPTION})
+ * @param arguments Used for backward compatibility
+ * @see Option
+ */
+ public void exchangeDeclare(String exchangeName, String type, String alternateExchange, Map<String, ?> arguments,
+ Option... options);
+
+ /**
+ * This method deletes an exchange. When an exchange is deleted all queue bindings on the
+ * exchange are cancelled.
+ * <p> Following are the valid options:
+ * <ul>
+ * <li> {@link Option#IF_UNUSED}: <p> If set, the server will only delete the exchange if it has no queue bindings. If the
+ * exchange has queue bindings the server does not delete it but raises a channel exception
+ * instead.
+ * <li> {@link Option#NO_OPTION}: <p> Has no effect as it represents an �empty� option.
+ * </ul>
+ * <p>In the absence of a particular option, the defaul value is false for each option
+ *
+ * @param exchangeName The name of exchange to be deleted.
+ * @param options Set of options (valid options are: {@link Option#IF_UNUSED}, {@link Option#NO_OPTION})
+ * @see Option
+ */
+ public void exchangeDelete(String exchangeName, Option... options);
+
+
+ /**
+ * This method is used to request information on a particular exchange.
+ *
+ * @param exchangeName The name of the exchange for which information is requested. If not specified explicitly
+ * the default exchange is implied.
+ * @result Information on the specified exchange.
+ */
+ public Future<ExchangeQueryResult> exchangeQuery(String exchangeName);
+
+ /**
+ * If the session receives a sessionClosed with an error code it
+ * informs the session's ExceptionListener
+ *
+ * @param exceptionListner The execptionListener
+ */
+ public void setExceptionListener(ExceptionListener exceptionListner);
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/nclient/impl/ClientSession.java b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/impl/ClientSession.java
new file mode 100644
index 0000000000..f6aa444881
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/impl/ClientSession.java
@@ -0,0 +1,119 @@
+package org.apache.qpidity.nclient.impl;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.qpidity.transport.Option;
+import org.apache.qpidity.QpidException;
+import org.apache.qpidity.transport.Range;
+import org.apache.qpidity.transport.RangeSet;
+import org.apache.qpidity.api.Message;
+import org.apache.qpidity.nclient.ExceptionListener;
+import org.apache.qpidity.nclient.MessagePartListener;
+
+/**
+ * Implements a Qpid Sesion.
+ */
+public class ClientSession extends org.apache.qpidity.transport.Session implements org.apache.qpidity.nclient.DtxSession
+{
+ private Map<String,MessagePartListener> _messageListeners = new HashMap<String,MessagePartListener>();
+ private ExceptionListener _exceptionListner;
+ private RangeSet _acquiredMessages;
+ private RangeSet _rejectedMessages;
+
+ public void messageAcknowledge(RangeSet ranges)
+ {
+ for (Range range : ranges)
+ {
+ for (long l = range.getLower(); l <= range.getUpper(); l++)
+ {
+ System.out.println("Acknowleding transfer id : " + l);
+ super.processed(l);
+ }
+ }
+ }
+
+ public void messageSubscribe(String queue, String destination, short confirmMode, short acquireMode, MessagePartListener listener, Map<String, ?> filter, Option... options)
+ {
+ setMessageListener(destination,listener);
+ super.messageSubscribe(queue, destination, confirmMode, acquireMode, filter, options);
+ }
+
+ public void messageTransfer(String destination, Message msg, short confirmMode, short acquireMode) throws IOException
+ {
+ // The javadoc clearly says that this method is suitable for small messages
+ // therefore reading the content in one shot.
+ super.messageTransfer(destination, confirmMode, acquireMode);
+ super.header(msg.getDeliveryProperties(),msg.getMessageProperties());
+ super.data(msg.readData());
+ super.endData();
+ }
+
+ public void messageStream(String destination, Message msg, short confirmMode, short acquireMode) throws IOException
+ {
+ super.messageTransfer(destination, confirmMode, acquireMode);
+ super.header(msg.getDeliveryProperties(),msg.getMessageProperties());
+ boolean b = true;
+ int count = 0;
+ while(b)
+ {
+ try
+ {
+ System.out.println("count : " + count++);
+ super.data(msg.readData());
+ }
+ catch(EOFException e)
+ {
+ b = false;
+ }
+ }
+
+ super.endData();
+ }
+
+ public RangeSet getAccquiredMessages()
+ {
+ return _acquiredMessages;
+ }
+
+ public RangeSet getRejectedMessages()
+ {
+ return _rejectedMessages;
+ }
+
+ public void setMessageListener(String destination, MessagePartListener listener)
+ {
+ if (listener == null)
+ {
+ throw new IllegalArgumentException("Cannot set message listener to null");
+ }
+ _messageListeners.put(destination, listener);
+ }
+
+ public void setExceptionListener(ExceptionListener exceptionListner)
+ {
+ _exceptionListner = exceptionListner;
+ }
+
+ void setAccquiredMessages(RangeSet acquiredMessages)
+ {
+ _acquiredMessages = acquiredMessages;
+ }
+
+ void setRejectedMessages(RangeSet rejectedMessages)
+ {
+ _rejectedMessages = rejectedMessages;
+ }
+
+ void notifyException(QpidException ex)
+ {
+ _exceptionListner.onException(ex);
+ }
+
+ Map<String,MessagePartListener> getMessageListerners()
+ {
+ return _messageListeners;
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/nclient/impl/ClientSessionDelegate.java b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/impl/ClientSessionDelegate.java
new file mode 100644
index 0000000000..a19d1016f5
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/impl/ClientSessionDelegate.java
@@ -0,0 +1,80 @@
+package org.apache.qpidity.nclient.impl;
+
+import java.nio.ByteBuffer;
+
+import org.apache.qpidity.ErrorCode;
+
+import org.apache.qpidity.nclient.MessagePartListener;
+
+import org.apache.qpidity.QpidException;
+import org.apache.qpidity.transport.Data;
+import org.apache.qpidity.transport.Header;
+import org.apache.qpidity.transport.MessageAcquired;
+import org.apache.qpidity.transport.MessageReject;
+import org.apache.qpidity.transport.MessageTransfer;
+import org.apache.qpidity.transport.Range;
+import org.apache.qpidity.transport.Session;
+import org.apache.qpidity.transport.SessionClosed;
+import org.apache.qpidity.transport.SessionDelegate;
+
+
+public class ClientSessionDelegate extends SessionDelegate
+{
+ private MessageTransfer _currentTransfer;
+ private MessagePartListener _currentMessageListener;
+
+ @Override public void sessionClosed(Session ssn,SessionClosed sessionClosed)
+ {
+ ((ClientSession)ssn).notifyException(new QpidException(sessionClosed.getReplyText(),ErrorCode.get(sessionClosed.getReplyCode()),null));
+ }
+
+ // --------------------------------------------
+ // Message methods
+ // --------------------------------------------
+ @Override public void data(Session ssn, Data data)
+ {
+ for (ByteBuffer b : data.getFragments())
+ {
+ _currentMessageListener.data(b);
+ }
+ if (data.isLast())
+ {
+ _currentMessageListener.messageReceived();
+ }
+
+ }
+
+ @Override public void header(Session ssn, Header header)
+ {
+ _currentMessageListener.messageHeader(header);
+ }
+
+
+ @Override public void messageTransfer(Session session, MessageTransfer currentTransfer)
+ {
+ _currentTransfer = currentTransfer;
+ _currentMessageListener = ((ClientSession)session).getMessageListerners().get(currentTransfer.getDestination());
+ _currentMessageListener.messageTransfer(currentTransfer.getId());
+ }
+
+ @Override public void messageReject(Session session, MessageReject struct)
+ {
+ for (Range range : struct.getTransfers())
+ {
+ for (long l = range.getLower(); l <= range.getUpper(); l++)
+ {
+ System.out.println("message rejected: " +
+ session.getCommand((int) l));
+ }
+ }
+ ((ClientSession)session).setRejectedMessages(struct.getTransfers());
+ ((ClientSession)session).notifyException(new QpidException("Message Rejected",ErrorCode.MESSAGE_REJECTED,null));
+ session.processed(struct);
+ }
+
+ @Override public void messageAcquired(Session session, MessageAcquired struct)
+ {
+ ((ClientSession)session).setAccquiredMessages(struct.getTransfers());
+ session.processed(struct);
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/nclient/impl/DemoClient.java b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/impl/DemoClient.java
new file mode 100644
index 0000000000..9c6ce45e25
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/impl/DemoClient.java
@@ -0,0 +1,89 @@
+package org.apache.qpidity.nclient.impl;
+
+import org.apache.qpidity.QpidException;
+import org.apache.qpidity.api.Message;
+import org.apache.qpidity.nclient.Client;
+import org.apache.qpidity.nclient.Connection;
+import org.apache.qpidity.nclient.ExceptionListener;
+import org.apache.qpidity.nclient.Session;
+import org.apache.qpidity.nclient.util.MessageListener;
+import org.apache.qpidity.nclient.util.MessagePartListenerAdapter;
+import org.apache.qpidity.transport.DeliveryProperties;
+import org.apache.qpidity.transport.MessageProperties;
+
+public class DemoClient
+{
+ public static MessagePartListenerAdapter createAdapter()
+ {
+ return new MessagePartListenerAdapter(new MessageListener()
+ {
+ public void onMessage(Message m)
+ {
+ System.out.println("\n================== Received Msg ==================");
+ System.out.println("Message Id : " + m.getMessageProperties().getMessageId());
+ System.out.println(m.toString());
+ System.out.println("================== End Msg ==================\n");
+ }
+
+ });
+ }
+
+ public static final void main(String[] args)
+ {
+ Connection conn = Client.createConnection();
+ try{
+ conn.connect("0.0.0.0", 5672, "test", "guest", "guest");
+ }catch(Exception e){
+ e.printStackTrace();
+ }
+
+ Session ssn = conn.createSession(50000);
+ ssn.setExceptionListener(new ExceptionListener()
+ {
+ public void onException(QpidException e)
+ {
+ System.out.println(e);
+ }
+ });
+ ssn.queueDeclare("queue1", null, null);
+ ssn.queueBind("queue1", "amq.direct", "queue1",null);
+ ssn.sync();
+
+ ssn.messageSubscribe("queue1", "myDest", (short)0, (short)0,createAdapter(), null);
+
+ // queue
+ ssn.messageTransfer("amq.direct", (short) 0, (short) 1);
+ ssn.header(new DeliveryProperties().setRoutingKey("queue1"),new MessageProperties().setMessageId("123"));
+ ssn.data("this is the data");
+ ssn.endData();
+
+ //reject
+ ssn.messageTransfer("amq.direct", (short) 0, (short) 1);
+ ssn.data("this should be rejected");
+ ssn.header(new DeliveryProperties().setRoutingKey("stocks"));
+ ssn.endData();
+ ssn.sync();
+
+ // topic subs
+ ssn.messageSubscribe("topic1", "myDest2", (short)0, (short)0,createAdapter(), null);
+ ssn.messageSubscribe("topic2", "myDest3", (short)0, (short)0,createAdapter(), null);
+ ssn.messageSubscribe("topic3", "myDest4", (short)0, (short)0,createAdapter(), null);
+ ssn.sync();
+
+ ssn.queueDeclare("topic1", null, null);
+ ssn.queueBind("topic1", "amq.topic", "stock.*",null);
+ ssn.queueDeclare("topic2", null, null);
+ ssn.queueBind("topic2", "amq.topic", "stock.us.*",null);
+ ssn.queueDeclare("topic3", null, null);
+ ssn.queueBind("topic3", "amq.topic", "stock.us.rh",null);
+ ssn.sync();
+
+ // topic
+ ssn.messageTransfer("amq.topic", (short) 0, (short) 1);
+ ssn.data("Topic message");
+ ssn.header(new DeliveryProperties().setRoutingKey("stock.us.ibm"),new MessageProperties().setMessageId("456"));
+ ssn.endData();
+ ssn.sync();
+ }
+
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/nclient/impl/LargeMsgDemoClient.java b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/impl/LargeMsgDemoClient.java
new file mode 100644
index 0000000000..022edf0dd9
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/impl/LargeMsgDemoClient.java
@@ -0,0 +1,74 @@
+package org.apache.qpidity.nclient.impl;
+
+import java.io.FileInputStream;
+
+import org.apache.qpidity.QpidException;
+import org.apache.qpidity.api.Message;
+import org.apache.qpidity.nclient.Client;
+import org.apache.qpidity.nclient.Connection;
+import org.apache.qpidity.nclient.ExceptionListener;
+import org.apache.qpidity.nclient.Session;
+import org.apache.qpidity.nclient.util.FileMessage;
+import org.apache.qpidity.nclient.util.MessageListener;
+import org.apache.qpidity.nclient.util.MessagePartListenerAdapter;
+import org.apache.qpidity.transport.DeliveryProperties;
+import org.apache.qpidity.transport.MessageProperties;
+
+public class LargeMsgDemoClient
+{
+ public static MessagePartListenerAdapter createAdapter()
+ {
+ return new MessagePartListenerAdapter(new MessageListener()
+ {
+ public void onMessage(Message m)
+ {
+ System.out.println("\n================== Received Msg ==================");
+ System.out.println("Message Id : " + m.getMessageProperties().getMessageId());
+ System.out.println(m.toString());
+ System.out.println("================== End Msg ==================\n");
+ }
+
+ });
+ }
+
+ public static final void main(String[] args)
+ {
+ Connection conn = Client.createConnection();
+ try{
+ conn.connect("0.0.0.0", 5672, "test", "guest", "guest");
+ }catch(Exception e){
+ e.printStackTrace();
+ }
+
+ Session ssn = conn.createSession(50000);
+ ssn.setExceptionListener(new ExceptionListener()
+ {
+ public void onException(QpidException e)
+ {
+ System.out.println(e);
+ }
+ });
+ ssn.queueDeclare("queue1", null, null);
+ ssn.queueBind("queue1", "amq.direct", "queue1",null);
+ ssn.sync();
+
+ ssn.messageSubscribe("queue1", "myDest", (short)0, (short)0,createAdapter(), null);
+
+ try
+ {
+ FileMessage msg = new FileMessage(new FileInputStream("/home/rajith/TestFile"),
+ 1024,
+ new DeliveryProperties().setRoutingKey("queue1"),
+ new MessageProperties().setMessageId("123"));
+
+ // queue
+ ssn.messageStream("amq.direct",msg, (short) 0, (short) 1);
+ ssn.sync();
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/ByteBufferMessage.java b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/ByteBufferMessage.java
new file mode 100644
index 0000000000..dec5677679
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/ByteBufferMessage.java
@@ -0,0 +1,150 @@
+package org.apache.qpidity.nclient.util;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.Queue;
+
+import org.apache.qpidity.transport.DeliveryProperties;
+import org.apache.qpidity.transport.MessageProperties;
+import org.apache.qpidity.api.Message;
+
+/**
+ * <p>A Simple implementation of the message interface
+ * for small messages. When the readData methods are called
+ * we assume the message is complete. i.e there want be any
+ * appendData operations after that.</p>
+ *
+ * <p>If you need large message support please see
+ * <code>FileMessage</code> and <code>StreamingMessage</code>
+ * </p>
+ */
+public class ByteBufferMessage implements Message
+{
+ private Queue<ByteBuffer> _data = new LinkedList<ByteBuffer>();
+ private ByteBuffer _readBuffer;
+ private int _dataSize;
+ private DeliveryProperties _currentDeliveryProps;
+ private MessageProperties _currentMessageProps;
+ private long _transferId;
+
+ public ByteBufferMessage()
+ {
+ _currentDeliveryProps = new DeliveryProperties();
+ _currentMessageProps = new MessageProperties();
+ }
+
+ public ByteBufferMessage(long transferId)
+ {
+ _transferId = transferId;
+ }
+
+ public long getMessageTransferId()
+ {
+ return _transferId;
+ }
+
+ public void clearData()
+ {
+ _data = new LinkedList<ByteBuffer>();
+ _readBuffer = null;
+ }
+
+ public void appendData(byte[] src) throws IOException
+ {
+ appendData(ByteBuffer.wrap(src));
+ }
+
+ /**
+ * write the data from the current position up to the buffer limit
+ */
+ public void appendData(ByteBuffer src) throws IOException
+ {
+ _data.offer(src);
+ _dataSize += src.remaining();
+ }
+
+ public DeliveryProperties getDeliveryProperties()
+ {
+ return _currentDeliveryProps;
+ }
+
+ public MessageProperties getMessageProperties()
+ {
+ System.out.println("MessageProperties is null ? " + _currentMessageProps == null? "true":"false");
+ return _currentMessageProps;
+ }
+
+ public void setDeliveryProperties(DeliveryProperties props)
+ {
+ _currentDeliveryProps = props;
+ }
+
+ public void setMessageProperties(MessageProperties props)
+ {
+ _currentMessageProps = props;
+ }
+
+ public void readData(byte[] target) throws IOException
+ {
+ getReadBuffer().get(target);
+ }
+
+ public ByteBuffer readData() throws IOException
+ {
+ return getReadBuffer();
+ }
+
+ private void buildReadBuffer()
+ {
+ //optimize for the simple cases
+ if(_data.size() == 1)
+ {
+ _readBuffer = _data.element().duplicate();
+ }
+ else
+ {
+ _readBuffer = ByteBuffer.allocate(_dataSize);
+ for(ByteBuffer buf:_data)
+ {
+ _readBuffer.put(buf);
+ }
+ }
+ }
+
+ private ByteBuffer getReadBuffer() throws IOException
+ {
+ if (_readBuffer != null )
+ {
+ return _readBuffer.slice();
+ }
+ else
+ {
+ if (_data.size() >0)
+ {
+ buildReadBuffer();
+ return _readBuffer.slice();
+ }
+ else
+ {
+ throw new IOException("No Data to read");
+ }
+ }
+ }
+
+ //hack for testing
+ @Override public String toString()
+ {
+ try
+ {
+ ByteBuffer temp = getReadBuffer();
+ byte[] b = new byte[temp.remaining()];
+ temp.get(b);
+ return new String(b);
+ }
+ catch(IOException e)
+ {
+ return "No data";
+ }
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/FileMessage.java b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/FileMessage.java
new file mode 100644
index 0000000000..6d925a0ad3
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/FileMessage.java
@@ -0,0 +1,87 @@
+package org.apache.qpidity.nclient.util;
+
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+
+import org.apache.qpidity.transport.DeliveryProperties;
+import org.apache.qpidity.transport.MessageProperties;
+import org.apache.qpidity.api.Message;
+
+/**
+ * FileMessage provides pull style semantics for
+ * larges messages backed by a disk.
+ * Instead of loading all data into memeory it uses
+ * FileChannel to map regions of the file into memeory
+ * at a time.
+ *
+ * The write methods are not supported.
+ *
+ * From the standpoint of performance it is generally
+ * only worth mapping relatively large files into memory.
+ *
+ * FileMessage msg = new FileMessage(in,delProps,msgProps);
+ * session.messageTransfer(dest,msg,0,0);
+ *
+ * The messageTransfer method will read the file in chunks
+ * and stream it.
+ *
+ */
+public class FileMessage extends ReadOnlyMessage implements Message
+{
+ private FileChannel _fileChannel;
+ private int _chunkSize;
+ private long _fileSize;
+ private long _pos = 0;
+
+ public FileMessage(FileInputStream in,int chunkSize,DeliveryProperties deliveryProperties,MessageProperties messageProperties)throws IOException
+ {
+ _messageProperties = messageProperties;
+ _deliveryProperties = deliveryProperties;
+
+ _fileChannel = in.getChannel();
+ _chunkSize = chunkSize;
+ _fileSize = _fileChannel.size();
+
+ if (_fileSize <= _chunkSize)
+ {
+ _chunkSize = (int)_fileSize;
+ }
+ }
+
+ public void readData(byte[] target) throws IOException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public ByteBuffer readData() throws IOException
+ {
+ if (_pos == _fileSize)
+ {
+ throw new EOFException();
+ }
+
+ if (_pos + _chunkSize > _fileSize)
+ {
+ _chunkSize = (int)(_fileSize - _pos);
+ }
+ MappedByteBuffer bb = _fileChannel.map(FileChannel.MapMode.READ_ONLY, _pos, _chunkSize);
+ _pos += _chunkSize;
+ return bb;
+ }
+
+ /**
+ * This message is used by an application user to
+ * provide data to the client library using pull style
+ * semantics. Since the message is not transfered yet, it
+ * does not have a transfer id. Hence this method is not
+ * applicable to this implementation.
+ */
+ public long getMessageTransferId()
+ {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/MessageListener.java b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/MessageListener.java
new file mode 100644
index 0000000000..43c20eb6b5
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/MessageListener.java
@@ -0,0 +1,34 @@
+/*
+ * 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.qpidity.nclient.util;
+
+import org.apache.qpidity.api.Message;
+
+/**
+ *A message listener
+ */
+public interface MessageListener
+{
+ /**
+ * Process an incoming message.
+ *
+ * @param message The incoming message.
+ */
+ public void onMessage(Message message);
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/MessagePartListenerAdapter.java b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/MessagePartListenerAdapter.java
new file mode 100644
index 0000000000..885841bc2a
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/MessagePartListenerAdapter.java
@@ -0,0 +1,59 @@
+package org.apache.qpidity.nclient.util;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.apache.qpidity.transport.DeliveryProperties;
+import org.apache.qpidity.transport.MessageProperties;
+import org.apache.qpidity.transport.Header;
+import org.apache.qpidity.nclient.MessagePartListener;
+
+/**
+ * This is a simple message assembler.
+ * Will call onMessage method of the adaptee
+ * when all message data is read.
+ *
+ * This is a good convinience utility for handling
+ * small messages
+ */
+public class MessagePartListenerAdapter implements MessagePartListener
+{
+ MessageListener _adaptee;
+ ByteBufferMessage _currentMsg;
+
+ public MessagePartListenerAdapter(MessageListener listener)
+ {
+ _adaptee = listener;
+ }
+
+ public void messageTransfer(long transferId)
+ {
+ _currentMsg = new ByteBufferMessage(transferId);
+ }
+
+ public void data(ByteBuffer src)
+ {
+ try
+ {
+ _currentMsg.appendData(src);
+ }
+ catch(IOException e)
+ {
+ // A chance for IO exception
+ // doesn't occur as we are using
+ // a ByteBuffer
+ }
+ }
+
+ public void messageHeader(Header header)
+ {
+ _currentMsg.setDeliveryProperties(header.get(DeliveryProperties.class));
+ _currentMsg.setMessageProperties(header.get(MessageProperties.class));
+ }
+
+ public void messageReceived()
+ {
+ _adaptee.onMessage(_currentMsg);
+ }
+
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/ReadOnlyMessage.java b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/ReadOnlyMessage.java
new file mode 100644
index 0000000000..8ff5c62a25
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/ReadOnlyMessage.java
@@ -0,0 +1,38 @@
+package org.apache.qpidity.nclient.util;
+
+import java.nio.ByteBuffer;
+
+import org.apache.qpidity.transport.DeliveryProperties;
+import org.apache.qpidity.transport.MessageProperties;
+import org.apache.qpidity.api.Message;
+
+public abstract class ReadOnlyMessage implements Message
+{
+ MessageProperties _messageProperties;
+ DeliveryProperties _deliveryProperties;
+
+ public void appendData(byte[] src)
+ {
+ throw new UnsupportedOperationException("This Message is read only after the initial source");
+ }
+
+ public void appendData(ByteBuffer src)
+ {
+ throw new UnsupportedOperationException("This Message is read only after the initial source");
+ }
+
+ public DeliveryProperties getDeliveryProperties()
+ {
+ return _deliveryProperties;
+ }
+
+ public MessageProperties getMessageProperties()
+ {
+ return _messageProperties;
+ }
+
+ public void clearData()
+ {
+ throw new UnsupportedOperationException("This Message is read only after the initial source, cannot clear data");
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/StreamingMessage.java b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/StreamingMessage.java
new file mode 100644
index 0000000000..2422de3877
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/nclient/util/StreamingMessage.java
@@ -0,0 +1,59 @@
+package org.apache.qpidity.nclient.util;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+
+import org.apache.qpidity.transport.DeliveryProperties;
+import org.apache.qpidity.transport.MessageProperties;
+import org.apache.qpidity.api.Message;
+
+public class StreamingMessage extends ReadOnlyMessage implements Message
+{
+ SocketChannel _socChannel;
+ private int _chunkSize;
+ private ByteBuffer _readBuf;
+
+ public StreamingMessage(SocketChannel in,int chunkSize,DeliveryProperties deliveryProperties,MessageProperties messageProperties)throws IOException
+ {
+ _messageProperties = messageProperties;
+ _deliveryProperties = deliveryProperties;
+
+ _socChannel = in;
+ _chunkSize = chunkSize;
+ _readBuf = ByteBuffer.allocate(_chunkSize);
+ }
+
+ public void readData(byte[] target) throws IOException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public ByteBuffer readData() throws IOException
+ {
+ if(_socChannel.isConnected() && _socChannel.isOpen())
+ {
+ _readBuf.clear();
+ _socChannel.read(_readBuf);
+ }
+ else
+ {
+ throw new EOFException("The underlying socket/channel has closed");
+ }
+
+ return _readBuf.duplicate();
+ }
+
+ /**
+ * This message is used by an application user to
+ * provide data to the client library using pull style
+ * semantics. Since the message is not transfered yet, it
+ * does not have a transfer id. Hence this method is not
+ * applicable to this implementation.
+ */
+ public long getMessageTransferId()
+ {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/ConnectionFactoryImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/ConnectionFactoryImpl.java
new file mode 100644
index 0000000000..23ddf1f2e0
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/ConnectionFactoryImpl.java
@@ -0,0 +1,521 @@
+package org.apache.qpidity.njms;
+
+import javax.jms.*;
+import javax.naming.*;
+import javax.naming.spi.ObjectFactory;
+
+import org.apache.qpidity.QpidException;
+import org.apache.qpidity.BrokerDetails;
+import org.apache.qpidity.url.QpidURLImpl;
+import org.apache.qpidity.url.QpidURL;
+import org.apache.qpidity.url.BindingURLImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Hashtable;
+import java.net.MalformedURLException;
+
+/**
+ * Implements all the JMS connection factories.
+ * <p> In all the implementations in our code base
+ * when we create a Reference we pass in <code>ConnectionFactoryImpl</code> 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.
+ * <p>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 <code>ConnectionFactoryImpl</code> 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,
+ ObjectFactory, Referenceable
+{
+ /**
+ * this ConnectionFactoryImpl's logger
+ */
+ private static final Logger _logger = LoggerFactory.getLogger(ConnectionFactoryImpl.class);
+
+ /**
+ * The virtual host on which the broker is deployed.
+ */
+ private String _host;
+ /**
+ * The port on which the broker is listening for connection.
+ */
+ private int _port;
+ /**
+ * The default user name used of user identification.
+ */
+ private String _defaultUsername;
+ /**
+ * The default password used of user identification.
+ */
+ private String _defaultPassword;
+ /**
+ * The virtual host on which the broker is deployed.
+ */
+ private String _virtualHost;
+ /**
+ * The URL used to build this factory, (not yet supported)
+ */
+ private QpidURL _qpidURL;
+
+ // Undefined at the moment
+ public ConnectionFactoryImpl(QpidURL url)
+ {
+ _qpidURL = url;
+ }
+
+ public ConnectionFactoryImpl(String url) throws MalformedURLException
+ {
+ _qpidURL = new QpidURLImpl(url);
+ BrokerDetails bd = _qpidURL.getAllBrokerDetails().get(0);
+ _host = bd.getHost();
+ _port = bd.getPort();
+ _defaultUsername = bd.getUserName();
+ _defaultPassword = bd.getPassword();
+ _virtualHost = bd.getVirtualHost();
+ }
+
+ /**
+ * Create a connection Factory
+ *
+ * @param host The broker host name.
+ * @param port The port on which the broker is listening for connection.
+ * @param virtualHost The virtual host on which the broker is deployed.
+ * @param defaultUsername The user name used of user identification.
+ * @param defaultPassword The password used of user identification.
+ */
+ public ConnectionFactoryImpl(String host, int port, String virtualHost, String defaultUsername,
+ String defaultPassword)
+ {
+ _host = host;
+ _port = port;
+ _defaultUsername = defaultUsername;
+ _defaultPassword = defaultPassword;
+ _virtualHost = virtualHost;
+ }
+
+ //-- Interface ConnectionFactory
+
+ /**
+ * Creates a connection with the default user identity.
+ * <p> The connection is created in stopped mode. No messages
+ * will be delivered until the <code>Connection.start</code> method
+ * is explicitly called.
+ *
+ * @return A newly created connection.
+ * @throws JMSException If creating the connection fails due to some internal error.
+ * @throws JMSSecurityException If client authentication fails due to an invalid user name or password.
+ */
+ public Connection createConnection() throws JMSException
+ {
+ try
+ {
+ return new ConnectionImpl(_host, _port, _virtualHost, _defaultUsername, _defaultPassword);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("PRoblem when creating connection", e);
+ }
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+
+ /**
+ * Creates a connection with the specified user identity.
+ * <p> The connection is created in stopped mode. No messages
+ * will be delivered until the <code>Connection.start</code> method
+ * is explicitly called.
+ *
+ * @param username the caller's user name
+ * @param password the caller's password
+ * @return A newly created connection.
+ * @throws JMSException If creating the connection fails due to some internal error.
+ * @throws JMSSecurityException If client authentication fails due to an invalid user name or password.
+ */
+ public Connection createConnection(String username, String password) throws JMSException
+ {
+ try
+ {
+ return new ConnectionImpl(_host, _port, _virtualHost, username, password);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("PRoblem when creating connection", e);
+ }
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+
+ // ----------------------------------------
+ // Support for JMS 1.0 classes
+ // ----------------------------------------
+ //--- Interface QueueConnection
+ /**
+ * Creates a queueConnection with the default user identity.
+ * <p> The queueConnection is created in stopped mode. No messages
+ * will be delivered until the <code>Connection.start</code> method
+ * is explicitly called.
+ *
+ * @return A newly created queueConnection
+ * @throws JMSException If creating the queueConnection fails due to some internal error.
+ * @throws JMSSecurityException If client authentication fails due to an invalid user name or password.
+ */
+ public QueueConnection createQueueConnection() throws JMSException
+ {
+ try
+ {
+ return new QueueConnectionImpl(_host, _port, _virtualHost, _defaultUsername, _defaultPassword);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("PRoblem when creating connection", e);
+ }
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+
+ /**
+ * Creates a queueConnection with the specified user identity.
+ * <p> The queueConnection is created in stopped mode. No messages
+ * will be delivered until the <code>Connection.start</code> method
+ * is explicitly called.
+ *
+ * @param username the caller's user name
+ * @param password the caller's password
+ * @return A newly created queueConnection.
+ * @throws JMSException If creating the queueConnection fails due to some internal error.
+ * @throws JMSSecurityException If client authentication fails due to an invalid user name or password.
+ */
+ public QueueConnection createQueueConnection(String username, String password) throws JMSException
+ {
+ try
+ {
+ return new QueueConnectionImpl(_host, _port, _virtualHost, username, password);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("PRoblem when creating connection", e);
+ }
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+
+ //--- Interface TopicConnection
+ /**
+ * Creates a topicConnection with the default user identity.
+ * <p> The topicConnection is created in stopped mode. No messages
+ * will be delivered until the <code>Connection.start</code> method
+ * is explicitly called.
+ *
+ * @return A newly created topicConnection
+ * @throws JMSException If creating the topicConnection fails due to some internal error.
+ * @throws JMSSecurityException If client authentication fails due to an invalid user name or password.
+ */
+ public TopicConnection createTopicConnection() throws JMSException
+ {
+ try
+ {
+ return new TopicConnectionImpl(_host, _port, _virtualHost, _defaultUsername, _defaultPassword);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("PRoblem when creating connection", e);
+ }
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+
+ /**
+ * Creates a topicConnection with the specified user identity.
+ * <p> The topicConnection is created in stopped mode. No messages
+ * will be delivered until the <code>Connection.start</code> method
+ * is explicitly called.
+ *
+ * @param username the caller's user name
+ * @param password the caller's password
+ * @return A newly created topicConnection.
+ * @throws JMSException If creating the topicConnection fails due to some internal error.
+ * @throws JMSSecurityException If client authentication fails due to an invalid user name or password.
+ */
+ public TopicConnection createTopicConnection(String username, String password) throws JMSException
+ {
+ try
+ {
+ return new TopicConnectionImpl(_host, _port, _virtualHost, username, password);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("PRoblem when creating connection", e);
+ }
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+
+ // ---------------------------------------------------------------------------------------------------
+ // the following methods are provided for XA compatibility
+ // ---------------------------------------------------------------------------------------------------
+
+ /**
+ * Creates a XAConnection with the default user identity.
+ * <p> The XAConnection is created in stopped mode. No messages
+ * will be delivered until the <code>Connection.start</code> method
+ * is explicitly called.
+ *
+ * @return A newly created XAConnection
+ * @throws JMSException If creating the XAConnection fails due to some internal error.
+ * @throws JMSSecurityException If client authentication fails due to an invalid user name or password.
+ */
+ public XAConnection createXAConnection() throws JMSException
+ {
+ try
+ {
+ return new XAConnectionImpl(_host, _port, _virtualHost, _defaultUsername, _defaultPassword);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("PRoblem when creating connection", e);
+ }
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+
+ /**
+ * Creates a XAConnection with the specified user identity.
+ * <p> The XAConnection is created in stopped mode. No messages
+ * will be delivered until the <code>Connection.start</code> method
+ * is explicitly called.
+ *
+ * @param username the caller's user name
+ * @param password the caller's password
+ * @return A newly created XAConnection.
+ * @throws JMSException If creating the XAConnection fails due to some internal error.
+ * @throws JMSSecurityException If client authentication fails due to an invalid user name or password.
+ */
+ public XAConnection createXAConnection(String username, String password) throws JMSException
+ {
+ try
+ {
+ return new XAConnectionImpl(_host, _port, _virtualHost, username, password);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("PRoblem when creating connection", e);
+ }
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+
+
+ /**
+ * Creates a XATopicConnection with the default user identity.
+ * <p> The XATopicConnection is created in stopped mode. No messages
+ * will be delivered until the <code>Connection.start</code> method
+ * is explicitly called.
+ *
+ * @return A newly created XATopicConnection
+ * @throws JMSException If creating the XATopicConnection fails due to some internal error.
+ * @throws JMSSecurityException If client authentication fails due to an invalid user name or password.
+ */
+ public XATopicConnection createXATopicConnection() throws JMSException
+ {
+ try
+ {
+ return new XATopicConnectionImpl(_host, _port, _virtualHost, _defaultUsername, _defaultPassword);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("PRoblem when creating connection", e);
+ }
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+
+ /**
+ * Creates a XATopicConnection with the specified user identity.
+ * <p> The XATopicConnection is created in stopped mode. No messages
+ * will be delivered until the <code>Connection.start</code> method
+ * is explicitly called.
+ *
+ * @param username the caller's user name
+ * @param password the caller's password
+ * @return A newly created XATopicConnection.
+ * @throws JMSException If creating the XATopicConnection fails due to some internal error.
+ * @throws JMSSecurityException If client authentication fails due to an invalid user name or password.
+ */
+ public XATopicConnection createXATopicConnection(String username, String password) throws JMSException
+ {
+ try
+ {
+ return new XATopicConnectionImpl(_host, _port, _virtualHost, username, password);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("PRoblem when creating connection", e);
+ }
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+
+ /**
+ * Creates a XAQueueConnection with the default user identity.
+ * <p> The XAQueueConnection is created in stopped mode. No messages
+ * will be delivered until the <code>Connection.start</code> method
+ * is explicitly called.
+ *
+ * @return A newly created XAQueueConnection
+ * @throws JMSException If creating the XAQueueConnection fails due to some internal error.
+ * @throws JMSSecurityException If client authentication fails due to an invalid user name or password.
+ */
+ public XAQueueConnection createXAQueueConnection() throws JMSException
+ {
+ try
+ {
+ return new XAQueueConnectionImpl(_host, _port, _virtualHost, _defaultUsername, _defaultPassword);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("PRoblem when creating connection", e);
+ }
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+
+ /**
+ * Creates a XAQueueConnection with the specified user identity.
+ * <p> The XAQueueConnection is created in stopped mode. No messages
+ * will be delivered until the <code>Connection.start</code> method
+ * is explicitly called.
+ *
+ * @param username the caller's user name
+ * @param password the caller's password
+ * @return A newly created XAQueueConnection.
+ * @throws JMSException If creating the XAQueueConnection fails due to some internal error.
+ * @throws JMSSecurityException If client authentication fails due to an invalid user name or password.
+ */
+ public XAQueueConnection createXAQueueConnection(String username, String password) throws JMSException
+ {
+ try
+ {
+ return new XAQueueConnectionImpl(_host, _port, _virtualHost, username, password);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("PRoblem when creating connection", e);
+ }
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+
+ // ----------------------------------------
+ // 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 <code>nameCtx</code>,
+ * or null if no name is specified.
+ * @param nameCtx The context relative to which the <code>name</code>
+ * parameter is specified, or null if <code>name</code> 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(DestinationImpl.class.getName()))
+ {
+ RefAddr addr = ref.get(DestinationImpl.class.getName());
+
+ if (addr != null)
+ {
+ return new DestinationImpl(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(), _qpidURL.getURL()),
+ ConnectionFactoryImpl.class.getName(), null);
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/ConnectionImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/ConnectionImpl.java
new file mode 100644
index 0000000000..e1ad6eb991
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/ConnectionImpl.java
@@ -0,0 +1,503 @@
+/* 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.qpidity.njms;
+
+import java.util.Vector;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionConsumer;
+import javax.jms.ConnectionMetaData;
+import javax.jms.Destination;
+import javax.jms.ExceptionListener;
+import javax.jms.IllegalStateException;
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueSession;
+import javax.jms.ServerSessionPool;
+import javax.jms.Session;
+import javax.jms.Topic;
+import javax.jms.TopicSession;
+
+import org.apache.qpidity.QpidException;
+import org.apache.qpidity.url.QpidURL;
+import org.apache.qpidity.nclient.Client;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Implements javax.njms.Connection, javax.njms.QueueConnection and javax.njms.TopicConnection
+ */
+public class ConnectionImpl implements Connection
+{
+ /**
+ * This class's logger
+ */
+ private static final Logger _logger = LoggerFactory.getLogger(ConnectionImpl.class);
+
+ /**
+ * Maps from session id (Integer) to SessionImpl instance
+ */
+ protected final Vector<SessionImpl> _sessions = new Vector<SessionImpl>();
+
+ /**
+ * This is the clientID
+ */
+ private String _clientID;
+
+ /**
+ * The Exception listenr get informed when a serious problem is detected
+ */
+ private ExceptionListener _exceptionListener;
+
+ /**
+ * Whether this connection is started, i.e. whether messages are flowing to consumers.
+ * It has no meaning for message publication.
+ */
+ private boolean _started;
+
+ /**
+ * set to true if this Connection has been closed.
+ * <p/>
+ * A closed Connection cannot accept invocations to any of its methods with the exception
+ * of close(). All other methods should throw javax.njms.IllegalStateExceptions if the
+ * Connection has been closed.
+ * <p/>
+ * A Connection is open after creation, but not started. Once it has been closed, a Connection
+ * cannot be reused any more.
+ */
+ private boolean _isClosed = false;
+
+
+ /**
+ * The QpidConeection instance that is mapped with thie JMS connection
+ */
+ org.apache.qpidity.nclient.Connection _qpidConnection;
+
+ /**
+ * This is the exception listener for this qpid connection.
+ * The njms exception listener is registered with this listener.
+ */
+ QpidExceptionListenerImpl _qpidExceptionListener;
+
+ //------ Constructors ---//
+ /**
+ * Create a connection.
+ *
+ * @param host The broker host name.
+ * @param port The port on which the broker is listening for connection.
+ * @param virtualHost The virtual host on which the broker is deployed.
+ * @param username The user name used of user identification.
+ * @param password The password name used of user identification.
+ * @throws QpidException If creating a connection fails due to some internal error.
+ */
+ protected ConnectionImpl(String host, int port, String virtualHost, String username, String password)
+ throws QpidException
+ {
+ _qpidConnection = Client.createConnection();
+ _qpidConnection.connect(host, port, virtualHost, username, password);
+ }
+
+ /**
+ * 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.njms.Connection ---//
+ /**
+ * Creates a Session
+ *
+ * @param transacted Indicates whether the session is transacted.
+ * @param acknowledgeMode ignored if the session is transacted. Legal values are <code>Session.AUTO_ACKNOWLEDGE</code>,
+ * <code>Session.CLIENT_ACKNOWLEDGE</code>, and <code>Session.DUPS_OK_ACKNOWLEDGE</code>.
+ * @return A newly created session
+ * @throws JMSException If the Connection object fails to create a session due to some internal error.
+ */
+ public synchronized Session createSession(boolean transacted, int acknowledgeMode) throws JMSException
+ {
+ checkNotClosed();
+ SessionImpl session;
+ try
+ {
+ session = new SessionImpl(this, transacted, acknowledgeMode, false);
+ }
+ catch (QpidException e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ // add this session with the list of session that are handled by this connection
+ _sessions.add(session);
+ return session;
+ }
+
+ /**
+ * Gets the client identifier for this connection.
+ * <P>It is either preconfigured as a JNDI property or assigned dynamically by the application
+ * by calling the <code>setClientID</code> method.
+ * <p/>
+ * TODO: Make sure that the client identifier can be set on the <CODE>ConnectionFactory</CODE>
+ *
+ * @return The unique client identifier.
+ * @throws JMSException If this connection is closed.
+ */
+ public String getClientID() throws JMSException
+ {
+ checkNotClosed();
+ return _clientID;
+ }
+
+ /**
+ * Sets the client identifier for this connection.
+ * <P>The preferred way to assign a JMS client's client identifier is for
+ * it to be configured in a client-specific <CODE>ConnectionFactory</CODE>
+ * object and transparently assigned to the <CODE>Connection</CODE> object
+ * it creates.
+ * <p> 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 <CODE>ConnectionFactory</CODE>
+ *
+ * @param clientID the unique client identifier
+ * @throws JMSException Always as clientID is always set at construction time.
+ */
+ public void setClientID(String clientID) throws JMSException
+ {
+ checkNotClosed();
+ throw new IllegalStateException("Client name cannot be changed after being set");
+ }
+
+ /**
+ * Gets the metadata for this connection.
+ *
+ * @return The connection metadata
+ * @throws JMSException If there ie a problem getting the connection metadata for this connection.
+ * @see javax.jms.ConnectionMetaData
+ */
+ public ConnectionMetaData getMetaData() throws JMSException
+ {
+ checkNotClosed();
+ return ConnectionMetaDataImpl.getInstance();
+ }
+
+ /**
+ * Gets the <CODE>ExceptionListener</CODE> object for this connection.
+ *
+ * @return the <CODE>ExceptionListener</CODE> for this connection
+ * @throws JMSException In case of unforeseen problem
+ */
+ public synchronized ExceptionListener getExceptionListener() throws JMSException
+ {
+ checkNotClosed();
+ return _exceptionListener;
+ }
+
+ /**
+ * Sets an exception listener for this connection.
+ * <p/>
+ * <p> The JMS specification says:
+ * <P>If a JMS provider detects a serious problem with a connection, it
+ * informs the connection's <CODE>ExceptionListener</CODE>, if one has been
+ * registered. It does this by calling the listener's
+ * <CODE>onException</CODE> method, passing it a <CODE>JMSException</CODE>
+ * object describing the problem.
+ * <p/>
+ * <P>A connection serializes execution of its
+ * <CODE>ExceptionListener</CODE>.
+ * <p/>
+ * <P>A JMS provider should attempt to resolve connection problems
+ * itself before it notifies the client of them.
+ *
+ * @param exceptionListener The connection listener.
+ * @throws JMSException If the connection is closed.
+ */
+ public synchronized void setExceptionListener(ExceptionListener exceptionListener) throws JMSException
+ {
+ checkNotClosed();
+ _exceptionListener = exceptionListener;
+ _qpidExceptionListener.setJMSExceptionListner(_exceptionListener);
+ }
+
+ /**
+ * Starts (or restarts) a connection's delivery of incoming messages.
+ * A call to start on a connection that has already been
+ * started is ignored.
+ *
+ * @throws JMSException In case of a problem due to some internal error.
+ */
+ public synchronized void start() throws JMSException
+ {
+ checkNotClosed();
+ if (!_started)
+ {
+ // start all the sessions
+ for (SessionImpl session : _sessions)
+ {
+ try
+ {
+ session.start();
+ }
+ catch (Exception e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+ _started = true;
+ }
+ }
+
+ /**
+ * Temporarily stops a connection's delivery of incoming messages.
+ * <p> The JMS specification says:
+ * <p> Delivery can be restarted using the connection's <CODE>start</CODE>
+ * method. When the connection is stopped, delivery to all the connection's message consumers is inhibited:
+ * synchronous receives block, and messages are not delivered to message listeners.
+ * <P>This call blocks until receives and/or message listeners in progress have completed.
+ *
+ * @throws JMSException In case of a problem due to some internal error.
+ */
+ public synchronized void stop() throws JMSException
+ {
+ checkNotClosed();
+ if (_started)
+ {
+ // stop all the sessions
+ for (SessionImpl session : _sessions)
+ {
+ try
+ {
+ session.stop();
+ }
+ catch (Exception e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+ _started = false;
+ }
+ }
+
+ /**
+ * Closes the connection.
+ * <p/>
+ * <p> The JMS specification says:
+ * <P>Since a provider typically allocates significant resources outside
+ * the JVM on behalf of a connection, clients should close these resources
+ * when they are not needed. Relying on garbage collection to eventually
+ * reclaim these resources may not be timely enough.
+ * <P>There is no need to close the sessions, producers, and consumers of a closed connection.
+ * <P>Closing a connection causes all temporary destinations to be deleted.
+ * <P>When this method is invoked, it should not return until message
+ * processing has been shut down in an orderly fashion.
+ *
+ * @throws JMSException In case of a problem due to some internal error.
+ */
+ public synchronized void close() throws JMSException
+ {
+ checkNotClosed();
+ if (!_isClosed)
+ {
+ _isClosed = true;
+ _started = false;
+ // close all the sessions
+ for (SessionImpl session : _sessions)
+ {
+ session.close();
+ }
+ // close the underlaying Qpid connection
+ try
+ {
+ _qpidConnection.close();
+ }
+ catch (QpidException e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+ }
+
+ /**
+ * Creates a connection consumer for this connection (optional operation).
+ * This is an expert facility for App server integration.
+ *
+ * @param destination The destination to access.
+ * @param messageSelector Only messages with properties matching the message selector expression are delivered.
+ * @param sessionPool The session pool to associate with this connection consumer.
+ * @param maxMessages The maximum number of messages that can be assigned to a server session at one time.
+ * @return Null for the moment.
+ * @throws JMSException In case of a problem due to some internal error.
+ */
+ public ConnectionConsumer createConnectionConsumer(Destination destination, String messageSelector,
+ ServerSessionPool sessionPool, int maxMessages)
+ throws JMSException
+ {
+ checkNotClosed();
+ return null;
+ }
+
+ /**
+ * Create a durable connection consumer for this connection (optional operation).
+ *
+ * @param topic The topic to access.
+ * @param subscriptionName Durable subscription name.
+ * @param messageSelector Only messages with properties matching the message selector expression are delivered.
+ * @param sessionPool The server session pool to associate with this durable connection consumer.
+ * @param maxMessages The maximum number of messages that can be assigned to a server session at one time.
+ * @return Null for the moment.
+ * @throws JMSException In case of a problem due to some internal error.
+ */
+ public ConnectionConsumer createDurableConnectionConsumer(Topic topic, String subscriptionName,
+ String messageSelector, ServerSessionPool sessionPool,
+ int maxMessages) throws JMSException
+ {
+ checkNotClosed();
+ return null;
+ }
+
+ //-------------- QueueConnection API
+
+ /**
+ * Create a QueueSession.
+ *
+ * @param transacted Indicates whether the session is transacted.
+ * @param acknowledgeMode Indicates whether the consumer or the
+ * client will acknowledge any messages it receives; ignored if the session
+ * is transacted. Legal values are <code>Session.AUTO_ACKNOWLEDGE</code>,
+ * <code>Session.CLIENT_ACKNOWLEDGE</code> and <code>Session.DUPS_OK_ACKNOWLEDGE</code>.
+ * @return A queueSession object/
+ * @throws JMSException If creating a QueueSession fails due to some internal error.
+ */
+ public synchronized QueueSession createQueueSession(boolean transacted, int acknowledgeMode) throws JMSException
+ {
+ checkNotClosed();
+ QueueSessionImpl queueSession;
+ try
+ {
+ queueSession = new QueueSessionImpl(this, transacted, acknowledgeMode);
+ }
+ catch (QpidException e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ // add this session to the list of handled sessions.
+ _sessions.add(queueSession);
+ return queueSession;
+ }
+
+ /**
+ * Creates a connection consumer for this connection (optional operation).
+ * This is an expert facility for App server integration.
+ *
+ * @param queue The queue to access.
+ * @param messageSelector Only messages with properties matching the message selector expression are delivered.
+ * @param sessionPool The session pool to associate with this connection consumer.
+ * @param maxMessages The maximum number of messages that can be assigned to a server session at one time.
+ * @return Null for the moment.
+ * @throws JMSException In case of a problem due to some internal error.
+ */
+ public ConnectionConsumer createConnectionConsumer(Queue queue, String messageSelector,
+ ServerSessionPool sessionPool, int maxMessages)
+ throws JMSException
+ {
+ return createConnectionConsumer((Destination) queue, messageSelector, sessionPool, maxMessages);
+ }
+
+ //-------------- TopicConnection API
+ /**
+ * Create a TopicSession.
+ *
+ * @param transacted Indicates whether the session is transacted
+ * @param acknowledgeMode Legal values are <code>Session.AUTO_ACKNOWLEDGE</code>, <code>Session.CLIENT_ACKNOWLEDGE</code>, and
+ * <code>Session.DUPS_OK_ACKNOWLEDGE</code>.
+ * @return a newly created topic session
+ * @throws JMSException If creating the session fails due to some internal error.
+ */
+ public synchronized TopicSession createTopicSession(boolean transacted, int acknowledgeMode) throws JMSException
+ {
+ checkNotClosed();
+ TopicSessionImpl session;
+ try
+ {
+ session = new TopicSessionImpl(this, transacted, acknowledgeMode);
+ }
+ catch (QpidException e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ // add the session with this Connection's sessions
+ // important for when the Connection is closed.
+ _sessions.add(session);
+ return session;
+ }
+
+ /**
+ * Creates a connection consumer for this connection (optional operation).
+ * This is an expert facility for App server integration.
+ *
+ * @param topic The topic to access.
+ * @param messageSelector Only messages with properties matching the message selector expression are delivered.
+ * @param sessionPool The session pool to associate with this connection consumer.
+ * @param maxMessages The maximum number of messages that can be assigned to a server session at one time.
+ * @return Null for the moment.
+ * @throws JMSException In case of a problem due to some internal error.
+ */
+ public ConnectionConsumer createConnectionConsumer(Topic topic, String messageSelector,
+ ServerSessionPool sessionPool, int maxMessages)
+ throws JMSException
+ {
+ return createConnectionConsumer((Destination) topic, messageSelector, sessionPool, maxMessages);
+ }
+
+ //-------------- protected and private methods
+ /**
+ * Validate that the Connection is not closed.
+ * <p/>
+ * If the Connection has been closed, throw a IllegalStateException. This behaviour is
+ * required by the JMS specification.
+ *
+ * @throws IllegalStateException If the session is closed.
+ */
+ protected synchronized void checkNotClosed() throws IllegalStateException
+ {
+ if (_isClosed)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Connection has been closed. Cannot invoke any further operations.");
+ }
+ throw new javax.jms.IllegalStateException(
+ "Connection has been closed. Cannot invoke any further operations.");
+ }
+ }
+
+ /**
+ * Provide access to the underlying qpid Connection.
+ *
+ * @return This JMS connection underlying Qpid Connection.
+ */
+ protected org.apache.qpidity.nclient.Connection getQpidConnection()
+ {
+ return _qpidConnection;
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/ConnectionMetaDataImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/ConnectionMetaDataImpl.java
new file mode 100644
index 0000000000..9f2e9104e6
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/ConnectionMetaDataImpl.java
@@ -0,0 +1,165 @@
+/* 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.qpidity.njms;
+
+import org.apache.qpid.common.QpidProperties;
+
+import javax.jms.ConnectionMetaData;
+import javax.jms.JMSException;
+import java.util.Enumeration;
+
+/**
+ * Implements javax.njms.ConnectionMetaData
+ * A ConnectionMetaDataImpl provides information describing the JMS <code>Connection</code>.
+ */
+public class ConnectionMetaDataImpl implements ConnectionMetaData
+{
+
+ /**
+ * A singleton instance.
+ */
+ static ConnectionMetaDataImpl _singleton = new ConnectionMetaDataImpl();
+
+ // ------------------------ The metadata
+ // JMS major version
+ private static final int JMS_MAJOR_VERSION = 1;
+ // JMS minor version
+ private static final int JMS_MINOR_VERSION = 1;
+ // JMS version
+ private static final String JMS_VERSION = "1.1";
+ // Provider name
+ private static final String PROVIDER_NAME = "Apache " + QpidProperties.getProductName();
+ // Provider major version
+ private static final int PROVIDER_MAJOR_VERSION = 0;
+ // Provider minor version
+ private static final int PROVIDER_MINOR_VERSION = 10;
+ // Provider version
+ private static final String PROVIDER_VERSION = QpidProperties.getProductName() + " (Client: [" + QpidProperties.getBuildVersion() + "] ; Protocol: [ 0.10 ] )";
+
+ /**
+ * Prevent instantiation.
+ */
+ private ConnectionMetaDataImpl()
+ {
+ }
+
+ /**
+ * Get the singleton instance of ConnectionMetaDataImpl.
+ *
+ * @return the singleton instance of ConnectionMetaDataImpl.
+ */
+ public static ConnectionMetaDataImpl getInstance()
+ {
+ return _singleton;
+ }
+
+ //-- Connection MetaData API
+
+ /**
+ * Gets the JMS API version.
+ *
+ * @return the JMS API version
+ * @throws JMSException Never
+ */
+ public String getJMSVersion() throws JMSException
+ {
+ return JMS_VERSION;
+ }
+
+
+ /**
+ * Gets the JMS major version number.
+ *
+ * @return the JMS API major version number
+ * @throws JMSException Never
+ */
+ public int getJMSMajorVersion() throws JMSException
+ {
+ return JMS_MAJOR_VERSION;
+ }
+
+
+ /**
+ * Gets the JMS minor version number.
+ *
+ * @return the JMS API minor version number
+ * @throws JMSException Never
+ */
+ public int getJMSMinorVersion() throws JMSException
+ {
+ return JMS_MINOR_VERSION;
+ }
+
+
+ /**
+ * Gets Qpid name.
+ *
+ * @return Qpid name
+ * @throws JMSException Never
+ */
+ public String getJMSProviderName() throws JMSException
+ {
+ return PROVIDER_NAME;
+ }
+
+ /**
+ * Gets Qpid version.
+ *
+ * @return Qpid version
+ * @throws JMSException Never
+ */
+ public String getProviderVersion() throws JMSException
+ {
+ return PROVIDER_VERSION;
+ // TODO: We certainly can dynamically get the server version.
+ }
+
+ /**
+ * Gets Qpid major version number.
+ *
+ * @return Qpid major version number
+ * @throws JMSException Never
+ */
+ public int getProviderMajorVersion() throws JMSException
+ {
+ return PROVIDER_MAJOR_VERSION;
+ }
+
+ /**
+ * Gets Qpid minor version number.
+ *
+ * @return Qpid minor version number
+ * @throws JMSException Never
+ */
+ public int getProviderMinorVersion() throws JMSException
+ {
+ return PROVIDER_MINOR_VERSION;
+ }
+
+ /**
+ * Gets an enumeration of the JMSX property names.
+ *
+ * @return an Enumeration of JMSX property names
+ * @throws JMSException if cannot retrieve metadata due to some internal error.
+ */
+ public Enumeration getJMSXPropertyNames() throws JMSException
+ {
+ return CustomJMSXProperty.asEnumeration();
+ }
+
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/CustomJMSXProperty.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/CustomJMSXProperty.java
new file mode 100644
index 0000000000..c2a3af1c9b
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/CustomJMSXProperty.java
@@ -0,0 +1,47 @@
+/* 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.qpidity.njms;
+
+import java.util.Enumeration;
+import java.util.ArrayList;
+import java.util.Collections;
+
+public enum CustomJMSXProperty
+{
+ JMS_AMQP_NULL,
+ JMS_QPID_DESTTYPE,
+ JMSXGroupID,
+ JMSXGroupSeq;
+
+ private static Enumeration _names;
+
+ public static synchronized Enumeration asEnumeration()
+ {
+ if (_names == null)
+ {
+ CustomJMSXProperty[] properties = values();
+ ArrayList<String> nameList = new ArrayList<String>(properties.length);
+ for (CustomJMSXProperty property : properties)
+ {
+ nameList.add(property.toString());
+ }
+ _names = Collections.enumeration(nameList);
+ }
+ return _names;
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/DestinationImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/DestinationImpl.java
new file mode 100644
index 0000000000..247a41255e
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/DestinationImpl.java
@@ -0,0 +1,259 @@
+/* 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.qpidity.njms;
+
+import org.apache.qpidity.QpidException;
+import org.apache.qpidity.url.BindingURL;
+import org.apache.qpidity.url.BindingURLImpl;
+import org.apache.qpidity.url.URLSyntaxException;
+import org.apache.qpid.url.URLHelper;
+
+import javax.jms.Destination;
+import javax.naming.Reference;
+import javax.naming.NamingException;
+import javax.naming.StringRefAddr;
+import javax.naming.Referenceable;
+
+/**
+ * Implementation of the JMS Destination interface
+ */
+public class DestinationImpl implements Destination, Referenceable
+{
+ /**
+ * The destination's name
+ */
+ protected String _destinationName = null;
+
+ /**
+ * The excahnge name
+ */
+ protected String _exchangeName;
+
+ /**
+ * The excahnge class
+ */
+ protected String _exchangeType;
+
+ /**
+ * The queue name
+ */
+ protected String _queueName;
+
+ /**
+ * Indicate whether this destination is exclusive
+ */
+ protected boolean _isExclusive;
+
+ /**
+ * Indicates whether this destination is auto delete.
+ */
+ protected boolean _isAutoDelete;
+
+ /**
+ * Indicates whether this destination is durable
+ */
+ protected boolean _isDurable;
+
+ protected String _routingKey;
+
+ /**
+ * The biding URL used to create this destiantion
+ */
+ protected BindingURL _url;
+
+ //--- Constructor
+
+ protected DestinationImpl(String name) throws QpidException
+ {
+ _queueName = name;
+ _routingKey = name;
+ }
+
+ /**
+ * Create a destiantion from a binding URL
+ *
+ * @param binding The URL
+ * @throws QpidException If the URL is not valid
+ */
+ public DestinationImpl(BindingURL binding) throws QpidException
+ {
+ _exchangeName = binding.getExchangeName();
+ _exchangeType = binding.getExchangeClass();
+ _destinationName = binding.getDestinationName();
+ _isExclusive = Boolean.parseBoolean(binding.getOption(BindingURL.OPTION_EXCLUSIVE));
+ _isAutoDelete = Boolean.parseBoolean(binding.getOption(BindingURL.OPTION_AUTODELETE));
+ _isDurable = Boolean.parseBoolean(binding.getOption(BindingURL.OPTION_DURABLE));
+ _queueName = binding.getQueueName();
+ _routingKey = binding.getQueueName();
+ _url = binding;
+ }
+
+ //---- Getters and Setters
+ /**
+ * Overrides Object.toString();
+ *
+ * @return Stringified destination representation.
+ */
+ public String toString()
+ {
+ return _destinationName;
+ }
+
+ /**
+ * Get the destination name.
+ *
+ * @return The destination name
+ */
+ public String getDestinationName()
+ {
+ return _destinationName;
+ }
+
+
+ /**
+ * The exchange name
+ *
+ * @return The exchange name
+ */
+ public String getExchangeName()
+ {
+ return _exchangeName;
+ }
+
+ /**
+ * The exchange type.
+ *
+ * @return The exchange type.
+ */
+ public String getExchangeType()
+ {
+ return _exchangeType;
+ }
+
+ /**
+ * The queue name.
+ *
+ * @return The queue name.
+ */
+ public String getQpidQueueName()
+ {
+ return _queueName;
+ }
+
+ /**
+ * Indicates whether this destination is exclusive.
+ *
+ * @return true if this destination is exclusive.
+ */
+ public boolean isExclusive()
+ {
+ return _isExclusive;
+ }
+
+ /**
+ * Indicates whether this destination is AutoDelete.
+ *
+ * @return true if this destination is AutoDelete.
+ */
+ public boolean isAutoDelete()
+ {
+ return _isAutoDelete;
+ }
+
+ public String getRoutingKey()
+ {
+ return _routingKey;
+ }
+
+ /**
+ * Indicates whether this destination is Durable.
+ *
+ * @return true if this destination is Durable.
+ */
+ public boolean isDurable()
+ {
+ return _isDurable;
+ }
+
+ //----- Interface Referenceable
+ public Reference getReference() throws NamingException
+ {
+ return new Reference(this.getClass().getName(), new StringRefAddr(this.getClass().getName(), toURL()),
+ ConnectionFactoryImpl.class.getName(), // factory
+ null); // factory location
+ }
+
+ //--- non public method s
+
+ /**
+ * Get the URL used to create this destiantion
+ *
+ * @return The URL used to create this destiantion
+ */
+ public String toURL()
+ {
+ if (_url == null)
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append(_exchangeType);
+ sb.append("://");
+ sb.append(_exchangeName);
+ sb.append('/');
+ if (_destinationName != null)
+ {
+ sb.append(_destinationName);
+ }
+ sb.append('/');
+ if (_queueName != null)
+ {
+ sb.append(_queueName);
+ }
+ sb.append('?');
+ if (_isDurable)
+ {
+ sb.append(org.apache.qpid.url.BindingURL.OPTION_DURABLE);
+ sb.append("='true'");
+ sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR);
+ }
+ if (_isExclusive)
+ {
+ sb.append(org.apache.qpid.url.BindingURL.OPTION_EXCLUSIVE);
+ sb.append("='true'");
+ sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR);
+ }
+ if (_isAutoDelete)
+ {
+ sb.append(org.apache.qpid.url.BindingURL.OPTION_AUTODELETE);
+ sb.append("='true'");
+ sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR);
+ }
+ //removeKey the last char '?' if there is no options , ',' if there are.
+ sb.deleteCharAt(sb.length() - 1);
+ try
+ {
+ _url = new BindingURLImpl(sb.toString());
+ }
+ catch (URLSyntaxException e)
+ {
+ // this should not happen.
+ }
+ }
+ return _url.getURL();
+ }
+}
+
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/ExceptionHelper.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/ExceptionHelper.java
new file mode 100644
index 0000000000..d3b2e88f23
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/ExceptionHelper.java
@@ -0,0 +1,59 @@
+/* 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.qpidity.njms;
+
+import org.apache.qpidity.QpidException;
+
+import javax.jms.JMSException;
+import javax.transaction.xa.XAException;
+
+/**
+ * Helper class for handling exceptions
+ */
+public class ExceptionHelper
+{
+ static public JMSException convertQpidExceptionToJMSException(Exception exception)
+ {
+ JMSException jmsException = null;
+ if (!(exception instanceof JMSException))
+ {
+ if (exception instanceof QpidException)
+ {
+ jmsException = new JMSException(exception.getMessage(), String.valueOf(((QpidException) exception).getErrorCode()));
+ }
+ else
+ {
+ jmsException = new JMSException(exception.getMessage());
+ }
+ jmsException.setLinkedException(exception);
+ }
+ else
+ {
+ jmsException = (JMSException) exception;
+ }
+ return jmsException;
+ }
+
+ static public XAException convertQpidExceptionToXAException(QpidException exception)
+ {
+ String qpidErrorCode = String.valueOf(exception.getErrorCode());
+ // todo map this error to an XA code
+ int xaCode = XAException.XAER_PROTO;
+ return new XAException(xaCode);
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/MessageActor.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/MessageActor.java
new file mode 100644
index 0000000000..698bccdeb5
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/MessageActor.java
@@ -0,0 +1,176 @@
+/* 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.qpidity.njms;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.IllegalStateException;
+import javax.jms.JMSException;
+
+/**
+ * MessageActor is the superclass for MessageProducerImpl and MessageProducerImpl.
+ */
+public abstract class MessageActor
+{
+ /**
+ * Used for debugging.
+ */
+ protected static final Logger _logger = LoggerFactory.getLogger(MessageActor.class);
+
+ /**
+ * Indicates whether this MessageActor is closed.
+ */
+ protected boolean _isClosed = false;
+
+ /**
+ * This messageActor's session
+ */
+ private SessionImpl _session;
+
+ /**
+ * The JMS destination this actor is set for.
+ */
+ DestinationImpl _destination;
+
+ /**
+ * Indicates that this actor is stopped
+ */
+ protected boolean _isStopped;
+
+ /**
+ * The ID of this actor for the session.
+ */
+ private String _messageActorID;
+
+ //-- Constructor
+
+ //TODO define the parameters
+
+ protected MessageActor(String messageActorID)
+ {
+ _messageActorID = messageActorID;
+ }
+
+ protected MessageActor(SessionImpl session, DestinationImpl destination,String messageActorID)
+ {
+ _session = session;
+ _destination = destination;
+ _messageActorID = messageActorID;
+ }
+
+ //--- public methods (part of the njms public API)
+ /**
+ * Closes the MessageActor and deregister it from its session.
+ *
+ * @throws JMSException if the MessaeActor cannot be closed due to some internal error.
+ */
+ public void close() throws JMSException
+ {
+ if (!_isClosed)
+ {
+ closeMessageActor();
+ getSession().getQpidSession().messageCancel(getMessageActorID());
+ //todo: We need to unset the qpid message listener
+ // notify the session that this message actor is closing
+ _session.closeMessageActor(this);
+ }
+ }
+
+ //-- protected methods
+
+ /**
+ * Stop this message actor
+ *
+ * @throws Exception If the consumer cannot be stopped due to some internal error.
+ */
+ protected void stop() throws Exception
+ {
+ _isStopped = true;
+ }
+
+ /**
+ * Start this message Actor
+ *
+ * @throws Exception If the consumer cannot be started due to some internal error.
+ */
+ protected void start() throws Exception
+ {
+
+ _isStopped = false;
+
+ }
+
+ /**
+ * Check if this MessageActor is not closed.
+ * <p> If the MessageActor is closed, throw a javax.njms.IllegalStateException.
+ * <p> The method is not synchronized, since MessageProducers can only be used by a single thread.
+ *
+ * @throws IllegalStateException if the MessageActor is closed
+ */
+ protected void checkNotClosed() throws IllegalStateException
+ {
+ if (_isClosed || _session == null)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Actor " + this + " is already closed");
+ }
+ throw new IllegalStateException("Actor " + this + " is already closed");
+ }
+ _session.checkNotClosed();
+ }
+
+ /**
+ * Closes a MessageActor.
+ * <p> This method is invoked when the session is closing or when this
+ * messageActor is closing.
+ *
+ * @throws JMSException If the MessaeActor cannot be closed due to some internal error.
+ */
+ protected void closeMessageActor() throws JMSException
+ {
+ if (!_isClosed)
+ {
+ getSession().getQpidSession().messageCancel(getMessageActorID());
+ _isClosed = true;
+ }
+ }
+
+ /**
+ * Get the associated session object.
+ *
+ * @return This Actor's Session.
+ */
+ public SessionImpl getSession()
+ {
+ return _session;
+ }
+
+ /**
+ * Get the ID of this actor within its session.
+ *
+ * @return This actor ID.
+ */
+ protected String getMessageActorID()
+ {
+ return _messageActorID;
+ }
+
+
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/MessageConsumerImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/MessageConsumerImpl.java
new file mode 100644
index 0000000000..7f55dcbd67
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/MessageConsumerImpl.java
@@ -0,0 +1,662 @@
+/* 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.qpidity.njms;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.Queue;
+
+import org.apache.qpidity.QpidException;
+import org.apache.qpidity.nclient.MessagePartListener;
+import org.apache.qpidity.nclient.util.MessagePartListenerAdapter;
+import org.apache.qpidity.exchange.ExchangeDefaults;
+import org.apache.qpidity.filter.JMSSelectorFilter;
+import org.apache.qpidity.filter.MessageFilter;
+import org.apache.qpidity.njms.message.MessageFactory;
+import org.apache.qpidity.njms.message.QpidMessage;
+import org.apache.qpidity.transport.Option;
+import org.apache.qpidity.transport.RangeSet;
+
+/**
+ * Implementation of JMS message consumer
+ */
+public class MessageConsumerImpl extends MessageActor
+ implements MessageConsumer, org.apache.qpidity.nclient.util.MessageListener
+{
+ // we can receive up to 100 messages for an asynchronous listener
+ public static final int MAX_MESSAGE_TRANSFERRED = 100;
+
+ /**
+ * This MessageConsumer's messageselector.
+ */
+ private String _messageSelector = null;
+
+ /**
+ * The message selector filter associated with this consumer message selector
+ */
+ private MessageFilter _filter = null;
+
+ /**
+ * NoLocal
+ * If true, and the destination is a topic then inhibits the delivery of messages published
+ * by its own connection. The behavior for NoLocal is not specified if the destination is a queue.
+ */
+ protected boolean _noLocal;
+
+ /**
+ * The subscription name
+ */
+ protected String _subscriptionName;
+
+ /**
+ * Indicates whether this consumer receives pre-acquired messages
+ */
+ private boolean _preAcquire = true;
+
+ /**
+ * A MessagePartListener set up for this consumer.
+ */
+ private MessageListener _messageListener;
+
+ /**
+ * A lcok on the syncrhonous message
+ */
+ private final Object _incomingMessageLock = new Object();
+
+
+ /**
+ * Number of mesages received asynchronously
+ * Nether exceed MAX_MESSAGE_TRANSFERRED
+ */
+ private int _messageAsyncrhonouslyReceived = 0;
+
+ private LinkedBlockingQueue<QpidMessage> _queue = new LinkedBlockingQueue<QpidMessage>();
+
+ //----- Constructors
+ /**
+ * Create a new MessageProducerImpl.
+ *
+ * @param session The session from which the MessageProducerImpl is instantiated
+ * @param destination The default destination for this MessageProducerImpl
+ * @param messageSelector The message selector for this QueueReceiverImpl.
+ * @param noLocal If true inhibits the delivery of messages published by its own connection.
+ * @param subscriptionName Name of the subscription if this is to be created as a durable subscriber.
+ * If this value is null, a non-durable subscription is created.
+ * @param consumerTag Thi actor ID
+ * @throws Exception If the MessageProducerImpl cannot be created due to some internal error.
+ */
+ protected MessageConsumerImpl(SessionImpl session, DestinationImpl destination, String messageSelector,
+ boolean noLocal, String subscriptionName, String consumerTag) throws Exception
+ {
+ super(session, destination, consumerTag);
+ if (messageSelector != null)
+ {
+ _messageSelector = messageSelector;
+ _filter = new JMSSelectorFilter(messageSelector);
+ }
+ _noLocal = noLocal;
+ _subscriptionName = subscriptionName;
+ _isStopped = getSession().isStopped();
+ // let's create a message part assembler
+
+ MessagePartListener messageAssembler = new MessagePartListenerAdapter(this);
+
+ if (destination instanceof Queue)
+ {
+ // this is a queue we expect that this queue exists
+ getSession().getQpidSession()
+ .messageSubscribe(destination.getQpidQueueName(), // queue
+ getMessageActorID(), // destination
+ org.apache.qpidity.nclient.Session.TRANSFER_CONFIRM_MODE_NOT_REQUIRED,
+ // When the message selctor is set we do not acquire the messages
+ _messageSelector != null ? org.apache.qpidity.nclient.Session.TRANSFER_ACQUIRE_MODE_NO_ACQUIRE : org.apache.qpidity.nclient.Session.TRANSFER_ACQUIRE_MODE_PRE_ACQUIRE,
+ messageAssembler, null, _noLocal ? Option.NO_LOCAL : Option.NO_OPTION);
+ if (_messageSelector != null)
+ {
+ _preAcquire = false;
+ }
+ }
+ else
+ {
+ // this is a topic we need to create a temporary queue for this consumer
+ // unless this is a durable subscriber
+ String queueName;
+ if (subscriptionName != null)
+ {
+ // this ia a durable subscriber
+ // create a persistent queue for this subscriber
+ queueName = "topic-" + subscriptionName;
+ getSession().getQpidSession()
+ .queueDeclare(queueName, null, null, Option.EXCLUSIVE, Option.DURABLE);
+ }
+ else
+ {
+ // this is a non durable subscriber
+ queueName = destination.getQpidQueueName();
+ getSession().getQpidSession()
+ .queueDeclare(queueName, null, null, Option.AUTO_DELETE, Option.EXCLUSIVE);
+ }
+ // bind this queue with the topic exchange
+ getSession().getQpidSession()
+ .queueBind(queueName, ExchangeDefaults.TOPIC_EXCHANGE_NAME, destination.getRoutingKey(), null);
+ // subscribe to this topic
+ getSession().getQpidSession()
+ .messageSubscribe(queueName, getMessageActorID(),
+ org.apache.qpidity.nclient.Session.TRANSFER_CONFIRM_MODE_NOT_REQUIRED,
+ // We always acquire the messages
+ org.apache.qpidity.nclient.Session.TRANSFER_ACQUIRE_MODE_PRE_ACQUIRE,
+ messageAssembler, null, _noLocal ? Option.NO_LOCAL : Option.NO_OPTION,
+ // Request exclusive subscription access, meaning only this subscription
+ // can access the queue.
+ Option.EXCLUSIVE);
+
+ }
+ // set the flow mode
+ getSession().getQpidSession()
+ .messageFlowMode(getMessageActorID(), org.apache.qpidity.nclient.Session.MESSAGE_FLOW_MODE_CREDIT);
+
+ // this will prevent the broker from sending more than one message
+ // When a messageListener is set the flow will be adjusted.
+ // until then we assume it's for synchronous message consumption
+ requestCredit(1);
+ requestSync();
+ // check for an exception
+ if (getSession().getCurrentException() != null)
+ {
+ throw getSession().getCurrentException();
+ }
+ }
+
+ //----- Message consumer API
+ /**
+ * Gets this MessageConsumer's message selector.
+ *
+ * @return This MessageConsumer's message selector, or null if no
+ * message selector exists for the message consumer (that is, if
+ * the message selector was not set or was set to null or the
+ * empty string)
+ * @throws JMSException if getting the message selector fails due to some internal error.
+ */
+ public String getMessageSelector() throws JMSException
+ {
+ checkNotClosed();
+ return _messageSelector;
+ }
+
+ /**
+ * Gets this MessageConsumer's <CODE>MessagePartListener</CODE>.
+ *
+ * @return The listener for the MessageConsumer, or null if no listener is set
+ * @throws JMSException if getting the message listener fails due to some internal error.
+ */
+ public MessageListener getMessageListener() throws JMSException
+ {
+ checkNotClosed();
+ return _messageListener;
+ }
+
+ /**
+ * Sets the MessageConsumer's <CODE>MessagePartListener</CODE>.
+ * <p> The JMS specification says:
+ * <P>Setting the message listener to null is the equivalent of
+ * unsetting the message listener for the message consumer.
+ * <P>The effect of calling <CODE>MessageConsumer.setMessageListener</CODE>
+ * while messages are being consumed by an existing listener
+ * or the consumer is being used to consume messages synchronously
+ * is undefined.
+ *
+ * @param messageListener The listener to which the messages are to be delivered
+ * @throws JMSException If setting the message listener fails due to some internal error.
+ */
+ public synchronized void setMessageListener(MessageListener messageListener) throws JMSException
+ {
+ // this method is synchronized as onMessage also access _messagelistener
+ // onMessage, getMessageListener and this method are the only synchronized methods
+ checkNotClosed();
+ try
+ {
+ _messageListener = messageListener;
+ if (messageListener != null)
+ {
+ resetAsynchMessageReceived();
+ }
+ }
+ catch (Exception e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+
+ /**
+ * Contact the broker and ask for the delivery of MAX_MESSAGE_TRANSFERRED messages
+ *
+ * @throws QpidException If there is a communication error
+ */
+ private void resetAsynchMessageReceived() throws QpidException
+ {
+ if (!_isStopped && _messageAsyncrhonouslyReceived >= MAX_MESSAGE_TRANSFERRED)
+ {
+ getSession().getQpidSession().messageStop(getMessageActorID());
+ }
+ _messageAsyncrhonouslyReceived = 0;
+ requestCredit(MAX_MESSAGE_TRANSFERRED);
+ }
+
+ /**
+ * Receive the next message produced for this message consumer.
+ * <P>This call blocks indefinitely until a message is produced or until this message consumer is closed.
+ *
+ * @return The next message produced for this message consumer, or
+ * null if this message consumer is concurrently closed
+ * @throws JMSException If receiving the next message fails due to some internal error.
+ */
+ public Message receive() throws JMSException
+ {
+ // Check if we can get a message immediately
+ Message result;
+ result = receiveNoWait();
+ if (result != null)
+ {
+ return result;
+ }
+ try
+ {
+ // Now issue a credit and wait for the broker to send a message
+ // IMO no point doing a credit() flush() and sync() in a loop.
+ // This will only overload the broker. After the initial try we can wait
+ // for the broker to send a message when it gets one
+ requestCredit(1);
+ return (Message) _queue.take();
+ }
+ catch (Exception e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+
+ /**
+ * Receive the next message that arrives within the specified timeout interval.
+ * <p> This call blocks until a message arrives, the timeout expires, or this message consumer
+ * is closed.
+ * <p> A timeout of zero never expires, and the call blocks indefinitely.
+ * <p> A timeout less than 0 throws a JMSException.
+ *
+ * @param timeout The timeout value (in milliseconds)
+ * @return The next message that arrives within the specified timeout interval.
+ * @throws JMSException If receiving the next message fails due to some internal error.
+ */
+ public Message receive(long timeout) throws JMSException
+ {
+ checkClosed();
+ checkIfListenerSet();
+ if (timeout < 0)
+ {
+ throw new JMSException("Invalid timeout value: " + timeout);
+ }
+
+ Message result;
+ try
+ {
+ // first check if we have any in the queue already
+ result = (Message) _queue.poll();
+ if (result == null)
+ {
+ requestCredit(1);
+ requestFlush();
+ // We shouldn't do a sync(). Bcos the timeout can happen
+ // before the sync() returns
+ return (Message) _queue.poll(timeout, TimeUnit.MILLISECONDS);
+ }
+ else
+ {
+ return result;
+ }
+ }
+ catch (Exception e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+
+ /**
+ * Receive the next message if one is immediately available.
+ *
+ * @return the next message or null if one is not available.
+ * @throws JMSException If receiving the next message fails due to some internal error.
+ */
+ public Message receiveNoWait() throws JMSException
+ {
+ checkClosed();
+ checkIfListenerSet();
+ Message result;
+ try
+ {
+ // first check if we have any in the queue already
+ result = (Message) _queue.poll();
+ if (result == null)
+ {
+ requestCredit(1);
+ requestFlush();
+ requestSync();
+ return (Message) _queue.poll();
+ }
+ else
+ {
+ return result;
+ }
+ }
+ catch (Exception e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+
+ // not public methods
+ /**
+ * Upon receipt of this method, the broker adds "value"
+ * number of messages to the available credit balance for this consumer.
+ *
+ * @param value Number of credits, a value of 0 indicates an infinite amount of credit.
+ */
+ private void requestCredit(int value)
+ {
+ getSession().getQpidSession()
+ .messageFlow(getMessageActorID(), org.apache.qpidity.nclient.Session.MESSAGE_FLOW_UNIT_MESSAGE, value);
+ }
+
+ /**
+ * Forces the broker to exhaust its credit supply.
+ * <p> The broker's credit will always be zero when
+ * this method completes.
+ */
+ private void requestFlush()
+ {
+ getSession().getQpidSession().messageFlush(getMessageActorID());
+ }
+
+ /**
+ * Sync method will block until all outstanding broker
+ * commands
+ * are executed.
+ */
+ private void requestSync()
+ {
+ getSession().getQpidSession().sync();
+ }
+
+ /**
+ * Check whether this consumer is closed.
+ *
+ * @throws JMSException If this consumer is closed.
+ */
+ private void checkClosed() throws JMSException
+ {
+ if (_isStopped)
+ {
+ throw new JMSException("Session is closed");
+ }
+ }
+
+ /**
+ * Stop the delivery of messages to this consumer.
+ * <p>For asynchronous receiver, this operation blocks until the message listener
+ * finishes processing the current message,
+ *
+ * @throws Exception If the consumer cannot be stopped due to some internal error.
+ */
+ protected void stop() throws Exception
+ {
+ getSession().getQpidSession().messageStop(getMessageActorID());
+ _isStopped = true;
+ }
+
+ /**
+ * Start the delivery of messages to this consumer.
+ *
+ * @throws Exception If the consumer cannot be started due to some internal error.
+ */
+ protected void start() throws Exception
+ {
+ synchronized (_incomingMessageLock)
+ {
+ _isStopped = false;
+ }
+ }
+
+ /**
+ * This method notifies this consumer that a message has been delivered
+ * @param message The received message.
+ */
+ public void onMessage(org.apache.qpidity.api.Message message)
+ {
+ try
+ {
+ QpidMessage jmsMessage = MessageFactory.getQpidMessage(message);
+ if (checkPreConditions(jmsMessage))
+ {
+ preApplicationProcessing(jmsMessage);
+
+ if (_messageListener == null)
+ {
+ _queue.offer(jmsMessage);
+ }
+ else
+ {
+ // I still think we don't need that additional thread in SessionImpl
+ // if the Application blocks on a message thats fine
+ // getSession().dispatchMessage(getMessageActorID(), jmsMessage);
+ notifyMessageListener(jmsMessage);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+
+ public void notifyMessageListener(QpidMessage message) throws RuntimeException
+ {
+ try
+ {
+ _messageAsyncrhonouslyReceived++;
+ if (_messageAsyncrhonouslyReceived >= MAX_MESSAGE_TRANSFERRED)
+ {
+ // ask the server for the delivery of MAX_MESSAGE_TRANSFERRED more messages
+ resetAsynchMessageReceived();
+ }
+
+ // The JMS specs says:
+ /* The result of a listener throwing a RuntimeException depends on the session?s
+ * acknowledgment mode.
+ ? --- AUTO_ACKNOWLEDGE or DUPS_OK_ACKNOWLEDGE - the message
+ * will be immediately redelivered. The number of times a JMS provider will
+ * redeliver the same message before giving up is provider-dependent.
+ ? --- CLIENT_ACKNOWLEDGE - the next message for the listener is delivered.
+ * --- Transacted Session - the next message for the listener is delivered.
+ *
+ * The number of time we try redelivering the message is 0
+ **/
+ try
+ {
+
+ _messageListener.onMessage((Message) message);
+ }
+ catch (RuntimeException re)
+ {
+ // do nothing as this message will not be redelivered
+ }
+
+
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+ /**
+ * Check whether this consumer is asynchronous
+ *
+ * @throws javax.jms.IllegalStateException If this consumer is asynchronous.
+ */
+ private void checkIfListenerSet() throws javax.jms.IllegalStateException
+ {
+
+ if (_messageListener != null)
+ {
+ throw new javax.jms.IllegalStateException("A listener has already been set.");
+ }
+ }
+
+ /**
+ * pre process a received message.
+ *
+ * @param message The message to pre-process.
+ * @throws Exception If the message cannot be pre-processed due to some internal error.
+ */
+ private void preApplicationProcessing(QpidMessage message) throws Exception
+ {
+ getSession().preProcessMessage(message);
+ // If the session is transacted we need to ack the message first
+ // This is because a message is associated with its tx only when acked
+ if (getSession().getTransacted())
+ {
+ getSession().acknowledgeMessage(message);
+ }
+ message.afterMessageReceive();
+ }
+
+ /**
+ * Check whether a message can be delivered to this consumer.
+ *
+ * @param message The message to be checked.
+ * @return true if the message matches the selector and can be acquired, false otherwise.
+ * @throws QpidException If the message preConditions cannot be checked due to some internal error.
+ */
+ private boolean checkPreConditions(QpidMessage message) throws QpidException
+ {
+ boolean messageOk = true;
+ if (_messageSelector != null)
+ {
+ messageOk = _filter.matches((Message) message);
+ }
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("messageOk " + messageOk);
+ _logger.debug("_preAcquire " + _preAcquire);
+ }
+ if (!messageOk && _preAcquire)
+ {
+ // this is the case for topics
+ // We need to ack this message
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("filterMessage - trying to ack message");
+ }
+ acknowledgeMessage(message);
+ }
+ else if (!messageOk)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Message not OK, releasing");
+ }
+ releaseMessage(message);
+ }
+ // now we need to acquire this message if needed
+ // this is the case of queue with a message selector set
+ if (!_preAcquire && messageOk)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("filterMessage - trying to acquire message");
+ }
+ messageOk = acquireMessage(message);
+ }
+ return messageOk;
+ }
+
+ /**
+ * Release a message
+ *
+ * @param message The message to be released
+ * @throws QpidException If the message cannot be released due to some internal error.
+ */
+ private void releaseMessage(QpidMessage message) throws QpidException
+ {
+ if (_preAcquire)
+ {
+ RangeSet ranges = new RangeSet();
+ ranges.add(message.getMessageTransferId());
+ getSession().getQpidSession().messageRelease(ranges);
+ getSession().testQpidException();
+ }
+ }
+
+ /**
+ * Acquire a message
+ *
+ * @param message The message to be acquired
+ * @return true if the message has been acquired, false otherwise.
+ * @throws QpidException If the message cannot be acquired due to some internal error.
+ */
+ private boolean acquireMessage(QpidMessage message) throws QpidException
+ {
+ boolean result = false;
+ if (!_preAcquire)
+ {
+ RangeSet ranges = new RangeSet();
+ ranges.add(message.getMessageTransferId());
+
+ getSession().getQpidSession()
+ .messageAcquire(ranges, org.apache.qpidity.nclient.Session.MESSAGE_ACQUIRE_ANY_AVAILABLE_MESSAGE);
+ getSession().getQpidSession().sync();
+ RangeSet acquired = getSession().getQpidSession().getAccquiredMessages();
+ if (acquired.size() > 0)
+ {
+ result = true;
+ }
+ getSession().testQpidException();
+ }
+ return result;
+ }
+
+ /**
+ * Acknowledge a message
+ *
+ * @param message The message to be acknowledged
+ * @throws QpidException If the message cannot be acquired due to some internal error.
+ */
+ private void acknowledgeMessage(QpidMessage message) throws QpidException
+ {
+ if (!_preAcquire)
+ {
+ RangeSet ranges = new RangeSet();
+ ranges.add(message.getMessageTransferId());
+ getSession().getQpidSession().messageAcknowledge(ranges);
+ getSession().testQpidException();
+ }
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/MessageProducerImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/MessageProducerImpl.java
new file mode 100644
index 0000000000..a61f4f1431
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/MessageProducerImpl.java
@@ -0,0 +1,384 @@
+/* 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.qpidity.njms;
+
+import org.apache.qpidity.njms.message.MessageHelper;
+import org.apache.qpidity.njms.message.MessageImpl;
+import org.apache.qpidity.QpidException;
+
+import javax.jms.*;
+import java.util.UUID;
+import java.io.IOException;
+
+/**
+ * Implements MessageProducer
+ */
+public class MessageProducerImpl extends MessageActor implements MessageProducer
+{
+ /**
+ * If true, messages will not get a timestamp.
+ */
+ private boolean _disableTimestamps = false;
+
+ /**
+ * Priority of messages created by this producer.
+ */
+ private int _messagePriority = Message.DEFAULT_PRIORITY;
+
+ /**
+ * Time to live of messages. Specified in milliseconds but AMQ has 1 second resolution.
+ */
+ private long _timeToLive;
+
+ /**
+ * Delivery mode used for this producer.
+ */
+ private int _deliveryMode = DeliveryMode.PERSISTENT;
+
+ /**
+ * Speicify whether the messageID is disable
+ */
+ private boolean _disableMessageId = false;
+
+ //-- constructors
+ public MessageProducerImpl(SessionImpl session, DestinationImpl destination)
+ {
+ super(session, destination,"");
+ }
+
+ //--- Interface javax.njms.MessageProducer
+ /**
+ * Sets whether message IDs are disabled.
+ *
+ * @param value Specify whether the MessageID must be disabled
+ * @throws JMSException If disabling messageID fails due to some internal error.
+ */
+ public void setDisableMessageID(boolean value) throws JMSException
+ {
+ checkNotClosed();
+ _disableMessageId = value;
+ }
+
+ /**
+ * Gets an indication of whether message IDs are disabled.
+ *
+ * @return true is messageID is disabled, false otherwise
+ * @throws JMSException If getting whether messagID is disabled fails due to some internal error.
+ */
+ public boolean getDisableMessageID() throws JMSException
+ {
+ checkNotClosed();
+ return _disableMessageId;
+ }
+
+ /**
+ * Sets whether message timestamps are disabled.
+ * <P> JMS spec says:
+ * <p> Since timestamps take some effort to create and increase a
+ * message's size, some JMS providers may be able to optimize message
+ * overhead if they are given a hint that the timestamp is not used by an
+ * application....
+ * these messages must have the timestamp set to zero; if the provider
+ * ignores the hint, the timestamp must be set to its normal value.
+ * <p>Message timestamps are enabled by default.
+ *
+ * @param value Indicates if message timestamps are disabled
+ * @throws JMSException if disabling the timestamps fails due to some internal error.
+ */
+ public void setDisableMessageTimestamp(boolean value) throws JMSException
+ {
+ checkNotClosed();
+ _disableTimestamps = value;
+ }
+
+ /**
+ * Gets an indication of whether message timestamps are disabled.
+ *
+ * @return an indication of whether message timestamps are disabled
+ * @throws JMSException if getting whether timestamps are disabled fails due to some internal error.
+ */
+ public boolean getDisableMessageTimestamp() throws JMSException
+ {
+ checkNotClosed();
+ return _disableTimestamps;
+ }
+
+ /**
+ * Sets the producer's default delivery mode.
+ * <p> JMS specification says:
+ * <p>Delivery mode is set to {@link DeliveryMode#PERSISTENT} by default.
+ *
+ * @param deliveryMode The message delivery mode for this message producer; legal
+ * values are {@link DeliveryMode#NON_PERSISTENT}
+ * and {@link DeliveryMode#PERSISTENT}.
+ * @throws JMSException if setting the delivery mode fails due to some internal error.
+ */
+ public void setDeliveryMode(int deliveryMode) throws JMSException
+ {
+ checkNotClosed();
+ if ((deliveryMode != DeliveryMode.NON_PERSISTENT) && (deliveryMode != DeliveryMode.PERSISTENT))
+ {
+ throw new JMSException(
+ "DeliveryMode must be either NON_PERSISTENT or PERSISTENT. Value of " + deliveryMode + " is illegal");
+ }
+ _deliveryMode = deliveryMode;
+ }
+
+ /**
+ * Gets the producer's delivery mode.
+ *
+ * @return The message delivery mode for this message producer
+ * @throws JMSException If getting the delivery mode fails due to some internal error.
+ */
+ public int getDeliveryMode() throws JMSException
+ {
+ checkNotClosed();
+ return _deliveryMode;
+ }
+
+ /**
+ * Sets the producer's message priority.
+ * <p> The njms spec says:
+ * <p> The JMS API defines ten levels of priority value, with 0 as the
+ * lowest priority and 9 as the highest. Clients should consider priorities
+ * 0-4 as gradations of normal priority and priorities 5-9 as gradations
+ * of expedited priority.
+ * <p> Priority is set to 4 by default.
+ *
+ * @param priority The message priority for this message producer; must be a value between 0 and 9
+ * @throws JMSException if setting this producer priority fails due to some internal error.
+ */
+ public void setPriority(int priority) throws JMSException
+ {
+ checkNotClosed();
+ if ((priority < 0) || (priority > 9))
+ {
+ throw new IllegalArgumentException(
+ "Priority of " + priority + " is illegal. Value must be in range 0 to 9");
+ }
+ _messagePriority = priority;
+ }
+
+ /**
+ * Gets the producer's message priority.
+ *
+ * @return The message priority for this message producer.
+ * @throws JMSException If getting this producer message priority fails due to some internal error.
+ */
+ public int getPriority() throws JMSException
+ {
+ checkNotClosed();
+ return _messagePriority;
+ }
+
+ /**
+ * Sets the default length of time in milliseconds from its dispatch time
+ * that a produced message should be retained by the message system.
+ * <p> The JMS spec says that time to live must be set to zero by default.
+ *
+ * @param timeToLive The message time to live in milliseconds; zero is unlimited
+ * @throws JMSException If setting the default time to live fails due to some internal error.
+ */
+ public void setTimeToLive(long timeToLive) throws JMSException
+ {
+ checkNotClosed();
+ if (timeToLive < 0)
+ {
+ throw new IllegalArgumentException("Time to live must be non-negative - supplied value was " + timeToLive);
+ }
+ _timeToLive = timeToLive;
+ }
+
+ /**
+ * Gets the default length of time in milliseconds from its dispatch time
+ * that a produced message should be retained by the message system.
+ *
+ * @return The default message time to live in milliseconds; zero is unlimited
+ * @throws JMSException if getting the default time to live fails due to some internal error.
+ * @see javax.jms.MessageProducer#setTimeToLive
+ */
+ public long getTimeToLive() throws JMSException
+ {
+ checkNotClosed();
+ return _timeToLive;
+ }
+
+ /**
+ * Gets the destination associated with this producer.
+ *
+ * @return This producer's destination.
+ * @throws JMSException If getting the destination for this producer fails
+ * due to some internal error.
+ */
+ public Destination getDestination() throws JMSException
+ {
+ checkNotClosed();
+ return _destination;
+ }
+
+ /**
+ * Sends a message using the producer's default delivery mode, priority, destination
+ * and time to live.
+ *
+ * @param message the message to be sent
+ * @throws JMSException If sending the message fails due to some internal error.
+ * @throws MessageFormatException If an invalid message is specified.
+ * @throws InvalidDestinationException If this producer destination is invalid.
+ * @throws java.lang.UnsupportedOperationException
+ * If a client uses this method with a producer that did
+ * not specify a destination at creation time.
+ */
+ public void send(Message message) throws JMSException
+ {
+ send(message, _deliveryMode, _messagePriority, _timeToLive);
+ }
+
+ /**
+ * Sends a message to this producer default destination, specifying delivery mode,
+ * priority, and time to live.
+ *
+ * @param message The message to send
+ * @param deliveryMode The delivery mode to use
+ * @param priority The priority for this message
+ * @param timeToLive The message's lifetime (in milliseconds)
+ * @throws JMSException If sending the message fails due to some internal error.
+ * @throws MessageFormatException If an invalid message is specified.
+ * @throws InvalidDestinationException If this producer's destination is invalid.
+ * @throws java.lang.UnsupportedOperationException
+ * If a client uses this method with a producer that did
+ * not specify a destination at creation time.
+ */
+ public void send(Message message, int deliveryMode, int priority, long timeToLive) throws JMSException
+ {
+ send(_destination, message, deliveryMode, priority, timeToLive);
+ }
+
+ /**
+ * Sends a message to a specified destination using this producer's default
+ * delivery mode, priority and time to live.
+ * <p/>
+ * <P>Typically, a message producer is assigned a destination at creation
+ * time; however, the JMS API also supports unidentified message producers,
+ * which require that the destination be supplied every time a message is
+ * sent.
+ *
+ * @param destination The destination to send this message to
+ * @param message The message to send
+ * @throws JMSException If sending the message fails due to some internal error.
+ * @throws MessageFormatException If an invalid message is specified.
+ * @throws InvalidDestinationException If an invalid destination is specified.
+ */
+ public void send(Destination destination, Message message) throws JMSException
+ {
+ send(destination, message, _deliveryMode, _messagePriority, _timeToLive);
+ }
+
+ /**
+ * Sends a message to a destination specifying delivery mode, priority and time to live.
+ *
+ * @param destination The destination to send this message to.
+ * @param message The message to be sent.
+ * @param deliveryMode The delivery mode to use.
+ * @param priority The priority for this message.
+ * @param timeToLive The message's lifetime (in milliseconds)
+ * @throws JMSException If sending the message fails due to some internal error.
+ * @throws MessageFormatException If an invalid message is specified.
+ * @throws InvalidDestinationException If an invalid destination is specified.
+ */
+ public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive)
+ throws JMSException
+ {
+ checkNotClosed();
+ getSession().checkDestination(destination);
+ // Do not allow negative timeToLive values
+ if (timeToLive < 0)
+ {
+ throw new IllegalArgumentException("Time to live must be non-negative - supplied value was " + timeToLive);
+ }
+ // Only get current time if required
+ long currentTime = Long.MIN_VALUE;
+ if (!((timeToLive == 0) && _disableTimestamps))
+ {
+ currentTime = System.currentTimeMillis();
+ }
+ // the messae UID
+ String uid = (_disableMessageId) ? "MSG_ID_DISABLED" : UUID.randomUUID().toString();
+ MessageImpl qpidMessage;
+ // check that the message is not a foreign one
+ try
+ {
+ qpidMessage = (MessageImpl) message;
+ }
+ catch (ClassCastException cce)
+ {
+ // this is a foreign message
+ qpidMessage = MessageHelper.transformMessage(message);
+ // set message's properties in case they are queried after send.
+ message.setJMSDestination(destination);
+ message.setJMSDeliveryMode(deliveryMode);
+ message.setJMSPriority(priority);
+ message.setJMSMessageID(uid);
+ if (timeToLive != 0)
+ {
+ message.setJMSExpiration(timeToLive + currentTime);
+ _logger.debug("Setting JMSExpiration:" + message.getJMSExpiration());
+ }
+ else
+ {
+ message.setJMSExpiration(timeToLive);
+ }
+ message.setJMSTimestamp(currentTime);
+ }
+ // set the message properties
+ qpidMessage.setJMSDestination(destination);
+ qpidMessage.setJMSMessageID(uid);
+ qpidMessage.setJMSDeliveryMode(deliveryMode);
+ qpidMessage.setJMSPriority(priority);
+ if (timeToLive != 0)
+ {
+ qpidMessage.setJMSExpiration(timeToLive + currentTime);
+ }
+ else
+ {
+ qpidMessage.setJMSExpiration(timeToLive);
+ }
+ qpidMessage.setJMSTimestamp(currentTime);
+ qpidMessage.setRoutingKey(((DestinationImpl) destination).getDestinationName());
+ qpidMessage.setExchangeName(((DestinationImpl) destination).getExchangeName());
+ // call beforeMessageDispatch
+ try
+ {
+ qpidMessage.beforeMessageDispatch();
+ }
+ catch (QpidException e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ try
+ {
+ getSession().getQpidSession().messageTransfer(qpidMessage.getExchangeName(),
+ qpidMessage.getQpidityMessage(),
+ org.apache.qpidity.nclient.Session.TRANSFER_CONFIRM_MODE_NOT_REQUIRED,
+ org.apache.qpidity.nclient.Session.TRANSFER_ACQUIRE_MODE_PRE_ACQUIRE);
+ }
+ catch (IOException e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/QpidBrowserListener.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/QpidBrowserListener.java
new file mode 100644
index 0000000000..59e772db85
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/QpidBrowserListener.java
@@ -0,0 +1,70 @@
+/* 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.qpidity.njms;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.qpidity.api.Message;
+import org.apache.qpidity.nclient.util.MessageListener;
+
+/**
+ * This listener idspatches messaes to its browser.
+ */
+public class QpidBrowserListener implements MessageListener
+{
+ /**
+ * Used for debugging.
+ */
+ private static final Logger _logger = LoggerFactory.getLogger(SessionImpl.class);
+
+ /**
+ * This message listener's browser.
+ */
+ QueueBrowserImpl _browser = null;
+
+ //---- constructor
+ /**
+ * Create a message listener wrapper for a given browser
+ *
+ * @param browser The browser of this listener
+ */
+ public QpidBrowserListener(QueueBrowserImpl browser)
+ {
+ _browser = browser;
+ }
+
+ //---- org.apache.qpidity.MessagePartListener API
+ /**
+ * Deliver a message to the listener.
+ *
+ * @param message The message delivered to the listner.
+ */
+ public void onMessage(Message message)
+ {
+ try
+ {
+ //convert this message into a JMS one
+ javax.jms.Message jmsMessage = null; // todo
+ _browser.receiveMessage(jmsMessage);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/QpidExceptionListenerImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/QpidExceptionListenerImpl.java
new file mode 100644
index 0000000000..0b6068f05d
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/QpidExceptionListenerImpl.java
@@ -0,0 +1,51 @@
+/* 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.qpidity.njms;
+
+import org.apache.qpidity.QpidException;
+
+import javax.jms.JMSException;
+
+/**
+ * An exception listner
+ */
+public class QpidExceptionListenerImpl //implements ExceptionListener
+{
+ private javax.jms.ExceptionListener _jmsExceptionListener;
+
+ public QpidExceptionListenerImpl()
+ {
+ }
+
+ void setJMSExceptionListner(javax.jms.ExceptionListener jmsExceptionListener)
+ {
+ _jmsExceptionListener = jmsExceptionListener;
+ }
+ //----- ExceptionListener API
+
+ public void onException(QpidException exception)
+ {
+ // convert this exception in a JMS exception
+ JMSException jmsException = ExceptionHelper.convertQpidExceptionToJMSException(exception);
+ // propagate to the njms exception listener
+ if (_jmsExceptionListener != null)
+ {
+ _jmsExceptionListener.onException(jmsException);
+ }
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueBrowserImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueBrowserImpl.java
new file mode 100644
index 0000000000..4478048528
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueBrowserImpl.java
@@ -0,0 +1,256 @@
+/* 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.qpidity.njms;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.Queue;
+import javax.jms.QueueBrowser;
+
+import org.apache.qpidity.nclient.MessagePartListener;
+import org.apache.qpidity.nclient.util.MessagePartListenerAdapter;
+import org.apache.qpidity.filter.JMSSelectorFilter;
+import org.apache.qpidity.filter.MessageFilter;
+
+/**
+ * Implementation of the JMS QueueBrowser interface
+ */
+public class QueueBrowserImpl extends MessageActor implements QueueBrowser
+{
+ /**
+ * The browsers MessageSelector.
+ */
+ private String _messageSelector = null;
+
+ /**
+ * The message selector filter associated with this browser
+ */
+ private MessageFilter _filter = null;
+
+ /**
+ * The batch of messages to browse.
+ */
+ private Message[] _messages;
+
+ /**
+ * The number of messages read from current batch.
+ */
+ private int _browsed = 0;
+
+ /**
+ * The number of messages received from current batch.
+ */
+ private int _received = 0;
+
+ /**
+ * Indicates whether the last message has been received.
+ */
+ private int _batchLength;
+
+ /**
+ * The batch max size
+ */
+ private final int _maxbatchlength = 10;
+
+ //--- constructor
+
+ /**
+ * Create a QueueBrowser for a specific queue and a given message selector.
+ *
+ * @param session The session of this browser.
+ * @param queue The queue name for this browser
+ * @param messageSelector only messages with properties matching the message selector expression are delivered.
+ * @throws Exception In case of internal problem when creating this browser.
+ */
+ protected QueueBrowserImpl(SessionImpl session, Queue queue, String messageSelector,String consumerTag) throws Exception
+ {
+ super(session, (DestinationImpl) queue,consumerTag);
+ // this is an array representing a batch of messages for this browser.
+ _messages = new Message[_maxbatchlength];
+ if (messageSelector != null)
+ {
+ _messageSelector = messageSelector;
+ _filter = new JMSSelectorFilter(messageSelector);
+ }
+ MessagePartListener messageAssembler = new MessagePartListenerAdapter(new QpidBrowserListener(this));
+ // this is a queue we expect that this queue exists
+ getSession().getQpidSession()
+ .messageSubscribe(queue.getQueueName(), getMessageActorID(),
+ org.apache.qpidity.nclient.Session.TRANSFER_CONFIRM_MODE_NOT_REQUIRED,
+ // We do not acquire those messages
+ org.apache.qpidity.nclient.Session.TRANSFER_ACQUIRE_MODE_NO_ACQUIRE, messageAssembler, null);
+
+ }
+
+ //--- javax.njms.QueueBrowser API
+ /**
+ * Get an enumeration for browsing the current queue messages in the order they would be received.
+ *
+ * @return An enumeration for browsing the messages
+ * @throws JMSException If getting the enumeration for this browser fails due to some internal error.
+ */
+ public Enumeration getEnumeration() throws JMSException
+ {
+ requestMessages();
+ return new MessageEnumeration();
+ }
+
+
+ /**
+ * Get the queue associated with this queue browser.
+ *
+ * @return The queue associated with this queue browser.
+ * @throws JMSException If getting the queue associated with this browser failts due to some internal error.
+ */
+ public Queue getQueue() throws JMSException
+ {
+ checkNotClosed();
+ return (Queue) _destination;
+ }
+
+ /**
+ * Get this queue browser's message selector expression.
+ *
+ * @return This queue browser's message selector, or null if no message selector exists.
+ * @throws JMSException if getting the message selector for this browser fails due to some internal error.
+ */
+ public String getMessageSelector() throws JMSException
+ {
+ checkNotClosed();
+ return _messageSelector;
+ }
+
+ //-- overwritten methods.
+ /**
+ * Closes the browser and deregister it from its session.
+ *
+ * @throws JMSException if the MessaeActor cannot be closed due to some internal error.
+ */
+ public void close() throws JMSException
+ {
+ synchronized (_messages)
+ {
+ _received = 0;
+ _browsed = 0;
+ _batchLength = 0;
+ _messages.notify();
+ }
+ super.close();
+ }
+
+ //-- nonpublic methods
+ /**
+ * Request _maxbatchlength messages
+ *
+ * @throws JMSException If requesting more messages fails due to some internal error.
+ */
+ private void requestMessages() throws JMSException
+ {
+ _browsed = 0;
+ _received = 0;
+ // request messages
+ int received = 0;
+ getSession().getQpidSession()
+ .messageFlow(getMessageActorID(), org.apache.qpidity.nclient.Session.MESSAGE_FLOW_UNIT_MESSAGE,
+ _maxbatchlength);
+ _batchLength = 0; //getSession().getQpidSession().messageFlush(getMessageActorID());
+ }
+
+ /**
+ * This method is invoked by the listener when a message is dispatched to this browser.
+ *
+ * @param m A received message
+ */
+ protected void receiveMessage(Message m)
+ {
+ synchronized (_messages)
+ {
+ _messages[_received] = m;
+ _received++;
+ _messages.notify();
+ }
+ }
+
+ //-- inner class
+ /**
+ * This is an implementation of the Enumeration interface.
+ */
+ private class MessageEnumeration implements Enumeration
+ {
+ /*
+ * Whether this enumeration has any more elements.
+ *
+ * @return True if there any more elements.
+ */
+ public boolean hasMoreElements()
+ {
+ boolean result = false;
+ // Try to work out whether there are any more messages available.
+ try
+ {
+ if (_browsed >= _maxbatchlength)
+ {
+ requestMessages();
+ }
+ synchronized (_messages)
+ {
+ while (_received == _browsed && _batchLength > _browsed)
+ {
+ // we expect more messages
+ _messages.wait();
+ }
+ if (_browsed < _received && _batchLength != _browsed)
+ {
+ result = true;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ // If no batch could be returned, the result should be false, therefore do nothing
+ }
+ return result;
+ }
+
+ /**
+ * Get the next message element
+ *
+ * @return The next element.
+ */
+ public Object nextElement()
+ {
+ if (hasMoreElements())
+ {
+ synchronized (_messages)
+ {
+ Message message = _messages[_browsed];
+ _browsed = _browsed + 1;
+ return message;
+ }
+ }
+ else
+ {
+ throw new NoSuchElementException();
+ }
+ }
+ }
+
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueConnectionImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueConnectionImpl.java
new file mode 100644
index 0000000000..cab683b0a4
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueConnectionImpl.java
@@ -0,0 +1,36 @@
+/* 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.qpidity.njms;
+
+import org.apache.qpidity.njms.ConnectionImpl;
+import org.apache.qpidity.QpidException;
+
+import javax.jms.QueueConnection;
+
+/**
+ *
+ * Implements javax.njms.QueueConnection
+ */
+public class QueueConnectionImpl extends ConnectionImpl implements QueueConnection
+{
+ //-- constructor
+ public QueueConnectionImpl(String host,int port,String virtualHost,String username,String password) throws QpidException
+ {
+ super(host, port, virtualHost, username, password);
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueImpl.java
new file mode 100644
index 0000000000..43e8e9e43f
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueImpl.java
@@ -0,0 +1,136 @@
+/* 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.qpidity.njms;
+
+import org.apache.qpidity.QpidException;
+import org.apache.qpidity.transport.Option;
+import org.apache.qpidity.url.BindingURL;
+import org.apache.qpidity.exchange.ExchangeDefaults;
+
+import javax.jms.Queue;
+import javax.jms.JMSException;
+
+/**
+ * Implementation of the JMS Queue interface
+ */
+public class QueueImpl extends DestinationImpl implements Queue
+{
+ //--- Constructor
+ /**
+ * Create a new QueueImpl with a given name.
+ *
+ * @param name The name of this queue.
+ * @param session The session used to create this queue.
+ * @throws QpidException If the queue name is not valid
+ */
+ protected QueueImpl(SessionImpl session, String name) throws QpidException
+ {
+ super(name);
+ _queueName = name;
+ _destinationName = name;
+ _exchangeName = ExchangeDefaults.DIRECT_EXCHANGE_NAME;
+ _exchangeType = ExchangeDefaults.DIRECT_EXCHANGE_CLASS;
+ _isAutoDelete = false;
+ _isDurable = true;
+ _isExclusive = false;
+ registerQueue(session, false);
+ }
+
+ /**
+ * Create a destiantion from a binding URL
+ *
+ * @param session The session used to create this queue.
+ * @param binding The URL
+ * @throws QpidException If the URL is not valid
+ */
+ protected QueueImpl(SessionImpl session, BindingURL binding) throws QpidException
+ {
+ super(binding);
+ registerQueue(session, false);
+ }
+
+ /**
+ * Create a destiantion from a binding URL
+ *
+ * @param binding The URL
+ * @throws QpidException If the URL is not valid
+ */
+ public QueueImpl(BindingURL binding) throws QpidException
+ {
+ super(binding);
+ }
+
+ /**
+ * Create a new QueueImpl with a given name.
+ *
+ * @param name The name of this queue.
+ * @throws QpidException If the queue name is not valid
+ */
+ public QueueImpl(String name) throws QpidException
+ {
+ super(name);
+ _queueName = name;
+ _destinationName = name;
+ _exchangeName = ExchangeDefaults.DIRECT_EXCHANGE_NAME;
+ _exchangeType = ExchangeDefaults.DIRECT_EXCHANGE_CLASS;
+ _isAutoDelete = false;
+ _isDurable = true;
+ _isExclusive = false;
+ }
+
+ //---- Interface javax.njms.Queue
+ /**
+ * Gets the name of this queue.
+ *
+ * @return This queue's name.
+ */
+ public String getQueueName() throws JMSException
+ {
+ return _queueName;
+ }
+
+ //---Private methods
+ /**
+ * Check that this queue exists and declare it if required.
+ *
+ * @param session The session used to create this destination
+ * @param declare Specify whether the queue should be declared
+ * @throws QpidException If this queue does not exists on the broker.
+ */
+ protected void registerQueue(SessionImpl session, boolean declare) throws QpidException
+ {
+ // test if this exchange exist on the broker
+ //todo we can also specify if the excahnge is autodlete and durable
+ session.getQpidSession().exchangeDeclare(_exchangeName, _exchangeType, null, null, Option.PASSIVE);
+ // wait for the broker response
+ session.getQpidSession().sync();
+ // If this exchange does not exist then we will get an Expection 404 does notexist
+ //todo check for an execption
+ // now check if the queue exists
+ session.getQpidSession().queueDeclare(_queueName, null, null, _isDurable ? Option.DURABLE : Option.NO_OPTION,
+ _isAutoDelete ? Option.AUTO_DELETE : Option.NO_OPTION,
+ _isExclusive ? Option.EXCLUSIVE : Option.NO_OPTION,
+ declare ? Option.PASSIVE : Option.NO_OPTION);
+ // wait for the broker response
+ session.getQpidSession().sync();
+ // If this queue does not exist then we will get an Expection 404 does notexist
+ session.getQpidSession().queueBind(_queueName, _exchangeName, _destinationName, null);
+ // we don't have to sync as we don't expect an error
+ }
+
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueReceiverImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueReceiverImpl.java
new file mode 100644
index 0000000000..67db7e5001
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueReceiverImpl.java
@@ -0,0 +1,55 @@
+/* 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.qpidity.njms;
+
+import javax.jms.QueueReceiver;
+import javax.jms.JMSException;
+import javax.jms.Queue;
+
+/**
+ * Implements javax.njms.QueueReceiver
+ */
+public class QueueReceiverImpl extends MessageConsumerImpl implements QueueReceiver
+{
+ //--- Constructor
+ /**
+ * create a new QueueReceiverImpl.
+ *
+ * @param session The session from which the QueueReceiverImpl is instantiated.
+ * @param queue The default queue for this QueueReceiverImpl.
+ * @param messageSelector the message selector for this QueueReceiverImpl.
+ * @throws Exception If the QueueReceiverImpl cannot be created due to some internal error.
+ */
+ protected QueueReceiverImpl(SessionImpl session, Queue queue, String messageSelector,String consumerTag) throws Exception
+ {
+ super(session, (DestinationImpl) queue, messageSelector, false, null,consumerTag);
+ }
+
+ //--- Interface QueueReceiver
+ /**
+ * Get the Queue associated with this queue receiver.
+ *
+ * @return this receiver's Queue
+ * @throws JMSException If getting the queue for this queue receiver fails due to some internal error.
+ */
+ public Queue getQueue() throws JMSException
+ {
+ checkNotClosed();
+ return (QueueImpl) _destination;
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueSenderImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueSenderImpl.java
new file mode 100644
index 0000000000..d03bb3a6b7
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueSenderImpl.java
@@ -0,0 +1,131 @@
+/* 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.qpidity.njms;
+
+import javax.jms.QueueSender;
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.Message;
+
+/**
+ * Implements javax.njms.QueueSender
+ */
+public class QueueSenderImpl extends MessageProducerImpl implements QueueSender
+{
+ //--- Constructor
+ /**
+ * Create a new QueueSenderImpl.
+ *
+ * @param session the session from which the QueueSenderImpl is instantiated
+ * @param queue the default queue for this QueueSenderImpl
+ * @throws JMSException If the QueueSenderImpl cannot be created due to some internal error.
+ */
+ protected QueueSenderImpl(SessionImpl session, QueueImpl queue) throws JMSException
+ {
+ super(session, queue);
+ }
+
+ //--- Interface javax.njms.QueueSender
+ /**
+ * Get the queue associated with this QueueSender.
+ *
+ * @return This QueueSender's queue
+ * @throws JMSException If getting the queue for this QueueSender fails due to some internal error.
+ */
+ public Queue getQueue() throws JMSException
+ {
+ return (Queue) getDestination();
+ }
+
+ /**
+ * Sends a message to the queue. Uses the <CODE>QueueSender</CODE>'s default delivery mode, priority,
+ * and time to live.
+ *
+ * @param message The message to send.
+ * @throws JMSException if sending the message fails due to some internal error.
+ * @throws javax.jms.MessageFormatException
+ * If an invalid message is specified.
+ * @throws javax.jms.InvalidDestinationException
+ * If the queue is invalid.
+ * @throws java.lang.UnsupportedOperationException
+ * If invoked on QueueSender that did not specify a queue at creation time.
+ */
+ public void send(Message message) throws JMSException
+ {
+ super.send(message);
+ }
+
+ /**
+ * Send a message to the queue, specifying delivery mode, priority, and time to live.
+ *
+ * @param message The message to send
+ * @param deliveryMode The delivery mode to use
+ * @param priority The priority for this message
+ * @param timeToLive The message's lifetime (in milliseconds)
+ *
+ * @throws JMSException if sending the message fails due to some internal error.
+ * @throws javax.jms.MessageFormatException
+ * If an invalid message is specified.
+ * @throws javax.jms.InvalidDestinationException
+ * If the queue is invalid.
+ * @throws java.lang.UnsupportedOperationException
+ * If invoked on QueueSender that did not specify a queue at creation time.
+ */
+ public void send(Message message, int deliveryMode, int priority, long timeToLive) throws JMSException
+ {
+ super.send(message, deliveryMode, priority, timeToLive);
+ }
+
+ /**
+ * Send a message to a queue for an unidentified message producer.
+ * Uses the <CODE>QueueSender</CODE>'s default delivery mode, priority,
+ * and time to live.
+ *
+ * @param queue The queue to send this message to
+ * @param message The message to send
+ * @throws JMSException if sending the message fails due to some internal error.
+ * @throws javax.jms.MessageFormatException
+ * If an invalid message is specified.
+ * @throws javax.jms.InvalidDestinationException
+ * If the queue is invalid.
+ */
+ public void send(Queue queue, Message message) throws JMSException
+ {
+ super.send(queue, message);
+ }
+
+ /**
+ * Sends a message to a queue for an unidentified message producer,
+ * specifying delivery mode, priority and time to live.
+ *
+ * @param queue The queue to send this message to
+ * @param message The message to send
+ * @param deliveryMode The delivery mode to use
+ * @param priority The priority for this message
+ * @param timeToLive The message's lifetime (in milliseconds)
+ * @throws JMSException if sending the message fails due to some internal error.
+ * @throws javax.jms.MessageFormatException
+ * If an invalid message is specified.
+ * @throws javax.jms.InvalidDestinationException
+ * If the queue is invalid.
+ */
+ public void send(Queue queue, Message message, int deliveryMode, int priority, long timeToLive) throws JMSException
+ {
+ super.send(queue, message, deliveryMode, priority, timeToLive);
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueSessionImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueSessionImpl.java
new file mode 100644
index 0000000000..9ae0725f00
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/QueueSessionImpl.java
@@ -0,0 +1,154 @@
+/* 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.qpidity.njms;
+
+import javax.jms.*;
+import javax.jms.IllegalStateException;
+
+import org.apache.qpidity.QpidException;
+
+/**
+ * Implementation of javax.njms.QueueSession
+ */
+public class QueueSessionImpl extends SessionImpl implements QueueSession
+{
+ //--- constructor
+ /**
+ * Create a JMS Session
+ *
+ * @param connection The ConnectionImpl object from which the Session is created.
+ * @param transacted Indicates if the session transacted.
+ * @param acknowledgeMode The session's acknowledgement mode. This value is ignored and set to
+ * {@link javax.jms.Session#SESSION_TRANSACTED} if the <code>transacted</code>
+ * parameter is true.
+ * @throws javax.jms.JMSSecurityException If the user could not be authenticated.
+ * @throws javax.jms.JMSException In case of internal error.
+ */
+ protected QueueSessionImpl(ConnectionImpl connection, boolean transacted, int acknowledgeMode) throws QpidException, JMSException
+ {
+ super(connection, transacted, acknowledgeMode,false);
+ }
+
+ //-- Overwritten methods
+ /**
+ * Creates a durable subscriber to the specified topic,
+ *
+ * @param topic The non-temporary <CODE>Topic</CODE> to subscribe to.
+ * @param name The name used to identify this subscription.
+ * @return Always throws an exception
+ * @throws IllegalStateException Always
+ */
+ @Override
+ public TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException
+ {
+ throw new IllegalStateException("Cannot invoke createDurableSubscriber from QueueSession");
+ }
+
+ /**
+ * Create a TemporaryTopic.
+ *
+ * @return Always throws an exception
+ * @throws IllegalStateException Always
+ */
+ @Override
+ public TemporaryTopic createTemporaryTopic() throws JMSException
+ {
+ throw new IllegalStateException("Cannot invoke createTemporaryTopic from QueueSession");
+ }
+
+ /**
+ * Creates a topic identity given a Topicname.
+ *
+ * @param topicName The name of this <CODE>Topic</CODE>
+ * @return Always throws an exception
+ * @throws IllegalStateException Always
+ */
+ @Override
+ public Topic createTopic(String topicName) throws JMSException
+ {
+ throw new IllegalStateException("Cannot invoke createTopic from QueueSession");
+ }
+
+ /**
+ * Unsubscribes a durable subscription that has been created by a client.
+ *
+ * @param name the name used to identify this subscription
+ * @throws IllegalStateException Always
+ */
+ @Override
+ public void unsubscribe(String name) throws JMSException
+ {
+ throw new IllegalStateException("Cannot invoke unsubscribe from QueueSession");
+ }
+
+ //--- Interface javax.njms.QueueSession
+ /**
+ * Create a QueueReceiver to receive messages from the specified queue.
+ *
+ * @param queue the <CODE>Queue</CODE> to access
+ * @return A QueueReceiver
+ * @throws JMSException If creating a receiver fails due to some internal error.
+ * @throws InvalidDestinationException If an invalid queue is specified.
+ */
+ public QueueReceiver createReceiver(Queue queue) throws JMSException
+ {
+ return createReceiver(queue, null);
+ }
+
+ /**
+ * Create a QueueReceiver to receive messages from the specified queue for a given message selector.
+ *
+ * @param queue the Queue to access
+ * @param messageSelector A value of null or an empty string indicates that
+ * there is no message selector for the message consumer.
+ * @return A QueueReceiver
+ * @throws JMSException If creating a receiver fails due to some internal error.
+ * @throws InvalidDestinationException If an invalid queue is specified.
+ * @throws InvalidSelectorException If the message selector is invalid.
+ */
+ public QueueReceiver createReceiver(Queue queue, String messageSelector) throws JMSException
+ {
+ checkNotClosed();
+ checkDestination(queue);
+ QueueReceiver receiver;
+ try
+ {
+ receiver = new QueueReceiverImpl(this, queue, messageSelector,String.valueOf(_consumerTag.incrementAndGet()));
+ }
+ catch (Exception e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ return receiver;
+ }
+
+ /**
+ * Create a QueueSender object to send messages to the specified queue.
+ *
+ * @param queue the Queue to access, or null if this is an unidentified producer
+ * @return A QueueSender
+ * @throws JMSException If creating the sender fails due to some internal error.
+ * @throws InvalidDestinationException If an invalid queue is specified.
+ */
+ public QueueSender createSender(Queue queue) throws JMSException
+ {
+ checkNotClosed();
+ // we do not check the destination since unidentified producers are allowed (no default destination).
+ return new QueueSenderImpl(this, (QueueImpl) queue);
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/SessionImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/SessionImpl.java
new file mode 100644
index 0000000000..a7c1d00b72
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/SessionImpl.java
@@ -0,0 +1,1324 @@
+/* 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.qpidity.njms;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.qpidity.njms.message.*;
+import org.apache.qpidity.QpidException;
+import org.apache.qpidity.transport.RangeSet;
+
+import javax.jms.*;
+import javax.jms.IllegalStateException;
+import java.io.Serializable;
+import java.util.LinkedList;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Implementation of the JMS Session interface
+ */
+public class SessionImpl implements Session
+{
+ /**
+ * this session's logger
+ */
+ private static final Logger _logger = LoggerFactory.getLogger(SessionImpl.class);
+
+ /**
+ * A queue for incoming asynch messages.
+ */
+ private final LinkedList<IncomingMessage> _incomingAsynchronousMessages = new LinkedList<IncomingMessage>();
+
+ //--- MessageDispatcherThread and Session locking
+ /**
+ * indicates that the MessageDispatcherThread has stopped
+ */
+ private boolean _hasStopped = false;
+
+ /**
+ * lock for the MessageDispatcherThread to wait until the session is stopped
+ */
+ private final Object _stoppingLock = new Object();
+
+ /**
+ * lock for the stopper thread to wait on when the MessageDispatcherThread is stopping
+ */
+ private final Object _stoppingJoin = new Object();
+
+ /**
+ * thread to dispatch messages to async consumers
+ */
+ private MessageDispatcherThread _messageDispatcherThread = null;
+ //----END
+
+ /**
+ * The messageActors of this session.
+ */
+ private final HashMap<String, MessageActor> _messageActors = new HashMap<String, MessageActor>();
+
+ /**
+ * All the not yet acknoledged messages
+ */
+ private final ArrayList<QpidMessage> _unacknowledgedMessages = new ArrayList<QpidMessage>();
+
+ /**
+ * Indicates whether this session is closed.
+ */
+ private boolean _isClosed = false;
+
+ /**
+ * Indicates whether this session is closing.
+ */
+ private boolean _isClosing = false;
+
+ /**
+ * Indicates whether this session is stopped.
+ */
+ private boolean _isStopped = false;
+
+ /**
+ * Used to indicate whether or not this is a transactional session.
+ */
+ private boolean _transacted;
+
+ /**
+ * Holds the sessions acknowledgement mode.
+ */
+ private int _acknowledgeMode;
+
+ /**
+ * The underlying QpidSession
+ */
+ private org.apache.qpidity.nclient.Session _qpidSession;
+
+ /**
+ * The latest qpid Exception that has been reaised.
+ */
+ private QpidException _currentException;
+
+ /**
+ * Indicates whether this session is recovering
+ */
+ private boolean _inRecovery = false;
+
+ /**
+ * This session connection
+ */
+ private ConnectionImpl _connection;
+
+ /**
+ * This will be used as the message actor id
+ * This in turn will be set as the destination
+ */
+ protected AtomicInteger _consumerTag = new AtomicInteger();
+
+ //--- Constructor
+ /**
+ * Create a JMS Session
+ *
+ * @param connection The ConnectionImpl object from which the Session is created.
+ * @param transacted Indicates if the session transacted.
+ * @param acknowledgeMode The session's acknowledgement mode. This value is ignored and set to
+ * {@link Session#SESSION_TRANSACTED} if the <code>transacted</code> parameter is true.
+ * @param isXA Indicates whether this session is an XA session.
+ * @throws QpidException In case of internal error.
+ */
+ protected SessionImpl(ConnectionImpl connection, boolean transacted, int acknowledgeMode, boolean isXA)
+ throws QpidException
+ {
+ _connection = connection;
+ _transacted = transacted;
+ // for transacted sessions we ignore the acknowledgeMode and use GenericAckMode.SESSION_TRANSACTED
+ if (_transacted)
+ {
+ acknowledgeMode = Session.SESSION_TRANSACTED;
+ }
+ _acknowledgeMode = acknowledgeMode;
+
+ // create the qpid session with an expiry <= 0 so that the session does not expire
+ _qpidSession = _connection.getQpidConnection().createSession(0);
+ // set the exception listnere for this session
+ _qpidSession.setExceptionListener(new QpidSessionExceptionListener());
+ // set transacted if required
+ if (_transacted && !isXA)
+ {
+ _qpidSession.txSelect();
+ }
+ testQpidException();
+ // init the message dispatcher.
+ initMessageDispatcherThread();
+ }
+
+ //--- javax.njms.Session API
+ /**
+ * Creates a <CODE>BytesMessage</CODE> object used to send a message
+ * containing a stream of uninterpreted bytes.
+ *
+ * @return A BytesMessage.
+ * @throws JMSException If Creating a BytesMessage object fails due to some internal error.
+ */
+ public BytesMessage createBytesMessage() throws JMSException
+ {
+ checkNotClosed();
+ return new BytesMessageImpl();
+ }
+
+ /**
+ * Creates a <CODE>MapMessage</CODE> object used to send a self-defining set
+ * of name-value pairs, where names are Strings and values are primitive values.
+ *
+ * @return A MapMessage.
+ * @throws JMSException If Creating a MapMessage object fails due to some internal error.
+ */
+ public MapMessage createMapMessage() throws JMSException
+ {
+ checkNotClosed();
+ return new MapMessageImpl();
+ }
+
+ /**
+ * Creates a <code>Message</code> object that holds all the standard message header information.
+ * It can be sent when a message containing only header information is sufficient.
+ * We simply return a ByteMessage
+ *
+ * @return A Message.
+ * @throws JMSException If Creating a Message object fails due to some internal error.
+ */
+ public Message createMessage() throws JMSException
+ {
+ return new MessageImpl();
+ }
+
+ /**
+ * Creates an <code>ObjectMessage</code> used to send a message
+ * that contains a serializable Java object.
+ *
+ * @return An ObjectMessage.
+ * @throws JMSException If Creating an ObjectMessage object fails due to some internal error.
+ */
+ public ObjectMessage createObjectMessage() throws JMSException
+ {
+ checkNotClosed();
+ return new ObjectMessageImpl();
+ }
+
+ /**
+ * Creates an initialized <code>ObjectMessage</code> used to send a message that contains
+ * a serializable Java object.
+ *
+ * @param serializable The object to use to initialize this message.
+ * @return An initialised ObjectMessage.
+ * @throws JMSException If Creating an initialised ObjectMessage object fails due to some internal error.
+ */
+ public ObjectMessage createObjectMessage(Serializable serializable) throws JMSException
+ {
+ ObjectMessage msg = createObjectMessage();
+ msg.setObject(serializable);
+ return msg;
+ }
+
+ /**
+ * Creates a <code>StreamMessage</code> used to send a
+ * self-defining stream of primitive values in the Java programming
+ * language.
+ *
+ * @return A StreamMessage
+ * @throws JMSException If Creating an StreamMessage object fails due to some internal error.
+ */
+ public StreamMessage createStreamMessage() throws JMSException
+ {
+ checkNotClosed();
+ return new StreamMessageImpl();
+ }
+
+ /**
+ * Creates a <code>TextMessage</code> object used to send a message containing a String.
+ *
+ * @return A TextMessage object
+ * @throws JMSException If Creating an TextMessage object fails due to some internal error.
+ */
+ public TextMessage createTextMessage() throws JMSException
+ {
+ checkNotClosed();
+ return new TextMessageImpl();
+ }
+
+ /**
+ * Creates an initialized <code>TextMessage</code> used to send
+ * a message containing a String.
+ *
+ * @param text The string used to initialize this message.
+ * @return An initialized TextMessage
+ * @throws JMSException If Creating an initialised TextMessage object fails due to some internal error.
+ */
+ public TextMessage createTextMessage(String text) throws JMSException
+ {
+ TextMessage msg = createTextMessage();
+ msg.setText(text);
+ return msg;
+ }
+
+ /**
+ * Indicates whether the session is in transacted mode.
+ *
+ * @return true if the session is in transacted mode
+ * @throws JMSException If geting the transaction mode fails due to some internal error.
+ */
+ public boolean getTransacted() throws JMSException
+ {
+ checkNotClosed();
+ return _transacted;
+ }
+
+ /**
+ * Returns the acknowledgement mode of this session.
+ * <p> The acknowledgement mode is set at the time that the session is created.
+ * If the session is transacted, the acknowledgement mode is ignored.
+ *
+ * @return If the session is not transacted, returns the current acknowledgement mode for the session.
+ * else returns SESSION_TRANSACTED.
+ * @throws JMSException if geting the acknowledgement mode fails due to some internal error.
+ */
+ public int getAcknowledgeMode() throws JMSException
+ {
+ checkNotClosed();
+ return _acknowledgeMode;
+ }
+
+ /**
+ * Commits all messages done in this transaction.
+ *
+ * @throws JMSException If committing the transaction fails due to some internal error.
+ * @throws TransactionRolledBackException If the transaction is rolled back due to some internal error during commit.
+ * @throws javax.jms.IllegalStateException
+ * If the method is not called by a transacted session.
+ */
+ public void commit() throws JMSException
+ {
+ checkNotClosed();
+ //make sure the Session is a transacted one
+ if (!_transacted)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Cannot commit non-transacted session, throwing IllegalStateException");
+ }
+ throw new IllegalStateException("Cannot commit non-transacted session", "Session is not transacted");
+ }
+ // commit the underlying Qpid Session
+ _qpidSession.txCommit();
+ try
+ {
+ testQpidException();
+ }
+ catch (QpidException e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+
+ /**
+ * Rolls back any messages done in this transaction.
+ *
+ * @throws JMSException If rolling back the session fails due to some internal error.
+ * @throws javax.jms.IllegalStateException
+ * If the method is not called by a transacted session.
+ */
+ public void rollback() throws JMSException
+ {
+ checkNotClosed();
+ //make sure the Session is a transacted one
+ if (!_transacted)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Cannot rollback non-transacted session, throwing IllegalStateException");
+ }
+ throw new IllegalStateException("Cannot rollback non-transacted session", "Session is not transacted");
+ }
+ // rollback the underlying Qpid Session
+ _qpidSession.txRollback();
+ try
+ {
+ testQpidException();
+ }
+ catch (QpidException e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+
+ /**
+ * Closes this session.
+ * <p> The JMS specification says
+ * <P> This call will block until a <code>receive</code> call or message
+ * listener in progress has completed. A blocked message consumer
+ * <code>receive</code> call returns <code>null</code> when this session is closed.
+ * <P>Closing a transacted session must roll back the transaction in progress.
+ * <P>This method is the only <code>Session</code> method that can be called concurrently.
+ * <P>Invoking any other <code>Session</code> method on a closed session
+ * must throw a <code>javax.njms.IllegalStateException</code>.
+ * <p> Closing a closed session must <I>not</I> throw an exception.
+ *
+ * @throws JMSException If closing the session fails due to some internal error.
+ */
+ public synchronized void close() throws JMSException
+ {
+ if (!_isClosed)
+ {
+ _messageDispatcherThread.interrupt();
+ if (!_isClosing)
+ {
+ _isClosing = true;
+ // if the session is stopped then restart it before notifying on the lock
+ // that will stop the sessionThread
+ if (_isStopped)
+ {
+ startDispatchThread();
+ }
+ //notify the sessionThread
+ synchronized (_incomingAsynchronousMessages)
+ {
+ _incomingAsynchronousMessages.notifyAll();
+ }
+
+ try
+ {
+ _messageDispatcherThread.join();
+ _messageDispatcherThread = null;
+ }
+ catch (InterruptedException ie)
+ {
+ /* ignore */
+ }
+ }
+ // from now all the session methods will throw a IllegalStateException
+ _isClosed = true;
+ // close all the actors
+ closeAllMessageActors();
+ _messageActors.clear();
+ // We may have a thread trying to add a message
+ synchronized (_incomingAsynchronousMessages)
+ {
+ _incomingAsynchronousMessages.clear();
+ _incomingAsynchronousMessages.notifyAll();
+ }
+ // close the underlaying QpidSession
+ _qpidSession.sessionClose();
+ try
+ {
+ testQpidException();
+ }
+ catch (QpidException e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+ }
+
+ /**
+ * Stops message delivery in this session, and restarts message delivery with
+ * the oldest unacknowledged message.
+ * <p>Recovering a session causes it to take the following actions:
+ * <ul>
+ * <li>Stop message delivery.
+ * <li>Mark all messages that might have been delivered but not acknowledged as "redelivered".
+ * <li>Restart the delivery sequence including all unacknowledged messages that had been
+ * previously delivered.
+ * Redelivered messages do not have to be delivered in exactly their original delivery order.
+ * </ul>
+ *
+ * @throws JMSException If the JMS provider fails to stop and restart message delivery due to some internal error.
+ * Not that this does not necessarily mean that the recovery has failed, but simply that it is
+ * not possible to tell if it has or not.
+ */
+ public void recover() throws JMSException
+ {
+ // Ensure that the session is open.
+ checkNotClosed();
+ // we are recovering
+ _inRecovery = true;
+ // Ensure that the session is not transacted.
+ if (getTransacted())
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Trying to recover a transacted Session, throwing IllegalStateException");
+ }
+ throw new IllegalStateException("Session is transacted");
+ }
+ // release all unack messages
+ RangeSet ranges = new RangeSet();
+ for (QpidMessage message : _unacknowledgedMessages)
+ {
+ // release this message
+ ranges.add(message.getMessageTransferId());
+ }
+ getQpidSession().messageRelease(ranges);
+ }
+
+ /**
+ * Returns the session's distinguished message listener (optional).
+ * <p>This is an expert facility used only by Application Servers.
+ * <p> This is an optional operation that is not yet supported
+ *
+ * @return The message listener associated with this session.
+ * @throws JMSException If getting the message listener fails due to an internal error.
+ */
+ public MessageListener getMessageListener() throws JMSException
+ {
+ checkNotClosed();
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug(
+ "Getting session's distinguished message listener, not supported," + " throwing UnsupportedOperationException");
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Sets the session's distinguished message listener.
+ * <p>This is an expert facility used only by Application Servers.
+ * <p> This is an optional operation that is not yet supported
+ *
+ * @param messageListener The message listener to associate with this session
+ * @throws JMSException If setting the message listener fails due to an internal error.
+ */
+ public void setMessageListener(MessageListener messageListener) throws JMSException
+ {
+ checkNotClosed();
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug(
+ "Setting the session's distinguished message listener, not supported," + " throwing UnsupportedOperationException");
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Optional operation, intended to be used only by Application Servers,
+ * not by ordinary JMS clients.
+ * <p> This is an optional operation that is not yet supported
+ */
+ public void run()
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Running this session, not supported," + " throwing UnsupportedOperationException");
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Creates a MessageProducer to send messages to the specified destination.
+ *
+ * @param destination the Destination to send messages to, or null if this is a producer
+ * which does not have a specified destination.
+ * @return A new MessageProducer
+ * @throws JMSException If the session fails to create a MessageProducer
+ * due to some internal error.
+ * @throws InvalidDestinationException If an invalid destination is specified.
+ */
+ public MessageProducer createProducer(Destination destination) throws JMSException
+ {
+ checkNotClosed();
+ MessageProducerImpl producer = new MessageProducerImpl(this, (DestinationImpl) destination);
+ // register this actor with the session
+ _messageActors.put(producer.getMessageActorID(), producer);
+ return producer;
+ }
+
+ /**
+ * Creates a MessageConsumer for the specified destination.
+ *
+ * @param destination The <code>Destination</code> to access
+ * @return A new MessageConsumer for the specified destination.
+ * @throws JMSException If the session fails to create a MessageConsumer due to some internal error.
+ * @throws InvalidDestinationException If an invalid destination is specified.
+ */
+ public MessageConsumer createConsumer(Destination destination) throws JMSException
+ {
+ return createConsumer(destination, null);
+ }
+
+ /**
+ * Creates a MessageConsumer for the specified destination, using a message selector.
+ *
+ * @param destination The <code>Destination</code> to access
+ * @param messageSelector Only messages with properties matching the message selector expression are delivered.
+ * @return A new MessageConsumer for the specified destination.
+ * @throws JMSException If the session fails to create a MessageConsumer due to some internal error.
+ * @throws InvalidDestinationException If an invalid destination is specified.
+ * @throws InvalidSelectorException If the message selector is invalid.
+ */
+ public MessageConsumer createConsumer(Destination destination, String messageSelector) throws JMSException
+ {
+ return createConsumer(destination, messageSelector, false);
+ }
+
+ /**
+ * Creates MessageConsumer for the specified destination, using a message selector.
+ * <p> This method can specify whether messages published by its own connection should
+ * be delivered to it, if the destination is a topic.
+ * <p/>
+ * <P>In some cases, a connection may both publish and subscribe to a topic. The consumer
+ * NoLocal attribute allows a consumer to inhibit the delivery of messages published by its
+ * own connection. The default value for this attribute is False.
+ *
+ * @param destination The <code>Destination</code> to access
+ * @param messageSelector Only messages with properties matching the message selector expression are delivered.
+ * @param noLocal If true, and the destination is a topic, inhibits the delivery of messages published
+ * by its own connection.
+ * @return A new MessageConsumer for the specified destination.
+ * @throws JMSException If the session fails to create a MessageConsumer due to some internal error.
+ * @throws InvalidDestinationException If an invalid destination is specified.
+ * @throws InvalidSelectorException If the message selector is invalid.
+ */
+ public MessageConsumer createConsumer(Destination destination, String messageSelector, boolean noLocal)
+ throws JMSException
+ {
+ checkNotClosed();
+ checkDestination(destination);
+ MessageConsumerImpl consumer;
+ try
+ {
+ consumer = new MessageConsumerImpl(this, (DestinationImpl) destination, messageSelector, noLocal, null,
+ String.valueOf(_consumerTag.incrementAndGet()));
+ }
+ catch (Exception e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Problem when creating consumer.", e);
+ }
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ // register this actor with the session
+ _messageActors.put(consumer.getMessageActorID(), consumer);
+ return consumer;
+ }
+
+ /**
+ * Creates a queue identity by a given name.
+ * <P>This facility is provided for the rare cases where clients need to
+ * dynamically manipulate queue identity. It allows the creation of a
+ * queue identity with a provider-specific name. Clients that depend
+ * on this ability are not portable.
+ * <P>Note that this method is not for creating the physical queue.
+ * The physical creation of queues is an administrative task and is not
+ * to be initiated by the JMS API. The one exception is the
+ * creation of temporary queues, which is accomplished with the
+ * <code>createTemporaryQueue</code> method.
+ *
+ * @param queueName the name of this <code>Queue</code>
+ * @return a <code>Queue</code> with the given name
+ * @throws JMSException If the session fails to create a queue due to some internal error.
+ */
+ public Queue createQueue(String queueName) throws JMSException
+ {
+ checkNotClosed();
+ Queue result;
+ try
+ {
+ result = new QueueImpl(this, queueName);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Problem when creating Queue.", e);
+ }
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ return result;
+ }
+
+ /**
+ * Creates a topic identity given a Topicname.
+ * <P>This facility is provided for the rare cases where clients need to
+ * dynamically manipulate queue identity. It allows the creation of a
+ * queue identity with a provider-specific name. Clients that depend
+ * on this ability are not portable.
+ * <P>Note that this method is not for creating the physical queue.
+ * The physical creation of queues is an administrative task and is not
+ * to be initiated by the JMS API. The one exception is the
+ * creation of temporary queues, which is accomplished with the
+ * <code>createTemporaryTopic</code> method.
+ *
+ * @param topicName The name of this <code>Topic</code>
+ * @return a <code>Topic</code> with the given name
+ * @throws JMSException If the session fails to create a topic due to some internal error.
+ */
+ public Topic createTopic(String topicName) throws JMSException
+ {
+ checkNotClosed();
+ Topic result;
+ try
+ {
+ result = new TopicImpl(this, topicName);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Problem when creating Topic.", e);
+ }
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ return result;
+ }
+
+ /**
+ * Creates a durable subscriber to the specified topic,
+ *
+ * @param topic The non-temporary <code>Topic</code> to subscribe to.
+ * @param name The name used to identify this subscription.
+ * @return A durable subscriber to the specified topic,
+ * @throws JMSException If creating a subscriber fails due to some internal error.
+ * @throws InvalidDestinationException If an invalid topic is specified.
+ * @throws InvalidSelectorException If the message selector is invalid.
+ */
+ public TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException
+ {
+ // by default, use a null messageselector and set noLocal to falsen
+ return createDurableSubscriber(topic, name, null, false);
+ }
+
+ /**
+ * Creates a durable subscriber to the specified topic, using a message selector and specifying whether messages
+ * published by its
+ * own connection should be delivered to it.
+ * <p> A client can change an existing durable subscription by creating a durable <code>TopicSubscriber</code> with
+ * the same name and a new topic and/or message selector. Changing a durable subscriber is equivalent to
+ * unsubscribing (deleting) the old one and creating a new one.
+ *
+ * @param topic The non-temporary <code>Topic</code> to subscribe to.
+ * @param name The name used to identify this subscription.
+ * @param messageSelector Only messages with properties matching the message selector expression are delivered.
+ * @param noLocal If set, inhibits the delivery of messages published by its own connection
+ * @return A durable subscriber to the specified topic,
+ * @throws JMSException If creating a subscriber fails due to some internal error.
+ * @throws InvalidDestinationException If an invalid topic is specified.
+ * @throws InvalidSelectorException If the message selector is invalid.
+ */
+ public TopicSubscriber createDurableSubscriber(Topic topic, String name, String messageSelector, boolean noLocal)
+ throws JMSException
+ {
+ checkNotClosed();
+ checkDestination(topic);
+ TopicSubscriberImpl subscriber;
+ try
+ {
+ subscriber = new TopicSubscriberImpl(this, topic, messageSelector, noLocal,
+ _connection.getClientID() + ":" + name,
+ String.valueOf(_consumerTag.incrementAndGet()));
+ }
+ catch (Exception e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Problem when creating Durable Subscriber.", e);
+ }
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ _messageActors.put(subscriber.getMessageActorID(), subscriber);
+ return subscriber;
+ }
+
+ /**
+ * Create a QueueBrowser to peek at the messages on the specified queue.
+ *
+ * @param queue The <code>Queue</code> to browse.
+ * @return A QueueBrowser.
+ * @throws JMSException If creating a browser fails due to some internal error.
+ * @throws InvalidDestinationException If an invalid queue is specified.
+ */
+ public QueueBrowser createBrowser(Queue queue) throws JMSException
+ {
+ return createBrowser(queue, null);
+ }
+
+ /**
+ * Create a QueueBrowser to peek at the messages on the specified queue using a message selector.
+ *
+ * @param queue The <code>Queue</code> to browse.
+ * @param messageSelector Only messages with properties matching the message selector expression are delivered.
+ * @return A QueueBrowser.
+ * @throws JMSException If creating a browser fails due to some internal error.
+ * @throws InvalidDestinationException If an invalid queue is specified.
+ * @throws InvalidSelectorException If the message selector is invalid.
+ */
+ public QueueBrowser createBrowser(Queue queue, String messageSelector) throws JMSException
+ {
+ checkNotClosed();
+ checkDestination(queue);
+ QueueBrowserImpl browser;
+ try
+ {
+ browser =
+ new QueueBrowserImpl(this, queue, messageSelector, String.valueOf(_consumerTag.incrementAndGet()));
+ }
+ catch (Exception e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Problem when creating Durable Browser.", e);
+ }
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ // register this actor with the session
+ _messageActors.put(browser.getMessageActorID(), browser);
+ return browser;
+ }
+
+ /**
+ * Create a TemporaryQueue. Its lifetime will be the Connection unless it is deleted earlier.
+ *
+ * @return A temporary queue.
+ * @throws JMSException If creating the temporary queue fails due to some internal error.
+ */
+ public TemporaryQueue createTemporaryQueue() throws JMSException
+ {
+ TemporaryQueue result;
+ try
+ {
+ result = new TemporaryQueueImpl(this);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Problem when creating Durable Temporary Queue.", e);
+ }
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ return result;
+ }
+
+ /**
+ * Create a TemporaryTopic. Its lifetime will be the Connection unless it is deleted earlier.
+ *
+ * @return A temporary topic.
+ * @throws JMSException If creating the temporary topic fails due to some internal error.
+ */
+ public TemporaryTopic createTemporaryTopic() throws JMSException
+ {
+ TemporaryTopic result;
+ try
+ {
+ result = new TemporaryTopicImpl(this);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Problem when creating Durable Temporary Topic.", e);
+ }
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ return result;
+ }
+
+ /**
+ * Unsubscribes a durable subscription that has been created by a client.
+ * <p/>
+ * <P>This method deletes the state being maintained on behalf of the
+ * subscriber by its provider.
+ * <p/>
+ * <P>It is erroneous for a client to delete a durable subscription
+ * while there is an active <code>TopicSubscriber</code> for the
+ * subscription, or while a consumed message is part of a pending
+ * transaction or has not been acknowledged in the session.
+ *
+ * @param name the name used to identify this subscription
+ * @throws JMSException if the session fails to unsubscribe to the durable subscription due to some internal error.
+ * @throws InvalidDestinationException if an invalid subscription name
+ * is specified.
+ */
+ public void unsubscribe(String name) throws JMSException
+ {
+ checkNotClosed();
+ }
+
+ /**
+ * Get the latest thrown exception.
+ *
+ * @return The latest thrown exception.
+ */
+ public synchronized QpidException getCurrentException()
+ {
+ QpidException result = _currentException;
+ _currentException = null;
+ return result;
+ }
+ //----- Protected methods
+
+ /**
+ * Remove a message actor form this session
+ * <p> This method is called when an actor is independently closed.
+ *
+ * @param messageActor The closed actor.
+ */
+ protected void closeMessageActor(MessageActor messageActor)
+ {
+ _messageActors.remove(messageActor.getMessageActorID());
+ }
+
+ /**
+ * Idincates whether this session is stopped.
+ *
+ * @return True is this session is stopped, false otherwise.
+ */
+ protected boolean isStopped()
+ {
+ return _isStopped;
+ }
+
+ /**
+ * Start the flow of message to this session.
+ *
+ * @throws Exception If starting the session fails due to some communication error.
+ */
+ protected synchronized void start() throws Exception
+ {
+ if (_isStopped)
+ {
+ // start all the MessageActors
+ for (MessageActor messageActor : _messageActors.values())
+ {
+ messageActor.start();
+ }
+ startDispatchThread();
+ }
+ }
+
+ /**
+ * Restart delivery of asynch messages
+ */
+ private void startDispatchThread()
+ {
+ synchronized (_stoppingLock)
+ {
+ _isStopped = false;
+ _stoppingLock.notify();
+ }
+ synchronized (_stoppingJoin)
+ {
+ _hasStopped = false;
+ }
+ }
+
+ /**
+ * Stop the flow of message to this session.
+ *
+ * @throws Exception If stopping the session fails due to some communication error.
+ */
+ protected synchronized void stop() throws Exception
+ {
+ if (!_isClosing && !_isStopped)
+ {
+ // stop all the MessageActors
+ for (MessageActor messageActor : _messageActors.values())
+ {
+ messageActor.stop();
+ }
+ synchronized (_incomingAsynchronousMessages)
+ {
+ _isStopped = true;
+ // unlock the sessionThread that will then wait on _stoppingLock
+ _incomingAsynchronousMessages.notifyAll();
+ }
+ // wait for the sessionThread to stop processing messages
+ synchronized (_stoppingJoin)
+ {
+ while (!_hasStopped)
+ {
+ try
+ {
+ _stoppingJoin.wait();
+ }
+ catch (InterruptedException e)
+ {
+ /* ignore */
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Notify this session that a message is processed
+ *
+ * @param message The processed message.
+ */
+ protected void preProcessMessage(QpidMessage message)
+ {
+ _inRecovery = false;
+ }
+
+ /**
+ * Dispatch this message to this session asynchronous consumers
+ *
+ * @param consumerID The consumer ID.
+ * @param message The message to be dispatched.
+ */
+ public void dispatchMessage(String consumerID, QpidMessage message)
+ {
+ synchronized (_incomingAsynchronousMessages)
+ {
+ _incomingAsynchronousMessages.addLast(new IncomingMessage(consumerID, message));
+ _incomingAsynchronousMessages.notifyAll();
+ }
+ }
+
+ /**
+ * Indicate whether this session is recovering .
+ *
+ * @return true if this session is recovering.
+ */
+ protected boolean isInRecovery()
+ {
+ return _inRecovery;
+ }
+
+ /**
+ * Validate that the Session is not closed.
+ * <p/>
+ * If the Session has been closed, throw a IllegalStateException. This behaviour is
+ * required by the JMS specification.
+ *
+ * @throws IllegalStateException If the session is closed.
+ */
+ protected void checkNotClosed() throws IllegalStateException
+ {
+ if (_isClosed)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Session has been closed. Cannot invoke any further operations.");
+ }
+ throw new javax.jms.IllegalStateException("Session has been closed. Cannot invoke any further operations.");
+ }
+ }
+
+ /**
+ * Validate that the destination is valid i.e. it is not null
+ *
+ * @param dest The destination to be checked
+ * @throws InvalidDestinationException If the destination not valid.
+ */
+ protected void checkDestination(Destination dest) throws InvalidDestinationException
+ {
+ if (dest == null)
+ {
+ throw new javax.jms.InvalidDestinationException("Invalid destination specified: " + dest,
+ "Invalid destination");
+ }
+ }
+
+ /**
+ * A session keeps the list of unack messages only when the ack mode is
+ * set to client ack mode. Otherwise messages are always ack.
+ * <p> We can use an ack heuristic for dups ok mode where bunch of messages are ack.
+ * This has to be done.
+ *
+ * @param message The message to be acknowledged.
+ * @throws JMSException If the message cannot be acknowledged due to an internal error.
+ */
+ protected void acknowledgeMessage(QpidMessage message) throws JMSException
+ {
+ if (getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE)
+ {
+ // messages will be acknowldeged by the client application.
+ // store this message for acknowledging it afterward
+ synchronized (_unacknowledgedMessages)
+ {
+ _unacknowledgedMessages.add(message);
+ }
+ }
+ else
+ {
+ // acknowledge this message
+ RangeSet ranges = new RangeSet();
+ ranges.add(message.getMessageTransferId());
+ getQpidSession().messageAcknowledge(ranges);
+ }
+ //tobedone: Implement DUPS OK heuristic
+ }
+
+ /**
+ * This method is called when a message is acked.
+ * <p/>
+ * <P>Acknowledgment of a message automatically acknowledges all
+ * messages previously received by the session. Clients may
+ * individually acknowledge messages or they may choose to acknowledge
+ * messages in application defined groups (which is done by acknowledging
+ * the last received message in the group).
+ *
+ * @throws JMSException If this method is called on a closed session.
+ */
+ public void acknowledge() throws JMSException
+ {
+ checkNotClosed();
+ if (getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE)
+ {
+ synchronized (_unacknowledgedMessages)
+ {
+ for (QpidMessage message : _unacknowledgedMessages)
+ {
+ // acknowledge this message
+ RangeSet ranges = new RangeSet();
+ ranges.add(message.getMessageTransferId());
+ getQpidSession().messageAcknowledge(ranges);
+ }
+ //empty the list of unack messages
+ _unacknowledgedMessages.clear();
+ }
+ }
+ //else there is no effect
+ }
+
+ /**
+ * Access to the underlying Qpid Session
+ *
+ * @return The associated Qpid Session.
+ */
+ protected org.apache.qpidity.nclient.Session getQpidSession()
+ {
+ return _qpidSession;
+ }
+
+ /**
+ * Get this session's conneciton
+ *
+ * @return This session's connection
+ */
+ protected ConnectionImpl getConnection()
+ {
+ return _connection;
+ }
+
+ /**
+ * sync and return the potential exception
+ *
+ * @throws QpidException If an exception has been thrown by the broker.
+ */
+ protected void testQpidException() throws QpidException
+ {
+ //_qpidSession.sync();
+ QpidException qe = getCurrentException();
+ if (qe != null)
+ {
+ throw qe;
+ }
+ }
+
+ //------ Private Methods
+ /**
+ * Close the producer and the consumers of this session
+ *
+ * @throws JMSException If one of the MessaeActor cannot be closed due to some internal error.
+ */
+ private void closeAllMessageActors() throws JMSException
+ {
+ for (MessageActor messageActor : _messageActors.values())
+ {
+ messageActor.closeMessageActor();
+ }
+ }
+
+ /**
+ * create and start the MessageDispatcherThread.
+ */
+ private synchronized void initMessageDispatcherThread()
+ {
+ // Create and start a MessageDispatcherThread
+ // This thread is dispatching messages to the async consumers
+ _messageDispatcherThread = new MessageDispatcherThread();
+ _messageDispatcherThread.start();
+ }
+
+ //------ Inner classes
+
+ /**
+ * Lstener for qpid protocol exceptions
+ */
+ private class QpidSessionExceptionListener implements org.apache.qpidity.nclient.ExceptionListener
+ {
+ public void onException(QpidException exception)
+ {
+ synchronized (this)
+ {
+ //todo check the error code for finding out if we need to notify the
+ // JMS connection exception listener
+ _currentException = exception;
+ }
+ }
+ }
+
+ /**
+ * Convenient class for storing incoming messages associated with a consumer ID.
+ * <p> Those messages are enqueued in _incomingAsynchronousMessages
+ */
+ private class IncomingMessage
+ {
+ // The consumer ID
+ private String _consumerId;
+ // The message
+ private QpidMessage _message;
+
+ //-- constructor
+ /**
+ * Creat a new incoming message
+ *
+ * @param consumerId The consumer ID
+ * @param message The message to be delivered
+ */
+ IncomingMessage(String consumerId, QpidMessage message)
+ {
+ _consumerId = consumerId;
+ _message = message;
+ }
+
+ // Getters
+ /**
+ * Get the consumer ID
+ *
+ * @return The consumer ID for this message
+ */
+ public String getConsumerId()
+ {
+ return _consumerId;
+ }
+
+ /**
+ * Get the message.
+ *
+ * @return The message.
+ */
+ public QpidMessage getMessage()
+ {
+ return _message;
+ }
+ }
+
+ /**
+ * A MessageDispatcherThread is attached to every SessionImpl.
+ * <p/>
+ * This thread is responsible for removing messages from m_incomingMessages and
+ * dispatching them to the appropriate MessageConsumer.
+ * <p> Messages have to be dispatched serially.
+ */
+ private class MessageDispatcherThread extends Thread
+ {
+ //--- Constructor
+ /**
+ * Create a Deamon thread for dispatching messages to this session listeners.
+ */
+ MessageDispatcherThread()
+ {
+ super("MessageDispatcher");
+ // this thread is Deamon
+ setDaemon(true);
+ }
+
+ /**
+ * Use to run this thread.
+ */
+ public void run()
+ {
+ IncomingMessage message = null;
+ // deliver messages to asynchronous consumers until the stop flag is set.
+ do
+ {
+ // When this session is not closing and and stopped
+ // then this thread needs to wait until messages are delivered.
+ synchronized (_incomingAsynchronousMessages)
+ {
+ while (!_isClosing && !_isStopped && _incomingAsynchronousMessages.isEmpty())
+ {
+ try
+ {
+ _incomingAsynchronousMessages.wait();
+ }
+ catch (InterruptedException ie)
+ {
+ /* ignore */
+ }
+ }
+ }
+ // If this session is stopped then we need to wait on the stoppingLock
+ synchronized (_stoppingLock)
+ {
+ try
+ {
+ while (_isStopped)
+ {
+ // if the session is stopped we have to notify the stopper thread
+ synchronized (_stoppingJoin)
+ {
+ _hasStopped = true;
+ _stoppingJoin.notify();
+ }
+ _stoppingLock.wait();
+ }
+ }
+ catch (Exception ie)
+ {
+ /* ignore */
+ }
+ }
+ synchronized (_incomingAsynchronousMessages)
+ {
+ if (!_isClosing && !_incomingAsynchronousMessages.isEmpty())
+ {
+ message = _incomingAsynchronousMessages.getFirst();
+ }
+ }
+
+ if (message != null)
+ {
+ MessageConsumerImpl mc;
+ synchronized (_messageActors)
+ {
+ mc = (MessageConsumerImpl) _messageActors.get(message.getConsumerId());
+ }
+ if (mc != null)
+ {
+ try
+ {
+ // mc.onMessage(message.getMessage());
+ mc.notifyMessageListener(message.getMessage());
+ }
+ catch (RuntimeException t)
+ {
+ // the JMS specification tells us to flag that to the client!
+ _logger.error(
+ "Warning! Asynchronous message consumer" + mc + " from session " + this + " has thrown a RunTimeException " + t);
+ }
+ }
+ }
+ message = null;
+ }
+ while (!_isClosing); // repeat as long as this session is not closing
+ }
+ }
+
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/TemporaryDestination.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/TemporaryDestination.java
new file mode 100644
index 0000000000..34d2cbd1b1
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/TemporaryDestination.java
@@ -0,0 +1,41 @@
+/*
+ * 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.qpidity.njms;
+
+import javax.jms.JMSException;
+
+/**
+ * Interface to abstract functionalities of temporary destinations.
+ */
+public interface TemporaryDestination
+{
+ /**
+ * Delete this temporary destination.
+ *
+ * @throws javax.jms.JMSException If the temporary destination cannot be deleted due to some internal error.
+ */
+ public void delete() throws JMSException;
+
+ /**
+ * Indicate whether this temporary destination is deleted
+ * @return True is this temporary destination is deleted, false otherwise
+ */
+ public boolean isdeleted();
+
+} \ No newline at end of file
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/TemporaryQueueImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/TemporaryQueueImpl.java
new file mode 100644
index 0000000000..88dae74cc1
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/TemporaryQueueImpl.java
@@ -0,0 +1,88 @@
+/* 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.qpidity.njms;
+
+import org.apache.qpidity.QpidException;
+
+import javax.jms.TemporaryQueue;
+import javax.jms.JMSException;
+import java.util.UUID;
+
+/**
+ * Implements TemporaryQueue
+ */
+public class TemporaryQueueImpl extends QueueImpl implements TemporaryQueue, TemporaryDestination
+{
+ /**
+ * Indicates whether this temporary queue is deleted.
+ */
+ private boolean _isDeleted;
+
+ /**
+ * The session used to create this destination
+ */
+ private SessionImpl _session;
+
+ //--- constructor
+ /**
+ * Create a new TemporaryQueueImpl.
+ *
+ * @param session The session used to create this TemporaryQueueImpl.
+ * @throws QpidException If creating the TemporaryQueueImpl fails due to some error.
+ */
+ protected TemporaryQueueImpl(SessionImpl session) throws QpidException
+ {
+ super("TempQueue-" + UUID.randomUUID());
+ // temporary destinations do not have names
+ _isAutoDelete = false;
+ _isDurable = false;
+ _isExclusive = false;
+ _isDeleted = false;
+ _session = session;
+ // we must create this queue
+ registerQueue(session, true);
+ }
+
+ //-- TemporaryDestination Interface
+ /**
+ * Specify whether this temporary destination is deleted.
+ *
+ * @return true is this temporary destination is deleted.
+ */
+ public boolean isdeleted()
+ {
+ return _isDeleted;
+ }
+
+ //-- TemporaryQueue Interface
+ /**
+ * Delete this temporary destinaiton
+ *
+ * @throws JMSException If deleting this temporary queue fails due to some error.
+ */
+ public void delete() throws JMSException
+ {
+ if (!_isDeleted)
+ {
+ _session.getQpidSession().queueDelete(_queueName);
+ }
+ _isDeleted = true;
+ }
+
+}
+
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/TemporaryTopicImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/TemporaryTopicImpl.java
new file mode 100644
index 0000000000..63fdc68c65
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/TemporaryTopicImpl.java
@@ -0,0 +1,71 @@
+/* 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.qpidity.njms;
+
+import org.apache.qpidity.QpidException;
+
+import javax.jms.TemporaryTopic;
+import javax.jms.JMSException;
+import java.util.UUID;
+
+
+/**
+ * Implements TemporaryTopic
+ */
+public class TemporaryTopicImpl extends TopicImpl implements TemporaryTopic, TemporaryDestination
+{
+ /**
+ * Indicates whether this temporary topic is deleted.
+ */
+ private boolean _isDeleted = false;
+
+ /**
+ * The session used to create this destination
+ */
+ private SessionImpl _session;
+
+ //--- constructor
+ /**
+ * Create a new TemporaryTopicImpl with a given name.
+ *
+ * @param session The session used to create this TemporaryTopicImpl.
+ * @throws QpidException If creating the TemporaryTopicImpl fails due to some error.
+ */
+ protected TemporaryTopicImpl(SessionImpl session) throws QpidException
+ {
+ // temporary destinations do not have names.
+ super(session, "TemporayTopic-" + UUID.randomUUID());
+ _session = session;
+ }
+
+ //-- TemporaryDestination Interface
+ public boolean isdeleted()
+ {
+ return _isDeleted;
+ }
+
+ //-- TemporaryTopic Interface
+ public void delete() throws JMSException
+ {
+ if (!_isDeleted)
+ {
+ _session.getQpidSession().queueDelete(_queueName);
+ }
+ _isDeleted = true;
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicConnectionImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicConnectionImpl.java
new file mode 100644
index 0000000000..4174c6a5d5
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicConnectionImpl.java
@@ -0,0 +1,35 @@
+/* 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.qpidity.njms;
+
+import org.apache.qpidity.QpidException;
+
+import javax.jms.TopicConnection;
+
+/**
+ * Implements javax.njms.TopicConnection
+ */
+public class TopicConnectionImpl extends ConnectionImpl implements TopicConnection
+{
+ //-- constructor
+ public TopicConnectionImpl(String host, int port, String virtualHost, String username, String password)
+ throws QpidException
+ {
+ super(host, port, virtualHost, username, password);
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicImpl.java
new file mode 100644
index 0000000000..fc6ea08897
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicImpl.java
@@ -0,0 +1,129 @@
+/* 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.qpidity.njms;
+
+import org.apache.qpidity.QpidException;
+import org.apache.qpidity.exchange.ExchangeDefaults;
+import org.apache.qpidity.transport.Option;
+import org.apache.qpidity.url.BindingURL;
+
+import javax.jms.Topic;
+import java.util.UUID;
+
+/**
+ * Implementation of the javax.njms.Topic interface.
+ */
+public class TopicImpl extends DestinationImpl implements Topic
+{
+ //--- Constructor
+ /**
+ * Create a new TopicImpl with a given name.
+ *
+ * @param name The name of this topic
+ * @param session The session used to create this queue.
+ * @throws QpidException If the topic name is not valid
+ */
+ protected TopicImpl(SessionImpl session, String name) throws QpidException
+ {
+ super(name);
+ _queueName = "Topic-" + UUID.randomUUID();
+ _routingKey = name;
+ _destinationName = name;
+ _exchangeName = ExchangeDefaults.TOPIC_EXCHANGE_NAME;
+ _exchangeType = ExchangeDefaults.TOPIC_EXCHANGE_CLASS;
+ _isAutoDelete = true;
+ _isDurable = false;
+ _isExclusive = true;
+ checkTopicExists(session);
+ }
+
+ /**
+ * Create a new TopicImpl with a given name.
+ *
+ * @param name The name of this topic
+ * @throws QpidException If the topic name is not valid
+ */
+ public TopicImpl(String name) throws QpidException
+ {
+ super(name);
+ _queueName = "Topic-" + UUID.randomUUID();
+ _routingKey = name;
+ _destinationName = name;
+ _exchangeName = ExchangeDefaults.TOPIC_EXCHANGE_NAME;
+ _exchangeType = ExchangeDefaults.TOPIC_EXCHANGE_CLASS;
+ _isAutoDelete = true;
+ _isDurable = false;
+ _isExclusive = true;
+ }
+
+ /**
+ * Create a TopicImpl from a binding URL
+ *
+ * @param session The session used to create this Topic.
+ * @param binding The URL
+ * @throws QpidException If the URL is not valid
+ */
+ protected TopicImpl(SessionImpl session, BindingURL binding) throws QpidException
+ {
+ super(binding);
+ checkTopicExists(session);
+ }
+
+
+ /**
+ * Create a TopicImpl from a binding URL
+ *
+ * @param binding The URL
+ * @throws QpidException If the URL is not valid
+ */
+ public TopicImpl(BindingURL binding) throws QpidException
+ {
+ super(binding);
+ }
+
+ //--- javax.jsm.Topic Interface
+ /**
+ * Gets the name of this topic.
+ *
+ * @return This topic's name.
+ */
+ public String getTopicName()
+ {
+ return _destinationName;
+ }
+
+ /**
+ * Check that this exchange exists
+ *
+ * @param session The session used to create this Topic.
+ * @throws QpidException If this exchange does not exists on the broker.
+ */
+ private void checkTopicExists(SessionImpl session) throws QpidException
+ {
+ // test if this exchange exist on the broker
+ session.getQpidSession().exchangeDeclare(_exchangeName, _exchangeType, null, null, Option.PASSIVE);
+ // wait for the broker response
+ System.out.println("Checking for exchange");
+
+ session.getQpidSession().sync();
+
+ System.out.println("Calling sync()");
+ // todo get the exception
+ }
+
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicPublisherImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicPublisherImpl.java
new file mode 100644
index 0000000000..7f65261486
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicPublisherImpl.java
@@ -0,0 +1,128 @@
+/* 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.qpidity.njms;
+
+import javax.jms.*;
+
+/**
+ * Implements TopicPublisher
+ */
+public class TopicPublisherImpl extends MessageProducerImpl implements TopicPublisher
+{
+ //--- Constructor
+ /**
+ * Create a TopicPublisherImpl.
+ *
+ * @param session The session from which the TopicPublisherImpl is instantiated
+ * @param topic The default topic for this TopicPublisherImpl
+ * @throws JMSException If the TopicPublisherImpl cannot be created due to some internal error.
+ */
+ protected TopicPublisherImpl(SessionImpl session, Topic topic) throws JMSException
+ {
+ super(session, (DestinationImpl) topic);
+ }
+
+ //--- Interface javax.njms.TopicPublisher
+ /**
+ * Get the topic associated with this TopicPublisher.
+ *
+ * @return This publisher's topic
+ * @throws JMSException If getting the topic fails due to some internal error.
+ */
+ public Topic getTopic() throws JMSException
+ {
+ return (Topic) getDestination();
+ }
+
+
+ /**
+ * Publish a message to the topic using the default delivery mode, priority and time to live.
+ *
+ * @param message The message to publish
+ * @throws JMSException If publishing the message fails due to some internal error.
+ * @throws javax.jms.MessageFormatException
+ * If an invalid message is specified.
+ * @throws javax.jms.InvalidDestinationException
+ * If an invalid topic is specified.
+ * @throws java.lang.UnsupportedOperationException
+ * If that publisher topic was not specified at creation time.
+ */
+ public void publish(Message message) throws JMSException
+ {
+ super.send(message);
+ }
+
+ /**
+ * Publish a message to the topic, specifying delivery mode, priority and time to live.
+ *
+ * @param message The message to publish
+ * @param deliveryMode The delivery mode to use
+ * @param priority The priority for this message
+ * @param timeToLive The message's lifetime (in milliseconds)
+ * @throws JMSException If publishing the message fails due to some internal error.
+ * @throws javax.jms.MessageFormatException
+ * If an invalid message is specified.
+ * @throws javax.jms.InvalidDestinationException
+ * If an invalid topic is specified.
+ * @throws java.lang.UnsupportedOperationException
+ * If that publisher topic was not specified at creation time.
+ */
+ public void publish(Message message, int deliveryMode, int priority, long timeToLive) throws JMSException
+ {
+ super.send(message, deliveryMode, priority, timeToLive);
+ }
+
+
+ /**
+ * Publish a message to a topic for an unidentified message producer.
+ * Uses this TopicPublisher's default delivery mode, priority and time to live.
+ *
+ * @param topic The topic to publish this message to
+ * @param message The message to publish
+ * @throws JMSException If publishing the message fails due to some internal error.
+ * @throws javax.jms.MessageFormatException
+ * If an invalid message is specified.
+ * @throws javax.jms.InvalidDestinationException
+ * If an invalid topic is specified.
+ */
+ public void publish(Topic topic, Message message) throws JMSException
+ {
+ super.send(topic, message);
+ }
+
+ /**
+ * Publishes a message to a topic for an unidentified message
+ * producer, specifying delivery mode, priority and time to live.
+ *
+ * @param topic The topic to publish this message to
+ * @param message The message to publish
+ * @param deliveryMode The delivery mode
+ * @param priority The priority for this message
+ * @param timeToLive The message's lifetime (in milliseconds)
+ * @throws JMSException If publishing the message fails due to some internal error.
+ * @throws javax.jms.MessageFormatException
+ * If an invalid message is specified.
+ * @throws javax.jms.InvalidDestinationException
+ * If an invalid topic is specified.
+ */
+ public void publish(Topic topic, Message message, int deliveryMode, int priority, long timeToLive) throws
+ JMSException
+ {
+ super.send(topic, message, deliveryMode, priority, timeToLive);
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicSessionImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicSessionImpl.java
new file mode 100644
index 0000000000..74c5a2c0f7
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicSessionImpl.java
@@ -0,0 +1,155 @@
+/* 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.qpidity.njms;
+
+import javax.jms.*;
+import javax.jms.IllegalStateException;
+
+import org.apache.qpidity.QpidException;
+
+/**
+ * Implements TopicSession
+ */
+public class TopicSessionImpl extends SessionImpl implements TopicSession
+{
+ //-- constructor
+ /**
+ * Create a new TopicSessionImpl.
+ *
+ * @param connection The ConnectionImpl object from which the Session is created.
+ * @param transacted Specifiy whether this session is transacted?
+ * @param acknowledgeMode The session's acknowledgement mode. This value is ignored and set to
+ * {@link javax.jms.Session#SESSION_TRANSACTED} if the <code>transacted</code> parameter
+ * is true.
+ * @throws javax.jms.JMSSecurityException If the user could not be authenticated.
+ * @throws javax.jms.JMSException In case of internal error.
+ */
+ protected TopicSessionImpl(ConnectionImpl connection, boolean transacted, int acknowledgeMode) throws QpidException, JMSException
+ {
+ super(connection, transacted, acknowledgeMode,false);
+ }
+
+ //-- Overwritten methods
+ /**
+ * Create a QueueBrowser.
+ *
+ * @param queue The <CODE>Queue</CODE> to browse.
+ * @param messageSelector Only messages with properties matching the message selector expression are delivered.
+ * @return Always throws an exception
+ * @throws IllegalStateException Always
+ */
+ @Override
+ public QueueBrowser createBrowser(Queue queue, String messageSelector) throws JMSException
+ {
+ throw new IllegalStateException("Cannot invoke createBrowser from TopicSession");
+ }
+
+ /**
+ * Create a QueueBrowser.
+ *
+ * @param queue The <CODE>Queue</CODE> to browse.
+ * @return Always throws an exception
+ * @throws IllegalStateException Always
+ */
+ @Override
+ public QueueBrowser createBrowser(Queue queue) throws JMSException
+ {
+ throw new IllegalStateException("Cannot invoke createBrowser from TopicSession");
+ }
+
+ /**
+ * Creates a temporary queue.
+ *
+ * @return Always throws an exception
+ * @throws IllegalStateException Always
+ */
+ @Override
+ public TemporaryQueue createTemporaryQueue() throws JMSException
+ {
+ throw new IllegalStateException("Cannot invoke createTemporaryQueue from TopicSession");
+ }
+
+ /**
+ * Creates a queue identity by a given name.
+ *
+ * @param queueName the name of this <CODE>Queue</CODE>
+ * @return Always throws an exception
+ * @throws IllegalStateException Always
+ */
+ @Override
+ public Queue createQueue(String queueName) throws JMSException
+ {
+ throw new IllegalStateException("Cannot invoke createQueue from TopicSession");
+ }
+
+ //--- Interface TopicSession
+ /**
+ * Create a publisher for the specified topic.
+ *
+ * @param topic the <CODE>Topic</CODE> to publish to, or null if this is an unidentified publisher.
+ * @throws JMSException If the creating a publisher fails due to some internal error.
+ * @throws InvalidDestinationException If an invalid topic is specified.
+ */
+ public TopicPublisher createPublisher(Topic topic) throws JMSException
+ {
+
+ checkNotClosed();
+ // we do not check the destination topic here, since unidentified publishers are allowed.
+ return new TopicPublisherImpl(this, topic);
+ }
+
+ /**
+ * Creates a nondurable subscriber to the specified topic.
+ *
+ * @param topic The Topic to subscribe to
+ * @throws JMSException If creating a subscriber fails due to some internal error.
+ * @throws InvalidDestinationException If an invalid topic is specified.
+ */
+ public TopicSubscriber createSubscriber(Topic topic) throws JMSException
+ {
+ return createSubscriber(topic, null, false);
+ }
+
+ /**
+ * Creates a nondurable subscriber to the specified topic, using a
+ * message selector or specifying whether messages published by its
+ * own connection should be delivered to it.
+ *
+ * @param topic The Topic to subscribe to
+ * @param messageSelector A value of null or an empty string indicates that there is no message selector.
+ * @param noLocal If true then inhibits the delivery of messages published by this subscriber's connection.
+ * @throws JMSException If creating a subscriber fails due to some internal error.
+ * @throws InvalidDestinationException If an invalid topic is specified.
+ * @throws InvalidSelectorException If the message selector is invalid.
+ */
+ public TopicSubscriber createSubscriber(Topic topic, String messageSelector, boolean noLocal) throws JMSException
+ {
+ checkNotClosed();
+ checkDestination(topic);
+ TopicSubscriber topicSubscriber;
+ try
+ {
+ topicSubscriber = new TopicSubscriberImpl(this, topic, messageSelector, noLocal, null,String.valueOf(_consumerTag.incrementAndGet()));
+ }
+ catch (Exception e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ return topicSubscriber;
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicSubscriberImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicSubscriberImpl.java
new file mode 100644
index 0000000000..73d03db055
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/TopicSubscriberImpl.java
@@ -0,0 +1,72 @@
+/* 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.qpidity.njms;
+
+import javax.jms.TopicSubscriber;
+import javax.jms.Topic;
+import javax.jms.JMSException;
+
+/**
+ * Implementation of the JMS TopicSubscriber interface.
+ */
+public class TopicSubscriberImpl extends MessageConsumerImpl implements TopicSubscriber
+{
+ //--- Constructor
+ /**
+ * Create a new TopicSubscriberImpl.
+ *
+ * @param session The session of this topic subscriber.
+ * @param topic The default topic for this TopicSubscriberImpl
+ * @param messageSelector The MessageSelector
+ * @param noLocal If true inhibits the delivery of messages published by its own connection.
+ * @param subscriptionName Name of the subscription if this is to be created as a durable subscriber.
+ * If this value is null, a non-durable subscription is created.
+ * @throws Exception If the TopicSubscriberImpl cannot be created due to internal error.
+ */
+ protected TopicSubscriberImpl(SessionImpl session, Topic topic, String messageSelector, boolean noLocal,
+ String subscriptionName,String consumerTag) throws Exception
+ {
+ super(session, (DestinationImpl) topic, messageSelector, noLocal, subscriptionName,consumerTag);
+ }
+
+ //--- javax.njms.TopicSubscriber interface
+ /**
+ * Get the Topic associated with this subscriber.
+ *
+ * @return This subscriber's Topic
+ * @throws JMSException if getting the topic for this topicSubscriber fails due to some internal error.
+ */
+ public Topic getTopic() throws JMSException
+ {
+ checkNotClosed();
+ return (TopicImpl) _destination;
+ }
+
+
+ /**
+ * Get NoLocal for this subscriber.
+ *
+ * @return True if locally published messages are being inhibited, false otherwise
+ * @throws JMSException If getting NoLocal for this topic subscriber fails due to some internal error.
+ */
+ public boolean getNoLocal() throws JMSException
+ {
+ checkNotClosed();
+ return _noLocal;
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/XAConnectionImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/XAConnectionImpl.java
new file mode 100644
index 0000000000..3d181db1f6
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/XAConnectionImpl.java
@@ -0,0 +1,71 @@
+/* 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.qpidity.njms;
+
+import org.apache.qpidity.QpidException;
+
+import javax.jms.XAConnection;
+import javax.jms.JMSException;
+import javax.jms.XASession;
+
+/**
+ * This class implements the javax.njms.XAConnection interface
+ */
+public class XAConnectionImpl extends ConnectionImpl implements XAConnection
+{
+ //-- constructor
+ /**
+ * Create a XAConnection.
+ *
+ * @param host The broker host name.
+ * @param port The port on which the broker is listening for connection.
+ * @param virtualHost The virtual host on which the broker is deployed.
+ * @param username The user name used of user identification.
+ * @param password The password name used of user identification.
+ * @throws QpidException If creating a connection fails due to some internal error.
+ */
+ protected XAConnectionImpl(String host, int port, String virtualHost, String username, String password) throws QpidException
+ {
+ super(host, port, virtualHost, username, password);
+ }
+
+ //-- interface XAConnection
+ /**
+ * Creates an XASession.
+ *
+ * @return A newly created XASession.
+ * @throws JMSException If the XAConnectiono fails to create an XASession due to
+ * some internal error.
+ */
+ public synchronized XASession createXASession() throws JMSException
+ {
+ checkNotClosed();
+ XASessionImpl xasession;
+ try
+ {
+ xasession = new XASessionImpl(this);
+ }
+ catch (QpidException e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ // add this session with the list of session that are handled by this connection
+ _sessions.add(xasession);
+ return xasession;
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/XAQueueConnectionImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/XAQueueConnectionImpl.java
new file mode 100644
index 0000000000..b500208036
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/XAQueueConnectionImpl.java
@@ -0,0 +1,72 @@
+/* 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.qpidity.njms;
+
+import org.apache.qpidity.QpidException;
+
+import javax.jms.XAQueueConnection;
+import javax.jms.JMSException;
+import javax.jms.XAQueueSession;
+
+/**
+ * Implements XAQueueConnection
+ */
+public class XAQueueConnectionImpl extends XAConnectionImpl implements XAQueueConnection
+{
+ //-- constructor
+ /**
+ * Create a XAQueueConnection.
+ *
+ * @param host The broker host name.
+ * @param port The port on which the broker is listening for connection.
+ * @param virtualHost The virtual host on which the broker is deployed.
+ * @param username The user name used of user identification.
+ * @param password The password name used of user identification.
+ * @throws QpidException If creating a XAQueueConnection fails due to some internal error.
+ */
+ public XAQueueConnectionImpl(String host, int port, String virtualHost, String username, String password)
+ throws QpidException
+ {
+ super(host, port, virtualHost, username, password);
+ }
+
+ //-- Interface XAQueueConnection
+ /**
+ * Creates an XAQueueSession.
+ *
+ * @return A newly created XASession.
+ * @throws JMSException If the XAQueueConnectionImpl fails to create an XASession due to
+ * some internal error.
+ */
+ public synchronized XAQueueSession createXAQueueSession() throws JMSException
+ {
+ checkNotClosed();
+ XAQueueSessionImpl xaQueueSession;
+ try
+ {
+ xaQueueSession = new XAQueueSessionImpl(this);
+ }
+ catch (QpidException e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ // add this session with the list of session that are handled by this connection
+ _sessions.add(xaQueueSession);
+ return xaQueueSession;
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/XAQueueSessionImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/XAQueueSessionImpl.java
new file mode 100644
index 0000000000..3df8a0928e
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/XAQueueSessionImpl.java
@@ -0,0 +1,64 @@
+/* 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.qpidity.njms;
+
+import org.apache.qpidity.QpidException;
+
+import javax.jms.QueueSession;
+import javax.jms.JMSException;
+import javax.jms.XAQueueSession;
+
+/**
+ * Imeplements javax.njms.XAQueueSessionImpl
+ */
+public class XAQueueSessionImpl extends XASessionImpl implements XAQueueSession
+{
+ /**
+ * The standard session
+ */
+ private QueueSession _jmsQueueSession;
+
+ //-- Constructors
+ /**
+ * Create a JMS XAQueueSessionImpl
+ *
+ * @param connection The ConnectionImpl object from which the Session is created.
+ * @throws org.apache.qpidity.QpidException
+ * In case of internal error.
+ */
+ protected XAQueueSessionImpl(ConnectionImpl connection) throws QpidException
+ {
+ super(connection);
+ }
+
+ //--- interface XAQueueSession
+ /**
+ * Gets the topic session associated with this <CODE>XATopicSession</CODE>.
+ *
+ * @return the topic session object
+ * @throws JMSException If an internal error occurs.
+ */
+ public QueueSession getQueueSession() throws JMSException
+ {
+ if (_jmsQueueSession == null)
+ {
+ _jmsQueueSession = getConnection().createQueueSession(true, getAcknowledgeMode());
+ }
+ return _jmsQueueSession;
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/XAResourceImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/XAResourceImpl.java
new file mode 100644
index 0000000000..37921ece4d
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/XAResourceImpl.java
@@ -0,0 +1,507 @@
+/* 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.qpidity.njms;
+
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import org.apache.qpidity.QpidException;
+import org.apache.qpidity.dtx.XidImpl;
+import org.apache.qpidity.transport.*;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is an implementation of javax.njms.XAResource.
+ */
+public class XAResourceImpl implements XAResource
+{
+ /**
+ * this XAResourceImpl's logger
+ */
+ private static final Logger _logger = LoggerFactory.getLogger(XAResourceImpl.class);
+
+ /**
+ * Reference to the associated XASession
+ */
+ private XASessionImpl _xaSession = null;
+
+ /**
+ * The XID of this resource
+ */
+ private Xid _xid;
+
+ //--- constructor
+
+ /**
+ * Create an XAResource associated with a XASession
+ *
+ * @param xaSession The session XAresource
+ */
+ protected XAResourceImpl(XASessionImpl xaSession)
+ {
+ _xaSession = xaSession;
+ }
+
+ //--- The XAResource
+ /**
+ * Commits the global transaction specified by xid.
+ *
+ * @param xid A global transaction identifier
+ * @param b If true, use a one-phase commit protocol to commit the work done on behalf of xid.
+ * @throws XAException An error has occurred. An error has occurred. Possible XAExceptions are XA_HEURHAZ,
+ * XA_HEURCOM, XA_HEURRB, XA_HEURMIX, XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or XAER_PROTO.
+ */
+ public void commit(Xid xid, boolean b) throws XAException
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("commit ", xid);
+ }
+ if (xid == null)
+ {
+ throw new XAException(XAException.XAER_PROTO);
+ }
+ Future<DtxCoordinationCommitResult> future;
+ try
+ {
+ future = _xaSession.getQpidSession()
+ .dtxCoordinationCommit(XidImpl.convertToString(xid), b ? Option.ONE_PHASE : Option.NO_OPTION);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Cannot convert Xid into String format ", e);
+ }
+ throw new XAException(XAException.XAER_PROTO);
+ }
+ // now wait on the future for the result
+ DtxCoordinationCommitResult result = future.get();
+ int status = result.getStatus();
+ switch (status)
+ {
+ case Constant.XA_OK:
+ // do nothing this ok
+ break;
+ case Constant.XA_HEURHAZ:
+ throw new XAException(XAException.XA_HEURHAZ);
+ case Constant.XA_HEURCOM:
+ throw new XAException(XAException.XA_HEURCOM);
+ case Constant.XA_HEURRB:
+ throw new XAException(XAException.XA_HEURRB);
+ case Constant.XA_HEURMIX:
+ throw new XAException(XAException.XA_HEURMIX);
+ case Constant.XA_RBROLLBACK:
+ throw new XAException(XAException.XA_RBROLLBACK);
+ case Constant.XA_RBTIMEOUT:
+ throw new XAException(XAException.XA_RBTIMEOUT);
+ default:
+ // this should not happen
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("got unexpected status value: ", status);
+ }
+ throw new XAException(XAException.XAER_PROTO);
+ }
+ }
+
+ /**
+ * Ends the work performed on behalf of a transaction branch.
+ * The resource manager disassociates the XA resource from the transaction branch specified
+ * and lets the transaction complete.
+ * <ul>
+ * <li> If TMSUSPEND is specified in the flags, the transaction branch is temporarily suspended in an incomplete state.
+ * The transaction context is in a suspended state and must be resumed via the start method with TMRESUME specified.
+ * <li> If TMFAIL is specified, the portion of work has failed. The resource manager may mark the transaction as rollback-only
+ * <li> If TMSUCCESS is specified, the portion of work has completed successfully.
+ * /ul>
+ *
+ * @param xid A global transaction identifier that is the same as the identifier used previously in the start method
+ * @param flag One of TMSUCCESS, TMFAIL, or TMSUSPEND.
+ * @throws XAException An error has occurred. An error has occurred. Possible XAException values are XAER_RMERR,
+ * XAER_RMFAILED, XAER_NOTA, XAER_INVAL, XAER_PROTO, or XA_RB*.
+ */
+ public void end(Xid xid, int flag) throws XAException
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("end ", xid);
+ }
+ if (xid == null)
+ {
+ throw new XAException(XAException.XAER_PROTO);
+ }
+ Future<DtxDemarcationEndResult> future;
+ try
+ {
+ future = _xaSession.getQpidSession()
+ .dtxDemarcationEnd(XidImpl.convertToString(xid),
+ flag == XAResource.TMFAIL ? Option.FAIL : Option.NO_OPTION,
+ flag == XAResource.TMSUSPEND ? Option.SUSPEND : Option.NO_OPTION);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Cannot convert Xid into String format ", e);
+ }
+ throw new XAException(XAException.XAER_PROTO);
+ }
+ // now wait on the future for the result
+ DtxDemarcationEndResult result = future.get();
+ int status = result.getStatus();
+ switch (status)
+ {
+ case Constant.XA_OK:
+ // do nothing this ok
+ break;
+ case Constant.XA_RBROLLBACK:
+ throw new XAException(XAException.XA_RBROLLBACK);
+ case Constant.XA_RBTIMEOUT:
+ throw new XAException(XAException.XA_RBTIMEOUT);
+ default:
+ // this should not happen
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("got unexpected status value: ", status);
+ }
+ throw new XAException(XAException.XAER_PROTO);
+ }
+ }
+
+ /**
+ * Tells the resource manager to forget about a heuristically completed transaction branch.
+ *
+ * @param xid String(xid.getGlobalTransactionId() A global transaction identifier
+ * @throws XAException An error has occurred. Possible exception values are XAER_RMERR, XAER_RMFAIL,
+ * XAER_NOTA, XAER_INVAL, or XAER_PROTO.
+ */
+ public void forget(Xid xid) throws XAException
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("forget ", xid);
+ }
+ if (xid == null)
+ {
+ throw new XAException(XAException.XAER_PROTO);
+ }
+ _xaSession.getQpidSession().dtxCoordinationForget(new String(xid.getGlobalTransactionId()));
+ }
+
+ /**
+ * Obtains the current transaction timeout value set for this XAResource instance.
+ * If XAResource.setTransactionTimeout was not used prior to invoking this method,
+ * the return value is the default timeout i.e. 0;
+ * otherwise, the value used in the previous setTransactionTimeout call is returned.
+ *
+ * @return The transaction timeout value in seconds.
+ * @throws XAException An error has occurred. Possible exception values are XAER_RMERR, XAER_RMFAIL.
+ */
+ public int getTransactionTimeout() throws XAException
+ {
+ int result = 0;
+ if (_xid != null)
+ {
+ try
+ {
+ Future<DtxCoordinationGetTimeoutResult> future =
+ _xaSession.getQpidSession().dtxCoordinationGetTimeout(XidImpl.convertToString(_xid));
+ result = (int) future.get().getTimeout();
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Cannot convert Xid into String format ", e);
+ }
+ throw new XAException(XAException.XAER_PROTO);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method is called to determine if the resource manager instance represented
+ * by the target object is the same as the resouce manager instance represented by
+ * the parameter xaResource.
+ *
+ * @param xaResource An XAResource object whose resource manager instance is to
+ * be compared with the resource manager instance of the target object
+ * @return <code>true</code> if it's the same RM instance; otherwise <code>false</code>.
+ * @throws XAException An error has occurred. Possible exception values are XAER_RMERR, XAER_RMFAIL.
+ */
+ public boolean isSameRM(XAResource xaResource) throws XAException
+ {
+ // TODO : get the server identity of xaResource and compare it with our own one
+ return false;
+ }
+
+ /**
+ * Prepare for a transaction commit of the transaction specified in <code>Xid</code>.
+ *
+ * @param xid A global transaction identifier.
+ * @return A value indicating the resource manager's vote on the outcome of the transaction.
+ * The possible values are: XA_RDONLY or XA_OK.
+ * @throws XAException An error has occurred. Possible exception values are: XAER_RMERR or XAER_NOTA
+ */
+ public int prepare(Xid xid) throws XAException
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("prepare ", xid);
+ }
+ if (xid == null)
+ {
+ throw new XAException(XAException.XAER_PROTO);
+ }
+ Future<DtxCoordinationPrepareResult> future;
+ try
+ {
+ future = _xaSession.getQpidSession()
+ .dtxCoordinationPrepare(XidImpl.convertToString(xid));
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Cannot convert Xid into String format ", e);
+ }
+ throw new XAException(XAException.XAER_PROTO);
+ }
+ DtxCoordinationPrepareResult result = future.get();
+ int status = result.getStatus();
+ int outcome;
+ switch (status)
+ {
+ case Constant.XA_OK:
+ outcome = XAResource.XA_OK;
+ break;
+ case Constant.XA_RDONLY:
+ outcome = XAResource.XA_RDONLY;
+ break;
+ case Constant.XA_RBROLLBACK:
+ throw new XAException(XAException.XA_RBROLLBACK);
+ case Constant.XA_RBTIMEOUT:
+ throw new XAException(XAException.XA_RBTIMEOUT);
+ default:
+ // this should not happen
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("got unexpected status value: ", status);
+ }
+ throw new XAException(XAException.XAER_PROTO);
+ }
+ return outcome;
+ }
+
+ /**
+ * Obtains a list of prepared transaction branches.
+ * <p/>
+ * The transaction manager calls this method during recovery to obtain the list of transaction branches
+ * that are currently in prepared or heuristically completed states.
+ *
+ * @param flag One of TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS.
+ * TMNOFLAGS must be used when no other flags are set in the parameter.
+ * @return zero or more XIDs of the transaction branches that are currently in a prepared or heuristically
+ * completed state.
+ * @throws XAException An error has occurred. Possible value is XAER_INVAL.
+ */
+ public Xid[] recover(int flag) throws XAException
+ {
+ // the flag is ignored
+ Future<DtxCoordinationRecoverResult> future = _xaSession.getQpidSession().dtxCoordinationRecover();
+ DtxCoordinationRecoverResult res = future.get();
+ // todo make sure that the keys of the returned map are the xids
+ Xid[] result = new Xid[res.getInDoubt().size()];
+ int i = 0;
+ try
+ {
+ for (String xid : res.getInDoubt().keySet())
+ {
+ result[i] = new XidImpl(xid);
+ i++;
+ }
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Cannot convert string into Xid ", e);
+ }
+ throw new XAException(XAException.XAER_PROTO);
+ }
+ return result;
+ }
+
+ /**
+ * Informs the resource manager to roll back work done on behalf of a transaction branch
+ *
+ * @param xid A global transaction identifier.
+ * @throws XAException An error has occurred.
+ */
+ public void rollback(Xid xid) throws XAException
+ {
+ if (xid == null)
+ {
+ throw new XAException(XAException.XAER_PROTO);
+ }
+ // the flag is ignored
+ Future<DtxCoordinationRollbackResult> future;
+ try
+ {
+ future = _xaSession.getQpidSession()
+ .dtxCoordinationRollback(XidImpl.convertToString(xid));
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Cannot convert Xid into String format ", e);
+ }
+ throw new XAException(XAException.XAER_PROTO);
+ }
+ // now wait on the future for the result
+ DtxCoordinationRollbackResult result = future.get();
+ int status = result.getStatus();
+ switch (status)
+ {
+ case Constant.XA_OK:
+ // do nothing this ok
+ break;
+ case Constant.XA_HEURHAZ:
+ throw new XAException(XAException.XA_HEURHAZ);
+ case Constant.XA_HEURCOM:
+ throw new XAException(XAException.XA_HEURCOM);
+ case Constant.XA_HEURRB:
+ throw new XAException(XAException.XA_HEURRB);
+ case Constant.XA_HEURMIX:
+ throw new XAException(XAException.XA_HEURMIX);
+ case Constant.XA_RBROLLBACK:
+ throw new XAException(XAException.XA_RBROLLBACK);
+ case Constant.XA_RBTIMEOUT:
+ throw new XAException(XAException.XA_RBTIMEOUT);
+ default:
+ // this should not happen
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("got unexpected status value: ", status);
+ }
+ throw new XAException(XAException.XAER_PROTO);
+ }
+ }
+
+ /**
+ * Sets the current transaction timeout value for this XAResource instance.
+ * Once set, this timeout value is effective until setTransactionTimeout is
+ * invoked again with a different value.
+ * To reset the timeout value to the default value used by the resource manager, set the value to zero.
+ *
+ * @param timeout The transaction timeout value in seconds.
+ * @return true if transaction timeout value is set successfully; otherwise false.
+ * @throws XAException An error has occurred. Possible exception values are XAER_RMERR, XAER_RMFAIL, or XAER_INVAL.
+ */
+ public boolean setTransactionTimeout(int timeout) throws XAException
+ {
+ boolean result = false;
+ if (_xid != null)
+ {
+ try
+ {
+ _xaSession.getQpidSession()
+ .dtxCoordinationSetTimeout(XidImpl.convertToString(_xid), timeout);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Cannot convert Xid into String format ", e);
+ }
+ throw new XAException(XAException.XAER_PROTO);
+ }
+ result = true;
+ }
+ return result;
+ }
+
+ /**
+ * Starts work on behalf of a transaction branch specified in xid.
+ * <ul>
+ * <li> If TMJOIN is specified, an exception is thrown as it is not supported
+ * <li> If TMRESUME is specified, the start applies to resuming a suspended transaction specified in the parameter xid.
+ * <li> If neither TMJOIN nor TMRESUME is specified and the transaction specified by xid has previously been seen by the
+ * resource manager, the resource manager throws the XAException exception with XAER_DUPID error code.
+ * </ul>
+ *
+ * @param xid A global transaction identifier to be associated with the resource
+ * @param flag One of TMNOFLAGS, TMJOIN, or TMRESUME
+ * @throws XAException An error has occurred. Possible exceptions
+ * are XA_RB*, XAER_RMERR, XAER_RMFAIL, XAER_DUPID, XAER_OUTSIDE, XAER_NOTA, XAER_INVAL, or XAER_PROTO.
+ */
+ public void start(Xid xid, int flag) throws XAException
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("start ", xid);
+ }
+ if (xid == null)
+ {
+ throw new XAException(XAException.XAER_PROTO);
+ }
+ _xid = xid;
+ Future<DtxDemarcationStartResult> future;
+ try
+ {
+ future = _xaSession.getQpidSession()
+ .dtxDemarcationStart(XidImpl.convertToString(xid),
+ flag == XAResource.TMJOIN ? Option.JOIN : Option.NO_OPTION,
+ flag == XAResource.TMRESUME ? Option.RESUME : Option.NO_OPTION);
+ }
+ catch (QpidException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Cannot convert Xid into String format ", e);
+ }
+ throw new XAException(XAException.XAER_PROTO);
+ }
+ // now wait on the future for the result
+ DtxDemarcationStartResult result = future.get();
+ int status = result.getStatus();
+ switch (status)
+ {
+ case Constant.XA_OK:
+ // do nothing this ok
+ break;
+ case Constant.XA_RBROLLBACK:
+ throw new XAException(XAException.XA_RBROLLBACK);
+ case Constant.XA_RBTIMEOUT:
+ throw new XAException(XAException.XA_RBTIMEOUT);
+ default:
+ // this should not happen
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("got unexpected status value: ", status);
+ }
+ throw new XAException(XAException.XAER_PROTO);
+ }
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/XASessionImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/XASessionImpl.java
new file mode 100644
index 0000000000..65f57b528c
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/XASessionImpl.java
@@ -0,0 +1,126 @@
+/* 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.qpidity.njms;
+
+import org.apache.qpidity.QpidException;
+import org.apache.qpidity.nclient.DtxSession;
+
+import javax.jms.XASession;
+import javax.jms.Session;
+import javax.jms.JMSException;
+import javax.jms.TransactionInProgressException;
+import javax.transaction.xa.XAResource;
+
+/**
+ * This is an implementation of the javax.njms.XASEssion interface.
+ */
+public class XASessionImpl extends SessionImpl implements XASession
+{
+ /**
+ * XAResource associated with this XASession
+ */
+ private final XAResourceImpl _xaResource;
+
+ /**
+ * This XASession Qpid DtxSession
+ */
+ private DtxSession _qpidDtxSession;
+
+ /**
+ * The standard session
+ */
+ private Session _jmsSession;
+
+ //-- Constructors
+ /**
+ * Create a JMS XASession
+ *
+ * @param connection The ConnectionImpl object from which the Session is created.
+ * @throws QpidException In case of internal error.
+ */
+ protected XASessionImpl(ConnectionImpl connection) throws QpidException
+ {
+ super(connection, true, // this is a transacted session
+ Session.SESSION_TRANSACTED, // the ack mode is transacted
+ true); // this is an XA session so do not set tx
+ _qpidDtxSession = getConnection().getQpidConnection().createDTXSession(0);
+ _xaResource = new XAResourceImpl(this);
+ }
+
+ //--- javax.njms.XASEssion API
+
+ /**
+ * Gets the session associated with this XASession.
+ *
+ * @return The session object.
+ * @throws JMSException if an internal error occurs.
+ */
+ public Session getSession() throws JMSException
+ {
+ if( _jmsSession == null )
+ {
+ _jmsSession = getConnection().createSession(true, getAcknowledgeMode());
+ }
+ return _jmsSession;
+ }
+
+ /**
+ * Returns an XA resource.
+ *
+ * @return An XA resource.
+ */
+ public XAResource getXAResource()
+ {
+ return _xaResource;
+ }
+
+ //-- overwritten mehtods
+ /**
+ * Throws a {@link TransactionInProgressException}, since it should
+ * not be called for an XASession object.
+ *
+ * @throws TransactionInProgressException always.
+ */
+ public void commit() throws JMSException
+ {
+ throw new TransactionInProgressException(
+ "XASession: A direct invocation of the commit operation is probibited!");
+ }
+
+ /**
+ * Throws a {@link TransactionInProgressException}, since it should
+ * not be called for an XASession object.
+ *
+ * @throws TransactionInProgressException always.
+ */
+ public void rollback() throws JMSException
+ {
+ throw new TransactionInProgressException(
+ "XASession: A direct invocation of the rollback operation is probibited!");
+ }
+
+ /**
+ * Access to the underlying Qpid Session
+ *
+ * @return The associated Qpid Session.
+ */
+ protected org.apache.qpidity.nclient.DtxSession getQpidSession()
+ {
+ return _qpidDtxSession;
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/XATopicConnectionImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/XATopicConnectionImpl.java
new file mode 100644
index 0000000000..d7e56e5af9
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/XATopicConnectionImpl.java
@@ -0,0 +1,71 @@
+/* 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.qpidity.njms;
+
+import org.apache.qpidity.QpidException;
+
+import javax.jms.XATopicConnection;
+import javax.jms.JMSException;
+import javax.jms.XATopicSession;
+
+/**
+ * implements javax.njms.XATopicConnection
+ */
+public class XATopicConnectionImpl extends XAConnectionImpl implements XATopicConnection
+{
+ //-- constructor
+ /**
+ * Create a XATopicConnection.
+ *
+ * @param host The broker host name.
+ * @param port The port on which the broker is listening for connection.
+ * @param virtualHost The virtual host on which the broker is deployed.
+ * @param username The user name used of user identification.
+ * @param password The password name used of user identification.
+ * @throws QpidException If creating a XATopicConnection fails due to some internal error.
+ */
+ public XATopicConnectionImpl(String host, int port, String virtualHost, String username, String password)
+ throws QpidException
+ {
+ super(host, port, virtualHost, username, password);
+ }
+
+ /**
+ * Creates an XATopicSession.
+ *
+ * @return A newly created XATopicSession.
+ * @throws JMSException If the XAConnectiono fails to create an XATopicSession due to
+ * some internal error.
+ */
+ public synchronized XATopicSession createXATopicSession() throws JMSException
+ {
+ checkNotClosed();
+ XATopicSessionImpl xaTopicSession;
+ try
+ {
+ xaTopicSession = new XATopicSessionImpl(this);
+ }
+ catch (QpidException e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ // add this session with the list of session that are handled by this connection
+ _sessions.add(xaTopicSession);
+ return xaTopicSession;
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/XATopicSessionImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/XATopicSessionImpl.java
new file mode 100644
index 0000000000..bf76669470
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/XATopicSessionImpl.java
@@ -0,0 +1,63 @@
+/* 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.qpidity.njms;
+
+import org.apache.qpidity.QpidException;
+
+import javax.jms.*;
+
+/**
+ * Implements javax.njms.XATopicSession and javax.njms.TopicSession
+ */
+public class XATopicSessionImpl extends XASessionImpl implements XATopicSession
+{
+ /**
+ * The standard session
+ */
+ private TopicSession _jmsTopicSession;
+
+ //-- Constructors
+ /**
+ * Create a JMS XASession
+ *
+ * @param connection The ConnectionImpl object from which the Session is created.
+ * @throws org.apache.qpidity.QpidException
+ * In case of internal error.
+ */
+ protected XATopicSessionImpl(ConnectionImpl connection) throws QpidException
+ {
+ super(connection);
+ }
+
+ //--- interface XATopicSession
+
+ /**
+ * Gets the topic session associated with this <CODE>XATopicSession</CODE>.
+ *
+ * @return the topic session object
+ * @throws JMSException If an internal error occurs.
+ */
+ public TopicSession getTopicSession() throws JMSException
+ {
+ if (_jmsTopicSession == null)
+ {
+ _jmsTopicSession = getConnection().createTopicSession(true, getAcknowledgeMode());
+ }
+ return _jmsTopicSession;
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/BytesMessageImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/BytesMessageImpl.java
new file mode 100644
index 0000000000..8a5f910d03
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/BytesMessageImpl.java
@@ -0,0 +1,863 @@
+/* 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.qpidity.njms.message;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.qpidity.QpidException;
+
+import javax.jms.*;
+import java.io.*;
+import java.nio.ByteBuffer;
+
+/**
+ * Implements javax.njms.BytesMessage
+ */
+public class BytesMessageImpl extends MessageImpl implements BytesMessage
+{
+ /**
+ * this BytesMessageImpl's logger
+ */
+ private static final Logger _logger = LoggerFactory.getLogger(BytesMessageImpl.class);
+
+ /**
+ * An input stream for reading this message data
+ * This stream wrappes the received byteBuffer.
+ */
+ protected DataInputStream _dataIn = null;
+
+ /**
+ * Used to store written data.
+ */
+ protected ByteArrayOutputStream _storedData = new ByteArrayOutputStream();
+
+ /**
+ * DataOutputStream used to write the data
+ */
+ protected DataOutputStream _dataOut = new DataOutputStream(_storedData);
+
+ //--- Constructor
+ /**
+ * Constructor used by SessionImpl.
+ */
+ public BytesMessageImpl()
+ {
+ super();
+ setMessageType(String.valueOf(MessageFactory.JAVAX_JMS_BYTESMESSAGE));
+ }
+
+ /**
+ * Constructor used by MessageFactory
+ *
+ * @param message The new qpid message.
+ * @throws QpidException In case of problem when receiving the message body.
+ */
+ protected BytesMessageImpl(org.apache.qpidity.api.Message message) throws QpidException
+ {
+ super(message);
+ }
+
+ //--- BytesMessage API
+ /**
+ * Gets the number of bytes of the message body when the message
+ * is in read-only mode.
+ * <p> The value returned is the entire length of the message
+ * body, regardless of where the pointer for reading the message
+ * is currently located.
+ *
+ * @return Number of bytes in the message
+ * @throws JMSException If reading the message body length fails due to some error.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ */
+ public long getBodyLength() throws JMSException
+ {
+ isReadable();
+ return getMessageData().capacity();
+ }
+
+ /**
+ * Reads a boolean.
+ *
+ * @return The boolean value read
+ * @throws JMSException If reading fails due to some error.
+ * @throws MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ */
+ public boolean readBoolean() throws JMSException
+ {
+ isReadable();
+ try
+ {
+ return _dataIn.readBoolean();
+ }
+ catch (EOFException e)
+ {
+ throw new MessageEOFException("Reach end of data when reading message data");
+ }
+ catch (IOException ioe)
+ {
+ throw new JMSException("Problem when reading data", ioe.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Reads a signed 8-bit.
+ *
+ * @return The signed 8-bit read
+ * @throws JMSException If reading a signed 8-bit fails due to some error.
+ * @throws MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ */
+ public byte readByte() throws JMSException
+ {
+ isReadable();
+ try
+ {
+ return _dataIn.readByte();
+ }
+ catch (EOFException e)
+ {
+ throw new MessageEOFException("Reach end of data when reading message data");
+ }
+ catch (IOException ioe)
+ {
+ throw new JMSException("Problem when reading data", ioe.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Reads an unsigned 8-bit.
+ *
+ * @return The signed 8-bit read
+ * @throws JMSException If reading an unsigned 8-bit fails due to some error.
+ * @throws MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ */
+ public int readUnsignedByte() throws JMSException
+ {
+ isReadable();
+ try
+ {
+ return _dataIn.readUnsignedByte();
+ }
+ catch (EOFException e)
+ {
+ throw new MessageEOFException("Reach end of data when reading message data");
+ }
+ catch (IOException ioe)
+ {
+ throw new JMSException("Problem when reading data", ioe.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Reads a short.
+ *
+ * @return The short read
+ * @throws JMSException If reading a short fails due to some error.
+ * @throws MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ */
+ public short readShort() throws JMSException
+ {
+ isReadable();
+ try
+ {
+ return _dataIn.readShort();
+ }
+ catch (EOFException e)
+ {
+ throw new MessageEOFException("Reach end of data when reading message data");
+ }
+ catch (IOException ioe)
+ {
+ throw new JMSException("Problem when reading data", ioe.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Reads an unsigned short.
+ *
+ * @return The unsigned short read
+ * @throws JMSException If reading an unsigned short fails due to some error.
+ * @throws MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ */
+ public int readUnsignedShort() throws JMSException
+ {
+ isReadable();
+ try
+ {
+ return _dataIn.readUnsignedShort();
+ }
+ catch (EOFException e)
+ {
+ throw new MessageEOFException("Reach end of data when reading message data");
+ }
+ catch (IOException ioe)
+ {
+ throw new JMSException("Problem when reading data", ioe.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Reads a char.
+ *
+ * @return The char read
+ * @throws JMSException If reading a char fails due to some error.
+ * @throws MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ */
+ public char readChar() throws JMSException
+ {
+ isReadable();
+ try
+ {
+ return _dataIn.readChar();
+ }
+ catch (EOFException e)
+ {
+ throw new MessageEOFException("Reach end of data when reading message data");
+ }
+ catch (IOException ioe)
+ {
+ throw new JMSException("Problem when reading data", ioe.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Reads an int.
+ *
+ * @return The int read
+ * @throws JMSException If reading an int fails due to some error.
+ * @throws MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ */
+ public int readInt() throws JMSException
+ {
+ isReadable();
+ try
+ {
+ return _dataIn.readInt();
+ }
+ catch (EOFException e)
+ {
+ throw new MessageEOFException("Reach end of data when reading message data");
+ }
+ catch (IOException ioe)
+ {
+ throw new JMSException("Problem when reading data", ioe.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Reads a long.
+ *
+ * @return The long read
+ * @throws JMSException If reading a long fails due to some error.
+ * @throws MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ */
+ public long readLong() throws JMSException
+ {
+ isReadable();
+ try
+ {
+ return _dataIn.readLong();
+ }
+ catch (EOFException e)
+ {
+ throw new MessageEOFException("Reach end of data when reading message data");
+ }
+ catch (IOException ioe)
+ {
+ throw new JMSException("Problem when reading data", ioe.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Read a float.
+ *
+ * @return The float read
+ * @throws JMSException If reading a float fails due to some error.
+ * @throws MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ */
+ public float readFloat() throws JMSException
+ {
+ isReadable();
+ try
+ {
+ return _dataIn.readFloat();
+ }
+ catch (EOFException e)
+ {
+ throw new MessageEOFException("Reach end of data when reading message data");
+ }
+ catch (IOException ioe)
+ {
+ throw new JMSException("Problem when reading data", ioe.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Read a double.
+ *
+ * @return The double read
+ * @throws JMSException If reading a double fails due to some error.
+ * @throws MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ */
+ public double readDouble() throws JMSException
+ {
+ isReadable();
+ try
+ {
+ return _dataIn.readDouble();
+ }
+ catch (EOFException e)
+ {
+ throw new MessageEOFException("Reach end of data when reading message data");
+ }
+ catch (IOException ioe)
+ {
+ throw new JMSException("Problem when reading data", ioe.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Reads a string that has been encoded using a modified UTF-8 format.
+ *
+ * @return The String read
+ * @throws JMSException If reading a String fails due to some error.
+ * @throws MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ */
+ public String readUTF() throws JMSException
+ {
+ isReadable();
+ try
+ {
+ return _dataIn.readUTF();
+ }
+ catch (EOFException e)
+ {
+ throw new MessageEOFException("Reach end of data when reading message data");
+ }
+ catch (IOException ioe)
+ {
+ throw new JMSException("Problem when reading data", ioe.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Reads a byte array from the bytes message data.
+ * <p> JMS sepc says:
+ * <P>If the length of array <code>bytes</code> is less than the number of
+ * bytes remaining to be read from the stream, the array should
+ * be filled. A subsequent call reads the next increment, and so on.
+ * <P>If the number of bytes remaining in the stream is less than the
+ * length of
+ * array <code>bytes</code>, the bytes should be read into the array.
+ * The return value of the total number of bytes read will be less than
+ * the length of the array, indicating that there are no more bytes left
+ * to be read from the stream. The next read of the stream returns -1.
+ *
+ * @param b The array into which the data is read.
+ * @return The total number of bytes read into the buffer, or -1 if
+ * there is no more data because the end of the stream has been reached
+ * @throws JMSException If reading a byte array fails due to some error.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ */
+ public int readBytes(byte[] b) throws JMSException
+ {
+ isReadable();
+ try
+ {
+ return _dataIn.read(b);
+ }
+ catch (IOException ioe)
+ {
+ throw new JMSException("Problem when reading data", ioe.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Reads a portion of the bytes message data.
+ * <p> The JMS spec says
+ * <P>If the length of array <code>b</code> is less than the number of
+ * bytes remaining to be read from the stream, the array should
+ * be filled. A subsequent call reads the next increment, and so on.
+ * <P>If the number of bytes remaining in the stream is less than the
+ * length of array <code>b</code>, the bytes should be read into the array.
+ * The return value of the total number of bytes read will be less than
+ * the length of the array, indicating that there are no more bytes left
+ * to be read from the stream. The next read of the stream returns -1.
+ * <p> If <code>length</code> is negative, or
+ * <code>length</code> is greater than the length of the array
+ * <code>b</code>, then an <code>IndexOutOfBoundsException</code> is
+ * thrown. No bytes will be read from the stream for this exception case.
+ *
+ * @param b The buffer into which the data is read
+ * @param length The number of bytes to read; must be less than or equal to length.
+ * @return The total number of bytes read into the buffer, or -1 if
+ * there is no more data because the end of the data has been reached
+ * @throws JMSException If reading a byte array fails due to some error.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ */
+ public int readBytes(byte[] b, int length) throws JMSException
+ {
+ isReadable();
+ try
+ {
+ return _dataIn.read(b, 0, length);
+ }
+ catch (IOException ioe)
+ {
+ throw new JMSException("Problem when reading data", ioe.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes a boolean to the bytes message.
+ *
+ * @param val The boolean value to be written
+ * @throws JMSException If writting a boolean fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeBoolean(boolean val) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeBoolean(val);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes a byte to the bytes message.
+ *
+ * @param val The byte value to be written
+ * @throws JMSException If writting a byte fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeByte(byte val) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeByte(val);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes a short to the bytes message.
+ *
+ * @param val The short value to be written
+ * @throws JMSException If writting a short fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeShort(short val) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeShort(val);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+
+ }
+
+ /**
+ * Writes a char to the bytes message.
+ *
+ * @param c The char value to be written
+ * @throws JMSException If writting a char fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeChar(char c) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeChar(c);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes an int to the bytes message.
+ *
+ * @param val The int value to be written
+ * @throws JMSException If writting an int fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeInt(int val) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeInt(val);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+
+ }
+
+ /**
+ * Writes a long to the bytes message.
+ *
+ * @param val The long value to be written
+ * @throws JMSException If writting a long fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeLong(long val) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeLong(val);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes a float to the bytes message.
+ *
+ * @param val The float value to be written
+ * @throws JMSException If writting a float fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeFloat(float val) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeFloat(val);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes a double to the bytes message.
+ *
+ * @param val The double value to be written
+ * @throws JMSException If writting a double fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeDouble(double val) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeDouble(val);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes a string to the bytes message.
+ *
+ * @param val The string value to be written
+ * @throws JMSException If writting a string fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeUTF(String val) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeUTF(val);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+
+ }
+
+ /**
+ * Writes a byte array to the bytes message.
+ *
+ * @param bytes The byte array value to be written
+ * @throws JMSException If writting a byte array fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeBytes(byte[] bytes) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.write(bytes);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes a portion of byte array to the bytes message.
+ *
+ * @param val The byte array value to be written
+ * @throws JMSException If writting a byte array fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeBytes(byte[] val, int offset, int length) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.write(val, offset, length);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes an Object to the bytes message.
+ * JMS spec says:
+ * <p>This method works only for the objectified primitive
+ * object types Integer, Double, Long, String and byte
+ * arrays.
+ *
+ * @param val The short value to be written
+ * @throws JMSException If writting a short fails due to some error.
+ * @throws NullPointerException if the parameter val is null.
+ * @throws MessageFormatException If the object is of an invalid type.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeObject(Object val) throws JMSException
+ {
+ if (val == null)
+ {
+ throw new NullPointerException("Cannot write null value to message");
+ }
+ if (val instanceof byte[])
+ {
+ writeBytes((byte[]) val);
+ }
+ else if (val instanceof String)
+ {
+ writeUTF((String) val);
+ }
+ else if (val instanceof Boolean)
+ {
+ writeBoolean(((Boolean) val).booleanValue());
+ }
+ else if (val instanceof Number)
+ {
+ if (val instanceof Byte)
+ {
+ writeByte(((Byte) val).byteValue());
+ }
+ else if (val instanceof Short)
+ {
+ writeShort(((Short) val).shortValue());
+ }
+ else if (val instanceof Integer)
+ {
+ writeInt(((Integer) val).intValue());
+ }
+ else if (val instanceof Long)
+ {
+ writeLong(((Long) val).longValue());
+ }
+ else if (val instanceof Float)
+ {
+ writeFloat(((Float) val).floatValue());
+ }
+ else if (val instanceof Double)
+ {
+ writeDouble(((Double) val).doubleValue());
+ }
+ else
+ {
+ throw new MessageFormatException("Trying to write an invalid obejct type: " + val);
+ }
+ }
+ else if (val instanceof Character)
+ {
+ writeChar(((Character) val).charValue());
+ }
+ else
+ {
+ throw new MessageFormatException("Trying to write an invalid obejct type: " + val);
+ }
+ }
+
+ /**
+ * Puts the message body in read-only mode and repositions the stream of
+ * bytes to the beginning.
+ *
+ * @throws JMSException If resetting the message fails due to some internal error.
+ * @throws MessageFormatException If the message has an invalid format.
+ */
+ public void reset() throws JMSException
+ {
+ _readOnly = true;
+ if (_dataIn == null)
+ {
+ // We were writting on this messsage so now read it
+ _dataIn = new DataInputStream(new ByteArrayInputStream(_storedData.toByteArray()));
+ }
+ else
+ {
+ // We were reading so reset it
+ try
+ {
+ _dataIn.reset();
+ }
+ catch (IOException e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ // we log this exception as this should not happen
+ _logger.debug("Problem when resetting message: ", e);
+ }
+ throw new JMSException("Problem when resetting message: " + e.getLocalizedMessage());
+ }
+ }
+ }
+
+ //-- overwritten methods
+ /**
+ * Clear out the message body. Clearing a message's body does not clear
+ * its header values or property entries.
+ * <p>If this message body was read-only, calling this method leaves
+ * the message body is in the same state as an empty body in a newly
+ * created message.
+ *
+ * @throws JMSException If clearing this message body fails to due to some error.
+ */
+ public void clearBody() throws JMSException
+ {
+ super.clearBody();
+ _dataIn = null;
+ _storedData = new ByteArrayOutputStream();
+ _dataOut = new DataOutputStream(_storedData);
+ }
+
+
+ /**
+ * This method is invoked before a message dispatch operation.
+ *
+ * @throws org.apache.qpidity.QpidException
+ * If the destination is not set
+ */
+ public void beforeMessageDispatch() throws QpidException
+ {
+ if (_dataOut.size() > 0)
+ {
+ setMessageData(ByteBuffer.wrap(_storedData.toByteArray()));
+ }
+ super.beforeMessageDispatch();
+ }
+
+ /**
+ * This method is invoked after this message is received.
+ *
+ * @throws QpidException
+ */
+ @Override
+ public void afterMessageReceive() throws QpidException
+ {
+ super.afterMessageReceive();
+ ByteBuffer messageData = getMessageData();
+ if (messageData != null)
+ {
+ try
+ {
+ _dataIn = new DataInputStream(asInputStream());
+ }
+ catch (Exception e)
+ {
+ throw new QpidException("Cannot retrieve data from message ", null, e);
+ }
+ }
+ }
+
+ //-- helper mehtods
+ /**
+ * Test whether this message is readable by throwing a MessageNotReadableException if this
+ * message cannot be read.
+ *
+ * @throws MessageNotReadableException If this message cannot be read.
+ */
+ protected void isReadable() throws MessageNotReadableException
+ {
+ if (_dataIn == null)
+ {
+ throw new MessageNotReadableException("Cannot read this message");
+ }
+ }
+}
+
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/MapMessageImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/MapMessageImpl.java
new file mode 100644
index 0000000000..26012e63b3
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/MapMessageImpl.java
@@ -0,0 +1,628 @@
+/* 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.qpidity.njms.message;
+
+import org.apache.qpidity.QpidException;
+
+import javax.jms.MapMessage;
+import javax.jms.JMSException;
+import javax.jms.MessageFormatException;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Vector;
+import java.io.*;
+import java.nio.ByteBuffer;
+
+/**
+ * Implements javax.njms.MapMessage
+ */
+public class MapMessageImpl extends MessageImpl implements MapMessage
+{
+
+ /**
+ * The MapMessage's payload.
+ */
+ private Map<String, Object> _map = new HashMap<String, Object>();
+
+ //--- Constructor
+ /**
+ * Constructor used by SessionImpl.
+ */
+ public MapMessageImpl()
+ {
+ super();
+ setMessageType(String.valueOf(MessageFactory.JAVAX_JMS_MAPMESSAGE));
+ }
+
+ /**
+ * Constructor used by MessageFactory
+ *
+ * @param message The new qpid message.
+ * @throws QpidException In case of IO problem when reading the received message.
+ */
+ protected MapMessageImpl(org.apache.qpidity.api.Message message) throws QpidException
+ {
+ super(message);
+ }
+
+ //-- Map Message API
+ /**
+ * Indicates whether an key exists in this MapMessage.
+ *
+ * @param key the name of the key to test
+ * @return true if the key exists
+ * @throws JMSException If determinein if the key exists fails due to some internal error
+ */
+ public boolean itemExists(String key) throws JMSException
+ {
+ return _map.containsKey(key);
+ }
+
+ /**
+ * Returns the booleanvalue with the specified key.
+ *
+ * @param key The key name.
+ * @return The boolean value with the specified key.
+ * @throws JMSException If reading the message fails due to some internal error.
+ * @throws javax.jms.MessageFormatException
+ * If this type conversion is invalid.
+ */
+ public boolean getBoolean(String key) throws JMSException
+ {
+ boolean result = false;
+ if (_map.containsKey(key))
+ {
+ try
+ {
+ Object objValue = _map.get(key);
+ if (objValue != null)
+ {
+ result = MessageHelper.convertToBoolean(_map.get(key));
+ }
+ }
+ catch (ClassCastException e)
+ {
+ throw new MessageFormatException("Wrong type for key: " + key);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns the byte value with the specified name.
+ *
+ * @param key The key name.
+ * @return The byte value with the specified key.
+ * @throws JMSException If reading the message fails due to some internal error.
+ * @throws javax.jms.MessageFormatException
+ * If this type conversion is invalid.
+ */
+ public byte getByte(String key) throws JMSException
+ {
+ Object objValue = _map.get(key);
+ if (objValue == null)
+ {
+ throw new NumberFormatException("Wrong type for key: " + key);
+ }
+ return MessageHelper.convertToByte(objValue);
+ }
+
+ /**
+ * Returns the <CODE>short</CODE> value with the specified name.
+ *
+ * @param key The key name.
+ * @return The <CODE>short</CODE> value with the specified key.
+ * @throws JMSException If reading the message fails due to some internal error.
+ * @throws javax.jms.MessageFormatException
+ * If this type conversion is invalid.
+ */
+ public short getShort(String key) throws JMSException
+ {
+ Object objValue = _map.get(key);
+ if (objValue == null)
+ {
+ throw new NumberFormatException("Wrong type for key: " + key);
+ }
+ return MessageHelper.convertToShort(objValue);
+ }
+
+ /**
+ * Returns the Unicode character value with the specified name.
+ *
+ * @param key The key name.
+ * @return The Unicode charactervalue with the specified key.
+ * @throws JMSException If reading the message fails due to some internal error.
+ * @throws javax.jms.MessageFormatException
+ * If this type conversion is invalid.
+ */
+ public char getChar(String key) throws JMSException
+ {
+ Object objValue = _map.get(key);
+ if (objValue == null)
+ {
+ throw new java.lang.NullPointerException();
+ }
+ return MessageHelper.convertToChar(objValue);
+ }
+
+ /**
+ * Returns the intvalue with the specified name.
+ *
+ * @param key The key name.
+ * @return The int value with the specified key.
+ * @throws JMSException If reading the message fails due to some internal error.
+ * @throws javax.jms.MessageFormatException
+ * If this type conversion is invalid.
+ */
+ public int getInt(String key) throws JMSException
+ {
+ Object objValue = _map.get(key);
+ if (objValue == null)
+ {
+ throw new NumberFormatException("Wrong type for key: " + key);
+ }
+ return MessageHelper.convertToInt(objValue);
+ }
+
+ /**
+ * Returns the longvalue with the specified name.
+ *
+ * @param key The key name.
+ * @return The long value with the specified key.
+ * @throws JMSException If reading the message fails due to some internal error.
+ * @throws javax.jms.MessageFormatException
+ * If this type conversion is invalid.
+ */
+ public long getLong(String key) throws JMSException
+ {
+ Object objValue = _map.get(key);
+ if (objValue == null)
+ {
+ throw new NumberFormatException("Wrong type for key: " + key);
+ }
+ return MessageHelper.convertToLong(objValue);
+ }
+
+ /**
+ * Returns the float value with the specified name.
+ *
+ * @param key The key name.
+ * @return The float value with the specified key.
+ * @throws JMSException If reading the message fails due to some internal error.
+ * @throws javax.jms.MessageFormatException
+ * If this type conversion is invalid.
+ */
+ public float getFloat(String key) throws JMSException
+ {
+ Object objValue = _map.get(key);
+ if (objValue == null)
+ {
+ throw new NumberFormatException("Wrong type for key: " + key);
+ }
+ return MessageHelper.convertToFloat(objValue);
+ }
+
+ /**
+ * Returns the double value with the specified name.
+ *
+ * @param key The key name.
+ * @return The double value with the specified key.
+ * @throws JMSException If reading the message fails due to some internal error.
+ * @throws javax.jms.MessageFormatException
+ * If this type conversion is invalid.
+ */
+ public double getDouble(String key) throws JMSException
+ {
+ Object objValue = _map.get(key);
+ if (objValue == null)
+ {
+ throw new NumberFormatException("Wrong type for key: " + key);
+ }
+ return MessageHelper.convertToDouble(objValue);
+ }
+
+ /**
+ * Returns the String value with the specified name.
+ *
+ * @param key The key name.
+ * @return The String value with the specified key.
+ * @throws JMSException If reading the message fails due to some internal error.
+ * @throws javax.jms.MessageFormatException
+ * If this type conversion is invalid.
+ */
+ public String getString(String key) throws JMSException
+ {
+ String result = null;
+ Object objValue = _map.get(key);
+ if (objValue != null)
+ {
+ if (objValue instanceof byte[])
+ {
+ throw new NumberFormatException("Wrong type for key: " + key);
+ }
+ else
+ {
+ result = objValue.toString();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns the byte array value with the specified name.
+ *
+ * @param key The key name.
+ * @return The byte value with the specified key.
+ * @throws JMSException If reading the message fails due to some internal error.
+ * @throws javax.jms.MessageFormatException
+ * If this type conversion is invalid.
+ */
+ public byte[] getBytes(String key) throws JMSException
+ {
+ Object objValue = _map.get(key);
+ if (objValue == null)
+ {
+ return null;
+ }
+ if (objValue instanceof byte[])
+ {
+ byte[] value = (byte[]) objValue;
+ byte[] toReturn = new byte[value.length];
+ System.arraycopy(value, 0, toReturn, 0, value.length);
+ return toReturn;
+ }
+ throw new MessageFormatException("Wrong type for key: " + key);
+ }
+
+ /**
+ * Returns the value of the object with the specified name.
+ *
+ * @param key The key name.
+ * @return The byte value with the specified key.
+ * @throws JMSException If reading the message fails due to some internal error.
+ */
+ public Object getObject(String key) throws JMSException
+ {
+ try
+ {
+ Object objValue = _map.get(key);
+ if (objValue == null)
+ {
+ return null;
+ }
+ else if (objValue instanceof byte[])
+ {
+ byte[] value = (byte[]) objValue;
+ byte[] toReturn = new byte[value.length];
+ System.arraycopy(value, 0, toReturn, 0, value.length);
+ return toReturn;
+ }
+ else
+ {
+ return objValue;
+ }
+ }
+ catch (java.lang.ClassCastException cce)
+ {
+ throw new MessageFormatException("Wrong type for key: " + key);
+ }
+ }
+
+ /**
+ * Returns an Enumeration of all the keys
+ *
+ * @return an enumeration of all the keys in this MapMessage
+ * @throws JMSException If reading the message fails due to some internal error.
+ */
+ public Enumeration getMapNames() throws JMSException
+ {
+ Vector<String> propVector = new Vector<String>(_map.keySet());
+ return propVector.elements();
+ }
+
+ /**
+ * Sets a boolean value with the specified key into the Map.
+ *
+ * @param key The key name.
+ * @param value The boolean value to set in the Map.
+ * @throws JMSException If writting the message fails due to some internal error.
+ * @throws IllegalArgumentException If the key is nul or an empty string.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void setBoolean(String key, boolean value) throws JMSException, IllegalArgumentException
+ {
+ isWriteable();
+ checkNotNullKey(key);
+ _map.put(key, value);
+ }
+
+ /**
+ * Sets a byte value with the specified name into the Map.
+ *
+ * @param key The key name.
+ * @param value The byte value to set in the Map.
+ * @throws JMSException If writting the message fails due to some internal error.
+ * @throws IllegalArgumentException If the key is nul or an empty string.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void setByte(String key, byte value) throws JMSException, IllegalArgumentException
+ {
+ isWriteable();
+ checkNotNullKey(key);
+ _map.put(key, value);
+ }
+
+ /**
+ * Sets a shortvalue with the specified name into the Map.
+ *
+ * @param key The key name.
+ * @param value The short value to set in the Map.
+ * @throws JMSException If writting the message fails due to some internal error.
+ * @throws IllegalArgumentException If the key is nul or an empty string.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void setShort(String key, short value) throws JMSException, IllegalArgumentException
+ {
+ isWriteable();
+ checkNotNullKey(key);
+ _map.put(key, value);
+ }
+
+ /**
+ * Sets a Unicode character value with the specified name into the Map.
+ *
+ * @param key The key name.
+ * @param value The character value to set in the Map.
+ * @throws JMSException If writting the message fails due to some internal error.
+ * @throws IllegalArgumentException If the key is nul or an empty string.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void setChar(String key, char value) throws JMSException, IllegalArgumentException
+ {
+ isWriteable();
+ checkNotNullKey(key);
+ _map.put(key, value);
+ }
+
+ /**
+ * Sets an intvalue with the specified name into the Map.
+ *
+ * @param key The key name.
+ * @param value The int value to set in the Map.
+ * @throws JMSException If writting the message fails due to some internal error.
+ * @throws IllegalArgumentException If the key is nul or an empty string.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void setInt(String key, int value) throws JMSException, IllegalArgumentException
+ {
+ isWriteable();
+ checkNotNullKey(key);
+ _map.put(key, value);
+ }
+
+ /**
+ * Sets a long value with the specified name into the Map.
+ *
+ * @param key The key name.
+ * @param value The long value to set in the Map.
+ * @throws JMSException If writting the message fails due to some internal error.
+ * @throws IllegalArgumentException If the key is nul or an empty string.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void setLong(String key, long value) throws JMSException, IllegalArgumentException
+ {
+ isWriteable();
+ checkNotNullKey(key);
+ _map.put(key, value);
+ }
+
+ /**
+ * Sets a float value with the specified name into the Map.
+ *
+ * @param key The key name.
+ * @param value The float value to set in the Map.
+ * @throws JMSException If writting the message fails due to some internal error.
+ * @throws IllegalArgumentException If the key is nul or an empty string.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void setFloat(String key, float value) throws JMSException, IllegalArgumentException
+ {
+ isWriteable();
+ checkNotNullKey(key);
+ _map.put(key, value);
+ }
+
+ /**
+ * Sets a double value with the specified name into the Map.
+ *
+ * @param key The key name.
+ * @param value The double value to set in the Map.
+ * @throws JMSException If writting the message fails due to some internal error.
+ * @throws IllegalArgumentException If the key is nul or an empty string.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void setDouble(String key, double value) throws JMSException, IllegalArgumentException
+ {
+ isWriteable();
+ checkNotNullKey(key);
+ _map.put(key, value);
+ }
+
+ /**
+ * Sets a String value with the specified name into the Map.
+ *
+ * @param key The key name.
+ * @param value The String value to set in the Map.
+ * @throws JMSException If writting the message fails due to some internal error.
+ * @throws IllegalArgumentException If the key is nul or an empty string.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void setString(String key, String value) throws JMSException, IllegalArgumentException
+ {
+ isWriteable();
+ checkNotNullKey(key);
+ _map.put(key, value);
+ }
+
+ /**
+ * Sets a byte array value with the specified name into the Map.
+ *
+ * @param key the name of the byte array
+ * @param value the byte array value to set in the Map; the array
+ * is copied so that the value for <CODE>name</CODE> will
+ * not be altered by future modifications
+ * @throws JMSException If writting the message fails due to some internal error.
+ * @throws IllegalArgumentException If the key is nul or an empty string.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void setBytes(String key, byte[] value) throws JMSException, NullPointerException
+ {
+ isWriteable();
+ checkNotNullKey(key);
+ byte[] newBytes = new byte[value.length];
+ System.arraycopy(value, 0, newBytes, 0, value.length);
+ _map.put(key, value);
+ }
+
+ /**
+ * Sets a portion of the byte array value with the specified name into the
+ * Map.
+ *
+ * @param key the name of the byte array
+ * @param value the byte array value to set in the Map; the array
+ * is copied so that the value for <CODE>name</CODE> will
+ * not be altered by future modifications
+ * @throws JMSException If writting the message fails due to some internal error.
+ * @throws IllegalArgumentException If the key is nul or an empty string.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void setBytes(String key, byte[] value, int offset, int length) throws JMSException, IllegalArgumentException
+ {
+ isWriteable();
+ checkNotNullKey(key);
+ byte[] newBytes = new byte[length];
+ System.arraycopy(value, offset, newBytes, 0, length);
+ _map.put(key, newBytes);
+ }
+
+ /**
+ * Sets an object value with the specified name into the Map.
+ *
+ * @param key the name of the byte array
+ * @param value the byte array value to set in the Map; the array
+ * is copied so that the value for <CODE>name</CODE> will
+ * not be altered by future modifications
+ * @throws JMSException If writting the message fails due to some internal error.
+ * @throws IllegalArgumentException If the key is nul or an empty string.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void setObject(String key, Object value) throws JMSException, IllegalArgumentException
+ {
+ isWriteable();
+ checkNotNullKey(key);
+ if ((value instanceof Boolean) || (value instanceof Byte) || (value instanceof Short) || (value instanceof Integer) || (value instanceof Long) || (value instanceof Character) || (value instanceof Float) || (value instanceof Double) || (value instanceof String) || (value instanceof byte[]) || (value == null))
+ {
+ _map.put(key, value);
+ }
+ else
+ {
+ throw new MessageFormatException("Cannot set property " + key + " to value " + value + "of type " + value
+ .getClass().getName() + ".");
+ }
+ }
+
+ //-- Overwritten methods
+ /**
+ * This method is invoked before this message is dispatched.
+ */
+ @Override
+ public void beforeMessageDispatch() throws QpidException
+ {
+ try
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(_map);
+ byte[] bytes = baos.toByteArray();
+ setMessageData(ByteBuffer.wrap(bytes));
+ }
+ catch (java.io.IOException ioe)
+ {
+ throw new QpidException("problem when dispatching message", null, ioe);
+ }
+ super.beforeMessageDispatch();
+ }
+
+
+ /**
+ * This method is invoked after this message has been received.
+ */
+ @Override
+ public void afterMessageReceive() throws QpidException
+ {
+ super.afterMessageReceive();
+ ByteBuffer messageData = getMessageData();
+ if (messageData != null)
+ {
+ try
+ {
+ ObjectInputStream ois = new ObjectInputStream(asInputStream());
+ _map = (Map<String, Object>) ois.readObject();
+ }
+ catch (IOException ioe)
+ {
+ throw new QpidException(
+ "Unexpected error during rebuild of message in afterReceive(): " + "- IO Exception", null, ioe);
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new QpidException(
+ "Unexpected error during rebuild of message in afterReceive(): " + "- Could not find the required class in classpath.",
+ null, e);
+ }
+ }
+ }
+
+ //-- protected methods
+ /**
+ * This method throws an <CODE>IllegalArgumentException</CODE> if the supplied parameter is null.
+ *
+ * @param key The key to check.
+ * @throws IllegalArgumentException If the key is null.
+ */
+ private void checkNotNullKey(String key) throws IllegalArgumentException
+ {
+ if (key == null || key.equals(""))
+ {
+ throw new IllegalArgumentException("Key cannot be null");
+ }
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/MessageFactory.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/MessageFactory.java
new file mode 100644
index 0000000000..fa7ebe7d1b
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/MessageFactory.java
@@ -0,0 +1,75 @@
+/* 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.qpidity.njms.message;
+
+import org.apache.qpidity.QpidException;
+
+/**
+ * A factory for JMS messages
+ */
+public class MessageFactory
+{
+ /**
+ * JMS Message hierarchy.
+ */
+ public static final byte JAVAX_JMS_MESSAGE = 1;
+ public static final byte JAVAX_JMS_TEXTMESSAGE = 2;
+ public static final byte JAVAX_JMS_STREAMMESSAGE = 3;
+ public static final byte JAVAX_JMS_BYTESMESSAGE = 4;
+ public static final byte JAVAX_JMS_OBJECTMESSAGE = 5;
+ public static final byte JAVAX_JMS_MAPMESSAGE = 6;
+
+ /**
+ * Create a QpidMessage subclass according to the JMS message type.
+ *
+ * @param message The received qpidity messsage
+ * @return The newly craeted JMS message
+ * @throws QpidException If an appropriate Message class cannot be created.
+ */
+ public static QpidMessage getQpidMessage(org.apache.qpidity.api.Message message) throws QpidException
+ {
+ QpidMessage result = null;
+ byte type = Byte.valueOf(message.getMessageProperties().getType());
+ switch (type)
+ {
+ case JAVAX_JMS_MESSAGE:
+ result = new MessageImpl(message);
+ break;
+ case JAVAX_JMS_TEXTMESSAGE:
+ result = new TextMessageImpl(message);
+ break;
+ case JAVAX_JMS_STREAMMESSAGE:
+ result = new StreamMessageImpl(message);
+ break;
+ case JAVAX_JMS_BYTESMESSAGE:
+ result = new BytesMessageImpl(message);
+ break;
+ case JAVAX_JMS_OBJECTMESSAGE:
+ result = new ObjectMessageImpl(message);
+ break;
+ case JAVAX_JMS_MAPMESSAGE:
+ result = new MapMessageImpl(message);
+ break;
+ default:
+ throw new QpidException(
+ "Message type identifier is not mapped " + "to a Message class in the current factory: " + type,
+ null, null);
+ }
+ return result;
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/MessageHelper.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/MessageHelper.java
new file mode 100644
index 0000000000..0cbe188f6e
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/MessageHelper.java
@@ -0,0 +1,456 @@
+/* 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.qpidity.njms.message;
+
+import javax.jms.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+/**
+ * This is an helper class for performing data convertion
+ */
+public class MessageHelper
+{
+ /**
+ * Convert an object into a boolean value
+ *
+ * @param obj object that may contain boolean value
+ * @return A boolean value.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public static boolean convertToBoolean(Object obj) throws JMSException
+ {
+ boolean result;
+ if (obj instanceof Boolean)
+ {
+ result = (Boolean) obj;
+ }
+ else if (obj instanceof String)
+ {
+ result = ((String) obj).equalsIgnoreCase("true");
+ }
+ else
+ {
+ throw new MessageFormatException("boolean property type convertion error",
+ "Messasge property type convertion error");
+ }
+ return result;
+ }
+
+ /**
+ * Convert an object into a byte value
+ *
+ * @param obj The object that may contain byte value
+ * @return The convertToed byte value.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public static byte convertToByte(Object obj) throws JMSException
+ {
+ byte result;
+ if (obj instanceof Byte)
+ {
+ result = ((Number) obj).byteValue();
+ }
+ else if (obj instanceof String)
+ {
+ result = Byte.parseByte((String) obj);
+ }
+ else
+ {
+ throw new MessageFormatException("byte property type convertion error",
+ "Messasge property type convertion error");
+ }
+ return result;
+ }
+
+ /**
+ * Convert an object into a short value
+ *
+ * @param obj The object that may contain short value
+ * @return The convertToed short value.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public static short convertToShort(Object obj) throws JMSException
+ {
+ short result;
+ if ((obj instanceof Short) || (obj instanceof Byte))
+ {
+ result = ((Number) obj).shortValue();
+ }
+ else if (obj instanceof String)
+ {
+ result = Short.parseShort((String) obj);
+ }
+ else
+ {
+ throw new MessageFormatException("short property type convertion error",
+ "Messasge property type convertion error");
+ }
+ return result;
+ }
+
+ /**
+ * Convert an object into a int value
+ *
+ * @param obj The object that may contain int value
+ * @return The convertToed int value.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public static int convertToInt(Object obj) throws JMSException
+ {
+ int result;
+ if ((obj instanceof Integer) || (obj instanceof Byte) || (obj instanceof Short))
+ {
+ result = ((Number) obj).intValue();
+ }
+ else if (obj instanceof String)
+ {
+ result = Integer.parseInt((String) obj);
+ }
+ else
+ {
+ throw new MessageFormatException("int property type convertion error",
+ "Messasge property type convertion error");
+ }
+ return result;
+ }
+
+ /**
+ * Convert an object into a long value
+ *
+ * @param obj The object that may contain long value
+ * @return The convertToed long value.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public static long convertToLong(Object obj) throws JMSException
+ {
+ long result;
+ if ((obj instanceof Number) && !((obj instanceof Float) || (obj instanceof Double)))
+ {
+ result = ((Number) obj).longValue();
+ }
+ else if (obj instanceof String)
+ {
+
+ result = Long.parseLong((String) obj);
+ }
+ else
+ {
+ throw new MessageFormatException("long property type convertion error",
+ "Messasge property type convertion error");
+ }
+ return result;
+ }
+
+ /**
+ * Convert an object into a float value
+ *
+ * @param obj The object that may contain float value
+ * @return The convertToed float value.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public static float convertToFloat(Object obj) throws JMSException
+ {
+ float result;
+ if (obj instanceof Float)
+ {
+ result = ((Number) obj).floatValue();
+ }
+ else if (obj instanceof String)
+ {
+ result = Float.parseFloat((String) obj);
+ }
+ else
+ {
+ throw new MessageFormatException("float property type convertion error",
+ "Messasge property type convertion error");
+ }
+ return result;
+ }
+
+ /**
+ * Convert an object into a double value
+ *
+ * @param obj The object that may contain double value
+ * @return The convertToed double value.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public static double convertToDouble(Object obj) throws JMSException
+ {
+ double result;
+ if ((obj instanceof Double) || (obj instanceof Float))
+ {
+ result = ((Number) obj).doubleValue();
+ }
+ else if (obj instanceof String)
+ {
+ result = Double.parseDouble((String) obj);
+ }
+ else
+ {
+ throw new MessageFormatException("double property type convertion error",
+ "Messasge property type convertion error");
+ }
+ return result;
+ }
+
+ /**
+ * Convert an object into a char value
+ *
+ * @param obj The object that may contain char value
+ * @return The convertToed char value.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public static char convertToChar(Object obj) throws JMSException
+ {
+ char result;
+ if (obj instanceof Character)
+ {
+ result = (Character) obj;
+ }
+ else
+ {
+ throw new MessageFormatException("char property type convertion error",
+ "Messasge property type convertion error");
+ }
+ return result;
+ }
+
+ /**
+ * Convert an object into a String value
+ *
+ * @param obj The object that may contain String value
+ * @return The convertToed String value.
+ */
+ public static String convertToString(Object obj)
+ {
+ String stringValue;
+ if (obj instanceof String)
+ {
+ stringValue = (String) obj;
+ }
+ else
+ {
+ stringValue = obj.toString();
+ }
+ return stringValue;
+ }
+
+ /**
+ * Check if the passed object represents Java primitive type
+ *
+ * @param value object for inspection
+ * @return true if object represent Java primitive type; false otherwise
+ */
+ public static boolean isPrimitive(Object value)
+ {
+ // Innocent till proven guilty
+ boolean isPrimitive = true;
+ if (!((value instanceof String) || (value instanceof Boolean) || (value instanceof Character) || ((value instanceof Number) && !((value instanceof BigDecimal) || (value instanceof BigInteger)))))
+ {
+ isPrimitive = false;
+ }
+ return isPrimitive;
+ }
+
+ /**
+ * Transform a foreign message into an equivalent QPID representation.
+ *
+ * @param message The foreign message to be converted.
+ * @return A native message.
+ * @throws JMSException In case of problem when converting the message.
+ */
+ public static MessageImpl transformMessage(Message message) throws JMSException
+ {
+ MessageImpl messageImpl;
+
+ if (message instanceof BytesMessage)
+ {
+ messageImpl = transformBytesMessage((BytesMessage) message);
+ }
+ else if (message instanceof MapMessage)
+ {
+ messageImpl = transformMapMessage((MapMessage) message);
+ }
+ else if (message instanceof ObjectMessage)
+ {
+ messageImpl = transformObjectMessage((ObjectMessage) message);
+ }
+ else if (message instanceof StreamMessage)
+ {
+ messageImpl = transformStreamMessage((StreamMessage) message);
+ }
+ else if (message instanceof TextMessage)
+ {
+ messageImpl = transformTextMessage((TextMessage) message);
+ }
+ else
+ {
+ messageImpl = new MessageImpl();
+ }
+ transformHeaderAndProperties(message, messageImpl);
+ return messageImpl;
+ }
+
+ //---- Private methods
+ /**
+ * Exposed JMS defined properties on converted message:
+ * JMSDestination - we don't set here
+ * JMSDeliveryMode - we don't set here
+ * JMSExpiration - we don't set here
+ * JMSPriority - we don't set here
+ * JMSMessageID - we don't set here
+ * JMSTimestamp - we don't set here
+ * JMSCorrelationID - set
+ * JMSReplyTo - set
+ * JMSType - set
+ * JMSRedlivered - we don't set here
+ *
+ * @param message The foreign message to be converted.
+ * @param nativeMsg A native Qpid message.
+ * @throws JMSException In case of problem when converting the message.
+ */
+ private static void transformHeaderAndProperties(Message message, MessageImpl nativeMsg) throws JMSException
+ {
+ //Set the correlation ID
+ String correlationID = message.getJMSCorrelationID();
+ if (correlationID != null)
+ {
+ nativeMsg.setJMSCorrelationID(correlationID);
+ }
+ //Set JMS ReplyTo
+ if (message.getJMSReplyTo() != null)
+ {
+ nativeMsg.setJMSReplyTo(message.getJMSReplyTo());
+ }
+ //Set JMS type
+ String jmsType = message.getJMSType();
+ if (jmsType != null)
+ {
+ nativeMsg.setJMSType(jmsType);
+ }
+ // Sets all non-JMS defined properties on converted message
+ Enumeration propertyNames = message.getPropertyNames();
+ while (propertyNames.hasMoreElements())
+ {
+ String propertyName = String.valueOf(propertyNames.nextElement());
+ if (!propertyName.startsWith("JMSX_"))
+ {
+ Object value = message.getObjectProperty(propertyName);
+ nativeMsg.setObjectProperty(propertyName, value);
+ }
+ }
+ }
+
+ /**
+ * Transform a BytesMessage.
+ *
+ * @param bytesMessage a BytesMessage to be converted.
+ * @return a native BytesMessage.
+ * @throws JMSException In case of problem when converting the message.
+ */
+ private static BytesMessageImpl transformBytesMessage(BytesMessage bytesMessage) throws JMSException
+ {
+ //reset the BytesMessage (makes the body read-only and repositions
+ // the stream of bytes to the beginning
+ bytesMessage.reset();
+ BytesMessageImpl nativeMsg = new BytesMessageImpl();
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = bytesMessage.readBytes(buf)) != -1)
+ {
+ nativeMsg.writeBytes(buf, 0, len);
+ }
+ return nativeMsg;
+ }
+
+ /**
+ * Transform a MapMessage.
+ *
+ * @param mapMessage a MapMessage to be converted.
+ * @return a native MapMessage.
+ * @throws JMSException In case of problem when converting the message.
+ */
+ private static MapMessageImpl transformMapMessage(MapMessage mapMessage) throws JMSException
+ {
+ MapMessageImpl nativeMsg = new MapMessageImpl();
+ Enumeration mapNames = mapMessage.getMapNames();
+ while (mapNames.hasMoreElements())
+ {
+ String name = (String) mapNames.nextElement();
+ nativeMsg.setObject(name, mapMessage.getObject(name));
+ }
+ return nativeMsg;
+ }
+
+ /**
+ * Transform an ObjectMessage.
+ *
+ * @param objectMessage a ObjectMessage to be converted.
+ * @return a native ObjectMessage.
+ * @throws JMSException In case of problem when converting the message.
+ */
+ private static ObjectMessageImpl transformObjectMessage(ObjectMessage objectMessage) throws JMSException
+ {
+ ObjectMessageImpl nativeMsg = new ObjectMessageImpl();
+ nativeMsg.setObject(objectMessage.getObject());
+ return nativeMsg;
+ }
+
+ /**
+ * Transform a StreamMessage.
+ *
+ * @param streamMessage a StreamMessage to be converted.
+ * @return a native StreamMessage.
+ * @throws JMSException In case of problem when converting the message.
+ */
+ private static StreamMessageImpl transformStreamMessage(StreamMessage streamMessage) throws JMSException
+ {
+ StreamMessageImpl nativeMsg = new StreamMessageImpl();
+ try
+ {
+ //reset the stream message
+ streamMessage.reset();
+ while (true)
+ {
+ nativeMsg.writeObject(streamMessage.readObject());
+ }
+ }
+ catch (MessageEOFException e)
+ {
+ // we're at the end so don't mind the exception
+ }
+ return nativeMsg;
+ }
+
+ /**
+ * Transform a TextMessage.
+ *
+ * @param textMessage a TextMessage to be converted.
+ * @return a native TextMessage.
+ * @throws JMSException In case of problem when converting the message.
+ */
+ private static TextMessageImpl transformTextMessage(TextMessage textMessage) throws JMSException
+ {
+ TextMessageImpl nativeMsg = new TextMessageImpl();
+ nativeMsg.setText(textMessage.getText());
+ return nativeMsg;
+ }
+
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/MessageImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/MessageImpl.java
new file mode 100644
index 0000000000..a2d974f9ad
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/MessageImpl.java
@@ -0,0 +1,1011 @@
+/* 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.qpidity.njms.message;
+
+import org.apache.qpidity.njms.ExceptionHelper;
+import org.apache.qpidity.njms.MessageConsumerImpl;
+import org.apache.qpidity.QpidException;
+
+import javax.jms.*;
+import java.util.Enumeration;
+import java.io.InputStream;
+
+/**
+ * Implementation of javax.njms.Message
+ */
+public class MessageImpl extends QpidMessage implements Message
+{
+ /**
+ * name used to store JMSType.
+ */
+ private static final String JMS_MESSAGE_TYPE = "JMSType";
+
+ /**
+ * The ReplyTo destination for this message
+ */
+ private Destination _replyTo;
+
+ /**
+ * The destination to which the message has been sent.
+ * <p>When a message is sent this value is ignored. After completion
+ * of the send method it holds the destination specified by the send.
+ * <p>When a message is received, its destination value must be
+ * equivalent to the value assigned when it was sent.
+ */
+ private Destination _destination;
+
+ /**
+ * Indicates whether the message properties are in writeable status.
+ */
+ protected boolean _readOnly = false;
+
+ /**
+ * Indicate whether the message properties are in writeable status.
+ */
+ protected boolean _propertiesReadOnly = false;
+
+ /**
+ * The message consumer through which this message was received.
+ */
+ private MessageConsumerImpl _messageConsumer;
+
+ //--- Constructor
+ /**
+ * Constructor used by SessionImpl.
+ */
+ public MessageImpl()
+ {
+ super();
+ setMessageType(String.valueOf(MessageFactory.JAVAX_JMS_MESSAGE));
+ }
+
+ /**
+ * Constructor used by MessageFactory
+ *
+ * @param message The new qpid message.
+ * @throws QpidException In case of IO problem when reading the received message.
+ */
+ protected MessageImpl(org.apache.qpidity.api.Message message) throws QpidException
+ {
+ super(message);
+ }
+
+ //---- javax.njms.Message interface
+ /**
+ * Get the message ID.
+ * <p> The JMS sprec says:
+ * <p>The messageID header field contains a value that uniquely
+ * identifies each message sent by a provider.
+ * <p>When a message is sent, messageID can be ignored. When
+ * the send method returns it contains a provider-assigned value.
+ * <P>All JMSMessageID values must start with the prefix `ID:'.
+ * Uniqueness of message ID values across different providers is
+ * not required.
+ *
+ * @return The message ID
+ * @throws JMSException If getting the message Id fails due to internal error.
+ */
+ public String getJMSMessageID() throws JMSException
+ {
+ String messageID = super.getMessageID();
+
+ if (messageID != null)
+ {
+ messageID = "ID:" + messageID;
+ }
+ return messageID;
+ }
+
+ /**
+ * Set the message ID.
+ * <p> The JMS spec says:
+ * <P>Providers set this field when a message is sent. This operation
+ * can be used to change the value of a message that's been received.
+ *
+ * @param messageID The ID of the message
+ * @throws JMSException If setting the message Id fails due to internal error.
+ */
+ public void setJMSMessageID(String messageID) throws JMSException
+ {
+ String qpidmessageID = null;
+ if (messageID != null)
+ {
+ if (messageID.substring(0, 3).equals("ID:"))
+ {
+ qpidmessageID = messageID.substring(3, messageID.length());
+ }
+ }
+ super.setMessageID(qpidmessageID);
+ }
+
+ /**
+ * Get the message timestamp.
+ * <p> The JMS sepc says:
+ * <P>The JMSTimestamp header field contains the time a message was
+ * handed off to a provider to be sent. It is not the time the
+ * message was actually transmitted because the actual send may occur
+ * later due to transactions or other client side queueing of messages.
+ * <P>When a message is sent, JMSTimestamp is ignored. When the send
+ * method returns it contains a a time value somewhere in the interval
+ * between the call and the return. It is in the format of a normal
+ * Java millis time value.
+ * <P>Since timestamps take some effort to create and increase a
+ * message's size, some JMS providers may be able to optimize message
+ * overhead if they are given a hint that timestamp is not used by an
+ * application. JMS message Producers provide a hint to disable
+ * timestamps. When a client sets a producer to disable timestamps
+ * they are saying that they do not depend on the value of timestamp
+ * for the messages it produces. These messages must either have
+ * timestamp set to null or, if the hint is ignored, timestamp must
+ * be set to its normal value.
+ *
+ * @return the message timestamp
+ * @throws JMSException If getting the Timestamp fails due to internal error.
+ */
+ public long getJMSTimestamp() throws JMSException
+ {
+ return super.getTimestamp();
+ }
+
+ /**
+ * Set the message timestamp.
+ * <p> The JMS spec says:
+ * <P>Providers set this field when a message is sent. This operation
+ * can be used to change the value of a message that's been received.
+ *
+ * @param timestamp The timestamp for this message
+ * @throws JMSException If setting the timestamp fails due to some internal error.
+ */
+ public void setJMSTimestamp(long timestamp) throws JMSException
+ {
+ super.setTimestamp(timestamp);
+ }
+
+ /**
+ * Get the correlation ID as an array of bytes for the message.
+ * <p> JMS spec says:
+ * <P>The use of a byte[] value for JMSCorrelationID is non-portable.
+ *
+ * @return the correlation ID of a message as an array of bytes.
+ * @throws JMSException If getting correlationId fails due to some internal error.
+ */
+ public byte[] getJMSCorrelationIDAsBytes() throws JMSException
+ {
+ String correlationID = getJMSCorrelationID();
+ if (correlationID != null)
+ {
+ return correlationID.getBytes();
+ }
+ return null;
+ }
+
+ /**
+ * Set the correlation ID as an array of bytes for the message.
+ * <p> JMS spec says:
+ * <P>If a provider supports the native concept of correlation id, a
+ * JMS client may need to assign specific JMSCorrelationID values to
+ * match those expected by non-JMS clients. JMS providers without native
+ * correlation id values are not required to support this (and the
+ * corresponding get) method; their implementation may throw
+ * java.lang.UnsupportedOperationException).
+ * <P>The use of a byte[] value for JMSCorrelationID is non-portable.
+ *
+ * @param correlationID The correlation ID value as an array of bytes.
+ * @throws JMSException If setting correlationId fails due to some internal error.
+ */
+ public void setJMSCorrelationIDAsBytes(byte[] correlationID) throws JMSException
+ {
+ setJMSCorrelationID(new String(correlationID));
+ }
+
+ /**
+ * Set the correlation ID for the message.
+ * <p> JMS spec says:
+ * <P>A client can use the JMSCorrelationID header field to link one
+ * message with another. A typically use is to link a response message
+ * with its request message.
+ * <P>Since each message sent by a JMS provider is assigned a message ID
+ * value it is convenient to link messages via message ID. All message ID
+ * values must start with the `ID:' prefix.
+ * <P>In some cases, an application (made up of several clients) needs to
+ * use an application specific value for linking messages. For instance,
+ * an application may use JMSCorrelationID to hold a value referencing
+ * some external information. Application specified values must not start
+ * with the `ID:' prefix; this is reserved for provider-generated message
+ * ID values.
+ *
+ * @param correlationID The message ID of a message being referred to.
+ * @throws JMSException If setting the correlationId fails due to some internal error.
+ */
+ public void setJMSCorrelationID(String correlationID) throws JMSException
+ {
+ super.setCorrelationID(correlationID);
+
+ }
+
+ /**
+ * Get the correlation ID for the message.
+ *
+ * @return The correlation ID of a message as a String.
+ * @throws JMSException If getting the correlationId fails due to some internal error.
+ */
+ public String getJMSCorrelationID() throws JMSException
+ {
+ return super.getCorrelationID();
+ }
+
+ /**
+ * Get where a reply to this message should be sent.
+ *
+ * @return The destination where a reply to this message should be sent.
+ * @throws JMSException If getting the ReplyTo Destination fails due to some internal error.
+ */
+ public Destination getJMSReplyTo() throws JMSException
+ {
+ return _replyTo;
+ }
+
+ /**
+ * Set where a reply to this message should be sent.
+ * <p> The JMS spec says:
+ * <P>The replyTo header field contains the destination where a reply
+ * to the current message should be sent. If it is null no reply is
+ * expected. The destination may be either a Queue or a Topic.
+ * <P>Messages with a null replyTo value are called JMS datagrams.
+ * Datagrams may be a notification of some change in the sender (i.e.
+ * they signal a sender event) or they may just be some data the sender
+ * thinks is of interest.
+ * <p> Messages with a replyTo value are typically expecting a response.
+ * A response may be optional, it is up to the client to decide. These
+ * messages are called JMS requests. A message sent in response to a
+ * request is called a reply.
+ *
+ * @param destination The destination where a reply to this message should be sent.
+ * @throws JMSException If setting the ReplyTo Destination fails due to some internal error.
+ */
+ public void setJMSReplyTo(Destination destination) throws JMSException
+ {
+ _replyTo = destination;
+ }
+
+ /**
+ * Get the destination for this message.
+ * <p> The JMS spec says:
+ * <p>The destination field contains the destination to which the
+ * message is being sent.
+ * <p>When a message is sent this value is ignored. After completion
+ * of the send method it holds the destination specified by the send.
+ * <p>When a message is received, its destination value must be
+ * equivalent to the value assigned when it was sent.
+ *
+ * @return The destination of this message.
+ * @throws JMSException If getting the JMS Destination fails due to some internal error.
+ */
+ public Destination getJMSDestination() throws JMSException
+ {
+ return _destination;
+ }
+
+ /**
+ * Set the destination for this message.
+ * <p: JMS spec says:
+ * <p>Providers set this field when a message is sent. This operation
+ * can be used to change the value of a message that's been received.
+ *
+ * @param destination The destination this message has been sent.
+ * @throws JMSException If setting the JMS Destination fails due to some internal error.
+ */
+ public void setJMSDestination(Destination destination) throws JMSException
+ {
+ _destination = destination;
+ }
+
+ /**
+ * Get the delivery mode for this message.
+ *
+ * @return the delivery mode of this message.
+ * @throws JMSException If getting the JMS DeliveryMode fails due to some internal error.
+ */
+ public int getJMSDeliveryMode() throws JMSException
+ {
+ int result = DeliveryMode.NON_PERSISTENT;
+ short amqpDeliveryMode = super.getdeliveryMode();
+ if (amqpDeliveryMode == QpidMessage.DELIVERY_MODE_PERSISTENT)
+ {
+ result = DeliveryMode.PERSISTENT;
+ }
+ else if (amqpDeliveryMode != DELIVERY_MODE_NON_PERSISTENT)
+ {
+ throw new JMSException("Problem when accessing message delivery mode");
+ }
+ return result;
+ }
+
+ /**
+ * Set the delivery mode for this message.
+ * <p> The JMS spec says:
+ * <p>Providers set this field when a message is sent. This operation
+ * can be used to change the value of a message that's been received.
+ *
+ * @param deliveryMode the delivery mode for this message.
+ * @throws JMSException If setting the JMS DeliveryMode fails due to some internal error.
+ */
+ public void setJMSDeliveryMode(int deliveryMode) throws JMSException
+ {
+ short amqpDeliveryMode = DELIVERY_MODE_PERSISTENT;
+ if (deliveryMode == DeliveryMode.NON_PERSISTENT)
+ {
+ amqpDeliveryMode = DELIVERY_MODE_NON_PERSISTENT;
+ }
+ else if (deliveryMode != DeliveryMode.PERSISTENT)
+ {
+ throw new JMSException(
+ "Problem when setting message delivery mode, " + deliveryMode + " is not a valid mode");
+ }
+ try
+ {
+ super.setDeliveryMode(amqpDeliveryMode);
+ }
+ catch (QpidException e)
+ {
+ throw ExceptionHelper.convertQpidExceptionToJMSException(e);
+ }
+ }
+
+ /**
+ * Get an indication of whether this message is being redelivered.
+ * <p> The JMS spec says:
+ * <p>If a client receives a message with the redelivered indicator set,
+ * it is likely, but not guaranteed, that this message was delivered to
+ * the client earlier but the client did not acknowledge its receipt at
+ * that earlier time.
+ *
+ * @return true if this message is being redelivered, false otherwise
+ * @throws JMSException If getting the JMS Redelivered fails due to some internal error.
+ */
+ public boolean getJMSRedelivered() throws JMSException
+ {
+ return super.getRedelivered();
+ }
+
+ /**
+ * Indicate whether this message is being redelivered.
+ * <p> The JMS spec says:
+ * <p>This field is set at the time the message is delivered. This
+ * operation can be used to change the value of a message that's
+ * been received.
+ *
+ * @param redelivered true indicates that the message is being redelivered.
+ * @throws JMSException If setting the JMS Redelivered fails due to some internal error.
+ */
+ public void setJMSRedelivered(boolean redelivered) throws JMSException
+ {
+ super.setRedelivered(redelivered);
+ }
+
+ /**
+ * Get the message type.
+ * <p> The JMS spec says:
+ * <p>Some JMS providers use a message repository that contains the
+ * definition of messages sent by applications. The type header field
+ * contains the name of a message's definition.
+ * <p>JMS does not define a standard message definition repository nor
+ * does it define a naming policy for the definitions it contains. JMS
+ * clients should use symbolic values for type that can be configured
+ * at installation time to the values defined in the current providers
+ * message repository.
+ * <p>JMS clients should assign a value to type whether the application
+ * makes use of it or not. This insures that it is properly set for
+ * those providers that require it.
+ *
+ * @return The message type
+ * @throws JMSException If getting the JMS message type fails due to some internal error.
+ */
+ public String getJMSType() throws JMSException
+ {
+ return getStringProperty(JMS_MESSAGE_TYPE);
+ }
+
+ /**
+ * Set this message type.
+ *
+ * @param type The type of message.
+ * @throws JMSException If setting the JMS message type fails due to some internal error.
+ */
+ public void setJMSType(String type) throws JMSException
+ {
+ if (type == null)
+ {
+ throw new JMSException("Invalid message type null");
+ }
+ else
+ {
+ super.setProperty(JMS_MESSAGE_TYPE, type);
+ }
+ }
+
+ /**
+ * Get the message's expiration value.
+ * <p> The JMS spec says:
+ * <p>When a message is sent, expiration is left unassigned. After
+ * completion of the send method, it holds the expiration time of the
+ * message. This is the sum of the time-to-live value specified by the
+ * client and the GMT at the time of the send.
+ * <p>If the time-to-live is specified as zero, expiration is set to
+ * zero which indicates the message does not expire.
+ *
+ * @return The time the message expires.
+ * @throws JMSException If getting the JMS message expiration fails due to some internal error.
+ */
+ public long getJMSExpiration() throws JMSException
+ {
+ return super.getExpiration();
+ }
+
+ /**
+ * Set the message's expiration value.
+ *
+ * @param expiration the message's expiration time
+ * @throws JMSException If setting the JMS message expiration fails due to some internal error.
+ */
+ public void setJMSExpiration(long expiration) throws JMSException
+ {
+ super.setExpiration(expiration);
+ }
+
+
+ /**
+ * Get the message priority.
+ * <p> The JMS spec says:
+ * <p>JMS defines a ten level priority value with 0 as the lowest
+ * priority and 9 as the highest. In addition, clients should consider
+ * priorities 0-4 as gradations of normal priority and priorities 5-9
+ * as gradations of expedited priority.
+ *
+ * @return The message priority.
+ * @throws JMSException If getting the JMS message priority fails due to some internal error.
+ */
+ public int getJMSPriority() throws JMSException
+ {
+ return super.getMessagePriority();
+ }
+
+ /**
+ * Set the priority for this message.
+ *
+ * @param priority The priority of this message.
+ * @throws JMSException If setting the JMS message priority fails due to some internal error.
+ */
+ public void setJMSPriority(int priority) throws JMSException
+ {
+ super.setMessagePriority((short) priority);
+ }
+
+ /**
+ * Clear the message's properties.
+ * <p/>
+ * The message header fields and body are not cleared.
+ *
+ * @throws JMSException if clearing JMS message properties fails due to some internal error.
+ */
+ public void clearProperties() throws JMSException
+ {
+ // The properties can now be written
+ // Properties are read only when the message is received.
+ _propertiesReadOnly = false;
+ super.clearMessageProperties();
+ }
+
+ /**
+ * Indicates whether a property value exists.
+ *
+ * @param name The name of the property to test the existence
+ * @return True if the property exists, false otherwise.
+ * @throws JMSException if checking if the property exists fails due to some internal error.
+ */
+ public boolean propertyExists(String name) throws JMSException
+ {
+ // Access the property; if the result is null,
+ // then the property value does not exist
+ return (super.getProperty(name) != null);
+ }
+
+ /**
+ * Access a boolean property value with the given name.
+ *
+ * @param name The name of the boolean property.
+ * @return The boolean property value with the given name.
+ * @throws JMSException if getting the boolean property fails due to some internal error.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public boolean getBooleanProperty(String name) throws JMSException
+ {
+ Object booleanProperty = getObjectProperty(name);
+ return booleanProperty != null && MessageHelper.convertToBoolean(booleanProperty);
+ }
+
+ /**
+ * Access a byte property value with the given name.
+ *
+ * @param name The name of the byte property.
+ * @return The byte property value with the given name.
+ * @throws JMSException if getting the byte property fails due to some internal error.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public byte getByteProperty(String name) throws JMSException
+ {
+ Object byteProperty = getObjectProperty(name);
+ if (byteProperty == null)
+ {
+ throw new NumberFormatException("Proerty " + name + " is null");
+ }
+ else
+ {
+ return MessageHelper.convertToByte(byteProperty);
+ }
+ }
+
+ /**
+ * Access a short property value with the given name.
+ *
+ * @param name The name of the short property.
+ * @return The short property value with the given name.
+ * @throws JMSException if getting the short property fails due to some internal error.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public short getShortProperty(String name) throws JMSException
+ {
+ Object shortProperty = getObjectProperty(name);
+ if (shortProperty == null)
+ {
+ throw new NumberFormatException("Proerty " + name + " is null");
+ }
+ else
+ {
+ return MessageHelper.convertToShort(shortProperty);
+ }
+ }
+
+ /**
+ * Access a int property value with the given name.
+ *
+ * @param name The name of the int property.
+ * @return The int property value with the given name.
+ * @throws JMSException if getting the int property fails due to some internal error.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public int getIntProperty(String name) throws JMSException
+ {
+ Object intProperty = getObjectProperty(name);
+ if (intProperty == null)
+ {
+ throw new NumberFormatException("Proerty " + name + " is null");
+ }
+ else
+ {
+ return MessageHelper.convertToInt(intProperty);
+ }
+ }
+
+ /**
+ * Access a long property value with the given name.
+ *
+ * @param name The name of the long property.
+ * @return The long property value with the given name.
+ * @throws JMSException if getting the long property fails due to some internal error.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public long getLongProperty(String name) throws JMSException
+ {
+ Object longProperty = getObjectProperty(name);
+ if (longProperty == null)
+ {
+ throw new NumberFormatException("Proerty " + name + " is null");
+ }
+ else
+ {
+ return MessageHelper.convertToLong(longProperty);
+ }
+ }
+
+ /**
+ * Access a long property value with the given name.
+ *
+ * @param name The name of the long property.
+ * @return The long property value with the given name.
+ * @throws JMSException if getting the long property fails due to some internal error.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public float getFloatProperty(String name) throws JMSException
+ {
+ Object floatProperty = getObjectProperty(name);
+ if (floatProperty == null)
+ {
+ throw new NumberFormatException("Proerty " + name + " is null");
+ }
+ else
+ {
+ return MessageHelper.convertToFloat(floatProperty);
+ }
+ }
+
+ /**
+ * Access a double property value with the given name.
+ *
+ * @param name The name of the double property.
+ * @return The double property value with the given name.
+ * @throws JMSException if getting the double property fails due to some internal error.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public double getDoubleProperty(String name) throws JMSException
+ {
+ Object doubleProperty = getObjectProperty(name);
+ if (doubleProperty == null)
+ {
+ throw new NumberFormatException("Proerty " + name + " is null");
+ }
+ else
+ {
+ return MessageHelper.convertToDouble(doubleProperty);
+ }
+ }
+
+ /**
+ * Access a String property value with the given name.
+ *
+ * @param name The name of the String property.
+ * @return The String property value with the given name.
+ * @throws JMSException if getting the String property fails due to some internal error.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public String getStringProperty(String name) throws JMSException
+ {
+ Object stringProperty = getObjectProperty(name);
+ String result = null;
+ if (stringProperty != null)
+ {
+ result = MessageHelper.convertToString(stringProperty);
+ }
+ return result;
+ }
+
+ /**
+ * Return the object property value with the given name.
+ *
+ * @param name the name of the Java object property
+ * @return the Java object property value with the given name, in
+ * objectified format (ie. if it set as an int, then a Integer is
+ * returned). If there is no property by this name, a null value
+ * is returned.
+ * @throws JMSException If getting the object property fails due to some internal error.
+ */
+ public Object getObjectProperty(String name) throws JMSException
+ {
+ return super.getProperty(name);
+ }
+
+ /**
+ * Get an Enumeration of all the property names.
+ *
+ * @return An enumeration of all the names of property values.
+ * @throws JMSException If getting the property names fails due to some internal JMS error.
+ */
+ public Enumeration getPropertyNames() throws JMSException
+ {
+ return super.getAllPropertyNames();
+ }
+
+ /**
+ * Set a boolean property value with the given name.
+ *
+ * @param name The name of the boolean property
+ * @param value The boolean property value to set.
+ * @throws JMSException If setting the property fails due to some internal JMS error.
+ * @throws MessageNotWriteableException If the message properties are read-only.
+ */
+ public void setBooleanProperty(String name, boolean value) throws JMSException
+ {
+ setObjectProperty(name, value);
+ }
+
+ /**
+ * Set a byte property value with the given name.
+ *
+ * @param name The name of the byte property
+ * @param value The byte property value to set.
+ * @throws JMSException If setting the property fails due to some internal JMS error.
+ * @throws MessageNotWriteableException If the message properties are read-only.
+ */
+ public void setByteProperty(String name, byte value) throws JMSException
+ {
+ setObjectProperty(name, value);
+ }
+
+ /**
+ * Set a short property value with the given name.
+ *
+ * @param name The name of the short property
+ * @param value The short property value to set.
+ * @throws JMSException If setting the property fails due to some internal JMS error.
+ * @throws MessageNotWriteableException If the message properties are read-only.
+ */
+ public void setShortProperty(String name, short value) throws JMSException
+ {
+ setObjectProperty(name, value);
+ }
+
+ /**
+ * Set an int property value with the given name.
+ *
+ * @param name The name of the int property
+ * @param value The int property value to set.
+ * @throws JMSException If setting the property fails due to some internal JMS error.
+ * @throws MessageNotWriteableException If the message properties are read-only.
+ */
+ public void setIntProperty(String name, int value) throws JMSException
+ {
+ setObjectProperty(name, value);
+ }
+
+ /**
+ * Set a long property value with the given name.
+ *
+ * @param name The name of the long property
+ * @param value The long property value to set.
+ * @throws JMSException If setting the property fails due to some internal JMS error.
+ * @throws MessageNotWriteableException If the message properties are read-only.
+ */
+ public void setLongProperty(String name, long value) throws JMSException
+ {
+ setObjectProperty(name, value);
+ }
+
+ /**
+ * Set a float property value with the given name.
+ *
+ * @param name The name of the float property
+ * @param value The float property value to set.
+ * @throws JMSException If setting the property fails due to some internal JMS error.
+ * @throws MessageNotWriteableException If the message properties are read-only.
+ */
+ public void setFloatProperty(String name, float value) throws JMSException
+ {
+ setObjectProperty(name, value);
+ }
+
+ /**
+ * Set a double property value with the given name.
+ *
+ * @param name The name of the double property
+ * @param value The double property value to set.
+ * @throws JMSException If setting the property fails due to some internal JMS error.
+ * @throws MessageNotWriteableException If the message properties are read-only.
+ */
+ public void setDoubleProperty(String name, double value) throws JMSException
+ {
+ setObjectProperty(name, value);
+ }
+
+ /**
+ * Set a string property value with the given name.
+ *
+ * @param name The name of the string property
+ * @param value The string property value to set.
+ * @throws JMSException If setting the property fails due to some internal JMS error.
+ * @throws MessageNotWriteableException If the message properties are read-only.
+ */
+ public void setStringProperty(String name, String value) throws JMSException
+ {
+ setObjectProperty(name, value);
+ }
+
+ /**
+ * Set a Java object property value with the given name.
+ * <p> The JMS spec says:
+ * <p> The setObjectProperty method accepts values of class Boolean, Byte, Short, Integer,
+ * Long, Float, Double, and String. An attempt to use any other class must throw a JMSException.
+ *
+ * @param name the name of the Java object property.
+ * @param value the Java object property value to set in the Message.
+ * @throws JMSException If setting the property fails due to some internal JMS error.
+ * @throws MessageFormatException If the object is invalid
+ * @throws MessageNotWriteableException If the message properties are read-only.
+ */
+ public void setObjectProperty(String name, Object value) throws JMSException
+ {
+ if (_propertiesReadOnly)
+ {
+ throw new MessageNotWriteableException("Error the message properties are read only");
+ }
+ if (!(value instanceof String || value instanceof Byte || value instanceof Short || value instanceof Integer || value instanceof Long || value instanceof Float || value instanceof Double || value instanceof Boolean || value == null))
+ {
+ throw new MessageFormatException("Format of object " + value + " is not supported");
+ }
+ super.setProperty(name, value);
+ }
+
+ /**
+ * Acknowledgment of a message automatically acknowledges all
+ * messages previously received by the session. Clients may
+ * individually acknowledge messages or they may choose to acknowledge
+ * messages in application defined groups (which is done by acknowledging
+ * the last received message in the group).
+ *
+ * @throws JMSException If this method is called on a closed session.
+ */
+ public void acknowledge() throws JMSException
+ {
+ _messageConsumer.getSession().acknowledge();
+ }
+
+ /**
+ * Clear out the message body. Clearing a message's body does not clear
+ * its header values or property entries.
+ * <P>If this message body was read-only, calling this method leaves
+ * the message body in the same state as an empty body in a newly
+ * created message.
+ *
+ * @throws JMSException If clearing this message body fails to due to some error.
+ */
+ public void clearBody() throws JMSException
+ {
+ super.clearMessageData();
+ _readOnly = false;
+ }
+
+ //--- Additional public methods
+ /**
+ * This method is invoked before a message dispatch operation.
+ *
+ * @throws QpidException If the destination is not set
+ */
+ public void beforeMessageDispatch() throws QpidException
+ {
+ if (_destination == null)
+ {
+ throw new QpidException("Invalid destination null", null, null);
+ }
+ super.beforeMessageDispatch();
+ }
+
+ /**
+ * This method is invoked after this message is received.
+ *
+ * @throws QpidException If there is an internal error when procesing this message.
+ */
+ @Override
+ public void afterMessageReceive() throws QpidException
+ {
+ // recreate a destination object for the encoded destination
+ // _destination = // todo
+ // recreate a destination object for the encoded ReplyTo destination (if it exists)
+ // _replyTo = // todo
+
+ _propertiesReadOnly = true;
+ _readOnly = true;
+ }
+
+ /**
+ * Test whether this message is readonly by throwing a MessageNotWriteableException if this
+ * message is readonly
+ *
+ * @throws MessageNotWriteableException If this message is readonly
+ */
+ protected void isWriteable() throws MessageNotWriteableException
+ {
+ if (_readOnly)
+ {
+ throw new MessageNotWriteableException("Cannot update message");
+ }
+ }
+
+ /**
+ * Set the MessageConsumerImpl through which this message was received.
+ * <p> This method is called after a message is received.
+ *
+ * @param messageConsumer the MessageConsumerImpl reference through which this message was received.
+ */
+ public void setMessageConsumer(MessageConsumerImpl messageConsumer)
+ {
+ _messageConsumer = messageConsumer;
+ }
+
+ /**
+ * Returns an {@link java.io.InputStream} that reads the data from this mesage buffer.
+ * {@link java.io.InputStream#read()} returns <tt>-1</tt> if the buffer position
+ * reaches to the limit.
+ *
+ * @return An {@link java.io.InputStream} that reads the data from this mesage buffer.
+ */
+ public InputStream asInputStream()
+ {
+ return new InputStream()
+ {
+ @Override
+ public int available()
+ {
+ return getMessageData().remaining();
+ }
+
+ @Override
+ public synchronized void mark(int readlimit)
+ {
+ getMessageData().mark();
+ }
+
+ @Override
+ public boolean markSupported()
+ {
+ return true;
+ }
+
+ @Override
+ public int read()
+ {
+ if (getMessageData().hasRemaining())
+ {
+ return getMessageData().get() & 0xff;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len)
+ {
+ int remaining = getMessageData().remaining();
+ if (remaining > 0)
+ {
+ int readBytes = Math.min(remaining, len);
+ getMessageData().get(b, off, readBytes);
+ return readBytes;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ @Override
+ public synchronized void reset()
+ {
+ getMessageData().reset();
+ }
+
+ @Override
+ public long skip(long n)
+ {
+ int bytes;
+ if (n > Integer.MAX_VALUE)
+ {
+ bytes = getMessageData().remaining();
+ }
+ else
+ {
+ bytes = Math.min(getMessageData().remaining(), (int) n);
+ }
+ getMessageData().position(getMessageData().position() + bytes);
+ return bytes;
+ }
+ };
+ }
+
+
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/ObjectMessageImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/ObjectMessageImpl.java
new file mode 100644
index 0000000000..70afddc9a3
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/ObjectMessageImpl.java
@@ -0,0 +1,178 @@
+/* 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.qpidity.njms.message;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.qpidity.QpidException;
+
+import javax.jms.ObjectMessage;
+import javax.jms.JMSException;
+import javax.jms.MessageNotWriteableException;
+import java.io.*;
+import java.nio.ByteBuffer;
+
+/**
+ * Implemetns javax.njms.ObjectMessage
+ */
+public class ObjectMessageImpl extends MessageImpl implements ObjectMessage
+{
+
+ /**
+ * this ObjectMessageImpl's logger
+ */
+ private static final Logger _logger = LoggerFactory.getLogger(ObjectMessageImpl.class);
+
+ /**
+ * The ObjectMessage's payload.
+ */
+ private Serializable _object = null;
+
+ //--- Constructor
+ /**
+ * Constructor used by SessionImpl.
+ */
+ public ObjectMessageImpl()
+ {
+ super();
+ setMessageType(String.valueOf(MessageFactory.JAVAX_JMS_OBJECTMESSAGE));
+ }
+
+ /**
+ * Constructor used by MessageFactory
+ *
+ * @param message The new qpid message.
+ * @throws QpidException In case of IO problem when reading the received message.
+ */
+ protected ObjectMessageImpl(org.apache.qpidity.api.Message message) throws QpidException
+ {
+ super(message);
+ }
+
+ //--- Interface ObjctMessage
+ /**
+ * Sets the serializable object containing this message's data.
+ * <p> JE JMS spec says:
+ * <p> It is important to note that an <CODE>ObjectMessage</CODE>
+ * contains a snapshot of the object at the time <CODE>setObject()</CODE>
+ * is called; subsequent modifications of the object will have no
+ * effect on the <CODE>ObjectMessage</CODE> body.
+ *
+ * @param object The message's data
+ * @throws JMSException If setting the object fails due to some error.
+ * @throws javax.jms.MessageFormatException
+ * If object serialization fails.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void setObject(Serializable object) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ // Serialize the passed in object, then de-serialize it
+ // so that changes to it do not affect m_data (JAVA's way to perform a deep clone)
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ObjectOutputStream objOut = new ObjectOutputStream(bOut);
+ objOut.writeObject(object);
+ byte[] bArray = bOut.toByteArray();
+ ByteArrayInputStream bIn = new ByteArrayInputStream(bArray);
+ ObjectInputStream objIn = new ObjectInputStream(bIn);
+ _object = (Serializable) objIn.readObject();
+ objOut.close();
+ objIn.close();
+ }
+ catch (Exception e)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Unexpected exeption when performing object deep clone", e);
+ }
+ throw new MessageNotWriteableException("Unexpected exeption when performing object deep clone",
+ e.getMessage());
+ }
+ }
+
+ /**
+ * Gets the serializable object containing this message's data. The
+ * default value is null.
+ *
+ * @return The serializable object containing this message's data
+ * @throws JMSException If getting the object fails due to some internal error.
+ */
+ public Serializable getObject() throws JMSException
+ {
+ return _object;
+ }
+
+ //--- Overwritten methods
+ /**
+ * This method is invoked before a message dispatch operation.
+ *
+ * @throws org.apache.qpidity.QpidException
+ * If the destination is not set
+ */
+ public void beforeMessageDispatch() throws QpidException
+ {
+ try
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(_object);
+ byte[] bytes = baos.toByteArray();
+ setMessageData(ByteBuffer.wrap(bytes));
+ }
+ catch (IOException e)
+ {
+ throw new QpidException("Problem when setting object of object message", null, e);
+ }
+ super.beforeMessageDispatch();
+ }
+
+ /**
+ * This method is invoked after this message is received.
+ *
+ * @throws QpidException
+ */
+ @Override
+ public void afterMessageReceive() throws QpidException
+ {
+ super.afterMessageReceive();
+ ByteBuffer messageData = getMessageData();
+ if (messageData != null)
+ {
+ try
+ {
+ ObjectInputStream ois = new ObjectInputStream(asInputStream());
+ _object = (Serializable) ois.readObject();
+ }
+ catch (IOException ioe)
+ {
+ throw new QpidException(
+ "Unexpected error during rebuild of message in afterReceive() - " + "The Object stored in the message was not a Serializable object.",
+ null, ioe);
+ }
+ catch (ClassNotFoundException clnfe)
+ {
+ throw new QpidException(
+ "Unexpected error during rebuild of message in afterReceive() - " + "Could not find the required class in classpath.",
+ null, clnfe);
+ }
+ }
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/QpidMessage.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/QpidMessage.java
new file mode 100644
index 0000000000..2426231c15
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/QpidMessage.java
@@ -0,0 +1,445 @@
+/*
+ *
+ * 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.qpidity.njms.message;
+
+import java.nio.ByteBuffer;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+import java.io.IOException;
+
+import org.apache.qpidity.ErrorCode;
+import org.apache.qpidity.QpidException;
+import org.apache.qpidity.nclient.util.ByteBufferMessage;
+import org.apache.qpidity.transport.ReplyTo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class QpidMessage
+{
+ /**
+ * this QpidMessage's logger
+ */
+ private static final Logger _logger = LoggerFactory.getLogger(QpidMessage.class);
+
+ /**
+ * The underlying qpidity message
+ */
+ private org.apache.qpidity.api.Message _qpidityMessage;
+
+ /**
+ * This message specific properties.
+ */
+ private Map<String, Object> _messageProperties;
+
+ /**
+ * This message data
+ */
+ private ByteBuffer _messageData;
+
+
+ //--- This is required as AMQP delivery modes are different from the JMS ones
+ public static final short DELIVERY_MODE_PERSISTENT = 2;
+ public static final short DELIVERY_MODE_NON_PERSISTENT = 1;
+
+
+ //-- Constructors
+ /**
+ * Constructor used when JMS messages are created by SessionImpl.
+ */
+ protected QpidMessage()
+ {
+ // We us a byteBufferMessage as default
+ _qpidityMessage = new ByteBufferMessage();
+ System.out.println("Creating a bytes message");
+ _messageProperties = new HashMap<String, Object>();
+ // This is a newly created messsage so the data is empty
+ _messageData = ByteBuffer.allocate(1024);
+ }
+
+ /**
+ * Constructor used when a Qpid message is received
+ *
+ * @param message The received message.
+ * @throws QpidException In case of problem when receiving the message body.
+ */
+ protected QpidMessage(org.apache.qpidity.api.Message message) throws QpidException
+ {
+ try
+ {
+ _qpidityMessage = message;
+ _messageProperties = (Map<String, Object>) message.getMessageProperties().getApplicationHeaders();
+ _messageData = _qpidityMessage.readData();
+ }
+ catch (IOException ioe)
+ {
+ throw new QpidException("IO problem when creating message", ErrorCode.UNDEFINED, ioe);
+ }
+ }
+
+ //---- getters and setters.
+ /**
+ * Get the message ID.
+ *
+ * @return The message ID
+ */
+ public String getMessageID()
+ {
+ return _qpidityMessage.getMessageProperties().getMessageId();
+ }
+
+ /**
+ * Set the message ID.
+ *
+ * @param messageID The ID of the message
+ */
+ protected void setMessageID(String messageID)
+ {
+ _qpidityMessage.getMessageProperties().setMessageId(messageID);
+ }
+
+ /**
+ * Get the message timestamp.
+ *
+ * @return The message timestamp.
+ */
+ protected long getTimestamp()
+ {
+ return _qpidityMessage.getDeliveryProperties().getTimestamp();
+ }
+
+ /**
+ * Set the message timestamp.
+ *
+ * @param timestamp the timestamp for this message
+ */
+ protected void setTimestamp(long timestamp)
+ {
+ _qpidityMessage.getDeliveryProperties().setTimestamp(timestamp);
+ }
+
+ /**
+ * Set the JMS correlation ID
+ *
+ * @param correlationID The JMS correlation ID.
+ */
+ protected void setCorrelationID(String correlationID)
+ {
+ _qpidityMessage.getMessageProperties().setCorrelationId(correlationID);
+ }
+
+ /**
+ * Get the JMS correlation ID
+ *
+ * @return The JMS correlation ID
+ */
+ protected String getCorrelationID()
+ {
+ return _qpidityMessage.getMessageProperties().getCorrelationId();
+ }
+
+ /**
+ * Get the ReplyTo for this message.
+ *
+ * @return The ReplyTo for this message.
+ */
+ protected ReplyTo getReplyTo()
+ {
+ return _qpidityMessage.getMessageProperties().getReplyTo();
+ }
+
+ /**
+ * Set the ReplyTo for this message.
+ *
+ * @param replyTo The ReplyTo for this message.
+ */
+ protected void setReplyTo(ReplyTo replyTo)
+ {
+ _qpidityMessage.getMessageProperties().setReplyTo(replyTo);
+ }
+
+ /**
+ * Get this message Delivery mode
+ * The delivery mode may be non-persistent (1) or persistent (2)
+ *
+ * @return the delivery mode of this message.
+ */
+ protected short getdeliveryMode()
+ {
+ return _qpidityMessage.getDeliveryProperties().getDeliveryMode();
+ }
+
+ /**
+ * Set the delivery mode for this message.
+ *
+ * @param deliveryMode the delivery mode for this message.
+ * @throws QpidException If the delivery mode is not supported.
+ */
+ protected void setDeliveryMode(short deliveryMode) throws QpidException
+ {
+ if (deliveryMode != DELIVERY_MODE_PERSISTENT && deliveryMode != DELIVERY_MODE_NON_PERSISTENT)
+ {
+ throw new QpidException(
+ "Problem when setting message delivery mode, " + deliveryMode + " is not a valid mode",
+ ErrorCode.UNDEFINED, null);
+ }
+ _qpidityMessage.getDeliveryProperties().setDeliveryMode(deliveryMode);
+ }
+
+ /**
+ * Get an indication of whether this message is being redelivered.
+ *
+ * @return true if this message is redelivered, false otherwise.
+ */
+ protected boolean getRedelivered()
+ {
+ return _qpidityMessage.getDeliveryProperties().getRedelivered();
+ }
+
+ /**
+ * Indicate whether this message is being redelivered.
+ *
+ * @param redelivered true indicates that the message is being redelivered.
+ */
+ protected void setRedelivered(boolean redelivered)
+ {
+ _qpidityMessage.getDeliveryProperties().setRedelivered(redelivered);
+ }
+
+ /**
+ * Get this message type.
+ *
+ * @return This message type.
+ */
+ protected String getMessageType()
+ {
+ return _qpidityMessage.getMessageProperties().getType();
+ }
+
+ /**
+ * Set this message type.
+ *
+ * @param type The type of message.
+ */
+ protected void setMessageType(String type)
+ {
+ _qpidityMessage.getMessageProperties().setType(type);
+ }
+
+ /**
+ * Get the message's expiration value.
+ *
+ * @return The message's expiration value.
+ */
+ protected long getExpiration()
+ {
+ return _qpidityMessage.getDeliveryProperties().getExpiration();
+ }
+
+ /**
+ * Set the message's expiration value.
+ *
+ * @param expiration The message's expiration value.
+ */
+ protected void setExpiration(long expiration)
+ {
+ _qpidityMessage.getDeliveryProperties().setExpiration(expiration);
+ }
+
+ /**
+ * Get the priority for this message.
+ *
+ * @return The priority for this message.
+ */
+ protected short getMessagePriority()
+ {
+ return _qpidityMessage.getDeliveryProperties().getPriority();
+ }
+
+ /**
+ * Set the priority for this message.
+ *
+ * @param priority The priority for this message.
+ */
+ protected void setMessagePriority(short priority)
+ {
+ _qpidityMessage.getDeliveryProperties().setPriority(priority);
+ }
+
+ /**
+ * Clear this messasge specific properties.
+ */
+ protected void clearMessageProperties()
+ {
+ _messageProperties.clear();
+ }
+
+ /**
+ * Access to a message specific property.
+ *
+ * @param name The property to access.
+ * @return The value associated with this property, mull if the value is null or the property does not exist.
+ */
+ protected Object getProperty(String name)
+ {
+ return _messageProperties.get(name);
+ }
+
+ /**
+ * Set a property for this message
+ *
+ * @param name The name of the property to set.
+ * @param value The value of the rpoperty.
+ */
+ protected void setProperty(String name, Object value)
+ {
+ _messageProperties.put(name, value);
+ }
+
+ /**
+ * Get an Enumeration of all the property names
+ *
+ * @return An Enumeration of all the property names.
+ */
+ protected Enumeration<String> getAllPropertyNames()
+ {
+ Vector<String> vec = new Vector<String>(_messageProperties.keySet());
+ return vec.elements();
+ }
+
+ /**
+ * Set this message body
+ *
+ * @param messageBody The buffer containing this message data
+ */
+ protected void setMessageData(ByteBuffer messageBody)
+ {
+ _messageData = messageBody; // we shouldn't need that .duplicate();
+ }
+
+ /**
+ * Access this messaage data.
+ *
+ * @return This message data.
+ */
+ protected ByteBuffer getMessageData()
+ {
+ return _messageData;
+ }
+
+ /**
+ * Clear this message data
+ */
+ protected void clearMessageData()
+ {
+ _messageData = ByteBuffer.allocate(1024);
+ }
+
+ /**
+ * Set this message AMQP routingkey
+ *
+ * @param routingKey This message AMQP routingkey
+ */
+ public void setRoutingKey(String routingKey)
+ {
+ _qpidityMessage.getDeliveryProperties().setRoutingKey(routingKey);
+ }
+
+ /**
+ * Set this message AMQP exchange name.
+ *
+ * @param exchangeName This message AMQP exchange name.
+ */
+ public void setExchangeName(String exchangeName)
+ {
+ _qpidityMessage.getDeliveryProperties().setExchange(exchangeName);
+ }
+
+ /**
+ * Get this message excahgne name
+ *
+ * @return this message excahgne name
+ */
+ public String getExchangeName()
+ {
+ return _qpidityMessage.getDeliveryProperties().getExchange();
+ }
+
+ /**
+ * This method is invoked before a message dispatch operation.
+ *
+ * @throws QpidException If the destination is not set
+ */
+ public void beforeMessageDispatch() throws QpidException
+ {
+ try
+ {
+ // set the message data
+ _qpidityMessage.clearData();
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("_messageData POS " + _messageData.position());
+ _logger.debug("_messageData limit " + _messageData.limit());
+ }
+ _qpidityMessage.appendData(_messageData);
+ _qpidityMessage.getMessageProperties().setApplicationHeaders(_messageProperties);
+ }
+ catch (IOException e)
+ {
+ throw new QpidException("IO exception when sending message", ErrorCode.UNDEFINED, e);
+ }
+ }
+
+ /**
+ * Get the underlying qpidity message
+ *
+ * @return The underlying qpidity message.
+ */
+ public org.apache.qpidity.api.Message getQpidityMessage()
+ {
+ return _qpidityMessage;
+ }
+
+ /**
+ * Get this message transfer ID.
+ *
+ * @return This message transfer ID.
+ */
+ public long getMessageTransferId()
+ {
+ return _qpidityMessage.getMessageTransferId();
+ }
+
+ /**
+ * This method is invoked after this message is received.
+ *
+ * @throws QpidException If there is an internal error when procesing this message.
+ */
+ public void afterMessageReceive() throws QpidException
+ {
+ // do nothing for now
+ }
+
+}
+
+
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/StreamMessageImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/StreamMessageImpl.java
new file mode 100644
index 0000000000..670dddd969
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/StreamMessageImpl.java
@@ -0,0 +1,1115 @@
+/* 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.qpidity.njms.message;
+
+import org.apache.qpidity.QpidException;
+
+import javax.jms.*;
+import java.io.IOException;
+import java.io.EOFException;
+
+/**
+ * The JMS spec says:
+ * StreamMessage objects support the following conversion table.
+ * The marked cases must be supported. The unmarked cases must throw a JMSException.
+ * The String-to-primitive conversions may throw a runtime exception if the
+ * primitive's valueOf() method does not accept it as a valid String representation of the primitive.
+ * <p> A value written as the row type can be read as the column type.
+ * <p/>
+ * | | boolean byte short char int long float double String byte[]
+ * |----------------------------------------------------------------------
+ * |boolean | X X
+ * |byte | X X X X X
+ * |short | X X X X
+ * |char | X X
+ * |int | X X X
+ * |long | X X
+ * |float | X X X
+ * |double | X X
+ * |String | X X X X X X X X
+ * |byte[] | X
+ * |----------------------------------------------------------------------
+ */
+public class StreamMessageImpl extends BytesMessageImpl implements StreamMessage
+{
+ /**
+ * Those statics represent incoming field types. The type of a field is
+ * written first in the stream
+ */
+ private static final byte BOOLEAN = 1;
+ private static final byte BYTE = 2;
+ private static final byte CHAR = 3;
+ private static final byte DOUBLE = 4;
+ private static final byte FLOAT = 5;
+ private static final byte INT = 6;
+ private static final byte LONG = 7;
+ private static final byte SHORT = 8;
+ private static final byte STRING = 9;
+ private static final byte BYTEARRAY = 10;
+ private static final byte NULL = 11;
+
+ /**
+ * The size of the byteArray written in this stream
+ */
+ private int _sizeOfByteArray = 0;
+
+ //--- Constructor
+ /**
+ * Constructor used by SessionImpl.
+ */
+ public StreamMessageImpl()
+ {
+ super();
+ setMessageType(String.valueOf(MessageFactory.JAVAX_JMS_STREAMMESSAGE));
+ }
+
+ /**
+ * Constructor used by MessageFactory
+ *
+ * @param message The new qpid message.
+ * @throws QpidException In case of problem when receiving the message body.
+ */
+ protected StreamMessageImpl(org.apache.qpidity.api.Message message) throws QpidException
+ {
+ super(message);
+ }
+
+ //--- Interface StreamMessage
+ /**
+ * Reads a boolean.
+ * <p/>
+ * | | boolean byte short char int long float double String byte[]
+ * |----------------------------------------------------------------------
+ * |boolean | X X
+ *
+ * @return The boolean value read
+ * @throws JMSException If reading a boolean fails due to some error.
+ * @throws javax.jms.MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public boolean readBoolean() throws JMSException
+ {
+ isReadableAndNotReadingByteArray();
+ boolean result;
+ try
+ {
+ _dataIn.mark(10);
+ byte type = _dataIn.readByte();
+ switch (type)
+ {
+ case BOOLEAN:
+ result = super.readBoolean();
+ break;
+ case STRING:
+ int len = _dataIn.readInt();
+ byte[] bArray = new byte[len];
+ _dataIn.readFully(bArray);
+ result = Boolean.valueOf(new String(bArray));
+ break;
+ case NULL:
+ result = false;
+ break;
+ default:
+ _dataIn.reset();
+ throw new MessageFormatException("Invalid Object Type");
+ }
+ }
+ catch (EOFException eof)
+ {
+ throw new MessageEOFException("End of file Reached when reading message");
+ }
+ catch (IOException io)
+ {
+ throw new JMSException("IO exception when reading message");
+ }
+ return result;
+ }
+
+ /**
+ * Reads a byte.
+ * <p/>
+ * | | boolean byte short char int long float double String byte[]
+ * |----------------------------------------------------------------------
+ * |byte | X X
+ *
+ * @return The byte value read
+ * @throws JMSException If reading fails due to some error.
+ * @throws javax.jms.MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public byte readByte() throws JMSException
+ {
+ isReadableAndNotReadingByteArray();
+ byte result;
+ try
+ {
+ _dataIn.mark(10);
+ byte type = _dataIn.readByte();
+ switch (type)
+ {
+ case BYTE:
+ result = super.readByte();
+ break;
+ case STRING:
+ int len = _dataIn.readInt();
+ byte[] bArray = new byte[len];
+ _dataIn.readFully(bArray);
+ result = new Byte(new String(bArray));
+ break;
+ case NULL:
+ result = Byte.valueOf(null);
+ break;
+ default:
+ _dataIn.reset();
+ throw new MessageFormatException("Invalid Object Type");
+ }
+ }
+ catch (EOFException eof)
+ {
+ throw new MessageEOFException("End of file Reached when reading message");
+ }
+ catch (IOException io)
+ {
+ throw new JMSException("IO exception when reading message");
+ }
+ return result;
+ }
+
+ /**
+ * Reads a short.
+ * <p/>
+ * | | boolean byte short char int long float double String byte[]
+ * |----------------------------------------------------------------------
+ * |short | X X X
+ *
+ * @return The short value read
+ * @throws JMSException If reading fails due to some error.
+ * @throws javax.jms.MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public short readShort() throws JMSException
+ {
+ isReadableAndNotReadingByteArray();
+ short result;
+ try
+ {
+ _dataIn.mark(10);
+ byte type = _dataIn.readByte();
+ switch (type)
+ {
+ case SHORT:
+ result = super.readShort();
+ break;
+ case BYTE:
+ result = super.readByte();
+ break;
+ case STRING:
+ int len = _dataIn.readInt();
+ byte[] bArray = new byte[len];
+ _dataIn.readFully(bArray);
+ result = new Short(new String(bArray));
+ break;
+ case NULL:
+ result = Short.valueOf(null);
+ break;
+ default:
+ _dataIn.reset();
+ throw new MessageFormatException("Invalid Object Type");
+ }
+ }
+ catch (EOFException eof)
+ {
+ throw new MessageEOFException("End of file Reached when reading message");
+ }
+ catch (IOException io)
+ {
+ throw new JMSException("IO exception when reading message");
+ }
+ return result;
+ }
+
+ /**
+ * Reads a char.
+ * <p/>
+ * | | boolean byte short char int long float double String byte[]
+ * |----------------------------------------------------------------------
+ * |char | X
+ *
+ * @return The char value read
+ * @throws JMSException If reading fails due to some error.
+ * @throws javax.jms.MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public char readChar() throws JMSException
+ {
+ isReadableAndNotReadingByteArray();
+ char result;
+ try
+ {
+ _dataIn.mark(10);
+ byte type = _dataIn.readByte();
+ switch (type)
+ {
+ case CHAR:
+ result = super.readChar();
+ break;
+ case NULL:
+ _dataIn.reset();
+ throw new NullPointerException();
+ default:
+ _dataIn.reset();
+ throw new MessageFormatException("Invalid Object Type");
+ }
+ }
+ catch (EOFException eof)
+ {
+ throw new MessageEOFException("End of file Reached when reading message");
+ }
+ catch (IOException io)
+ {
+ throw new JMSException("IO exception when reading message");
+ }
+ return result;
+ }
+
+ /**
+ * Reads an Int.
+ * <p/>
+ * | | boolean byte short char int long float double String byte[]
+ * |----------------------------------------------------------------------
+ * |int | X X X X
+ *
+ * @return The int value read
+ * @throws JMSException If reading fails due to some error.
+ * @throws javax.jms.MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public int readInt() throws JMSException
+ {
+ isReadableAndNotReadingByteArray();
+ int result;
+ try
+ {
+ _dataIn.mark(10);
+ byte type = _dataIn.readByte();
+ switch (type)
+ {
+ case INT:
+ result = super.readInt();
+ break;
+ case SHORT:
+ result = super.readShort();
+ break;
+ case BYTE:
+ result = super.readByte();
+ break;
+ case STRING:
+ int len = _dataIn.readInt();
+ byte[] bArray = new byte[len];
+ _dataIn.readFully(bArray);
+ result = new Integer(new String(bArray));
+ break;
+ case NULL:
+ result = Integer.valueOf(null);
+ break;
+ default:
+ _dataIn.reset();
+ throw new MessageFormatException("Invalid Object Type");
+ }
+ }
+ catch (EOFException eof)
+ {
+ throw new MessageEOFException("End of file Reached when reading message");
+ }
+ catch (IOException io)
+ {
+ throw new JMSException("IO exception when reading message");
+ }
+ return result;
+ }
+
+ /**
+ * Reads an Long.
+ * <p/>
+ * | | boolean byte short char int long float double String byte[]
+ * |----------------------------------------------------------------------
+ * |long | X X X X X
+ *
+ * @return The long value read
+ * @throws JMSException If reading fails due to some error.
+ * @throws javax.jms.MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public long readLong() throws JMSException
+ {
+ isReadableAndNotReadingByteArray();
+ long result;
+ try
+ {
+ _dataIn.mark(10);
+ byte type = _dataIn.readByte();
+ switch (type)
+ {
+ case LONG:
+ result = super.readLong();
+ break;
+ case INT:
+ result = super.readInt();
+ break;
+ case SHORT:
+ result = super.readShort();
+ break;
+ case BYTE:
+ result = super.readByte();
+ break;
+ case STRING:
+ int len = _dataIn.readInt();
+ byte[] bArray = new byte[len];
+ _dataIn.readFully(bArray);
+ result = (new Long(new String(bArray)));
+ break;
+ case NULL:
+ result = Long.valueOf(null);
+ break;
+ default:
+ _dataIn.reset();
+ throw new MessageFormatException("Invalid Object Type");
+ }
+ }
+ catch (EOFException eof)
+ {
+ throw new MessageEOFException("End of file Reached when reading message");
+ }
+ catch (IOException io)
+ {
+ throw new JMSException("IO exception when reading message");
+ }
+ return result;
+ }
+
+ /**
+ * Reads an Float.
+ * <p/>
+ * | | boolean byte short char int long float double String byte[]
+ * |----------------------------------------------------------------------
+ * |float | X X
+ *
+ * @return The float value read
+ * @throws JMSException If reading fails due to some error.
+ * @throws javax.jms.MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public float readFloat() throws JMSException
+ {
+ isReadableAndNotReadingByteArray();
+ float result;
+ try
+ {
+ _dataIn.mark(10);
+ byte type = _dataIn.readByte();
+ switch (type)
+ {
+ case FLOAT:
+ result = super.readFloat();
+ break;
+ case STRING:
+ int len = _dataIn.readInt();
+ byte[] bArray = new byte[len];
+ _dataIn.readFully(bArray);
+ result = new Float(new String(bArray));
+ break;
+ case NULL:
+ result = Float.valueOf(null);
+ break;
+ default:
+ _dataIn.reset();
+ throw new MessageFormatException("Invalid Object Type");
+ }
+ }
+ catch (EOFException eof)
+ {
+ throw new MessageEOFException("End of file Reached when reading message");
+ }
+ catch (IOException io)
+ {
+ throw new JMSException("IO exception when reading message");
+ }
+ return result;
+ }
+
+ /**
+ * Reads an double.
+ * <p/>
+ * | | boolean byte short char int long float double String byte[]
+ * |----------------------------------------------------------------------
+ * |double | X X X
+ *
+ * @return The double value read
+ * @throws JMSException If reading fails due to some error.
+ * @throws javax.jms.MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public double readDouble() throws JMSException
+ {
+ isReadableAndNotReadingByteArray();
+ double result;
+ try
+ {
+ _dataIn.mark(10);
+ byte type = _dataIn.readByte();
+ switch (type)
+ {
+ case DOUBLE:
+ result = super.readDouble();
+ break;
+ case FLOAT:
+ result = super.readFloat();
+ break;
+ case STRING:
+ int len = _dataIn.readInt();
+ byte[] bArray = new byte[len];
+ _dataIn.readFully(bArray);
+ result = new Double(new String(bArray));
+ break;
+ case NULL:
+ result = Double.valueOf(null);
+ break;
+ default:
+ _dataIn.reset();
+ throw new MessageFormatException("Invalid Object Type");
+ }
+ }
+ catch (EOFException eof)
+ {
+ throw new MessageEOFException("End of file Reached when reading message");
+ }
+ catch (IOException io)
+ {
+ throw new JMSException("IO exception when reading message");
+ }
+ return result;
+ }
+
+ /**
+ * Reads an string.
+ * <p/>
+ * | | boolean byte short char int long float double String byte[]
+ * |----------------------------------------------------------------------
+ * |double | X X X X X X X X X
+ *
+ * @return The string value read
+ * @throws JMSException If reading fails due to some error.
+ * @throws javax.jms.MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public String readString() throws JMSException
+ {
+ isReadableAndNotReadingByteArray();
+ String result;
+ try
+ {
+ _dataIn.mark(10);
+ byte type = _dataIn.readByte();
+ switch (type)
+ {
+ case BOOLEAN:
+ result = Boolean.valueOf(super.readBoolean()).toString();
+ break;
+ case BYTE:
+ result = Byte.valueOf(super.readByte()).toString();
+ break;
+ case SHORT:
+ result = Short.valueOf(super.readShort()).toString();
+ break;
+ case CHAR:
+ result = Character.valueOf(super.readChar()).toString();
+ break;
+ case INT:
+ result = Integer.valueOf(super.readInt()).toString();
+ break;
+ case LONG:
+ result = Long.valueOf(super.readLong()).toString();
+ break;
+ case FLOAT:
+ result = Float.valueOf(super.readFloat()).toString();
+ break;
+ case DOUBLE:
+ result = Double.valueOf(super.readDouble()).toString();
+ break;
+ case STRING:
+ int len = _dataIn.readInt();
+ if (len == 0)
+ {
+ throw new NullPointerException("trying to read a null String");
+ }
+ byte[] bArray = new byte[len];
+ _dataIn.readFully(bArray);
+ result = new String(bArray);
+ break;
+ case NULL:
+ result = null;
+ break;
+ default:
+ _dataIn.reset();
+ throw new MessageFormatException("Invalid Object Type");
+ }
+ }
+ catch (EOFException eof)
+ {
+ throw new MessageEOFException("End of file Reached when reading message");
+ }
+ catch (IOException io)
+ {
+ throw new JMSException("IO exception when reading message");
+ }
+ return result;
+ }
+
+ /**
+ * Reads an byte[].
+ * <p/>
+ * | | boolean byte short char int long float double String byte[]
+ * |----------------------------------------------------------------------
+ * |byte[] | X
+ * <p> The JMS spec says:
+ * To read the field value, readBytes should be successively called until
+ * it returns a value less than the length
+ * of the read buffer. The value of the bytes in the buffer following the last byte read is undefined.
+ *
+ * @param value The byte array into which the data is read.
+ * @return the total number of bytes read into the array, or -1 if
+ * there is no more data because the end of the byte field has been
+ * reached.
+ * @throws JMSException If reading fails due to some error.
+ * @throws javax.jms.MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public int readBytes(byte[] value) throws JMSException
+ {
+ isReadable();
+ int result = -1;
+ try
+ {
+ byte type = BYTEARRAY;
+ if (_sizeOfByteArray == 0)
+ {
+ // we are not in the middle of reading this byte array
+ _dataIn.mark(10);
+ type = _dataIn.readByte();
+ }
+ switch (type)
+ {
+ case BYTEARRAY:
+ if (_sizeOfByteArray == 0)
+ {
+ // we need to read the size of this byte array
+ _sizeOfByteArray = _dataIn.readInt();
+ }
+ result = _dataIn.read(value, 0, value.length);
+ if (result != -1)
+ {
+ _sizeOfByteArray = _sizeOfByteArray - result;
+ }
+ else
+ {
+ _sizeOfByteArray = 0;
+ }
+ case NULL:
+ // result = -1;
+ break;
+ default:
+ _dataIn.reset();
+ throw new MessageFormatException("Invalid Object Type");
+ }
+ }
+ catch (EOFException eof)
+ {
+ throw new MessageEOFException("End of file Reached when reading message");
+ }
+ catch (IOException io)
+ {
+ throw new JMSException("IO exception when reading message");
+ }
+ return result;
+ }
+
+ /**
+ * Reads an object from the stream message.
+ * <p> The JMS spec says:
+ * <P>This method can be used to return, in objectified format,
+ * an object in the Java programming language ("Java object") that has
+ * been written to the stream with the equivalent
+ * <CODE>writeObject</CODE> method call, or its equivalent primitive
+ * <CODE>write<I>type</I></CODE> method.
+ * <P>An attempt to call <CODE>readObject</CODE> to read a byte field
+ * value into a new <CODE>byte[]</CODE> object before the full value of the
+ * byte field has been read will throw a
+ * <CODE>MessageFormatException</CODE>.
+ *
+ * @return A Java object from the stream message, in objectified
+ * format
+ * @throws JMSException If reading fails due to some error.
+ * @throws javax.jms.MessageEOFException If unexpected end of message data has been reached.
+ * @throws javax.jms.MessageNotReadableException
+ * If the message is in write-only mode.
+ * @throws MessageFormatException If this type conversion is invalid.
+ */
+ public Object readObject() throws JMSException
+ {
+ isReadableAndNotReadingByteArray();
+ Object result;
+ try
+ {
+ _dataIn.mark(10);
+ byte type = _dataIn.readByte();
+ switch (type)
+ {
+ case BOOLEAN:
+ result = super.readBoolean();
+ break;
+ case BYTE:
+ result = super.readByte();
+ break;
+ case SHORT:
+ result = super.readShort();
+ break;
+ case CHAR:
+ result = super.readChar();
+ break;
+ case INT:
+ result = super.readInt();
+ break;
+ case LONG:
+ result = super.readLong();
+ break;
+ case FLOAT:
+ result = super.readFloat();
+ break;
+ case DOUBLE:
+ result = super.readDouble();
+ break;
+ case STRING:
+ int len = _dataIn.readInt();
+ if (len == 0)
+ {
+ result = null;
+ }
+ else
+ {
+ byte[] bArray = new byte[len];
+ _dataIn.readFully(bArray);
+ result = new String(bArray);
+ }
+ break;
+ case BYTEARRAY:
+ int totalBytes = _dataIn.readInt();
+ byte[] bArray = new byte[totalBytes];
+ _dataIn.read(bArray, 0, totalBytes);
+ result = bArray;
+ break;
+ case NULL:
+ result = null;
+ break;
+ default:
+ _dataIn.reset();
+ throw new MessageFormatException("Invalid Object Type");
+ }
+ }
+ catch (EOFException eof)
+ {
+ throw new MessageEOFException("End of file Reached when reading message");
+ }
+ catch (IOException io)
+ {
+ throw new JMSException("IO exception when reading message");
+ }
+ return result;
+ }
+
+ /**
+ * Writes a boolean to the stream message.
+ *
+ * @param val The boolean value to be written
+ * @throws JMSException If writting a boolean fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeBoolean(boolean val) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeShort(BOOLEAN);
+ super.writeBoolean(val);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes a byte to the stream message.
+ *
+ * @param val The byte value to be written
+ * @throws JMSException If writting a boolean fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeByte(byte val) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeShort(BYTE);
+ super.writeByte(val);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes a short to the stream message.
+ *
+ * @param val The short value to be written
+ * @throws JMSException If writting a boolean fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeShort(short val) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeShort(SHORT);
+ super.writeShort(val);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes a char to the stream message.
+ *
+ * @param val The char value to be written
+ * @throws JMSException If writting a boolean fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeChar(char val) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeShort(CHAR);
+ super.writeChar(val);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes a int to the stream message.
+ *
+ * @param val The int value to be written
+ * @throws JMSException If writting a boolean fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeInt(int val) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeShort(INT);
+ super.writeInt(val);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes a long to the stream message.
+ *
+ * @param val The long value to be written
+ * @throws JMSException If writting a boolean fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeLong(long val) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeShort(LONG);
+ super.writeLong(val);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes a float to the stream message.
+ *
+ * @param val The float value to be written
+ * @throws JMSException If writting a boolean fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeFloat(float val) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeShort(FLOAT);
+ super.writeFloat(val);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes a double to the stream message.
+ *
+ * @param val The double value to be written
+ * @throws JMSException If writting a boolean fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeDouble(double val) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeShort(DOUBLE);
+ super.writeDouble(val);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes a string to the stream message.
+ *
+ * @param val The string value to be written
+ * @throws JMSException If writting a boolean fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeString(String val) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeShort(STRING);
+ if (val == null)
+ {
+ _dataOut.writeInt(0);
+ }
+ else
+ {
+ byte[] bArray = val.getBytes();
+ int len = bArray.length;
+ _dataOut.writeInt(len);
+ _dataOut.write(bArray);
+ }
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes a byte array to the stream message.
+ *
+ * @param val The byte array value to be written
+ * @throws JMSException If writting a boolean fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeBytes(byte[] val) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeShort(BYTEARRAY);
+ _dataOut.writeInt(val.length);
+ super.writeBytes(val);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes a portion of byte array to the bytes message.
+ *
+ * @param val The byte array value to be written
+ * @throws JMSException If writting a byte array fails due to some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeBytes(byte[] val, int offset, int length) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ _dataOut.writeShort(BYTEARRAY);
+ _dataOut.writeInt(length);
+ super.writeBytes(val, offset, length);
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Writes an Object to the bytes message.
+ * JMS spec says:
+ * <p>This method works only for the objectified primitive
+ * object types Integer, Double, Long, String and byte
+ * arrays.
+ *
+ * @param val The short value to be written
+ * @throws JMSException If writting a short fails due to some error.
+ * @throws NullPointerException if the parameter val is null.
+ * @throws MessageFormatException If the object is of an invalid type.
+ * @throws javax.jms.MessageNotWriteableException
+ * If the message is in read-only mode.
+ */
+ public void writeObject(Object val) throws JMSException
+ {
+ isWriteable();
+ try
+ {
+ if (val == null)
+ {
+ _dataOut.writeShort(NULL);
+ }
+ else if (val instanceof Byte)
+ {
+ writeByte((Byte) val);
+ }
+ else if (val instanceof Boolean)
+ {
+ writeBoolean((Boolean) val);
+ }
+ else if (val instanceof Short)
+ {
+ writeShort((Short) val);
+ }
+ else if (val instanceof Integer)
+ {
+ writeInt((Integer) val);
+ }
+ else if (val instanceof Long)
+ {
+ writeLong((Long) val);
+ }
+ else if (val instanceof Double)
+ {
+ writeDouble((Double) val);
+ }
+ else if (val instanceof Float)
+ {
+ writeFloat((Float) val);
+ }
+ else if (val instanceof Character)
+ {
+ writeChar((Character) val);
+ }
+ else if (val instanceof String)
+ {
+ writeString((String) val);
+ }
+ else if (val instanceof byte[])
+ {
+ writeBytes((byte[]) val);
+ }
+ else
+ {
+ throw new MessageFormatException(
+ "The data type of the object specified as the value to writeObject " + "was of an invalid type.");
+ }
+ }
+ catch (IOException e)
+ {
+ throw new JMSException("IO problem when writting " + e.getLocalizedMessage());
+ }
+ }
+
+ //-- overwritten methods
+ /**
+ * Test whether this message is readable by throwing a MessageNotReadableException if this
+ * message cannot be read.
+ *
+ * @throws javax.jms.MessageNotReadableException
+ * If this message cannot be read.
+ * @throws javax.jms.MessageFormatException
+ * If reading a byte array.
+ */
+ protected void isReadableAndNotReadingByteArray() throws MessageNotReadableException, MessageFormatException
+ {
+ if (_dataIn == null)
+ {
+ throw new MessageNotReadableException("Cannot read this message");
+ }
+ if (_sizeOfByteArray > 0)
+ {
+ throw new MessageFormatException(
+ "Read of object attempted while incomplete byteArray stored in message " + "- finish reading byte array first.");
+ }
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/TextMessageImpl.java b/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/TextMessageImpl.java
new file mode 100644
index 0000000000..577b586fe2
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpidity/njms/message/TextMessageImpl.java
@@ -0,0 +1,328 @@
+/* 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.qpidity.njms.message;
+
+import org.apache.qpidity.QpidException;
+
+import javax.jms.TextMessage;
+import javax.jms.JMSException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.*;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Implements the interface javax.njms.TextMessage
+ */
+public class TextMessageImpl extends MessageImpl implements TextMessage
+{
+ /**
+ * The character encoding for converting non ASCII characters
+ * Default UTF-16
+ */
+ private static final String CHARACTER_ENCODING = "UTF-16";
+
+ /**
+ * This message text. The byte form is set when this message is sent
+ * the text is set when the message is received.
+ */
+ private String _messageText;
+
+ //--- Constructor
+ /**
+ * Constructor used by SessionImpl.
+ */
+ public TextMessageImpl()
+ {
+ super();
+ setMessageType(String.valueOf(MessageFactory.JAVAX_JMS_STREAMMESSAGE));
+ }
+
+ /**
+ * Constructor used by MessageFactory
+ *
+ * @param message The new qpid message.
+ * @throws QpidException In case of IO problem when reading the received message.
+ */
+ protected TextMessageImpl(org.apache.qpidity.api.Message message) throws QpidException
+ {
+ super(message);
+ }
+
+ //--- interface TextMessage
+
+ public String getText() throws JMSException
+ {
+ return _messageText;
+ }
+
+ /**
+ * Set the text (String) of this TextMessage.
+ *
+ * @param text The String containing the text.
+ * @throws JMSException If setting the text fails due some error.
+ * @throws javax.jms.MessageNotWriteableException
+ * If message in read-only mode.
+ */
+ public void setText(String text) throws JMSException
+ {
+ isWriteable();
+ _messageText = text;
+ }
+
+ //-- Overwritten methods
+
+ /**
+ * This method is invoked before this message is dispatched.
+ * <p>This class uses it to convert its text payload into a ByteBuffer
+ */
+ public void beforeMessageDispatch() throws QpidException
+ {
+ if (_messageText != null)
+ {
+ // set this message data
+ try
+ {
+ setMessageData(ByteBuffer.wrap(_messageText.getBytes(CHARACTER_ENCODING)));
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new QpidException("Problem when encoding text " + _messageText, null, e);
+ }
+ }
+ super.beforeMessageDispatch();
+ }
+
+
+ /**
+ * This method is invoked after this message has been received.
+ */
+ @Override
+ public void afterMessageReceive() throws QpidException
+ {
+ super.afterMessageReceive();
+ ByteBuffer messageData = getMessageData();
+ if (messageData != null)
+ {
+ try
+ {
+ _messageText = getString();
+ }
+ catch (Exception e)
+ {
+ throw new QpidException("Problem when decoding text", null, e);
+ }
+ }
+ }
+
+ /**
+ * Clear out the message body. Clearing a message's body does not clear
+ * its header values or property entries.
+ * <P>If this message body was read-only, calling this method leaves
+ * the message body is in the same state as an empty body in a newly
+ * created message.
+ *
+ * @throws JMSException If clearing this message body fails to due to some error.
+ */
+ public void clearBody() throws JMSException
+ {
+ super.clearBody();
+ _messageText = null;
+ }
+
+ /**
+ * This method is taken from Mina code
+ *
+ * Reads a <code>NUL</code>-terminated string from this buffer using the
+ * specified <code>decoder</code> and returns it. This method reads
+ * until the limit of this buffer if no <tt>NUL</tt> is found.
+ *
+ * @return
+ * @throws java.nio.charset.CharacterCodingException
+ *
+ */
+ public String getString() throws CharacterCodingException
+ {
+ if (!getMessageData().hasRemaining())
+ {
+ return "";
+ }
+ Charset charset = Charset.forName(CHARACTER_ENCODING);
+ CharsetDecoder decoder = charset.newDecoder();
+
+ boolean utf16 = decoder.charset().name().startsWith("UTF-16");
+
+ int oldPos = getMessageData().position();
+ int oldLimit = getMessageData().limit();
+ int end = -1;
+ int newPos;
+
+ if (!utf16)
+ {
+ end = indexOf((byte) 0x00);
+ if (end < 0)
+ {
+ newPos = end = oldLimit;
+ }
+ else
+ {
+ newPos = end + 1;
+ }
+ }
+ else
+ {
+ int i = oldPos;
+ for (; ;)
+ {
+ boolean wasZero = getMessageData().get(i) == 0;
+ i++;
+
+ if (i >= oldLimit)
+ {
+ break;
+ }
+
+ if (getMessageData().get(i) != 0)
+ {
+ i++;
+ if (i >= oldLimit)
+ {
+ break;
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ if (wasZero)
+ {
+ end = i - 1;
+ break;
+ }
+ }
+
+ if (end < 0)
+ {
+ newPos = end = oldPos + ((oldLimit - oldPos) & 0xFFFFFFFE);
+ }
+ else
+ {
+ if (end + 2 <= oldLimit)
+ {
+ newPos = end + 2;
+ }
+ else
+ {
+ newPos = end;
+ }
+ }
+ }
+
+ if (oldPos == end)
+ {
+ getMessageData().position(newPos);
+ return "";
+ }
+
+ getMessageData().limit(end);
+ decoder.reset();
+
+ int expectedLength = (int) (getMessageData().remaining() * decoder.averageCharsPerByte()) + 1;
+ CharBuffer out = CharBuffer.allocate(expectedLength);
+ for (; ;)
+ {
+ CoderResult cr;
+ if (getMessageData().hasRemaining())
+ {
+ cr = decoder.decode(getMessageData(), out, true);
+ }
+ else
+ {
+ cr = decoder.flush(out);
+ }
+
+ if (cr.isUnderflow())
+ {
+ break;
+ }
+
+ if (cr.isOverflow())
+ {
+ CharBuffer o = CharBuffer.allocate(out.capacity() + expectedLength);
+ out.flip();
+ o.put(out);
+ out = o;
+ continue;
+ }
+
+ if (cr.isError())
+ {
+ // Revert the buffer back to the previous state.
+ getMessageData().limit(oldLimit);
+ getMessageData().position(oldPos);
+ cr.throwException();
+ }
+ }
+
+ getMessageData().limit(oldLimit);
+ getMessageData().position(newPos);
+ return out.flip().toString();
+ }
+
+ /**
+ * Returns the first occurence position of the specified byte from the current position to
+ * the current limit.
+ *
+ * @return <tt>-1</tt> if the specified byte is not found
+ * @param b
+ */
+ public int indexOf(byte b)
+ {
+ if (getMessageData().hasArray())
+ {
+ int arrayOffset = getMessageData().arrayOffset();
+ int beginPos = arrayOffset + getMessageData().position();
+ int limit = arrayOffset + getMessageData().limit();
+ byte[] array = getMessageData().array();
+
+ for (int i = beginPos; i < limit; i++)
+ {
+ if (array[i] == b)
+ {
+ return i - arrayOffset;
+ }
+ }
+ }
+ else
+ {
+ int beginPos = getMessageData().position();
+ int limit = getMessageData().limit();
+
+ for (int i = beginPos; i < limit; i++)
+ {
+ if (getMessageData().get(i) == b)
+ {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+}
+