diff options
author | Robert Greig <rgreig@apache.org> | 2006-12-21 21:08:38 +0000 |
---|---|---|
committer | Robert Greig <rgreig@apache.org> | 2006-12-21 21:08:38 +0000 |
commit | 7cedc91fe5ec9859d932e63dc1f7c809860126d0 (patch) | |
tree | 9b368e30db8554c7d7da1fbbcae4a1dec2fdf3ed | |
parent | c3343495a4b4da8d894d5ac89c2e8856b413ecdf (diff) | |
download | qpid-python-7cedc91fe5ec9859d932e63dc1f7c809860126d0.tar.gz |
Merge from trunk up to revision 487903
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/branches/new_persistence@489461 13f79535-47bb-0310-9956-ffa450edef68
82 files changed, 5645 insertions, 1782 deletions
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java index c7722d0e67..b5c7a2ba23 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java @@ -7,9 +7,9 @@ * 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 @@ -196,4 +196,34 @@ public class DestNameExchange extends AbstractExchange } } } + + public boolean isBound(String routingKey, AMQQueue queue) throws AMQException + { + final List<AMQQueue> queues = _index.get(routingKey); + return queues != null && queues.contains(queue); + } + + public boolean isBound(String routingKey) throws AMQException + { + final List<AMQQueue> queues = _index.get(routingKey); + return queues != null && !queues.isEmpty(); + } + + public boolean isBound(AMQQueue queue) throws AMQException + { + Map<String, List<AMQQueue>> bindings = _index.getBindingsMap(); + for (List<AMQQueue> queues : bindings.values()) + { + if (queues.contains(queue)) + { + return true; + } + } + return false; + } + + public boolean hasBindings() throws AMQException + { + return !_index.getBindingsMap().isEmpty(); + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java index 6ac73c4484..c341f30ab6 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java @@ -7,9 +7,9 @@ * 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 @@ -126,10 +126,11 @@ public class DestWildExchange extends AbstractExchange } // End of MBean class - public void registerQueue(String routingKey, AMQQueue queue, FieldTable args) throws AMQException + public synchronized void registerQueue(String routingKey, AMQQueue queue, FieldTable args) throws AMQException { assert queue != null; assert routingKey != null; + _logger.debug("Registering queue " + queue.getName() + " with routing key " + routingKey); // we need to use putIfAbsent, which is an atomic operation, to avoid a race condition List<AMQQueue> queueList = _routingKey2queues.putIfAbsent(routingKey, new CopyOnWriteArrayList<AMQQueue>()); // if we got null back, no previous value was associated with the specified routing key hence @@ -159,6 +160,8 @@ public class DestWildExchange extends AbstractExchange // TODO: add support for the immediate flag if (queues == null) { + _logger.warn("No queues found for routing key " + routingKey); + _logger.warn("Routing map contains: " + _routingKey2queues); //todo Check for valid topic - mritchie return; } @@ -172,7 +175,37 @@ public class DestWildExchange extends AbstractExchange } } - public void deregisterQueue(String routingKey, AMQQueue queue) throws AMQException + public boolean isBound(String routingKey, AMQQueue queue) throws AMQException + { + List<AMQQueue> queues = _routingKey2queues.get(routingKey); + return queues != null && queues.contains(queue); + } + + + public boolean isBound(String routingKey) throws AMQException + { + List<AMQQueue> queues = _routingKey2queues.get(routingKey); + return queues != null && !queues.isEmpty(); + } + + public boolean isBound(AMQQueue queue) throws AMQException + { + for (List<AMQQueue> queues : _routingKey2queues.values()) + { + if (queues.contains(queue)) + { + return true; + } + } + return false; + } + + public boolean hasBindings() throws AMQException + { + return !_routingKey2queues.isEmpty(); + } + + public synchronized void deregisterQueue(String routingKey, AMQQueue queue) throws AMQException { assert queue != null; assert routingKey != null; @@ -190,6 +223,10 @@ public class DestWildExchange extends AbstractExchange throw new AMQException("Queue " + queue + " was not registered with exchange " + this.getName() + " with routing key " + routingKey); } + if (queues.isEmpty()) + { + _routingKey2queues.remove(queues); + } } protected ExchangeMBean createMBean() throws AMQException diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java index f6d84dea02..8ef5f0ab29 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java @@ -7,9 +7,9 @@ * 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 @@ -47,4 +47,30 @@ public interface Exchange void deregisterQueue(String routingKey, AMQQueue queue) throws AMQException; void route(AMQMessage message) throws AMQException; + + /** + * Determines whether a message would be isBound to a particular queue using a specific routing key + * @param routingKey + * @param queue + * @return + * @throws AMQException + */ + boolean isBound(String routingKey, AMQQueue queue) throws AMQException; + + /** + * Determines whether a message is routing to any queue using a specific routing key + * @param routingKey + * @return + * @throws AMQException + */ + boolean isBound(String routingKey) throws AMQException; + + boolean isBound(AMQQueue queue) throws AMQException; + + /** + * Returns true if this exchange has at least one binding associated with it. + * @return + * @throws AMQException + */ + boolean hasBindings() throws AMQException; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java index dbf4ef91db..97b4adecdd 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java @@ -221,6 +221,33 @@ public class HeadersExchange extends AbstractExchange } } + public boolean isBound(String routingKey, AMQQueue queue) throws AMQException + { + return isBound(queue); + } + + public boolean isBound(String routingKey) throws AMQException + { + return hasBindings(); + } + + public boolean isBound(AMQQueue queue) throws AMQException + { + for (Registration r : _bindings) + { + if (r.queue.equals(queue)) + { + return true; + } + } + return false; + } + + public boolean hasBindings() throws AMQException + { + return !_bindings.isEmpty(); + } + protected Map getHeaders(ContentHeaderBody contentHeaderFrame) { //what if the content type is not 'basic'? 'file' and 'stream' content classes also define headers, diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java index fb48729c9e..485c4739bd 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java @@ -7,9 +7,9 @@ * 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 @@ -24,6 +24,7 @@ import org.apache.qpid.server.queue.AMQQueue; import java.util.List; import java.util.Map; +import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; @@ -37,7 +38,7 @@ class Index private ConcurrentMap<String, List<AMQQueue>> _index = new ConcurrentHashMap<String, List<AMQQueue>>(); - boolean add(String key, AMQQueue queue) + synchronized boolean add(String key, AMQQueue queue) { List<AMQQueue> queues = _index.get(key); if(queues == null) @@ -61,7 +62,7 @@ class Index } } - boolean remove(String key, AMQQueue queue) + synchronized boolean remove(String key, AMQQueue queue) { List<AMQQueue> queues = _index.get(key); if (queues != null) @@ -83,6 +84,6 @@ class Index Map<String, List<AMQQueue>> getBindingsMap() { - return _index; + return new HashMap<String, List<AMQQueue>>(_index); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java new file mode 100644 index 0000000000..5aaf78d6b7 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java @@ -0,0 +1,163 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.qpid.server.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.ExchangeBoundBody; +import org.apache.qpid.framing.ExchangeBoundOkBody; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.protocol.AMQMethodEvent; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; + +/** + * @author Apache Software Foundation + */ +public class ExchangeBoundHandler implements StateAwareMethodListener<ExchangeBoundBody> +{ + private static final ExchangeBoundHandler _instance = new ExchangeBoundHandler(); + + public static final int OK = 0; + + public static final int EXCHANGE_NOT_FOUND = 1; + + public static final int QUEUE_NOT_FOUND = 2; + + public static final int NO_BINDINGS = 3; + + public static final int QUEUE_NOT_BOUND = 4; + + public static final int NO_QUEUE_BOUND_WITH_RK = 5; + + public static final int SPECIFIC_QUEUE_NOT_BOUND_WITH_RK = 6; + + public static ExchangeBoundHandler getInstance() + { + return _instance; + } + + private ExchangeBoundHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry, + ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession, + AMQMethodEvent<ExchangeBoundBody> evt) throws AMQException + { + ExchangeBoundBody body = evt.getMethod(); + + String exchangeName = body.exchange; + String queueName = body.queue; + String routingKey = body.routingKey; + if (exchangeName == null) + { + throw new AMQException("Exchange exchange must not be null"); + } + Exchange exchange = exchangeRegistry.getExchange(exchangeName); + AMQFrame response; + if (exchange == null) + { + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), EXCHANGE_NOT_FOUND, + "Exchange " + exchangeName + " not found"); + } + else if (routingKey == null) + { + if (queueName == null) + { + if (exchange.hasBindings()) + { + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), OK, null); + } + else + { + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), NO_BINDINGS, null); + } + } + else + { + AMQQueue queue = queueRegistry.getQueue(queueName); + if (queue == null) + { + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), QUEUE_NOT_FOUND, + "Queue " + queueName + " not found"); + } + else + { + if (exchange.isBound(queue)) + { + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), OK, null); + } + else + { + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), QUEUE_NOT_BOUND, + "Queue " + queueName + " not bound to exchange " + + exchangeName); + } + } + } + } + else if (queueName != null) + { + AMQQueue queue = queueRegistry.getQueue(queueName); + if (queue == null) + { + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), QUEUE_NOT_FOUND, + "Queue " + queueName + " not found"); + } + else + { + if (exchange.isBound(body.routingKey, queue)) + { + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), OK, + null); + } + else + { + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), + SPECIFIC_QUEUE_NOT_BOUND_WITH_RK, + "Queue " + queueName + + " not bound with routing key " + + body.routingKey + " to exchange " + + exchangeName); + } + } + } + else + { + if (exchange.isBound(body.routingKey)) + { + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), OK, + null); + } + else + { + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), + NO_QUEUE_BOUND_WITH_RK, + "No queue bound with routing key " + + body.routingKey + " to exchange " + + exchangeName); + } + } + protocolSession.writeFrame(response); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java index fd97bf0d96..1832f01b7a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java @@ -31,22 +31,19 @@ import org.apache.qpid.codec.AMQDecoder; import org.apache.qpid.framing.*; import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.exchange.ExchangeRegistry; -import org.apache.qpid.server.management.*; +import org.apache.qpid.server.management.Managable; +import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.queue.QueueRegistry; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.state.AMQStateManager; import javax.management.JMException; -import javax.management.MBeanException; -import javax.management.MBeanNotificationInfo; -import javax.management.Notification; -import javax.management.monitor.MonitorNotification; -import javax.management.openmbean.*; import javax.security.sasl.SaslServer; import java.net.InetSocketAddress; import java.net.SocketAddress; -import java.util.Date; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArraySet; @@ -72,7 +69,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, private AMQCodecFactory _codecFactory; - private ManagedAMQProtocolSession _managedObject; + private AMQProtocolSessionMBean _managedObject; private SaslServer _saslServer; @@ -81,11 +78,10 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, private Object _lastSent; private boolean _closed; - + // maximum number of channels this session should have private long _maxNoOfChannels = 1000; /* AMQP Version for this session */ - private byte _major; private byte _minor; @@ -94,190 +90,6 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, return _managedObject; } - /** - * This class implements the management interface (is an MBean). In order to - * make more attributes, operations and notifications available over JMX simply - * augment the ManagedConnection interface and add the appropriate implementation here. - */ - @MBeanDescription("Management Bean for an AMQ Broker Connection") - private final class ManagedAMQProtocolSession extends AMQManagedObject implements ManagedConnection - { - private String _name = null; - //openmbean data types for representing the channel attributes - private String[] _channelAtttibuteNames = { "Channel Id", "Transactional", "Default Queue", "Unacknowledged Message Count"}; - private String[] _indexNames = {_channelAtttibuteNames[0]}; - private OpenType[] _channelAttributeTypes = {SimpleType.INTEGER, SimpleType.BOOLEAN, SimpleType.STRING, SimpleType.INTEGER}; - private CompositeType _channelType = null; // represents the data type for channel data - private TabularType _channelsType = null; // Data type for list of channels type - private TabularDataSupport _channelsList = null; - - @MBeanConstructor("Creates an MBean exposing an AMQ Broker Connection") - public ManagedAMQProtocolSession() throws JMException - { - super(ManagedConnection.class, ManagedConnection.TYPE); - init(); - } - - /** - * initialises the openmbean data types - */ - private void init() throws OpenDataException - { - String remote = getRemoteAddress(); - remote = "anonymous".equals(remote) ? remote + hashCode() : remote; - _name = jmxEncode(new StringBuffer(remote), 0).toString(); - _channelType = new CompositeType("Channel", "Channel Details", _channelAtttibuteNames, - _channelAtttibuteNames, _channelAttributeTypes); - _channelsType = new TabularType("Channels", "Channels", _channelType, _indexNames); - } - - public Date getLastIoTime() - { - return new Date(_minaProtocolSession.getLastIoTime()); - } - - public String getRemoteAddress() - { - return _minaProtocolSession.getRemoteAddress().toString(); - } - - public Long getWrittenBytes() - { - return _minaProtocolSession.getWrittenBytes(); - } - - public Long getReadBytes() - { - return _minaProtocolSession.getReadBytes(); - } - - public Long getMaximumNumberOfChannels() - { - return _maxNoOfChannels; - } - - public void setMaximumNumberOfChannels(Long value) - { - _maxNoOfChannels = value; - } - - public String getObjectInstanceName() - { - return _name; - } - - public void commitTransactions(int channelId) throws JMException - { - try - { - AMQChannel channel = _channelMap.get(channelId); - if (channel == null) - { - throw new JMException("The channel (channel Id = " + channelId + ") does not exist"); - } - if (channel.isTransactional()) - { - channel.commit(); - } - } - catch(AMQException ex) - { - throw new MBeanException(ex, ex.toString()); - } - } - - public void rollbackTransactions(int channelId) throws JMException - { - try - { - AMQChannel channel = _channelMap.get(channelId); - if (channel == null) - { - throw new JMException("The channel (channel Id = " + channelId + ") does not exist"); - } - if (channel.isTransactional()) - { - channel.rollback(); - } - } - catch(AMQException ex) - { - throw new MBeanException(ex, ex.toString()); - } - } - - /** - * Creates the list of channels in tabular form from the _channelMap. - * @return list of channels in tabular form. - * @throws OpenDataException - */ - public TabularData channels() throws OpenDataException - { - _channelsList = new TabularDataSupport(_channelsType); - - for (Map.Entry<Integer, AMQChannel> entry : _channelMap.entrySet()) - { - AMQChannel channel = entry.getValue(); - Object[] itemValues = {channel.getChannelId(), channel.isTransactional(), - (channel.getDefaultQueue() != null) ? channel.getDefaultQueue().getName() : null, - channel.getUnacknowledgedMessageMap().size()}; - - CompositeData channelData = new CompositeDataSupport(_channelType, _channelAtttibuteNames, itemValues); - _channelsList.put(channelData); - } - - return _channelsList; - } - - public void closeChannel(int id) throws Exception - { - try - { - AMQMinaProtocolSession.this.closeChannel(id); - } - catch (AMQException ex) - { - throw new Exception(ex.toString()); - } - } - - public void closeConnection() throws Exception - { - try - { - AMQMinaProtocolSession.this.closeSession(); - } - catch (AMQException ex) - { - throw new Exception(ex.toString()); - } - } - - @Override - public MBeanNotificationInfo[] getNotificationInfo() - { - String[] notificationTypes = new String[] {MonitorNotification.THRESHOLD_VALUE_EXCEEDED}; - String name = MonitorNotification.class.getName(); - String description = "Channel count has reached threshold value"; - MBeanNotificationInfo info1 = new MBeanNotificationInfo(notificationTypes, name, description); - - return new MBeanNotificationInfo[] {info1}; - } - - private void checkForNotification() - { - int channelsCount = _channelMap.size(); - if (channelsCount >= getMaximumNumberOfChannels()) - { - Notification n = new Notification(MonitorNotification.THRESHOLD_VALUE_EXCEEDED, this, - ++_notificationSequenceNumber, System.currentTimeMillis(), - "Channel count (" + channelsCount + ") has reached the threshold value"); - - _broadcaster.sendNotification(n); - } - } - - } // End of MBean class public AMQMinaProtocolSession(IoSession session, QueueRegistry queueRegistry, ExchangeRegistry exchangeRegistry, AMQCodecFactory codecFactory) @@ -301,11 +113,11 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, _managedObject.register(); } - private ManagedAMQProtocolSession createMBean() throws AMQException + private AMQProtocolSessionMBean createMBean() throws AMQException { try { - return new ManagedAMQProtocolSession(); + return new AMQProtocolSessionMBean(this); } catch(JMException ex) { @@ -314,6 +126,11 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, } } + public IoSession getIOSession() + { + return _minaProtocolSession; + } + public static AMQProtocolSession getAMQProtocolSession(IoSession minaProtocolSession) { return (AMQProtocolSession) minaProtocolSession.getAttachment(); @@ -467,6 +284,11 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, _contextKey = contextKey; } + public List<AMQChannel> getChannels() + { + return new ArrayList<AMQChannel>(_channelMap.values()); + } + public AMQChannel getChannel(int channelId) throws AMQException { return _channelMap.get(channelId); @@ -475,7 +297,42 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, public void addChannel(AMQChannel channel) { _channelMap.put(channel.getChannelId(), channel); - _managedObject.checkForNotification(); + checkForNotification(); + } + + private void checkForNotification() + { + int channelsCount = _channelMap.size(); + if (channelsCount >= _maxNoOfChannels) + { + _managedObject.notifyClients("Channel count (" + channelsCount + ") has reached the threshold value"); + } + } + + public Long getMaximumNumberOfChannels() + { + return _maxNoOfChannels; + } + + public void setMaximumNumberOfChannels(Long value) + { + _maxNoOfChannels = value; + } + + public void commitTransactions(AMQChannel channel) throws AMQException + { + if (channel != null && channel.isTransactional()) + { + channel.commit(); + } + } + + public void rollbackTransactions(AMQChannel channel) throws AMQException + { + if (channel != null && channel.isTransactional()) + { + channel.rollback(); + } } /** diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java new file mode 100644 index 0000000000..a47d462810 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java @@ -0,0 +1,241 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.qpid.server.protocol; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.management.AMQManagedObject; +import org.apache.qpid.server.management.MBeanConstructor; +import org.apache.qpid.server.management.MBeanDescription; + +import javax.management.JMException; +import javax.management.MBeanException; +import javax.management.MBeanNotificationInfo; +import javax.management.Notification; +import javax.management.monitor.MonitorNotification; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; +import java.util.Date; +import java.util.List; + +/** + * This MBean class implements the management interface. In order to make more attributes, operations and notifications + * available over JMX simply augment the ManagedConnection interface and add the appropriate implementation here. + */ +@MBeanDescription("Management Bean for an AMQ Broker Connection") +public class AMQProtocolSessionMBean extends AMQManagedObject implements ManagedConnection +{ + private AMQMinaProtocolSession _session = null; + private String _name = null; + //openmbean data types for representing the channel attributes + private String[] _channelAtttibuteNames = {"Channel Id", "Transactional", "Default Queue", "Unacknowledged Message Count"}; + private String[] _indexNames = {_channelAtttibuteNames[0]}; + private OpenType[] _channelAttributeTypes = {SimpleType.INTEGER, SimpleType.BOOLEAN, SimpleType.STRING, SimpleType.INTEGER}; + private CompositeType _channelType = null; // represents the data type for channel data + private TabularType _channelsType = null; // Data type for list of channels type + + @MBeanConstructor("Creates an MBean exposing an AMQ Broker Connection") + public AMQProtocolSessionMBean(AMQMinaProtocolSession session) throws JMException + { + super(ManagedConnection.class, ManagedConnection.TYPE); + _session = session; + init(); + } + + /** + * initialises the openmbean data types + */ + private void init() throws OpenDataException + { + String remote = getRemoteAddress(); + remote = "anonymous".equals(remote) ? remote + hashCode() : remote; + _name = jmxEncode(new StringBuffer(remote), 0).toString(); + _channelType = new CompositeType("Channel", "Channel Details", _channelAtttibuteNames, + _channelAtttibuteNames, _channelAttributeTypes); + _channelsType = new TabularType("Channels", "Channels", _channelType, _indexNames); + } + + public Date getLastIoTime() + { + return new Date(_session.getIOSession().getLastIoTime()); + } + + public String getRemoteAddress() + { + return _session.getIOSession().getRemoteAddress().toString(); + } + + public Long getWrittenBytes() + { + return _session.getIOSession().getWrittenBytes(); + } + + public Long getReadBytes() + { + return _session.getIOSession().getReadBytes(); + } + + public Long getMaximumNumberOfChannels() + { + return _session.getMaximumNumberOfChannels(); + } + + public void setMaximumNumberOfChannels(Long value) + { + _session.setMaximumNumberOfChannels(value); + } + + public String getObjectInstanceName() + { + return _name; + } + + /** + * commits transactions for a transactional channel + * + * @param channelId + * @throws JMException if channel with given id doesn't exist or if commit fails + */ + public void commitTransactions(int channelId) throws JMException + { + try + { + AMQChannel channel = _session.getChannel(channelId); + if (channel == null) + { + throw new JMException("The channel (channel Id = " + channelId + ") does not exist"); + } + _session.commitTransactions(channel); + } + catch (AMQException ex) + { + throw new MBeanException(ex, ex.toString()); + } + } + + /** + * rollsback the transactions for a transactional channel + * + * @param channelId + * @throws JMException if channel with given id doesn't exist or if rollback fails + */ + public void rollbackTransactions(int channelId) throws JMException + { + try + { + AMQChannel channel = _session.getChannel(channelId); + if (channel == null) + { + throw new JMException("The channel (channel Id = " + channelId + ") does not exist"); + } + _session.rollbackTransactions(channel); + } + catch (AMQException ex) + { + throw new MBeanException(ex, ex.toString()); + } + } + + /** + * Creates the list of channels in tabular form from the _channelMap. + * + * @return list of channels in tabular form. + * @throws OpenDataException + */ + public TabularData channels() throws OpenDataException + { + TabularDataSupport channelsList = new TabularDataSupport(_channelsType); + List<AMQChannel> list = _session.getChannels(); + + for (AMQChannel channel : list) + { + Object[] itemValues = {channel.getChannelId(), channel.isTransactional(), + (channel.getDefaultQueue() != null) ? channel.getDefaultQueue().getName() : null, + channel.getUnacknowledgedMessageMap().size()}; + + CompositeData channelData = new CompositeDataSupport(_channelType, _channelAtttibuteNames, itemValues); + channelsList.put(channelData); + } + + return channelsList; + } + + /** + * @see AMQMinaProtocolSession#closeChannel(int) + */ + public void closeChannel(int id) throws JMException + { + try + { + AMQChannel channel = _session.getChannel(id); + if (channel == null) + { + throw new JMException("The channel (channel Id = " + id + ") does not exist"); + } + + _session.closeChannel(id); + } + catch (AMQException ex) + { + throw new MBeanException(ex, ex.toString()); + } + } + + /** + * closes the connection. The administrator can use this management operation to close connection to free up + * resources. + * @throws JMException + */ + public void closeConnection() throws JMException + { + try + { + _session.closeSession(); + } + catch (AMQException ex) + { + throw new MBeanException(ex, ex.toString()); + } + } + + @Override + public MBeanNotificationInfo[] getNotificationInfo() + { + String[] notificationTypes = new String[]{MonitorNotification.THRESHOLD_VALUE_EXCEEDED}; + String name = MonitorNotification.class.getName(); + String description = "Channel count has reached threshold value"; + MBeanNotificationInfo info1 = new MBeanNotificationInfo(notificationTypes, name, description); + + return new MBeanNotificationInfo[]{info1}; + } + + public void notifyClients(String notificationMsg) + { + Notification n = new Notification(MonitorNotification.THRESHOLD_VALUE_EXCEEDED, this, + ++_notificationSequenceNumber, System.currentTimeMillis(), notificationMsg); + _broadcaster.sendNotification(n); + } + +} // End of MBean class diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java index 7f3d772e47..5f6d4c2939 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java @@ -22,19 +22,15 @@ package org.apache.qpid.server.queue; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; -import org.apache.qpid.framing.ContentBody; -import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.BasicContentHeaderProperties; import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.management.*; +import org.apache.qpid.server.management.Managable; +import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.mina.common.ByteBuffer; -import javax.management.*; -import javax.management.monitor.MonitorNotification; -import javax.management.openmbean.*; +import javax.management.JMException; import java.text.MessageFormat; -import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.Executor; @@ -100,7 +96,7 @@ public class AMQQueue implements Managable, Comparable * max allowed number of messages on a queue. */ private Integer _maxMessageCount = 10000; - + /** * max queue depth(KB) for the queue */ @@ -116,340 +112,6 @@ public class AMQQueue implements Managable, Comparable return _name.compareTo(((AMQQueue) o).getName()); } - /** - * MBean class for AMQQueue. It implements all the management features exposed - * for an AMQQueue. - */ - @MBeanDescription("Management Interface for AMQQueue") - private final class AMQQueueMBean extends AMQManagedObject implements ManagedQueue - { - private String _queueName = null; - // OpenMBean data types for viewMessages method - private String[] _msgAttributeNames = {"Message Id", "Header", "Size(bytes)", "Redelivered"}; - private String[] _msgAttributeIndex = {_msgAttributeNames[0]}; - private OpenType[] _msgAttributeTypes = new OpenType[4]; // AMQ message attribute types. - private CompositeType _messageDataType = null; // Composite type for representing AMQ Message data. - private TabularType _messagelistDataType = null; // Datatype for representing AMQ messages list. - - // OpenMBean data types for viewMessageContent method - private CompositeType _msgContentType = null; - private String[] _msgContentAttributes = {"Message Id", "MimeType", "Encoding", "Content"}; - private OpenType[] _msgContentAttributeTypes = new OpenType[4]; - - @MBeanConstructor("Creates an MBean exposing an AMQQueue") - public AMQQueueMBean() throws JMException - { - super(ManagedQueue.class, ManagedQueue.TYPE); - init(); - } - - /** - * initialises the openmbean data types - */ - private void init() throws OpenDataException - { - _queueName = jmxEncode(new StringBuffer(_name), 0).toString(); - _msgContentAttributeTypes[0] = SimpleType.LONG; // For message id - _msgContentAttributeTypes[1] = SimpleType.STRING; // For MimeType - _msgContentAttributeTypes[2] = SimpleType.STRING; // For Encoding - _msgContentAttributeTypes[3] = new ArrayType(1, SimpleType.BYTE); // For message content - _msgContentType = new CompositeType("Message Content", "AMQ Message Content", _msgContentAttributes, - _msgContentAttributes, _msgContentAttributeTypes); - - _msgAttributeTypes[0] = SimpleType.LONG; // For message id - _msgAttributeTypes[1] = new ArrayType(1, SimpleType.STRING); // For header attributes - _msgAttributeTypes[2] = SimpleType.LONG; // For size - _msgAttributeTypes[3] = SimpleType.BOOLEAN; // For redelivered - - _messageDataType = new CompositeType("Message","AMQ Message", _msgAttributeNames, _msgAttributeNames, _msgAttributeTypes); - _messagelistDataType = new TabularType("Messages", "List of messages", _messageDataType, _msgAttributeIndex); - } - - public String getObjectInstanceName() - { - return _queueName; - } - - public String getName() - { - return _name; - } - - public boolean isDurable() - { - return _durable; - } - - public String getOwner() - { - return _owner; - } - - public boolean isAutoDelete() - { - return _autoDelete; - } - - public Integer getMessageCount() - { - return _deliveryMgr.getQueueMessageCount(); - } - - public Long getMaximumMessageSize() - { - return _maxMessageSize; - } - - public void setMaximumMessageSize(Long value) - { - _maxMessageSize = value; - } - - public Integer getConsumerCount() - { - return _subscribers.size(); - } - - public Integer getActiveConsumerCount() - { - return _subscribers.getWeight(); - } - - public Long getReceivedMessageCount() - { - return _totalMessagesReceived; - } - - public Integer getMaximumMessageCount() - { - return _maxMessageCount; - } - - public void setMaximumMessageCount(Integer value) - { - _maxMessageCount = value; - } - - public Long getMaximumQueueDepth() - { - return _maxQueueDepth; - } - - // Sets the queue depth, the max queue size - public void setMaximumQueueDepth(Long value) - { - _maxQueueDepth = value; - } - - /** - * returns the size of messages(KB) in the queue. - */ - public Long getQueueDepth() - { - /* TODO: this must return a maintained count not - * iterate through all messages - */ - return 0L; - /*List<AMQMessage> list = _deliveryMgr.getMessages(); - if (list.size() == 0) - { - return 0l; - } - - long queueDepth = 0; - for (AMQMessage message : list) - { - queueDepth = queueDepth + getMessageSize(message); - } - return (long)Math.round(queueDepth / 1000); - */ - } - // Operations - - /** - * returns size of message in bytes - */ - private long getMessageSize(AMQMessage msg) throws AMQException - { - if (msg == null) - { - return 0L; - } - - return msg.getContentHeaderBody().bodySize; - } - - /** - * Checks if there is any notification to be send to the listeners - */ - private void checkForNotification(AMQMessage msg) throws AMQException - { - // Check for threshold message count - Integer msgCount = getMessageCount(); - if (msgCount >= getMaximumMessageCount()) - { - notifyClients("Message count(" + msgCount + ") has reached or exceeded the threshold high value"); - } - - // Check for threshold message size - long messageSize = getMessageSize(msg); - if (messageSize >= _maxMessageSize) - { - notifyClients("Message size(ID=" + msg.getMessageId() + ", size=" + messageSize + " bytes) is higher than the threshold value"); - } - - // Check for threshold queue depth in bytes - long queueDepth = getQueueDepth(); - if (queueDepth >= _maxQueueDepth) - { - notifyClients("Queue depth(" + queueDepth + "), Queue size has reached the threshold high value"); - } - } - - /** - * Sends the notification to the listeners - */ - private void notifyClients(String notificationMsg) - { - Notification n = new Notification(MonitorNotification.THRESHOLD_VALUE_EXCEEDED, this, - ++_notificationSequenceNumber, System.currentTimeMillis(), notificationMsg); - - _broadcaster.sendNotification(n); - } - - public void deleteMessageFromTop() throws JMException - { - try - { - _deliveryMgr.removeAMessageFromTop(); - } - catch (AMQException ex) - { - throw new MBeanException(ex, ex.toString()); - } - } - - public void clearQueue() throws JMException - { - try - { - _deliveryMgr.clearAllMessages(); - } - catch (AMQException ex) - { - throw new MBeanException(ex, ex.toString()); - } - } - - /** - * returns message content as byte array and related attributes for the given message id. - */ - public CompositeData viewMessageContent(long msgId) throws JMException - { - List<AMQMessage> list = _deliveryMgr.getMessages(); - AMQMessage msg = null; - for (AMQMessage message : list) - { - if (message.getMessageId() == msgId) - { - msg = message; - break; - } - } - - if (msg != null) - { - try - { - // get message content - Iterator<ContentBody> cBodies = msg.getContentBodyIterator(); - List<Byte> msgContent = new ArrayList<Byte>(); - while (cBodies.hasNext()) - { - ContentBody body = cBodies.next(); - if (body.getSize() != 0) - { - ByteBuffer slice = body.payload.slice(); - for (int j = 0; j < slice.limit(); j++) - { - msgContent.add(slice.get()); - } - } - } - - // Create header attributes list - BasicContentHeaderProperties headerProperties = (BasicContentHeaderProperties)msg.getContentHeaderBody().properties; - String mimeType = headerProperties.getContentType(); - String encoding = headerProperties.getEncoding() == null ? "" : headerProperties.getEncoding(); - Object[] itemValues = {msgId, mimeType, encoding, msgContent.toArray(new Byte[0])}; - - return new CompositeDataSupport(_msgContentType, _msgContentAttributes, itemValues); - } - catch (AMQException e) - { - throw new JMException(e.getMessage()); - } - } - else - { - throw new OperationsException("AMQMessage with message id = " + msgId + " is not in the " + _queueName); - } - } - - /** - * Returns the header contents of the messages stored in this queue in tabular form. - */ - public TabularData viewMessages(int beginIndex, int endIndex) throws JMException, AMQException - { - if ((beginIndex > endIndex) || (beginIndex < 1)) - { - throw new JMException("From Index = " + beginIndex + ", To Index = " + endIndex + - "\nFrom Index should be greater than 0 and less than To Index"); - } - - List<AMQMessage> list = _deliveryMgr.getMessages(); - TabularDataSupport _messageList = new TabularDataSupport(_messagelistDataType); - - // Create the tabular list of message header contents - for (int i = beginIndex; i <= endIndex && i <= list.size(); i++) - { - AMQMessage msg = list.get(i - 1); - ContentHeaderBody headerBody = msg.getContentHeaderBody(); - long size = headerBody.bodySize; - // Create header attributes list - BasicContentHeaderProperties headerProperties = (BasicContentHeaderProperties)headerBody.properties; - List<String> headerAttribsList = new ArrayList<String>(); - headerAttribsList.add("App Id=" + headerProperties.getAppId()); - headerAttribsList.add("MimeType=" + headerProperties.getContentType()); - headerAttribsList.add("Correlation Id=" + headerProperties.getCorrelationId()); - headerAttribsList.add("Encoding=" + headerProperties.getEncoding()); - headerAttribsList.add(headerProperties.toString()); - - Object[] itemValues = {msg.getMessageId(), headerAttribsList.toArray(new String[0]), - headerBody.bodySize, msg.isRedelivered()}; - CompositeData messageData = new CompositeDataSupport(_messageDataType, _msgAttributeNames, itemValues); - _messageList.put(messageData); - } - - return _messageList; - } - - /** - * returns Notifications sent by this MBean. - */ - @Override - public MBeanNotificationInfo[] getNotificationInfo() - { - String[] notificationTypes = new String[] {MonitorNotification.THRESHOLD_VALUE_EXCEEDED}; - String name = MonitorNotification.class.getName(); - String description = "Either Message count or Queue depth or Message size has reached threshold high value"; - MBeanNotificationInfo info1 = new MBeanNotificationInfo(notificationTypes, name, description); - - return new MBeanNotificationInfo[]{info1}; - } - - } // End of AMQMBean class - public AMQQueue(String name, boolean durable, String owner, boolean autoDelete, QueueRegistry queueRegistry) throws AMQException @@ -543,7 +205,7 @@ public class AMQQueue implements Managable, Comparable { try { - return new AMQQueueMBean(); + return new AMQQueueMBean(this); } catch (JMException ex) { @@ -576,16 +238,112 @@ public class AMQQueue implements Managable, Comparable return _autoDelete; } + /** + * @return no of messages(undelivered) on the queue. + */ public int getMessageCount() { return _deliveryMgr.getQueueMessageCount(); } + /** + * @return List of messages(undelivered) on the queue. + */ + public List<AMQMessage> getMessagesOnTheQueue() + { + return _deliveryMgr.getMessages(); + } + + /** + * @param messageId + * @return AMQMessage with give id if exists. null if AMQMessage with given id doesn't exist. + */ + public AMQMessage getMessageOnTheQueue(long messageId) + { + List<AMQMessage> list = getMessagesOnTheQueue(); + AMQMessage msg = null; + for (AMQMessage message : list) + { + if (message.getMessageId() == messageId) + { + msg = message; + break; + } + } + + return msg; + } + + /** + * @return MBean object associated with this Queue + */ public ManagedObject getManagedObject() { return _managedObject; } + public Long getMaximumMessageSize() + { + return _maxMessageSize; + } + + public void setMaximumMessageSize(Long value) + { + _maxMessageSize = value; + } + + public Integer getConsumerCount() + { + return _subscribers.size(); + } + + public Integer getActiveConsumerCount() + { + return _subscribers.getWeight(); + } + + public Long getReceivedMessageCount() + { + return _totalMessagesReceived; + } + + public Integer getMaximumMessageCount() + { + return _maxMessageCount; + } + + public void setMaximumMessageCount(Integer value) + { + _maxMessageCount = value; + } + + public Long getMaximumQueueDepth() + { + return _maxQueueDepth; + } + + // Sets the queue depth, the max queue size + public void setMaximumQueueDepth(Long value) + { + _maxQueueDepth = value; + } + + /** + * Removes the AMQMessage from the top of the queue. + */ + public void deleteMessageFromTop() throws AMQException + { + _deliveryMgr.removeAMessageFromTop(); + } + + /** + * removes all the messages from the queue. + */ + public void clearQueue() throws AMQException + { + _deliveryMgr.clearAllMessages(); + } + public void bind(String routingKey, Exchange exchange) { _bindings.addBinding(routingKey, exchange); @@ -729,7 +487,14 @@ public class AMQQueue implements Managable, Comparable protected void updateReceivedMessageCount(AMQMessage msg) throws AMQException { _totalMessagesReceived++; - _managedObject.checkForNotification(msg); + try + { + _managedObject.checkForNotification(msg); + } + catch (JMException e) + { + throw new AMQException("Unable to get notification from manage queue: " + e, e); + } } public boolean equals(Object o) diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java new file mode 100644 index 0000000000..e09b177fb9 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java @@ -0,0 +1,366 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.qpid.server.management.MBeanDescription; +import org.apache.qpid.server.management.AMQManagedObject; +import org.apache.qpid.server.management.MBeanConstructor; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.mina.common.ByteBuffer; + +import javax.management.openmbean.*; +import javax.management.JMException; +import javax.management.Notification; +import javax.management.MBeanException; +import javax.management.MBeanNotificationInfo; +import javax.management.OperationsException; +import javax.management.monitor.MonitorNotification; +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; + +/** + * MBean class for AMQQueue. It implements all the management features exposed + * for an AMQQueue. + */ +@MBeanDescription("Management Interface for AMQQueue") +public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue +{ + private AMQQueue _queue = null; + private String _queueName = null; + // OpenMBean data types for viewMessages method + private String[] _msgAttributeNames = {"Message Id", "Header", "Size(bytes)", "Redelivered"}; + private String[] _msgAttributeIndex = {_msgAttributeNames[0]}; + private OpenType[] _msgAttributeTypes = new OpenType[4]; // AMQ message attribute types. + private CompositeType _messageDataType = null; // Composite type for representing AMQ Message data. + private TabularType _messagelistDataType = null; // Datatype for representing AMQ messages list. + + // OpenMBean data types for viewMessageContent method + private CompositeType _msgContentType = null; + private String[] _msgContentAttributes = {"Message Id", "MimeType", "Encoding", "Content"}; + private OpenType[] _msgContentAttributeTypes = new OpenType[4]; + + @MBeanConstructor("Creates an MBean exposing an AMQQueue") + public AMQQueueMBean(AMQQueue queue) throws JMException + { + super(ManagedQueue.class, ManagedQueue.TYPE); + _queue = queue; + _queueName = jmxEncode(new StringBuffer(queue.getName()), 0).toString(); + init(); + } + + /** + * initialises the openmbean data types + */ + private void init() throws OpenDataException + { + _msgContentAttributeTypes[0] = SimpleType.LONG; // For message id + _msgContentAttributeTypes[1] = SimpleType.STRING; // For MimeType + _msgContentAttributeTypes[2] = SimpleType.STRING; // For Encoding + _msgContentAttributeTypes[3] = new ArrayType(1, SimpleType.BYTE); // For message content + _msgContentType = new CompositeType("Message Content", "AMQ Message Content", _msgContentAttributes, + _msgContentAttributes, _msgContentAttributeTypes); + + _msgAttributeTypes[0] = SimpleType.LONG; // For message id + _msgAttributeTypes[1] = new ArrayType(1, SimpleType.STRING); // For header attributes + _msgAttributeTypes[2] = SimpleType.LONG; // For size + _msgAttributeTypes[3] = SimpleType.BOOLEAN; // For redelivered + + _messageDataType = new CompositeType("Message", "AMQ Message", _msgAttributeNames, _msgAttributeNames, _msgAttributeTypes); + _messagelistDataType = new TabularType("Messages", "List of messages", _messageDataType, _msgAttributeIndex); + } + + public String getObjectInstanceName() + { + return _queueName; + } + + public String getName() + { + return _queueName; + } + + public boolean isDurable() + { + return _queue.isDurable(); + } + + public String getOwner() + { + return _queue.getOwner(); + } + + public boolean isAutoDelete() + { + return _queue.isAutoDelete(); + } + + public Integer getMessageCount() + { + return _queue.getMessageCount(); + } + + public Long getMaximumMessageSize() + { + return _queue.getMaximumMessageSize(); + } + + public void setMaximumMessageSize(Long value) + { + _queue.setMaximumMessageSize(value); + } + + public Integer getConsumerCount() + { + return _queue.getConsumerCount(); + } + + public Integer getActiveConsumerCount() + { + return _queue.getActiveConsumerCount(); + } + + public Long getReceivedMessageCount() + { + return _queue.getReceivedMessageCount(); + } + + public Integer getMaximumMessageCount() + { + return _queue.getMaximumMessageCount(); + } + + public void setMaximumMessageCount(Integer value) + { + _queue.setMaximumMessageCount(value); + } + + public Long getMaximumQueueDepth() + { + return _queue.getMaximumQueueDepth(); + } + + public void setMaximumQueueDepth(Long value) + { + _queue.setMaximumQueueDepth(value); + } + + /** + * returns the size of messages(KB) in the queue. + */ + public Long getQueueDepth() throws JMException + { + List<AMQMessage> list = _queue.getMessagesOnTheQueue(); + if (list.size() == 0) + { + return 0l; + } + + long queueDepth = 0; + try + { + for (AMQMessage message : list) + { + queueDepth = queueDepth + getMessageSize(message); + } + } + catch (AMQException e) + { + throw new JMException("Unable to get message size: " + e); + } + return (long) Math.round(queueDepth / 1000); + } + + /** + * returns size of message in bytes + */ + private long getMessageSize(AMQMessage msg) throws AMQException + { + if (msg == null) + { + return 0l; + } + + return msg.getContentHeaderBody().bodySize; + } + + /** + * Checks if there is any notification to be send to the listeners + */ + public void checkForNotification(AMQMessage msg) throws AMQException, JMException + { + // Check for threshold message count + Integer msgCount = getMessageCount(); + if (msgCount >= getMaximumMessageCount()) + { + notifyClients("Message count(" + msgCount + ") has reached or exceeded the threshold high value"); + } + + // Check for threshold message size + long messageSize = getMessageSize(msg); + if (messageSize >= _queue.getMaximumMessageSize()) + { + notifyClients("Message size(ID=" + msg.getMessageId() + ", size=" + messageSize + " bytes) is higher than the threshold value"); + } + + // Check for threshold queue depth in bytes + long queueDepth = getQueueDepth(); + if (queueDepth >= _queue.getMaximumQueueDepth()) + { + notifyClients("Queue depth(" + queueDepth + "), Queue size has reached the threshold high value"); + } + } + + /** + * Sends the notification to the listeners + */ + private void notifyClients(String notificationMsg) + { + Notification n = new Notification(MonitorNotification.THRESHOLD_VALUE_EXCEEDED, this, + ++_notificationSequenceNumber, System.currentTimeMillis(), notificationMsg); + + _broadcaster.sendNotification(n); + } + + /** + * @see org.apache.qpid.server.queue.AMQQueue#deleteMessageFromTop() + */ + public void deleteMessageFromTop() throws JMException + { + try + { + _queue.deleteMessageFromTop(); + } + catch (AMQException ex) + { + throw new MBeanException(ex, ex.toString()); + } + } + + /** + * @see org.apache.qpid.server.queue.AMQQueue#clearQueue() + */ + public void clearQueue() throws JMException + { + try + { + _queue.clearQueue(); + } + catch (AMQException ex) + { + throw new MBeanException(ex, ex.toString()); + } + } + + /** + * returns message content as byte array and related attributes for the given message id. + */ + public CompositeData viewMessageContent(long msgId) throws JMException + { + AMQMessage msg = _queue.getMessageOnTheQueue(msgId); + if (msg == null) + { + throw new OperationsException("AMQMessage with message id = " + msgId + " is not in the " + _queueName); + } + // get message content + Iterator<ContentBody> cBodies = msg.getContentBodyIterator(); + List<Byte> msgContent = new ArrayList<Byte>(); + while (cBodies.hasNext()) + { + ContentBody body = cBodies.next(); + if (body.getSize() != 0) + { + ByteBuffer slice = body.payload.slice(); + for (int j = 0; j < slice.limit(); j++) + { + msgContent.add(slice.get()); + } + } + } + + try + { + // Create header attributes list + BasicContentHeaderProperties headerProperties = (BasicContentHeaderProperties) msg.getContentHeaderBody().properties; + String mimeType = headerProperties.getContentType(); + String encoding = headerProperties.getEncoding() == null ? "" : headerProperties.getEncoding(); + Object[] itemValues = {msgId, mimeType, encoding, msgContent.toArray(new Byte[0])}; + + return new CompositeDataSupport(_msgContentType, _msgContentAttributes, itemValues); + } + catch (AMQException e) + { + throw new JMException("Error create header attributes list: " + e); + } + } + + /** + * Returns the header contents of the messages stored in this queue in tabular form. + */ + public TabularData viewMessages(int beginIndex, int endIndex) throws JMException + { + if ((beginIndex > endIndex) || (beginIndex < 1)) + { + throw new OperationsException("From Index = " + beginIndex + ", To Index = " + endIndex + + "\n\"From Index\" should be greater than 0 and less than \"To Index\""); + } + + List<AMQMessage> list = _queue.getMessagesOnTheQueue(); + TabularDataSupport _messageList = new TabularDataSupport(_messagelistDataType); + + try + { + // Create the tabular list of message header contents + for (int i = beginIndex; i <= endIndex && i <= list.size(); i++) + { + AMQMessage msg = list.get(i - 1); + ContentHeaderBody headerBody = msg.getContentHeaderBody(); + // Create header attributes list + BasicContentHeaderProperties headerProperties = (BasicContentHeaderProperties) headerBody.properties; + String[] headerAttributes = headerProperties.toString().split(","); + Object[] itemValues = {msg.getMessageId(), headerAttributes, headerBody.bodySize, msg.isRedelivered()}; + CompositeData messageData = new CompositeDataSupport(_messageDataType, _msgAttributeNames, itemValues); + _messageList.put(messageData); + } + } + catch (AMQException e) + { + throw new JMException("Error creating message contents: " + e); + } + + return _messageList; + } + + /** + * returns Notifications sent by this MBean. + */ + @Override + public MBeanNotificationInfo[] getNotificationInfo() + { + String[] notificationTypes = new String[]{MonitorNotification.THRESHOLD_VALUE_EXCEEDED}; + String name = MonitorNotification.class.getName(); + String description = "Either Message count or Queue depth or Message size has reached threshold high value"; + MBeanNotificationInfo info1 = new MBeanNotificationInfo(notificationTypes, name, description); + + return new MBeanNotificationInfo[]{info1}; + } + +} // End of AMQMBean class diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java index 8925ed9450..c36cc6bd7b 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java @@ -71,7 +71,7 @@ public interface ManagedQueue * @throws IOException */ @MBeanAttribute(name="QueueDepth", description="Size of messages(KB) in the queue") - Long getQueueDepth() throws IOException; + Long getQueueDepth() throws IOException, JMException; /** * Returns the total number of active subscribers to the queue. diff --git a/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java b/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java index 5e88ff7f2d..4e9deeb8db 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java @@ -7,9 +7,9 @@ * 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 @@ -109,6 +109,7 @@ public class AMQStateManager implements AMQMethodListener frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance()); frame2handlerMap.put(ExchangeDeclareBody.class, ExchangeDeclareHandler.getInstance()); frame2handlerMap.put(ExchangeDeleteBody.class, ExchangeDeleteHandler.getInstance()); + frame2handlerMap.put(ExchangeBoundBody.class, ExchangeBoundHandler.getInstance()); frame2handlerMap.put(BasicAckBody.class, BasicAckMethodHandler.getInstance()); frame2handlerMap.put(BasicRecoverBody.class, BasicRecoverMethodHandler.getInstance()); frame2handlerMap.put(BasicConsumeBody.class, BasicConsumeMethodHandler.getInstance()); diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java b/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java index 5c13e7861f..c6f3f9c492 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java @@ -7,9 +7,9 @@ * 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 @@ -79,13 +79,19 @@ public abstract class AMQDestination implements Destination, Referenceable protected AMQDestination(String exchangeName, String exchangeClass, String destinationName, boolean isExclusive, boolean isAutoDelete, String queueName) { + this(exchangeName, exchangeClass, destinationName, isExclusive, isAutoDelete, queueName, false); + } + + protected AMQDestination(String exchangeName, String exchangeClass, String destinationName, boolean isExclusive, + boolean isAutoDelete, String queueName, boolean isDurable) + { if (destinationName == null) { - throw new IllegalArgumentException("Destination name must not be null"); + throw new IllegalArgumentException("Destination exchange must not be null"); } if (exchangeName == null) { - throw new IllegalArgumentException("Exchange name must not be null"); + throw new IllegalArgumentException("Exchange exchange must not be null"); } if (exchangeClass == null) { @@ -97,6 +103,7 @@ public abstract class AMQDestination implements Destination, Referenceable _isExclusive = isExclusive; _isAutoDelete = isAutoDelete; _queueName = queueName; + _isDurable = isDurable; } public String getEncodedName() diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQSession.java b/java/client/src/main/java/org/apache/qpid/client/AMQSession.java index 8e93b19eea..21fc5c89c5 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQSession.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQSession.java @@ -23,13 +23,15 @@ package org.apache.qpid.client; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.AMQUndeliveredException; +import org.apache.qpid.server.handler.ExchangeBoundHandler; +import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.client.failover.FailoverSupport; import org.apache.qpid.client.message.AbstractJMSMessage; import org.apache.qpid.client.message.JMSStreamMessage; import org.apache.qpid.client.message.MessageFactoryRegistry; import org.apache.qpid.client.message.UnprocessedMessage; -import org.apache.qpid.client.protocol.AMQMethodEvent; import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.client.protocol.AMQMethodEvent; import org.apache.qpid.client.util.FlowControllingBlockingQueue; import org.apache.qpid.framing.*; import org.apache.qpid.jms.Session; @@ -37,6 +39,7 @@ import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.url.AMQBindingURL; import org.apache.qpid.url.URLSyntaxException; + import javax.jms.*; import javax.jms.IllegalStateException; import java.io.Serializable; @@ -46,6 +49,8 @@ import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; public class AMQSession extends Closeable implements Session, QueueSession, TopicSession { @@ -66,6 +71,17 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi private int _defaultPrefetchLowMark = DEFAULT_PREFETCH_LOW_MARK; /** + * Used to reference durable subscribers so they requests for unsubscribe can be handled + * correctly. Note this only keeps a record of subscriptions which have been created + * in the current instance. It does not remember subscriptions between executions of the + * client + */ + private final ConcurrentHashMap<String, TopicSubscriberAdaptor> _subscriptions = + new ConcurrentHashMap<String, TopicSubscriberAdaptor>(); + private final ConcurrentHashMap<BasicMessageConsumer, String> _reverseSubscriptionMap = + new ConcurrentHashMap<BasicMessageConsumer, String>(); + + /** * Used in the consume method. We generate the consume tag on the client so that we can use the nowait * feature. */ @@ -91,6 +107,12 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi private Map _consumers = new ConcurrentHashMap(); /** + * Maps from destination to count of JMSMessageConsumers + */ + private ConcurrentHashMap<Destination, AtomicInteger> _destinationConsumerCount = + new ConcurrentHashMap<Destination, AtomicInteger>(); + + /** * Default value for immediate flag used by producers created by this session is false, i.e. a consumer does not * need to be attached to a queue */ @@ -116,6 +138,9 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi */ private volatile AtomicBoolean _stopped = new AtomicBoolean(true); + private final AtomicLong _lastDeliveryTag = new AtomicLong(); + + /** * Responsible for decoding a message fragment and passing it to the appropriate message consumer. */ @@ -154,10 +179,14 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi if (consumer == null) { _logger.warn("Received a message from queue " + message.deliverBody.consumerTag + " without a handler - ignoring..."); + _logger.warn("Consumers that exist: " + _consumers); + _logger.warn("Session hashcode: " + System.identityHashCode(this)); } else { + consumer.notifyMessage(message, _channelId); + } } else @@ -673,6 +702,27 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi _connection.getProtocolHandler().writeFrame(BasicRecoverBody.createAMQFrame(_channelId, false)); } + + public void acknowledge() throws JMSException + { + if (getAMQConnection().isClosed()) + { + throw new javax.jms.IllegalStateException("Connection is already closed"); + } + if (isClosed()) + { + throw new javax.jms.IllegalStateException("Session is already closed"); + } + acknowledgeMessage(_lastDeliveryTag.get(), true); + + } + + void setLastDeliveredMessage(AbstractJMSMessage message) + { + _lastDeliveryTag.set(message.getDeliveryTag()); + } + + public MessageListener getMessageListener() throws JMSException { checkNotClosed(); @@ -775,20 +825,38 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi public MessageConsumer createConsumer(Destination destination) throws JMSException { checkValidDestination(destination); - return createConsumer(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, false, null); + return createConsumerImpl(destination, + _defaultPrefetchHighMark, + _defaultPrefetchLowMark, + false, + false, + null, + null); } public MessageConsumer createConsumer(Destination destination, String messageSelector) throws JMSException { checkValidDestination(destination); - return createConsumer(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, false, messageSelector); + return createConsumerImpl(destination, + _defaultPrefetchHighMark, + _defaultPrefetchLowMark, + false, + false, + messageSelector, + null); } public MessageConsumer createConsumer(Destination destination, String messageSelector, boolean noLocal) throws JMSException { checkValidDestination(destination); - return createConsumer(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, noLocal, false, messageSelector); + return createConsumerImpl(destination, + _defaultPrefetchHighMark, + _defaultPrefetchLowMark, + noLocal, + false, + messageSelector, + null); } public MessageConsumer createConsumer(Destination destination, @@ -798,7 +866,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi String selector) throws JMSException { checkValidDestination(destination); - return createConsumer(destination, prefetch, prefetch, noLocal, exclusive, selector, null); + return createConsumerImpl(destination, prefetch, prefetch, noLocal, exclusive, selector, null); } @@ -810,7 +878,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi String selector) throws JMSException { checkValidDestination(destination); - return createConsumer(destination, prefetchHigh, prefetchLow, noLocal, exclusive, selector, null); + return createConsumerImpl(destination, prefetchHigh, prefetchLow, noLocal, exclusive, selector, null); } public MessageConsumer createConsumer(Destination destination, @@ -846,6 +914,8 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi final String selector, final FieldTable rawSelector) throws JMSException { + checkTemporaryDestination(destination); + return (org.apache.qpid.jms.MessageConsumer) new FailoverSupport() { public Object operation() throws JMSException @@ -870,7 +940,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi try { - registerConsumer(consumer); + registerConsumer(consumer, false); } catch (AMQException e) { @@ -879,11 +949,46 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi throw ex; } + synchronized(destination) + { + _destinationConsumerCount.putIfAbsent(destination,new AtomicInteger()); + _destinationConsumerCount.get(destination).incrementAndGet(); + } + return consumer; } }.execute(_connection); } + private void checkTemporaryDestination(Destination destination) + throws JMSException + { + if((destination instanceof TemporaryDestination)) + { + _logger.debug("destination is temporary"); + final TemporaryDestination tempDest = (TemporaryDestination) destination; + if(tempDest.getSession() != this) + { + _logger.debug("destination is on different session"); + throw new JMSException("Cannot consume from a temporary destination created onanother session"); + } + if(tempDest.isDeleted()) + { + _logger.debug("destination is deleted"); + throw new JMSException("Cannot consume from a deleted destination"); + } + } + } + + + public boolean hasConsumer(Destination destination) + { + AtomicInteger counter = _destinationConsumerCount.get(destination); + + return (counter != null) && (counter.get() != 0); + } + + public void declareExchange(String name, String type) { declareExchange(name, type, _connection.getProtocolHandler()); @@ -947,20 +1052,38 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi * @param queueName * @return the consumer tag generated by the broker */ - private String consumeFromQueue(String queueName, AMQProtocolHandler protocolHandler, int prefetchHigh, int prefetchLow, - boolean noLocal, boolean exclusive, int acknowledgeMode) throws AMQException + private void consumeFromQueue(BasicMessageConsumer consumer, String queueName, AMQProtocolHandler protocolHandler, + boolean nowait) throws AMQException { //fixme prefetch values are not used here. Do we need to have them as parametsrs? //need to generate a consumer tag on the client so we can exploit the nowait flag String tag = Integer.toString(_nextTag++); - AMQFrame jmsConsume = BasicConsumeBody.createAMQFrame(_channelId, 0, - queueName, tag, noLocal, - acknowledgeMode == Session.NO_ACKNOWLEDGE, - exclusive, true); + consumer.setConsumerTag(tag); + // we must register the consumer in the map before we actually start listening + _consumers.put(tag, consumer); - protocolHandler.writeFrame(jmsConsume); - return tag; + try + { + AMQFrame jmsConsume = BasicConsumeBody.createAMQFrame(_channelId, 0, + queueName, tag, consumer.isNoLocal(), + consumer.getAcknowledgeMode() == Session.NO_ACKNOWLEDGE, + consumer.isExclusive(), nowait, null); + if (nowait) + { + protocolHandler.writeFrame(jmsConsume); + } + else + { + protocolHandler.syncWrite(jmsConsume, BasicConsumeOkBody.class); + } + } + catch (AMQException e) + { + // clean-up the map in the event of an error + _consumers.remove(tag); + throw e; + } } public Queue createQueue(String queueName) throws JMSException @@ -1081,19 +1204,55 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi return new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createConsumer(dest, messageSelector, noLocal)); } - /** - * Note, currently this does not handle reuse of the same name with different topics correctly. - * If a name is reused in creating a new subscriber with a different topic/selecto or no-local - * flag then the subcriber will receive messages matching the old subscription AND the new one. - * The spec states that the new one should replace the old one. - * TODO: fix it. - */ public TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException { checkNotClosed(); checkValidTopic(topic); - AMQTopic dest = new AMQTopic((AMQTopic) topic, _connection.getClientID(), name); - return new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createConsumer(dest)); + AMQTopic dest = AMQTopic.createDurableTopic((AMQTopic)topic, name, _connection); + TopicSubscriberAdaptor subscriber = _subscriptions.get(name); + if (subscriber != null) + { + if (subscriber.getTopic().equals(topic)) + { + throw new IllegalStateException("Already subscribed to topic " + topic + " with subscription exchange " + + name); + } + else + { + unsubscribe(name); + } + } + else + { + // if the queue is bound to the exchange but NOT for this topic, then the JMS spec + // says we must trash the subscription. + if (isQueueBound(dest.getQueueName()) && + !isQueueBound(dest.getQueueName(), topic.getTopicName())) + { + deleteQueue(dest.getQueueName()); + } + } + + subscriber = new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createConsumer(dest)); + + _subscriptions.put(name,subscriber); + _reverseSubscriptionMap.put(subscriber.getMessageConsumer(),name); + + return subscriber; + } + + void deleteQueue(String queueName) throws JMSException + { + try + { + AMQFrame queueDeleteFrame = QueueDeleteBody.createAMQFrame(_channelId, 0, queueName, false, + false, true); + _connection.getProtocolHandler().syncWrite(queueDeleteFrame, QueueDeleteOkBody.class); + } + catch (AMQException e) + { + throw new JMSAMQException(e); + } } /** @@ -1104,9 +1263,12 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi { checkNotClosed(); checkValidTopic(topic); - AMQTopic dest = new AMQTopic((AMQTopic) topic, _connection.getClientID(), name); + AMQTopic dest = AMQTopic.createDurableTopic((AMQTopic) topic, name, _connection); BasicMessageConsumer consumer = (BasicMessageConsumer) createConsumer(dest, messageSelector, noLocal); - return new TopicSubscriberAdaptor(dest, consumer); + TopicSubscriberAdaptor subscriber = new TopicSubscriberAdaptor(dest, consumer); + _subscriptions.put(name,subscriber); + _reverseSubscriptionMap.put(subscriber.getMessageConsumer(),name); + return subscriber; } public TopicPublisher createPublisher(Topic topic) throws JMSException @@ -1132,32 +1294,59 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi public TemporaryQueue createTemporaryQueue() throws JMSException { checkNotClosed(); - return new AMQTemporaryQueue(); + return new AMQTemporaryQueue(this); } public TemporaryTopic createTemporaryTopic() throws JMSException { checkNotClosed(); - return new AMQTemporaryTopic(); + return new AMQTemporaryTopic(this); } public void unsubscribe(String name) throws JMSException { checkNotClosed(); - - String queue = _connection.getClientID() + ":" + name; - - AMQFrame queueDeclareFrame = QueueDeclareBody.createAMQFrame(_channelId,0,queue,true,false, false, false, true, null); - - try { - AMQMethodEvent event = _connection.getProtocolHandler().syncWrite(queueDeclareFrame,QueueDeclareOkBody.class); - // if this method doen't throw an exception means we have received a queue declare ok. - } catch (AMQException e) { - throw new javax.jms.InvalidDestinationException("This destination doesn't exist"); - } - //send a queue.delete for the subscription - AMQFrame frame = QueueDeleteBody.createAMQFrame(_channelId, 0, queue, false, false, true); - _connection.getProtocolHandler().writeFrame(frame); + TopicSubscriberAdaptor subscriber = _subscriptions.get(name); + if (subscriber != null) + { + // send a queue.delete for the subscription + deleteQueue(AMQTopic.getDurableTopicQueueName(name, _connection)); + _subscriptions.remove(name); + _reverseSubscriptionMap.remove(subscriber); + } + else + { + if (isQueueBound(AMQTopic.getDurableTopicQueueName(name, _connection))) + { + deleteQueue(AMQTopic.getDurableTopicQueueName(name, _connection)); + } + else + { + throw new InvalidDestinationException("Unknown subscription exchange:" + name); + } + } + } + + boolean isQueueBound(String queueName) throws JMSException + { + return isQueueBound(queueName, null); + } + + boolean isQueueBound(String queueName, String routingKey) throws JMSException + { + AMQFrame boundFrame = ExchangeBoundBody.createAMQFrame(_channelId, ExchangeDefaults.TOPIC_EXCHANGE_NAME, + routingKey, queueName); + AMQMethodEvent response = null; + try + { + response = _connection.getProtocolHandler().syncWrite(boundFrame, ExchangeBoundOkBody.class); + } + catch (AMQException e) + { + throw new JMSAMQException(e); + } + ExchangeBoundOkBody responseBody = (ExchangeBoundOkBody) response.getMethod(); + return (responseBody.replyCode == ExchangeBoundHandler.OK); } private void checkTransacted() throws JMSException @@ -1263,7 +1452,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi * @param consumer * @throws AMQException */ - void registerConsumer(BasicMessageConsumer consumer) throws AMQException + void registerConsumer(BasicMessageConsumer consumer, boolean nowait) throws AMQException { AMQDestination amqd = consumer.getDestination(); @@ -1275,22 +1464,32 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi bindQueue(amqd, queueName, protocolHandler, consumer.getRawSelectorFieldTable()); - String consumerTag = consumeFromQueue(queueName, protocolHandler, consumer.getPrefetchHigh(), consumer.getPrefetchLow(), - consumer.isNoLocal(), consumer.isExclusive(), consumer.getAcknowledgeMode()); - - consumer.setConsumerTag(consumerTag); - _consumers.put(consumerTag, consumer); + consumeFromQueue(consumer, queueName, protocolHandler, nowait); } /** * Called by the MessageConsumer when closing, to deregister the consumer from the * map from consumerTag to consumer instance. * - * @param consumerTag the consumer tag, that was broker-generated + * @param consumer the consum */ - void deregisterConsumer(String consumerTag) + void deregisterConsumer(BasicMessageConsumer consumer) { - _consumers.remove(consumerTag); + _consumers.remove(consumer.getConsumerTag()); + String subscriptionName = _reverseSubscriptionMap.remove(consumer); + if(subscriptionName != null) + { + _subscriptions.remove(subscriptionName); + } + + Destination dest = consumer.getDestination(); + synchronized(dest) + { + if(_destinationConsumerCount.get(dest).decrementAndGet() == 0) + { + _destinationConsumerCount.remove(dest); + } + } } private void registerProducer(long producerId, MessageProducer producer) @@ -1338,7 +1537,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi for (Iterator it = consumers.iterator(); it.hasNext();) { BasicMessageConsumer consumer = (BasicMessageConsumer) it.next(); - registerConsumer(consumer); + registerConsumer(consumer, true); } } @@ -1359,12 +1558,16 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi /* * I could have combined the last 3 methods, but this way it improves readability */ - private void checkValidTopic(Topic topic) throws InvalidDestinationException + private void checkValidTopic(Topic topic) throws JMSException { if (topic == null) { throw new javax.jms.InvalidDestinationException("Invalid Topic"); } + if((topic instanceof TemporaryDestination) && ((TemporaryDestination)topic).getSession() != this) + { + throw new JMSException("Cannot create a subscription on a temporary topic created in another session"); + } } private void checkValidQueue(Queue queue) throws InvalidDestinationException diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryQueue.java b/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryQueue.java index 6b41ea0112..81fee69f90 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryQueue.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryQueue.java @@ -26,25 +26,45 @@ import javax.jms.TemporaryQueue; /** * AMQ implementation of a TemporaryQueue. */ -final class AMQTemporaryQueue extends AMQQueue implements TemporaryQueue +final class AMQTemporaryQueue extends AMQQueue implements TemporaryQueue, TemporaryDestination { + private final AMQSession _session; + private boolean _deleted; + /** * Create a new instance of an AMQTemporaryQueue */ - public AMQTemporaryQueue() + public AMQTemporaryQueue(AMQSession session) { super("TempQueue" + Long.toString(System.currentTimeMillis()), true); + _session = session; } /** * @see javax.jms.TemporaryQueue#delete() */ - public void delete() throws JMSException + public synchronized void delete() throws JMSException { - throw new UnsupportedOperationException("Delete not supported, " + - "will auto-delete when connection closed"); + if(_session.hasConsumer(this)) + { + throw new JMSException("Temporary Queue has consumers so cannot be deleted"); + } + _deleted = true; + + // Currently TemporaryQueue is set to be auto-delete which means that the queue will be deleted + // by the server when there are no more subscriptions to that queue. This is probably not + // quite right for JMSCompliance. } + public AMQSession getSession() + { + return _session; + } + + public boolean isDeleted() + { + return _deleted; + } } diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java b/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java index 0ba5cb3c3a..241a9abc9b 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java @@ -26,15 +26,18 @@ import javax.jms.TemporaryTopic; /** * AMQ implementation of TemporaryTopic. */ -class AMQTemporaryTopic extends AMQTopic implements TemporaryTopic +class AMQTemporaryTopic extends AMQTopic implements TemporaryTopic, TemporaryDestination { + private final AMQSession _session; + private boolean _deleted; /** * Create new temporary topic. */ - public AMQTemporaryTopic() + public AMQTemporaryTopic(AMQSession session) { super("TempQueue" + Long.toString(System.currentTimeMillis())); + _session = session; } /** @@ -42,8 +45,25 @@ class AMQTemporaryTopic extends AMQTopic implements TemporaryTopic */ public void delete() throws JMSException { - throw new UnsupportedOperationException("Delete not supported, " + - "will auto-delete when connection closed"); + if(_session.hasConsumer(this)) + { + throw new JMSException("Temporary Topic has consumers so cannot be deleted"); + } + + _deleted = true; + // Currently TemporaryQueue is set to be auto-delete which means that the queue will be deleted + // by the server when there are no more subscriptions to that queue. This is probably not + // quite right for JMSCompliance. + } + + public AMQSession getSession() + { + return _session; + } + + public boolean isDeleted() + { + return _deleted; } } diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java b/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java index 4dd38eea18..39304f3f4c 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java @@ -7,9 +7,9 @@ * 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 @@ -40,20 +40,25 @@ public class AMQTopic extends AMQDestination implements Topic public AMQTopic(String name) { - super(ExchangeDefaults.TOPIC_EXCHANGE_NAME, ExchangeDefaults.TOPIC_EXCHANGE_CLASS, name, true, true, null); - _isDurable = false; + this(name, true, null, false); } - /** - * Constructor for use in creating a topic to represent a durable subscription - * @param topic - * @param clientId - * @param subscriptionName - */ - public AMQTopic(AMQTopic topic, String clientId, String subscriptionName) + public AMQTopic(String name, boolean isAutoDelete, String queueName, boolean isDurable) + { + super(ExchangeDefaults.TOPIC_EXCHANGE_NAME, ExchangeDefaults.TOPIC_EXCHANGE_CLASS, name, true, isAutoDelete, + queueName, isDurable); + } + + public static AMQTopic createDurableTopic(AMQTopic topic, String subscriptionName, AMQConnection connection) + throws JMSException + { + return new AMQTopic(topic.getDestinationName(), false, getDurableTopicQueueName(subscriptionName, connection), + true); + } + + public static String getDurableTopicQueueName(String subscriptionName, AMQConnection connection) throws JMSException { - super(ExchangeDefaults.TOPIC_EXCHANGE_NAME, ExchangeDefaults.TOPIC_EXCHANGE_CLASS, topic.getDestinationName(), true, false, clientId + ":" + subscriptionName); - _isDurable = true; + return connection.getClientID() + ":" + subscriptionName; } public String getTopicName() throws JMSException diff --git a/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java b/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java index 4fb62b49fc..9f9038fddd 100644 --- a/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java +++ b/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java @@ -7,9 +7,9 @@ * 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 @@ -214,10 +214,10 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer { //handle case where connection has already been started, and the dispatcher is blocked //doing a put on the _synchronousQueue - Object msg = _synchronousQueue.poll(); - if (msg != null) + AbstractJMSMessage jmsMsg = (AbstractJMSMessage)_synchronousQueue.poll(); + if (jmsMsg != null) { - AbstractJMSMessage jmsMsg = (AbstractJMSMessage) msg; + _session.setLastDeliveredMessage(jmsMsg); messageListener.onMessage(jmsMsg); postDeliver(jmsMsg); } @@ -280,7 +280,7 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer public Message receive(long l) throws JMSException { checkPreConditions(); - + acquireReceiving(); try @@ -297,12 +297,15 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer final AbstractJMSMessage m = returnMessageOrThrow(o); if (m != null) { + _session.setLastDeliveredMessage(m); postDeliver(m); } + return m; } catch (InterruptedException e) { + _logger.warn("Interrupted: " + e, e); return null; } finally @@ -323,8 +326,10 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer final AbstractJMSMessage m = returnMessageOrThrow(o); if (m != null) { + _session.setLastDeliveredMessage(m); postDeliver(m); } + return m; } finally @@ -423,6 +428,7 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer { //we do not need a lock around the test above, and the dispatch below as it is invalid //for an application to alter an installed listener while the session is started + _session.setLastDeliveredMessage(jmsMessage); getMessageListener().onMessage(jmsMessage); postDeliver(jmsMessage); } @@ -459,8 +465,9 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer } } - private void postDeliver(AbstractJMSMessage msg) + private void postDeliver(AbstractJMSMessage msg) throws JMSException { + msg.setJMSDestination(_destination); switch (_acknowledgeMode) { case Session.DUPS_OK_ACKNOWLEDGE: @@ -522,7 +529,7 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer */ private void deregisterConsumer() { - _session.deregisterConsumer(_consumerTag); + _session.deregisterConsumer(this); } public String getConsumerTag() @@ -531,18 +538,18 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer } public void setConsumerTag(String consumerTag) - { + { _consumerTag = consumerTag; } public AMQSession getSession() { return _session; } - + private void checkPreConditions() throws JMSException{ - + this.checkNotClosed(); - + if(_session == null || _session.isClosed()){ throw new javax.jms.IllegalStateException("Invalid Session"); } diff --git a/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java b/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java index 8c53d93de6..e11d70cf41 100644 --- a/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java +++ b/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java @@ -24,11 +24,13 @@ import org.apache.log4j.Logger; import org.apache.mina.common.ByteBuffer; import org.apache.qpid.AMQException; import org.apache.qpid.client.message.AbstractJMSMessage; +import org.apache.qpid.client.message.JMSBytesMessage; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.framing.*; import javax.jms.*; import java.io.UnsupportedEncodingException; +import java.util.Enumeration; public class BasicMessageProducer extends Closeable implements org.apache.qpid.jms.MessageProducer { @@ -140,7 +142,7 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j public void setDisableMessageID(boolean b) throws JMSException { - checkPreConditions(); + checkPreConditions(); checkNotClosed(); // IGNORED } @@ -154,7 +156,7 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j public void setDisableMessageTimestamp(boolean b) throws JMSException { - checkPreConditions(); + checkPreConditions(); _disableTimestamps = b; } @@ -166,11 +168,11 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j public void setDeliveryMode(int i) throws JMSException { - checkPreConditions(); + checkPreConditions(); if (i != DeliveryMode.NON_PERSISTENT && i != DeliveryMode.PERSISTENT) { throw new JMSException("DeliveryMode must be either NON_PERSISTENT or PERSISTENT. Value of " + i + - " is illegal"); + " is illegal"); } _deliveryMode = i; } @@ -183,7 +185,7 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j public void setPriority(int i) throws JMSException { - checkPreConditions(); + checkPreConditions(); if (i < 0 || i > 9) { throw new IllegalArgumentException("Priority of " + i + " is illegal. Value must be in range 0 to 9"); @@ -199,7 +201,7 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j public void setTimeToLive(long l) throws JMSException { - checkPreConditions(); + checkPreConditions(); if (l < 0) { throw new IllegalArgumentException("Time to live must be non-negative - supplied value was " + l); @@ -227,33 +229,36 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j public void send(Message message) throws JMSException { - checkPreConditions(); - checkInitialDestination(); + checkPreConditions(); + checkInitialDestination(); + + synchronized (_connection.getFailoverMutex()) { - sendImpl(_destination, (AbstractJMSMessage) message, _deliveryMode, _messagePriority, _timeToLive, + sendImpl(_destination, message, _deliveryMode, _messagePriority, _timeToLive, _mandatory, _immediate); } } public void send(Message message, int deliveryMode) throws JMSException { - checkPreConditions(); - checkInitialDestination(); + checkPreConditions(); + checkInitialDestination(); + synchronized (_connection.getFailoverMutex()) { - sendImpl(_destination, (AbstractJMSMessage) message, deliveryMode, _messagePriority, _timeToLive, + sendImpl(_destination, message, deliveryMode, _messagePriority, _timeToLive, _mandatory, _immediate); } } public void send(Message message, int deliveryMode, boolean immediate) throws JMSException { - checkPreConditions(); - checkInitialDestination(); + checkPreConditions(); + checkInitialDestination(); synchronized (_connection.getFailoverMutex()) { - sendImpl(_destination, (AbstractJMSMessage) message, deliveryMode, _messagePriority, _timeToLive, + sendImpl(_destination, message, deliveryMode, _messagePriority, _timeToLive, _mandatory, immediate); } } @@ -261,23 +266,23 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j public void send(Message message, int deliveryMode, int priority, long timeToLive) throws JMSException { - checkPreConditions(); - checkInitialDestination(); + checkPreConditions(); + checkInitialDestination(); synchronized (_connection.getFailoverMutex()) { - sendImpl(_destination, (AbstractJMSMessage)message, deliveryMode, priority, timeToLive, _mandatory, + sendImpl(_destination, message, deliveryMode, priority, timeToLive, _mandatory, _immediate); } } public void send(Destination destination, Message message) throws JMSException { - checkPreConditions(); - checkDestination(destination); + checkPreConditions(); + checkDestination(destination); synchronized (_connection.getFailoverMutex()) { validateDestination(destination); - sendImpl((AMQDestination) destination, (AbstractJMSMessage) message, _deliveryMode, _messagePriority, _timeToLive, + sendImpl((AMQDestination) destination, message, _deliveryMode, _messagePriority, _timeToLive, _mandatory, _immediate); } } @@ -286,12 +291,12 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j int priority, long timeToLive) throws JMSException { - checkPreConditions(); - checkDestination(destination); + checkPreConditions(); + checkDestination(destination); synchronized (_connection.getFailoverMutex()) { validateDestination(destination); - sendImpl((AMQDestination) destination, (AbstractJMSMessage) message, deliveryMode, priority, timeToLive, + sendImpl((AMQDestination) destination, message, deliveryMode, priority, timeToLive, _mandatory, _immediate); } } @@ -305,7 +310,7 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j synchronized (_connection.getFailoverMutex()) { validateDestination(destination); - sendImpl((AMQDestination) destination, (AbstractJMSMessage) message, deliveryMode, priority, timeToLive, + sendImpl((AMQDestination) destination, message, deliveryMode, priority, timeToLive, mandatory, _immediate); } } @@ -314,12 +319,12 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j int priority, long timeToLive, boolean mandatory, boolean immediate) throws JMSException { - checkPreConditions(); - checkDestination(destination); + checkPreConditions(); + checkDestination(destination); synchronized (_connection.getFailoverMutex()) { validateDestination(destination); - sendImpl((AMQDestination) destination, (AbstractJMSMessage) message, deliveryMode, priority, timeToLive, + sendImpl((AMQDestination) destination, message, deliveryMode, priority, timeToLive, mandatory, immediate); } } @@ -329,27 +334,158 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j boolean immediate, boolean waitUntilSent) throws JMSException { - checkPreConditions(); - checkDestination(destination); + checkPreConditions(); + checkDestination(destination); synchronized (_connection.getFailoverMutex()) { validateDestination(destination); - sendImpl((AMQDestination) destination, (AbstractJMSMessage) message, deliveryMode, priority, timeToLive, + sendImpl((AMQDestination) destination, message, deliveryMode, priority, timeToLive, mandatory, immediate, waitUntilSent); } } + + private AbstractJMSMessage convertToNativeMessage(Message message) throws JMSException + { + if (message instanceof AbstractJMSMessage) + { + return (AbstractJMSMessage) message; + } + else + { + AbstractJMSMessage newMessage; + + if (message instanceof BytesMessage) + { + BytesMessage bytesMessage = (BytesMessage) message; + bytesMessage.reset(); + + JMSBytesMessage nativeMsg = (JMSBytesMessage) _session.createBytesMessage(); + + + byte[] buf = new byte[1024]; + + int len; + + while ((len = bytesMessage.readBytes(buf)) != -1) + { + nativeMsg.writeBytes(buf, 0, len); + } + + newMessage = nativeMsg; + } + else if (message instanceof MapMessage) + { + MapMessage origMessage = (MapMessage) message; + MapMessage nativeMessage = _session.createMapMessage(); + + Enumeration mapNames = origMessage.getMapNames(); + while (mapNames.hasMoreElements()) + { + String name = (String) mapNames.nextElement(); + nativeMessage.setObject(name, origMessage.getObject(name)); + } + newMessage = (AbstractJMSMessage) nativeMessage; + } + else if (message instanceof ObjectMessage) + { + ObjectMessage origMessage = (ObjectMessage) message; + ObjectMessage nativeMessage = _session.createObjectMessage(); + + nativeMessage.setObject(origMessage.getObject()); + + newMessage = (AbstractJMSMessage) nativeMessage; + } + else if (message instanceof TextMessage) + { + TextMessage origMessage = (TextMessage) message; + TextMessage nativeMessage = _session.createTextMessage(); + + nativeMessage.setText(origMessage.getText()); + + newMessage = (AbstractJMSMessage) nativeMessage; + } + else if (message instanceof StreamMessage) + { + StreamMessage origMessage = (StreamMessage) message; + StreamMessage nativeMessage = _session.createStreamMessage(); + + + try + { + origMessage.reset(); + while (true) + { + nativeMessage.writeObject(origMessage.readObject()); + } + } + catch (MessageEOFException e) + { + ;// + } + newMessage = (AbstractJMSMessage) nativeMessage; + } + else + { + newMessage = (AbstractJMSMessage) _session.createMessage(); + + } + + Enumeration propertyNames = message.getPropertyNames(); + while (propertyNames.hasMoreElements()) + { + String propertyName = String.valueOf(propertyNames.nextElement()); + if (!propertyName.startsWith("JMSX_")) + { + Object value = message.getObjectProperty(propertyName); + newMessage.setObjectProperty(propertyName, value); + } + } + + newMessage.setJMSDeliveryMode(message.getJMSDeliveryMode()); + + + int priority = message.getJMSPriority(); + if (priority < 0) + { + priority = 0; + } + else if (priority > 9) + { + priority = 9; + } + + newMessage.setJMSPriority(priority); + if (message.getJMSReplyTo() != null) + { + newMessage.setJMSReplyTo(message.getJMSReplyTo()); + } + newMessage.setJMSType(message.getJMSType()); + + + if (newMessage != null) + { + return newMessage; + } + else + { + throw new JMSException("Unable to send message, due to class conversion error: " + message.getClass().getName()); + } + } + } + + private void validateDestination(Destination destination) throws JMSException { if (!(destination instanceof AMQDestination)) { throw new JMSException("Unsupported destination class: " + - (destination != null ? destination.getClass() : null)); + (destination != null ? destination.getClass() : null)); } - declareDestination((AMQDestination)destination); + declareDestination((AMQDestination) destination); } - protected void sendImpl(AMQDestination destination, AbstractJMSMessage message, int deliveryMode, int priority, + protected void sendImpl(AMQDestination destination, Message message, int deliveryMode, int priority, long timeToLive, boolean mandatory, boolean immediate) throws JMSException { sendImpl(destination, message, deliveryMode, priority, timeToLive, mandatory, immediate, _waitUntilSent); @@ -357,8 +493,9 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j /** * The caller of this method must hold the failover mutex. + * * @param destination - * @param message + * @param origMessage * @param deliveryMode * @param priority * @param timeToLive @@ -366,9 +503,12 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j * @param immediate * @throws JMSException */ - protected void sendImpl(AMQDestination destination, AbstractJMSMessage message, int deliveryMode, int priority, + protected void sendImpl(AMQDestination destination, Message origMessage, int deliveryMode, int priority, long timeToLive, boolean mandatory, boolean immediate, boolean wait) throws JMSException { + checkTemporaryDestination(destination); + + AbstractJMSMessage message = convertToNativeMessage(origMessage); AMQFrame publishFrame = BasicPublishBody.createAMQFrame(_channelId, 0, destination.getExchangeName(), destination.getRoutingKey(), mandatory, immediate); @@ -424,11 +564,42 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j frames[1] = contentHeaderFrame; CompositeAMQDataBlock compositeFrame = new CompositeAMQDataBlock(frames); _protocolHandler.writeFrame(compositeFrame, wait); + + + if (message != origMessage) + { + _logger.warn("Updating original message"); + origMessage.setJMSPriority(message.getJMSPriority()); + origMessage.setJMSTimestamp(message.getJMSTimestamp()); + _logger.warn("Setting JMSExpiration:" + message.getJMSExpiration()); + origMessage.setJMSExpiration(message.getJMSExpiration()); + origMessage.setJMSMessageID(message.getJMSMessageID()); + } + } + + private void checkTemporaryDestination(AMQDestination destination) throws JMSException + { + if(destination instanceof TemporaryDestination) + { + _logger.debug("destination is temporary destination"); + TemporaryDestination tempDest = (TemporaryDestination) destination; + if(tempDest.getSession().isClosed()) + { + _logger.debug("session is closed"); + throw new JMSException("Session for temporary destination has been closed"); + } + if(tempDest.isDeleted()) + { + _logger.debug("destination is deleted"); + throw new JMSException("Cannot send to a deleted temporary destination"); + } + } } /** * Create content bodies. This will split a large message into numerous bodies depending on the negotiated * maximum frame size. + * * @param payload * @return the array of content bodies */ @@ -458,8 +629,8 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j for (int i = 0; i < bodies.length; i++) { bodies[i] = new ContentBody(); - payload.position((int)framePayloadMax * i); - int length = (remaining >= framePayloadMax) ? (int)framePayloadMax : (int)remaining; + payload.position((int) framePayloadMax * i); + int length = (remaining >= framePayloadMax) ? (int) framePayloadMax : (int) remaining; payload.limit(payload.position() + length); bodies[i].payload = payload.slice(); remaining -= length; @@ -480,32 +651,42 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j _encoding = encoding; } - private void checkPreConditions() throws javax.jms.IllegalStateException, JMSException { - checkNotClosed(); + private void checkPreConditions() throws javax.jms.IllegalStateException, JMSException + { + checkNotClosed(); + + if (_session == null || _session.isClosed()) + { + throw new javax.jms.IllegalStateException("Invalid Session"); + } + } + + private void checkInitialDestination() + { + if (_destination == null) + { + throw new UnsupportedOperationException("Destination is null"); + } + } - if(_session == null || _session.isClosed()){ - throw new javax.jms.IllegalStateException("Invalid Session"); - } - } + private void checkDestination(Destination suppliedDestination) throws InvalidDestinationException + { + if (_destination != null && suppliedDestination != null) + { + throw new UnsupportedOperationException("This message producer was created with a Destination, therefore you cannot use an unidentified Destination"); + } - private void checkInitialDestination(){ - if(_destination == null){ - throw new UnsupportedOperationException("Destination is null"); - } - } + if (suppliedDestination == null) + { + throw new InvalidDestinationException("Supplied Destination was invalid"); + } - private void checkDestination(Destination suppliedDestination) throws InvalidDestinationException{ - if (_destination != null && suppliedDestination != null){ - throw new UnsupportedOperationException("This message producer was created with a Destination, therefore you cannot use an unidentified Destination"); - } - if (suppliedDestination == null){ - throw new InvalidDestinationException("Supplied Destination was invalid"); - } - } + } - public AMQSession getSession() { - return _session; - } + public AMQSession getSession() + { + return _session; + } } diff --git a/java/client/src/main/java/org/apache/qpid/client/JMSAMQException.java b/java/client/src/main/java/org/apache/qpid/client/JMSAMQException.java new file mode 100644 index 0000000000..34ec49436e --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/JMSAMQException.java @@ -0,0 +1,34 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.qpid.client; + +import org.apache.qpid.AMQException; + +import javax.jms.JMSException; + +/** + * @author Apache Software Foundation + */ +public class JMSAMQException extends JMSException +{ + public JMSAMQException(AMQException s) + { + super(s.getMessage(), String.valueOf(s.getErrorCode())); + setLinkedException(s); + } +} diff --git a/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java b/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java index f90cc97a80..c8de298ba1 100644 --- a/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java +++ b/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java @@ -10,119 +10,124 @@ import javax.jms.QueueSender; public class QueueSenderAdapter implements QueueSender { - private MessageProducer delegate; - private Queue queue; + private MessageProducer _delegate; + private Queue _queue; private boolean closed = false; public QueueSenderAdapter(MessageProducer msgProducer, Queue queue){ - delegate = msgProducer; - this.queue = queue; + _delegate = msgProducer; + _queue = queue; } public Queue getQueue() throws JMSException { checkPreConditions(); - return queue; + return _queue; } public void send(Message msg) throws JMSException { checkPreConditions(); - delegate.send(msg); + _delegate.send(msg); } public void send(Queue queue, Message msg) throws JMSException { - checkPreConditions(); - delegate.send(queue, msg); + checkPreConditions(queue); + _delegate.send(queue, msg); } public void publish(Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException { checkPreConditions(); - delegate.send(msg, deliveryMode,priority,timeToLive); + _delegate.send(msg, deliveryMode,priority,timeToLive); } public void send(Queue queue,Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException { - checkPreConditions(); - delegate.send(queue,msg, deliveryMode,priority,timeToLive); + checkPreConditions(queue); + _delegate.send(queue,msg, deliveryMode,priority,timeToLive); } public void close() throws JMSException { - delegate.close(); + _delegate.close(); closed = true; } public int getDeliveryMode() throws JMSException { checkPreConditions(); - return delegate.getDeliveryMode(); + return _delegate.getDeliveryMode(); } public Destination getDestination() throws JMSException { checkPreConditions(); - return delegate.getDestination(); + return _delegate.getDestination(); } public boolean getDisableMessageID() throws JMSException { checkPreConditions(); - return delegate.getDisableMessageID(); + return _delegate.getDisableMessageID(); } public boolean getDisableMessageTimestamp() throws JMSException { checkPreConditions(); - return delegate.getDisableMessageTimestamp(); + return _delegate.getDisableMessageTimestamp(); } public int getPriority() throws JMSException { checkPreConditions(); - return delegate.getPriority(); + return _delegate.getPriority(); } public long getTimeToLive() throws JMSException { checkPreConditions(); - return delegate.getTimeToLive(); + return _delegate.getTimeToLive(); } public void send(Destination dest, Message msg) throws JMSException { - checkPreConditions(); - delegate.send(dest,msg); + checkPreConditions((Queue)dest); + _delegate.send(dest,msg); } public void send(Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException { checkPreConditions(); - delegate.send(msg, deliveryMode,priority,timeToLive); + _delegate.send(msg, deliveryMode,priority,timeToLive); } public void send(Destination dest, Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException { - checkPreConditions(); - delegate.send(dest,msg, deliveryMode,priority,timeToLive); + checkPreConditions((Queue)dest); + _delegate.send(dest,msg, deliveryMode,priority,timeToLive); } public void setDeliveryMode(int deliveryMode) throws JMSException { checkPreConditions(); - delegate.setDeliveryMode(deliveryMode); + _delegate.setDeliveryMode(deliveryMode); } public void setDisableMessageID(boolean disableMessageID) throws JMSException { checkPreConditions(); - delegate.setDisableMessageID(disableMessageID); + _delegate.setDisableMessageID(disableMessageID); } public void setDisableMessageTimestamp(boolean disableMessageTimestamp) throws JMSException { checkPreConditions(); - delegate.setDisableMessageTimestamp(disableMessageTimestamp); + _delegate.setDisableMessageTimestamp(disableMessageTimestamp); } public void setPriority(int priority) throws JMSException { checkPreConditions(); - delegate.setPriority(priority); + _delegate.setPriority(priority); } public void setTimeToLive(long timeToLive) throws JMSException { checkPreConditions(); - delegate.setTimeToLive(timeToLive); + _delegate.setTimeToLive(timeToLive); } - - private void checkPreConditions() throws IllegalStateException, IllegalStateException { + + private void checkPreConditions() throws IllegalStateException, IllegalStateException + { + checkPreConditions(_queue); + } + + private void checkPreConditions(Queue queue) throws IllegalStateException, IllegalStateException { if (closed){ throw new javax.jms.IllegalStateException("Publisher is closed"); } @@ -131,7 +136,7 @@ public class QueueSenderAdapter implements QueueSender { throw new UnsupportedOperationException("Queue is null"); } - AMQSession session = ((BasicMessageProducer)delegate).getSession(); + AMQSession session = ((BasicMessageProducer) _delegate).getSession(); if(session == null || session.isClosed()){ throw new javax.jms.IllegalStateException("Invalid Session"); diff --git a/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java b/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java new file mode 100644 index 0000000000..8c11672a65 --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java @@ -0,0 +1,17 @@ +package org.apache.qpid.client;
+
+import javax.jms.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Provides support for covenience interface implemented by both AMQTemporaryTopic and AMQTemporaryQueue
+ * so that operations related to their "temporary-ness" can be abstracted out.
+ */
+interface TemporaryDestination extends Destination
+{
+
+ public void delete() throws JMSException;
+ public AMQSession getSession();
+ public boolean isDeleted();
+
+}
diff --git a/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java b/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java index 014c7c3311..dbc7b72813 100644 --- a/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java +++ b/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java @@ -35,10 +35,10 @@ import javax.jms.TopicSubscriber; class TopicSubscriberAdaptor implements TopicSubscriber { private final Topic _topic; - private final MessageConsumer _consumer; + private final BasicMessageConsumer _consumer; private final boolean _noLocal; - TopicSubscriberAdaptor(Topic topic, MessageConsumer consumer, boolean noLocal) + TopicSubscriberAdaptor(Topic topic, BasicMessageConsumer consumer, boolean noLocal) { _topic = topic; _consumer = consumer; @@ -119,4 +119,10 @@ class TopicSubscriberAdaptor implements TopicSubscriber throw new javax.jms.IllegalStateException("Invalid Session"); } } + + BasicMessageConsumer getMessageConsumer() + { + return _consumer; + } + } diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java new file mode 100644 index 0000000000..858726745e --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java @@ -0,0 +1,54 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.qpid.client.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.client.protocol.AMQMethodEvent; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.ExchangeBoundOkBody; + +/** + * @author Apache Software Foundation + */ +public class ExchangeBoundOkMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(ExchangeBoundOkMethodHandler.class); + private static final ExchangeBoundOkMethodHandler _instance = new ExchangeBoundOkMethodHandler(); + + public static ExchangeBoundOkMethodHandler getInstance() + { + return _instance; + } + + private ExchangeBoundOkMethodHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException + { + if (_logger.isDebugEnabled()) + { + ExchangeBoundOkBody body = (ExchangeBoundOkBody) evt.getMethod(); + _logger.debug("Received Exchange.Bound-Ok message, response code: " + body.replyCode + " text: " + + body.replyText); + } + } +} + diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java new file mode 100644 index 0000000000..3271a715a2 --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java @@ -0,0 +1,53 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.qpid.client.handler; + +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.protocol.AMQMethodEvent; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.QueueDeleteOkBody; +import org.apache.log4j.Logger; + +/** + * @author Apache Software Foundation + */ +public class QueueDeleteOkMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(QueueDeleteOkMethodHandler.class); + private static final QueueDeleteOkMethodHandler _instance = new QueueDeleteOkMethodHandler(); + + public static QueueDeleteOkMethodHandler getInstance() + { + return _instance; + } + + private QueueDeleteOkMethodHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException + { + if (_logger.isDebugEnabled()) + { + QueueDeleteOkBody body = (QueueDeleteOkBody) evt.getMethod(); + _logger.debug("Received Queue.Delete-Ok message, message count: " + body.messageCount); + } + } +} + diff --git a/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java index c1ed88b167..75e84fee96 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java @@ -32,16 +32,18 @@ import org.apache.qpid.client.AMQTopic; import org.apache.qpid.client.JmsNotImplementedException; import org.apache.qpid.framing.BasicContentHeaderProperties; import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.JMSPropertyFieldTable; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.MessageNotReadableException; import javax.jms.MessageNotWriteableException; +import javax.jms.MessageFormatException; import java.util.Collections; import java.util.Enumeration; import java.util.Map; -public abstract class AbstractJMSMessage extends AMQMessage implements javax.jms.Message +public abstract class AbstractJMSMessage extends AMQMessage implements org.apache.qpid.jms.Message { private static final Map _destinationCache = Collections.synchronizedMap(new ReferenceMap()); @@ -50,7 +52,8 @@ public abstract class AbstractJMSMessage extends AMQMessage implements javax.jms protected ByteBuffer _data; private boolean _readableProperties = false; private boolean _readableMessage = false; - + private Destination _destination; + protected AbstractJMSMessage(ByteBuffer data) { super(new BasicContentHeaderProperties()); @@ -174,12 +177,12 @@ public abstract class AbstractJMSMessage extends AMQMessage implements javax.jms public Destination getJMSDestination() throws JMSException { // TODO: implement this once we have sorted out how to figure out the exchange class - throw new JmsNotImplementedException(); + return _destination; } public void setJMSDestination(Destination destination) throws JMSException { - throw new JmsNotImplementedException(); + _destination = destination; } public int getJMSDeliveryMode() throws JMSException @@ -234,7 +237,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements javax.jms public void clearProperties() throws JMSException { - getJmsContentHeaderProperties().getHeaders().clear(); + getJmsContentHeaderProperties().getJMSHeaders().clear(); _readableProperties = false; } @@ -249,138 +252,139 @@ public abstract class AbstractJMSMessage extends AMQMessage implements javax.jms public boolean propertyExists(String propertyName) throws JMSException { checkPropertyName(propertyName); - return getJmsContentHeaderProperties().getHeaders().propertyExists(propertyName); + return getJmsContentHeaderProperties().getJMSHeaders().propertyExists(propertyName); } public boolean getBooleanProperty(String propertyName) throws JMSException { checkPropertyName(propertyName); - return getJmsContentHeaderProperties().getHeaders().getBoolean(propertyName); + + return getJmsContentHeaderProperties().getJMSHeaders().getBoolean(propertyName); } public byte getByteProperty(String propertyName) throws JMSException { checkPropertyName(propertyName); - return getJmsContentHeaderProperties().getHeaders().getByte(propertyName); + return getJmsContentHeaderProperties().getJMSHeaders().getByte(propertyName); } public short getShortProperty(String propertyName) throws JMSException { checkPropertyName(propertyName); - return getJmsContentHeaderProperties().getHeaders().getShort(propertyName); + return getJmsContentHeaderProperties().getJMSHeaders().getShort(propertyName); } public int getIntProperty(String propertyName) throws JMSException { checkPropertyName(propertyName); - return getJmsContentHeaderProperties().getHeaders().getInteger(propertyName); + return getJmsContentHeaderProperties().getJMSHeaders().getInteger(propertyName); } public long getLongProperty(String propertyName) throws JMSException { checkPropertyName(propertyName); - return getJmsContentHeaderProperties().getHeaders().getLong(propertyName); + return getJmsContentHeaderProperties().getJMSHeaders().getLong(propertyName); } public float getFloatProperty(String propertyName) throws JMSException { checkPropertyName(propertyName); - return getJmsContentHeaderProperties().getHeaders().getFloat(propertyName); + return getJmsContentHeaderProperties().getJMSHeaders().getFloat(propertyName); } public double getDoubleProperty(String propertyName) throws JMSException { checkPropertyName(propertyName); - return getJmsContentHeaderProperties().getHeaders().getDouble(propertyName); + return getJmsContentHeaderProperties().getJMSHeaders().getDouble(propertyName); } public String getStringProperty(String propertyName) throws JMSException { checkPropertyName(propertyName); - return getJmsContentHeaderProperties().getHeaders().getString(propertyName); + return getJmsContentHeaderProperties().getJMSHeaders().getString(propertyName); } public Object getObjectProperty(String propertyName) throws JMSException { checkPropertyName(propertyName); - return getJmsContentHeaderProperties().getHeaders().getObject(propertyName); + return getJmsContentHeaderProperties().getJMSHeaders().getObject(propertyName); } public Enumeration getPropertyNames() throws JMSException { - return getJmsContentHeaderProperties().getHeaders().getPropertyNames(); + return getJmsContentHeaderProperties().getJMSHeaders().getPropertyNames(); } public void setBooleanProperty(String propertyName, boolean b) throws JMSException { checkWritableProperties(); checkPropertyName(propertyName); - getJmsContentHeaderProperties().getHeaders().setBoolean(propertyName, b); + getJmsContentHeaderProperties().getJMSHeaders().setBoolean(propertyName, b); } public void setByteProperty(String propertyName, byte b) throws JMSException { checkWritableProperties(); checkPropertyName(propertyName); - getJmsContentHeaderProperties().getHeaders().setByte(propertyName, new Byte(b)); + getJmsContentHeaderProperties().getJMSHeaders().setByte(propertyName, new Byte(b)); } public void setShortProperty(String propertyName, short i) throws JMSException { checkWritableProperties(); checkPropertyName(propertyName); - getJmsContentHeaderProperties().getHeaders().setShort(propertyName, new Short(i)); + getJmsContentHeaderProperties().getJMSHeaders().setShort(propertyName, new Short(i)); } public void setIntProperty(String propertyName, int i) throws JMSException { checkWritableProperties(); checkPropertyName(propertyName); - getJmsContentHeaderProperties().getHeaders().setInteger(propertyName, new Integer(i)); + getJmsContentHeaderProperties().getJMSHeaders().setInteger(propertyName, new Integer(i)); } public void setLongProperty(String propertyName, long l) throws JMSException { checkWritableProperties(); checkPropertyName(propertyName); - getJmsContentHeaderProperties().getHeaders().setLong(propertyName, new Long(l)); + getJmsContentHeaderProperties().getJMSHeaders().setLong(propertyName, new Long(l)); } public void setFloatProperty(String propertyName, float f) throws JMSException { checkWritableProperties(); checkPropertyName(propertyName); - getJmsContentHeaderProperties().getHeaders().setFloat(propertyName, new Float(f)); + getJmsContentHeaderProperties().getJMSHeaders().setFloat(propertyName, new Float(f)); } public void setDoubleProperty(String propertyName, double v) throws JMSException { checkWritableProperties(); checkPropertyName(propertyName); - getJmsContentHeaderProperties().getHeaders().setDouble(propertyName, new Double(v)); + getJmsContentHeaderProperties().getJMSHeaders().setDouble(propertyName, new Double(v)); } public void setStringProperty(String propertyName, String value) throws JMSException { checkWritableProperties(); checkPropertyName(propertyName); - getJmsContentHeaderProperties().getHeaders().setString(propertyName, value); + getJmsContentHeaderProperties().getJMSHeaders().setString(propertyName, value); } public void setObjectProperty(String propertyName, Object object) throws JMSException { checkWritableProperties(); checkPropertyName(propertyName); - getJmsContentHeaderProperties().getHeaders().setObject(propertyName, object); + getJmsContentHeaderProperties().getJMSHeaders().setObject(propertyName, object); } protected void removeProperty(String propertyName) throws JMSException { checkPropertyName(propertyName); - getJmsContentHeaderProperties().getHeaders().remove(propertyName); + getJmsContentHeaderProperties().getJMSHeaders().remove(propertyName); } - public void acknowledge() throws JMSException + public void acknowledgeThis() throws JMSException { // the JMS 1.1 spec says in section 3.6 that calls to acknowledge are ignored when client acknowledge // is not specified. In our case, we only set the session field where client acknowledge mode is specified. @@ -397,6 +401,14 @@ public abstract class AbstractJMSMessage extends AMQMessage implements javax.jms } } + public void acknowledge() throws JMSException + { + if(_session != null) + { + _session.acknowledge(); + } + } + /** * This forces concrete classes to implement clearBody() @@ -426,13 +438,13 @@ public abstract class AbstractJMSMessage extends AMQMessage implements javax.jms buf.append("\nJMS reply to: ").append(String.valueOf(getJMSReplyTo())); buf.append("\nAMQ message number: ").append(_deliveryTag); buf.append("\nProperties:"); - if (getJmsContentHeaderProperties().getHeaders().isEmpty()) + if (getJmsContentHeaderProperties().getJMSHeaders().isEmpty()) { buf.append("<NONE>"); } else { - buf.append('\n').append(getJmsContentHeaderProperties().getHeaders()); + buf.append('\n').append(getJmsContentHeaderProperties().getJMSHeaders()); } return buf.toString(); } @@ -462,9 +474,6 @@ public abstract class AbstractJMSMessage extends AMQMessage implements javax.jms { throw new IllegalArgumentException("Property name must not be the empty string"); } - - // Call to ensure that the it has been set. - getJmsContentHeaderProperties().getHeaders(); } public BasicContentHeaderProperties getJmsContentHeaderProperties() @@ -478,14 +487,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements javax.jms // position beyond the start if (_data != null) { - if (!_readableMessage) - { - _data.flip(); - } - else - { - _data.rewind(); - } + reset(); } return _data; } @@ -524,7 +526,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements javax.jms return !_readableMessage; } - public void reset() throws JMSException + public void reset() { if (_readableMessage) { diff --git a/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.java index 456d4d520c..debabfd559 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.java @@ -30,6 +30,9 @@ import javax.jms.MessageFormatException; import javax.jms.MessageEOFException; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CharsetDecoder; +import java.nio.CharBuffer; public class JMSBytesMessage extends AbstractBytesMessage implements BytesMessage { @@ -149,10 +152,27 @@ public class JMSBytesMessage extends AbstractBytesMessage implements BytesMessag checkReadable(); // we check only for one byte since theoretically the string could be only a // single byte when using UTF-8 encoding - checkAvailable(1); + try { - return _data.getString(Charset.forName("UTF-8").newDecoder()); + short length = readShort(); + if(length == 0) + { + return ""; + } + else + { + CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder(); + ByteBuffer encodedString = _data.slice(); + encodedString.limit(length); + _data.position(_data.position()+length); + CharBuffer string = decoder.decode(encodedString.buf()); + + return string.toString(); + } + + + } catch (CharacterCodingException e) { @@ -257,9 +277,15 @@ public class JMSBytesMessage extends AbstractBytesMessage implements BytesMessag checkWritable(); try { - _data.putString(string, Charset.forName("UTF-8").newEncoder()); + CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder(); + java.nio.ByteBuffer encodedString = encoder.encode(CharBuffer.wrap(string)); + + _data.putShort((short)encodedString.limit()); + _data.put(encodedString); + + //_data.putString(string, Charset.forName("UTF-8").newEncoder()); // we must add the null terminator manually - _data.put((byte)0); + //_data.put((byte)0); } catch (CharacterCodingException e) { diff --git a/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java index 85d434e4eb..f69bed0fc0 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java @@ -25,6 +25,8 @@ import org.apache.qpid.framing.PropertyFieldTable; import org.apache.qpid.framing.FieldTableFactory; import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.EncodingUtils; +import org.apache.qpid.framing.JMSPropertyFieldTable; +import org.apache.qpid.framing.AMQFrameDecodingException; import org.apache.qpid.AMQException; import org.apache.log4j.Logger; @@ -39,7 +41,7 @@ public class JMSMapMessage extends JMSBytesMessage implements javax.jms.MapMessa public static final String MIME_TYPE = "jms/map-message"; - private PropertyFieldTable _map; + private JMSPropertyFieldTable _properties; JMSMapMessage() throws JMSException { @@ -49,10 +51,9 @@ public class JMSMapMessage extends JMSBytesMessage implements javax.jms.MapMessa JMSMapMessage(ByteBuffer data) throws JMSException { super(data); // this instantiates a content header - _map = new PropertyFieldTable(); + _properties = new JMSPropertyFieldTable(); } - JMSMapMessage(long messageNbr, ContentHeaderBody contentHeader, ByteBuffer data) throws AMQException { @@ -62,19 +63,33 @@ public class JMSMapMessage extends JMSBytesMessage implements javax.jms.MapMessa { long tableSize = EncodingUtils.readInteger(_data); - _map = (PropertyFieldTable) FieldTableFactory.newFieldTable(_data, tableSize); - + try + { + _properties = new JMSPropertyFieldTable(_data, tableSize); + } + catch (JMSException e) + { + Exception error = e.getLinkedException(); + if (error instanceof AMQFrameDecodingException) + { + throw(AMQFrameDecodingException) error; + } + else + { + throw new AMQException(e.getMessage(), e); + } + } } else { - _map = (PropertyFieldTable) FieldTableFactory.newFieldTable(); + _properties = new JMSPropertyFieldTable(); } } public String toBodyString() throws JMSException { - return "MapSize:" + _map.getEncodedSize() + "\nMapData:\n" + _map.toString(); + return _properties.toString(); } public String getMimeType() @@ -82,85 +97,43 @@ public class JMSMapMessage extends JMSBytesMessage implements javax.jms.MapMessa return MIME_TYPE; } - // MapMessage Interface - public boolean getBoolean(String string) throws JMSException + public ByteBuffer getData() { - Boolean b = _map.getBoolean(string); - - if (b == null) - { - if (_map.containsKey(string)) - { - Object str = _map.getObject(string); + //What if _data is null? + _properties.writeToBuffer(_data); + return super.getData(); + } - if (str == null || !(str instanceof String)) - { - throw new MessageFormatException("getBoolean can't use " + string + " item."); - } - else - { - return Boolean.valueOf((String) str); - } - } - else - { - b = Boolean.valueOf(null); - } - } + @Override + public void clearBodyImpl() throws JMSException + { + super.clearBodyImpl(); + _properties.clear(); + } - return b; + public boolean getBoolean(String string) throws JMSException + { + return _properties.getBoolean(string); } public byte getByte(String string) throws JMSException { - Byte b = _map.getByte(string); - if (b == null) - { - if (_map.containsKey(string)) - { - Object str = _map.getObject(string); - - if (str == null || !(str instanceof String)) - { - throw new MessageFormatException("getByte can't use " + string + " item."); - } - else - { - return Byte.valueOf((String) str); - } - } - else - { - b = Byte.valueOf(null); - } - } - - return b; + return _properties.getByte(string); } public short getShort(String string) throws JMSException { - { - Short s = _map.getShort(string); - - if (s == null) - { - s = Short.valueOf(getByte(string)); - } - - return s; - } + return _properties.getShort(string); } public char getChar(String string) throws JMSException { - - Character result = _map.getCharacter(string); + Character result = _properties.getCharacter(string); if (result == null) { - throw new MessageFormatException("getChar couldn't find " + string + " item."); + throw new NullPointerException("getChar couldn't find " + string + " item."); } else { @@ -170,179 +143,97 @@ public class JMSMapMessage extends JMSBytesMessage implements javax.jms.MapMessa public int getInt(String string) throws JMSException { - Integer i = _map.getInteger(string); - - if (i == null) - { - i = Integer.valueOf(getShort(string)); - } - - return i; + return _properties.getInteger(string); } public long getLong(String string) throws JMSException { - - Long l = _map.getLong(string); - - if (l == null) - { - l = Long.valueOf(getInt(string)); - } - - return l; - + return _properties.getLong(string); } public float getFloat(String string) throws JMSException { - - Float f = _map.getFloat(string); - - if (f == null) - { - if (_map.containsKey(string)) - { - Object str = _map.getObject(string); - - if (str == null || !(str instanceof String)) - { - throw new MessageFormatException("getFloat can't use " + string + " item."); - } - else - { - return Float.valueOf((String) str); - } - } - else - { - f = Float.valueOf(null); - } - - } - - return f; - + return _properties.getFloat(string); } public double getDouble(String string) throws JMSException { - Double d = _map.getDouble(string); - - if (d == null) - { - d = Double.valueOf(getFloat(string)); - } - - return d; + return _properties.getDouble(string); } public String getString(String string) throws JMSException { - String s = _map.getString(string); - - if (s == null) - { - if (_map.containsKey(string)) - { - Object o = _map.getObject(string); - if (o instanceof byte[]) - { - throw new MessageFormatException("getObject couldn't find " + string + " item."); - } - else - { - if (o == null) - { - return null; - } - else - { - s = String.valueOf(o); - } - } - } - } - - return s; + return _properties.getString(string); } public byte[] getBytes(String string) throws JMSException { - - byte[] result = _map.getBytes(string); - - if (result == null) - { - throw new MessageFormatException("getBytes couldn't find " + string + " item."); - } - - return result; - + return _properties.getBytes(string); } public Object getObject(String string) throws JMSException { - return _map.getObject(string); + return _properties.getObject(string); } public Enumeration getMapNames() throws JMSException { - return _map.getPropertyNames(); + return _properties.getMapNames(); } + public void setBoolean(String string, boolean b) throws JMSException { checkWritable(); - _map.setBoolean(string, b); + _properties.setBoolean(string, b); } public void setByte(String string, byte b) throws JMSException { checkWritable(); - _map.setByte(string, b); + _properties.setByte(string, b); } public void setShort(String string, short i) throws JMSException { checkWritable(); - _map.setShort(string, i); + _properties.setShort(string, i); } public void setChar(String string, char c) throws JMSException { checkWritable(); - _map.setChar(string, c); + _properties.setChar(string, c); } public void setInt(String string, int i) throws JMSException { checkWritable(); - _map.setInteger(string, i); + _properties.setInteger(string, i); } public void setLong(String string, long l) throws JMSException { checkWritable(); - _map.setLong(string, l); + _properties.setLong(string, l); } public void setFloat(String string, float v) throws JMSException { checkWritable(); - _map.setFloat(string, v); + _properties.setFloat(string, v); } public void setDouble(String string, double v) throws JMSException { checkWritable(); - _map.setDouble(string, v); + _properties.setDouble(string, v); } public void setString(String string, String string1) throws JMSException { checkWritable(); - _map.setString(string, string1); + _properties.setString(string, string1); } public void setBytes(String string, byte[] bytes) throws JMSException @@ -353,25 +244,18 @@ public class JMSMapMessage extends JMSBytesMessage implements javax.jms.MapMessa public void setBytes(String string, byte[] bytes, int i, int i1) throws JMSException { checkWritable(); - _map.setBytes(string, bytes, i, i1); + _properties.setBytes(string, bytes, i, i1); } public void setObject(String string, Object object) throws JMSException { checkWritable(); - _map.setObject(string, object); + _properties.setObject(string, object); } public boolean itemExists(String string) throws JMSException { - return _map.itemExists(string); - } - - public ByteBuffer getData() - { - //What if _data is null? - _map.writeToBuffer(_data); - return super.getData(); + return _properties.itemExists(string); } } diff --git a/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java index 7393cea714..4fb070d2ff 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java @@ -72,6 +72,7 @@ public class JMSObjectMessage extends AbstractJMSMessage implements ObjectMessag _data.release(); } _data = null; + } public String toBodyString() throws JMSException @@ -97,6 +98,7 @@ public class JMSObjectMessage extends AbstractJMSMessage implements ObjectMessag { _data.rewind(); } + try { ObjectOutputStream out = new ObjectOutputStream(_data.asOutputStream()); @@ -108,6 +110,7 @@ public class JMSObjectMessage extends AbstractJMSMessage implements ObjectMessag { throw new MessageFormatException("Message not serializable: " + e); } + } public Serializable getObject() throws JMSException @@ -120,15 +123,18 @@ public class JMSObjectMessage extends AbstractJMSMessage implements ObjectMessag try { + _data.rewind(); in = new ObjectInputStream(_data.asInputStream()); return (Serializable) in.readObject(); } catch (IOException e) - { - throw new MessageFormatException("Could not deserialize message: " + e); + { + e.printStackTrace(); + throw new MessageFormatException("Could not deserialize message: " + e); } catch (ClassNotFoundException e) { + e.printStackTrace(); throw new MessageFormatException("Could not deserialize message: " + e); } finally diff --git a/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.java index 04f3c5ee17..c2dfdc1b65 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.java @@ -226,6 +226,10 @@ public class JMSStreamMessage extends AbstractBytesMessage implements StreamMess byte wireType = readWireType(); try { + if(wireType == NULL_STRING_TYPE){ + throw new NullPointerException(); + } + if (wireType != CHAR_TYPE) { _data.position(position); @@ -428,7 +432,7 @@ public class JMSStreamMessage extends AbstractBytesMessage implements StreamMess break; case NULL_STRING_TYPE: result = null; - break; + throw new NullPointerException("data is null"); case BOOLEAN_TYPE: checkAvailable(1); result = String.valueOf(readBooleanImpl()); diff --git a/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java b/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java index ab707bb51d..887850c06e 100644 --- a/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java +++ b/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java @@ -7,9 +7,9 @@ * 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 @@ -104,6 +104,8 @@ public class AMQStateManager implements AMQMethodListener frame2handlerMap.put(BasicDeliverBody.class, BasicDeliverMethodHandler.getInstance()); frame2handlerMap.put(BasicReturnBody.class, BasicReturnMethodHandler.getInstance()); frame2handlerMap.put(ChannelFlowOkBody.class, ChannelFlowOkMethodHandler.getInstance()); + frame2handlerMap.put(QueueDeleteOkBody.class, QueueDeleteOkMethodHandler.getInstance()); + frame2handlerMap.put(ExchangeBoundOkBody.class, ExchangeBoundOkMethodHandler.getInstance()); _state2HandlersMap.put(AMQState.CONNECTION_OPEN, frame2handlerMap); } diff --git a/java/client/src/main/java/org/apache/qpid/jms/Message.java b/java/client/src/main/java/org/apache/qpid/jms/Message.java new file mode 100644 index 0000000000..d73e51d755 --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/jms/Message.java @@ -0,0 +1,28 @@ +/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.jms;
+
+import javax.jms.*;
+
+public interface Message extends javax.jms.Message
+{
+ public void acknowledgeThis() throws JMSException;
+}
diff --git a/java/client/src/test/java/org/apache/qpid/transacted/Config.java b/java/client/src/old_test/java/org/apache/qpid/transacted/Config.java index bd104e5407..bd104e5407 100644 --- a/java/client/src/test/java/org/apache/qpid/transacted/Config.java +++ b/java/client/src/old_test/java/org/apache/qpid/transacted/Config.java diff --git a/java/client/src/test/java/org/apache/qpid/transacted/Ping.java b/java/client/src/old_test/java/org/apache/qpid/transacted/Ping.java index e0af4422a6..e0af4422a6 100644 --- a/java/client/src/test/java/org/apache/qpid/transacted/Ping.java +++ b/java/client/src/old_test/java/org/apache/qpid/transacted/Ping.java diff --git a/java/client/src/test/java/org/apache/qpid/transacted/Pong.java b/java/client/src/old_test/java/org/apache/qpid/transacted/Pong.java index 13295c137a..13295c137a 100644 --- a/java/client/src/test/java/org/apache/qpid/transacted/Pong.java +++ b/java/client/src/old_test/java/org/apache/qpid/transacted/Pong.java diff --git a/java/client/src/test/java/org/apache/qpid/transacted/Relay.java b/java/client/src/old_test/java/org/apache/qpid/transacted/Relay.java index cede95e5f0..cede95e5f0 100644 --- a/java/client/src/test/java/org/apache/qpid/transacted/Relay.java +++ b/java/client/src/old_test/java/org/apache/qpid/transacted/Relay.java diff --git a/java/client/src/test/java/org/apache/qpid/transacted/Start.java b/java/client/src/old_test/java/org/apache/qpid/transacted/Start.java index 5564ed93ab..5564ed93ab 100644 --- a/java/client/src/test/java/org/apache/qpid/transacted/Start.java +++ b/java/client/src/old_test/java/org/apache/qpid/transacted/Start.java diff --git a/java/client/src/test/java/org/apache/qpid/weblogic/ServiceProvider.java b/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceProvider.java index 71d806b338..71d806b338 100644 --- a/java/client/src/test/java/org/apache/qpid/weblogic/ServiceProvider.java +++ b/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceProvider.java diff --git a/java/client/src/test/java/org/apache/qpid/weblogic/ServiceRequestingClient.java b/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceRequestingClient.java index a1e15258c3..a1e15258c3 100644 --- a/java/client/src/test/java/org/apache/qpid/weblogic/ServiceRequestingClient.java +++ b/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceRequestingClient.java diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java index bd68f32c23..dc0ade76c4 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java @@ -36,18 +36,21 @@ public class RecoverTest extends TestCase { private static final Logger _logger = Logger.getLogger(RecoverTest.class); - static + protected void setUp() throws Exception { - String workdir = System.getProperty("QPID_WORK"); - if (workdir == null || workdir.equals("")) - { - String tempdir = System.getProperty("java.io.tmpdir"); - System.out.println("QPID_WORK not set using tmp directory: " + tempdir); - System.setProperty("QPID_WORK", tempdir); - } - DOMConfigurator.configure("../broker/etc/log4j.xml"); + super.setUp(); + TransportConnection.createVMBroker(1); } + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killAllVMBrokers(); + //Thread.sleep(2000); + } + + + public void testRecoverResendsMsgs() throws Exception { Connection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "/test"); @@ -104,8 +107,74 @@ public class RecoverTest extends TestCase con.close(); } + + public void testRecoverResendsMsgsAckOnEarlier() throws Exception + { + Connection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "/test"); + + Session consumerSession = con.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Queue queue = new AMQQueue("someQ", "someQ", false, true); + MessageConsumer consumer = consumerSession.createConsumer(queue); + //force synch to ensure the consumer has resulted in a bound queue + ((AMQSession) consumerSession).declareExchangeSynch("amq.direct", "direct"); + + Connection con2 = new AMQConnection("vm://:1", "guest", "guest", "producer1", "/test"); + Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); + MessageProducer producer = producerSession.createProducer(queue); + + _logger.info("Sending four messages"); + producer.send(producerSession.createTextMessage("msg1")); + producer.send(producerSession.createTextMessage("msg2")); + producer.send(producerSession.createTextMessage("msg3")); + producer.send(producerSession.createTextMessage("msg4")); + + con2.close(); + + _logger.info("Starting connection"); + con.start(); + TextMessage tm = (TextMessage) consumer.receive(); + TextMessage tm2 = (TextMessage) consumer.receive(); + tm.acknowledge(); + _logger.info("Received 2 messages, acknowledge() first message, should acknowledge both"); + + consumer.receive(); + consumer.receive(); + _logger.info("Received all four messages. Calling recover with two outstanding messages"); + // no ack for last three messages so when I call recover I expect to get three messages back + consumerSession.recover(); + TextMessage tm3 = (TextMessage) consumer.receive(3000); + assertEquals("msg3", tm3.getText()); + + TextMessage tm4 = (TextMessage) consumer.receive(3000); + assertEquals("msg4", tm4.getText()); + + + _logger.info("Received redelivery of two messages. calling acknolwedgeThis() first of those message"); + ((org.apache.qpid.jms.Message)tm3).acknowledgeThis(); + + _logger.info("Calling recover"); + // all acked so no messages to be delivered + consumerSession.recover(); + + tm4 = (TextMessage) consumer.receive(3000); + assertEquals("msg4", tm4.getText()); + ((org.apache.qpid.jms.Message)tm4).acknowledgeThis(); + + _logger.info("Calling recover"); + // all acked so no messages to be delivered + consumerSession.recover(); + + + tm = (TextMessage) consumer.receiveNoWait(); + assertNull(tm); + _logger.info("No messages redelivered as is expected"); + + con.close(); + } + + public static junit.framework.Test suite() { - return new VMBrokerSetup(new junit.framework.TestSuite(RecoverTest.class)); + return new junit.framework.TestSuite(RecoverTest.class); } } diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java index 3fb1dd3f7d..f4efd64dbb 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java @@ -26,7 +26,6 @@ import org.apache.qpid.client.AMQQueue; import org.apache.qpid.client.AMQSession; import org.apache.qpid.client.message.JMSBytesMessage; import org.apache.qpid.framing.AMQFrameDecodingException; -import org.apache.qpid.framing.FieldTableTest; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.FieldTableFactory; import org.apache.qpid.testutil.VMBrokerSetup; @@ -134,7 +133,11 @@ public class FieldTableMessageTest extends TestCase implements MessageListener { ByteBuffer buffer = ((JMSBytesMessage) m).getData(); FieldTable actual = FieldTableFactory.newFieldTable(buffer, buffer.remaining()); - new FieldTableTest().assertEquivalent(_expected, actual); + for (Object o : _expected.keySet()) + { + String key = (String) o; + assertEquals("Values for " + key + " did not match", _expected.get(key), actual.get(key)); + } } } diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java index 1a469c1d12..02a98f67d9 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java @@ -20,20 +20,19 @@ */ package org.apache.qpid.test.unit.basic; +import junit.framework.Assert; +import junit.framework.TestCase; +import org.apache.log4j.Logger; import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQQueue; import org.apache.qpid.client.AMQSession; import org.apache.qpid.client.message.JMSMapMessage; -import org.apache.qpid.testutil.VMBrokerSetup; -import org.apache.log4j.Logger; +import org.apache.qpid.client.transport.TransportConnection; +import javax.jms.*; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import javax.jms.*; - -import junit.framework.TestCase; -import junit.framework.Assert; public class MapMessageTest extends TestCase implements MessageListener { @@ -56,7 +55,7 @@ public class MapMessageTest extends TestCase implements MessageListener super.setUp(); try { - //TransportConnection.createVMBroker(1); + TransportConnection.createVMBroker(1); init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "/test_path")); } catch (Exception e) @@ -69,7 +68,7 @@ public class MapMessageTest extends TestCase implements MessageListener { _logger.info("Tearing Down unit.basic.MapMessageTest"); super.tearDown(); - //TransportConnection.killAllVMBrokers(); + TransportConnection.killAllVMBrokers(); } private void init(AMQConnection connection) throws Exception @@ -166,12 +165,12 @@ public class MapMessageTest extends TestCase implements MessageListener testMapValues(m, count); + testCorrectExceptions(m); + testMessageWriteStatus(m); testPropertyWriteStatus(m); - testCorrectExceptions(m); - count++; } } @@ -230,7 +229,7 @@ public class MapMessageTest extends TestCase implements MessageListener m.getChar("message"); fail("Exception Expected."); } - catch (MessageFormatException nfe) + catch (MessageFormatException npe) { //normal execution } @@ -320,7 +319,7 @@ public class MapMessageTest extends TestCase implements MessageListener m.getChar("short"); fail("Exception Expected."); } - catch (MessageFormatException nfe) + catch (MessageFormatException npe) { //normal execution } @@ -403,7 +402,7 @@ public class MapMessageTest extends TestCase implements MessageListener m.getChar("long"); fail("Exception Expected."); } - catch (MessageFormatException nfe) + catch (MessageFormatException npe) { //normal execution } @@ -494,7 +493,7 @@ public class MapMessageTest extends TestCase implements MessageListener m.getChar("double"); fail("Exception Expected."); } - catch (MessageFormatException nfe) + catch (MessageFormatException npe) { //normal execution } @@ -587,7 +586,7 @@ public class MapMessageTest extends TestCase implements MessageListener m.getChar("float"); fail("Exception Expected."); } - catch (MessageFormatException nfe) + catch (MessageFormatException npe) { //normal execution } @@ -671,7 +670,7 @@ public class MapMessageTest extends TestCase implements MessageListener m.getChar("int"); fail("Exception Expected."); } - catch (MessageFormatException nfe) + catch (MessageFormatException npe) { //normal execution } @@ -843,7 +842,7 @@ public class MapMessageTest extends TestCase implements MessageListener m.getChar("bytes"); fail("Exception Expected."); } - catch (MessageFormatException nfe) + catch (MessageFormatException npe) { //normal execution } @@ -928,7 +927,7 @@ public class MapMessageTest extends TestCase implements MessageListener m.getChar("byte"); fail("Exception Expected."); } - catch (MessageFormatException nfe) + catch (MessageFormatException npe) { //normal execution } @@ -1005,7 +1004,7 @@ public class MapMessageTest extends TestCase implements MessageListener m.getChar("odd"); fail("Exception Expected."); } - catch (MessageFormatException nfe) + catch (MessageFormatException npe) { //normal execution } @@ -1249,6 +1248,6 @@ public class MapMessageTest extends TestCase implements MessageListener public static junit.framework.Test suite() { - return new VMBrokerSetup(new junit.framework.TestSuite(MapMessageTest.class)); + return new junit.framework.TestSuite(MapMessageTest.class); } } diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/basic/MultipleConnectionTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/MultipleConnectionTest.java index 80af81652e..c88024f39f 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/basic/MultipleConnectionTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/basic/MultipleConnectionTest.java @@ -6,9 +6,9 @@ * 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 @@ -19,18 +19,15 @@ */ package org.apache.qpid.test.unit.basic; +import junit.framework.TestCase; import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQDestination; import org.apache.qpid.client.AMQSession; import org.apache.qpid.client.AMQTopic; -import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException; import org.apache.qpid.client.transport.TransportConnection; -import org.apache.qpid.testutil.VMBrokerSetup; import javax.jms.*; -import junit.framework.TestCase; - public class MultipleConnectionTest extends TestCase { public static final String _defaultBroker = "vm://:1"; @@ -138,6 +135,19 @@ public class MultipleConnectionTest extends TestCase } } + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killAllVMBrokers(); + } + private static void waitForCompletion(int expected, long wait, Receiver[] receivers) throws InterruptedException { for (int i = 0; i < receivers.length; i++) @@ -209,6 +219,6 @@ public class MultipleConnectionTest extends TestCase public static junit.framework.Test suite() { - return new VMBrokerSetup(new junit.framework.TestSuite(MultipleConnectionTest.class)); + return new junit.framework.TestSuite(MultipleConnectionTest.class); } } diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/basic/PubSubTwoConnectionTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/PubSubTwoConnectionTest.java new file mode 100644 index 0000000000..f4814795c4 --- /dev/null +++ b/java/client/src/test/java/org/apache/qpid/test/unit/basic/PubSubTwoConnectionTest.java @@ -0,0 +1,67 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.basic; + +import junit.framework.TestCase; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.AMQTopic; + +import javax.jms.*; + +/** + * @author Apache Software Foundation + */ +public class PubSubTwoConnectionTest extends TestCase +{ + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + } + + /** + * This tests that a consumer is set up synchronously + * @throws Exception + */ + public void testTwoConnections() throws Exception + { + Topic topic = new AMQTopic("MyTopic"); + Connection con1 = new AMQConnection("vm://:1", "guest", "guest", "Client1", "/test_path"); + Session session1 = con1.createSession(false, AMQSession.NO_ACKNOWLEDGE); + MessageProducer producer = session1.createProducer(topic); + + Connection con2 = new AMQConnection("vm://:1", "guest", "guest", "Client2", "/test_path"); + Session session2 = con2.createSession(false, AMQSession.NO_ACKNOWLEDGE); + MessageConsumer consumer = session2.createConsumer(topic); + con2.start(); + producer.send(session1.createTextMessage("Hello")); + TextMessage tm1 = (TextMessage) consumer.receive(2000); + assertNotNull(tm1); + assertEquals("Hello", tm1.getText()); + } +} diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java index 04ad15da7a..903f6a9da9 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java @@ -21,11 +21,8 @@ package org.apache.qpid.test.unit.basic; import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.AMQDestination; import org.apache.qpid.client.AMQQueue; import org.apache.qpid.client.AMQSession; -import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException; -import org.apache.qpid.client.transport.TransportConnection; import org.apache.qpid.client.message.JMSTextMessage; import org.apache.qpid.testutil.VMBrokerSetup; import org.apache.log4j.Logger; diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/message/BytesMessageTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/message/BytesMessageTest.java index 7ffb3ca469..a0e4aa9787 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/client/message/BytesMessageTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/message/BytesMessageTest.java @@ -552,16 +552,6 @@ public class BytesMessageTest extends TestCase assertEquals((byte)0, result[2]); } - public void testToBodyString() throws Exception - { - JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); - final String testText = "This is a test"; - bm.writeUTF(testText); - bm.reset(); - String result = bm.toBodyString(); - assertEquals(testText, result); - } - public void testToBodyStringWithNull() throws Exception { JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/message/MapMessageTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/message/MapMessageTest.java index e5458fd89e..bd4b3b3987 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/client/message/MapMessageTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/message/MapMessageTest.java @@ -14,18 +14,16 @@ * "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. + * under the License. + * * - * */ package org.apache.qpid.test.unit.client.message; -import junit.framework.TestCase; import junit.framework.Assert; -import org.apache.qpid.framing.PropertyFieldTable; +import junit.framework.TestCase; import org.apache.qpid.client.message.JMSMapMessage; import org.apache.qpid.client.message.TestMessageHelper; -import org.apache.log4j.Logger; import javax.jms.JMSException; import javax.jms.MessageFormatException; @@ -104,6 +102,27 @@ public class MapMessageTest extends TestCase { Assert.fail("JMSException received." + e); } + + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + + mm.setString("value", null); + char c = mm.getChar("value"); + fail("Expected NullPointerException"); + + } + catch (NullPointerException e) + { + ; // pass + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + + + } public void testDoubleLookup() @@ -204,7 +223,7 @@ public class MapMessageTest extends TestCase { JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); mm.getByte("random"); - Assert.fail("MessageFormatException expected"); + Assert.fail("NumberFormatException expected"); } catch (NumberFormatException e) { @@ -331,7 +350,7 @@ public class MapMessageTest extends TestCase { JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); mm.getShort("random"); - Assert.fail("NumberFormatException should be received."); + Assert.fail("NumberFormatException should be received."); } catch (NumberFormatException e) { diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java index 07eedc8bb9..bbd1870168 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java @@ -167,6 +167,12 @@ public class ObjectMessageTest extends TestCase implements MessageListener } + public void testSetObjectForNull() throws Exception + { + ObjectMessage msg = session.createObjectMessage(); + msg.setObject(null); + assertNull(msg.getObject()); + } private void send() throws Exception diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/message/TextMessageTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/message/TextMessageTest.java index 337b0f3bbc..64d10fb13f 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/client/message/TextMessageTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/message/TextMessageTest.java @@ -7,9 +7,9 @@ * 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 @@ -22,8 +22,13 @@ package org.apache.qpid.test.unit.client.message; import org.apache.qpid.client.message.TestMessageHelper; import org.apache.qpid.client.message.JMSTextMessage; +import org.apache.qpid.client.message.JMSMapMessage; import junit.framework.TestCase; +import junit.framework.Assert; + +import javax.jms.JMSException; +import javax.jms.MessageFormatException; public class TextMessageTest extends TestCase { @@ -47,6 +52,248 @@ public class TextMessageTest extends TestCase assertEquals(val, "Banana"); } + + public void testBooleanPropertyLookup() + { + try + { + JMSTextMessage tm = TestMessageHelper.newJMSTextMessage(); + + tm.setBooleanProperty("value", true); + Assert.assertEquals(true, tm.getBooleanProperty("value")); + Assert.assertEquals("true", tm.getStringProperty("value")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testBytePropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.setByteProperty("value", Byte.MAX_VALUE); + + Assert.assertEquals(Byte.MAX_VALUE, mm.getByteProperty("value")); + Assert.assertEquals((short) Byte.MAX_VALUE, mm.getShortProperty("value")); + Assert.assertEquals(Byte.MAX_VALUE, mm.getIntProperty("value")); + Assert.assertEquals((long) Byte.MAX_VALUE, mm.getLongProperty("value")); + Assert.assertEquals("" + Byte.MAX_VALUE, mm.getStringProperty("value")); + + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testShortPropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.setShortProperty("value", Short.MAX_VALUE); + Assert.assertEquals(Short.MAX_VALUE, mm.getShortProperty("value")); + Assert.assertEquals((int) Short.MAX_VALUE, mm.getIntProperty("value")); + Assert.assertEquals((long) Short.MAX_VALUE, mm.getLongProperty("value")); + Assert.assertEquals("" + Short.MAX_VALUE, mm.getStringProperty("value")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testDoublePropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.setDoubleProperty("value", Double.MAX_VALUE); + Assert.assertEquals(Double.MAX_VALUE, mm.getDoubleProperty("value")); + Assert.assertEquals("" + Double.MAX_VALUE, mm.getStringProperty("value")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testFloatPropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.setFloatProperty("value", Float.MAX_VALUE); + Assert.assertEquals(Float.MAX_VALUE, mm.getFloatProperty("value")); + Assert.assertEquals((double) Float.MAX_VALUE, mm.getDoubleProperty("value")); + Assert.assertEquals("" + Float.MAX_VALUE, mm.getStringProperty("value")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testIntPropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.setIntProperty("value", Integer.MAX_VALUE); + Assert.assertEquals(Integer.MAX_VALUE, mm.getIntProperty("value")); + Assert.assertEquals((long) Integer.MAX_VALUE, mm.getLongProperty("value")); + Assert.assertEquals("" + Integer.MAX_VALUE, mm.getStringProperty("value")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testLongPropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.setLongProperty("value", Long.MAX_VALUE); + Assert.assertEquals(Long.MAX_VALUE, mm.getLongProperty("value")); + Assert.assertEquals("" + Long.MAX_VALUE, mm.getStringProperty("value")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + + // Failed Lookups + + public void testFailedBooleanPropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + Assert.assertEquals(false, mm.getBooleanProperty("int")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testFailedBytePropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getByteProperty("random"); + Assert.fail("NumberFormatException expected"); + } + catch (NumberFormatException e) + { + //normal execution + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + + } + + public void testFailedDoublePropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getDoubleProperty("random"); + Assert.fail("NullPointerException should be received."); + } + catch (NullPointerException e) + { + //normal execution + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + } + + public void testFailedFloatPropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getFloatProperty("random"); + Assert.fail("NullPointerException should be received."); + } + catch (NullPointerException e) + { + //normal execution + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + } + + public void testFailedIntPropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getIntProperty("random"); + Assert.fail("NumberFormatException should be received."); + } + catch (NumberFormatException e) + { + //normal execution + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + } + + public void testFailedLongPropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getLongProperty("random"); + Assert.fail("NumberFormatException should be received."); + } + catch (NumberFormatException e) + { + //normal execution + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + } + + public void testFailedShortPropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getShortProperty("random"); + Assert.fail("NumberFormatException should be received."); + } + catch (NumberFormatException e) + { + //normal execution + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + } + + public static junit.framework.Test suite() { return new junit.framework.TestSuite(TextMessageTest.class); diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java new file mode 100644 index 0000000000..1e13962f01 --- /dev/null +++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java @@ -0,0 +1,80 @@ +package org.apache.qpid.test.unit.client.temporaryqueue;
+
+import junit.framework.TestCase;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQAuthenticationException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQConnectionException;
+import org.apache.qpid.AMQUnresolvedAddressException;
+import org.apache.qpid.url.URLSyntaxException;
+
+import javax.jms.*;
+
+public class TemporaryQueueTest extends TestCase
+{
+
+ String _broker = "vm://:1";
+
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ TransportConnection.killAllVMBrokers();
+ }
+
+ protected Connection createConnection() throws AMQException, URLSyntaxException
+ {
+ return new AMQConnection(_broker, "guest", "guest",
+ "fred", "/test");
+ }
+
+ public void testTempoaryQueue() throws Exception
+ {
+ Connection conn = createConnection();
+ Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ TemporaryQueue queue = session.createTemporaryQueue();
+ assertNotNull(queue);
+ MessageProducer producer = session.createProducer(queue);
+ MessageConsumer consumer = session.createConsumer(queue);
+ conn.start();
+ producer.send(session.createTextMessage("hello"));
+ TextMessage tm = (TextMessage) consumer.receive(2000);
+ assertNotNull(tm);
+ assertEquals("hello",tm.getText());
+
+ try
+ {
+ queue.delete();
+ fail("Expected JMSException : should not be able to delete while there are active consumers");
+ }
+ catch(JMSException je)
+ {
+ ; //pass
+ }
+
+ consumer.close();
+
+ try
+ {
+ queue.delete();
+ }
+ catch(JMSException je)
+ {
+ fail("Unexpected Exception: " + je.getMessage());
+ }
+
+ conn.close();
+ }
+
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(TemporaryQueueTest.class);
+ }
+}
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/message/StreamMessageTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/message/StreamMessageTest.java index 50944730c3..315ba6ae4c 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/message/StreamMessageTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/message/StreamMessageTest.java @@ -73,7 +73,7 @@ public class StreamMessageTest extends TestCase MessageProducer mandatoryProducer = producerSession.createProducer(queue); // Third test - should be routed - _logger.info("Sending routable message"); + _logger.info("Sending isBound message"); StreamMessage msg = producerSession.createStreamMessage(); msg.setStringProperty("F1000","1"); diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java index fa46a4bcfb..14ceaa75f1 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java @@ -26,10 +26,8 @@ import org.apache.qpid.client.AMQSession; import org.apache.qpid.client.AMQTopic; import org.apache.qpid.client.transport.TransportConnection; -import javax.jms.TopicSession; -import javax.jms.TextMessage; -import javax.jms.TopicPublisher; -import javax.jms.MessageConsumer; +import javax.jms.*; + /** * @author Apache Software Foundation @@ -46,12 +44,126 @@ public class TopicSessionTest extends TestCase { super.tearDown(); TransportConnection.killAllVMBrokers(); + //Thread.sleep(2000); } - public void testTextMessageCreation() throws Exception + + public void testTopicSubscriptionUnsubscription() throws Exception { AMQTopic topic = new AMQTopic("MyTopic"); - AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "test", "/test"); + AMQConnection con = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "/test"); + TopicSession session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE); + TopicSubscriber sub = session1.createDurableSubscriber(topic,"subscription0"); + TopicPublisher publisher = session1.createPublisher(topic); + + con.start(); + + TextMessage tm = session1.createTextMessage("Hello"); + publisher.publish(tm); + + tm = (TextMessage) sub.receive(2000); + assertNotNull(tm); + + session1.unsubscribe("subscription0"); + + try + { + session1.unsubscribe("not a subscription"); + fail("expected InvalidDestinationException when unsubscribing from unknown subscription"); + } + catch(InvalidDestinationException e) + { + ; // PASS + } + catch(Exception e) + { + fail("expected InvalidDestinationException when unsubscribing from unknown subscription, got: " + e); + } + + con.close(); + } + + public void testSubscriptionNameReuseForDifferentTopicSingleConnection() throws Exception + { + subscriptionNameReuseForDifferentTopic(false); + } + + public void testSubscriptionNameReuseForDifferentTopicTwoConnections() throws Exception + { + subscriptionNameReuseForDifferentTopic(true); + } + + private void subscriptionNameReuseForDifferentTopic(boolean shutdown) throws Exception + { + AMQTopic topic = new AMQTopic("MyTopic1" + String.valueOf(shutdown)); + AMQTopic topic2 = new AMQTopic("MyOtherTopic1" + String.valueOf(shutdown)); + AMQConnection con = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "/test"); + TopicSession session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE); + TopicSubscriber sub = session1.createDurableSubscriber(topic, "subscription0"); + TopicPublisher publisher = session1.createPublisher(null); + + con.start(); + + publisher.publish(topic, session1.createTextMessage("hello")); + TextMessage m = (TextMessage) sub.receive(2000); + assertNotNull(m); + + if (shutdown) + { + session1.close(); + con.close(); + con = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "/test"); + con.start(); + session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE); + publisher = session1.createPublisher(null); + } + TopicSubscriber sub2 = session1.createDurableSubscriber(topic2, "subscription0"); + publisher.publish(topic, session1.createTextMessage("hello")); + if (!shutdown) + { + m = (TextMessage) sub.receive(2000); + assertNull(m); + } + publisher.publish(topic2, session1.createTextMessage("goodbye")); + m = (TextMessage) sub2.receive(2000); + assertNotNull(m); + assertEquals("goodbye", m.getText()); + con.close(); + } + + public void testUnsubscriptionAfterConnectionClose() throws Exception + { + AMQTopic topic = new AMQTopic("MyTopic3"); + AMQConnection con1 = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "/test"); + TopicSession session1 = con1.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE); + TopicPublisher publisher = session1.createPublisher(topic); + + AMQConnection con2 = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test2", "/test"); + TopicSession session2 = con2.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE); + TopicSubscriber sub = session2.createDurableSubscriber(topic, "subscription0"); + + con2.start(); + + publisher.publish(session1.createTextMessage("Hello")); + TextMessage tm = (TextMessage) sub.receive(2000); + assertNotNull(tm); + con2.close(); + publisher.publish(session1.createTextMessage("Hello2")); + con2 = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test2", "/test"); + session2 = con2.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE); + sub = session2.createDurableSubscriber(topic, "subscription0"); + con2.start(); + tm = (TextMessage) sub.receive(2000); + assertNotNull(tm); + assertEquals("Hello2", tm.getText()); + con1.close(); + con2.close(); + } + + public void testTextMessageCreation() throws Exception + { + AMQTopic topic = new AMQTopic("MyTopic4"); + AMQConnection con = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "/test"); TopicSession session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE); TopicPublisher publisher = session1.createPublisher(topic); MessageConsumer consumer1 = session1.createConsumer(topic); @@ -85,8 +197,83 @@ public class TopicSessionTest extends TestCase tm = (TextMessage) consumer1.receive(2000); assertNotNull(tm); assertEquals("Empty string not returned", "", msgText); + con.close(); } + public void testSendingSameMessage() throws Exception + { + AMQConnection conn = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "/test"); + TopicSession session = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); + TemporaryTopic topic = session.createTemporaryTopic(); + assertNotNull(topic); + TopicPublisher producer = session.createPublisher(topic); + MessageConsumer consumer = session.createConsumer(topic); + conn.start(); + TextMessage sentMessage = session.createTextMessage("Test Message"); + producer.send(sentMessage); + TextMessage receivedMessage = (TextMessage) consumer.receive(2000); + assertNotNull(receivedMessage); + assertEquals(sentMessage.getText(),receivedMessage.getText()); + producer.send(sentMessage); + receivedMessage = (TextMessage) consumer.receive(2000); + assertNotNull(receivedMessage); + assertEquals(sentMessage.getText(),receivedMessage.getText()); + + + } + + public void testTemporaryTopic() throws Exception + { + AMQConnection conn = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "/test"); + TopicSession session = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); + TemporaryTopic topic = session.createTemporaryTopic(); + assertNotNull(topic); + TopicPublisher producer = session.createPublisher(topic); + MessageConsumer consumer = session.createConsumer(topic); + conn.start(); + producer.send(session.createTextMessage("hello")); + TextMessage tm = (TextMessage) consumer.receive(2000); + assertNotNull(tm); + assertEquals("hello",tm.getText()); + + try + { + topic.delete(); + fail("Expected JMSException : should not be able to delete while there are active consumers"); + } + catch(JMSException je) + { + ; //pass + } + + consumer.close(); + + try + { + topic.delete(); + } + catch(JMSException je) + { + fail("Unexpected Exception: " + je.getMessage()); + } + + TopicSession session2 = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); + try + { + MessageConsumer consumer2 = session2.createConsumer(topic); + fail("Expected a JMSException when subscribing to a temporary topic created on adifferent session"); + } + catch (JMSException je) + { + ; // pass + } + + + + conn.close(); + } + + public static junit.framework.Test suite() { return new junit.framework.TestSuite(TopicSessionTest.class); diff --git a/java/client/src/old_test/java/org/apache/qpid/transacted/TransactedTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/transacted/TransactedTest.java index e858e1ad36..90a11307b8 100644 --- a/java/client/src/old_test/java/org/apache/qpid/transacted/TransactedTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/transacted/TransactedTest.java @@ -23,8 +23,6 @@ package org.apache.qpid.test.unit.transacted; import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQQueue; import org.apache.qpid.client.AMQSession; -import org.apache.qpid.client.transport.TransportConnection; -import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException; import org.apache.qpid.testutil.VMBrokerSetup; import javax.jms.*; diff --git a/java/common/pom.xml b/java/common/pom.xml index bbc70bf7f6..653b2a8a9d 100644 --- a/java/common/pom.xml +++ b/java/common/pom.xml @@ -90,5 +90,9 @@ <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> + <dependency> + <groupId>org.apache.geronimo.specs</groupId> + <artifactId>geronimo-jms_1.1_spec</artifactId> + </dependency> </dependencies> </project> diff --git a/java/common/src/main/java/org/apache/qpid/AMQPInvalidClassException.java b/java/common/src/main/java/org/apache/qpid/AMQPInvalidClassException.java new file mode 100644 index 0000000000..883e13e5e6 --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/AMQPInvalidClassException.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid; + + +public class AMQPInvalidClassException extends RuntimeException +{ + public AMQPInvalidClassException(String s) + { + super(s); + } +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java b/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java index 37efe1a34f..d829144b11 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java +++ b/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java @@ -24,7 +24,7 @@ import org.apache.mina.common.ByteBuffer; public abstract class AMQBody { - protected abstract byte getType(); + protected abstract byte getFrameType(); /** * Get the size of the body diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java b/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java index 21c4bc8d5b..e75f37d623 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java +++ b/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java @@ -45,7 +45,7 @@ public class AMQFrame extends AMQDataBlock implements EncodableAMQDataBlock public void writePayload(ByteBuffer buffer) { - buffer.put(bodyFrame.getType()); + buffer.put(bodyFrame.getFrameType()); // TODO: how does channel get populated EncodingUtils.writeUnsignedShort(buffer, channel); EncodingUtils.writeUnsignedInteger(buffer, bodyFrame.getSize()); diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java b/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java index c3e02aed56..6659b4ff8f 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java +++ b/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java @@ -42,7 +42,7 @@ public abstract class AMQMethodBody extends AMQBody protected abstract void writeMethodPayload(ByteBuffer buffer); - protected byte getType() + protected byte getFrameType() { return TYPE; } diff --git a/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java b/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java index a908c76286..61837f65cc 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java +++ b/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java @@ -57,6 +57,8 @@ public class BasicContentHeaderProperties implements ContentHeaderProperties private FieldTable _headers; + private JMSPropertyFieldTable _jmsHeaders; + private byte _deliveryMode; private byte _priority; @@ -276,6 +278,7 @@ public class BasicContentHeaderProperties implements ContentHeaderProperties if ((_propertyFlags & (1 << 13)) > 0) { _headers = EncodingUtils.readFieldTable(buffer); + setJMSHeaders(); } if ((_propertyFlags & (1 << 12)) > 0) { @@ -358,6 +361,8 @@ public class BasicContentHeaderProperties implements ContentHeaderProperties if ((_propertyFlags & (1 << 13)) > 0) { _headers = EncodingUtils.readFieldTable(buffer); + setJMSHeaders(); + } _decodedHeaders = true; } @@ -446,6 +451,26 @@ public class BasicContentHeaderProperties implements ContentHeaderProperties clearEncodedForm(); _propertyFlags |= (1 << 13); _headers = headers; + setJMSHeaders(); + } + + private void setJMSHeaders() + { + if (_jmsHeaders == null) + { + _jmsHeaders = new JMSPropertyFieldTable(_headers); + } + else + { + _jmsHeaders.setFieldTable(_headers); + } + } + + public JMSPropertyFieldTable getJMSHeaders() + { + //This will ensure we have a blank header + getHeaders(); + return _jmsHeaders; } public byte getDeliveryMode() diff --git a/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java b/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java index 2aae833fcd..3a2e4b3b3c 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java +++ b/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java @@ -28,7 +28,7 @@ public class ContentBody extends AMQBody public ByteBuffer payload; - protected byte getType() + protected byte getFrameType() { return TYPE; } diff --git a/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java b/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java index 46b933b2c3..a59869b1d8 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java +++ b/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java @@ -53,7 +53,7 @@ public class ContentHeaderBody extends AMQBody this.bodySize = bodySize; } - protected byte getType() + protected byte getFrameType() { return TYPE; } diff --git a/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java b/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java index 61a0787fcd..46dff9ffa8 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java +++ b/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java @@ -461,7 +461,7 @@ public class EncodingUtils //**** new methods - // BOOLEAN_PROPERTY_PREFIX + // AMQP_BOOLEAN_PROPERTY_PREFIX public static void writeBoolean(ByteBuffer buffer, Boolean aBoolean) { @@ -479,7 +479,7 @@ public class EncodingUtils return 1; } - // BYTE_PROPERTY_PREFIX + // AMQP_BYTE_PROPERTY_PREFIX public static void writeByte(ByteBuffer buffer, Byte aByte) { buffer.put(aByte); @@ -496,7 +496,7 @@ public class EncodingUtils } - // SHORT_PROPERTY_PREFIX + // AMQP_SHORT_PROPERTY_PREFIX public static void writeShort(ByteBuffer buffer, Short aShort) { buffer.putShort(aShort); @@ -528,7 +528,7 @@ public class EncodingUtils return 4; } - // LONG_PROPERTY_PREFIX + // AMQP_LONG_PROPERTY_PREFIX public static void writeLong(ByteBuffer buffer, Long aLong) { buffer.putLong(aLong); @@ -610,7 +610,7 @@ public class EncodingUtils } //CHAR_PROPERTY - public static long encodedCharLength() + public static int encodedCharLength() { return encodedByteLength(); } diff --git a/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java b/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java index 44d0268561..193c7adf1c 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java +++ b/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java @@ -93,4 +93,6 @@ public interface FieldTable extends Map public Object setObject(String string, Object object); + public boolean isNullStringValue(String name); + } diff --git a/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java b/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java index 6bbdaaeed5..7a160ef471 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java +++ b/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java @@ -27,7 +27,7 @@ public class HeartbeatBody extends AMQBody public static final byte TYPE = 8; public static AMQFrame FRAME = new HeartbeatBody().toFrame(); - protected byte getType() + protected byte getFrameType() { return TYPE; } diff --git a/java/common/src/main/java/org/apache/qpid/framing/JMSPropertyFieldTable.java b/java/common/src/main/java/org/apache/qpid/framing/JMSPropertyFieldTable.java new file mode 100644 index 0000000000..142a689a01 --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/framing/JMSPropertyFieldTable.java @@ -0,0 +1,471 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 +* + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.framing; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQPInvalidClassException; + +import javax.jms.MessageFormatException; +import javax.jms.JMSException; +import java.util.Enumeration; + + +public class JMSPropertyFieldTable +{ + private FieldTable _fieldtable; + + public JMSPropertyFieldTable() + { + _fieldtable = new PropertyFieldTable(); + } + + public JMSPropertyFieldTable(FieldTable table) + { + _fieldtable = table; + } + + public JMSPropertyFieldTable(ByteBuffer buffer, long length) throws JMSException + { + try + { + _fieldtable = new PropertyFieldTable(buffer, length); + } + catch (AMQFrameDecodingException e) + { + JMSException error = new JMSException(e.getMessage()); + error.setLinkedException(e); + throw error; + } + } + + private void checkPropertyName(String propertyName) + { + if (propertyName == null) + { + throw new IllegalArgumentException("Property name must not be null"); + } + else if ("".equals(propertyName)) + { + throw new IllegalArgumentException("Property name must not be the empty string"); + } + + checkIdentiferFormat(propertyName); + } + + protected static void checkIdentiferFormat(String propertyName) + { +// JMS requirements 3.5.1 Property Names +// Identifiers: +// - An identifier is an unlimited-length character sequence that must begin +// with a Java identifier start character; all following characters must be Java +// identifier part characters. An identifier start character is any character for +// which the method Character.isJavaIdentifierStart returns true. This includes +// '_' and '$'. An identifier part character is any character for which the +// method Character.isJavaIdentifierPart returns true. +// - Identifiers cannot be the names NULL, TRUE, or FALSE. +// – Identifiers cannot be NOT, AND, OR, BETWEEN, LIKE, IN, IS, or +// ESCAPE. +// – Identifiers are either header field references or property references. The +// type of a property value in a message selector corresponds to the type +// used to set the property. If a property that does not exist in a message is +// referenced, its value is NULL. The semantics of evaluating NULL values +// in a selector are described in Section 3.8.1.2, “Null Values.” +// – The conversions that apply to the get methods for properties do not +// apply when a property is used in a message selector expression. For +// example, suppose you set a property as a string value, as in the +// following: +// myMessage.setStringProperty("NumberOfOrders", "2"); +// The following expression in a message selector would evaluate to false, +// because a string cannot be used in an arithmetic expression: +// "NumberOfOrders > 1" +// – Identifiers are case sensitive. +// – Message header field references are restricted to JMSDeliveryMode, +// JMSPriority, JMSMessageID, JMSTimestamp, JMSCorrelationID, and +// JMSType. JMSMessageID, JMSCorrelationID, and JMSType values may be +// null and if so are treated as a NULL value. + + if (Boolean.getBoolean("strict-jms")) + { + // JMS start character + if (!(Character.isJavaIdentifierStart(propertyName.charAt(0)))) + { + throw new IllegalArgumentException("Identifier '" + propertyName + "' does not start with a valid JMS identifier start character"); + } + + // JMS part character + int length = propertyName.length(); + for (int c = 1; c < length; c++) + { + if (!(Character.isJavaIdentifierPart(propertyName.charAt(c)))) + { + throw new IllegalArgumentException("Identifier '" + propertyName + "' contains an invalid JMS identifier character"); + } + } + + // JMS invalid names + if ((propertyName.equals("NULL") + || propertyName.equals("TRUE") + || propertyName.equals("FALSE") + || propertyName.equals("NOT") + || propertyName.equals("AND") + || propertyName.equals("OR") + || propertyName.equals("BETWEEN") + || propertyName.equals("LIKE") + || propertyName.equals("IN") + || propertyName.equals("IS") + || propertyName.equals("ESCAPE"))) + { + throw new IllegalArgumentException("Identifier '" + propertyName + "' is not allowed in JMS"); + } + } + + } + + // MapMessage Interface + public boolean getBoolean(String string) throws JMSException + { + Boolean b = _fieldtable.getBoolean(string); + + if (b == null) + { + if (_fieldtable.containsKey(string)) + { + Object str = _fieldtable.getObject(string); + + if (str == null || !(str instanceof String)) + { + throw new MessageFormatException("getBoolean can't use " + string + " item."); + } + else + { + return Boolean.valueOf((String) str); + } + } + else + { + b = Boolean.valueOf(null); + } + } + + return b; + } + + public char getCharacter(String string) throws JMSException + { + Character c = _fieldtable.getCharacter(string); + + if (c == null) + { + if (_fieldtable.isNullStringValue(string)) + { + throw new NullPointerException("Cannot convert null char"); + } + else + { + throw new MessageFormatException("getChar can't use " + string + " item."); + } + } + else + { + return (char) c; + } + } + + public byte[] getBytes(String string) throws JMSException + { + byte[] bs = _fieldtable.getBytes(string); + + if (bs == null) + { + throw new MessageFormatException("getBytes can't use " + string + " item."); + } + else + { + return bs; + } + } + + public byte getByte(String string) throws JMSException + { + Byte b = _fieldtable.getByte(string); + if (b == null) + { + if (_fieldtable.containsKey(string)) + { + Object str = _fieldtable.getObject(string); + + if (str == null || !(str instanceof String)) + { + throw new MessageFormatException("getByte can't use " + string + " item."); + } + else + { + return Byte.valueOf((String) str); + } + } + else + { + b = Byte.valueOf(null); + } + } + + return b; + } + + public short getShort(String string) throws JMSException + { + Short s = _fieldtable.getShort(string); + + if (s == null) + { + s = Short.valueOf(getByte(string)); + } + + return s; + } + + public int getInteger(String string) throws JMSException + { + Integer i = _fieldtable.getInteger(string); + + if (i == null) + { + i = Integer.valueOf(getShort(string)); + } + + return i; + } + + public long getLong(String string) throws JMSException + { + Long l = _fieldtable.getLong(string); + + if (l == null) + { + l = Long.valueOf(getInteger(string)); + } + + return l; + } + + public float getFloat(String string) throws JMSException + { + Float f = _fieldtable.getFloat(string); + + if (f == null) + { + if (_fieldtable.containsKey(string)) + { + Object str = _fieldtable.getObject(string); + + if (str == null || !(str instanceof String)) + { + throw new MessageFormatException("getFloat can't use " + string + " item."); + } + else + { + return Float.valueOf((String) str); + } + } + else + { + f = Float.valueOf(null); + } + + } + + return f; + } + + public double getDouble(String string) throws JMSException + { + Double d = _fieldtable.getDouble(string); + + if (d == null) + { + d = Double.valueOf(getFloat(string)); + } + + return d; + } + + public String getString(String string) throws JMSException + { + String s = _fieldtable.getString(string); + + if (s == null) + { + if (_fieldtable.containsKey(string)) + { + Object o = _fieldtable.getObject(string); + if (o instanceof byte[]) + { + throw new MessageFormatException("getObject couldn't find " + string + " item."); + } + else + { + if (o == null) + { + return null; + } + else + { + s = String.valueOf(o); + } + } + } + } + + return s; + } + + public Object getObject(String string) throws JMSException + { + return _fieldtable.getObject(string); + } + + public void setBoolean(String string, boolean b) throws JMSException + { + checkPropertyName(string); + _fieldtable.setBoolean(string, b); + } + + public void setChar(String string, char c) throws JMSException + { + checkPropertyName(string); + _fieldtable.setChar(string, c); + } + + public Object setBytes(String string, byte[] bytes) + { + return _fieldtable.setBytes(string, bytes, 0, bytes.length); + } + + public Object setBytes(String string, byte[] bytes, int start, int length) + { + return _fieldtable.setBytes(string, bytes, start, length); + } + + public void setByte(String string, byte b) throws JMSException + { + checkPropertyName(string); + _fieldtable.setByte(string, b); + } + + public void setShort(String string, short i) throws JMSException + { + checkPropertyName(string); + _fieldtable.setShort(string, i); + } + + public void setInteger(String string, int i) throws JMSException + { + checkPropertyName(string); + _fieldtable.setInteger(string, i); + } + + public void setLong(String string, long l) throws JMSException + { + checkPropertyName(string); + _fieldtable.setLong(string, l); + } + + public void setFloat(String string, float v) throws JMSException + { + checkPropertyName(string); + _fieldtable.setFloat(string, v); + } + + public void setDouble(String string, double v) throws JMSException + { + checkPropertyName(string); + _fieldtable.setDouble(string, v); + } + + public void setString(String string, String string1) throws JMSException + { + checkPropertyName(string); + _fieldtable.setString(string, string1); + } + + public void setObject(String string, Object object) throws JMSException + { + checkPropertyName(string); + try + { + _fieldtable.setObject(string, object); + } + catch (AMQPInvalidClassException aice) + { + throw new MessageFormatException("Only primatives are allowed object is:" + object.getClass()); + } + } + + public boolean itemExists(String string) throws JMSException + { + return _fieldtable.containsKey(string); + } + + public void setFieldTable(FieldTable headers) + { + _fieldtable = headers; + } + + public Enumeration getPropertyNames() + { + return _fieldtable.getPropertyNames(); + } + + public void clear() + { + _fieldtable.clear(); + } + + public boolean propertyExists(String propertyName) + { + return _fieldtable.propertyExists(propertyName); + } + + public Object put(Object key, Object value) + { + return _fieldtable.put(key, value); + } + + public Object remove(String propertyName) + { + return _fieldtable.remove(propertyName); + } + + public boolean isEmpty() + { + return _fieldtable.isEmpty(); + } + + public void writeToBuffer(ByteBuffer data) + { + _fieldtable.writeToBuffer(data); + } + + public Enumeration getMapNames() + { + return getPropertyNames(); + } +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/PropertyFieldTable.java b/java/common/src/main/java/org/apache/qpid/framing/PropertyFieldTable.java index 8c9b5f3b4c..1292ff2f6e 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/PropertyFieldTable.java +++ b/java/common/src/main/java/org/apache/qpid/framing/PropertyFieldTable.java @@ -22,6 +22,7 @@ package org.apache.qpid.framing; import org.apache.log4j.Logger; import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQPInvalidClassException; import java.util.Collection; import java.util.Enumeration; @@ -31,38 +32,13 @@ import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import java.util.Vector; +import java.util.HashMap; //extends FieldTable public class PropertyFieldTable implements FieldTable { private static final Logger _logger = Logger.getLogger(PropertyFieldTable.class); - public static final char AMQP_DECIMAL_PROPERTY_PREFIX = 'D'; - public static final char AMQP_UNSIGNEDINT_PROPERTY_PREFIX = 'I'; - public static final char AMQP_TIMESTAMP_PROPERTY_PREFIX = 'T'; - public static final char AMQP_STRING_PROPERTY_PREFIX = 'S'; - public static final char AMQP_ASCII_CHARACTER_PROPERTY_PREFIX = 'k'; - public static final char AMQP_ASCII_STRING_PROPERTY_PREFIX = 'c'; - public static final char AMQP_WIDE_STRING_PROPERTY_PREFIX = 'C'; - public static final char AMQP_BINARY_PROPERTY_PREFIX = 'x'; - public static final char AMQP_NULL_STRING_PROPERTY_PREFIX = 'n'; - - public static final char BOOLEAN_PROPERTY_PREFIX = 't'; - public static final char BYTE_PROPERTY_PREFIX = 'b'; - public static final char SHORT_PROPERTY_PREFIX = 's'; - public static final char INT_PROPERTY_PREFIX = 'i'; - public static final char LONG_PROPERTY_PREFIX = 'l'; - public static final char FLOAT_PROPERTY_PREFIX = 'f'; - public static final char DOUBLE_PROPERTY_PREFIX = 'd'; - - public static final char NULL_STRING_PROPERTY_PREFIX = AMQP_NULL_STRING_PROPERTY_PREFIX; - public static final char STRING_PROPERTY_PREFIX = AMQP_STRING_PROPERTY_PREFIX; - public static final char CHAR_PROPERTY_PREFIX = AMQP_ASCII_CHARACTER_PROPERTY_PREFIX; - public static final char BYTES_PROPERTY_PREFIX = AMQP_BINARY_PROPERTY_PREFIX; - - //Our custom prefix for encoding across the wire - private static final char XML_PROPERTY_PREFIX = 'X'; - private static final String BOOLEAN = "boolean"; private static final String BYTE = "byte"; private static final String BYTES = "bytes"; @@ -72,6 +48,7 @@ public class PropertyFieldTable implements FieldTable private static final String FLOAT = "float"; private static final String DOUBLE = "double"; private static final String STRING = "string"; + private static final String NULL_STRING = "nullstring"; private static final String CHAR = "char"; private static final String UNKNOWN = "unknown type"; @@ -80,15 +57,67 @@ public class PropertyFieldTable implements FieldTable private static final String BYTES_CLOSE_XML = "</" + BYTES + ">"; private static final String BYTES_OPEN_XML_START = "<" + BYTES; + public static enum Prefix + { + //AMQP FieldTable Wire Types + AMQP_DECIMAL_PROPERTY_PREFIX('D'), + AMQP_UNSIGNED_SHORT_PROPERTY_PREFIX('S'), + AMQP_UNSIGNED_INT_PROPERTY_PREFIX('I'), + AMQP_UNSIGNED_LONG_PROPERTY_PREFIX('L'), + AMQP_DOUBLE_EXTTENDED_PROPERTY_PREFIX('D'), + + AMQP_TIMESTAMP_PROPERTY_PREFIX('T'), + AMQP_BINARY_PROPERTY_PREFIX('x'), + + //Strings + AMQP_ASCII_STRING_PROPERTY_PREFIX('c'), + AMQP_WIDE_STRING_PROPERTY_PREFIX('C'), + AMQP_NULL_STRING_PROPERTY_PREFIX('n'), + + //Java Primative Types + AMQP_BOOLEAN_PROPERTY_PREFIX('t'), + AMQP_BYTE_PROPERTY_PREFIX('b'), + AMQP_ASCII_CHARACTER_PROPERTY_PREFIX('k'), + AMQP_SHORT_PROPERTY_PREFIX('s'), + AMQP_INT_PROPERTY_PREFIX('i'), + AMQP_LONG_PROPERTY_PREFIX('l'), + AMQP_FLOAT_PROPERTY_PREFIX('f'), + AMQP_DOUBLE_PROPERTY_PREFIX('d'); + + private final char _identifier; + + Prefix(char identifier) + { + _identifier = identifier; + //_reverseTypeMap.put(identifier, this); + } + + public final char identifier() + { + return _identifier; + } + + } + + public static Map<Character, Prefix> _reverseTypeMap = new HashMap<Character, Prefix>(); + + static + { + for (Prefix p : Prefix.values()) + { + _reverseTypeMap.put(p.identifier(), p); + } + } + private LinkedHashMap<String, Object> _properties; - private LinkedHashMap<String, String> _propertyNamesTypeMap; - private long _encodedSize = 0;//EncodingUtils.unsignedIntegerLength(); + private LinkedHashMap<String, Prefix> _propertyNamesTypeMap; + private long _encodedSize = 0; public PropertyFieldTable() { super(); _properties = new LinkedHashMap<String, Object>(); - _propertyNamesTypeMap = new LinkedHashMap<String, String>(); + _propertyNamesTypeMap = new LinkedHashMap<String, Prefix>(); } public PropertyFieldTable(String textFormat) @@ -100,7 +129,8 @@ public class PropertyFieldTable implements FieldTable } catch (Exception e) { - _logger.error("Unable to decode PropertyFieldTable format:" + textFormat, e); + _logger.warn("Unable to decode PropertyFieldTable format:" + textFormat, e); + throw new IllegalArgumentException("Unable to decode PropertyFieldTable format:" + textFormat); } } @@ -118,17 +148,17 @@ public class PropertyFieldTable implements FieldTable } // ************ Getters - - private Object get(String propertyName, char prefix) + private Object get(String propertyName, Prefix prefix) { - String type = _propertyNamesTypeMap.get(propertyName); + //Retrieve the type associated with this name + Prefix type = _propertyNamesTypeMap.get(propertyName); if (type == null) { return null; } - if (type.equals("" + prefix)) + if (type.equals(prefix)) { return _properties.get(propertyName); } @@ -140,7 +170,7 @@ public class PropertyFieldTable implements FieldTable public Boolean getBoolean(String string) { - Object o = get(string, BOOLEAN_PROPERTY_PREFIX); + Object o = get(string, Prefix.AMQP_BOOLEAN_PROPERTY_PREFIX); if (o != null && o instanceof Boolean) { return (Boolean) o; @@ -153,7 +183,7 @@ public class PropertyFieldTable implements FieldTable public Byte getByte(String string) { - Object o = get(string, BYTE_PROPERTY_PREFIX); + Object o = get(string, Prefix.AMQP_BYTE_PROPERTY_PREFIX); if (o != null) { return (Byte) o; @@ -166,7 +196,7 @@ public class PropertyFieldTable implements FieldTable public Short getShort(String string) { - Object o = get(string, SHORT_PROPERTY_PREFIX); + Object o = get(string, Prefix.AMQP_SHORT_PROPERTY_PREFIX); if (o != null) { return (Short) o; @@ -179,7 +209,7 @@ public class PropertyFieldTable implements FieldTable public Integer getInteger(String string) { - Object o = get(string, INT_PROPERTY_PREFIX); + Object o = get(string, Prefix.AMQP_INT_PROPERTY_PREFIX); if (o != null) { return (Integer) o; @@ -192,7 +222,7 @@ public class PropertyFieldTable implements FieldTable public Long getLong(String string) { - Object o = get(string, LONG_PROPERTY_PREFIX); + Object o = get(string, Prefix.AMQP_LONG_PROPERTY_PREFIX); if (o != null) { return (Long) o; @@ -205,20 +235,20 @@ public class PropertyFieldTable implements FieldTable public Float getFloat(String string) { - Object o = get(string, FLOAT_PROPERTY_PREFIX); + Object o = get(string, Prefix.AMQP_FLOAT_PROPERTY_PREFIX); if (o != null) { return (Float) o; } else { - return null; //Float.valueOf(null); ??? + return null; } } public Double getDouble(String string) { - Object o = get(string, DOUBLE_PROPERTY_PREFIX); + Object o = get(string, Prefix.AMQP_DOUBLE_PROPERTY_PREFIX); if (o != null) { return (Double) o; @@ -231,50 +261,55 @@ public class PropertyFieldTable implements FieldTable public String getString(String string) { - Object o = get(string, STRING_PROPERTY_PREFIX); + Object o = get(string, Prefix.AMQP_ASCII_STRING_PROPERTY_PREFIX); if (o != null) { return (String) o; } else { - - - String type = _propertyNamesTypeMap.get(string); - - if (type == null || type.equals("" + NULL_STRING_PROPERTY_PREFIX)) + o = get(string, Prefix.AMQP_WIDE_STRING_PROPERTY_PREFIX); + if (o != null) { - return null; + return (String) o; } else { - char itype = type.charAt(0); - Object value = _properties.get(string); + Prefix type = _propertyNamesTypeMap.get(string); - switch (itype) + if (type == null || type.equals(Prefix.AMQP_NULL_STRING_PROPERTY_PREFIX)) { - case STRING_PROPERTY_PREFIX: - case BYTES_PROPERTY_PREFIX: - return null; - default: - case BYTE_PROPERTY_PREFIX: - case BOOLEAN_PROPERTY_PREFIX: - case SHORT_PROPERTY_PREFIX: - case INT_PROPERTY_PREFIX: - case LONG_PROPERTY_PREFIX: - case FLOAT_PROPERTY_PREFIX: - case DOUBLE_PROPERTY_PREFIX: - return String.valueOf(value); - case CHAR_PROPERTY_PREFIX: - if (value == null) - { - throw new NullPointerException("null char cannot be converted to String"); - } - else - { - return String.valueOf(value); - } + return null; + } + else + { + switch (type) + { + case AMQP_ASCII_STRING_PROPERTY_PREFIX: + case AMQP_WIDE_STRING_PROPERTY_PREFIX: + case AMQP_BINARY_PROPERTY_PREFIX: + return null; + default: + case AMQP_BYTE_PROPERTY_PREFIX: + case AMQP_BOOLEAN_PROPERTY_PREFIX: + case AMQP_SHORT_PROPERTY_PREFIX: + case AMQP_INT_PROPERTY_PREFIX: + case AMQP_LONG_PROPERTY_PREFIX: + case AMQP_FLOAT_PROPERTY_PREFIX: + case AMQP_DOUBLE_PROPERTY_PREFIX: + return String.valueOf(_properties.get(string)); + case AMQP_ASCII_CHARACTER_PROPERTY_PREFIX: + Object value = _properties.get(string); + if (value == null) + { + throw new NullPointerException("null char cannot be converted to String"); + } + else + { + return String.valueOf(value); + } + } } } } @@ -282,7 +317,7 @@ public class PropertyFieldTable implements FieldTable public Character getCharacter(String string) { - Object o = get(string, CHAR_PROPERTY_PREFIX); + Object o = get(string, Prefix.AMQP_ASCII_CHARACTER_PROPERTY_PREFIX); if (o != null) { return (Character) o; @@ -295,7 +330,7 @@ public class PropertyFieldTable implements FieldTable public byte[] getBytes(String string) { - Object o = get(string, BYTES_PROPERTY_PREFIX); + Object o = get(string, Prefix.AMQP_BINARY_PROPERTY_PREFIX); if (o != null) { return (byte[]) o; @@ -315,54 +350,62 @@ public class PropertyFieldTable implements FieldTable public Object setBoolean(String string, boolean b) { - return put(BOOLEAN_PROPERTY_PREFIX + string, b); + return put(Prefix.AMQP_BOOLEAN_PROPERTY_PREFIX, string, b); } public Object setByte(String string, byte b) { - return put(BYTE_PROPERTY_PREFIX + string, b); + return put(Prefix.AMQP_BYTE_PROPERTY_PREFIX, string, b); } public Object setShort(String string, short i) { - return put(SHORT_PROPERTY_PREFIX + string, i); + return put(Prefix.AMQP_SHORT_PROPERTY_PREFIX, string, i); } public Object setInteger(String string, int i) { - return put(INT_PROPERTY_PREFIX + string, i); + return put(Prefix.AMQP_INT_PROPERTY_PREFIX, string, i); } public Object setLong(String string, long l) { - return put(LONG_PROPERTY_PREFIX + string, l); + return put(Prefix.AMQP_LONG_PROPERTY_PREFIX, string, l); } public Object setFloat(String string, float v) { - return put(FLOAT_PROPERTY_PREFIX + string, v); + return put(Prefix.AMQP_FLOAT_PROPERTY_PREFIX, string, v); } public Object setDouble(String string, double v) { - return put(DOUBLE_PROPERTY_PREFIX + string, v); + return put(Prefix.AMQP_DOUBLE_PROPERTY_PREFIX, string, v); } public Object setString(String string, String string1) { if (string1 == null) { - return put(NULL_STRING_PROPERTY_PREFIX + string, null); + return put(Prefix.AMQP_NULL_STRING_PROPERTY_PREFIX, string, null); } else { - return put(STRING_PROPERTY_PREFIX + string, string1); + //FIXME: determine string encoding and set either WIDE or ASCII string +// if () + { + return put(Prefix.AMQP_WIDE_STRING_PROPERTY_PREFIX, string, string1); + } +// else +// { +// return put(Prefix.AMQP_ASCII_STRING_PROPERTY_PREFIX, string, string1); +// } } } public Object setChar(String string, char c) { - return put(CHAR_PROPERTY_PREFIX + string, c); + return put(Prefix.AMQP_ASCII_CHARACTER_PROPERTY_PREFIX, string, c); } public Object setBytes(String string, byte[] bytes) @@ -372,7 +415,7 @@ public class PropertyFieldTable implements FieldTable public Object setBytes(String string, byte[] bytes, int start, int length) { - return put(BYTES_PROPERTY_PREFIX + string, sizeByteArray(bytes, start, length)); + return put(Prefix.AMQP_BINARY_PROPERTY_PREFIX, string, sizeByteArray(bytes, start, length)); } private byte[] sizeByteArray(byte[] bytes, int start, int length) @@ -395,70 +438,53 @@ public class PropertyFieldTable implements FieldTable { return setBoolean(string, (Boolean) object); } - else + else if (object instanceof Byte) { - if (object instanceof Byte) - { - return setByte(string, (Byte) object); - } - else - { - if (object instanceof Short) - { - return setShort(string, (Short) object); - } - else - { - if (object instanceof Integer) - { - return setInteger(string, (Integer) object); - } - else - { - if (object instanceof Long) - { - return setLong(string, (Long) object); - } - else - { - if (object instanceof Float) - { - return setFloat(string, (Float) object); - } - else - { - if (object instanceof Double) - { - return setDouble(string, (Double) object); - } - else - { - if (object instanceof String) - { - return setString(string, (String) object); - } - else - { - if (object instanceof Character) - { - return setChar(string, (Character) object); - } - else - { - if (object instanceof byte[]) - { - return setBytes(string, (byte[]) object); - } - } - } - } - } - } - } - } - } + return setByte(string, (Byte) object); + } + else if (object instanceof Short) + { + return setShort(string, (Short) object); + } + else if (object instanceof Integer) + { + return setInteger(string, (Integer) object); + } + else if (object instanceof Long) + { + return setLong(string, (Long) object); + } + else if (object instanceof Float) + { + return setFloat(string, (Float) object); + } + else if (object instanceof Double) + { + return setDouble(string, (Double) object); + } + else if (object instanceof String) + { + return setString(string, (String) object); + } + else if (object instanceof Character) + { + return setChar(string, (Character) object); } - return null; + else if (object instanceof byte[]) + { + return setBytes(string, (byte[]) object); + } + + throw new AMQPInvalidClassException("Only Primatives objects allowed Object is:" + object.getClass()); + } + + + public boolean isNullStringValue(String name) + { + return _properties.containsKey(name) && (_properties.get(name) == null) && + _propertyNamesTypeMap.get(name).equals(Prefix.AMQP_NULL_STRING_PROPERTY_PREFIX); + + } // ***** Methods @@ -481,23 +507,12 @@ public class PropertyFieldTable implements FieldTable public boolean propertyExists(String propertyName) { - return _propertyNamesTypeMap.containsKey(propertyName); + return itemExists(propertyName); } public boolean itemExists(String string) { - Iterator keys = _properties.keySet().iterator(); - - while (keys.hasNext()) - { - String key = (String) keys.next(); - - if (key.endsWith(string)) - { - return true; - } - } - return false; + return _properties.containsKey(string); } public String toString() @@ -515,16 +530,9 @@ public class PropertyFieldTable implements FieldTable { final Map.Entry entry = (Map.Entry) it.next(); final String propertyName = (String) entry.getKey(); - if (propertyName == null) - { - buf.append("\nInternal error: Property with NULL key defined"); - } - else - { - buf.append('\n'); - buf.append(valueAsXML(table._propertyNamesTypeMap.get(propertyName) + propertyName, entry.getValue())); - } + buf.append('\n'); + buf.append(valueAsXML(table._propertyNamesTypeMap.get(propertyName), propertyName, entry.getValue())); } buf.append("\n"); buf.append(PROPERTY_FIELD_TABLE_CLOSE_XML); @@ -532,18 +540,14 @@ public class PropertyFieldTable implements FieldTable return buf.toString(); } - private static String valueAsXML(String name, Object value) + private static String valueAsXML(Prefix type, String propertyName, Object value) { - char propertyPrefix = name.charAt(0); - String propertyName = name.substring(1); - - StringBuffer buf = new StringBuffer(); // Start Tag - buf.append(propertyXML(name, true)); + buf.append(propertyXML(type, propertyName, true)); // Value - if (propertyPrefix == BYTES_PROPERTY_PREFIX) + if (type.equals(Prefix.AMQP_BINARY_PROPERTY_PREFIX)) { //remove '>' buf.deleteCharAt(buf.length() - 1); @@ -555,22 +559,19 @@ public class PropertyFieldTable implements FieldTable } else { - buf.append(String.valueOf(value)); + if (!type.equals(Prefix.AMQP_NULL_STRING_PROPERTY_PREFIX)) + { + buf.append(String.valueOf(value)); + } } - //End Tag - buf.append(propertyXML(name, false)); + buf.append(propertyXML(type, propertyName, false)); return buf.toString(); } - private Object checkPropertyName(String name) + private void checkPropertyName(String propertyName) { - String propertyName = name.substring(1); - char propertyPrefix = name.charAt(0); - - Object previous = null; - if (propertyName == null) { throw new IllegalArgumentException("Property name must not be null"); @@ -581,34 +582,11 @@ public class PropertyFieldTable implements FieldTable } checkIdentiferFormat(propertyName); - - String currentValue = _propertyNamesTypeMap.get(propertyName); - - if (currentValue != null) - { - previous = _properties.remove(currentValue + propertyName); - - // If we are in effect deleting the value (see comment on null values being deleted - // below) then we also need to remove the name from the encoding length. - if (previous == null) - { - _encodedSize -= EncodingUtils.encodedShortStringLength(propertyName); - } - - // FIXME: Should be able to short-cut this process if the old and new values are - // the same object and/or type and size... - _encodedSize -= getEncodingSize(currentValue + propertyName, previous) + 1; - } - - _propertyNamesTypeMap.put(propertyName, "" + propertyPrefix); - - return previous; } protected static void checkIdentiferFormat(String propertyName) { - // AMQP Spec: 4.2.5.5 Field Tables // Guidelines for implementers: // * Field names MUST start with a letter, '$' or '#' and may continue with @@ -619,96 +597,23 @@ public class PropertyFieldTable implements FieldTable // 503 (syntax error). Conformance test: amq_wlp_table_01. // * A peer MUST handle duplicate fields by using only the first instance. -// JMS requirements 3.5.1 Property Names -// Identifiers: -// - An identifier is an unlimited-length character sequence that must begin -// with a Java identifier start character; all following characters must be Java -// identifier part characters. An identifier start character is any character for -// which the method Character.isJavaIdentifierStart returns true. This includes -// '_' and '$'. An identifier part character is any character for which the -// method Character.isJavaIdentifierPart returns true. -// - Identifiers cannot be the names NULL, TRUE, or FALSE. -// – Identifiers cannot be NOT, AND, OR, BETWEEN, LIKE, IN, IS, or -// ESCAPE. -// – Identifiers are either header field references or property references. The -// type of a property value in a message selector corresponds to the type -// used to set the property. If a property that does not exist in a message is -// referenced, its value is NULL. The semantics of evaluating NULL values -// in a selector are described in Section 3.8.1.2, “Null Values.” -// – The conversions that apply to the get methods for properties do not -// apply when a property is used in a message selector expression. For -// example, suppose you set a property as a string value, as in the -// following: -// myMessage.setStringProperty("NumberOfOrders", "2"); -// The following expression in a message selector would evaluate to false, -// because a string cannot be used in an arithmetic expression: -// "NumberOfOrders > 1" -// – Identifiers are case sensitive. -// – Message header field references are restricted to JMSDeliveryMode, -// JMSPriority, JMSMessageID, JMSTimestamp, JMSCorrelationID, and -// JMSType. JMSMessageID, JMSCorrelationID, and JMSType values may be -// null and if so are treated as a NULL value. - - - if (Boolean.getBoolean("strict-jms")) - { - // JMS start character - if (!(Character.isJavaIdentifierStart(propertyName.charAt(0)))) - { - throw new IllegalArgumentException("Identifier '" + propertyName + "' does not start with a valid JMS identifier start character"); - } - - // JMS part character - int length = propertyName.length(); - for (int c = 1; c < length; c++) - { - if (!(Character.isJavaIdentifierPart(propertyName.charAt(c)))) - { - throw new IllegalArgumentException("Identifier '" + propertyName + "' contains an invalid JMS identifier character"); - } - } - - // JMS invalid names - if (!(propertyName.equals("NULL") - || propertyName.equals("TRUE") - || propertyName.equals("FALSE") - || propertyName.equals("NOT") - || propertyName.equals("AND") - || propertyName.equals("OR") - || propertyName.equals("BETWEEN") - || propertyName.equals("LIKE") - || propertyName.equals("IN") - || propertyName.equals("IS") - || propertyName.equals("ESCAPE"))) - { - throw new IllegalArgumentException("Identifier '" + propertyName + "' is not allowed in JMS"); - } - - } - else + // AMQP length limit + if (propertyName.length() > 128) { - // AMQP length limit - if (propertyName.length() > 128) - { - throw new IllegalArgumentException("AMQP limits property names to 128 characters"); - } - - // AMQ start character - if (!(Character.isLetter(propertyName.charAt(0)) - || propertyName.charAt(0) == '$' - || propertyName.charAt(0) == '#')) - { - throw new IllegalArgumentException("Identifier '" + propertyName + "' does not start with a valid AMQP start character"); - } + throw new IllegalArgumentException("AMQP limits property names to 128 characters"); } + // AMQ start character + if (!(Character.isLetter(propertyName.charAt(0)) + || propertyName.charAt(0) == '$' + || propertyName.charAt(0) == '#')) + { + throw new IllegalArgumentException("Identifier '" + propertyName + "' does not start with a valid AMQP start character"); + } } - private static String propertyXML(String name, boolean start) + private static String propertyXML(Prefix type, String propertyName, boolean start) { - char propertyPrefix = name.charAt(0); - String propertyName = name.substring(1); - StringBuffer buf = new StringBuffer(); if (start) @@ -720,40 +625,44 @@ public class PropertyFieldTable implements FieldTable buf.append("</"); } - switch (propertyPrefix) + switch (type) { - case BOOLEAN_PROPERTY_PREFIX: + case AMQP_BOOLEAN_PROPERTY_PREFIX: buf.append(BOOLEAN); break; - case BYTE_PROPERTY_PREFIX: + case AMQP_BYTE_PROPERTY_PREFIX: buf.append(BYTE); break; - case BYTES_PROPERTY_PREFIX: + case AMQP_BINARY_PROPERTY_PREFIX: buf.append(BYTES); break; - case SHORT_PROPERTY_PREFIX: + case AMQP_SHORT_PROPERTY_PREFIX: buf.append(SHORT); break; - case INT_PROPERTY_PREFIX: + case AMQP_INT_PROPERTY_PREFIX: buf.append(INT); break; - case LONG_PROPERTY_PREFIX: + case AMQP_LONG_PROPERTY_PREFIX: buf.append(LONG); break; - case FLOAT_PROPERTY_PREFIX: + case AMQP_FLOAT_PROPERTY_PREFIX: buf.append(FLOAT); break; - case DOUBLE_PROPERTY_PREFIX: + case AMQP_DOUBLE_PROPERTY_PREFIX: buf.append(DOUBLE); break; - case STRING_PROPERTY_PREFIX: + case AMQP_NULL_STRING_PROPERTY_PREFIX: + buf.append(NULL_STRING); + break; + case AMQP_ASCII_STRING_PROPERTY_PREFIX: + case AMQP_WIDE_STRING_PROPERTY_PREFIX: buf.append(STRING); break; - case CHAR_PROPERTY_PREFIX: + case AMQP_ASCII_CHARACTER_PROPERTY_PREFIX: buf.append(CHAR); break; default: - buf.append(UNKNOWN + " (identifier ").append(propertyPrefix).append(")"); + buf.append(UNKNOWN + " (identifier ").append(type.identifier()).append(")"); break; } @@ -774,9 +683,9 @@ public class PropertyFieldTable implements FieldTable for (int index = 0; index < bytes.length; index++) { buf.append("\n"); - buf.append(propertyXML(BYTE_PROPERTY_PREFIX + propertyName + "[" + index + "]", true)); + buf.append(propertyXML(Prefix.AMQP_BYTE_PROPERTY_PREFIX, propertyName + "[" + index + "]", true)); buf.append(bytes[index]); - buf.append(propertyXML(BYTE_PROPERTY_PREFIX + propertyName + "[" + index + "]", false)); + buf.append(propertyXML(Prefix.AMQP_BYTE_PROPERTY_PREFIX, propertyName + "[" + index + "]", false)); } buf.append("\n"); return buf.toString(); @@ -800,16 +709,26 @@ public class PropertyFieldTable implements FieldTable { StringTokenizer tokenizer = new StringTokenizer(textFormat, "\n"); + boolean finished = false; boolean processing = false; boolean processing_bytes = false; + if (!tokenizer.hasMoreTokens()) + { + throw new IllegalArgumentException("XML has no tokens to parse."); + } + while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); - if (token.equals(PROPERTY_FIELD_TABLE_CLOSE_XML) - || token.equals(BYTES_CLOSE_XML)) + if (token.equals(PROPERTY_FIELD_TABLE_CLOSE_XML)) + { + processing = false; + finished = true; + } + if (token.equals(BYTES_CLOSE_XML)) { processing = false; } @@ -840,6 +759,12 @@ public class PropertyFieldTable implements FieldTable processing = true; } } + + if (!finished) + { + throw new IllegalArgumentException("XML was not in a valid format."); + } + } private void processXMLLine(String xmlline) @@ -887,6 +812,7 @@ public class PropertyFieldTable implements FieldTable int byteStart = xmlline.indexOf('<', headerEnd); + //Don't think this is required. if (byteStart > 0) { while (!xmlline.startsWith(BYTES_CLOSE_XML, byteStart)) @@ -924,8 +850,12 @@ public class PropertyFieldTable implements FieldTable { setDouble(propertyName, Double.parseDouble(value)); } - if (type.equals(STRING)) + if (type.equals(STRING) || type.equals(NULL_STRING)) { + if (type.equals(NULL_STRING)) + { + value = null; + } setString(propertyName, value); } if (type.equals(CHAR)) @@ -934,7 +864,7 @@ public class PropertyFieldTable implements FieldTable } if (type.equals(UNKNOWN)) { - _logger.error("Ignoring unknown property value:" + xmlline); + _logger.warn("Ignoring unknown property value:" + xmlline); } } @@ -942,16 +872,14 @@ public class PropertyFieldTable implements FieldTable public void writeToBuffer(ByteBuffer buffer) { - final boolean debug = _logger.isDebugEnabled(); + final boolean trace = _logger.isTraceEnabled(); - if (debug) + if (trace) { - _logger.debug("FieldTable::writeToBuffer: Writing encoded size of " + _encodedSize + "..."); + _logger.trace("FieldTable::writeToBuffer: Writing encoded size of " + _encodedSize + "..."); } EncodingUtils.writeUnsignedInteger(buffer, _encodedSize); - //EncodingUtils.writeLong(buffer, _encodedSize); - putDataInBuffer(buffer); } @@ -1001,43 +929,39 @@ public class PropertyFieldTable implements FieldTable return setObject(key.toString(), value); } - protected Object put(String key, Object value) + protected Object put(Prefix type, String propertyName, Object value) { - Object previous = checkPropertyName(key); + checkPropertyName(propertyName); + //remove the previous value + Object previous = remove(propertyName); - String propertyName = key.substring(1); - char propertyPrefix = _propertyNamesTypeMap.get(propertyName).charAt(0); - if (_logger.isDebugEnabled()) + if (_logger.isTraceEnabled()) { - _logger.debug("Put:" + propertyName + - " encoding Now:" + _encodedSize + + int valueSize = 0; + if (value != null) + { + valueSize = getEncodingSize(type, value); + } + _logger.trace("Put:" + propertyName + + " encoding size Now:" + _encodedSize + " name size= " + EncodingUtils.encodedShortStringLength(propertyName) + - " value size= " + getEncodingSize(key, value)); + " value size= " + valueSize); } - // This prevents the item from being sent. - // JMS needs these propertyNames for lookups. - //if (value != null) - { - //Add the size of the propertyName - _encodedSize += EncodingUtils.encodedShortStringLength(propertyName); - - // For now: Setting a null value is the equivalent of deleting it. - // This is ambiguous in the JMS spec and needs thrashing out and potentially - // testing against other implementations. + //Add the size of the propertyName plus one for the type identifier + _encodedSize += EncodingUtils.encodedShortStringLength(propertyName) + 1; - //Add the size of the content plus one for the type identifier - _encodedSize += getEncodingSize(key, value) + 1; - } - - if (_logger.isDebugEnabled()) + if (value != null) { - _logger.debug("Put: new encodingSize " + _encodedSize); + //Add the size of the content + _encodedSize += getEncodingSize(type, value); } - _properties.put((String) propertyName, value); + //Store new values + _propertyNamesTypeMap.put(propertyName, type); + _properties.put(propertyName, value); return previous; } @@ -1047,14 +971,14 @@ public class PropertyFieldTable implements FieldTable if (_properties.containsKey(key)) { final Object value = _properties.remove(key); - String typePrefix = _propertyNamesTypeMap.remove(key); + Prefix type = _propertyNamesTypeMap.remove(key); // plus one for the type _encodedSize -= EncodingUtils.encodedShortStringLength(((String) key)) + 1; // This check is, for now, unnecessary (we don't store null values). if (value != null) { - _encodedSize -= getEncodingSize(typePrefix, value); + _encodedSize -= getEncodingSize(type, value); } return value; } @@ -1077,6 +1001,7 @@ public class PropertyFieldTable implements FieldTable public void clear() { + _encodedSize = 0; _properties.clear(); _propertyNamesTypeMap.clear(); } @@ -1113,93 +1038,82 @@ public class PropertyFieldTable implements FieldTable while (it.hasNext()) { - Map.Entry me = (Map.Entry) it.next(); String propertyName = (String) me.getKey(); //The type value - char propertyPrefix = _propertyNamesTypeMap.get(propertyName).charAt(0); + Prefix type = _propertyNamesTypeMap.get(propertyName); Object value = me.getValue(); try { - - if (_logger.isDebugEnabled()) + if (_logger.isTraceEnabled()) { - _logger.debug("Writing Property:" + propertyName + - " Type:" + propertyPrefix + + _logger.trace("Writing Property:" + propertyName + + " Type:" + type + " Value:" + value); - _logger.debug("Buffer Position:" + buffer.position() + + _logger.trace("Buffer Position:" + buffer.position() + " Remaining:" + buffer.remaining()); } - //The actual param name skipping type + //Write the actual parameter name EncodingUtils.writeShortStringBytes(buffer, propertyName); - - switch (propertyPrefix) + switch (type) { - - case BOOLEAN_PROPERTY_PREFIX: - buffer.put((byte) BOOLEAN_PROPERTY_PREFIX); + case AMQP_BOOLEAN_PROPERTY_PREFIX: + buffer.put((byte) Prefix.AMQP_BOOLEAN_PROPERTY_PREFIX.identifier()); EncodingUtils.writeBoolean(buffer, (Boolean) value); break; - case BYTE_PROPERTY_PREFIX: - buffer.put((byte) BYTE_PROPERTY_PREFIX); + case AMQP_BYTE_PROPERTY_PREFIX: + buffer.put((byte) Prefix.AMQP_BYTE_PROPERTY_PREFIX.identifier()); EncodingUtils.writeByte(buffer, (Byte) value); break; - case SHORT_PROPERTY_PREFIX: - buffer.put((byte) SHORT_PROPERTY_PREFIX); + case AMQP_SHORT_PROPERTY_PREFIX: + buffer.put((byte) Prefix.AMQP_SHORT_PROPERTY_PREFIX.identifier()); EncodingUtils.writeShort(buffer, (Short) value); break; - case INT_PROPERTY_PREFIX: - buffer.put((byte) INT_PROPERTY_PREFIX); + case AMQP_INT_PROPERTY_PREFIX: + buffer.put((byte) Prefix.AMQP_INT_PROPERTY_PREFIX.identifier()); EncodingUtils.writeInteger(buffer, (Integer) value); break; - case AMQP_UNSIGNEDINT_PROPERTY_PREFIX: // Currently we don't create these - buffer.put((byte) AMQP_UNSIGNEDINT_PROPERTY_PREFIX); + case AMQP_UNSIGNED_INT_PROPERTY_PREFIX: // Currently we don't create these + buffer.put((byte) Prefix.AMQP_UNSIGNED_INT_PROPERTY_PREFIX.identifier()); EncodingUtils.writeUnsignedInteger(buffer, (Long) value); break; - case LONG_PROPERTY_PREFIX: - buffer.put((byte) LONG_PROPERTY_PREFIX); + case AMQP_LONG_PROPERTY_PREFIX: + buffer.put((byte) Prefix.AMQP_LONG_PROPERTY_PREFIX.identifier()); EncodingUtils.writeLong(buffer, (Long) value); break; - case FLOAT_PROPERTY_PREFIX: - buffer.put((byte) FLOAT_PROPERTY_PREFIX); + case AMQP_FLOAT_PROPERTY_PREFIX: + buffer.put((byte) Prefix.AMQP_FLOAT_PROPERTY_PREFIX.identifier()); EncodingUtils.writeFloat(buffer, (Float) value); break; - case DOUBLE_PROPERTY_PREFIX: - buffer.put((byte) DOUBLE_PROPERTY_PREFIX); + case AMQP_DOUBLE_PROPERTY_PREFIX: + buffer.put((byte) Prefix.AMQP_DOUBLE_PROPERTY_PREFIX.identifier()); EncodingUtils.writeDouble(buffer, (Double) value); break; - case NULL_STRING_PROPERTY_PREFIX: - buffer.put((byte) NULL_STRING_PROPERTY_PREFIX); + case AMQP_NULL_STRING_PROPERTY_PREFIX: + buffer.put((byte) Prefix.AMQP_NULL_STRING_PROPERTY_PREFIX.identifier()); break; - case AMQP_WIDE_STRING_PROPERTY_PREFIX: - // TODO: look at using proper charset encoder - buffer.put((byte) STRING_PROPERTY_PREFIX); + buffer.put((byte) Prefix.AMQP_WIDE_STRING_PROPERTY_PREFIX.identifier()); + // FIXME: use proper charset encoder EncodingUtils.writeLongStringBytes(buffer, (String) value); break; case AMQP_ASCII_STRING_PROPERTY_PREFIX: - case STRING_PROPERTY_PREFIX: // AMQP_STRING_PROPERTY_PREFIX: //This is a simple ASCII string - buffer.put((byte) STRING_PROPERTY_PREFIX); + buffer.put((byte) Prefix.AMQP_ASCII_STRING_PROPERTY_PREFIX.identifier()); EncodingUtils.writeLongStringBytes(buffer, (String) value); break; - case CHAR_PROPERTY_PREFIX: - buffer.put((byte) CHAR_PROPERTY_PREFIX); + case AMQP_ASCII_CHARACTER_PROPERTY_PREFIX: + buffer.put((byte) Prefix.AMQP_ASCII_CHARACTER_PROPERTY_PREFIX.identifier()); EncodingUtils.writeChar(buffer, (Character) value); break; - case BYTES_PROPERTY_PREFIX: - buffer.put((byte) BYTES_PROPERTY_PREFIX); + case AMQP_BINARY_PROPERTY_PREFIX: + buffer.put((byte) Prefix.AMQP_BINARY_PROPERTY_PREFIX.identifier()); EncodingUtils.writeBytes(buffer, (byte[]) value); break; - case XML_PROPERTY_PREFIX: - // Encode as XML - buffer.put((byte) XML_PROPERTY_PREFIX); - EncodingUtils.writeLongStringBytes(buffer, valueAsXML(propertyPrefix + propertyName, value)); - break; default: { // Should never get here @@ -1209,172 +1123,158 @@ public class PropertyFieldTable implements FieldTable } catch (Exception e) { - if (_logger.isDebugEnabled()) + if (_logger.isTraceEnabled()) { - _logger.debug("Exception thrown:" + e); - _logger.debug("Writing Property:" + propertyName + - " Type:" + propertyPrefix + + _logger.trace("Exception thrown:" + e); + _logger.trace("Writing Property:" + propertyName + + " Type:" + type + " Value:" + value); - _logger.debug("Buffer Position:" + buffer.position() + + _logger.trace("Buffer Position:" + buffer.position() + " Remaining:" + buffer.remaining()); } throw new RuntimeException(e); } } - } public void setFromBuffer(ByteBuffer buffer, long length) throws AMQFrameDecodingException { - final boolean debug = _logger.isDebugEnabled(); + final boolean trace = _logger.isTraceEnabled(); int sizeRead = 0; while (sizeRead < length) { int sizeRemaining = buffer.remaining(); final String key = EncodingUtils.readShortString(buffer); - // TODO: use proper charset decoder + byte iType = buffer.get(); - final char type = (char) iType; - Object value = null; + + Character mapKey = new Character((char) iType); + Prefix type = _reverseTypeMap.get(mapKey); + + if (type == null) + { + String msg = "Field '" + key + "' - unsupported field table type: " + type + "."; + //some extra trace information... + msg += " (" + iType + "), length=" + length + ", sizeRead=" + sizeRead + ", sizeRemaining=" + sizeRemaining; + throw new AMQFrameDecodingException(msg); + } + Object value; switch (type) { - case BOOLEAN_PROPERTY_PREFIX: + case AMQP_BOOLEAN_PROPERTY_PREFIX: value = EncodingUtils.readBoolean(buffer); break; - case BYTE_PROPERTY_PREFIX: + case AMQP_BYTE_PROPERTY_PREFIX: value = EncodingUtils.readByte(buffer); break; - case SHORT_PROPERTY_PREFIX: + case AMQP_SHORT_PROPERTY_PREFIX: value = EncodingUtils.readShort(buffer); break; - case INT_PROPERTY_PREFIX: + case AMQP_INT_PROPERTY_PREFIX: value = EncodingUtils.readInteger(buffer); break; - case AMQP_UNSIGNEDINT_PROPERTY_PREFIX:// This will only fit in a long - case LONG_PROPERTY_PREFIX: + case AMQP_UNSIGNED_INT_PROPERTY_PREFIX:// This will only fit in a long + //Change this type for java lookups + type = Prefix.AMQP_LONG_PROPERTY_PREFIX; + case AMQP_LONG_PROPERTY_PREFIX: value = EncodingUtils.readLong(buffer); break; - case FLOAT_PROPERTY_PREFIX: + case AMQP_FLOAT_PROPERTY_PREFIX: value = EncodingUtils.readFloat(buffer); break; - case DOUBLE_PROPERTY_PREFIX: + case AMQP_DOUBLE_PROPERTY_PREFIX: value = EncodingUtils.readDouble(buffer); break; - - // TODO: use proper charset decoder case AMQP_WIDE_STRING_PROPERTY_PREFIX: + // FIXME: use proper charset encoder case AMQP_ASCII_STRING_PROPERTY_PREFIX: - case STRING_PROPERTY_PREFIX: // AMQP_STRING_PROPERTY_PREFIX: value = EncodingUtils.readLongString(buffer); break; - case NULL_STRING_PROPERTY_PREFIX: + case AMQP_NULL_STRING_PROPERTY_PREFIX: value = null; break; - case CHAR_PROPERTY_PREFIX: + case AMQP_ASCII_CHARACTER_PROPERTY_PREFIX: value = EncodingUtils.readChar((buffer)); break; - case BYTES_PROPERTY_PREFIX: + case AMQP_BINARY_PROPERTY_PREFIX: value = EncodingUtils.readBytes(buffer); break; - case XML_PROPERTY_PREFIX: - processXMLLine(EncodingUtils.readLongString(buffer)); - break; default: - String msg = "Field '" + key + "' - unsupported field table type: " + type + "."; - //some extra debug information... - msg += " (" + iType + "), length=" + length + ", sizeRead=" + sizeRead + ", sizeRemaining=" + sizeRemaining; + String msg = "Internal error, the following type identifier is not handled: " + type; throw new AMQFrameDecodingException(msg); } sizeRead += (sizeRemaining - buffer.remaining()); - if (debug) + if (trace) { - _logger.debug("FieldTable::PropFieldTable(buffer," + length + "): Read type '" + type + "', key '" + key + "', value '" + value + "' (now read " + sizeRead + " of " + length + " encoded bytes)..."); + _logger.trace("FieldTable::PropFieldTable(buffer," + length + "): Read type '" + type + "', key '" + key + "', value '" + value + "' (now read " + sizeRead + " of " + length + " encoded bytes)..."); } - if (type != XML_PROPERTY_PREFIX) - { - setObject(key, value); - if (value == null) - { - _logger.debug("setFromBuffer: value is null for key:" + key); - _propertyNamesTypeMap.put(key, "" + type); - _properties.put(key, null); - } - } + put(type, key, value); } - if (debug) + if (trace) { - _logger.debug("FieldTable::FieldTable(buffer," + length + "): Done."); + _logger.trace("FieldTable::FieldTable(buffer," + length + "): Done."); } } - /** - * @param name the property name with type prefix + * @param type the type to calucluate encoding for * @param value the property value * @return integer */ - private static int getEncodingSize(String name, Object value) + private static int getEncodingSize(Prefix type, Object value) { int encodingSize = 0; - char propertyPrefix = name.charAt(0); - - switch (propertyPrefix) + switch (type) { - case BOOLEAN_PROPERTY_PREFIX: - encodingSize += EncodingUtils.encodedBooleanLength(); + case AMQP_BOOLEAN_PROPERTY_PREFIX: + encodingSize = EncodingUtils.encodedBooleanLength(); break; - case BYTE_PROPERTY_PREFIX: - encodingSize += EncodingUtils.encodedByteLength(); + case AMQP_BYTE_PROPERTY_PREFIX: + encodingSize = EncodingUtils.encodedByteLength(); break; - case SHORT_PROPERTY_PREFIX: - encodingSize += EncodingUtils.encodedShortLength(); + case AMQP_SHORT_PROPERTY_PREFIX: + encodingSize = EncodingUtils.encodedShortLength(); break; - case INT_PROPERTY_PREFIX: - encodingSize += EncodingUtils.encodedIntegerLength(); + case AMQP_INT_PROPERTY_PREFIX: + encodingSize = EncodingUtils.encodedIntegerLength(); break; - case LONG_PROPERTY_PREFIX: - encodingSize += EncodingUtils.encodedLongLength(); + case AMQP_LONG_PROPERTY_PREFIX: + encodingSize = EncodingUtils.encodedLongLength(); break; - case FLOAT_PROPERTY_PREFIX: - encodingSize += EncodingUtils.encodedFloatLength(); + case AMQP_FLOAT_PROPERTY_PREFIX: + encodingSize = EncodingUtils.encodedFloatLength(); break; - case DOUBLE_PROPERTY_PREFIX: - encodingSize += EncodingUtils.encodedDoubleLength(); + case AMQP_DOUBLE_PROPERTY_PREFIX: + encodingSize = EncodingUtils.encodedDoubleLength(); break; case AMQP_WIDE_STRING_PROPERTY_PREFIX: + // FIXME: use proper charset encoder case AMQP_ASCII_STRING_PROPERTY_PREFIX: - case STRING_PROPERTY_PREFIX: //AMQP_STRING_PROPERTY_PREFIX: - encodingSize += EncodingUtils.encodedLongStringLength((String) value); - break; - case NULL_STRING_PROPERTY_PREFIX: - // There is no need for additional size beyond the prefix - break; - case CHAR_PROPERTY_PREFIX: - encodingSize += EncodingUtils.encodedCharLength(); + encodingSize = EncodingUtils.encodedLongStringLength((String) value); break; - case BYTES_PROPERTY_PREFIX: - encodingSize += 1 + ((byte[]) value).length; +// This is not required as this method is never called if the value is null +// case AMQP_NULL_STRING_PROPERTY_PREFIX: +// // There is no need for additional size beyond the prefix +// break; + case AMQP_ASCII_CHARACTER_PROPERTY_PREFIX: + encodingSize = EncodingUtils.encodedCharLength(); break; - case XML_PROPERTY_PREFIX: - encodingSize += EncodingUtils.encodedLongStringLength(valueAsXML(name, value)); + case AMQP_BINARY_PROPERTY_PREFIX: + encodingSize = 1 + ((byte[]) value).length; break; default: - //encodingSize = 1 + EncodingUtils.encodedLongstrLength(String.valueOf(value)); - // We are using XML String encoding throw new IllegalArgumentException("Unsupported type in field table: " + value.getClass()); } -// the extra byte for the type indicator is calculated in the name + // the extra byte for the type indicator is calculated in the name return encodingSize; } - - } diff --git a/java/common/src/main/java/org/apache/qpid/pool/Event.java b/java/common/src/main/java/org/apache/qpid/pool/Event.java index 71ab6e7863..7364b9293a 100644 --- a/java/common/src/main/java/org/apache/qpid/pool/Event.java +++ b/java/common/src/main/java/org/apache/qpid/pool/Event.java @@ -109,6 +109,6 @@ class Event public String toString() { - return "Event: type " + type + ", data: " + data; + return "Event: type " + type + ", data: " + data; } } diff --git a/java/common/src/test/java/org/apache/qpid/framing/JMSPropertyFieldTableTest.java b/java/common/src/test/java/org/apache/qpid/framing/JMSPropertyFieldTableTest.java new file mode 100644 index 0000000000..865735499b --- /dev/null +++ b/java/common/src/test/java/org/apache/qpid/framing/JMSPropertyFieldTableTest.java @@ -0,0 +1,1020 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.framing; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Map; +import java.util.HashMap; + +import org.apache.mina.common.ByteBuffer; +import org.apache.log4j.Logger; + +import javax.jms.JMSException; +import javax.jms.MessageFormatException; + +public class JMSPropertyFieldTableTest extends TestCase +{ + + private static final Logger _logger = Logger.getLogger(JMSPropertyFieldTableTest.class); + + + public void setUp() + { + System.getProperties().setProperty("strict-jms", "true"); + } + + public void tearDown() + { + System.getProperties().remove("strict-jms"); + } + + /** + * Test that setting a similar named value replaces any previous value set on that name + */ + public void testReplacement() throws JMSException + { + JMSPropertyFieldTable table1 = new JMSPropertyFieldTable(); + //Set a boolean value + table1.setBoolean("value", true); + + // reset value to an integer + table1.setInteger("value", Integer.MAX_VALUE); + + //Check boolean value is null + try + { + table1.getBoolean("value"); + } + catch (MessageFormatException mfe) + { + //normal execution + } + // ... and integer value is good + Assert.assertEquals(Integer.MAX_VALUE, table1.getInteger("value")); + } + + public void testRemoval() throws JMSException + { + JMSPropertyFieldTable table1 = new JMSPropertyFieldTable(); + //Set a boolean value + table1.setBoolean("value", true); + + Assert.assertTrue(table1.getBoolean("value")); + + table1.remove("value"); + + //Check boolean value is null + try + { + table1.getBoolean("value"); + } + catch (MessageFormatException mfe) + { + //normal execution + } + } + + + /** + * Set a boolean and check that we can only get it back as a boolean and a string + * Check that attempting to lookup a non existent value returns null + */ + public void testBoolean() throws JMSException + { + JMSPropertyFieldTable table1 = new JMSPropertyFieldTable(); + table1.setBoolean("value", true); + Assert.assertTrue(table1.propertyExists("value")); + + //Test Getting right value back + Assert.assertEquals(true, table1.getBoolean("value")); + + //Check we don't get anything back for other gets + try + { + table1.getByte("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + + try + { + table1.getByte("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + + try + { + table1.getShort("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + + try + { + table1.getDouble("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + + try + { + table1.getFloat("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + + try + { + table1.getInteger("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + + try + { + table1.getLong("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + + //except value as a string + Assert.assertEquals("true", table1.getString("value")); + + table1.remove("value"); + //but after a remove it doesn't + Assert.assertFalse(table1.propertyExists("value")); + + // Table should now have zero size for encoding + checkEmpty(table1); + + //Looking up an invalid value will return false + Assert.assertFalse(table1.getBoolean("Rubbish")); + } + + /** + * Set a byte and check that we can only get it back as a byte and a string + * Check that attempting to lookup a non existent value returns null + */ + public void testByte() throws JMSException + { + JMSPropertyFieldTable table1 = new JMSPropertyFieldTable(); + table1.setByte("value", Byte.MAX_VALUE); + Assert.assertTrue(table1.propertyExists("value")); + + //Tets lookups we shouldn't get anything back for other gets + //we should get right value back for this type .... + try + { + table1.getBoolean("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + + try + { + table1.getDouble("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getFloat("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + + Assert.assertEquals(Byte.MAX_VALUE, (byte) table1.getShort("value")); + Assert.assertEquals(Byte.MAX_VALUE, (byte) table1.getInteger("value")); + Assert.assertEquals(Byte.MAX_VALUE, (byte) table1.getLong("value")); + Assert.assertEquals(Byte.MAX_VALUE, table1.getByte("value")); + //... and a the string value of it. + Assert.assertEquals("" + Byte.MAX_VALUE, table1.getString("value")); + + table1.remove("value"); + //but after a remove it doesn't + Assert.assertFalse(table1.propertyExists("value")); + + // Table should now have zero size for encoding + checkEmpty(table1); + + //Looking up an invalid value returns null + try + { + table1.getByte("Rubbish"); + fail("Should throw NumberFormatException"); + } + catch (NumberFormatException mfs) + { + //normal Execution + } + + } + + + /** + * Set a short and check that we can only get it back as a short and a string + * Check that attempting to lookup a non existent value returns null + */ + public void testShort() throws JMSException + { + JMSPropertyFieldTable table1 = new JMSPropertyFieldTable(); + table1.setShort("value", Short.MAX_VALUE); + Assert.assertTrue(table1.propertyExists("value")); + + //Tets lookups we shouldn't get anything back for other gets + //we should get right value back for this type .... + + try + { + table1.getBoolean("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getByte("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + + try + { + table1.getDouble("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getFloat("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + + + Assert.assertEquals(Short.MAX_VALUE, (short) table1.getLong("value")); + Assert.assertEquals(Short.MAX_VALUE, (short) table1.getInteger("value")); + Assert.assertEquals(Short.MAX_VALUE, table1.getShort("value")); + + //... and a the string value of it. + Assert.assertEquals("" + Short.MAX_VALUE, table1.getString("value")); + + table1.remove("value"); + //but after a remove it doesn't + Assert.assertFalse(table1.propertyExists("value")); + + // Table should now have zero size for encoding + checkEmpty(table1); + + //Looking up an invalid value returns null + try + { + table1.getShort("Rubbish"); + fail("Should throw NumberFormatException"); + } + catch (NumberFormatException mfe) + { + //normal path + } + } + + + /** + * Set a double and check that we can only get it back as a double + * Check that attempting to lookup a non existent value returns null + */ + public void testDouble() throws JMSException + { + JMSPropertyFieldTable table1 = new JMSPropertyFieldTable(); + table1.setDouble("value", Double.MAX_VALUE); + Assert.assertTrue(table1.propertyExists("value")); + + //Tets lookups we shouldn't get anything back for other gets + //we should get right value back for this type .... + try + { + table1.getBoolean("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getByte("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getShort("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getFloat("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getInteger("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getLong("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + + Assert.assertEquals(Double.MAX_VALUE, table1.getDouble("value")); + //... and a the string value of it. + Assert.assertEquals("" + Double.MAX_VALUE, table1.getString("value")); + + table1.remove("value"); + //but after a remove it doesn't + Assert.assertFalse(table1.propertyExists("value")); + + // Table should now have zero size for encoding + checkEmpty(table1); + + //Looking up an invalid value returns null + try + { + table1.getDouble("Rubbish"); + fail("Should throw NullPointerException as float.valueOf will try sunreadJavaFormatString"); + } + catch (NullPointerException mfe) + { + //normal path + } + + } + + + /** + * Set a float and check that we can only get it back as a float + * Check that attempting to lookup a non existent value returns null + */ + public void testFloat() throws JMSException + { + JMSPropertyFieldTable table1 = new JMSPropertyFieldTable(); + table1.setFloat("value", Float.MAX_VALUE); + Assert.assertTrue(table1.propertyExists("value")); + + //Tets lookups we shouldn't get anything back for other gets + //we should get right value back for this type .... + try + { + table1.getBoolean("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getByte("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getShort("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getInteger("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getLong("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + + + Assert.assertEquals(Float.MAX_VALUE, table1.getFloat("value")); + Assert.assertEquals(Float.MAX_VALUE, (float) table1.getDouble("value")); + + //... and a the string value of it. + Assert.assertEquals("" + Float.MAX_VALUE, table1.getString("value")); + + table1.remove("value"); + //but after a remove it doesn't + Assert.assertFalse(table1.propertyExists("value")); + + // Table should now have zero size for encoding + checkEmpty(table1); + + //Looking up an invalid value returns null + try + { + table1.getFloat("Rubbish"); + fail("Should throw NullPointerException as float.valueOf will try sunreadJavaFormatString"); + } + catch (NullPointerException mfe) + { + //normal path + } + } + + + /** + * Set an int and check that we can only get it back as an int + * Check that attempting to lookup a non existent value returns null + */ + public void testInt() throws JMSException + { + JMSPropertyFieldTable table1 = new JMSPropertyFieldTable(); + table1.setInteger("value", Integer.MAX_VALUE); + Assert.assertTrue(table1.propertyExists("value")); + + //Tets lookups we shouldn't get anything back for other gets + //we should get right value back for this type .... + try + { + table1.getBoolean("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getByte("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getShort("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getDouble("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getFloat("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + + + Assert.assertEquals(Integer.MAX_VALUE, table1.getLong("value")); + + Assert.assertEquals(Integer.MAX_VALUE, table1.getInteger("value")); + + //... and a the string value of it. + Assert.assertEquals("" + Integer.MAX_VALUE, table1.getString("value")); + + table1.remove("value"); + //but after a remove it doesn't + Assert.assertFalse(table1.propertyExists("value")); + + // Table should now have zero size for encoding + checkEmpty(table1); + + //Looking up an invalid value returns null + try + { + table1.getInteger("Rubbish"); + fail("Should throw NumberFormatException"); + } + catch (NumberFormatException mfe) + { + //normal path + } + } + + + /** + * Set a long and check that we can only get it back as a long + * Check that attempting to lookup a non existent value returns null + */ + public void testLong() throws JMSException + { + JMSPropertyFieldTable table1 = new JMSPropertyFieldTable(); + table1.setLong("value", Long.MAX_VALUE); + Assert.assertTrue(table1.propertyExists("value")); + + //Tets lookups we shouldn't get anything back for other gets + //we should get right value back for this type .... + try + { + table1.getBoolean("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getByte("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getShort("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getDouble("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getFloat("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + try + { + table1.getInteger("value"); + fail("Should throw MessageFormatException"); + } + catch (MessageFormatException mfs) + { + //normal Execution + } + + + Assert.assertEquals(Long.MAX_VALUE, table1.getLong("value")); + + //... and a the string value of it. + Assert.assertEquals("" + Long.MAX_VALUE, table1.getString("value")); + + table1.remove("value"); + //but after a remove it doesn't + Assert.assertFalse(table1.propertyExists("value")); + + // Table should now have zero size for encoding + checkEmpty(table1); + + //Looking up an invalid value + try + { + table1.getLong("Rubbish"); + fail("Should throw NumberFormatException"); + } + catch (NumberFormatException mfs) + { + //normal Execution + } + + } + + + /** + * Calls all methods that can be used to check the table is empty + * - getEncodedSize + * - isEmpty + * - size + * + * @param table to check is empty + */ + private void checkEmpty(JMSPropertyFieldTable table) + { + Assert.assertFalse(table.getPropertyNames().hasMoreElements()); + } + + + /** + * Set a String and check that we can only get it back as a String + * Check that attempting to lookup a non existent value returns null + */ + public void testString() throws JMSException + { + JMSPropertyFieldTable table1 = new JMSPropertyFieldTable(); + table1.setString("value", "Hello"); + Assert.assertTrue(table1.propertyExists("value")); + + //Tets lookups we shouldn't get anything back for other gets + //we should get right value back for this type .... + Assert.assertEquals(false, table1.getBoolean("value")); + + try + { + table1.getByte("value"); + fail("Should throw NumberFormatException"); + } + catch (NumberFormatException nfs) + { + //normal Execution + } + try + { + table1.getShort("value"); + fail("Should throw NumberFormatException"); + } + catch (NumberFormatException nfs) + { + //normal Execution + } + try + { + table1.getDouble("value"); + fail("Should throw NumberFormatException"); + } + catch (NumberFormatException nfs) + { + //normal Execution + } + try + { + table1.getFloat("value"); + fail("Should throw NumberFormatException"); + } + catch (NumberFormatException nfs) + { + //normal Execution + } + try + { + table1.getInteger("value"); + fail("Should throw NumberFormatException"); + } + catch (NumberFormatException nfs) + { + //normal Execution + } + try + { + table1.getLong("value"); + fail("Should throw NumberFormatException"); + } + catch (NumberFormatException nfs) + { + //normal Execution + } + + Assert.assertEquals("Hello", table1.getString("value")); + + table1.remove("value"); + //but after a remove it doesn't + Assert.assertFalse(table1.propertyExists("value")); + + checkEmpty(table1); + + //Looking up an invalid value returns null + Assert.assertEquals(null, table1.getString("Rubbish")); + + //Additional Test that haven't been covered for string + table1.setObject("value", "Hello"); + //Check that it was set correctly + Assert.assertEquals("Hello", table1.getString("value")); + } + + + public void testValues() throws JMSException + { + JMSPropertyFieldTable table = new JMSPropertyFieldTable(); + table.setBoolean("bool", true); + table.setDouble("double", Double.MAX_VALUE); + table.setFloat("float", Float.MAX_VALUE); + table.setInteger("int", Integer.MAX_VALUE); + table.setLong("long", Long.MAX_VALUE); + table.setShort("short", Short.MAX_VALUE); + table.setString("string", "Hello"); + table.setString("nullstring", null); + + table.setObject("objectbool", true); + table.setObject("objectdouble", Double.MAX_VALUE); + table.setObject("objectfloat", Float.MAX_VALUE); + table.setObject("objectint", Integer.MAX_VALUE); + table.setObject("objectlong", Long.MAX_VALUE); + table.setObject("objectshort", Short.MAX_VALUE); + table.setObject("objectstring", "Hello"); + + + Assert.assertEquals(true, table.getBoolean("bool")); + + Assert.assertEquals(Double.MAX_VALUE, table.getDouble("double")); + Assert.assertEquals(Float.MAX_VALUE, table.getFloat("float")); + Assert.assertEquals(Integer.MAX_VALUE, table.getInteger("int")); + Assert.assertEquals(Long.MAX_VALUE, table.getLong("long")); + Assert.assertEquals(Short.MAX_VALUE, table.getShort("short")); + Assert.assertEquals("Hello", table.getString("string")); + Assert.assertEquals(null, table.getString("null-string")); + + Assert.assertEquals(true, table.getObject("objectbool")); + Assert.assertEquals(Double.MAX_VALUE, table.getObject("objectdouble")); + Assert.assertEquals(Float.MAX_VALUE, table.getObject("objectfloat")); + Assert.assertEquals(Integer.MAX_VALUE, table.getObject("objectint")); + Assert.assertEquals(Long.MAX_VALUE, table.getObject("objectlong")); + Assert.assertEquals(Short.MAX_VALUE, table.getObject("objectshort")); + Assert.assertEquals("Hello", table.getObject("objectstring")); + } + + /** + * Additional test checkPropertyName doesn't accept Null + */ + public void testCheckPropertyNameasNull() throws JMSException + { + JMSPropertyFieldTable table = new JMSPropertyFieldTable(); + + try + { + table.setObject(null, "String"); + fail("Null property name is not allowed"); + } + catch (IllegalArgumentException iae) + { + //normal path + } + checkEmpty(table); + } + + + /** + * Additional test checkPropertyName doesn't accept an empty String + */ + public void testCheckPropertyNameasEmptyString() throws JMSException + { + JMSPropertyFieldTable table = new JMSPropertyFieldTable(); + + try + { + table.setObject("", "String"); + fail("empty property name is not allowed"); + } + catch (IllegalArgumentException iae) + { + //normal path + } + checkEmpty(table); + } + + + /** + * Additional test checkPropertyName doesn't accept an empty String + */ + public void testCheckPropertyNamehasMaxLength() throws JMSException + { + JMSPropertyFieldTable table = new JMSPropertyFieldTable(); + + StringBuffer longPropertyName = new StringBuffer(129); + + for (int i = 0; i < 129; i++) + { + longPropertyName.append("x"); + } + + try + { + table.setObject(longPropertyName.toString(), "String"); + fail("property name must be < 128 characters"); + } + catch (IllegalArgumentException iae) + { + _logger.warn("JMS requires infinite property names AMQP limits us to 128 characters"); + } + + checkEmpty(table); + } + + + /** + * Additional test checkPropertyName starts with a letter + */ + public void testCheckPropertyNameStartCharacterIsLetter() throws JMSException + { + JMSPropertyFieldTable table = new JMSPropertyFieldTable(); + + //Try a name that starts with a number + try + { + table.setObject("1", "String"); + fail("property name must start with a letter"); + } + catch (IllegalArgumentException iae) + { + //normal path + } + + checkEmpty(table); + } + + /** + * Additional test checkPropertyName starts with a letter + */ + public void testCheckPropertyNameContainsInvalidCharacter() throws JMSException + { + JMSPropertyFieldTable table = new JMSPropertyFieldTable(); + + //Try a name that starts with a number + try + { + table.setObject("hello there", "String"); + fail("property name cannot contain spaces"); + } + catch (IllegalArgumentException iae) + { + //normal path + } + + checkEmpty(table); + } + + + /** + * Additional test checkPropertyName starts with a letter + */ + public void testCheckPropertyNameIsInvalid() throws JMSException + { + JMSPropertyFieldTable table = new JMSPropertyFieldTable(); + + //Try a name that starts with a number + try + { + table.setObject("ESCAPE", "String"); + fail("property name must not contains spaces"); + } + catch (IllegalArgumentException iae) + { + //normal path + } + + checkEmpty(table); + } + + /** + * Additional test checkPropertyName starts with a hash or a dollar + */ + public void testCheckPropertyNameStartCharacterIsHashorDollar() throws JMSException + { + _logger.warn("Test:testCheckPropertyNameStartCharacterIsHashorDollar will fail JMS compilance as # and $ are not valid in a jms identifier"); +// JMSPropertyFieldTable table = new JMSPropertyFieldTable(); +// +// //Try a name that starts with a number +// try +// { +// table.setObject("#", "String"); +// table.setObject("$", "String"); +// } +// catch (IllegalArgumentException iae) +// { +// fail("property name are allowed to start with # and $s in AMQP"); +// } + } + + /** + * Test the contents of the sets + */ + public void testSets() + { + + JMSPropertyFieldTable table = new JMSPropertyFieldTable(); + + table.put("n1", "1"); + table.put("n2", "2"); + table.put("n3", "3"); + + Enumeration enumerator = table.getPropertyNames(); + Assert.assertEquals("n1", enumerator.nextElement()); + Assert.assertEquals("n2", enumerator.nextElement()); + Assert.assertEquals("n3", enumerator.nextElement()); + Assert.assertFalse(enumerator.hasMoreElements()); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(JMSPropertyFieldTableTest.class); + } + +} diff --git a/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java b/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java index f2067a5649..5256c62054 100644 --- a/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java +++ b/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java @@ -24,14 +24,22 @@ import junit.framework.Assert; import junit.framework.TestCase; import java.util.Enumeration; +import java.util.Iterator; +import java.util.Map; +import java.util.HashMap; import org.apache.mina.common.ByteBuffer; +import org.apache.log4j.Logger; +import org.apache.qpid.AMQPInvalidClassException; public class PropertyFieldTableTest extends TestCase { - //Test byte modification + private static final Logger _logger = Logger.getLogger(PropertyFieldTableTest.class); + /** + * Test that modifying a byte[] after setting property doesn't change property + */ public void testByteModification() { PropertyFieldTable table = new PropertyFieldTable(); @@ -46,197 +54,518 @@ public class PropertyFieldTableTest extends TestCase assertBytesNotEqual(bytes, table.getBytes("bytes")); } - //Test replacement - + /** + * Test that setting a similar named value replaces any previous value set on that name + */ public void testReplacement() { PropertyFieldTable table1 = new PropertyFieldTable(); + //Set a boolean value table1.setBoolean("value", true); + //Check size of table is correct (<Value length> + <type> + <Boolean length>) + int size = EncodingUtils.encodedShortStringLength("value") + 1 + EncodingUtils.encodedBooleanLength(); + Assert.assertEquals(size, table1.getEncodedSize()); + + // reset value to an integer table1.setInteger("value", Integer.MAX_VALUE); + + // Check the size has changed accordingly (<Value length> + <type> + <Integer length>) + size = EncodingUtils.encodedShortStringLength("value") + 1 + EncodingUtils.encodedIntegerLength(); + Assert.assertEquals(size, table1.getEncodedSize()); + + //Check boolean value is null Assert.assertEquals(null, table1.getBoolean("value")); + // ... and integer value is good Assert.assertEquals((Integer) Integer.MAX_VALUE, table1.getInteger("value")); } - //Test Lookups - public void testBooleanLookup() + /** + * Set a boolean and check that we can only get it back as a boolean and a string + * Check that attempting to lookup a non existent value returns null + */ + public void testBoolean() { PropertyFieldTable table1 = new PropertyFieldTable(); table1.setBoolean("value", true); + Assert.assertTrue(table1.propertyExists("value")); + //Test Getting right value back Assert.assertEquals((Boolean) true, table1.getBoolean("value")); - //Looking up an invalid value returns null - Assert.assertTrue(table1.getBoolean("Rubbish") == null); - - //Try reading value as a string + //Check we don't get anything back for other gets + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(null, table1.getShort("value")); + Assert.assertEquals(null, table1.getCharacter("value")); + Assert.assertEquals(null, table1.getDouble("value")); + Assert.assertEquals(null, table1.getFloat("value")); + Assert.assertEquals(null, table1.getInteger("value")); + Assert.assertEquals(null, table1.getLong("value")); + Assert.assertEquals(null, table1.getBytes("value")); + + //except value as a string Assert.assertEquals("true", table1.getString("value")); + + //Try setting a null value and read it back + table1.put(PropertyFieldTable.Prefix.AMQP_BOOLEAN_PROPERTY_PREFIX, "value", null); + + // Should be able to get the null back + Assert.assertEquals(null, table1.getBoolean("value")); + //but still contains the value + Assert.assertTrue(table1.containsKey("value")); + + table1.remove("value"); + //but after a remove it doesn't + Assert.assertFalse(table1.containsKey("value")); + + // Table should now have zero size for encoding + checkEmpty(table1); + + //Looking up an invalid value returns null + Assert.assertEquals(null, table1.getBoolean("Rubbish")); } - public void testByteLookup() + /** + * Set a byte and check that we can only get it back as a byte and a string + * Check that attempting to lookup a non existent value returns null + */ + public void testByte() { PropertyFieldTable table1 = new PropertyFieldTable(); - table1.setByte("value", (byte) 1); - Assert.assertEquals((Byte) (byte) 1, table1.getByte("value")); + table1.setByte("value", Byte.MAX_VALUE); + Assert.assertTrue(table1.propertyExists("value")); - //Looking up an invalid value returns null - Assert.assertTrue(table1.getByte("Rubbish") == null); + //Tets lookups we shouldn't get anything back for other gets + //we should get right value back for this type .... + Assert.assertEquals(null, table1.getBoolean("value")); + Assert.assertEquals(Byte.MAX_VALUE, (byte) table1.getByte("value")); + Assert.assertEquals(null, table1.getShort("value")); + Assert.assertEquals(null, table1.getCharacter("value")); + Assert.assertEquals(null, table1.getDouble("value")); + Assert.assertEquals(null, table1.getFloat("value")); + Assert.assertEquals(null, table1.getInteger("value")); + Assert.assertEquals(null, table1.getLong("value")); + Assert.assertEquals(null, table1.getBytes("value")); + + //... and a the string value of it. + Assert.assertEquals("" + Byte.MAX_VALUE, table1.getString("value")); + + //Try setting a null value and read it back + table1.put(PropertyFieldTable.Prefix.AMQP_BYTE_PROPERTY_PREFIX, "value", null); + + // Should be able to get the null back + Assert.assertEquals(null, table1.getByte("value")); - //Try reading value as a string - Assert.assertEquals("1", table1.getString("value")); + //but still contains the value + Assert.assertTrue(table1.containsKey("value")); + + table1.remove("value"); + //but after a remove it doesn't + Assert.assertFalse(table1.containsKey("value")); + + // Table should now have zero size for encoding + checkEmpty(table1); + + //Looking up an invalid value returns null + Assert.assertEquals(null, table1.getByte("Rubbish")); } - public void testShortLookup() + /** + * Set a short and check that we can only get it back as a short and a string + * Check that attempting to lookup a non existent value returns null + */ + public void testShort() { PropertyFieldTable table1 = new PropertyFieldTable(); table1.setShort("value", Short.MAX_VALUE); - Assert.assertEquals((Short) Short.MAX_VALUE, table1.getShort("value")); + Assert.assertTrue(table1.propertyExists("value")); - //Looking up an invalid value returns null - Assert.assertTrue(table1.getShort("Rubbish") == null); - - //Try reading value as a string + //Tets lookups we shouldn't get anything back for other gets + //we should get right value back for this type .... + Assert.assertEquals(null, table1.getBoolean("value")); + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(Short.MAX_VALUE, (short) table1.getShort("value")); + Assert.assertEquals(null, table1.getCharacter("value")); + Assert.assertEquals(null, table1.getDouble("value")); + Assert.assertEquals(null, table1.getFloat("value")); + Assert.assertEquals(null, table1.getInteger("value")); + Assert.assertEquals(null, table1.getLong("value")); + Assert.assertEquals(null, table1.getBytes("value")); + + //... and a the string value of it. Assert.assertEquals("" + Short.MAX_VALUE, table1.getString("value")); + + //Try setting a null value and read it back + table1.put(PropertyFieldTable.Prefix.AMQP_SHORT_PROPERTY_PREFIX, "value", null); + + // Should be able to get the null back + Assert.assertEquals(null, table1.getShort("value")); + + //but still contains the value + Assert.assertTrue(table1.containsKey("value")); + + table1.remove("value"); + //but after a remove it doesn't + Assert.assertFalse(table1.containsKey("value")); + + // Table should now have zero size for encoding + checkEmpty(table1); + + //Looking up an invalid value returns null + Assert.assertEquals(null, table1.getShort("Rubbish")); } - public void testCharLookup() + /** + * Set a char and check that we can only get it back as a char + * Check that attempting to lookup a non existent value returns null + */ + public void testChar() { PropertyFieldTable table1 = new PropertyFieldTable(); - table1.setChar("value", 'b'); - Assert.assertEquals((Character) 'b', table1.getCharacter("value")); + table1.setChar("value", 'c'); + Assert.assertTrue(table1.propertyExists("value")); - //Looking up an invalid value returns null - Assert.assertTrue(table1.getCharacter("Rubbish") == null); + //Tets lookups we shouldn't get anything back for other gets + //we should get right value back for this type .... + Assert.assertEquals(null, table1.getBoolean("value")); + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(null, table1.getShort("value")); + Assert.assertEquals('c', (char) table1.getCharacter("value")); + Assert.assertEquals(null, table1.getDouble("value")); + Assert.assertEquals(null, table1.getFloat("value")); + Assert.assertEquals(null, table1.getInteger("value")); + Assert.assertEquals(null, table1.getLong("value")); + Assert.assertEquals(null, table1.getBytes("value")); + + //... and a the string value of it. + Assert.assertEquals("c", table1.getString("value")); + + //Try setting a null value and read it back + table1.put(PropertyFieldTable.Prefix.AMQP_ASCII_CHARACTER_PROPERTY_PREFIX, "value", null); + + try + { + table1.getString("value"); + fail("Should throw NullPointerException"); + } + catch (NullPointerException npe) + { + //Normal Path + } + //but still contains the value + Assert.assertTrue(table1.containsKey("value")); + + table1.remove("value"); + //but after a remove it doesn't + Assert.assertFalse(table1.containsKey("value")); + + // Table should now have zero size for encoding + checkEmpty(table1); - //Try reading value as a string - Assert.assertEquals("b", table1.getString("value")); + //Looking up an invalid value returns null + Assert.assertEquals(null, table1.getCharacter("Rubbish")); } - public void testDoubleLookup() + + /** + * Set a double and check that we can only get it back as a double + * Check that attempting to lookup a non existent value returns null + */ + public void testDouble() { PropertyFieldTable table1 = new PropertyFieldTable(); table1.setDouble("value", Double.MAX_VALUE); - Assert.assertEquals(Double.MAX_VALUE, table1.getDouble("value")); - - //Looking up an invalid value returns null - Assert.assertTrue(table1.getDouble("Rubbish") == null); + Assert.assertTrue(table1.propertyExists("value")); - //Try reading value as a string + //Tets lookups we shouldn't get anything back for other gets + //we should get right value back for this type .... + Assert.assertEquals(null, table1.getBoolean("value")); + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(null, table1.getShort("value")); + Assert.assertEquals(null, table1.getCharacter("value")); + Assert.assertEquals(Double.MAX_VALUE, (double) table1.getDouble("value")); + Assert.assertEquals(null, table1.getFloat("value")); + Assert.assertEquals(null, table1.getInteger("value")); + Assert.assertEquals(null, table1.getLong("value")); + Assert.assertEquals(null, table1.getBytes("value")); + + //... and a the string value of it. Assert.assertEquals("" + Double.MAX_VALUE, table1.getString("value")); + //Try setting a null value and read it back + table1.put(PropertyFieldTable.Prefix.AMQP_DOUBLE_PROPERTY_PREFIX, "value", null); + + Assert.assertEquals(null, table1.getDouble("value")); + + //but still contains the value + Assert.assertTrue(table1.containsKey("value")); + + table1.remove("value"); + //but after a remove it doesn't + Assert.assertFalse(table1.containsKey("value")); + + // Table should now have zero size for encoding + checkEmpty(table1); + + //Looking up an invalid value returns null + Assert.assertEquals(null, table1.getDouble("Rubbish")); } - public void testFloatLookup() + + /** + * Set a float and check that we can only get it back as a float + * Check that attempting to lookup a non existent value returns null + */ + public void testFloat() { PropertyFieldTable table1 = new PropertyFieldTable(); table1.setFloat("value", Float.MAX_VALUE); - Assert.assertEquals(Float.MAX_VALUE, table1.getFloat("value")); + Assert.assertTrue(table1.propertyExists("value")); - //Looking up an invalid value returns null - Assert.assertTrue(table1.getFloat("Rubbish") == null); - - //Try reading value as a string + //Tets lookups we shouldn't get anything back for other gets + //we should get right value back for this type .... + Assert.assertEquals(null, table1.getBoolean("value")); + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(null, table1.getShort("value")); + Assert.assertEquals(null, table1.getCharacter("value")); + Assert.assertEquals(null, table1.getDouble("value")); + Assert.assertEquals(Float.MAX_VALUE, (float) table1.getFloat("value")); + Assert.assertEquals(null, table1.getInteger("value")); + Assert.assertEquals(null, table1.getLong("value")); + Assert.assertEquals(null, table1.getBytes("value")); + + //... and a the string value of it. Assert.assertEquals("" + Float.MAX_VALUE, table1.getString("value")); + //Try setting a null value and read it back + table1.put(PropertyFieldTable.Prefix.AMQP_FLOAT_PROPERTY_PREFIX, "value", null); + + Assert.assertEquals(null, table1.getFloat("value")); + + //but still contains the value + Assert.assertTrue(table1.containsKey("value")); + + table1.remove("value"); + //but after a remove it doesn't + Assert.assertFalse(table1.containsKey("value")); + + // Table should now have zero size for encoding + checkEmpty(table1); + + //Looking up an invalid value returns null + Assert.assertEquals(null, table1.getFloat("Rubbish")); } - public void testIntLookup() + + /** + * Set an int and check that we can only get it back as an int + * Check that attempting to lookup a non existent value returns null + */ + public void testInt() { PropertyFieldTable table1 = new PropertyFieldTable(); table1.setInteger("value", Integer.MAX_VALUE); - Assert.assertEquals((Integer) Integer.MAX_VALUE, table1.getInteger("value")); + Assert.assertTrue(table1.propertyExists("value")); - //Looking up an invalid value returns null - Assert.assertTrue(table1.getInteger("Rubbish") == null); - - //Try reading value as a string + //Tets lookups we shouldn't get anything back for other gets + //we should get right value back for this type .... + Assert.assertEquals(null, table1.getBoolean("value")); + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(null, table1.getShort("value")); + Assert.assertEquals(null, table1.getCharacter("value")); + Assert.assertEquals(null, table1.getDouble("value")); + Assert.assertEquals(null, table1.getFloat("value")); + Assert.assertEquals(Integer.MAX_VALUE, (int) table1.getInteger("value")); + Assert.assertEquals(null, table1.getLong("value")); + Assert.assertEquals(null, table1.getBytes("value")); + + //... and a the string value of it. Assert.assertEquals("" + Integer.MAX_VALUE, table1.getString("value")); + //Try setting a null value and read it back + table1.put(PropertyFieldTable.Prefix.AMQP_INT_PROPERTY_PREFIX, "value", null); + + Assert.assertEquals(null, table1.getInteger("value")); + + //but still contains the value + Assert.assertTrue(table1.containsKey("value")); + + table1.remove("value"); + //but after a remove it doesn't + Assert.assertFalse(table1.containsKey("value")); + + // Table should now have zero size for encoding + checkEmpty(table1); + + //Looking up an invalid value returns null + Assert.assertEquals(null, table1.getInteger("Rubbish")); } - public void testLongLookup() + + /** + * Set a long and check that we can only get it back as a long + * Check that attempting to lookup a non existent value returns null + */ + public void testLong() { PropertyFieldTable table1 = new PropertyFieldTable(); table1.setLong("value", Long.MAX_VALUE); - Assert.assertEquals((Long) Long.MAX_VALUE, table1.getLong("value")); - - //Looking up an invalid value returns null - Assert.assertTrue(table1.getLong("Rubbish") == null); + Assert.assertTrue(table1.propertyExists("value")); - //Try reading value as a string + //Tets lookups we shouldn't get anything back for other gets + //we should get right value back for this type .... + Assert.assertEquals(null, table1.getBoolean("value")); + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(null, table1.getShort("value")); + Assert.assertEquals(null, table1.getCharacter("value")); + Assert.assertEquals(null, table1.getDouble("value")); + Assert.assertEquals(null, table1.getFloat("value")); + Assert.assertEquals(null, table1.getInteger("value")); + Assert.assertEquals(Long.MAX_VALUE, (long) table1.getLong("value")); + Assert.assertEquals(null, table1.getBytes("value")); + + //... and a the string value of it. Assert.assertEquals("" + Long.MAX_VALUE, table1.getString("value")); - } + //Try setting a null value and read it back + table1.put(PropertyFieldTable.Prefix.AMQP_LONG_PROPERTY_PREFIX, "value", null); - public void testBytesLookup() - { - PropertyFieldTable table1 = new PropertyFieldTable(); - byte[] bytes = {99, 98, 97, 96, 95}; - table1.setBytes("bytes", bytes); - assertBytesEqual(bytes, table1.getBytes("bytes")); + Assert.assertEquals(null, table1.getLong("value")); + + //but still contains the value + Assert.assertTrue(table1.containsKey("value")); + + table1.remove("value"); + //but after a remove it doesn't + Assert.assertFalse(table1.containsKey("value")); + + // Table should now have zero size for encoding + checkEmpty(table1); //Looking up an invalid value returns null - Assert.assertTrue(table1.getBytes("Rubbish") == null); + Assert.assertEquals(null, table1.getLong("Rubbish")); } - // Failed Lookups - public void testFailedBooleanLookup() + /** + * Set a double and check that we can only get it back as a double + * Check that attempting to lookup a non existent value returns null + */ + public void testBytes() { - PropertyFieldTable table1 = new PropertyFieldTable(); - Assert.assertEquals(null, table1.getBoolean("int")); - } + byte[] bytes = {99, 98, 97, 96, 95}; - public void testFailedByteLookup() - { PropertyFieldTable table1 = new PropertyFieldTable(); - Assert.assertEquals(null, table1.getByte("int")); - } + table1.setBytes("value", bytes); + Assert.assertTrue(table1.propertyExists("value")); - public void testFailedBytesLookup() - { - PropertyFieldTable table1 = new PropertyFieldTable(); - Assert.assertEquals(null, table1.getBytes("int")); - } + //Tets lookups we shouldn't get anything back for other gets + //we should get right value back for this type .... + Assert.assertEquals(null, table1.getBoolean("value")); + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(null, table1.getShort("value")); + Assert.assertEquals(null, table1.getCharacter("value")); + Assert.assertEquals(null, table1.getDouble("value")); + Assert.assertEquals(null, table1.getFloat("value")); + Assert.assertEquals(null, table1.getInteger("value")); + Assert.assertEquals(null, table1.getLong("value")); + assertBytesEqual(bytes, table1.getBytes("value")); - public void testFailedCharLookup() - { - PropertyFieldTable table1 = new PropertyFieldTable(); - Assert.assertEquals(null, table1.getCharacter("int")); - } + //... and a the string value of it is null + Assert.assertEquals(null, table1.getString("value")); - public void testFailedDoubleLookup() - { - PropertyFieldTable table1 = new PropertyFieldTable(); - Assert.assertEquals(null, table1.getDouble("int")); - } + //Try setting a null value and read it back + table1.put(PropertyFieldTable.Prefix.AMQP_BINARY_PROPERTY_PREFIX, "value", null); - public void testFailedFloatLookup() - { - PropertyFieldTable table1 = new PropertyFieldTable(); - Assert.assertEquals(null, table1.getFloat("int")); - } + Assert.assertEquals(null, table1.getBytes("value")); - public void testFailedIntLookup() - { - PropertyFieldTable table1 = new PropertyFieldTable(); - Assert.assertEquals(null, table1.getInteger("int")); + //but still contains the value + Assert.assertTrue(table1.containsKey("value")); + + table1.remove("value"); + //but after a remove it doesn't + Assert.assertFalse(table1.containsKey("value")); + + // Table should now have zero size for encoding + checkEmpty(table1); + + //Looking up an invalid value returns null + Assert.assertEquals(null, table1.getBytes("Rubbish")); } - public void testFailedLongLookup() + /** + * Calls all methods that can be used to check the table is empty + * - getEncodedSize + * - isEmpty + * - size + * + * @param table to check is empty + */ + private void checkEmpty(PropertyFieldTable table) { - PropertyFieldTable table1 = new PropertyFieldTable(); - Assert.assertEquals(null, table1.getLong("int")); + Assert.assertEquals(0, table.getEncodedSize()); + Assert.assertTrue(table.isEmpty()); + Assert.assertEquals(0, table.size()); + + Assert.assertEquals(0, table.keySet().size()); + Assert.assertEquals(0, table.values().size()); + Assert.assertEquals(0, table.entrySet().size()); } - public void testFailedShortLookup() + + /** + * Set a String and check that we can only get it back as a String + * Check that attempting to lookup a non existent value returns null + */ + public void testString() { PropertyFieldTable table1 = new PropertyFieldTable(); - Assert.assertEquals(null, table1.getShort("int")); + table1.setString("value", "Hello"); + Assert.assertTrue(table1.propertyExists("value")); + + //Tets lookups we shouldn't get anything back for other gets + //we should get right value back for this type .... + Assert.assertEquals(null, table1.getBoolean("value")); + Assert.assertEquals(null, table1.getByte("value")); + Assert.assertEquals(null, table1.getShort("value")); + Assert.assertEquals(null, table1.getCharacter("value")); + Assert.assertEquals(null, table1.getDouble("value")); + Assert.assertEquals(null, table1.getFloat("value")); + Assert.assertEquals(null, table1.getInteger("value")); + Assert.assertEquals(null, table1.getLong("value")); + Assert.assertEquals(null, table1.getBytes("value")); + Assert.assertEquals("Hello", table1.getString("value")); + + //Try setting a null value and read it back + table1.setString("value", null); + + Assert.assertEquals(null, table1.getString("value")); + + //but still contains the value + Assert.assertTrue(table1.containsKey("value")); + + table1.remove("value"); + //but after a remove it doesn't + Assert.assertFalse(table1.containsKey("value")); + + checkEmpty(table1); + + //Looking up an invalid value returns null + Assert.assertEquals(null, table1.getString("Rubbish")); + + //Additional Test that haven't been covered for string + table1.setObject("value", "Hello"); + //Check that it was set correctly + Assert.assertEquals("Hello", table1.getString("value")); } - public void testXML() + + /** + * Test that the generated XML can be used to create a field table with the same values. + */ + public void testValidXML() { PropertyFieldTable table1 = new PropertyFieldTable(); table1.setBoolean("bool", true); @@ -249,6 +578,8 @@ public class PropertyFieldTableTest extends TestCase table1.setInteger("int", Integer.MAX_VALUE); table1.setLong("long", Long.MAX_VALUE); table1.setShort("short", Short.MAX_VALUE); + table1.setString("string", "Hello"); + table1.setString("null-string", null); table1.setObject("object-bool", true); table1.setObject("object-byte", Byte.MAX_VALUE); @@ -259,14 +590,48 @@ public class PropertyFieldTableTest extends TestCase table1.setObject("object-int", Integer.MAX_VALUE); table1.setObject("object-long", Long.MAX_VALUE); table1.setObject("object-short", Short.MAX_VALUE); + table1.setObject("object-string", "Hello"); + + Assert.assertEquals(21, table1.size()); String table1XML = table1.toString(); PropertyFieldTable table2 = new PropertyFieldTable(table1XML); Assert.assertEquals(table1XML, table2.toString()); + + //Check that when bytes is written out as a string with no new line between items that it is read in ok. + + } + + /** + * Test that invalid input throws the correct Exception + */ + public void testInvalidXML() + { + try + { + _logger.warn("Testing Invalid XML expecting IllegalArgumentException"); + new PropertyFieldTable("Rubbish"); + fail("IllegalArgumentException expected"); + } + catch (IllegalArgumentException iae) + { + //normal path + } + try + { + _logger.warn("Testing Invalid XML expecting IllegalArgumentException"); + new PropertyFieldTable(""); + fail("IllegalArgumentException expected"); + } + catch (IllegalArgumentException iae) + { + //normal path + } } + public void testKeyEnumeration() { PropertyFieldTable table = new PropertyFieldTable(); @@ -298,6 +663,8 @@ public class PropertyFieldTableTest extends TestCase table.setInteger("int", Integer.MAX_VALUE); table.setLong("long", Long.MAX_VALUE); table.setShort("short", Short.MAX_VALUE); + table.setString("string", "Hello"); + table.setString("null-string", null); table.setObject("object-bool", true); table.setObject("object-byte", Byte.MAX_VALUE); @@ -308,6 +675,7 @@ public class PropertyFieldTableTest extends TestCase table.setObject("object-int", Integer.MAX_VALUE); table.setObject("object-long", Long.MAX_VALUE); table.setObject("object-short", Short.MAX_VALUE); + table.setObject("object-string", "Hello"); Assert.assertEquals((Boolean) true, table.getBoolean("bool")); @@ -319,6 +687,8 @@ public class PropertyFieldTableTest extends TestCase Assert.assertEquals((Integer) Integer.MAX_VALUE, table.getInteger("int")); Assert.assertEquals((Long) Long.MAX_VALUE, table.getLong("long")); Assert.assertEquals((Short) Short.MAX_VALUE, table.getShort("short")); + Assert.assertEquals("Hello", table.getString("string")); + Assert.assertEquals(null, table.getString("null-string")); Assert.assertEquals(true, table.getObject("object-bool")); Assert.assertEquals(Byte.MAX_VALUE, table.getObject("object-byte")); @@ -329,6 +699,7 @@ public class PropertyFieldTableTest extends TestCase Assert.assertEquals(Integer.MAX_VALUE, table.getObject("object-int")); Assert.assertEquals(Long.MAX_VALUE, table.getObject("object-long")); Assert.assertEquals(Short.MAX_VALUE, table.getObject("object-short")); + Assert.assertEquals("Hello", table.getObject("object-string")); } @@ -347,6 +718,8 @@ public class PropertyFieldTableTest extends TestCase table.setInteger("int", Integer.MAX_VALUE); table.setLong("long", Long.MAX_VALUE); table.setShort("short", Short.MAX_VALUE); + table.setString("string", "hello"); + table.setString("null-string", null); final ByteBuffer buffer = ByteBuffer.allocate((int) table.getEncodedSize()); // FIXME XXX: Is cast a problem? @@ -370,6 +743,9 @@ public class PropertyFieldTableTest extends TestCase Assert.assertEquals((Integer) Integer.MAX_VALUE, table2.getInteger("int")); Assert.assertEquals((Long) Long.MAX_VALUE, table2.getLong("long")); Assert.assertEquals((Short) Short.MAX_VALUE, table2.getShort("short")); + Assert.assertEquals("hello", table2.getString("string")); + Assert.assertEquals(null, table2.getString("null-string")); + } catch (AMQFrameDecodingException e) { @@ -380,7 +756,7 @@ public class PropertyFieldTableTest extends TestCase public void testEncodingSize() { - FieldTable result = FieldTableFactory.newFieldTable(); + PropertyFieldTable result = new PropertyFieldTable(); int size = 0; result.setBoolean("boolean", true); @@ -468,63 +844,296 @@ public class PropertyFieldTableTest extends TestCase } - public void testEncodingSize1() +// public void testEncodingSize1() +// { +// PropertyFieldTable table = new PropertyFieldTable(); +// int size = 0; +// result.put("one", 1L); +// size = EncodingUtils.encodedShortStringLength("one"); +// size += 1 + EncodingUtils.encodedLongLength(); +// assertEquals(size, result.getEncodedSize()); +// +// result.put("two", 2L); +// size += EncodingUtils.encodedShortStringLength("two"); +// size += 1 + EncodingUtils.encodedLongLength(); +// assertEquals(size, result.getEncodedSize()); +// +// result.put("three", 3L); +// size += EncodingUtils.encodedShortStringLength("three"); +// size += 1 + EncodingUtils.encodedLongLength(); +// assertEquals(size, result.getEncodedSize()); +// +// result.put("four", 4L); +// size += EncodingUtils.encodedShortStringLength("four"); +// size += 1 + EncodingUtils.encodedLongLength(); +// assertEquals(size, result.getEncodedSize()); +// +// result.put("five", 5L); +// size += EncodingUtils.encodedShortStringLength("five"); +// size += 1 + EncodingUtils.encodedLongLength(); +// assertEquals(size, result.getEncodedSize()); +// +// //fixme should perhaps be expanded to incorporate all types. +// +// final ByteBuffer buffer = ByteBuffer.allocate((int) result.getEncodedSize()); // FIXME XXX: Is cast a problem? +// +// result.writeToBuffer(buffer); +// +// buffer.flip(); +// +// long length = buffer.getUnsignedInt(); +// +// try +// { +// PropertyFieldTable table2 = new PropertyFieldTable(buffer, length); +// +// Assert.assertEquals((Long) 1L, table2.getLong("one")); +// Assert.assertEquals((Long) 2L, table2.getLong("two")); +// Assert.assertEquals((Long) 3L, table2.getLong("three")); +// Assert.assertEquals((Long) 4L, table2.getLong("four")); +// Assert.assertEquals((Long) 5L, table2.getLong("five")); +// } +// catch (AMQFrameDecodingException e) +// { +// e.printStackTrace(); +// fail("PFT should be instantiated from bytes." + e.getCause()); +// } +// +// } + + + /** + * Additional test for setObject + */ + public void testSetObject() { - FieldTable result = FieldTableFactory.newFieldTable(); - int size = 0; - result.put("one", 1L); - size = EncodingUtils.encodedShortStringLength("one"); - size += 1 + EncodingUtils.encodedLongLength(); - assertEquals(size, result.getEncodedSize()); + PropertyFieldTable table = new PropertyFieldTable(); + + //Try setting a non primative object + + try + { + table.setObject("value", this); + fail("Only primative values allowed in setObject"); + } + catch (AMQPInvalidClassException iae) + { + //normal path + } + // so size should be zero + Assert.assertEquals(0, table.getEncodedSize()); + } - result.put("two", 2L); - size += EncodingUtils.encodedShortStringLength("two"); - size += 1 + EncodingUtils.encodedLongLength(); - assertEquals(size, result.getEncodedSize()); + /** + * Additional test checkPropertyName doesn't accept Null + */ + public void testCheckPropertyNameasNull() + { + PropertyFieldTable table = new PropertyFieldTable(); - result.put("three", 3L); - size += EncodingUtils.encodedShortStringLength("three"); - size += 1 + EncodingUtils.encodedLongLength(); - assertEquals(size, result.getEncodedSize()); + try + { + table.setObject(null, "String"); + fail("Null property name is not allowed"); + } + catch (IllegalArgumentException iae) + { + //normal path + } + // so size should be zero + Assert.assertEquals(0, table.getEncodedSize()); + } - result.put("four", 4L); - size += EncodingUtils.encodedShortStringLength("four"); - size += 1 + EncodingUtils.encodedLongLength(); - assertEquals(size, result.getEncodedSize()); - result.put("five", 5L); - size += EncodingUtils.encodedShortStringLength("five"); - size += 1 + EncodingUtils.encodedLongLength(); - assertEquals(size, result.getEncodedSize()); + /** + * Additional test checkPropertyName doesn't accept an empty String + */ + public void testCheckPropertyNameasEmptyString() + { + PropertyFieldTable table = new PropertyFieldTable(); - //fixme should perhaps be expanded to incorporate all types. + try + { + table.setObject("", "String"); + fail("empty property name is not allowed"); + } + catch (IllegalArgumentException iae) + { + //normal path + } + // so size should be zero + Assert.assertEquals(0, table.getEncodedSize()); + } - final ByteBuffer buffer = ByteBuffer.allocate((int) result.getEncodedSize()); // FIXME XXX: Is cast a problem? - result.writeToBuffer(buffer); + /** + * Additional test checkPropertyName doesn't accept an empty String + */ + public void testCheckPropertyNamehasMaxLength() + { + PropertyFieldTable table = new PropertyFieldTable(); - buffer.flip(); + StringBuffer longPropertyName = new StringBuffer(129); - long length = buffer.getUnsignedInt(); + for (int i = 0; i < 129; i++) + { + longPropertyName.append("x"); + } try { - PropertyFieldTable table2 = new PropertyFieldTable(buffer, length); + table.setObject(longPropertyName.toString(), "String"); + fail("property name must be < 128 characters"); + } + catch (IllegalArgumentException iae) + { + //normal path + } + // so size should be zero + Assert.assertEquals(0, table.getEncodedSize()); + } + - Assert.assertEquals((Long) 1L, table2.getLong("one")); - Assert.assertEquals((Long) 2L, table2.getLong("two")); - Assert.assertEquals((Long) 3L, table2.getLong("three")); - Assert.assertEquals((Long) 4L, table2.getLong("four")); - Assert.assertEquals((Long) 5L, table2.getLong("five")); + /** + * Additional test checkPropertyName starts with a letter + */ + public void testCheckPropertyNameStartCharacterIsLetter() + { + PropertyFieldTable table = new PropertyFieldTable(); + + //Try a name that starts with a number + try + { + table.setObject("1", "String"); + fail("property name must start with a letter"); } - catch (AMQFrameDecodingException e) + catch (IllegalArgumentException iae) { - e.printStackTrace(); - fail("PFT should be instantiated from bytes." + e.getCause()); + //normal path } + // so size should be zero + Assert.assertEquals(0, table.getEncodedSize()); + } + + + /** + * Additional test checkPropertyName starts with a hash or a dollar + */ + public void testCheckPropertyNameStartCharacterIsHashorDollar() + { + PropertyFieldTable table = new PropertyFieldTable(); + //Try a name that starts with a number + try + { + table.setObject("#", "String"); + table.setObject("$", "String"); + } + catch (IllegalArgumentException iae) + { + fail("property name are allowed to start with # and $s"); + } } + + /** + * Additional test to test the contents of the table + */ + public void testContents() + { + PropertyFieldTable table = new PropertyFieldTable(); + + table.put("StringProperty", "String"); + + Assert.assertTrue(table.containsValue("String")); + + Assert.assertEquals("String", table.get("StringProperty")); + + //Test Clear + + table.clear(); + + checkEmpty(table); + } + + /** + * Test the contents of the sets + */ + public void testSets() + { + + PropertyFieldTable table = new PropertyFieldTable(); + + table.put("n1", "1"); + table.put("n2", "2"); + table.put("n3", "3"); + + Iterator iterator = table.keySet().iterator(); + Assert.assertEquals("n1", iterator.next()); + Assert.assertEquals("n2", iterator.next()); + Assert.assertEquals("n3", iterator.next()); + Assert.assertFalse(iterator.hasNext()); + + + iterator = table.values().iterator(); + Assert.assertEquals("1", iterator.next()); + Assert.assertEquals("2", iterator.next()); + Assert.assertEquals("3", iterator.next()); + Assert.assertFalse(iterator.hasNext()); + + + iterator = table.entrySet().iterator(); + Map.Entry entry = (Map.Entry) iterator.next(); + Assert.assertEquals("n1", entry.getKey()); + Assert.assertEquals("1", entry.getValue()); + entry = (Map.Entry) iterator.next(); + Assert.assertEquals("n2", entry.getKey()); + Assert.assertEquals("2", entry.getValue()); + entry = (Map.Entry) iterator.next(); + Assert.assertEquals("n3", entry.getKey()); + Assert.assertEquals("3", entry.getValue()); + Assert.assertFalse(iterator.hasNext()); + + + } + + + /** + * Test that all the values are preserved after a putAll + */ + public void testPutAll() + { + Map map = new HashMap(); + + map.put("char", 'c'); + map.put("double", Double.MAX_VALUE); + map.put("float", Float.MAX_VALUE); + map.put("int", Integer.MAX_VALUE); + map.put("long", Long.MAX_VALUE); + map.put("short", Short.MAX_VALUE); + + PropertyFieldTable table = new PropertyFieldTable(); + + table.putAll(map); + + Assert.assertEquals(6, table.size()); + + Assert.assertTrue(table.containsKey("char")); + Assert.assertEquals('c', (char) table.getCharacter("char")); + Assert.assertTrue(table.containsKey("double")); + Assert.assertEquals(Double.MAX_VALUE, table.getDouble("double")); + Assert.assertTrue(table.containsKey("float")); + Assert.assertEquals(Float.MAX_VALUE, table.getFloat("float")); + Assert.assertTrue(table.containsKey("int")); + Assert.assertEquals(Integer.MAX_VALUE, (int) table.getInteger("int")); + Assert.assertTrue(table.containsKey("long")); + Assert.assertEquals(Long.MAX_VALUE, (long) table.getLong("long")); + Assert.assertTrue(table.containsKey("short")); + Assert.assertEquals(Short.MAX_VALUE, (short) table.getShort("short")); + Assert.assertEquals(Short.MAX_VALUE, (short) table.getShort("short")); + } + + private void assertBytesEqual(byte[] expected, byte[] actual) { Assert.assertEquals(expected.length, actual.length); diff --git a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Activator.java b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Activator.java index 61b6cfaa59..5eab267c28 100644 --- a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Activator.java +++ b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Activator.java @@ -26,6 +26,7 @@ import org.osgi.framework.BundleContext; /** * The activator class controls the plug-in life cycle + * @author Bhupendra Bhardwaj */ public class Activator extends AbstractUIPlugin { @@ -44,7 +45,6 @@ public class Activator extends AbstractUIPlugin } /* - * (non-Javadoc) * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) */ public void start(BundleContext context) throws Exception @@ -53,7 +53,6 @@ public class Activator extends AbstractUIPlugin } /* - * (non-Javadoc) * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) */ public void stop(BundleContext context) throws Exception @@ -67,18 +66,19 @@ public class Activator extends AbstractUIPlugin * * @return the shared instance */ - public static Activator getDefault() { + public static Activator getDefault() + { return plugin; } /** - * Returns an image descriptor for the image file at the given - * plug-in relative path + * Returns an image descriptor for the image file at the given plug-in relative path * * @param path the path * @return the image descriptor */ - public static ImageDescriptor getImageDescriptor(String path) { + public static ImageDescriptor getImageDescriptor(String path) + { return imageDescriptorFromPlugin(PLUGIN_ID, path); } } diff --git a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Application.java b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Application.java index 3d014e6968..a1c4b7ddb0 100644 --- a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Application.java +++ b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Application.java @@ -34,8 +34,8 @@ public class Application implements IPlatformRunnable static Shell shell = null; /* - * (non-Javadoc) - * + * The call to createAndRunWorkbench will not return until the workbench is closed. + * The SWT event loop and other low-level logistics are handled inside this method. * @see org.eclipse.core.runtime.IPlatformRunnable#run(java.lang.Object) */ public Object run(Object args) throws Exception diff --git a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java index 175130aea9..38a4d4561f 100644 --- a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java +++ b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java @@ -33,6 +33,10 @@ import org.eclipse.swt.graphics.Image; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.PlatformUI; +/** + * Main Application Registry, which contains shared resources and map to all connected servers. + * @author Bhupendra Bhardwaj + */ public abstract class ApplicationRegistry { private static ImageRegistry imageRegistry = new ImageRegistry(); diff --git a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationWorkbenchWindowAdvisor.java b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationWorkbenchWindowAdvisor.java index 472d003657..3d163fb111 100644 --- a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationWorkbenchWindowAdvisor.java +++ b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationWorkbenchWindowAdvisor.java @@ -28,6 +28,10 @@ import org.eclipse.ui.application.IActionBarConfigurer; import org.eclipse.ui.application.IWorkbenchWindowConfigurer; import org.eclipse.ui.application.WorkbenchWindowAdvisor; +/** + * + * @author Bhupendra Bhardwaj + */ public class ApplicationWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor { public ApplicationWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) @@ -49,7 +53,7 @@ public class ApplicationWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor configurer.setShowCoolBar(true); configurer.setShowStatusLine(false); - configurer.setTitle("Qpid Management Console"); + configurer.setTitle(Constants.APPLICATION_NAME); } public void postWindowCreate() diff --git a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Constants.java b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Constants.java index 1678cbac62..e7cd7f4671 100644 --- a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Constants.java +++ b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Constants.java @@ -20,6 +20,11 @@ */ package org.apache.qpid.management.ui; +/** + * Contains constants for the application + * @author Bhupendra Bhardwaj + * + */ public class Constants { public final static String APPLICATION_NAME = "Qpid Management Console"; diff --git a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedBean.java b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedBean.java index 767fd8b721..7da5621345 100644 --- a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedBean.java +++ b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedBean.java @@ -22,6 +22,11 @@ package org.apache.qpid.management.ui; import java.util.HashMap; +/** + * Class representing a managed bean on the managed server + * @author Bhupendra Bhardwaj + * + */ public abstract class ManagedBean extends ManagedObject { private String _uniqueName = ""; diff --git a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedObject.java b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedObject.java index 2ed463bdf8..96e0fa46c6 100644 --- a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedObject.java +++ b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedObject.java @@ -20,6 +20,10 @@ */ package org.apache.qpid.management.ui; +/** + * Abstract class representing a managed object + * @author Bhupendra Bhardwaj + */ public abstract class ManagedObject { private String _name; diff --git a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedServer.java b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedServer.java index 44f933a5d1..e3699bb1ee 100644 --- a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedServer.java +++ b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedServer.java @@ -20,6 +20,10 @@ */ package org.apache.qpid.management.ui; +/** + * Class representing a server being managed eg. MBeanServer + * @author Bhupendra Bhardwaj + */ public class ManagedServer extends ManagedObject { private String host; diff --git a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Perspective.java b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Perspective.java index 03477cb117..da49d70b88 100644 --- a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Perspective.java +++ b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Perspective.java @@ -25,6 +25,10 @@ import org.apache.qpid.management.ui.views.NavigationView; import org.eclipse.ui.IPageLayout; import org.eclipse.ui.IPerspectiveFactory; +/** + * + * @author Bhupendra Bhardwaj + */ public class Perspective implements IPerspectiveFactory { public void createInitialLayout(IPageLayout layout) @@ -32,20 +36,9 @@ public class Perspective implements IPerspectiveFactory String editorArea = layout.getEditorArea(); layout.setEditorAreaVisible(false); - // standalone view meaning it can't be docked or stacked with other views, - // and it doesn't have a title bar. - - layout.addStandaloneView(NavigationView.ID, - true, - IPageLayout.LEFT, - 0.25f, - editorArea); - - layout.addStandaloneView(MBeanView.ID, - true, - IPageLayout.RIGHT, - 0.75f, - editorArea); + // standalone view meaning it can't be docked or stacked with other views, and it doesn't have a title bar. + layout.addStandaloneView(NavigationView.ID, true, IPageLayout.LEFT, 0.25f, editorArea); + layout.addStandaloneView(MBeanView.ID, true, IPageLayout.RIGHT, 0.75f, editorArea); layout.getViewLayout(NavigationView.ID).setCloseable(false); layout.getViewLayout(MBeanView.ID).setCloseable(false); diff --git a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/exceptions/ManagementConsoleException.java b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/exceptions/ManagementConsoleException.java new file mode 100644 index 0000000000..ba6167591e --- /dev/null +++ b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/exceptions/ManagementConsoleException.java @@ -0,0 +1,10 @@ +package org.apache.qpid.management.ui.exceptions; + +@SuppressWarnings("serial") +public class ManagementConsoleException extends Exception +{ + public ManagementConsoleException(String message) + { + super(message); + } +} diff --git a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/MBeanUtility.java b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/MBeanUtility.java index 800a5b6ce3..923fd4a12a 100644 --- a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/MBeanUtility.java +++ b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/MBeanUtility.java @@ -41,6 +41,7 @@ import javax.management.ReflectionException; import org.apache.qpid.management.ui.ApplicationRegistry; import org.apache.qpid.management.ui.ManagedBean; import org.apache.qpid.management.ui.ManagedServer; +import org.apache.qpid.management.ui.exceptions.ManagementConsoleException; import org.apache.qpid.management.ui.model.AttributeData; import org.apache.qpid.management.ui.model.ManagedAttributeModel; import org.apache.qpid.management.ui.model.NotificationInfoModel; @@ -49,31 +50,47 @@ import org.apache.qpid.management.ui.model.OperationDataModel; import org.apache.qpid.management.ui.model.ParameterData; import org.apache.qpid.management.ui.views.ViewUtility; - +/** + * Utility class for all mbeanserver related operations. Keeps all JMX code out from view and model classes + * @author Bhupendra Bhardwaj + */ public class MBeanUtility { - - public static MBeanInfo getMBeanInfo(ManagedBean mbean) - throws IOException, JMException + /** + * Retrieves the MBeanInfo from MBeanServer and stores in the application registry + * @param mbean managed bean + * @return MBeanInfo + * @throws Exception, if server connection is null or if server throws Exception + */ + public static MBeanInfo getMBeanInfo(ManagedBean mbean) throws Exception { ManagedServer server = mbean.getServer(); JMXServerRegistry serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(server); MBeanServerConnection mbsc = serverRegistry.getServerConnection(); if (mbsc == null) - System.out.println("MBeanServerConnection does not exist in the Application registry."); + { + throw new ManagementConsoleException("Server connection is broken"); + } JMXManagedObject jmxbean = (JMXManagedObject)mbean; MBeanInfo mbeanInfo = mbsc.getMBeanInfo(jmxbean.getObjectName()); serverRegistry.putMBeanInfo(mbean, mbeanInfo); + // populate the server registry with attribute and operation info getAttributes(mbean); getOperations(mbean); return mbeanInfo; } - + /** + * executes the MBean operation + * @param mbean + * @param opData + * @return MBean operation return value + * @throws Exception if server connection is broken or if operation execution fails on the mbean server + */ public static Object execute(ManagedBean mbean, OperationData opData) throws Exception { String opName = opData.getName(); @@ -89,7 +106,6 @@ public class MBeanUtility { signature[i] = params.get(i).getType(); values[i] = params.get(i).getValue(); - System.out.println(params.get(i).getName() + " : " + params.get(i).getValue()); } } @@ -99,48 +115,27 @@ public class MBeanUtility MBeanServerConnection mbsc = serverRegistry.getServerConnection(); if (mbsc == null) { - System.out.println("MBeanServerConnection doesn't exist in the Application registry."); + throw new ManagementConsoleException("Server connection is broken"); // TODO - // throw exception to check if the server is added - // Or try and get the connection again if it was disconnected - return null; + // try and get the connection again if it was disconnected } JMXManagedObject jmxbean = (JMXManagedObject)mbean; return mbsc.invoke(jmxbean.getObjectName(), opName, values, signature); - - /* - try - { - - } - catch(MBeanException ex) - { - ex.printStackTrace(); - - } - catch(OperationsException ex) - { - ex.printStackTrace(); - - } - catch(JMException ex) - { - ex.printStackTrace(); - ViewUtility.popupError(new Exception(ex), "Operation failed"); - } - catch(IOException ex) - { - ex.printStackTrace(); - ViewUtility.popupError(new Exception(ex), "Operation failed"); - } - */ } + /** + * @see MBeanUtility#handleException(ManagedBean, Exception) + */ public static void handleException(Exception ex) { handleException(null, ex); } + /** + * handels the exception received. Shows the exception to the user in best suitable way + * @param mbean managed bean + * @param ex Exception + */ public static void handleException(ManagedBean mbean, Exception ex) { if (mbean == null) @@ -161,17 +156,35 @@ public class MBeanUtility } else if (ex instanceof MBeanException) { - ViewUtility.popupInfoMessage(mbean.getName(), ex.getMessage()); + String cause = ((MBeanException)ex).getTargetException().getMessage(); + if (cause == null) + cause = ex.getMessage(); + ViewUtility.popupInfoMessage(mbean.getName(), cause); } - else + else if (ex instanceof JMException) + { + ViewUtility.popupErrorMessage(mbean.getName(), ex.getMessage()); + } + else if (ex instanceof ManagementConsoleException) + { + ViewUtility.popupErrorMessage(mbean.getName(), ex.getMessage()); + } + else { ViewUtility.popupError(mbean.getName(), "Error occured", ex); } - ex.printStackTrace(); + //ex.printStackTrace(); } + /** + * Registers the notification listener with the MBeanServer + * @param mbean managed bean + * @param name notification name + * @param type notification type + * @throws Exception if server connection is broken or if listener could not be created + */ public static void createNotificationlistener(ManagedBean mbean, String name, String type) - throws IOException, Exception + throws Exception { JMXManagedObject jmxbean = (JMXManagedObject)mbean; JMXServerRegistry serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(mbean); @@ -180,23 +193,15 @@ public class MBeanUtility if (mbsc == null) { - throw new Exception("MBeanServer connection is broken"); + throw new ManagementConsoleException("Server connection is broken"); } mbsc.addNotificationListener(jmxbean.getObjectName(), serverRegistry.getNotificationListener(), null, null); - System.out.println("Listener created : " + jmxbean.getObjectName()); } public static void removeNotificationListener(ManagedBean mbean, String name, String type) throws Exception { - //JMXManagedObject jmxbean = (JMXManagedObject)mbean; JMXServerRegistry serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(mbean); serverRegistry.removeNotificationListener(mbean, name, type); - //MBeanServerConnection mbsc = serverRegistry.getServerConnection(); - - //if (mbsc != null) - //{ - // mbsc.removeNotificationListener(jmxbean.getObjectName(), serverRegistry.getNotificationListener()); - //} } public static int refreshAttribute(ManagedBean mbean, String attribute) throws Exception @@ -205,7 +210,9 @@ public class MBeanUtility MBeanServerConnection mbsc = serverRegistry.getServerConnection(); if (mbsc == null) - throw new Exception("Server connection is not available for " + mbean.getUniqueName()); + { + throw new ManagementConsoleException("Server connection is broken"); + } Object value = mbsc.getAttribute(((JMXManagedObject)mbean).getObjectName(), attribute); @@ -214,7 +221,13 @@ public class MBeanUtility return Integer.parseInt(String.valueOf(value)); } - public static ManagedAttributeModel getAttributes(ManagedBean mbean) + /** + * Retrieves the attribute values from MBeanSever and stores in the server registry. + * @param mbean + * @return the attribute model + * @throws Exception if attributes can not be retrieved from MBeanServer + */ + public static ManagedAttributeModel getAttributes(ManagedBean mbean) throws Exception { ObjectName objName = ((JMXManagedObject)mbean).getObjectName(); String[] attributes = null; @@ -243,29 +256,28 @@ public class MBeanUtility attributes = attributeModel.getAttributeNames().toArray(new String[0]); } - try + if (attributes.length != 0) { - if (attributes.length != 0) + list = mbsc.getAttributes(objName, attributes); + for (Iterator itr = list.iterator(); itr.hasNext();) { - list = mbsc.getAttributes(objName, attributes); - for (Iterator itr = list.iterator(); itr.hasNext();) - { - Attribute attrib = (Attribute)itr.next(); - attributeModel.setAttributeValue(attrib.getName(), attrib.getValue()); - } + Attribute attrib = (Attribute)itr.next(); + attributeModel.setAttributeValue(attrib.getName(), attrib.getValue()); } - } - catch (Exception ex) - { - ex.printStackTrace(); } - serverRegistry.setAttributeModel(mbean, attributeModel); - + serverRegistry.setAttributeModel(mbean, attributeModel); return attributeModel; } - public static void updateAttribute(ManagedBean mbean, AttributeData attribute, String value) + /** + * Updates the attribute value of an MBean + * @param mbean + * @param attribute + * @param value + * @throws Exception if MBeanServer throws exception in updating the attribute value + */ + public static void updateAttribute(ManagedBean mbean, AttributeData attribute, String value) throws Exception { JMXManagedObject jmxbean = (JMXManagedObject)mbean; JMXServerRegistry serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(mbean); @@ -273,11 +285,7 @@ public class MBeanUtility MBeanServerConnection mbsc = serverRegistry.getServerConnection(); Object newValue = value; - if (attribute.getDataType().equals(String.class.getName())) - { - - } - else if (attribute.getDataType().equals(Long.class.getName())) + if (attribute.getDataType().equals(Long.class.getName())) { newValue = new Long(Long.parseLong(value)); } @@ -286,24 +294,20 @@ public class MBeanUtility newValue = new Integer(Integer.parseInt(value)); } - try - { - mbsc.setAttribute(jmxbean.getObjectName(), new Attribute(attribute.getName(), newValue)); - - // Update the value in the registry, to avoid refreshing from mbsc - ManagedAttributeModel attributeModel = serverRegistry.getAttributeModel(mbean); - attributeModel.setAttributeValue(attribute.getName(), newValue); - } - catch(Exception ex) - { - ex.printStackTrace(); - } + mbsc.setAttribute(jmxbean.getObjectName(), new Attribute(attribute.getName(), newValue)); + // Update the value in the registry, to avoid refreshing from mbsc + ManagedAttributeModel attributeModel = serverRegistry.getAttributeModel(mbean); + attributeModel.setAttributeValue(attribute.getName(), newValue); } + /** + * populates the operation data model in server registry for given mbean + * @param mbean + * @return operation data model + */ public static OperationDataModel getOperations(ManagedBean mbean) { JMXServerRegistry serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(mbean); - OperationDataModel dataModel = serverRegistry.getOperationModel(mbean); if (dataModel == null) { @@ -322,6 +326,11 @@ public class MBeanUtility return dataModel; } + /** + * populates the notification in the server registry for given mbean + * @param mbean + * @return notification info model + */ public static NotificationInfoModel[] getNotificationInfo(ManagedBean mbean) { diff --git a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/AttributesTabControl.java b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/AttributesTabControl.java index b5c044e7be..04890e4e63 100644 --- a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/AttributesTabControl.java +++ b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/AttributesTabControl.java @@ -277,8 +277,16 @@ public class AttributesTabControl extends TabControl { public void widgetSelected(SelectionEvent e) { - // refresh the attributes list - refresh(_mbean); + try + { + // refresh the attributes list + refresh(_mbean); + } + catch (Exception ex) + { + MBeanUtility.handleException(_mbean, ex); + } + } }); } @@ -582,12 +590,19 @@ public class AttributesTabControl extends TabControl { public void widgetSelected(SelectionEvent event) { - Button button = (Button)event.widget; - Text text = (Text)button.getData(); - AttributeData data = (AttributeData)button.getParent().getData(); - MBeanUtility.updateAttribute(_mbean, data, text.getText()); - button.getShell().close(); - refresh(); + try + { + Button button = (Button)event.widget; + Text text = (Text)button.getData(); + AttributeData data = (AttributeData)button.getParent().getData(); + MBeanUtility.updateAttribute(_mbean, data, text.getText()); + button.getShell().close(); + refresh(); + } + catch (Exception ex) + { + MBeanUtility.handleException(_mbean, ex); + } } }); @@ -612,7 +627,15 @@ public class AttributesTabControl extends TabControl _tableViewer.setInput(null); return; } - ManagedAttributeModel attributesList = MBeanUtility.getAttributes(mbean); + ManagedAttributeModel attributesList = null; + try + { + attributesList = MBeanUtility.getAttributes(mbean); + } + catch(Exception ex) + { + MBeanUtility.handleException(_mbean, ex); + } _tableViewer.setInput(attributesList); _table.setItemCount(attributesList.getCount()); diff --git a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/OperationTabControl.java b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/OperationTabControl.java index 8563bdb882..c62f90353d 100644 --- a/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/OperationTabControl.java +++ b/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/OperationTabControl.java @@ -595,15 +595,6 @@ public class OperationTabControl extends TabControl else { ViewUtility.disposeChildren(_resultsComposite); - /* - if (_resultsComposite == null || _resultsComposite.isDisposed()) - { - _resultsComposite = _toolkit.createComposite(_form.getBody(), SWT.NONE); - GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true); - layoutData.verticalIndent = 20; - _resultsComposite.setLayoutData(layoutData); - _resultsComposite.setLayout(new GridLayout()); - }*/ populateResults(result, _resultsComposite); _resultsComposite.layout(); _form.layout(); |