diff options
| author | Robert Godfrey <rgodfrey@apache.org> | 2008-04-24 17:49:03 +0000 |
|---|---|---|
| committer | Robert Godfrey <rgodfrey@apache.org> | 2008-04-24 17:49:03 +0000 |
| commit | e32debe1df7d0a837e30cd937fb7a18fc5cfa203 (patch) | |
| tree | eda0dabab6fa1855d05df1ce8cab3f823f3e56ed | |
| parent | 1a18bfd552d2065df2da734b42258249b01c4271 (diff) | |
| download | qpid-python-e32debe1df7d0a837e30cd937fb7a18fc5cfa203.tar.gz | |
QPID-832 : Fix eol-style
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@651325 13f79535-47bb-0310-9956-ffa450edef68
166 files changed, 32444 insertions, 32444 deletions
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java index e7c887f306..f1b383eac9 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java @@ -1,240 +1,240 @@ -/*
- *
- * 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.server.exchange;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.protocol.AMQConstant;
-import org.apache.qpid.exchange.ExchangeDefaults;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.FieldTable;
-import org.apache.qpid.framing.abstraction.MessagePublishInfo;
-import org.apache.qpid.server.management.MBeanConstructor;
-import org.apache.qpid.server.management.MBeanDescription;
-import org.apache.qpid.server.queue.AMQMessage;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-
-import javax.management.JMException;
-import javax.management.MBeanException;
-import javax.management.openmbean.CompositeData;
-import javax.management.openmbean.CompositeDataSupport;
-import javax.management.openmbean.OpenDataException;
-import javax.management.openmbean.TabularData;
-import javax.management.openmbean.TabularDataSupport;
-import java.util.List;
-import java.util.Map;
-import java.util.ArrayList;
-import java.util.concurrent.CopyOnWriteArraySet;
-
-public class FanoutExchange extends AbstractExchange
-{
- private static final Logger _logger = Logger.getLogger(FanoutExchange.class);
-
- /**
- * Maps from queue name to queue instances
- */
- private final CopyOnWriteArraySet<AMQQueue> _queues = new CopyOnWriteArraySet<AMQQueue>();
-
- /**
- * MBean class implementing the management interfaces.
- */
- @MBeanDescription("Management Bean for Fanout Exchange")
- private final class FanoutExchangeMBean extends ExchangeMBean
- {
- @MBeanConstructor("Creates an MBean for AMQ fanout exchange")
- public FanoutExchangeMBean() throws JMException
- {
- super();
- _exchangeType = "fanout";
- init();
- }
-
- public TabularData bindings() throws OpenDataException
- {
-
- _bindingList = new TabularDataSupport(_bindinglistDataType);
-
- for (AMQQueue queue : _queues)
- {
- String queueName = queue.getName().toString();
-
- Object[] bindingItemValues = {queueName, new String[]{queueName}};
- CompositeData bindingData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues);
- _bindingList.put(bindingData);
- }
-
- return _bindingList;
- }
-
- public void createNewBinding(String queueName, String binding) throws JMException
- {
- AMQQueue queue = getQueueRegistry().getQueue(new AMQShortString(queueName));
- if (queue == null)
- {
- throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange.");
- }
-
- try
- {
- queue.bind(new AMQShortString(binding), null, FanoutExchange.this);
- }
- catch (AMQException ex)
- {
- throw new MBeanException(ex);
- }
- }
-
- } // End of MBean class
-
- protected ExchangeMBean createMBean() throws AMQException
- {
- try
- {
- return new FanoutExchange.FanoutExchangeMBean();
- }
- catch (JMException ex)
- {
- _logger.error("Exception occured in creating the direct exchange mbean", ex);
- throw new AMQException("Exception occured in creating the direct exchange mbean", ex);
- }
- }
-
- public static final ExchangeType<FanoutExchange> TYPE = new ExchangeType<FanoutExchange>()
- {
-
- public AMQShortString getName()
- {
- return ExchangeDefaults.FANOUT_EXCHANGE_CLASS;
- }
-
- public Class<FanoutExchange> getExchangeClass()
- {
- return FanoutExchange.class;
- }
-
- public FanoutExchange newInstance(VirtualHost host,
- AMQShortString name,
- boolean durable,
- int ticket,
- boolean autoDelete) throws AMQException
- {
- FanoutExchange exch = new FanoutExchange();
- exch.initialise(host, name, durable, ticket, autoDelete);
- return exch;
- }
-
- public AMQShortString getDefaultExchangeName()
- {
- return ExchangeDefaults.FANOUT_EXCHANGE_NAME;
- }
- };
-
- public Map<AMQShortString, List<AMQQueue>> getBindings()
- {
- return null;
- }
-
- public AMQShortString getType()
- {
- return ExchangeDefaults.FANOUT_EXCHANGE_CLASS;
- }
-
- public void registerQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException
- {
- assert queue != null;
-
- if (_queues.contains(queue))
- {
- _logger.debug("Queue " + queue + " is already registered");
- }
- else
- {
- _queues.add(queue);
- _logger.debug("Binding queue " + queue + " with routing key " + routingKey + " to exchange " + this);
- }
- }
-
- public void deregisterQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException
- {
- assert queue != null;
-
- if (!_queues.remove(queue))
- {
- throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue + " was not registered with exchange " + this.getName() + ". ");
- }
- }
-
- public void route(AMQMessage payload) throws AMQException
- {
- final MessagePublishInfo publishInfo = payload.getMessagePublishInfo();
- final AMQShortString routingKey = publishInfo.getRoutingKey();
- if ((_queues == null) || _queues.isEmpty())
- {
- String msg = "No queues bound to " + this;
- if (publishInfo.isMandatory() || publishInfo.isImmediate())
- {
- throw new NoRouteException(msg, payload);
- }
- else
- {
- _logger.warn(msg);
- }
- }
- else
- {
- if (_logger.isDebugEnabled())
- {
- _logger.debug("Publishing message to queue " + _queues);
- }
-
- payload.enqueue(new ArrayList(_queues));
-
- }
- }
-
- public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue)
- {
- return isBound(routingKey, queue);
- }
-
- public boolean isBound(AMQShortString routingKey, AMQQueue queue)
- {
- return _queues.contains(queue);
- }
-
- public boolean isBound(AMQShortString routingKey)
- {
-
- return (_queues != null) && !_queues.isEmpty();
- }
-
- public boolean isBound(AMQQueue queue)
- {
-
- return _queues.contains(queue);
- }
-
- public boolean hasBindings()
- {
- return !_queues.isEmpty();
- }
-}
+/* + * + * 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.server.exchange; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.server.management.MBeanConstructor; +import org.apache.qpid.server.management.MBeanDescription; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import javax.management.JMException; +import javax.management.MBeanException; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import java.util.List; +import java.util.Map; +import java.util.ArrayList; +import java.util.concurrent.CopyOnWriteArraySet; + +public class FanoutExchange extends AbstractExchange +{ + private static final Logger _logger = Logger.getLogger(FanoutExchange.class); + + /** + * Maps from queue name to queue instances + */ + private final CopyOnWriteArraySet<AMQQueue> _queues = new CopyOnWriteArraySet<AMQQueue>(); + + /** + * MBean class implementing the management interfaces. + */ + @MBeanDescription("Management Bean for Fanout Exchange") + private final class FanoutExchangeMBean extends ExchangeMBean + { + @MBeanConstructor("Creates an MBean for AMQ fanout exchange") + public FanoutExchangeMBean() throws JMException + { + super(); + _exchangeType = "fanout"; + init(); + } + + public TabularData bindings() throws OpenDataException + { + + _bindingList = new TabularDataSupport(_bindinglistDataType); + + for (AMQQueue queue : _queues) + { + String queueName = queue.getName().toString(); + + Object[] bindingItemValues = {queueName, new String[]{queueName}}; + CompositeData bindingData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues); + _bindingList.put(bindingData); + } + + return _bindingList; + } + + public void createNewBinding(String queueName, String binding) throws JMException + { + AMQQueue queue = getQueueRegistry().getQueue(new AMQShortString(queueName)); + if (queue == null) + { + throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange."); + } + + try + { + queue.bind(new AMQShortString(binding), null, FanoutExchange.this); + } + catch (AMQException ex) + { + throw new MBeanException(ex); + } + } + + } // End of MBean class + + protected ExchangeMBean createMBean() throws AMQException + { + try + { + return new FanoutExchange.FanoutExchangeMBean(); + } + catch (JMException ex) + { + _logger.error("Exception occured in creating the direct exchange mbean", ex); + throw new AMQException("Exception occured in creating the direct exchange mbean", ex); + } + } + + public static final ExchangeType<FanoutExchange> TYPE = new ExchangeType<FanoutExchange>() + { + + public AMQShortString getName() + { + return ExchangeDefaults.FANOUT_EXCHANGE_CLASS; + } + + public Class<FanoutExchange> getExchangeClass() + { + return FanoutExchange.class; + } + + public FanoutExchange newInstance(VirtualHost host, + AMQShortString name, + boolean durable, + int ticket, + boolean autoDelete) throws AMQException + { + FanoutExchange exch = new FanoutExchange(); + exch.initialise(host, name, durable, ticket, autoDelete); + return exch; + } + + public AMQShortString getDefaultExchangeName() + { + return ExchangeDefaults.FANOUT_EXCHANGE_NAME; + } + }; + + public Map<AMQShortString, List<AMQQueue>> getBindings() + { + return null; + } + + public AMQShortString getType() + { + return ExchangeDefaults.FANOUT_EXCHANGE_CLASS; + } + + public void registerQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException + { + assert queue != null; + + if (_queues.contains(queue)) + { + _logger.debug("Queue " + queue + " is already registered"); + } + else + { + _queues.add(queue); + _logger.debug("Binding queue " + queue + " with routing key " + routingKey + " to exchange " + this); + } + } + + public void deregisterQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException + { + assert queue != null; + + if (!_queues.remove(queue)) + { + throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue + " was not registered with exchange " + this.getName() + ". "); + } + } + + public void route(AMQMessage payload) throws AMQException + { + final MessagePublishInfo publishInfo = payload.getMessagePublishInfo(); + final AMQShortString routingKey = publishInfo.getRoutingKey(); + if ((_queues == null) || _queues.isEmpty()) + { + String msg = "No queues bound to " + this; + if (publishInfo.isMandatory() || publishInfo.isImmediate()) + { + throw new NoRouteException(msg, payload); + } + else + { + _logger.warn(msg); + } + } + else + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Publishing message to queue " + _queues); + } + + payload.enqueue(new ArrayList(_queues)); + + } + } + + public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) + { + return isBound(routingKey, queue); + } + + public boolean isBound(AMQShortString routingKey, AMQQueue queue) + { + return _queues.contains(queue); + } + + public boolean isBound(AMQShortString routingKey) + { + + return (_queues != null) && !_queues.isEmpty(); + } + + public boolean isBound(AMQQueue queue) + { + + return _queues.contains(queue); + } + + public boolean hasBindings() + { + return !_queues.isEmpty(); + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java index dd712a404c..133c97a146 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java @@ -1,65 +1,65 @@ -/*
- *
- * 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.server.handler;
-
-import org.apache.qpid.framing.*;
-import org.apache.qpid.server.state.StateAwareMethodListener;
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.exchange.Exchange;
-import org.apache.qpid.AMQException;
-
-/**
- * @author Apache Software Foundation
- *
- *
- */
-public class AccessRequestHandler implements StateAwareMethodListener<AccessRequestBody>
-{
- private static final AccessRequestHandler _instance = new AccessRequestHandler();
-
-
- public static AccessRequestHandler getInstance()
- {
- return _instance;
- }
-
- private AccessRequestHandler()
- {
- }
-
- public void methodReceived(AMQStateManager stateManager, AccessRequestBody body, int channelId) throws AMQException
- {
- AMQProtocolSession session = stateManager.getProtocolSession();
-
- MethodRegistry methodRegistry = session.getMethodRegistry();
-
- // We don't implement access control class, but to keep clients happy that expect it
- // always use the "0" ticket.
- AccessRequestOkBody response = methodRegistry.createAccessRequestOkBody(0);
-
- session.writeFrame(response.generateFrame(channelId));
- }
-}
+/* + * + * 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.server.handler; + +import org.apache.qpid.framing.*; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.AMQException; + +/** + * @author Apache Software Foundation + * + * + */ +public class AccessRequestHandler implements StateAwareMethodListener<AccessRequestBody> +{ + private static final AccessRequestHandler _instance = new AccessRequestHandler(); + + + public static AccessRequestHandler getInstance() + { + return _instance; + } + + private AccessRequestHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, AccessRequestBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + MethodRegistry methodRegistry = session.getMethodRegistry(); + + // We don't implement access control class, but to keep clients happy that expect it + // always use the "0" ticket. + AccessRequestOkBody response = methodRegistry.createAccessRequestOkBody(0); + + session.writeFrame(response.generateFrame(channelId)); + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java index f8f9127809..3731116cba 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java @@ -1,101 +1,101 @@ -/*
- * 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.server.handler;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.BasicGetBody;
-import org.apache.qpid.framing.BasicGetEmptyBody;
-import org.apache.qpid.framing.MethodRegistry;
-import org.apache.qpid.protocol.AMQConstant;
-import org.apache.qpid.server.AMQChannel;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.security.access.Permission;
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.server.state.StateAwareMethodListener;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-
-public class BasicGetMethodHandler implements StateAwareMethodListener<BasicGetBody>
-{
- private static final Logger _log = Logger.getLogger(BasicGetMethodHandler.class);
-
- private static final BasicGetMethodHandler _instance = new BasicGetMethodHandler();
-
- public static BasicGetMethodHandler getInstance()
- {
- return _instance;
- }
-
- private BasicGetMethodHandler()
- {
- }
-
- public void methodReceived(AMQStateManager stateManager, BasicGetBody body, int channelId) throws AMQException
- {
- AMQProtocolSession session = stateManager.getProtocolSession();
-
-
- VirtualHost vHost = session.getVirtualHost();
-
- AMQChannel channel = session.getChannel(channelId);
- if (channel == null)
- {
- throw body.getChannelNotFoundException(channelId);
- }
- else
- {
- AMQQueue queue = body.getQueue() == null ? channel.getDefaultQueue() : vHost.getQueueRegistry().getQueue(body.getQueue());
-
- if (queue == null)
- {
- _log.info("No queue for '" + body.getQueue() + "'");
- if(body.getQueue()!=null)
- {
- throw body.getConnectionException(AMQConstant.NOT_FOUND,
- "No such queue, '" + body.getQueue()+ "'");
- }
- else
- {
- throw body.getConnectionException(AMQConstant.NOT_ALLOWED,
- "No queue name provided, no default queue defined.");
- }
- }
- else
- {
-
- //Perform ACLs
- vHost.getAccessManager().authorise(session, Permission.CONSUME, body, queue);
-
- if (!queue.performGet(session, channel, !body.getNoAck()))
- {
- MethodRegistry methodRegistry = session.getMethodRegistry();
- // TODO - set clusterId
- BasicGetEmptyBody responseBody = methodRegistry.createBasicGetEmptyBody(null);
-
-
- session.writeFrame(responseBody.generateFrame(channelId));
- }
- }
- }
- }
-}
+/* + * 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.server.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.BasicGetBody; +import org.apache.qpid.framing.BasicGetEmptyBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.security.access.Permission; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class BasicGetMethodHandler implements StateAwareMethodListener<BasicGetBody> +{ + private static final Logger _log = Logger.getLogger(BasicGetMethodHandler.class); + + private static final BasicGetMethodHandler _instance = new BasicGetMethodHandler(); + + public static BasicGetMethodHandler getInstance() + { + return _instance; + } + + private BasicGetMethodHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, BasicGetBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + + VirtualHost vHost = session.getVirtualHost(); + + AMQChannel channel = session.getChannel(channelId); + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + else + { + AMQQueue queue = body.getQueue() == null ? channel.getDefaultQueue() : vHost.getQueueRegistry().getQueue(body.getQueue()); + + if (queue == null) + { + _log.info("No queue for '" + body.getQueue() + "'"); + if(body.getQueue()!=null) + { + throw body.getConnectionException(AMQConstant.NOT_FOUND, + "No such queue, '" + body.getQueue()+ "'"); + } + else + { + throw body.getConnectionException(AMQConstant.NOT_ALLOWED, + "No queue name provided, no default queue defined."); + } + } + else + { + + //Perform ACLs + vHost.getAccessManager().authorise(session, Permission.CONSUME, body, queue); + + if (!queue.performGet(session, channel, !body.getNoAck())) + { + MethodRegistry methodRegistry = session.getMethodRegistry(); + // TODO - set clusterId + BasicGetEmptyBody responseBody = methodRegistry.createBasicGetEmptyBody(null); + + + session.writeFrame(responseBody.generateFrame(channelId)); + } + } + } + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverSyncMethodHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverSyncMethodHandler.java index 3e2cab2e53..bca35be535 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverSyncMethodHandler.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverSyncMethodHandler.java @@ -1,75 +1,75 @@ -/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-package org.apache.qpid.server.handler;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.framing.BasicRecoverBody;
-import org.apache.qpid.framing.ProtocolVersion;
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.framing.BasicRecoverSyncBody;
-import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9;
-import org.apache.qpid.framing.amqp_8_0.MethodRegistry_8_0;
-import org.apache.qpid.server.state.StateAwareMethodListener;
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.AMQChannel;
-import org.apache.qpid.AMQException;
-
-public class BasicRecoverSyncMethodHandler implements StateAwareMethodListener<BasicRecoverSyncBody>
-{
- private static final Logger _logger = Logger.getLogger(BasicRecoverSyncMethodHandler.class);
-
- private static final BasicRecoverSyncMethodHandler _instance = new BasicRecoverSyncMethodHandler();
-
- public static BasicRecoverSyncMethodHandler getInstance()
- {
- return _instance;
- }
-
- public void methodReceived(AMQStateManager stateManager, BasicRecoverSyncBody body, int channelId) throws AMQException
- {
- AMQProtocolSession session = stateManager.getProtocolSession();
-
- _logger.debug("Recover received on protocol session " + session + " and channel " + channelId);
- AMQChannel channel = session.getChannel(channelId);
-
-
- if (channel == null)
- {
- throw body.getChannelNotFoundException(channelId);
- }
-
- channel.resend(body.getRequeue());
-
- // Qpid 0-8 hacks a synchronous -ok onto recover.
- // In Qpid 0-9 we create a separate sync-recover, sync-recover-ok pair to be "more" compliant
- if(session.getProtocolVersion().equals(ProtocolVersion.v0_9))
- {
- MethodRegistry_0_9 methodRegistry = (MethodRegistry_0_9) session.getMethodRegistry();
- AMQMethodBody recoverOk = methodRegistry.createBasicRecoverSyncOkBody();
- session.writeFrame(recoverOk.generateFrame(channelId));
-
- }
-
- }
-}
+/* + * + * 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.server.handler; + +import org.apache.log4j.Logger; + +import org.apache.qpid.framing.BasicRecoverBody; +import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.BasicRecoverSyncBody; +import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9; +import org.apache.qpid.framing.amqp_8_0.MethodRegistry_8_0; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.AMQException; + +public class BasicRecoverSyncMethodHandler implements StateAwareMethodListener<BasicRecoverSyncBody> +{ + private static final Logger _logger = Logger.getLogger(BasicRecoverSyncMethodHandler.class); + + private static final BasicRecoverSyncMethodHandler _instance = new BasicRecoverSyncMethodHandler(); + + public static BasicRecoverSyncMethodHandler getInstance() + { + return _instance; + } + + public void methodReceived(AMQStateManager stateManager, BasicRecoverSyncBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + _logger.debug("Recover received on protocol session " + session + " and channel " + channelId); + AMQChannel channel = session.getChannel(channelId); + + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + channel.resend(body.getRequeue()); + + // Qpid 0-8 hacks a synchronous -ok onto recover. + // In Qpid 0-9 we create a separate sync-recover, sync-recover-ok pair to be "more" compliant + if(session.getProtocolVersion().equals(ProtocolVersion.v0_9)) + { + MethodRegistry_0_9 methodRegistry = (MethodRegistry_0_9) session.getMethodRegistry(); + AMQMethodBody recoverOk = methodRegistry.createBasicRecoverSyncOkBody(); + session.writeFrame(recoverOk.generateFrame(channelId)); + + } + + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java index cce49f13c7..a854c97f60 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java @@ -1,121 +1,121 @@ -/*
- * 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.server.handler;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.QueuePurgeBody;
-import org.apache.qpid.framing.QueuePurgeOkBody;
-import org.apache.qpid.framing.MethodRegistry;
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.protocol.AMQConstant;
-import org.apache.qpid.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;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-import org.apache.qpid.server.AMQChannel;
-import org.apache.qpid.server.security.access.Permission;
-
-public class QueuePurgeHandler implements StateAwareMethodListener<QueuePurgeBody>
-{
- private static final QueuePurgeHandler _instance = new QueuePurgeHandler();
-
- public static QueuePurgeHandler getInstance()
- {
- return _instance;
- }
-
- private final boolean _failIfNotFound;
-
- public QueuePurgeHandler()
- {
- this(true);
- }
-
- public QueuePurgeHandler(boolean failIfNotFound)
- {
- _failIfNotFound = failIfNotFound;
- }
-
- public void methodReceived(AMQStateManager stateManager, QueuePurgeBody body, int channelId) throws AMQException
- {
- AMQProtocolSession session = stateManager.getProtocolSession();
- VirtualHost virtualHost = session.getVirtualHost();
- QueueRegistry queueRegistry = virtualHost.getQueueRegistry();
-
- AMQChannel channel = session.getChannel(channelId);
-
-
- AMQQueue queue;
- if(body.getQueue() == null)
- {
-
- if (channel == null)
- {
- throw body.getChannelNotFoundException(channelId);
- }
-
- //get the default queue on the channel:
- queue = channel.getDefaultQueue();
-
- if(queue == null)
- {
- if(_failIfNotFound)
- {
- throw body.getConnectionException(AMQConstant.NOT_ALLOWED,"No queue specified.");
- }
- }
- }
- else
- {
- queue = queueRegistry.getQueue(body.getQueue());
- }
-
- if(queue == null)
- {
- if(_failIfNotFound)
- {
- throw body.getChannelException(AMQConstant.NOT_FOUND, "Queue " + body.getQueue() + " does not exist.");
- }
- }
- else
- {
-
- //Perform ACLs
- virtualHost.getAccessManager().authorise(session, Permission.PURGE, body, queue);
-
- long purged = queue.clearQueue(channel.getStoreContext());
-
-
- if(!body.getNowait())
- {
-
- MethodRegistry methodRegistry = session.getMethodRegistry();
- AMQMethodBody responseBody = methodRegistry.createQueuePurgeOkBody(purged);
- session.writeFrame(responseBody.generateFrame(channelId));
-
- }
- }
- }
-}
+/* + * 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.server.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.QueuePurgeBody; +import org.apache.qpid.framing.QueuePurgeOkBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.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; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.security.access.Permission; + +public class QueuePurgeHandler implements StateAwareMethodListener<QueuePurgeBody> +{ + private static final QueuePurgeHandler _instance = new QueuePurgeHandler(); + + public static QueuePurgeHandler getInstance() + { + return _instance; + } + + private final boolean _failIfNotFound; + + public QueuePurgeHandler() + { + this(true); + } + + public QueuePurgeHandler(boolean failIfNotFound) + { + _failIfNotFound = failIfNotFound; + } + + public void methodReceived(AMQStateManager stateManager, QueuePurgeBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + + AMQChannel channel = session.getChannel(channelId); + + + AMQQueue queue; + if(body.getQueue() == null) + { + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + //get the default queue on the channel: + queue = channel.getDefaultQueue(); + + if(queue == null) + { + if(_failIfNotFound) + { + throw body.getConnectionException(AMQConstant.NOT_ALLOWED,"No queue specified."); + } + } + } + else + { + queue = queueRegistry.getQueue(body.getQueue()); + } + + if(queue == null) + { + if(_failIfNotFound) + { + throw body.getChannelException(AMQConstant.NOT_FOUND, "Queue " + body.getQueue() + " does not exist."); + } + } + else + { + + //Perform ACLs + virtualHost.getAccessManager().authorise(session, Permission.PURGE, body, queue); + + long purged = queue.clearQueue(channel.getStoreContext()); + + + if(!body.getNowait()) + { + + MethodRegistry methodRegistry = session.getMethodRegistry(); + AMQMethodBody responseBody = methodRegistry.createQueuePurgeOkBody(purged); + session.writeFrame(responseBody.generateFrame(channelId)); + + } + } + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl.java index 9475b83c8f..d24e4f6ffa 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl.java @@ -1,566 +1,566 @@ -/*
- *
- * 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.server.handler;
-
-import java.util.Map;
-import java.util.HashMap;
-
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.framing.*;
-import org.apache.qpid.AMQException;
-
-public class ServerMethodDispatcherImpl implements MethodDispatcher
-{
- private final AMQStateManager _stateManager;
-
- private static interface DispatcherFactory
- {
- public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager);
- }
-
- private static final Map<ProtocolVersion, DispatcherFactory> _dispatcherFactories =
- new HashMap<ProtocolVersion, DispatcherFactory>();
-
-
- static
- {
- _dispatcherFactories.put(ProtocolVersion.v8_0,
- new DispatcherFactory()
- {
- public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager)
- {
- return new ServerMethodDispatcherImpl_8_0(stateManager);
- }
- });
-
- _dispatcherFactories.put(ProtocolVersion.v0_9,
- new DispatcherFactory()
- {
- public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager)
- {
- return new ServerMethodDispatcherImpl_0_9(stateManager);
- }
- });
-
- }
-
-
- private static final AccessRequestHandler _accessRequestHandler = AccessRequestHandler.getInstance();
- private static final ChannelCloseHandler _channelCloseHandler = ChannelCloseHandler.getInstance();
- private static final ChannelOpenHandler _channelOpenHandler = ChannelOpenHandler.getInstance();
- private static final ChannelCloseOkHandler _channelCloseOkHandler = ChannelCloseOkHandler.getInstance();
- private static final ConnectionCloseMethodHandler _connectionCloseMethodHandler = ConnectionCloseMethodHandler.getInstance();
- private static final ConnectionCloseOkMethodHandler _connectionCloseOkMethodHandler = ConnectionCloseOkMethodHandler.getInstance();
- private static final ConnectionOpenMethodHandler _connectionOpenMethodHandler = ConnectionOpenMethodHandler.getInstance();
- private static final ConnectionTuneOkMethodHandler _connectionTuneOkMethodHandler = ConnectionTuneOkMethodHandler.getInstance();
- private static final ConnectionSecureOkMethodHandler _connectionSecureOkMethodHandler = ConnectionSecureOkMethodHandler.getInstance();
- private static final ConnectionStartOkMethodHandler _connectionStartOkMethodHandler = ConnectionStartOkMethodHandler.getInstance();
- private static final ExchangeDeclareHandler _exchangeDeclareHandler = ExchangeDeclareHandler.getInstance();
- private static final ExchangeDeleteHandler _exchangeDeleteHandler = ExchangeDeleteHandler.getInstance();
- private static final ExchangeBoundHandler _exchangeBoundHandler = ExchangeBoundHandler.getInstance();
- private static final BasicAckMethodHandler _basicAckMethodHandler = BasicAckMethodHandler.getInstance();
- private static final BasicRecoverMethodHandler _basicRecoverMethodHandler = BasicRecoverMethodHandler.getInstance();
- private static final BasicConsumeMethodHandler _basicConsumeMethodHandler = BasicConsumeMethodHandler.getInstance();
- private static final BasicGetMethodHandler _basicGetMethodHandler = BasicGetMethodHandler.getInstance();
- private static final BasicCancelMethodHandler _basicCancelMethodHandler = BasicCancelMethodHandler.getInstance();
- private static final BasicPublishMethodHandler _basicPublishMethodHandler = BasicPublishMethodHandler.getInstance();
- private static final BasicQosHandler _basicQosHandler = BasicQosHandler.getInstance();
- private static final QueueBindHandler _queueBindHandler = QueueBindHandler.getInstance();
- private static final QueueDeclareHandler _queueDeclareHandler = QueueDeclareHandler.getInstance();
- private static final QueueDeleteHandler _queueDeleteHandler = QueueDeleteHandler.getInstance();
- private static final QueuePurgeHandler _queuePurgeHandler = QueuePurgeHandler.getInstance();
- private static final ChannelFlowHandler _channelFlowHandler = ChannelFlowHandler.getInstance();
- private static final TxSelectHandler _txSelectHandler = TxSelectHandler.getInstance();
- private static final TxCommitHandler _txCommitHandler = TxCommitHandler.getInstance();
- private static final TxRollbackHandler _txRollbackHandler = TxRollbackHandler.getInstance();
- private static final BasicRejectMethodHandler _basicRejectMethodHandler = BasicRejectMethodHandler.getInstance();
-
-
-
- public static MethodDispatcher createMethodDispatcher(AMQStateManager stateManager, ProtocolVersion protocolVersion)
- {
- return _dispatcherFactories.get(protocolVersion).createMethodDispatcher(stateManager);
- }
-
-
- public ServerMethodDispatcherImpl(AMQStateManager stateManager)
- {
- _stateManager = stateManager;
- }
-
-
- protected AMQStateManager getStateManager()
- {
- return _stateManager;
- }
-
-
-
- public boolean dispatchAccessRequest(AccessRequestBody body, int channelId) throws AMQException
- {
- _accessRequestHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchBasicAck(BasicAckBody body, int channelId) throws AMQException
- {
- _basicAckMethodHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchBasicCancel(BasicCancelBody body, int channelId) throws AMQException
- {
- _basicCancelMethodHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchBasicConsume(BasicConsumeBody body, int channelId) throws AMQException
- {
- _basicConsumeMethodHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchBasicGet(BasicGetBody body, int channelId) throws AMQException
- {
- _basicGetMethodHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchBasicPublish(BasicPublishBody body, int channelId) throws AMQException
- {
- _basicPublishMethodHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchBasicQos(BasicQosBody body, int channelId) throws AMQException
- {
- _basicQosHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchBasicRecover(BasicRecoverBody body, int channelId) throws AMQException
- {
- _basicRecoverMethodHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchBasicReject(BasicRejectBody body, int channelId) throws AMQException
- {
- _basicRejectMethodHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchChannelOpen(ChannelOpenBody body, int channelId) throws AMQException
- {
- _channelOpenHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
-
- public boolean dispatchAccessRequestOk(AccessRequestOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchBasicCancelOk(BasicCancelOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchBasicConsumeOk(BasicConsumeOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchBasicDeliver(BasicDeliverBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchBasicGetEmpty(BasicGetEmptyBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchBasicGetOk(BasicGetOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchBasicQosOk(BasicQosOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchBasicReturn(BasicReturnBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchChannelClose(ChannelCloseBody body, int channelId) throws AMQException
- {
- _channelCloseHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
-
- public boolean dispatchChannelCloseOk(ChannelCloseOkBody body, int channelId) throws AMQException
- {
- _channelCloseOkHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
-
- public boolean dispatchChannelFlow(ChannelFlowBody body, int channelId) throws AMQException
- {
- _channelFlowHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchChannelFlowOk(ChannelFlowOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchChannelOpenOk(ChannelOpenOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
-
- public boolean dispatchConnectionOpen(ConnectionOpenBody body, int channelId) throws AMQException
- {
- _connectionOpenMethodHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
-
- public boolean dispatchConnectionClose(ConnectionCloseBody body, int channelId) throws AMQException
- {
- _connectionCloseMethodHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
-
- public boolean dispatchConnectionCloseOk(ConnectionCloseOkBody body, int channelId) throws AMQException
- {
- _connectionCloseOkMethodHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchConnectionOpenOk(ConnectionOpenOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchConnectionRedirect(ConnectionRedirectBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchConnectionSecure(ConnectionSecureBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchConnectionStart(ConnectionStartBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchConnectionTune(ConnectionTuneBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchDtxSelectOk(DtxSelectOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchDtxStartOk(DtxStartOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchExchangeBoundOk(ExchangeBoundOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchExchangeDeclareOk(ExchangeDeclareOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchExchangeDeleteOk(ExchangeDeleteOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchFileCancelOk(FileCancelOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchFileConsumeOk(FileConsumeOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchFileDeliver(FileDeliverBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchFileOpen(FileOpenBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchFileOpenOk(FileOpenOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchFileQosOk(FileQosOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchFileReturn(FileReturnBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchFileStage(FileStageBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchQueueBindOk(QueueBindOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchQueueDeclareOk(QueueDeclareOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchQueueDeleteOk(QueueDeleteOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchQueuePurgeOk(QueuePurgeOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchStreamCancelOk(StreamCancelOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchStreamConsumeOk(StreamConsumeOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchStreamDeliver(StreamDeliverBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchStreamQosOk(StreamQosOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchStreamReturn(StreamReturnBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchTxCommitOk(TxCommitOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchTxRollbackOk(TxRollbackOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchTxSelectOk(TxSelectOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
-
- public boolean dispatchConnectionSecureOk(ConnectionSecureOkBody body, int channelId) throws AMQException
- {
- _connectionSecureOkMethodHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchConnectionStartOk(ConnectionStartOkBody body, int channelId) throws AMQException
- {
- _connectionStartOkMethodHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchConnectionTuneOk(ConnectionTuneOkBody body, int channelId) throws AMQException
- {
- _connectionTuneOkMethodHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchDtxSelect(DtxSelectBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchDtxStart(DtxStartBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchExchangeBound(ExchangeBoundBody body, int channelId) throws AMQException
- {
- _exchangeBoundHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchExchangeDeclare(ExchangeDeclareBody body, int channelId) throws AMQException
- {
- _exchangeDeclareHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchExchangeDelete(ExchangeDeleteBody body, int channelId) throws AMQException
- {
- _exchangeDeleteHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchFileAck(FileAckBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchFileCancel(FileCancelBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchFileConsume(FileConsumeBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchFilePublish(FilePublishBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchFileQos(FileQosBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchFileReject(FileRejectBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchQueueBind(QueueBindBody body, int channelId) throws AMQException
- {
- _queueBindHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchQueueDeclare(QueueDeclareBody body, int channelId) throws AMQException
- {
- _queueDeclareHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchQueueDelete(QueueDeleteBody body, int channelId) throws AMQException
- {
- _queueDeleteHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchQueuePurge(QueuePurgeBody body, int channelId) throws AMQException
- {
- _queuePurgeHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchStreamCancel(StreamCancelBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchStreamConsume(StreamConsumeBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchStreamPublish(StreamPublishBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchStreamQos(StreamQosBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchTunnelRequest(TunnelRequestBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchTxCommit(TxCommitBody body, int channelId) throws AMQException
- {
- _txCommitHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchTxRollback(TxRollbackBody body, int channelId) throws AMQException
- {
- _txRollbackHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
- public boolean dispatchTxSelect(TxSelectBody body, int channelId) throws AMQException
- {
- _txSelectHandler.methodReceived(_stateManager, body, channelId);
- return true;
- }
-
-
-
-
-}
+/* + * + * 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.server.handler; + +import java.util.Map; +import java.util.HashMap; + +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.framing.*; +import org.apache.qpid.AMQException; + +public class ServerMethodDispatcherImpl implements MethodDispatcher +{ + private final AMQStateManager _stateManager; + + private static interface DispatcherFactory + { + public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager); + } + + private static final Map<ProtocolVersion, DispatcherFactory> _dispatcherFactories = + new HashMap<ProtocolVersion, DispatcherFactory>(); + + + static + { + _dispatcherFactories.put(ProtocolVersion.v8_0, + new DispatcherFactory() + { + public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager) + { + return new ServerMethodDispatcherImpl_8_0(stateManager); + } + }); + + _dispatcherFactories.put(ProtocolVersion.v0_9, + new DispatcherFactory() + { + public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager) + { + return new ServerMethodDispatcherImpl_0_9(stateManager); + } + }); + + } + + + private static final AccessRequestHandler _accessRequestHandler = AccessRequestHandler.getInstance(); + private static final ChannelCloseHandler _channelCloseHandler = ChannelCloseHandler.getInstance(); + private static final ChannelOpenHandler _channelOpenHandler = ChannelOpenHandler.getInstance(); + private static final ChannelCloseOkHandler _channelCloseOkHandler = ChannelCloseOkHandler.getInstance(); + private static final ConnectionCloseMethodHandler _connectionCloseMethodHandler = ConnectionCloseMethodHandler.getInstance(); + private static final ConnectionCloseOkMethodHandler _connectionCloseOkMethodHandler = ConnectionCloseOkMethodHandler.getInstance(); + private static final ConnectionOpenMethodHandler _connectionOpenMethodHandler = ConnectionOpenMethodHandler.getInstance(); + private static final ConnectionTuneOkMethodHandler _connectionTuneOkMethodHandler = ConnectionTuneOkMethodHandler.getInstance(); + private static final ConnectionSecureOkMethodHandler _connectionSecureOkMethodHandler = ConnectionSecureOkMethodHandler.getInstance(); + private static final ConnectionStartOkMethodHandler _connectionStartOkMethodHandler = ConnectionStartOkMethodHandler.getInstance(); + private static final ExchangeDeclareHandler _exchangeDeclareHandler = ExchangeDeclareHandler.getInstance(); + private static final ExchangeDeleteHandler _exchangeDeleteHandler = ExchangeDeleteHandler.getInstance(); + private static final ExchangeBoundHandler _exchangeBoundHandler = ExchangeBoundHandler.getInstance(); + private static final BasicAckMethodHandler _basicAckMethodHandler = BasicAckMethodHandler.getInstance(); + private static final BasicRecoverMethodHandler _basicRecoverMethodHandler = BasicRecoverMethodHandler.getInstance(); + private static final BasicConsumeMethodHandler _basicConsumeMethodHandler = BasicConsumeMethodHandler.getInstance(); + private static final BasicGetMethodHandler _basicGetMethodHandler = BasicGetMethodHandler.getInstance(); + private static final BasicCancelMethodHandler _basicCancelMethodHandler = BasicCancelMethodHandler.getInstance(); + private static final BasicPublishMethodHandler _basicPublishMethodHandler = BasicPublishMethodHandler.getInstance(); + private static final BasicQosHandler _basicQosHandler = BasicQosHandler.getInstance(); + private static final QueueBindHandler _queueBindHandler = QueueBindHandler.getInstance(); + private static final QueueDeclareHandler _queueDeclareHandler = QueueDeclareHandler.getInstance(); + private static final QueueDeleteHandler _queueDeleteHandler = QueueDeleteHandler.getInstance(); + private static final QueuePurgeHandler _queuePurgeHandler = QueuePurgeHandler.getInstance(); + private static final ChannelFlowHandler _channelFlowHandler = ChannelFlowHandler.getInstance(); + private static final TxSelectHandler _txSelectHandler = TxSelectHandler.getInstance(); + private static final TxCommitHandler _txCommitHandler = TxCommitHandler.getInstance(); + private static final TxRollbackHandler _txRollbackHandler = TxRollbackHandler.getInstance(); + private static final BasicRejectMethodHandler _basicRejectMethodHandler = BasicRejectMethodHandler.getInstance(); + + + + public static MethodDispatcher createMethodDispatcher(AMQStateManager stateManager, ProtocolVersion protocolVersion) + { + return _dispatcherFactories.get(protocolVersion).createMethodDispatcher(stateManager); + } + + + public ServerMethodDispatcherImpl(AMQStateManager stateManager) + { + _stateManager = stateManager; + } + + + protected AMQStateManager getStateManager() + { + return _stateManager; + } + + + + public boolean dispatchAccessRequest(AccessRequestBody body, int channelId) throws AMQException + { + _accessRequestHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicAck(BasicAckBody body, int channelId) throws AMQException + { + _basicAckMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicCancel(BasicCancelBody body, int channelId) throws AMQException + { + _basicCancelMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicConsume(BasicConsumeBody body, int channelId) throws AMQException + { + _basicConsumeMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicGet(BasicGetBody body, int channelId) throws AMQException + { + _basicGetMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicPublish(BasicPublishBody body, int channelId) throws AMQException + { + _basicPublishMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicQos(BasicQosBody body, int channelId) throws AMQException + { + _basicQosHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicRecover(BasicRecoverBody body, int channelId) throws AMQException + { + _basicRecoverMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicReject(BasicRejectBody body, int channelId) throws AMQException + { + _basicRejectMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchChannelOpen(ChannelOpenBody body, int channelId) throws AMQException + { + _channelOpenHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + + public boolean dispatchAccessRequestOk(AccessRequestOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicCancelOk(BasicCancelOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicConsumeOk(BasicConsumeOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicDeliver(BasicDeliverBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicGetEmpty(BasicGetEmptyBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicGetOk(BasicGetOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicQosOk(BasicQosOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicReturn(BasicReturnBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchChannelClose(ChannelCloseBody body, int channelId) throws AMQException + { + _channelCloseHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + + public boolean dispatchChannelCloseOk(ChannelCloseOkBody body, int channelId) throws AMQException + { + _channelCloseOkHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + + public boolean dispatchChannelFlow(ChannelFlowBody body, int channelId) throws AMQException + { + _channelFlowHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchChannelFlowOk(ChannelFlowOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchChannelOpenOk(ChannelOpenOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + + public boolean dispatchConnectionOpen(ConnectionOpenBody body, int channelId) throws AMQException + { + _connectionOpenMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + + public boolean dispatchConnectionClose(ConnectionCloseBody body, int channelId) throws AMQException + { + _connectionCloseMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + + public boolean dispatchConnectionCloseOk(ConnectionCloseOkBody body, int channelId) throws AMQException + { + _connectionCloseOkMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchConnectionOpenOk(ConnectionOpenOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchConnectionRedirect(ConnectionRedirectBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchConnectionSecure(ConnectionSecureBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchConnectionStart(ConnectionStartBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchConnectionTune(ConnectionTuneBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchDtxSelectOk(DtxSelectOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchDtxStartOk(DtxStartOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchExchangeBoundOk(ExchangeBoundOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchExchangeDeclareOk(ExchangeDeclareOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchExchangeDeleteOk(ExchangeDeleteOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileCancelOk(FileCancelOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileConsumeOk(FileConsumeOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileDeliver(FileDeliverBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileOpen(FileOpenBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileOpenOk(FileOpenOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileQosOk(FileQosOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileReturn(FileReturnBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileStage(FileStageBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchQueueBindOk(QueueBindOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchQueueDeclareOk(QueueDeclareOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchQueueDeleteOk(QueueDeleteOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchQueuePurgeOk(QueuePurgeOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchStreamCancelOk(StreamCancelOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchStreamConsumeOk(StreamConsumeOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchStreamDeliver(StreamDeliverBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchStreamQosOk(StreamQosOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchStreamReturn(StreamReturnBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchTxCommitOk(TxCommitOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchTxRollbackOk(TxRollbackOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchTxSelectOk(TxSelectOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + + public boolean dispatchConnectionSecureOk(ConnectionSecureOkBody body, int channelId) throws AMQException + { + _connectionSecureOkMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchConnectionStartOk(ConnectionStartOkBody body, int channelId) throws AMQException + { + _connectionStartOkMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchConnectionTuneOk(ConnectionTuneOkBody body, int channelId) throws AMQException + { + _connectionTuneOkMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchDtxSelect(DtxSelectBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchDtxStart(DtxStartBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchExchangeBound(ExchangeBoundBody body, int channelId) throws AMQException + { + _exchangeBoundHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchExchangeDeclare(ExchangeDeclareBody body, int channelId) throws AMQException + { + _exchangeDeclareHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchExchangeDelete(ExchangeDeleteBody body, int channelId) throws AMQException + { + _exchangeDeleteHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchFileAck(FileAckBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchFileCancel(FileCancelBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchFileConsume(FileConsumeBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchFilePublish(FilePublishBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchFileQos(FileQosBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchFileReject(FileRejectBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchQueueBind(QueueBindBody body, int channelId) throws AMQException + { + _queueBindHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchQueueDeclare(QueueDeclareBody body, int channelId) throws AMQException + { + _queueDeclareHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchQueueDelete(QueueDeleteBody body, int channelId) throws AMQException + { + _queueDeleteHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchQueuePurge(QueuePurgeBody body, int channelId) throws AMQException + { + _queuePurgeHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchStreamCancel(StreamCancelBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchStreamConsume(StreamConsumeBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchStreamPublish(StreamPublishBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchStreamQos(StreamQosBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTunnelRequest(TunnelRequestBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTxCommit(TxCommitBody body, int channelId) throws AMQException + { + _txCommitHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchTxRollback(TxRollbackBody body, int channelId) throws AMQException + { + _txRollbackHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchTxSelect(TxSelectBody body, int channelId) throws AMQException + { + _txSelectHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + + + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_0_9.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_0_9.java index 8b1dca77ba..382a85347b 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_0_9.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_0_9.java @@ -1,164 +1,164 @@ -/*
- *
- * 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.server.handler;
-
-
-import org.apache.qpid.framing.amqp_0_9.MethodDispatcher_0_9;
-import org.apache.qpid.framing.*;
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.AMQException;
-
-
-
-public class ServerMethodDispatcherImpl_0_9
- extends ServerMethodDispatcherImpl
- implements MethodDispatcher_0_9
-
-{
-
- private static final BasicRecoverSyncMethodHandler _basicRecoverSyncMethodHandler =
- BasicRecoverSyncMethodHandler.getInstance();
- private static final QueueUnbindHandler _queueUnbindHandler =
- QueueUnbindHandler.getInstance();
-
-
- public ServerMethodDispatcherImpl_0_9(AMQStateManager stateManager)
- {
- super(stateManager);
- }
-
- public boolean dispatchBasicRecoverSync(BasicRecoverSyncBody body, int channelId) throws AMQException
- {
- _basicRecoverSyncMethodHandler.methodReceived(getStateManager(), body, channelId);
- return true;
- }
-
- public boolean dispatchBasicRecoverSyncOk(BasicRecoverSyncOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchChannelOk(ChannelOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchChannelPing(ChannelPingBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchChannelPong(ChannelPongBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchChannelResume(ChannelResumeBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageAppend(MessageAppendBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageCancel(MessageCancelBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageCheckpoint(MessageCheckpointBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageClose(MessageCloseBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageConsume(MessageConsumeBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageEmpty(MessageEmptyBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageGet(MessageGetBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageOffset(MessageOffsetBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageOk(MessageOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageOpen(MessageOpenBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageQos(MessageQosBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageRecover(MessageRecoverBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageReject(MessageRejectBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageResume(MessageResumeBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageTransfer(MessageTransferBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchQueueUnbindOk(QueueUnbindOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchQueueUnbind(QueueUnbindBody body, int channelId) throws AMQException
- {
- _queueUnbindHandler.methodReceived(getStateManager(),body,channelId);
- return true;
- }
-}
+/* + * + * 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.server.handler; + + +import org.apache.qpid.framing.amqp_0_9.MethodDispatcher_0_9; +import org.apache.qpid.framing.*; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.AMQException; + + + +public class ServerMethodDispatcherImpl_0_9 + extends ServerMethodDispatcherImpl + implements MethodDispatcher_0_9 + +{ + + private static final BasicRecoverSyncMethodHandler _basicRecoverSyncMethodHandler = + BasicRecoverSyncMethodHandler.getInstance(); + private static final QueueUnbindHandler _queueUnbindHandler = + QueueUnbindHandler.getInstance(); + + + public ServerMethodDispatcherImpl_0_9(AMQStateManager stateManager) + { + super(stateManager); + } + + public boolean dispatchBasicRecoverSync(BasicRecoverSyncBody body, int channelId) throws AMQException + { + _basicRecoverSyncMethodHandler.methodReceived(getStateManager(), body, channelId); + return true; + } + + public boolean dispatchBasicRecoverSyncOk(BasicRecoverSyncOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchChannelOk(ChannelOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelPing(ChannelPingBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelPong(ChannelPongBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelResume(ChannelResumeBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageAppend(MessageAppendBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageCancel(MessageCancelBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageCheckpoint(MessageCheckpointBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageClose(MessageCloseBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageConsume(MessageConsumeBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageEmpty(MessageEmptyBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageGet(MessageGetBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageOffset(MessageOffsetBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageOk(MessageOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageOpen(MessageOpenBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageQos(MessageQosBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageRecover(MessageRecoverBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageReject(MessageRejectBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageResume(MessageResumeBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageTransfer(MessageTransferBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchQueueUnbindOk(QueueUnbindOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchQueueUnbind(QueueUnbindBody body, int channelId) throws AMQException + { + _queueUnbindHandler.methodReceived(getStateManager(),body,channelId); + return true; + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_8_0.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_8_0.java index d599ca3d4e..22f64cf7d3 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_8_0.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_8_0.java @@ -1,86 +1,86 @@ -/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.handler;
-
-import org.apache.qpid.framing.amqp_8_0.MethodDispatcher_8_0;
-import org.apache.qpid.framing.*;
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.AMQException;
-
-public class ServerMethodDispatcherImpl_8_0
- extends ServerMethodDispatcherImpl
- implements MethodDispatcher_8_0
-{
- public ServerMethodDispatcherImpl_8_0(AMQStateManager stateManager)
- {
- super(stateManager);
- }
-
- public boolean dispatchBasicRecoverOk(BasicRecoverOkBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchChannelAlert(ChannelAlertBody body, int channelId) throws AMQException
- {
- throw new UnexpectedMethodException(body);
- }
-
- public boolean dispatchTestContent(TestContentBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchTestContentOk(TestContentOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchTestInteger(TestIntegerBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchTestIntegerOk(TestIntegerOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchTestString(TestStringBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchTestStringOk(TestStringOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchTestTable(TestTableBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchTestTableOk(TestTableOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-}
+/* + * + * 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.server.handler; + +import org.apache.qpid.framing.amqp_8_0.MethodDispatcher_8_0; +import org.apache.qpid.framing.*; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.AMQException; + +public class ServerMethodDispatcherImpl_8_0 + extends ServerMethodDispatcherImpl + implements MethodDispatcher_8_0 +{ + public ServerMethodDispatcherImpl_8_0(AMQStateManager stateManager) + { + super(stateManager); + } + + public boolean dispatchBasicRecoverOk(BasicRecoverOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchChannelAlert(ChannelAlertBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchTestContent(TestContentBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestContentOk(TestContentOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestInteger(TestIntegerBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestIntegerOk(TestIntegerOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestString(TestStringBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestStringOk(TestStringOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestTable(TestTableBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestTableOk(TestTableOkBody body, int channelId) throws AMQException + { + return false; + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/UnexpectedMethodException.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/UnexpectedMethodException.java index fb18519fe1..0abb3cdd7d 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/UnexpectedMethodException.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/UnexpectedMethodException.java @@ -1,33 +1,33 @@ -/*
- *
- * 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.server.handler;
-
-
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.AMQException;
-
-public class UnexpectedMethodException extends AMQException
-{
- public UnexpectedMethodException(AMQMethodBody body)
- {
- super("Unexpected method recevied: " + body.getClass().getName());
- }
-}
+/* + * + * 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.server.handler; + + +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.AMQException; + +public class UnexpectedMethodException extends AMQException +{ + public UnexpectedMethodException(AMQMethodBody body) + { + super("Unexpected method recevied: " + body.getClass().getName()); + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java index e01c5aabbf..576d577b40 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java @@ -1,57 +1,57 @@ -/*
- *
- * 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.
- *
- */
-
-/*
- * This file is auto-generated by Qpid Gentools v.0.1 - do not modify.
- * Supported AMQP versions:
- * 8-0
- */
-package org.apache.qpid.server.output;
-
-import org.apache.qpid.server.queue.AMQMessage;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.AMQDataBlock;
-import org.apache.qpid.AMQException;
-
-public interface ProtocolOutputConverter
-{
- void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag);
-
- interface Factory
- {
- ProtocolOutputConverter newInstance(AMQProtocolSession session);
- }
-
- void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag)
- throws AMQException;
-
- void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException;
-
- byte getProtocolMinorVersion();
-
- byte getProtocolMajorVersion();
-
- void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText)
- throws AMQException;
-
- void writeFrame(AMQDataBlock block);
-}
+/* + * + * 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. + * + */ + +/* + * This file is auto-generated by Qpid Gentools v.0.1 - do not modify. + * Supported AMQP versions: + * 8-0 + */ +package org.apache.qpid.server.output; + +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.AMQException; + +public interface ProtocolOutputConverter +{ + void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag); + + interface Factory + { + ProtocolOutputConverter newInstance(AMQProtocolSession session); + } + + void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) + throws AMQException; + + void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException; + + byte getProtocolMinorVersion(); + + byte getProtocolMajorVersion(); + + void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) + throws AMQException; + + void writeFrame(AMQDataBlock block); +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java index 36e7e88fd6..02fb1429c0 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java @@ -1,61 +1,61 @@ -/*
- *
- * 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.
- *
- */
-
-/*
- * This file is auto-generated by Qpid Gentools v.0.1 - do not modify.
- * Supported AMQP versions:
- * 8-0
- */
-package org.apache.qpid.server.output;
-
-import org.apache.qpid.server.output.ProtocolOutputConverter.Factory;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.framing.ProtocolVersion;
-
-import java.util.Map;
-import java.util.HashMap;
-
-public class ProtocolOutputConverterRegistry
-{
-
- private static final Map<ProtocolVersion, Factory> _registry =
- new HashMap<ProtocolVersion, Factory>();
-
-
- static
- {
- register(ProtocolVersion.v8_0, org.apache.qpid.server.output.amqp0_8.ProtocolOutputConverterImpl.getInstanceFactory());
- register(ProtocolVersion.v0_9, org.apache.qpid.server.output.amqp0_9.ProtocolOutputConverterImpl.getInstanceFactory());
-
- }
-
- private static void register(ProtocolVersion version, Factory converter)
- {
-
- _registry.put(version,converter);
- }
-
-
- public static ProtocolOutputConverter getConverter(AMQProtocolSession session)
- {
- return _registry.get(session.getProtocolVersion()).newInstance(session);
- }
-}
+/* + * + * 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. + * + */ + +/* + * This file is auto-generated by Qpid Gentools v.0.1 - do not modify. + * Supported AMQP versions: + * 8-0 + */ +package org.apache.qpid.server.output; + +import org.apache.qpid.server.output.ProtocolOutputConverter.Factory; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.framing.ProtocolVersion; + +import java.util.Map; +import java.util.HashMap; + +public class ProtocolOutputConverterRegistry +{ + + private static final Map<ProtocolVersion, Factory> _registry = + new HashMap<ProtocolVersion, Factory>(); + + + static + { + register(ProtocolVersion.v8_0, org.apache.qpid.server.output.amqp0_8.ProtocolOutputConverterImpl.getInstanceFactory()); + register(ProtocolVersion.v0_9, org.apache.qpid.server.output.amqp0_9.ProtocolOutputConverterImpl.getInstanceFactory()); + + } + + private static void register(ProtocolVersion version, Factory converter) + { + + _registry.put(version,converter); + } + + + public static ProtocolOutputConverter getConverter(AMQProtocolSession session) + { + return _registry.get(session.getProtocolVersion()).newInstance(session); + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java index d7a879180a..d4cb7c878f 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java @@ -1,285 +1,285 @@ -/*
- *
- * 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.
- *
- */
-
-/*
- * This file is auto-generated by Qpid Gentools v.0.1 - do not modify.
- * Supported AMQP versions:
- * 8-0
- */
-package org.apache.qpid.server.output.amqp0_8;
-
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.AMQMessage;
-import org.apache.qpid.server.queue.AMQMessageHandle;
-import org.apache.qpid.server.store.StoreContext;
-import org.apache.qpid.server.output.ProtocolOutputConverter;
-import org.apache.qpid.framing.*;
-import org.apache.qpid.framing.abstraction.ContentChunk;
-import org.apache.qpid.framing.abstraction.MessagePublishInfo;
-import org.apache.qpid.AMQException;
-
-import org.apache.mina.common.ByteBuffer;
-
-import java.util.Iterator;
-
-public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
-{
-
-
- public static Factory getInstanceFactory()
- {
- return new Factory()
- {
-
- public ProtocolOutputConverter newInstance(AMQProtocolSession session)
- {
- return new ProtocolOutputConverterImpl(session);
- }
- };
- }
-
- private final AMQProtocolSession _protocolSession;
-
- private ProtocolOutputConverterImpl(AMQProtocolSession session)
- {
- _protocolSession = session;
- }
-
-
- public AMQProtocolSession getProtocolSession()
- {
- return _protocolSession;
- }
-
- public void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag)
- throws AMQException
- {
- AMQDataBlock deliver = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag);
- AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
- message.getContentHeaderBody());
-
- final AMQMessageHandle messageHandle = message.getMessageHandle();
- final StoreContext storeContext = message.getStoreContext();
- final Long messageId = message.getMessageId();
-
- final int bodyCount = messageHandle.getBodyCount(storeContext,messageId);
-
- if(bodyCount == 0)
- {
- SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver,
- contentHeader);
-
- writeFrame(compositeBlock);
- }
- else
- {
-
-
- //
- // Optimise the case where we have a single content body. In that case we create a composite block
- // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
- //
- ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0);
-
- AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb));
- AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody};
- CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks);
- writeFrame(compositeBlock);
-
- //
- // Now start writing out the other content bodies
- //
- for(int i = 1; i < bodyCount; i++)
- {
- cb = messageHandle.getContentChunk(storeContext,messageId, i);
- writeFrame(new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb)));
- }
-
-
- }
-
-
- }
-
-
- public void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException
- {
-
- final AMQMessageHandle messageHandle = message.getMessageHandle();
- final StoreContext storeContext = message.getStoreContext();
- final long messageId = message.getMessageId();
-
- AMQDataBlock deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize);
-
-
- AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
- message.getContentHeaderBody());
-
- final int bodyCount = messageHandle.getBodyCount(storeContext,messageId);
- if(bodyCount == 0)
- {
- SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver,
- contentHeader);
- writeFrame(compositeBlock);
- }
- else
- {
-
-
- //
- // Optimise the case where we have a single content body. In that case we create a composite block
- // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
- //
- ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0);
-
- AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb));
- AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody};
- CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks);
- writeFrame(compositeBlock);
-
- //
- // Now start writing out the other content bodies
- //
- for(int i = 1; i < bodyCount; i++)
- {
- cb = messageHandle.getContentChunk(storeContext, messageId, i);
- writeFrame(new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb)));
- }
-
-
- }
-
-
- }
-
-
- private AMQDataBlock createEncodedDeliverFrame(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag)
- throws AMQException
- {
- final MessagePublishInfo pb = message.getMessagePublishInfo();
- final AMQMessageHandle messageHandle = message.getMessageHandle();
-
- MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0);
- BasicDeliverBody deliverBody =
- methodRegistry.createBasicDeliverBody(consumerTag,
- deliveryTag,
- messageHandle.isRedelivered(),
- pb.getExchange(),
- pb.getRoutingKey());
- AMQFrame deliverFrame = deliverBody.generateFrame(channelId);
-
-
- return deliverFrame;
- }
-
- private AMQDataBlock createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize)
- throws AMQException
- {
- final MessagePublishInfo pb = message.getMessagePublishInfo();
- final AMQMessageHandle messageHandle = message.getMessageHandle();
-
- MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0);
- BasicGetOkBody getOkBody =
- methodRegistry.createBasicGetOkBody(deliveryTag,
- messageHandle.isRedelivered(),
- pb.getExchange(),
- pb.getRoutingKey(),
- queueSize);
- AMQFrame getOkFrame = getOkBody.generateFrame(channelId);
-
- return getOkFrame;
- }
-
- public byte getProtocolMinorVersion()
- {
- return getProtocolSession().getProtocolMinorVersion();
- }
-
- public byte getProtocolMajorVersion()
- {
- return getProtocolSession().getProtocolMajorVersion();
- }
-
- private AMQDataBlock createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException
- {
- MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0);
- BasicReturnBody basicReturnBody =
- methodRegistry.createBasicReturnBody(replyCode,
- replyText,
- message.getMessagePublishInfo().getExchange(),
- message.getMessagePublishInfo().getRoutingKey());
- AMQFrame returnFrame = basicReturnBody.generateFrame(channelId);
-
- return returnFrame;
- }
-
- public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText)
- throws AMQException
- {
- AMQDataBlock returnFrame = createEncodedReturnFrame(message, channelId, replyCode, replyText);
-
- AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
- message.getContentHeaderBody());
-
- Iterator<AMQDataBlock> bodyFrameIterator = message.getBodyFrameIterator(getProtocolSession(), channelId);
- //
- // Optimise the case where we have a single content body. In that case we create a composite block
- // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
- //
- if (bodyFrameIterator.hasNext())
- {
- AMQDataBlock firstContentBody = bodyFrameIterator.next();
- AMQDataBlock[] blocks = new AMQDataBlock[]{returnFrame, contentHeader, firstContentBody};
- CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks);
- writeFrame(compositeBlock);
- }
- else
- {
- CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(new AMQDataBlock[]{returnFrame, contentHeader});
-
- writeFrame(compositeBlock);
- }
-
- //
- // Now start writing out the other content bodies
- // TODO: MINA needs to be fixed so the the pending writes buffer is not unbounded
- //
- while (bodyFrameIterator.hasNext())
- {
- writeFrame(bodyFrameIterator.next());
- }
- }
-
-
- public void writeFrame(AMQDataBlock block)
- {
- getProtocolSession().writeFrame(block);
- }
-
-
- public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag)
- {
- MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0);
- BasicCancelOkBody basicCancelOkBody = methodRegistry.createBasicCancelOkBody(consumerTag);
- writeFrame(basicCancelOkBody.generateFrame(channelId));
-
- }
-}
+/* + * + * 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. + * + */ + +/* + * This file is auto-generated by Qpid Gentools v.0.1 - do not modify. + * Supported AMQP versions: + * 8-0 + */ +package org.apache.qpid.server.output.amqp0_8; + +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.AMQMessageHandle; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.output.ProtocolOutputConverter; +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.AMQException; + +import org.apache.mina.common.ByteBuffer; + +import java.util.Iterator; + +public class ProtocolOutputConverterImpl implements ProtocolOutputConverter +{ + + + public static Factory getInstanceFactory() + { + return new Factory() + { + + public ProtocolOutputConverter newInstance(AMQProtocolSession session) + { + return new ProtocolOutputConverterImpl(session); + } + }; + } + + private final AMQProtocolSession _protocolSession; + + private ProtocolOutputConverterImpl(AMQProtocolSession session) + { + _protocolSession = session; + } + + + public AMQProtocolSession getProtocolSession() + { + return _protocolSession; + } + + public void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) + throws AMQException + { + AMQDataBlock deliver = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag); + AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, + message.getContentHeaderBody()); + + final AMQMessageHandle messageHandle = message.getMessageHandle(); + final StoreContext storeContext = message.getStoreContext(); + final Long messageId = message.getMessageId(); + + final int bodyCount = messageHandle.getBodyCount(storeContext,messageId); + + if(bodyCount == 0) + { + SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver, + contentHeader); + + writeFrame(compositeBlock); + } + else + { + + + // + // Optimise the case where we have a single content body. In that case we create a composite block + // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. + // + ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0); + + AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb)); + AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); + writeFrame(compositeBlock); + + // + // Now start writing out the other content bodies + // + for(int i = 1; i < bodyCount; i++) + { + cb = messageHandle.getContentChunk(storeContext,messageId, i); + writeFrame(new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb))); + } + + + } + + + } + + + public void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException + { + + final AMQMessageHandle messageHandle = message.getMessageHandle(); + final StoreContext storeContext = message.getStoreContext(); + final long messageId = message.getMessageId(); + + AMQDataBlock deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize); + + + AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, + message.getContentHeaderBody()); + + final int bodyCount = messageHandle.getBodyCount(storeContext,messageId); + if(bodyCount == 0) + { + SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver, + contentHeader); + writeFrame(compositeBlock); + } + else + { + + + // + // Optimise the case where we have a single content body. In that case we create a composite block + // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. + // + ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0); + + AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb)); + AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); + writeFrame(compositeBlock); + + // + // Now start writing out the other content bodies + // + for(int i = 1; i < bodyCount; i++) + { + cb = messageHandle.getContentChunk(storeContext, messageId, i); + writeFrame(new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb))); + } + + + } + + + } + + + private AMQDataBlock createEncodedDeliverFrame(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) + throws AMQException + { + final MessagePublishInfo pb = message.getMessagePublishInfo(); + final AMQMessageHandle messageHandle = message.getMessageHandle(); + + MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); + BasicDeliverBody deliverBody = + methodRegistry.createBasicDeliverBody(consumerTag, + deliveryTag, + messageHandle.isRedelivered(), + pb.getExchange(), + pb.getRoutingKey()); + AMQFrame deliverFrame = deliverBody.generateFrame(channelId); + + + return deliverFrame; + } + + private AMQDataBlock createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize) + throws AMQException + { + final MessagePublishInfo pb = message.getMessagePublishInfo(); + final AMQMessageHandle messageHandle = message.getMessageHandle(); + + MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); + BasicGetOkBody getOkBody = + methodRegistry.createBasicGetOkBody(deliveryTag, + messageHandle.isRedelivered(), + pb.getExchange(), + pb.getRoutingKey(), + queueSize); + AMQFrame getOkFrame = getOkBody.generateFrame(channelId); + + return getOkFrame; + } + + public byte getProtocolMinorVersion() + { + return getProtocolSession().getProtocolMinorVersion(); + } + + public byte getProtocolMajorVersion() + { + return getProtocolSession().getProtocolMajorVersion(); + } + + private AMQDataBlock createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException + { + MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); + BasicReturnBody basicReturnBody = + methodRegistry.createBasicReturnBody(replyCode, + replyText, + message.getMessagePublishInfo().getExchange(), + message.getMessagePublishInfo().getRoutingKey()); + AMQFrame returnFrame = basicReturnBody.generateFrame(channelId); + + return returnFrame; + } + + public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) + throws AMQException + { + AMQDataBlock returnFrame = createEncodedReturnFrame(message, channelId, replyCode, replyText); + + AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, + message.getContentHeaderBody()); + + Iterator<AMQDataBlock> bodyFrameIterator = message.getBodyFrameIterator(getProtocolSession(), channelId); + // + // Optimise the case where we have a single content body. In that case we create a composite block + // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. + // + if (bodyFrameIterator.hasNext()) + { + AMQDataBlock firstContentBody = bodyFrameIterator.next(); + AMQDataBlock[] blocks = new AMQDataBlock[]{returnFrame, contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); + writeFrame(compositeBlock); + } + else + { + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(new AMQDataBlock[]{returnFrame, contentHeader}); + + writeFrame(compositeBlock); + } + + // + // Now start writing out the other content bodies + // TODO: MINA needs to be fixed so the the pending writes buffer is not unbounded + // + while (bodyFrameIterator.hasNext()) + { + writeFrame(bodyFrameIterator.next()); + } + } + + + public void writeFrame(AMQDataBlock block) + { + getProtocolSession().writeFrame(block); + } + + + public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag) + { + MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); + BasicCancelOkBody basicCancelOkBody = methodRegistry.createBasicCancelOkBody(consumerTag); + writeFrame(basicCancelOkBody.generateFrame(channelId)); + + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java index 48d2ca9bc9..f87d3bcae1 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java @@ -1,397 +1,397 @@ -/*
- *
- * 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.server.output.amqp0_9;
-
-import org.apache.mina.common.ByteBuffer;
-
-import java.util.Iterator;
-
-import org.apache.qpid.server.output.ProtocolOutputConverter;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.AMQMessage;
-import org.apache.qpid.server.queue.AMQMessageHandle;
-import org.apache.qpid.server.store.StoreContext;
-import org.apache.qpid.framing.*;
-import org.apache.qpid.framing.abstraction.ContentChunk;
-import org.apache.qpid.framing.abstraction.MessagePublishInfo;
-import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
-
-public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
-{
- private static final MethodRegistry METHOD_REGISTRY = MethodRegistry.getMethodRegistry(ProtocolVersion.v0_9);
- private static final ProtocolVersionMethodConverter PROTOCOL_METHOD_CONVERTER = METHOD_REGISTRY.getProtocolVersionMethodConverter();
-
-
- public static Factory getInstanceFactory()
- {
- return new Factory()
- {
-
- public ProtocolOutputConverter newInstance(AMQProtocolSession session)
- {
- return new ProtocolOutputConverterImpl(session);
- }
- };
- }
-
- private final AMQProtocolSession _protocolSession;
-
- private ProtocolOutputConverterImpl(AMQProtocolSession session)
- {
- _protocolSession = session;
- }
-
-
- public AMQProtocolSession getProtocolSession()
- {
- return _protocolSession;
- }
-
- public void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag)
- throws AMQException
- {
- AMQBody deliverBody = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag);
- final ContentHeaderBody contentHeaderBody = message.getContentHeaderBody();
-
-
- final AMQMessageHandle messageHandle = message.getMessageHandle();
- final StoreContext storeContext = message.getStoreContext();
- final Long messageId = message.getMessageId();
-
- final int bodyCount = messageHandle.getBodyCount(storeContext,messageId);
-
- if(bodyCount == 0)
- {
- SmallCompositeAMQBodyBlock compositeBlock = new SmallCompositeAMQBodyBlock(channelId, deliverBody,
- contentHeaderBody);
-
- writeFrame(compositeBlock);
- }
- else
- {
-
-
- //
- // Optimise the case where we have a single content body. In that case we create a composite block
- // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
- //
- ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0);
-
- AMQBody firstContentBody = PROTOCOL_METHOD_CONVERTER.convertToBody(cb);
-
- CompositeAMQBodyBlock compositeBlock = new CompositeAMQBodyBlock(channelId, deliverBody, contentHeaderBody, firstContentBody);
- writeFrame(compositeBlock);
-
- //
- // Now start writing out the other content bodies
- //
- for(int i = 1; i < bodyCount; i++)
- {
- cb = messageHandle.getContentChunk(storeContext,messageId, i);
- writeFrame(new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb)));
- }
-
-
- }
-
-
- }
-
- private AMQDataBlock createContentHeaderBlock(final int channelId, final ContentHeaderBody contentHeaderBody)
- {
-
- AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
- contentHeaderBody);
- return contentHeader;
- }
-
-
- public void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException
- {
-
- final AMQMessageHandle messageHandle = message.getMessageHandle();
- final StoreContext storeContext = message.getStoreContext();
- final long messageId = message.getMessageId();
-
- AMQFrame deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize);
-
-
- AMQDataBlock contentHeader = createContentHeaderBlock(channelId, message.getContentHeaderBody());
-
- final int bodyCount = messageHandle.getBodyCount(storeContext,messageId);
- if(bodyCount == 0)
- {
- SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver,
- contentHeader);
- writeFrame(compositeBlock);
- }
- else
- {
-
-
- //
- // Optimise the case where we have a single content body. In that case we create a composite block
- // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
- //
- ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0);
-
- AMQDataBlock firstContentBody = new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb));
- AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody};
- CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks);
- writeFrame(compositeBlock);
-
- //
- // Now start writing out the other content bodies
- //
- for(int i = 1; i < bodyCount; i++)
- {
- cb = messageHandle.getContentChunk(storeContext, messageId, i);
- writeFrame(new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb)));
- }
-
-
- }
-
-
- }
-
-
- private AMQBody createEncodedDeliverFrame(AMQMessage message, final int channelId, final long deliveryTag, final AMQShortString consumerTag)
- throws AMQException
- {
-
-
- final MessagePublishInfo pb = message.getMessagePublishInfo();
- final AMQMessageHandle messageHandle = message.getMessageHandle();
-
-
- final AMQBody returnBlock = new AMQBody()
- {
-
-
-
- private final boolean _isRedelivered = messageHandle.isRedelivered();
- private final AMQShortString _exchangeName = pb.getExchange();
- private final AMQShortString _routingKey = pb.getRoutingKey();
-
-
- public AMQBody _underlyingBody;
-
- public AMQBody createAMQBody()
- {
- return METHOD_REGISTRY.createBasicDeliverBody(consumerTag,
- deliveryTag,
- _isRedelivered,
- _exchangeName,
- _routingKey);
-
-
-
-
-
- }
-
- public byte getFrameType()
- {
- return AMQMethodBody.TYPE;
- }
-
- public int getSize()
- {
- if(_underlyingBody == null)
- {
- _underlyingBody = createAMQBody();
- }
- return _underlyingBody.getSize();
- }
-
- public void writePayload(ByteBuffer buffer)
- {
- if(_underlyingBody == null)
- {
- _underlyingBody = createAMQBody();
- }
- _underlyingBody.writePayload(buffer);
- }
-
- public void handle(final int channelId, final AMQVersionAwareProtocolSession amqMinaProtocolSession)
- throws AMQException
- {
- throw new AMQException("This block should never be dispatched!");
- }
- };
- return returnBlock;
- }
-
- private AMQFrame createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize)
- throws AMQException
- {
- final MessagePublishInfo pb = message.getMessagePublishInfo();
- final AMQMessageHandle messageHandle = message.getMessageHandle();
-
-
- BasicGetOkBody getOkBody =
- METHOD_REGISTRY.createBasicGetOkBody(deliveryTag,
- messageHandle.isRedelivered(),
- pb.getExchange(),
- pb.getRoutingKey(),
- queueSize);
- AMQFrame getOkFrame = getOkBody.generateFrame(channelId);
-
- return getOkFrame;
- }
-
- public byte getProtocolMinorVersion()
- {
- return getProtocolSession().getProtocolMinorVersion();
- }
-
- public byte getProtocolMajorVersion()
- {
- return getProtocolSession().getProtocolMajorVersion();
- }
-
- private AMQDataBlock createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException
- {
-
- BasicReturnBody basicReturnBody =
- METHOD_REGISTRY.createBasicReturnBody(replyCode,
- replyText,
- message.getMessagePublishInfo().getExchange(),
- message.getMessagePublishInfo().getRoutingKey());
- AMQFrame returnFrame = basicReturnBody.generateFrame(channelId);
-
- return returnFrame;
- }
-
- public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText)
- throws AMQException
- {
- AMQDataBlock returnFrame = createEncodedReturnFrame(message, channelId, replyCode, replyText);
-
- AMQDataBlock contentHeader = createContentHeaderBlock(channelId, message.getContentHeaderBody());
-
- Iterator<AMQDataBlock> bodyFrameIterator = message.getBodyFrameIterator(getProtocolSession(), channelId);
- //
- // Optimise the case where we have a single content body. In that case we create a composite block
- // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
- //
- if (bodyFrameIterator.hasNext())
- {
- AMQDataBlock firstContentBody = bodyFrameIterator.next();
- AMQDataBlock[] blocks = new AMQDataBlock[]{returnFrame, contentHeader, firstContentBody};
- CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks);
- writeFrame(compositeBlock);
- }
- else
- {
- CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(new AMQDataBlock[]{returnFrame, contentHeader});
-
- writeFrame(compositeBlock);
- }
-
- //
- // Now start writing out the other content bodies
- // TODO: MINA needs to be fixed so the the pending writes buffer is not unbounded
- //
- while (bodyFrameIterator.hasNext())
- {
- writeFrame(bodyFrameIterator.next());
- }
- }
-
-
- public void writeFrame(AMQDataBlock block)
- {
- getProtocolSession().writeFrame(block);
- }
-
-
- public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag)
- {
-
- BasicCancelOkBody basicCancelOkBody = METHOD_REGISTRY.createBasicCancelOkBody(consumerTag);
- writeFrame(basicCancelOkBody.generateFrame(channelId));
-
- }
-
-
- public static final class CompositeAMQBodyBlock extends AMQDataBlock
- {
- public static final int OVERHEAD = 3 * AMQFrame.getFrameOverhead();
-
- private final AMQBody _methodBody;
- private final AMQBody _headerBody;
- private final AMQBody _contentBody;
- private final int _channel;
-
-
- public CompositeAMQBodyBlock(int channel, AMQBody methodBody, AMQBody headerBody, AMQBody contentBody)
- {
- _channel = channel;
- _methodBody = methodBody;
- _headerBody = headerBody;
- _contentBody = contentBody;
-
- }
-
- public long getSize()
- {
- return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() + _contentBody.getSize();
- }
-
- public void writePayload(ByteBuffer buffer)
- {
- AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody, _contentBody);
- }
- }
-
- public static final class SmallCompositeAMQBodyBlock extends AMQDataBlock
- {
- public static final int OVERHEAD = 2 * AMQFrame.getFrameOverhead();
-
- private final AMQBody _methodBody;
- private final AMQBody _headerBody;
- private final int _channel;
-
-
- public SmallCompositeAMQBodyBlock(int channel, AMQBody methodBody, AMQBody headerBody)
- {
- _channel = channel;
- _methodBody = methodBody;
- _headerBody = headerBody;
-
- }
-
- public long getSize()
- {
- return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() ;
- }
-
- public void writePayload(ByteBuffer buffer)
- {
- AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody);
- }
- }
-
-}
+/* + * + * 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.server.output.amqp0_9; + +import org.apache.mina.common.ByteBuffer; + +import java.util.Iterator; + +import org.apache.qpid.server.output.ProtocolOutputConverter; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.AMQMessageHandle; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; + +public class ProtocolOutputConverterImpl implements ProtocolOutputConverter +{ + private static final MethodRegistry METHOD_REGISTRY = MethodRegistry.getMethodRegistry(ProtocolVersion.v0_9); + private static final ProtocolVersionMethodConverter PROTOCOL_METHOD_CONVERTER = METHOD_REGISTRY.getProtocolVersionMethodConverter(); + + + public static Factory getInstanceFactory() + { + return new Factory() + { + + public ProtocolOutputConverter newInstance(AMQProtocolSession session) + { + return new ProtocolOutputConverterImpl(session); + } + }; + } + + private final AMQProtocolSession _protocolSession; + + private ProtocolOutputConverterImpl(AMQProtocolSession session) + { + _protocolSession = session; + } + + + public AMQProtocolSession getProtocolSession() + { + return _protocolSession; + } + + public void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) + throws AMQException + { + AMQBody deliverBody = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag); + final ContentHeaderBody contentHeaderBody = message.getContentHeaderBody(); + + + final AMQMessageHandle messageHandle = message.getMessageHandle(); + final StoreContext storeContext = message.getStoreContext(); + final Long messageId = message.getMessageId(); + + final int bodyCount = messageHandle.getBodyCount(storeContext,messageId); + + if(bodyCount == 0) + { + SmallCompositeAMQBodyBlock compositeBlock = new SmallCompositeAMQBodyBlock(channelId, deliverBody, + contentHeaderBody); + + writeFrame(compositeBlock); + } + else + { + + + // + // Optimise the case where we have a single content body. In that case we create a composite block + // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. + // + ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0); + + AMQBody firstContentBody = PROTOCOL_METHOD_CONVERTER.convertToBody(cb); + + CompositeAMQBodyBlock compositeBlock = new CompositeAMQBodyBlock(channelId, deliverBody, contentHeaderBody, firstContentBody); + writeFrame(compositeBlock); + + // + // Now start writing out the other content bodies + // + for(int i = 1; i < bodyCount; i++) + { + cb = messageHandle.getContentChunk(storeContext,messageId, i); + writeFrame(new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb))); + } + + + } + + + } + + private AMQDataBlock createContentHeaderBlock(final int channelId, final ContentHeaderBody contentHeaderBody) + { + + AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, + contentHeaderBody); + return contentHeader; + } + + + public void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException + { + + final AMQMessageHandle messageHandle = message.getMessageHandle(); + final StoreContext storeContext = message.getStoreContext(); + final long messageId = message.getMessageId(); + + AMQFrame deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize); + + + AMQDataBlock contentHeader = createContentHeaderBlock(channelId, message.getContentHeaderBody()); + + final int bodyCount = messageHandle.getBodyCount(storeContext,messageId); + if(bodyCount == 0) + { + SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver, + contentHeader); + writeFrame(compositeBlock); + } + else + { + + + // + // Optimise the case where we have a single content body. In that case we create a composite block + // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. + // + ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0); + + AMQDataBlock firstContentBody = new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb)); + AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); + writeFrame(compositeBlock); + + // + // Now start writing out the other content bodies + // + for(int i = 1; i < bodyCount; i++) + { + cb = messageHandle.getContentChunk(storeContext, messageId, i); + writeFrame(new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb))); + } + + + } + + + } + + + private AMQBody createEncodedDeliverFrame(AMQMessage message, final int channelId, final long deliveryTag, final AMQShortString consumerTag) + throws AMQException + { + + + final MessagePublishInfo pb = message.getMessagePublishInfo(); + final AMQMessageHandle messageHandle = message.getMessageHandle(); + + + final AMQBody returnBlock = new AMQBody() + { + + + + private final boolean _isRedelivered = messageHandle.isRedelivered(); + private final AMQShortString _exchangeName = pb.getExchange(); + private final AMQShortString _routingKey = pb.getRoutingKey(); + + + public AMQBody _underlyingBody; + + public AMQBody createAMQBody() + { + return METHOD_REGISTRY.createBasicDeliverBody(consumerTag, + deliveryTag, + _isRedelivered, + _exchangeName, + _routingKey); + + + + + + } + + public byte getFrameType() + { + return AMQMethodBody.TYPE; + } + + public int getSize() + { + if(_underlyingBody == null) + { + _underlyingBody = createAMQBody(); + } + return _underlyingBody.getSize(); + } + + public void writePayload(ByteBuffer buffer) + { + if(_underlyingBody == null) + { + _underlyingBody = createAMQBody(); + } + _underlyingBody.writePayload(buffer); + } + + public void handle(final int channelId, final AMQVersionAwareProtocolSession amqMinaProtocolSession) + throws AMQException + { + throw new AMQException("This block should never be dispatched!"); + } + }; + return returnBlock; + } + + private AMQFrame createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize) + throws AMQException + { + final MessagePublishInfo pb = message.getMessagePublishInfo(); + final AMQMessageHandle messageHandle = message.getMessageHandle(); + + + BasicGetOkBody getOkBody = + METHOD_REGISTRY.createBasicGetOkBody(deliveryTag, + messageHandle.isRedelivered(), + pb.getExchange(), + pb.getRoutingKey(), + queueSize); + AMQFrame getOkFrame = getOkBody.generateFrame(channelId); + + return getOkFrame; + } + + public byte getProtocolMinorVersion() + { + return getProtocolSession().getProtocolMinorVersion(); + } + + public byte getProtocolMajorVersion() + { + return getProtocolSession().getProtocolMajorVersion(); + } + + private AMQDataBlock createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException + { + + BasicReturnBody basicReturnBody = + METHOD_REGISTRY.createBasicReturnBody(replyCode, + replyText, + message.getMessagePublishInfo().getExchange(), + message.getMessagePublishInfo().getRoutingKey()); + AMQFrame returnFrame = basicReturnBody.generateFrame(channelId); + + return returnFrame; + } + + public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) + throws AMQException + { + AMQDataBlock returnFrame = createEncodedReturnFrame(message, channelId, replyCode, replyText); + + AMQDataBlock contentHeader = createContentHeaderBlock(channelId, message.getContentHeaderBody()); + + Iterator<AMQDataBlock> bodyFrameIterator = message.getBodyFrameIterator(getProtocolSession(), channelId); + // + // Optimise the case where we have a single content body. In that case we create a composite block + // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. + // + if (bodyFrameIterator.hasNext()) + { + AMQDataBlock firstContentBody = bodyFrameIterator.next(); + AMQDataBlock[] blocks = new AMQDataBlock[]{returnFrame, contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); + writeFrame(compositeBlock); + } + else + { + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(new AMQDataBlock[]{returnFrame, contentHeader}); + + writeFrame(compositeBlock); + } + + // + // Now start writing out the other content bodies + // TODO: MINA needs to be fixed so the the pending writes buffer is not unbounded + // + while (bodyFrameIterator.hasNext()) + { + writeFrame(bodyFrameIterator.next()); + } + } + + + public void writeFrame(AMQDataBlock block) + { + getProtocolSession().writeFrame(block); + } + + + public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag) + { + + BasicCancelOkBody basicCancelOkBody = METHOD_REGISTRY.createBasicCancelOkBody(consumerTag); + writeFrame(basicCancelOkBody.generateFrame(channelId)); + + } + + + public static final class CompositeAMQBodyBlock extends AMQDataBlock + { + public static final int OVERHEAD = 3 * AMQFrame.getFrameOverhead(); + + private final AMQBody _methodBody; + private final AMQBody _headerBody; + private final AMQBody _contentBody; + private final int _channel; + + + public CompositeAMQBodyBlock(int channel, AMQBody methodBody, AMQBody headerBody, AMQBody contentBody) + { + _channel = channel; + _methodBody = methodBody; + _headerBody = headerBody; + _contentBody = contentBody; + + } + + public long getSize() + { + return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() + _contentBody.getSize(); + } + + public void writePayload(ByteBuffer buffer) + { + AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody, _contentBody); + } + } + + public static final class SmallCompositeAMQBodyBlock extends AMQDataBlock + { + public static final int OVERHEAD = 2 * AMQFrame.getFrameOverhead(); + + private final AMQBody _methodBody; + private final AMQBody _headerBody; + private final int _channel; + + + public SmallCompositeAMQBodyBlock(int channel, AMQBody methodBody, AMQBody headerBody) + { + _channel = channel; + _methodBody = methodBody; + _headerBody = headerBody; + + } + + public long getSize() + { + return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() ; + } + + public void writePayload(ByteBuffer buffer) + { + AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody); + } + } + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java index a7599a3e0d..92f951ce39 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java @@ -1,46 +1,46 @@ -/*
- *
- * 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.server.protocol;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.protocol.AMQMethodEvent;
-
-/**
- * AMQNoMethodHandlerException represents the case where no method handler exists to handle an AQMP method.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Represents failure to handle an AMQP method.
- * </table>
- *
- * @todo Not an AMQP exception as no status code.
- *
- * @todo Missing method handler. Unlikely to ever happen, and if it does its a coding error. Consider replacing with a
- * Runtime.
- */
-public class AMQNoMethodHandlerException extends AMQException
-{
- public AMQNoMethodHandlerException(AMQMethodEvent<AMQMethodBody> evt)
- {
- super("AMQMethodEvent " + evt + " was not processed by any listener on Broker.");
- }
-}
+/* + * + * 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.server.protocol; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.protocol.AMQMethodEvent; + +/** + * AMQNoMethodHandlerException represents the case where no method handler exists to handle an AQMP method. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represents failure to handle an AMQP method. + * </table> + * + * @todo Not an AMQP exception as no status code. + * + * @todo Missing method handler. Unlikely to ever happen, and if it does its a coding error. Consider replacing with a + * Runtime. + */ +public class AMQNoMethodHandlerException extends AMQException +{ + public AMQNoMethodHandlerException(AMQMethodEvent<AMQMethodBody> evt) + { + super("AMQMethodEvent " + evt + " was not processed by any listener on Broker."); + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java index 6e72aa062f..bb2db8d506 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java @@ -1,46 +1,46 @@ -/*
- *
- * 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.server.protocol;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQDataBlock;
-
-/**
- * UnknnownMessageTypeException represents a failure when Mina passes an unexpected frame type.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Represents failure to cast a frame to its expected type.
- * </table>
- *
- * @todo Not an AMQP exception as no status code.
- *
- * @todo Seems like this exception was created to handle an unsafe type cast that will never happen in practice. Would
- * be better just to leave that as a ClassCastException. However, check the framing layer catches this error
- * first.
- */
-public class UnknnownMessageTypeException extends AMQException
-{
- public UnknnownMessageTypeException(AMQDataBlock message)
- {
- super("Unknown message type: " + message.getClass().getName() + ": " + message);
- }
-}
+/* + * + * 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.server.protocol; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQDataBlock; + +/** + * UnknnownMessageTypeException represents a failure when Mina passes an unexpected frame type. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represents failure to cast a frame to its expected type. + * </table> + * + * @todo Not an AMQP exception as no status code. + * + * @todo Seems like this exception was created to handle an unsafe type cast that will never happen in practice. Would + * be better just to leave that as a ClassCastException. However, check the framing layer catches this error + * first. + */ +public class UnknnownMessageTypeException extends AMQException +{ + public UnknnownMessageTypeException(AMQDataBlock message) + { + super("Unknown message type: " + message.getClass().getName() + ": " + message); + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java index 6f9efd3200..65115e4103 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java @@ -1,138 +1,138 @@ -/*
- *
- * 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.server.queue;
-
-import org.apache.qpid.AMQException;
-
-public enum NotificationCheck
-{
-
- MESSAGE_COUNT_ALERT
- {
- boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener)
- {
- int msgCount;
- final long maximumMessageCount = queue.getMaximumMessageCount();
- if (maximumMessageCount!= 0 && (msgCount = queue.getMessageCount()) >= maximumMessageCount)
- {
- listener.notifyClients(this, queue, msgCount + ": Maximum count on queue threshold ("+ maximumMessageCount +") breached.");
- return true;
- }
- return false;
- }
- },
- MESSAGE_SIZE_ALERT(true)
- {
- boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener)
- {
- final long maximumMessageSize = queue.getMaximumMessageSize();
- if(maximumMessageSize != 0)
- {
- // Check for threshold message size
- long messageSize;
- try
- {
- messageSize = (msg == null) ? 0 : msg.getContentHeaderBody().bodySize;
- }
- catch (AMQException e)
- {
- messageSize = 0;
- }
-
-
- if (messageSize >= maximumMessageSize)
- {
- listener.notifyClients(this, queue, messageSize + "b : Maximum message size threshold ("+ maximumMessageSize +") breached. [Message ID=" + msg.getMessageId() + "]");
- return true;
- }
- }
- return false;
- }
-
- },
- QUEUE_DEPTH_ALERT
- {
- boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener)
- {
- // Check for threshold queue depth in bytes
- final long maximumQueueDepth = queue.getMaximumQueueDepth();
-
- if(maximumQueueDepth != 0)
- {
- final long queueDepth = queue.getQueueDepth();
-
- if (queueDepth >= maximumQueueDepth)
- {
- listener.notifyClients(this, queue, (queueDepth>>10) + "Kb : Maximum queue depth threshold ("+(maximumQueueDepth>>10)+"Kb) breached.");
- return true;
- }
- }
- return false;
- }
-
- },
- MESSAGE_AGE_ALERT
- {
- boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener)
- {
-
- final long maxMessageAge = queue.getMaximumMessageAge();
- if(maxMessageAge != 0)
- {
- final long currentTime = System.currentTimeMillis();
- final long thresholdTime = currentTime - maxMessageAge;
- final long firstArrivalTime = queue.getOldestMessageArrivalTime();
-
- if(firstArrivalTime < thresholdTime)
- {
- long oldestAge = currentTime - firstArrivalTime;
- listener.notifyClients(this, queue, (oldestAge/1000) + "s : Maximum age on queue threshold ("+(maxMessageAge /1000)+"s) breached.");
-
- return true;
- }
- }
- return false;
-
- }
-
- }
- ;
-
- private final boolean _messageSpecific;
-
- NotificationCheck()
- {
- this(false);
- }
-
- NotificationCheck(boolean messageSpecific)
- {
- _messageSpecific = messageSpecific;
- }
-
- public boolean isMessageSpecific()
- {
- return _messageSpecific;
- }
-
- abstract boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener);
-
-}
+/* + * + * 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.server.queue; + +import org.apache.qpid.AMQException; + +public enum NotificationCheck +{ + + MESSAGE_COUNT_ALERT + { + boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener) + { + int msgCount; + final long maximumMessageCount = queue.getMaximumMessageCount(); + if (maximumMessageCount!= 0 && (msgCount = queue.getMessageCount()) >= maximumMessageCount) + { + listener.notifyClients(this, queue, msgCount + ": Maximum count on queue threshold ("+ maximumMessageCount +") breached."); + return true; + } + return false; + } + }, + MESSAGE_SIZE_ALERT(true) + { + boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener) + { + final long maximumMessageSize = queue.getMaximumMessageSize(); + if(maximumMessageSize != 0) + { + // Check for threshold message size + long messageSize; + try + { + messageSize = (msg == null) ? 0 : msg.getContentHeaderBody().bodySize; + } + catch (AMQException e) + { + messageSize = 0; + } + + + if (messageSize >= maximumMessageSize) + { + listener.notifyClients(this, queue, messageSize + "b : Maximum message size threshold ("+ maximumMessageSize +") breached. [Message ID=" + msg.getMessageId() + "]"); + return true; + } + } + return false; + } + + }, + QUEUE_DEPTH_ALERT + { + boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener) + { + // Check for threshold queue depth in bytes + final long maximumQueueDepth = queue.getMaximumQueueDepth(); + + if(maximumQueueDepth != 0) + { + final long queueDepth = queue.getQueueDepth(); + + if (queueDepth >= maximumQueueDepth) + { + listener.notifyClients(this, queue, (queueDepth>>10) + "Kb : Maximum queue depth threshold ("+(maximumQueueDepth>>10)+"Kb) breached."); + return true; + } + } + return false; + } + + }, + MESSAGE_AGE_ALERT + { + boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener) + { + + final long maxMessageAge = queue.getMaximumMessageAge(); + if(maxMessageAge != 0) + { + final long currentTime = System.currentTimeMillis(); + final long thresholdTime = currentTime - maxMessageAge; + final long firstArrivalTime = queue.getOldestMessageArrivalTime(); + + if(firstArrivalTime < thresholdTime) + { + long oldestAge = currentTime - firstArrivalTime; + listener.notifyClients(this, queue, (oldestAge/1000) + "s : Maximum age on queue threshold ("+(maxMessageAge /1000)+"s) breached."); + + return true; + } + } + return false; + + } + + } + ; + + private final boolean _messageSpecific; + + NotificationCheck() + { + this(false); + } + + NotificationCheck(boolean messageSpecific) + { + _messageSpecific = messageSpecific; + } + + public boolean isMessageSpecific() + { + return _messageSpecific; + } + + abstract boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener); + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java index 959ca03c80..f1e7c98387 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java @@ -1,27 +1,27 @@ -/*
- *
- * 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.server.queue;
-
-
-public interface QueueNotificationListener
-{
- void notifyClients(NotificationCheck notification, AMQQueue queue, String notificationMsg);
-}
+/* + * + * 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.server.queue; + + +public interface QueueNotificationListener +{ + void notifyClients(NotificationCheck notification, AMQQueue queue, String notificationMsg); +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java index 85d804457e..70a76dd8c2 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java @@ -1,44 +1,44 @@ -/*
- *
- * 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.server.virtualhost;
-
-import java.io.IOException;
-
-import org.apache.qpid.server.management.MBeanAttribute;
-
-/**
- * The management interface exposed to allow management of an Exchange.
- * @version 0.1
- */
-public interface ManagedVirtualHost
-{
- static final String TYPE = "VirtualHost";
-
- /**
- * Returns the name of the managed virtualHost.
- * @return the name of the exchange.
- * @throws java.io.IOException
- */
- @MBeanAttribute(name="Name", description= TYPE + " Name")
- String getName() throws IOException;
-
-
-}
+/* + * + * 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.server.virtualhost; + +import java.io.IOException; + +import org.apache.qpid.server.management.MBeanAttribute; + +/** + * The management interface exposed to allow management of an Exchange. + * @version 0.1 + */ +public interface ManagedVirtualHost +{ + static final String TYPE = "VirtualHost"; + + /** + * Returns the name of the managed virtualHost. + * @return the name of the exchange. + * @throws java.io.IOException + */ + @MBeanAttribute(name="Name", description= TYPE + " Name") + String getName() throws IOException; + + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java index 27917fac8a..9b1619c609 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java @@ -1,70 +1,70 @@ -/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.virtualhost;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-
-public class VirtualHostRegistry
-{
- private final Map<String, VirtualHost> _registry = new ConcurrentHashMap<String,VirtualHost>();
-
-
- private String _defaultVirtualHostName;
-
- public synchronized void registerVirtualHost(VirtualHost host) throws Exception
- {
- if(_registry.containsKey(host.getName()))
- {
- throw new Exception("Virtual Host with name " + host.getName() + " already registered.");
- }
- _registry.put(host.getName(),host);
- }
-
- public VirtualHost getVirtualHost(String name)
- {
- if(name == null || name.trim().length() == 0 )
- {
- name = getDefaultVirtualHostName();
- }
-
- return _registry.get(name);
- }
-
- private String getDefaultVirtualHostName()
- {
- return _defaultVirtualHostName;
- }
-
- public void setDefaultVirtualHostName(String defaultVirtualHostName)
- {
- _defaultVirtualHostName = defaultVirtualHostName;
- }
-
-
- public Collection<VirtualHost> getVirtualHosts()
- {
- return new ArrayList<VirtualHost>(_registry.values());
- }
-}
+/* + * + * 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.server.virtualhost; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + + +public class VirtualHostRegistry +{ + private final Map<String, VirtualHost> _registry = new ConcurrentHashMap<String,VirtualHost>(); + + + private String _defaultVirtualHostName; + + public synchronized void registerVirtualHost(VirtualHost host) throws Exception + { + if(_registry.containsKey(host.getName())) + { + throw new Exception("Virtual Host with name " + host.getName() + " already registered."); + } + _registry.put(host.getName(),host); + } + + public VirtualHost getVirtualHost(String name) + { + if(name == null || name.trim().length() == 0 ) + { + name = getDefaultVirtualHostName(); + } + + return _registry.get(name); + } + + private String getDefaultVirtualHostName() + { + return _defaultVirtualHostName; + } + + public void setDefaultVirtualHostName(String defaultVirtualHostName) + { + _defaultVirtualHostName = defaultVirtualHostName; + } + + + public Collection<VirtualHost> getVirtualHosts() + { + return new ArrayList<VirtualHost>(_registry.values()); + } +} diff --git a/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/ClientFactoryImpl.java b/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/ClientFactoryImpl.java index 691020307a..ed8e4ad80f 100644 --- a/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/ClientFactoryImpl.java +++ b/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/ClientFactoryImpl.java @@ -1,343 +1,343 @@ -/*
- *
- * 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.sasl;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-
-import javax.security.auth.callback.*;
-import javax.security.sasl.Sasl;
-import javax.security.sasl.SaslClient;
-import javax.security.sasl.SaslClientFactory;
-import javax.security.sasl.SaslException;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.util.PrettyPrintingUtils;
-
-/**
- * Implements a factory for generating Sasl client implementations.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Provide a list of supported encryption mechansims that meet a defined set of Sasl properties.
- * <tr><td> Provide the best matching supported Sasl mechanism to a preference ordered list of mechanisms and Sasl
- * properties.
- * <tr><td> Perform username and password request call backs. <td> CallBackHandler
- * </table>
- */
-public class ClientFactoryImpl implements SaslClientFactory
-{
- //private static final Logger log = Logger.getLogger(ClientFactoryImpl.class);
-
- /** Holds the names of the supported encryption mechanisms. */
- private static final String[] SUPPORTED_MECHANISMS = { "CRAM-MD5", "PLAIN" };
-
- /** Defines index of the CRAM-MD5 mechanism within the supported mechanisms. */
- private static final int CRAM_MD5 = 0;
-
- /** Defines index of the PLAIN mechanism within the supported mechanisms. */
- private static final int PLAIN = 1;
-
- /** Bit mapping of the no plain text policy. */
- private static final int NOPLAINTEXT = 0x0001;
-
- /** Bit mapping of the no susceptible active attacks policy. */
- private static final int NOACTIVE = 0x0002;
-
- /** Bit mapping of the no susceptible to dictionary attacks policy. */
- private static final int NODICTIONARY = 0x0004;
-
- /** Bit mapping of the must use forward secrecy between sessions policy. */
- private static final int FORWARD_SECRECY = 0x0008;
-
- /** Bit mapping of the no anonymous logins policy. */
- private static final int NOANONYMOUS = 0x0010;
-
- /** Bit mapping of the must pass credentials policy. */
- private static final int PASS_CREDENTIALS = 0x0020;
-
- /** Defines a mapping from supported mechanisms to supported policy flags. */
- private static final int[] SUPPPORTED_MECHANISMS_POLICIES =
- {
- NOPLAINTEXT | NOANONYMOUS, // CRAM-MD5
- NOANONYMOUS // PLAIN
- };
-
- /**
- * Creates a SaslClient using the parameters supplied.
- *
- * @param mechanisms The non-null list of mechanism names to try. Each is the IANA-registered name of a SASL
- * mechanism. (e.g. "GSSAPI", "CRAM-MD5").
- * @param authorizationId The possibly null protocol-dependent identification to be used for authorization.
- * If null or empty, the server derives an authorization ID from the client's authentication
- * credentials. When the SASL authentication completes successfully, the specified entity is
- * granted access.
- * @param protocol The non-null string name of the protocol for which the authentication is being performed
- * (e.g., "ldap").
- * @param serverName The non-null fully qualified host name of the server to authenticate to.
- * @param props The possibly null set of properties used to select the SASL mechanism and to configure the
- * authentication exchange of the selected mechanism. See the <tt>Sasl</tt> class for a list
- * of standard properties. Other, possibly mechanism-specific, properties can be included.
- * Properties not relevant to the selected mechanism are ignored.
- * @param cbh The possibly null callback handler to used by the SASL mechanisms to get further
- * information from the application/library to complete the authentication. For example, a
- * SASL mechanism might require the authentication ID, password and realm from the caller.
- * The authentication ID is requested by using a <tt>NameCallback</tt>.
- * The password is requested by using a <tt>PasswordCallback</tt>.
- * The realm is requested by using a <tt>RealmChoiceCallback</tt> if there is a list
- * of realms to choose from, and by using a <tt>RealmCallback</tt> if
- * the realm must be entered.
- *
- * @return A possibly null <tt>SaslClient</tt> created using the parameters supplied. If null, this factory cannot
- * produce a <tt>SaslClient</tt> using the parameters supplied.
- *
- * @throws javax.security.sasl.SaslException If cannot create a <tt>SaslClient</tt> because of an error.
- */
- public SaslClient createSaslClient(String[] mechanisms, String authorizationId, String protocol, String serverName,
- Map props, CallbackHandler cbh) throws SaslException
- {
- /*log.debug("public SaslClient createSaslClient(String[] mechanisms = " + PrettyPrintingUtils.printArray(mechanisms)
- + ", String authorizationId = " + authorizationId + ", String protocol = " + protocol
- + ", String serverName = " + serverName + ", Map props = " + props + ", CallbackHandler cbh): called");*/
-
- // Get a list of all supported mechanisms that matched the required properties.
- String[] matchingMechanisms = getMechanismNames(props);
- //log.debug("matchingMechanisms = " + PrettyPrintingUtils.printArray(matchingMechanisms));
-
- // Scan down the list of mechanisms until the first one that matches one of the matching supported mechanisms
- // is found.
- String chosenMechanism = null;
-
- for (int i = 0; i < mechanisms.length; i++)
- {
- String mechanism = mechanisms[i];
-
- for (int j = 0; j < matchingMechanisms.length; j++)
- {
- String matchingMechanism = matchingMechanisms[j];
-
- if (mechanism.equals(matchingMechanism))
- {
- chosenMechanism = mechanism;
-
- break;
- }
- }
-
- // Stop scanning if a match has been found.
- if (chosenMechanism != null)
- {
- break;
- }
- }
-
- // Check that a matching mechanism was found or return null otherwise.
- if (chosenMechanism == null)
- {
- //log.debug("No matching mechanism could be found.");
-
- return null;
- }
-
- // Instantiate an appropriate client type for the chosen mechanism.
- if (chosenMechanism.equals(SUPPORTED_MECHANISMS[CRAM_MD5]))
- {
- Object[] uinfo = getUserInfo("CRAM-MD5", authorizationId, cbh);
-
- //log.debug("Using CRAM-MD5 mechanism.");
-
- return new CramMD5Client((String) uinfo[0], (byte[]) uinfo[1]);
- }
- else
- {
- Object[] uinfo = getUserInfo("PLAIN", authorizationId, cbh);
-
- //log.debug("Using PLAIN mechanism.");
-
- return new PlainClient(authorizationId, (String) uinfo[0], (byte[]) uinfo[1]);
- }
- }
-
- /**
- * Returns an array of names of mechanisms that match the specified
- * mechanism selection policies.
- *
- * @param props The possibly null set of properties used to specify the
- * security policy of the SASL mechanisms. For example, if <tt>props</tt>
- * contains the <tt>Sasl.POLICY_NOPLAINTEXT</tt> property with the value
- * <tt>"true"</tt>, then the factory must not return any SASL mechanisms
- * that are susceptible to simple plain passive attacks.
- * See the <tt>Sasl</tt> class for a complete list of policy properties.
- * Non-policy related properties, if present in <tt>props</tt>, are ignored.
- *
- * @return A non-null array containing a IANA-registered SASL mechanism names.
- */
- public String[] getMechanismNames(Map props)
- {
- //log.debug("public String[] getMechanismNames(Map props = " + props + "): called");
-
- // Used to build up the valid mechanisms in.
- List validMechanisms = new ArrayList();
-
- // Transform the Sasl properties into a set of bit mapped flags indicating the required properties of the
- // encryption mechanism employed.
- int requiredFlags = bitMapSaslProperties(props);
- //log.debug("requiredFlags = " + requiredFlags);
-
- // Scan down the list of supported mechanisms filtering in only those that satisfy all of the desired
- // encryption properties.
- for (int i = 0; i < SUPPORTED_MECHANISMS.length; i++)
- {
- int mechanismFlags = SUPPPORTED_MECHANISMS_POLICIES[i];
- //log.debug("mechanismFlags = " + mechanismFlags);
-
- // Check if the current mechanism contains all of the required flags.
- if ((requiredFlags & ~mechanismFlags) == 0)
- {
- //log.debug("Mechanism " + SUPPORTED_MECHANISMS[i] + " meets the required properties.");
- validMechanisms.add(SUPPORTED_MECHANISMS[i]);
- }
- }
-
- String[] result = (String[]) validMechanisms.toArray(new String[validMechanisms.size()]);
-
- //log.debug("result = " + PrettyPrintingUtils.printArray(result));
-
- return result;
- }
-
- /**
- * Transforms a set of Sasl properties, defined using the property names in javax.security.sasl.Sasl, into
- * a bit mapped set of property flags encoded using the bit mapping constants defined in this class.
- *
- * @param properties The Sasl properties to bit map.
- *
- * @return A set of bit mapped properties encoded in an integer.
- */
- private int bitMapSaslProperties(Map properties)
- {
- //log.debug("private int bitMapSaslProperties(Map properties = " + properties + "): called");
-
- int result = 0;
-
- // No flags set if no properties are set.
- if (properties == null)
- {
- return result;
- }
-
- if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NOPLAINTEXT)))
- {
- result |= NOPLAINTEXT;
- }
-
- if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NOACTIVE)))
- {
- result |= NOACTIVE;
- }
-
- if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NODICTIONARY)))
- {
- result |= NODICTIONARY;
- }
-
- if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NOANONYMOUS)))
- {
- result |= NOANONYMOUS;
- }
-
- if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_FORWARD_SECRECY)))
- {
- result |= FORWARD_SECRECY;
- }
-
- if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_PASS_CREDENTIALS)))
- {
- result |= PASS_CREDENTIALS;
- }
-
- return result;
- }
-
- /**
- * Uses the specified call back handler to query for the users log in name and password.
- *
- * @param prefix A prefix to prepend onto the username and password queries.
- * @param authorizationId The default autorhization name.
- * @param cbh The call back handler.
- *
- * @return The username and password from the callback.
- *
- * @throws SaslException If the callback fails for any reason.
- */
- private Object[] getUserInfo(String prefix, String authorizationId, CallbackHandler cbh) throws SaslException
- {
- // Check that the callback handler is defined.
- if (cbh == null)
- {
- throw new SaslException("Callback handler to get username/password required.");
- }
-
- try
- {
- String userPrompt = prefix + " authentication id: ";
- String passwdPrompt = prefix + " password: ";
-
- NameCallback ncb =
- (authorizationId == null) ? new NameCallback(userPrompt) : new NameCallback(userPrompt, authorizationId);
- PasswordCallback pcb = new PasswordCallback(passwdPrompt, false);
-
- // Ask the call back handler to get the users name and password.
- cbh.handle(new Callback[] { ncb, pcb });
-
- char[] pw = pcb.getPassword();
-
- byte[] bytepw;
- String authId;
-
- if (pw != null)
- {
- bytepw = new String(pw).getBytes("UTF8");
- pcb.clearPassword();
- }
- else
- {
- bytepw = null;
- }
-
- authId = ncb.getName();
-
- return new Object[] { authId, bytepw };
- }
- catch (IOException e)
- {
- throw new SaslException("Cannot get password.", e);
- }
- catch (UnsupportedCallbackException e)
- {
- throw new SaslException("Cannot get userid/password.", e);
- }
- }
-}
+/* + * + * 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.sasl; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.security.auth.callback.*; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslClientFactory; +import javax.security.sasl.SaslException; + +import org.apache.log4j.Logger; + +import org.apache.qpid.util.PrettyPrintingUtils; + +/** + * Implements a factory for generating Sasl client implementations. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Provide a list of supported encryption mechansims that meet a defined set of Sasl properties. + * <tr><td> Provide the best matching supported Sasl mechanism to a preference ordered list of mechanisms and Sasl + * properties. + * <tr><td> Perform username and password request call backs. <td> CallBackHandler + * </table> + */ +public class ClientFactoryImpl implements SaslClientFactory +{ + //private static final Logger log = Logger.getLogger(ClientFactoryImpl.class); + + /** Holds the names of the supported encryption mechanisms. */ + private static final String[] SUPPORTED_MECHANISMS = { "CRAM-MD5", "PLAIN" }; + + /** Defines index of the CRAM-MD5 mechanism within the supported mechanisms. */ + private static final int CRAM_MD5 = 0; + + /** Defines index of the PLAIN mechanism within the supported mechanisms. */ + private static final int PLAIN = 1; + + /** Bit mapping of the no plain text policy. */ + private static final int NOPLAINTEXT = 0x0001; + + /** Bit mapping of the no susceptible active attacks policy. */ + private static final int NOACTIVE = 0x0002; + + /** Bit mapping of the no susceptible to dictionary attacks policy. */ + private static final int NODICTIONARY = 0x0004; + + /** Bit mapping of the must use forward secrecy between sessions policy. */ + private static final int FORWARD_SECRECY = 0x0008; + + /** Bit mapping of the no anonymous logins policy. */ + private static final int NOANONYMOUS = 0x0010; + + /** Bit mapping of the must pass credentials policy. */ + private static final int PASS_CREDENTIALS = 0x0020; + + /** Defines a mapping from supported mechanisms to supported policy flags. */ + private static final int[] SUPPPORTED_MECHANISMS_POLICIES = + { + NOPLAINTEXT | NOANONYMOUS, // CRAM-MD5 + NOANONYMOUS // PLAIN + }; + + /** + * Creates a SaslClient using the parameters supplied. + * + * @param mechanisms The non-null list of mechanism names to try. Each is the IANA-registered name of a SASL + * mechanism. (e.g. "GSSAPI", "CRAM-MD5"). + * @param authorizationId The possibly null protocol-dependent identification to be used for authorization. + * If null or empty, the server derives an authorization ID from the client's authentication + * credentials. When the SASL authentication completes successfully, the specified entity is + * granted access. + * @param protocol The non-null string name of the protocol for which the authentication is being performed + * (e.g., "ldap"). + * @param serverName The non-null fully qualified host name of the server to authenticate to. + * @param props The possibly null set of properties used to select the SASL mechanism and to configure the + * authentication exchange of the selected mechanism. See the <tt>Sasl</tt> class for a list + * of standard properties. Other, possibly mechanism-specific, properties can be included. + * Properties not relevant to the selected mechanism are ignored. + * @param cbh The possibly null callback handler to used by the SASL mechanisms to get further + * information from the application/library to complete the authentication. For example, a + * SASL mechanism might require the authentication ID, password and realm from the caller. + * The authentication ID is requested by using a <tt>NameCallback</tt>. + * The password is requested by using a <tt>PasswordCallback</tt>. + * The realm is requested by using a <tt>RealmChoiceCallback</tt> if there is a list + * of realms to choose from, and by using a <tt>RealmCallback</tt> if + * the realm must be entered. + * + * @return A possibly null <tt>SaslClient</tt> created using the parameters supplied. If null, this factory cannot + * produce a <tt>SaslClient</tt> using the parameters supplied. + * + * @throws javax.security.sasl.SaslException If cannot create a <tt>SaslClient</tt> because of an error. + */ + public SaslClient createSaslClient(String[] mechanisms, String authorizationId, String protocol, String serverName, + Map props, CallbackHandler cbh) throws SaslException + { + /*log.debug("public SaslClient createSaslClient(String[] mechanisms = " + PrettyPrintingUtils.printArray(mechanisms) + + ", String authorizationId = " + authorizationId + ", String protocol = " + protocol + + ", String serverName = " + serverName + ", Map props = " + props + ", CallbackHandler cbh): called");*/ + + // Get a list of all supported mechanisms that matched the required properties. + String[] matchingMechanisms = getMechanismNames(props); + //log.debug("matchingMechanisms = " + PrettyPrintingUtils.printArray(matchingMechanisms)); + + // Scan down the list of mechanisms until the first one that matches one of the matching supported mechanisms + // is found. + String chosenMechanism = null; + + for (int i = 0; i < mechanisms.length; i++) + { + String mechanism = mechanisms[i]; + + for (int j = 0; j < matchingMechanisms.length; j++) + { + String matchingMechanism = matchingMechanisms[j]; + + if (mechanism.equals(matchingMechanism)) + { + chosenMechanism = mechanism; + + break; + } + } + + // Stop scanning if a match has been found. + if (chosenMechanism != null) + { + break; + } + } + + // Check that a matching mechanism was found or return null otherwise. + if (chosenMechanism == null) + { + //log.debug("No matching mechanism could be found."); + + return null; + } + + // Instantiate an appropriate client type for the chosen mechanism. + if (chosenMechanism.equals(SUPPORTED_MECHANISMS[CRAM_MD5])) + { + Object[] uinfo = getUserInfo("CRAM-MD5", authorizationId, cbh); + + //log.debug("Using CRAM-MD5 mechanism."); + + return new CramMD5Client((String) uinfo[0], (byte[]) uinfo[1]); + } + else + { + Object[] uinfo = getUserInfo("PLAIN", authorizationId, cbh); + + //log.debug("Using PLAIN mechanism."); + + return new PlainClient(authorizationId, (String) uinfo[0], (byte[]) uinfo[1]); + } + } + + /** + * Returns an array of names of mechanisms that match the specified + * mechanism selection policies. + * + * @param props The possibly null set of properties used to specify the + * security policy of the SASL mechanisms. For example, if <tt>props</tt> + * contains the <tt>Sasl.POLICY_NOPLAINTEXT</tt> property with the value + * <tt>"true"</tt>, then the factory must not return any SASL mechanisms + * that are susceptible to simple plain passive attacks. + * See the <tt>Sasl</tt> class for a complete list of policy properties. + * Non-policy related properties, if present in <tt>props</tt>, are ignored. + * + * @return A non-null array containing a IANA-registered SASL mechanism names. + */ + public String[] getMechanismNames(Map props) + { + //log.debug("public String[] getMechanismNames(Map props = " + props + "): called"); + + // Used to build up the valid mechanisms in. + List validMechanisms = new ArrayList(); + + // Transform the Sasl properties into a set of bit mapped flags indicating the required properties of the + // encryption mechanism employed. + int requiredFlags = bitMapSaslProperties(props); + //log.debug("requiredFlags = " + requiredFlags); + + // Scan down the list of supported mechanisms filtering in only those that satisfy all of the desired + // encryption properties. + for (int i = 0; i < SUPPORTED_MECHANISMS.length; i++) + { + int mechanismFlags = SUPPPORTED_MECHANISMS_POLICIES[i]; + //log.debug("mechanismFlags = " + mechanismFlags); + + // Check if the current mechanism contains all of the required flags. + if ((requiredFlags & ~mechanismFlags) == 0) + { + //log.debug("Mechanism " + SUPPORTED_MECHANISMS[i] + " meets the required properties."); + validMechanisms.add(SUPPORTED_MECHANISMS[i]); + } + } + + String[] result = (String[]) validMechanisms.toArray(new String[validMechanisms.size()]); + + //log.debug("result = " + PrettyPrintingUtils.printArray(result)); + + return result; + } + + /** + * Transforms a set of Sasl properties, defined using the property names in javax.security.sasl.Sasl, into + * a bit mapped set of property flags encoded using the bit mapping constants defined in this class. + * + * @param properties The Sasl properties to bit map. + * + * @return A set of bit mapped properties encoded in an integer. + */ + private int bitMapSaslProperties(Map properties) + { + //log.debug("private int bitMapSaslProperties(Map properties = " + properties + "): called"); + + int result = 0; + + // No flags set if no properties are set. + if (properties == null) + { + return result; + } + + if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NOPLAINTEXT))) + { + result |= NOPLAINTEXT; + } + + if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NOACTIVE))) + { + result |= NOACTIVE; + } + + if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NODICTIONARY))) + { + result |= NODICTIONARY; + } + + if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NOANONYMOUS))) + { + result |= NOANONYMOUS; + } + + if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_FORWARD_SECRECY))) + { + result |= FORWARD_SECRECY; + } + + if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_PASS_CREDENTIALS))) + { + result |= PASS_CREDENTIALS; + } + + return result; + } + + /** + * Uses the specified call back handler to query for the users log in name and password. + * + * @param prefix A prefix to prepend onto the username and password queries. + * @param authorizationId The default autorhization name. + * @param cbh The call back handler. + * + * @return The username and password from the callback. + * + * @throws SaslException If the callback fails for any reason. + */ + private Object[] getUserInfo(String prefix, String authorizationId, CallbackHandler cbh) throws SaslException + { + // Check that the callback handler is defined. + if (cbh == null) + { + throw new SaslException("Callback handler to get username/password required."); + } + + try + { + String userPrompt = prefix + " authentication id: "; + String passwdPrompt = prefix + " password: "; + + NameCallback ncb = + (authorizationId == null) ? new NameCallback(userPrompt) : new NameCallback(userPrompt, authorizationId); + PasswordCallback pcb = new PasswordCallback(passwdPrompt, false); + + // Ask the call back handler to get the users name and password. + cbh.handle(new Callback[] { ncb, pcb }); + + char[] pw = pcb.getPassword(); + + byte[] bytepw; + String authId; + + if (pw != null) + { + bytepw = new String(pw).getBytes("UTF8"); + pcb.clearPassword(); + } + else + { + bytepw = null; + } + + authId = ncb.getName(); + + return new Object[] { authId, bytepw }; + } + catch (IOException e) + { + throw new SaslException("Cannot get password.", e); + } + catch (UnsupportedCallbackException e) + { + throw new SaslException("Cannot get userid/password.", e); + } + } +} diff --git a/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/CramMD5Client.java b/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/CramMD5Client.java index 7d15f03329..4f890ffed5 100644 --- a/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/CramMD5Client.java +++ b/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/CramMD5Client.java @@ -1,347 +1,347 @@ -/*
- *
- * 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.sasl;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-import javax.security.sasl.Sasl;
-import javax.security.sasl.SaslClient;
-import javax.security.sasl.SaslException;
-
-/**
- * Implements the CRAM-MD5 SASL mechanism.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Concatenate the user id and password hashed by a challenge string as the challenge response.
- * <tr><td> Ensure password is wiped once a challenge has been processed.
- * </table>
- */
-public class CramMD5Client implements SaslClient
-{
- /** Defines the HMAC block size. */
- private static final int MD5_BLOCKSIZE = 64;
-
- /** Flag used to indicate that the authentication has completed. */
- private boolean completed = false;
-
- private String authenticationId;
- private byte[] password;
-
- /**
- * Creates a PLAIN SASL client for an authorization id, authentication id and password.
- *
- * @param authenticationId The authentication id.
- * @param password The password.
- *
- * @throws SaslException If the authentication id or password is null.
- */
- CramMD5Client(String authenticationId, byte[] password) throws SaslException
- {
- // Check that a username and password are specified.
- if ((authenticationId == null) || (password == null))
- {
- throw new SaslException("CRAM: authentication id and password must be specified");
- }
-
- // Keep the log in credentials.
- this.authenticationId = authenticationId;
- this.password = password;
- }
-
- /**
- * Returns the IANA-registered mechanism name of this SASL client. (e.g. "CRAM-MD5", "GSSAPI").
- *
- * @return A non-null string representing the IANA-registered mechanism name.
- */
- public String getMechanismName()
- {
- return "CRAM-MD5";
- }
-
- /**
- * Determines whether this mechanism has an optional initial response. If true, caller should call
- * <tt>evaluateChallenge()</tt> with an empty array to get the initial response.
- *
- * <p/>CRAM-MD5 has no intial response.
- *
- * @return true if this mechanism has an initial response.
- */
- public boolean hasInitialResponse()
- {
- return false;
- }
-
- /**
- * Evaluates the challenge data and generates a response. If a challenge is received from the server during the
- * authentication process, this method is called to prepare an appropriate next response to submit to the server.
- *
- * <p/>The initial response for the SASL command, for the CRAM-MD5 mechanism is the concatenation of authentication
- * ID and password HMAC-MD5 hashed by the server challenge.
- *
- * @param challenge The non-null challenge sent from the server. The challenge array may have zero length.
- *
- * @return The possibly null reponse to send to the server. It is null if the challenge accompanied a "SUCCESS"
- * status and the challenge only contains data for the client to update its state and no response
- * needs to be sent to the server. The response is a zero-length byte array if the client is to send a
- * response with no data.
- *
- * @throws javax.security.sasl.SaslException If an error occurred while processing the challenge or generating a
- * response.
- */
- public byte[] evaluateChallenge(byte[] challenge) throws SaslException
- {
- // Check that the authentication has not already been performed.
- if (completed)
- {
- throw new IllegalStateException("CRAM-MD5 authentication already completed.");
- }
-
- // Check if the password is null, this will be the case if a previous attempt to authenticated failed.
- if (password == null)
- {
- throw new IllegalStateException("CRAM-MD5 authentication previously aborted due to error.");
- }
-
- // Generate a keyed-MD5 digest from the user's password keyed by the challenge bytes.
- try
- {
- String digest = hmac_md5(password, challenge);
- String result = authenticationId + " " + digest;
-
- completed = true;
-
- return result.getBytes("UTF8");
- }
- catch (java.security.NoSuchAlgorithmException e)
- {
- throw new SaslException("MD5 algorithm not available on platform", e);
- }
- catch (java.io.UnsupportedEncodingException e)
- {
- throw new SaslException("UTF8 not available on platform", e);
- }
- finally
- {
- clearPassword();
- }
- }
-
- /**
- * Determines whether the authentication exchange has completed. This method may be called at any time, but
- * typically, it will not be called until the caller has received indication from the server (in a protocol-specific
- * manner) that the exchange has completed.
- *
- * @return true if the authentication exchange has completed; false otherwise.
- */
- public boolean isComplete()
- {
- return completed;
- }
-
- /**
- * Unwraps a byte array received from the server. This method can be called only after the authentication exchange
- * has completed (i.e., when <tt>isComplete()</tt> returns true) and only if the authentication exchange has
- * negotiated integrity and/or privacy as the quality of protection; otherwise, an <tt>IllegalStateException</tt> is
- * thrown.
- *
- * <p/><tt>incoming</tt> is the contents of the SASL buffer as defined in RFC 2222 without the leading four octet
- * field that represents the length. <tt>offset</tt> and <tt>len</tt> specify the portion of <tt>incoming</tt>
- * to use.
- *
- * @param incoming A non-null byte array containing the encoded bytes from the server.
- * @param offset The starting position at <tt>incoming</tt> of the bytes to use.
- * @param len The number of bytes from <tt>incoming</tt> to use.
- *
- * @return A non-null byte array containing the decoded bytes.
- *
- * @throws javax.security.sasl.SaslException If <tt>incoming</tt> cannot be successfully unwrapped.
- * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of
- * protection has neither integrity nor privacy.
- */
- public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException
- {
- throw new SaslException("CRAM-MD5 does not support quality of protection.");
- }
-
- /**
- * Wraps a byte array to be sent to the server. This method can be called only after the authentication exchange has
- * completed (i.e., when <tt>isComplete()</tt> returns true) and only if the authentication exchange has negotiated
- * integrity and/or privacy as the quality of protection; otherwise, an <tt>IllegalStateException</tt> is thrown.
- *
- * <p/>The result of this method will make up the contents of the SASL buffer as defined in RFC 2222 without the
- * leading four octet field that represents the length. <tt>offset</tt> and <tt>len</tt> specify the portion of
- * <tt>outgoing</tt> to use.
- *
- * @param outgoing A non-null byte array containing the bytes to encode.
- * @param offset The starting position at <tt>outgoing</tt> of the bytes to use.
- * @param len The number of bytes from <tt>outgoing</tt> to use.
- *
- * @return A non-null byte array containing the encoded bytes.
- *
- * @throws javax.security.sasl.SaslException If <tt>outgoing</tt> cannot be successfully wrapped.
- * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of
- * protection has neither integrity nor privacy.
- */
- public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException
- {
- throw new SaslException("CRAM-MD5 does not support quality of protection.");
- }
-
- /**
- * Retrieves the negotiated property. This method can be called only after the authentication exchange has
- * completed (i.e., when <tt>isComplete()</tt> returns true); otherwise, an <tt>IllegalStateException</tt> is thrown.
- *
- * @param propName The non-null property name.
- *
- * @return The value of the negotiated property. If null, the property was not negotiated or is not applicable to
- * this mechanism.
- *
- * @throws IllegalStateException If this authentication exchange has not completed.
- */
- public Object getNegotiatedProperty(String propName)
- {
- if (completed)
- {
- if (propName.equals(Sasl.QOP))
- {
- return "auth";
- }
- else
- {
- return null;
- }
- }
- else
- {
- throw new IllegalStateException("CRAM-MD5 authentication not completed");
- }
- }
-
- /**
- * Disposes of any system resources or security-sensitive information the SaslClient might be using. Invoking this
- * method invalidates the SaslClient instance. This method is idempotent.
- *
- * @throws javax.security.sasl.SaslException If a problem was encountered while disposing the resources.
- */
- public void dispose() throws SaslException
- { }
-
- /*
- * Hashes its input arguments according to HMAC-MD5 (RFC 2104) and returns the resulting digest in its ASCII
- * representation.
- *
- * <p/> The HMAC-MD5 function is described as follows:
- * <p/><pre>
- * MD5(key XOR opad, MD5(key XOR ipad, text))
- * </pre>
- *
- * <p/>Where key is an n byte key, ipad is the byte 0x36 repeated 64 times, opad is the byte 0x5c repeated 64 times
- * and text is the data to be protected.
- *
- * @param key The key to hash by.
- * @param text The plain text to hash.
- *
- * @return The hashed text.
- *
- * @throws NoSuchAlgorithmException If the Java platform does not supply an MD5 implementation.
- */
- private static final String hmac_md5(byte[] key, byte[] text) throws NoSuchAlgorithmException
- {
- MessageDigest md5 = MessageDigest.getInstance("MD5");
-
- /* digest the key if longer than 64 bytes */
- if (key.length > 64)
- {
- key = md5.digest(key);
- }
-
- byte[] ipad = new byte[MD5_BLOCKSIZE]; /* inner padding */
- byte[] opad = new byte[MD5_BLOCKSIZE]; /* outer padding */
- byte[] digest;
- int i;
-
- /* store key in pads */
- for (i = 0; i < MD5_BLOCKSIZE; i++)
- {
- for (; i < key.length; i++)
- {
- ipad[i] = key[i];
- opad[i] = key[i];
- }
-
- ipad[i] = 0x00;
- opad[i] = 0x00;
- }
-
- /* XOR key with pads */
- for (i = 0; i < MD5_BLOCKSIZE; i++)
- {
- ipad[i] ^= 0x36;
- opad[i] ^= 0x5c;
- }
-
- /* inner MD5 */
- md5.update(ipad);
- md5.update(text);
- digest = md5.digest();
-
- /* outer MD5 */
- md5.update(opad);
- md5.update(digest);
- digest = md5.digest();
-
- // Get character representation of digest
- StringBuffer digestString = new StringBuffer();
-
- for (i = 0; i < digest.length; i++)
- {
- if ((digest[i] & 0x000000ff) < 0x10)
- {
- digestString.append("0" + Integer.toHexString(digest[i] & 0x000000ff));
- }
- else
- {
- digestString.append(Integer.toHexString(digest[i] & 0x000000ff));
- }
- }
-
- return (digestString.toString());
- }
-
- /**
- * Overwrites the password with zeros.
- */
- private void clearPassword()
- {
- if (password != null)
- {
- // Zero out password.
- for (int i = 0; i < password.length; i++)
- {
- password[i] = (byte) 0;
- }
-
- password = null;
- }
- }
-}
+/* + * + * 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.sasl; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +/** + * Implements the CRAM-MD5 SASL mechanism. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Concatenate the user id and password hashed by a challenge string as the challenge response. + * <tr><td> Ensure password is wiped once a challenge has been processed. + * </table> + */ +public class CramMD5Client implements SaslClient +{ + /** Defines the HMAC block size. */ + private static final int MD5_BLOCKSIZE = 64; + + /** Flag used to indicate that the authentication has completed. */ + private boolean completed = false; + + private String authenticationId; + private byte[] password; + + /** + * Creates a PLAIN SASL client for an authorization id, authentication id and password. + * + * @param authenticationId The authentication id. + * @param password The password. + * + * @throws SaslException If the authentication id or password is null. + */ + CramMD5Client(String authenticationId, byte[] password) throws SaslException + { + // Check that a username and password are specified. + if ((authenticationId == null) || (password == null)) + { + throw new SaslException("CRAM: authentication id and password must be specified"); + } + + // Keep the log in credentials. + this.authenticationId = authenticationId; + this.password = password; + } + + /** + * Returns the IANA-registered mechanism name of this SASL client. (e.g. "CRAM-MD5", "GSSAPI"). + * + * @return A non-null string representing the IANA-registered mechanism name. + */ + public String getMechanismName() + { + return "CRAM-MD5"; + } + + /** + * Determines whether this mechanism has an optional initial response. If true, caller should call + * <tt>evaluateChallenge()</tt> with an empty array to get the initial response. + * + * <p/>CRAM-MD5 has no intial response. + * + * @return true if this mechanism has an initial response. + */ + public boolean hasInitialResponse() + { + return false; + } + + /** + * Evaluates the challenge data and generates a response. If a challenge is received from the server during the + * authentication process, this method is called to prepare an appropriate next response to submit to the server. + * + * <p/>The initial response for the SASL command, for the CRAM-MD5 mechanism is the concatenation of authentication + * ID and password HMAC-MD5 hashed by the server challenge. + * + * @param challenge The non-null challenge sent from the server. The challenge array may have zero length. + * + * @return The possibly null reponse to send to the server. It is null if the challenge accompanied a "SUCCESS" + * status and the challenge only contains data for the client to update its state and no response + * needs to be sent to the server. The response is a zero-length byte array if the client is to send a + * response with no data. + * + * @throws javax.security.sasl.SaslException If an error occurred while processing the challenge or generating a + * response. + */ + public byte[] evaluateChallenge(byte[] challenge) throws SaslException + { + // Check that the authentication has not already been performed. + if (completed) + { + throw new IllegalStateException("CRAM-MD5 authentication already completed."); + } + + // Check if the password is null, this will be the case if a previous attempt to authenticated failed. + if (password == null) + { + throw new IllegalStateException("CRAM-MD5 authentication previously aborted due to error."); + } + + // Generate a keyed-MD5 digest from the user's password keyed by the challenge bytes. + try + { + String digest = hmac_md5(password, challenge); + String result = authenticationId + " " + digest; + + completed = true; + + return result.getBytes("UTF8"); + } + catch (java.security.NoSuchAlgorithmException e) + { + throw new SaslException("MD5 algorithm not available on platform", e); + } + catch (java.io.UnsupportedEncodingException e) + { + throw new SaslException("UTF8 not available on platform", e); + } + finally + { + clearPassword(); + } + } + + /** + * Determines whether the authentication exchange has completed. This method may be called at any time, but + * typically, it will not be called until the caller has received indication from the server (in a protocol-specific + * manner) that the exchange has completed. + * + * @return true if the authentication exchange has completed; false otherwise. + */ + public boolean isComplete() + { + return completed; + } + + /** + * Unwraps a byte array received from the server. This method can be called only after the authentication exchange + * has completed (i.e., when <tt>isComplete()</tt> returns true) and only if the authentication exchange has + * negotiated integrity and/or privacy as the quality of protection; otherwise, an <tt>IllegalStateException</tt> is + * thrown. + * + * <p/><tt>incoming</tt> is the contents of the SASL buffer as defined in RFC 2222 without the leading four octet + * field that represents the length. <tt>offset</tt> and <tt>len</tt> specify the portion of <tt>incoming</tt> + * to use. + * + * @param incoming A non-null byte array containing the encoded bytes from the server. + * @param offset The starting position at <tt>incoming</tt> of the bytes to use. + * @param len The number of bytes from <tt>incoming</tt> to use. + * + * @return A non-null byte array containing the decoded bytes. + * + * @throws javax.security.sasl.SaslException If <tt>incoming</tt> cannot be successfully unwrapped. + * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of + * protection has neither integrity nor privacy. + */ + public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException + { + throw new SaslException("CRAM-MD5 does not support quality of protection."); + } + + /** + * Wraps a byte array to be sent to the server. This method can be called only after the authentication exchange has + * completed (i.e., when <tt>isComplete()</tt> returns true) and only if the authentication exchange has negotiated + * integrity and/or privacy as the quality of protection; otherwise, an <tt>IllegalStateException</tt> is thrown. + * + * <p/>The result of this method will make up the contents of the SASL buffer as defined in RFC 2222 without the + * leading four octet field that represents the length. <tt>offset</tt> and <tt>len</tt> specify the portion of + * <tt>outgoing</tt> to use. + * + * @param outgoing A non-null byte array containing the bytes to encode. + * @param offset The starting position at <tt>outgoing</tt> of the bytes to use. + * @param len The number of bytes from <tt>outgoing</tt> to use. + * + * @return A non-null byte array containing the encoded bytes. + * + * @throws javax.security.sasl.SaslException If <tt>outgoing</tt> cannot be successfully wrapped. + * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of + * protection has neither integrity nor privacy. + */ + public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException + { + throw new SaslException("CRAM-MD5 does not support quality of protection."); + } + + /** + * Retrieves the negotiated property. This method can be called only after the authentication exchange has + * completed (i.e., when <tt>isComplete()</tt> returns true); otherwise, an <tt>IllegalStateException</tt> is thrown. + * + * @param propName The non-null property name. + * + * @return The value of the negotiated property. If null, the property was not negotiated or is not applicable to + * this mechanism. + * + * @throws IllegalStateException If this authentication exchange has not completed. + */ + public Object getNegotiatedProperty(String propName) + { + if (completed) + { + if (propName.equals(Sasl.QOP)) + { + return "auth"; + } + else + { + return null; + } + } + else + { + throw new IllegalStateException("CRAM-MD5 authentication not completed"); + } + } + + /** + * Disposes of any system resources or security-sensitive information the SaslClient might be using. Invoking this + * method invalidates the SaslClient instance. This method is idempotent. + * + * @throws javax.security.sasl.SaslException If a problem was encountered while disposing the resources. + */ + public void dispose() throws SaslException + { } + + /* + * Hashes its input arguments according to HMAC-MD5 (RFC 2104) and returns the resulting digest in its ASCII + * representation. + * + * <p/> The HMAC-MD5 function is described as follows: + * <p/><pre> + * MD5(key XOR opad, MD5(key XOR ipad, text)) + * </pre> + * + * <p/>Where key is an n byte key, ipad is the byte 0x36 repeated 64 times, opad is the byte 0x5c repeated 64 times + * and text is the data to be protected. + * + * @param key The key to hash by. + * @param text The plain text to hash. + * + * @return The hashed text. + * + * @throws NoSuchAlgorithmException If the Java platform does not supply an MD5 implementation. + */ + private static final String hmac_md5(byte[] key, byte[] text) throws NoSuchAlgorithmException + { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + + /* digest the key if longer than 64 bytes */ + if (key.length > 64) + { + key = md5.digest(key); + } + + byte[] ipad = new byte[MD5_BLOCKSIZE]; /* inner padding */ + byte[] opad = new byte[MD5_BLOCKSIZE]; /* outer padding */ + byte[] digest; + int i; + + /* store key in pads */ + for (i = 0; i < MD5_BLOCKSIZE; i++) + { + for (; i < key.length; i++) + { + ipad[i] = key[i]; + opad[i] = key[i]; + } + + ipad[i] = 0x00; + opad[i] = 0x00; + } + + /* XOR key with pads */ + for (i = 0; i < MD5_BLOCKSIZE; i++) + { + ipad[i] ^= 0x36; + opad[i] ^= 0x5c; + } + + /* inner MD5 */ + md5.update(ipad); + md5.update(text); + digest = md5.digest(); + + /* outer MD5 */ + md5.update(opad); + md5.update(digest); + digest = md5.digest(); + + // Get character representation of digest + StringBuffer digestString = new StringBuffer(); + + for (i = 0; i < digest.length; i++) + { + if ((digest[i] & 0x000000ff) < 0x10) + { + digestString.append("0" + Integer.toHexString(digest[i] & 0x000000ff)); + } + else + { + digestString.append(Integer.toHexString(digest[i] & 0x000000ff)); + } + } + + return (digestString.toString()); + } + + /** + * Overwrites the password with zeros. + */ + private void clearPassword() + { + if (password != null) + { + // Zero out password. + for (int i = 0; i < password.length; i++) + { + password[i] = (byte) 0; + } + + password = null; + } + } +} diff --git a/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/PlainClient.java b/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/PlainClient.java index 97e607c57e..f5a9f150bb 100644 --- a/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/PlainClient.java +++ b/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/PlainClient.java @@ -1,275 +1,275 @@ -/*
- *
- * 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.sasl;
-
-import javax.security.sasl.Sasl;
-import javax.security.sasl.SaslClient;
-import javax.security.sasl.SaslException;
-
-/**
- * Implements the PLAIN SASL mechanism.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Concatenate the user id and password in plain text as the challenge respose.
- * <tr><td> Ensure password is wiped once a challenge has been processed.
- * </table>
- */
-public class PlainClient implements SaslClient
-{
- /** Flag used to indicate that the authentication has completed. */
- private boolean completed = false;
-
- private String authorizationId;
- private String authenticationId;
- private byte[] password;
-
- private static byte SEPERATOR = 0; // US-ASCII <NUL>
-
- /**
- * Creates a PLAIN SASL client for an authorization id, authentication id and password.
- *
- * @param authorizationId The authorization id. May be null.
- * @param authenticationId The authentication id.
- * @param password The password.
- *
- * @throws SaslException If the authentication id or password is null.
- */
- PlainClient(String authorizationId, String authenticationId, byte[] password) throws SaslException
- {
- // Check that a username and password are specified.
- if ((authenticationId == null) || (password == null))
- {
- throw new SaslException("PLAIN: authentication ID and password must be specified");
- }
-
- // Keep the log in credentials.
- this.authorizationId = authorizationId;
- this.authenticationId = authenticationId;
- this.password = password;
- }
-
- /**
- * Returns the IANA-registered mechanism name of this SASL client. (e.g. "CRAM-MD5", "GSSAPI").
- *
- * @return A non-null string representing the IANA-registered mechanism name.
- */
- public String getMechanismName()
- {
- return "PLAIN";
- }
-
- /**
- * Determines whether this mechanism has an optional initial response. If true, caller should call
- * <tt>evaluateChallenge()</tt> with an empty array to get the initial response.
- *
- * @return true if this mechanism has an initial response.
- */
- public boolean hasInitialResponse()
- {
- return true;
- }
-
- /**
- * Evaluates the challenge data and generates a response. If a challenge is received from the server during the
- * authentication process, this method is called to prepare an appropriate next response to submit to the server.
- *
- * <p/>The initial response for the SASL command, for the PLAIN mechanism is the concatenation of authorization ID,
- * authentication ID and password, with each component separated by the US-ASCII <NUL> byte.
- *
- * @param challenge The non-null challenge sent from the server. The challenge array may have zero length.
- *
- * @return The possibly null reponse to send to the server. It is null if the challenge accompanied a "SUCCESS"
- * status and the challenge only contains data for the client to update its state and no response
- * needs to be sent to the server. The response is a zero-length byte array if the client is to send a
- * response with no data.
- *
- * @throws javax.security.sasl.SaslException If an error occurred while processing the challenge or generating a
- * response.
- */
- public byte[] evaluateChallenge(byte[] challenge) throws SaslException
- {
- // Check that the authentication has not already been performed.
- if (completed)
- {
- throw new IllegalStateException("PLAIN authentication already completed");
- }
-
- try
- {
- // Get the authorization and authentication ids in bytes.
- byte[] authorizationBytes = (authorizationId != null) ? authorizationId.getBytes("UTF8") : null;
- byte[] authenticationBytes = authenticationId.getBytes("UTF8");
-
- // Create an array big enough to hold the results.
- byte[] result =
- new byte[password.length + authenticationBytes.length + 2
- + ((authorizationBytes == null) ? 0 : authorizationBytes.length)];
-
- // Copy the authorization id, authentication id and password into the results.
- int pos = 0;
- if (authorizationBytes != null)
- {
- System.arraycopy(authorizationBytes, 0, result, 0, authorizationBytes.length);
- pos = authorizationBytes.length;
- }
-
- result[pos++] = SEPERATOR;
- System.arraycopy(authenticationBytes, 0, result, pos, authenticationBytes.length);
-
- pos += authenticationBytes.length;
- result[pos++] = SEPERATOR;
-
- System.arraycopy(password, 0, result, pos, password.length);
-
- completed = true;
-
- return result;
- }
- catch (java.io.UnsupportedEncodingException e)
- {
- throw new SaslException("Cannot get UTF-8 encoding of ids", e);
- }
- finally
- {
- clearPassword();
- }
- }
-
- /**
- * Determines whether the authentication exchange has completed. This method may be called at any time, but
- * typically, it will not be called until the caller has received indication from the server (in a protocol-specific
- * manner) that the exchange has completed.
- *
- * @return true if the authentication exchange has completed; false otherwise.
- */
- public boolean isComplete()
- {
- return completed;
- }
-
- /**
- * Unwraps a byte array received from the server. This method can be called only after the authentication exchange has
- * completed (i.e., when <tt>isComplete()</tt> returns true) and only if the authentication exchange has negotiated
- * integrity and/or privacy as the quality of protection; otherwise, an <tt>IllegalStateException</tt> is thrown.
- *
- * <p/><tt>incoming</tt> is the contents of the SASL buffer as defined in RFC 2222 without the leading four octet
- * field that represents the length. <tt>offset</tt> and <tt>len</tt> specify the portion of <tt>incoming</tt>
- * to use.
- *
- * @param incoming A non-null byte array containing the encoded bytes
- * from the server.
- * @param offset The starting position at <tt>incoming</tt> of the bytes to use.
- * @param len The number of bytes from <tt>incoming</tt> to use.
- *
- * @return A non-null byte array containing the decoded bytes.
- *
- * @throws javax.security.sasl.SaslException If <tt>incoming</tt> cannot be successfully unwrapped.
- * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of
- * protection has neither integrity nor privacy.
- */
- public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException
- {
- throw new SaslException("PLAIN does not support quality of protection.");
- }
-
- /**
- * Wraps a byte array to be sent to the server. This method can be called only after the authentication exchange has
- * completed (i.e., when <tt>isComplete()</tt> returns true) and only if the authentication exchange has negotiated
- * integrity and/or privacy as the quality of protection; otherwise, an <tt>IllegalStateException</tt> is thrown.
- *
- * <p/>The result of this method will make up the contents of the SASL buffer as defined in RFC 2222 without the
- * leading four octet field that represents the length. <tt>offset</tt> and <tt>len</tt> specify the portion of
- * <tt>outgoing</tt> to use.
- *
- * @param outgoing A non-null byte array containing the bytes to encode.
- * @param offset The starting position at <tt>outgoing</tt> of the bytes to use.
- * @param len The number of bytes from <tt>outgoing</tt> to use.
- *
- * @return A non-null byte array containing the encoded bytes.
- *
- * @throws javax.security.sasl.SaslException If <tt>outgoing</tt> cannot be successfully wrapped.
- * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of
- * protection has neither integrity nor privacy.
- */
- public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException
- {
- throw new SaslException("PLAIN does not support quality of protection.");
- }
-
- /**
- * Retrieves the negotiated property. This method can be called only after the authentication exchange has
- * completed (i.e., when <tt>isComplete()</tt> returns true); otherwise, an <tt>IllegalStateException</tt> is thrown.
- *
- * @param propName The non-null property name.
- *
- * @return The value of the negotiated property. If null, the property was not negotiated or is not applicable to
- * this mechanism.
- *
- * @throws IllegalStateException If this authentication exchange has not completed.
- */
- public Object getNegotiatedProperty(String propName)
- {
- if (completed)
- {
- if (propName.equals(Sasl.QOP))
- {
- return "auth";
- }
- else
- {
- return null;
- }
- }
- else
- {
- throw new IllegalStateException("PLAIN authentication not completed");
- }
- }
-
- /**
- * Disposes of any system resources or security-sensitive information the SaslClient might be using. Invoking this
- * method invalidates the SaslClient instance. This method is idempotent.
- *
- * @throws javax.security.sasl.SaslException If a problem was encountered while disposing the resources.
- */
- public void dispose() throws SaslException
- {
- clearPassword();
- }
-
- /**
- * Overwrites the password with zeros.
- */
- private void clearPassword()
- {
- if (password != null)
- {
- // Zero out password.
- for (int i = 0; i < password.length; i++)
- {
- password[i] = (byte) 0;
- }
-
- password = null;
- }
- }
-}
+/* + * + * 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.sasl; + +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +/** + * Implements the PLAIN SASL mechanism. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Concatenate the user id and password in plain text as the challenge respose. + * <tr><td> Ensure password is wiped once a challenge has been processed. + * </table> + */ +public class PlainClient implements SaslClient +{ + /** Flag used to indicate that the authentication has completed. */ + private boolean completed = false; + + private String authorizationId; + private String authenticationId; + private byte[] password; + + private static byte SEPERATOR = 0; // US-ASCII <NUL> + + /** + * Creates a PLAIN SASL client for an authorization id, authentication id and password. + * + * @param authorizationId The authorization id. May be null. + * @param authenticationId The authentication id. + * @param password The password. + * + * @throws SaslException If the authentication id or password is null. + */ + PlainClient(String authorizationId, String authenticationId, byte[] password) throws SaslException + { + // Check that a username and password are specified. + if ((authenticationId == null) || (password == null)) + { + throw new SaslException("PLAIN: authentication ID and password must be specified"); + } + + // Keep the log in credentials. + this.authorizationId = authorizationId; + this.authenticationId = authenticationId; + this.password = password; + } + + /** + * Returns the IANA-registered mechanism name of this SASL client. (e.g. "CRAM-MD5", "GSSAPI"). + * + * @return A non-null string representing the IANA-registered mechanism name. + */ + public String getMechanismName() + { + return "PLAIN"; + } + + /** + * Determines whether this mechanism has an optional initial response. If true, caller should call + * <tt>evaluateChallenge()</tt> with an empty array to get the initial response. + * + * @return true if this mechanism has an initial response. + */ + public boolean hasInitialResponse() + { + return true; + } + + /** + * Evaluates the challenge data and generates a response. If a challenge is received from the server during the + * authentication process, this method is called to prepare an appropriate next response to submit to the server. + * + * <p/>The initial response for the SASL command, for the PLAIN mechanism is the concatenation of authorization ID, + * authentication ID and password, with each component separated by the US-ASCII <NUL> byte. + * + * @param challenge The non-null challenge sent from the server. The challenge array may have zero length. + * + * @return The possibly null reponse to send to the server. It is null if the challenge accompanied a "SUCCESS" + * status and the challenge only contains data for the client to update its state and no response + * needs to be sent to the server. The response is a zero-length byte array if the client is to send a + * response with no data. + * + * @throws javax.security.sasl.SaslException If an error occurred while processing the challenge or generating a + * response. + */ + public byte[] evaluateChallenge(byte[] challenge) throws SaslException + { + // Check that the authentication has not already been performed. + if (completed) + { + throw new IllegalStateException("PLAIN authentication already completed"); + } + + try + { + // Get the authorization and authentication ids in bytes. + byte[] authorizationBytes = (authorizationId != null) ? authorizationId.getBytes("UTF8") : null; + byte[] authenticationBytes = authenticationId.getBytes("UTF8"); + + // Create an array big enough to hold the results. + byte[] result = + new byte[password.length + authenticationBytes.length + 2 + + ((authorizationBytes == null) ? 0 : authorizationBytes.length)]; + + // Copy the authorization id, authentication id and password into the results. + int pos = 0; + if (authorizationBytes != null) + { + System.arraycopy(authorizationBytes, 0, result, 0, authorizationBytes.length); + pos = authorizationBytes.length; + } + + result[pos++] = SEPERATOR; + System.arraycopy(authenticationBytes, 0, result, pos, authenticationBytes.length); + + pos += authenticationBytes.length; + result[pos++] = SEPERATOR; + + System.arraycopy(password, 0, result, pos, password.length); + + completed = true; + + return result; + } + catch (java.io.UnsupportedEncodingException e) + { + throw new SaslException("Cannot get UTF-8 encoding of ids", e); + } + finally + { + clearPassword(); + } + } + + /** + * Determines whether the authentication exchange has completed. This method may be called at any time, but + * typically, it will not be called until the caller has received indication from the server (in a protocol-specific + * manner) that the exchange has completed. + * + * @return true if the authentication exchange has completed; false otherwise. + */ + public boolean isComplete() + { + return completed; + } + + /** + * Unwraps a byte array received from the server. This method can be called only after the authentication exchange has + * completed (i.e., when <tt>isComplete()</tt> returns true) and only if the authentication exchange has negotiated + * integrity and/or privacy as the quality of protection; otherwise, an <tt>IllegalStateException</tt> is thrown. + * + * <p/><tt>incoming</tt> is the contents of the SASL buffer as defined in RFC 2222 without the leading four octet + * field that represents the length. <tt>offset</tt> and <tt>len</tt> specify the portion of <tt>incoming</tt> + * to use. + * + * @param incoming A non-null byte array containing the encoded bytes + * from the server. + * @param offset The starting position at <tt>incoming</tt> of the bytes to use. + * @param len The number of bytes from <tt>incoming</tt> to use. + * + * @return A non-null byte array containing the decoded bytes. + * + * @throws javax.security.sasl.SaslException If <tt>incoming</tt> cannot be successfully unwrapped. + * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of + * protection has neither integrity nor privacy. + */ + public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException + { + throw new SaslException("PLAIN does not support quality of protection."); + } + + /** + * Wraps a byte array to be sent to the server. This method can be called only after the authentication exchange has + * completed (i.e., when <tt>isComplete()</tt> returns true) and only if the authentication exchange has negotiated + * integrity and/or privacy as the quality of protection; otherwise, an <tt>IllegalStateException</tt> is thrown. + * + * <p/>The result of this method will make up the contents of the SASL buffer as defined in RFC 2222 without the + * leading four octet field that represents the length. <tt>offset</tt> and <tt>len</tt> specify the portion of + * <tt>outgoing</tt> to use. + * + * @param outgoing A non-null byte array containing the bytes to encode. + * @param offset The starting position at <tt>outgoing</tt> of the bytes to use. + * @param len The number of bytes from <tt>outgoing</tt> to use. + * + * @return A non-null byte array containing the encoded bytes. + * + * @throws javax.security.sasl.SaslException If <tt>outgoing</tt> cannot be successfully wrapped. + * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of + * protection has neither integrity nor privacy. + */ + public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException + { + throw new SaslException("PLAIN does not support quality of protection."); + } + + /** + * Retrieves the negotiated property. This method can be called only after the authentication exchange has + * completed (i.e., when <tt>isComplete()</tt> returns true); otherwise, an <tt>IllegalStateException</tt> is thrown. + * + * @param propName The non-null property name. + * + * @return The value of the negotiated property. If null, the property was not negotiated or is not applicable to + * this mechanism. + * + * @throws IllegalStateException If this authentication exchange has not completed. + */ + public Object getNegotiatedProperty(String propName) + { + if (completed) + { + if (propName.equals(Sasl.QOP)) + { + return "auth"; + } + else + { + return null; + } + } + else + { + throw new IllegalStateException("PLAIN authentication not completed"); + } + } + + /** + * Disposes of any system resources or security-sensitive information the SaslClient might be using. Invoking this + * method invalidates the SaslClient instance. This method is idempotent. + * + * @throws javax.security.sasl.SaslException If a problem was encountered while disposing the resources. + */ + public void dispose() throws SaslException + { + clearPassword(); + } + + /** + * Overwrites the password with zeros. + */ + private void clearPassword() + { + if (password != null) + { + // Zero out password. + for (int i = 0; i < password.length; i++) + { + password[i] = (byte) 0; + } + + password = null; + } + } +} diff --git a/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/Provider.java b/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/Provider.java index f9a5c42c3d..ef075a0cad 100644 --- a/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/Provider.java +++ b/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/Provider.java @@ -1,61 +1,61 @@ -/*
- *
- * 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.sasl;
-
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-
-import org.apache.log4j.Logger;
-
-/**
- * A SASL provider for Java 1.4. Declares the capabilities of this implementation, which supports PLAIN and CRAM-MD5
- * client implementations only.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Declare PLAIN SASL support.
- * <tr><td> Declare CRAM-MD5 SASL support.
- * </table>
- */
-public class Provider extends java.security.Provider
-{
- //Logger log = Logger.getLogger(Provider.class);
-
- private static final String info = "Qpid SASL provider" + "(implements client mechanisms for: PLAIN, CRAM-MD5)";
-
- public Provider()
- {
- super("QpidSASL", 1.4, info);
-
- //log.debug("public Provider(): called");
-
- AccessController.doPrivileged(new PrivilegedAction()
- {
- public Object run()
- {
- put("SaslClientFactory.PLAIN", "org.apache.qpid.sasl.ClientFactoryImpl");
- put("SaslClientFactory.CRAM-MD5", "org.apache.qpid.sasl.ClientFactoryImpl");
-
- return null;
- }
- });
- }
-}
+/* + * + * 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.sasl; + +import java.security.AccessController; +import java.security.PrivilegedAction; + +import org.apache.log4j.Logger; + +/** + * A SASL provider for Java 1.4. Declares the capabilities of this implementation, which supports PLAIN and CRAM-MD5 + * client implementations only. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Declare PLAIN SASL support. + * <tr><td> Declare CRAM-MD5 SASL support. + * </table> + */ +public class Provider extends java.security.Provider +{ + //Logger log = Logger.getLogger(Provider.class); + + private static final String info = "Qpid SASL provider" + "(implements client mechanisms for: PLAIN, CRAM-MD5)"; + + public Provider() + { + super("QpidSASL", 1.4, info); + + //log.debug("public Provider(): called"); + + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + put("SaslClientFactory.PLAIN", "org.apache.qpid.sasl.ClientFactoryImpl"); + put("SaslClientFactory.CRAM-MD5", "org.apache.qpid.sasl.ClientFactoryImpl"); + + return null; + } + }); + } +} diff --git a/qpid/java/client-java14/src/test/java/org/apache/qpid/test/integration/client/ConnectionTest.java b/qpid/java/client-java14/src/test/java/org/apache/qpid/test/integration/client/ConnectionTest.java index 468beda5b5..77cc79bc49 100644 --- a/qpid/java/client-java14/src/test/java/org/apache/qpid/test/integration/client/ConnectionTest.java +++ b/qpid/java/client-java14/src/test/java/org/apache/qpid/test/integration/client/ConnectionTest.java @@ -1,66 +1,66 @@ -/*
- *
- * 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.integration.client;
-
-import junit.framework.Assert;
-import junit.framework.TestCase;
-
-import org.apache.log4j.Logger;
-import org.apache.log4j.NDC;
-
-import org.apache.qpid.client.AMQConnection;
-
-/**
- * Implements a trivial broker connection test, to a broker on the default port on localhost, to check that SASL
- * authentication works.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Check that a connection to a broker can be established.
- * </table>
- */
-public class ConnectionTest extends TestCase
-{
- private String BROKER_URL = "tcp://localhost:5672";
-
- public ConnectionTest(String name)
- {
- super(name);
- }
-
- /** Check that a connection to a broker can be established. */
- public void testConnection() throws Exception
- {
- // Open a connection to the broker and close it again.
- AMQConnection conn = new AMQConnection(BROKER_URL, "guest", "guest", "clientid", "test");
- conn.close();
- }
-
- protected void setUp()
- {
- NDC.push(getName());
- }
-
- protected void tearDown()
- {
- NDC.pop();
- }
-}
+/* + * + * 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.integration.client; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import org.apache.log4j.Logger; +import org.apache.log4j.NDC; + +import org.apache.qpid.client.AMQConnection; + +/** + * Implements a trivial broker connection test, to a broker on the default port on localhost, to check that SASL + * authentication works. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Check that a connection to a broker can be established. + * </table> + */ +public class ConnectionTest extends TestCase +{ + private String BROKER_URL = "tcp://localhost:5672"; + + public ConnectionTest(String name) + { + super(name); + } + + /** Check that a connection to a broker can be established. */ + public void testConnection() throws Exception + { + // Open a connection to the broker and close it again. + AMQConnection conn = new AMQConnection(BROKER_URL, "guest", "guest", "clientid", "test"); + conn.close(); + } + + protected void setUp() + { + NDC.push(getName()); + } + + protected void tearDown() + { + NDC.pop(); + } +} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSessionAdapter.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSessionAdapter.java index 93f10761e2..7e257e0c20 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSessionAdapter.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSessionAdapter.java @@ -1,26 +1,26 @@ -/*
- *
- * 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.client;
-
-public interface AMQSessionAdapter
-{
- public AMQSession getSession();
-}
+/* + * + * 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.client; + +public interface AMQSessionAdapter +{ + public AMQSession getSession(); +} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.java index 768e2862b3..fa2afb3ee4 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.java @@ -1,40 +1,40 @@ -/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.client;
-
-import org.apache.qpid.framing.AMQShortString;
-
-public class AMQUndefinedDestination extends AMQDestination
-{
-
- private static final AMQShortString UNKNOWN_EXCHANGE_CLASS = new AMQShortString("unknown");
-
-
- public AMQUndefinedDestination(AMQShortString exchange, AMQShortString routingKey, AMQShortString queueName)
- {
- super(exchange, UNKNOWN_EXCHANGE_CLASS, routingKey, queueName);
- }
-
- public boolean isNameRequired()
- {
- return getAMQQueueName() == null;
- }
-}
+/* + * + * 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.client; + +import org.apache.qpid.framing.AMQShortString; + +public class AMQUndefinedDestination extends AMQDestination +{ + + private static final AMQShortString UNKNOWN_EXCHANGE_CLASS = new AMQShortString("unknown"); + + + public AMQUndefinedDestination(AMQShortString exchange, AMQShortString routingKey, AMQShortString queueName) + { + super(exchange, UNKNOWN_EXCHANGE_CLASS, routingKey, queueName); + } + + public boolean isNameRequired() + { + return getAMQQueueName() == null; + } +} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java b/qpid/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java index 8cb285c1ac..7cc548915c 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java @@ -1,66 +1,66 @@ -/*
- *
- * 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.client;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-
-import org.apache.qpid.framing.AMQShortString;
-
-public enum CustomJMSXProperty
-{
- JMS_AMQP_NULL,
- JMS_QPID_DESTTYPE,
- JMSXGroupID,
- JMSXGroupSeq,
- JMSXUserID;
-
-
- private final AMQShortString _nameAsShortString;
-
- CustomJMSXProperty()
- {
- _nameAsShortString = new AMQShortString(toString());
- }
-
- public AMQShortString getShortStringName()
- {
- return _nameAsShortString;
- }
-
- private static Enumeration _names;
-
- public static synchronized Enumeration asEnumeration()
- {
- if(_names == null)
- {
- CustomJMSXProperty[] properties = values();
- ArrayList<String> nameList = new ArrayList<String>(properties.length);
- for(CustomJMSXProperty property : properties)
- {
- nameList.add(property.toString());
- }
- _names = Collections.enumeration(nameList);
- }
- return _names;
- }
-}
+/* + * + * 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.client; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; + +import org.apache.qpid.framing.AMQShortString; + +public enum CustomJMSXProperty +{ + JMS_AMQP_NULL, + JMS_QPID_DESTTYPE, + JMSXGroupID, + JMSXGroupSeq, + JMSXUserID; + + + private final AMQShortString _nameAsShortString; + + CustomJMSXProperty() + { + _nameAsShortString = new AMQShortString(toString()); + } + + public AMQShortString getShortStringName() + { + return _nameAsShortString; + } + + private static Enumeration _names; + + public static synchronized Enumeration asEnumeration() + { + if(_names == null) + { + CustomJMSXProperty[] properties = values(); + ArrayList<String> nameList = new ArrayList<String>(properties.length); + for(CustomJMSXProperty property : properties) + { + nameList.add(property.toString()); + } + _names = Collections.enumeration(nameList); + } + return _names; + } +} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java b/qpid/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java index 03d25aa243..7f8e80c73a 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java @@ -1,38 +1,38 @@ -/*
- * 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.client;
-
-import javax.jms.Destination;
-import javax.jms.JMSException;
-
-/**
- * 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();
-
-}
+/* + * 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.client; + +import javax.jms.Destination; +import javax.jms.JMSException; + +/** + * 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/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java index 1ec98efe0e..51cc94965a 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java @@ -1,75 +1,75 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- *
- */
-
-package org.apache.qpid.client.failover;
-
-import org.apache.qpid.client.AMQConnection;
-
-/**
- * FailoverNoopSupport is a {@link FailoverSupport} implementation that does not really provide any failover support
- * at all. It wraps a {@link FailoverProtectedOperation} but should that operation throw {@link FailoverException} this
- * support class simply re-raises that exception as an IllegalStateException. This support wrapper should only be
- * used where the caller can be certain that the failover protected operation cannot acutally throw a failover exception,
- * for example, because the caller already holds a lock preventing that condition from arising.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Perform a fail-over protected operation raising providing no handling of fail-over conditions.
- * </table>
- */
-public class FailoverNoopSupport<T, E extends Exception> implements FailoverSupport<T, E>
-{
- /** The protected operation that is to be retried in the event of fail-over. */
- FailoverProtectedOperation<T, E> operation;
-
- /** The connection on which the fail-over protected operation is to be performed. */
- AMQConnection connection;
-
- /**
- * Creates an automatic retrying fail-over handler for the specified operation.
- *
- * @param operation The fail-over protected operation to wrap in this handler.
- */
- public FailoverNoopSupport(FailoverProtectedOperation<T, E> operation, AMQConnection con)
- {
- this.operation = operation;
- this.connection = con;
- }
-
- /**
- * Delegates to another continuation which is to be provided with fail-over handling.
- *
- * @return The return value from the delegated to continuation.
- * @throws E Any exception that the delegated to continuation may raise.
- */
- public T execute() throws E
- {
- try
- {
- return operation.execute();
- }
- catch (FailoverException e)
- {
- throw new IllegalStateException("Fail-over interupted no-op failover support. "
- + "No-op support should only be used where the caller is certain fail-over cannot occur.", e);
- }
- }
-}
+/* + * 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.client.failover; + +import org.apache.qpid.client.AMQConnection; + +/** + * FailoverNoopSupport is a {@link FailoverSupport} implementation that does not really provide any failover support + * at all. It wraps a {@link FailoverProtectedOperation} but should that operation throw {@link FailoverException} this + * support class simply re-raises that exception as an IllegalStateException. This support wrapper should only be + * used where the caller can be certain that the failover protected operation cannot acutally throw a failover exception, + * for example, because the caller already holds a lock preventing that condition from arising. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Perform a fail-over protected operation raising providing no handling of fail-over conditions. + * </table> + */ +public class FailoverNoopSupport<T, E extends Exception> implements FailoverSupport<T, E> +{ + /** The protected operation that is to be retried in the event of fail-over. */ + FailoverProtectedOperation<T, E> operation; + + /** The connection on which the fail-over protected operation is to be performed. */ + AMQConnection connection; + + /** + * Creates an automatic retrying fail-over handler for the specified operation. + * + * @param operation The fail-over protected operation to wrap in this handler. + */ + public FailoverNoopSupport(FailoverProtectedOperation<T, E> operation, AMQConnection con) + { + this.operation = operation; + this.connection = con; + } + + /** + * Delegates to another continuation which is to be provided with fail-over handling. + * + * @return The return value from the delegated to continuation. + * @throws E Any exception that the delegated to continuation may raise. + */ + public T execute() throws E + { + try + { + return operation.execute(); + } + catch (FailoverException e) + { + throw new IllegalStateException("Fail-over interupted no-op failover support. " + + "No-op support should only be used where the caller is certain fail-over cannot occur.", e); + } + } +} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java index 9a7f43926e..e9c5f24791 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java @@ -1,49 +1,49 @@ -/*
- * 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.client.failover;
-
-/**
- * FailoverProtectedOperation is a continuation for an operation that may throw a {@link FailoverException} because
- * it has been interrupted by the fail-over process. The {@link FailoverRetrySupport} class defines support wrappers
- * for failover protected operations, in order to provide different handling schemes when failovers occurr.
- *
- * <p/>The type of checked exception that the operation may perform has been generified, in order that fail over
- * protected operations can be defined that raise arbitrary exceptions. The actuall exception types used should not
- * be sub-classes of FailoverException, or else catching FailoverException in the {@link FailoverRetrySupport} classes
- * will mask the exception.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities
- * <tr><td> Perform an operation that may be interrupted by fail-over.
- * </table>
- */
-public interface FailoverProtectedOperation<T, E extends Exception>
-{
- /**
- * Performs the continuations work.
- *
- * @return Provdes scope for the continuation to return an arbitrary value.
- *
- * @throws FailoverException If the operation is interrupted by a fail-over notification.
- */
- public abstract T execute() throws E, FailoverException;
-}
+/* + * 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.client.failover; + +/** + * FailoverProtectedOperation is a continuation for an operation that may throw a {@link FailoverException} because + * it has been interrupted by the fail-over process. The {@link FailoverRetrySupport} class defines support wrappers + * for failover protected operations, in order to provide different handling schemes when failovers occurr. + * + * <p/>The type of checked exception that the operation may perform has been generified, in order that fail over + * protected operations can be defined that raise arbitrary exceptions. The actuall exception types used should not + * be sub-classes of FailoverException, or else catching FailoverException in the {@link FailoverRetrySupport} classes + * will mask the exception. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities + * <tr><td> Perform an operation that may be interrupted by fail-over. + * </table> + */ +public interface FailoverProtectedOperation<T, E extends Exception> +{ + /** + * Performs the continuations work. + * + * @return Provdes scope for the continuation to return an arbitrary value. + * + * @throws FailoverException If the operation is interrupted by a fail-over notification. + */ + public abstract T execute() throws E, FailoverException; +} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java index e756d7baf9..cf7e978c03 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java @@ -1,135 +1,135 @@ -/*
- *
- * 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.client.failover;
-
-import org.apache.qpid.client.AMQConnection;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * FailoverRetrySupport is a continuation that wraps another continuation, delaying its execution until it is notified
- * that a blocking condition has been met, and executing the continuation within a mutex. If the continuation fails, due
- * to the original condition being broken, whilst the continuation is waiting for a reponse to a synchronous request,
- * FailoverRetrySupport automatcally rechecks the condition and re-acquires the mutex and re-runs the continution. This
- * automatic retrying is continued until the continuation succeeds, or throws an exception (different to
- * FailoverException, which is used to signal the failure of the original condition).
- *
- * <p/>The blocking condition used is that the connection is not currently failing over, and the mutex used is the
- * connection failover mutex, which guards against the fail-over process being run during fail-over vulnerable methods.
- * These are used like a lock and condition variable.
- *
- * <p/>The wrapped operation may throw a FailoverException, this is an exception that can be raised by a
- * {@link org.apache.qpid.client.protocol.BlockingMethodFrameListener}, in response to it being notified that a
- * fail-over wants to start whilst it was waiting. Methods that are vulnerable to fail-over are those that are
- * synchronous, where a failure will prevent them from getting the reply they are waiting for and asynchronous
- * methods that should not be attempted when a fail-over is in progress.
- *
- * <p/>Wrapping a synchronous method in a FailoverRetrySupport will have the effect that the operation will not be
- * started during fail-over, but be delayed until any current fail-over has completed. Should a fail-over process want
- * to start whilst waiting for the synchrnous reply, the FailoverRetrySupport will detect this and rety the operation
- * until it succeeds. Synchronous methods are usually coordinated with a
- * {@link org.apache.qpid.client.protocol.BlockingMethodFrameListener} which is notified when a fail-over process wants
- * to start and throws a FailoverException in response to this.
- *
- * <p/>Wrapping an asynchronous method in a FailoverRetrySupport will have the effect that the operation will not be
- * started during fail-over, but be delayed until any current fail-over has completed.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Provide a continuation synchronized on a fail-over lock and condition.
- * <tr><td> Automatically retry the continuation accross fail-overs until it succeeds, or raises an exception.
- * </table>
- *
- * @todo Another continuation. Could use an interface Continuation (as described in other todos, for example, see
- * {@link org.apache.qpid.pool.Job}). Then have a wrapping continuation (this), which blocks on an arbitrary
- * Condition or Latch (specified in constructor call), that this blocks on before calling the wrapped Continuation.
- * Must work on Java 1.4, so check retrotranslator works on Lock/Condition or latch first. Argument and return type
- * to match wrapped condition as type parameters. Rename to AsyncConditionalContinuation or something like that.
- *
- * @todo InterruptedException not handled well.
- */
-public class FailoverRetrySupport<T, E extends Exception> implements FailoverSupport<T, E>
-{
- /** Used for debugging. */
- private static final Logger _log = LoggerFactory.getLogger(FailoverRetrySupport.class);
-
- /** The protected operation that is to be retried in the event of fail-over. */
- FailoverProtectedOperation<T, E> operation;
-
- /** The connection on which the fail-over protected operation is to be performed. */
- AMQConnection connection;
-
- /**
- * Creates an automatic retrying fail-over handler for the specified operation.
- *
- * @param operation The fail-over protected operation to wrap in this handler.
- */
- public FailoverRetrySupport(FailoverProtectedOperation<T, E> operation, AMQConnection con)
- {
- this.operation = operation;
- this.connection = con;
- }
-
- /**
- * Delays a continuation until the "not failing over" condition is met on the specified connection. Repeats
- * until the operation throws AMQException or succeeds without being interrupted by fail-over.
- *
- * @return The result of executing the continuation.
- *
- * @throws E Any underlying exception is allowed to fall through.
- */
- public T execute() throws E
- {
- while (true)
- {
- try
- {
- connection.blockUntilNotFailingOver();
- }
- catch (InterruptedException e)
- {
- _log.debug("Interrupted: " + e, e);
-
- return null;
- }
-
- synchronized (connection.getFailoverMutex())
- {
- try
- {
- return operation.execute();
- }
- catch (FailoverException e)
- {
- _log.debug("Failover exception caught during operation: " + e, e);
- }
- catch (IllegalStateException e)
- {
- if (!(e.getMessage().startsWith("Fail-over interupted no-op failover support")))
- {
- throw e;
- }
- }
- }
- }
- }
-}
+/* + * + * 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.client.failover; + +import org.apache.qpid.client.AMQConnection; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * FailoverRetrySupport is a continuation that wraps another continuation, delaying its execution until it is notified + * that a blocking condition has been met, and executing the continuation within a mutex. If the continuation fails, due + * to the original condition being broken, whilst the continuation is waiting for a reponse to a synchronous request, + * FailoverRetrySupport automatcally rechecks the condition and re-acquires the mutex and re-runs the continution. This + * automatic retrying is continued until the continuation succeeds, or throws an exception (different to + * FailoverException, which is used to signal the failure of the original condition). + * + * <p/>The blocking condition used is that the connection is not currently failing over, and the mutex used is the + * connection failover mutex, which guards against the fail-over process being run during fail-over vulnerable methods. + * These are used like a lock and condition variable. + * + * <p/>The wrapped operation may throw a FailoverException, this is an exception that can be raised by a + * {@link org.apache.qpid.client.protocol.BlockingMethodFrameListener}, in response to it being notified that a + * fail-over wants to start whilst it was waiting. Methods that are vulnerable to fail-over are those that are + * synchronous, where a failure will prevent them from getting the reply they are waiting for and asynchronous + * methods that should not be attempted when a fail-over is in progress. + * + * <p/>Wrapping a synchronous method in a FailoverRetrySupport will have the effect that the operation will not be + * started during fail-over, but be delayed until any current fail-over has completed. Should a fail-over process want + * to start whilst waiting for the synchrnous reply, the FailoverRetrySupport will detect this and rety the operation + * until it succeeds. Synchronous methods are usually coordinated with a + * {@link org.apache.qpid.client.protocol.BlockingMethodFrameListener} which is notified when a fail-over process wants + * to start and throws a FailoverException in response to this. + * + * <p/>Wrapping an asynchronous method in a FailoverRetrySupport will have the effect that the operation will not be + * started during fail-over, but be delayed until any current fail-over has completed. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Provide a continuation synchronized on a fail-over lock and condition. + * <tr><td> Automatically retry the continuation accross fail-overs until it succeeds, or raises an exception. + * </table> + * + * @todo Another continuation. Could use an interface Continuation (as described in other todos, for example, see + * {@link org.apache.qpid.pool.Job}). Then have a wrapping continuation (this), which blocks on an arbitrary + * Condition or Latch (specified in constructor call), that this blocks on before calling the wrapped Continuation. + * Must work on Java 1.4, so check retrotranslator works on Lock/Condition or latch first. Argument and return type + * to match wrapped condition as type parameters. Rename to AsyncConditionalContinuation or something like that. + * + * @todo InterruptedException not handled well. + */ +public class FailoverRetrySupport<T, E extends Exception> implements FailoverSupport<T, E> +{ + /** Used for debugging. */ + private static final Logger _log = LoggerFactory.getLogger(FailoverRetrySupport.class); + + /** The protected operation that is to be retried in the event of fail-over. */ + FailoverProtectedOperation<T, E> operation; + + /** The connection on which the fail-over protected operation is to be performed. */ + AMQConnection connection; + + /** + * Creates an automatic retrying fail-over handler for the specified operation. + * + * @param operation The fail-over protected operation to wrap in this handler. + */ + public FailoverRetrySupport(FailoverProtectedOperation<T, E> operation, AMQConnection con) + { + this.operation = operation; + this.connection = con; + } + + /** + * Delays a continuation until the "not failing over" condition is met on the specified connection. Repeats + * until the operation throws AMQException or succeeds without being interrupted by fail-over. + * + * @return The result of executing the continuation. + * + * @throws E Any underlying exception is allowed to fall through. + */ + public T execute() throws E + { + while (true) + { + try + { + connection.blockUntilNotFailingOver(); + } + catch (InterruptedException e) + { + _log.debug("Interrupted: " + e, e); + + return null; + } + + synchronized (connection.getFailoverMutex()) + { + try + { + return operation.execute(); + } + catch (FailoverException e) + { + _log.debug("Failover exception caught during operation: " + e, e); + } + catch (IllegalStateException e) + { + if (!(e.getMessage().startsWith("Fail-over interupted no-op failover support"))) + { + throw e; + } + } + } + } + } +} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/AccessRequestOkMethodHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/AccessRequestOkMethodHandler.java index a150d1446a..5bd36aa88b 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/AccessRequestOkMethodHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/AccessRequestOkMethodHandler.java @@ -1,36 +1,36 @@ -package org.apache.qpid.client.handler;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.qpid.framing.*;
-import org.apache.qpid.client.state.StateAwareMethodListener;
-import org.apache.qpid.client.state.AMQStateManager;
-import org.apache.qpid.client.protocol.AMQProtocolSession;
-import org.apache.qpid.client.AMQNoConsumersException;
-import org.apache.qpid.client.AMQNoRouteException;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.AMQInvalidRoutingKeyException;
-import org.apache.qpid.AMQChannelClosedException;
-import org.apache.qpid.protocol.AMQConstant;
-
-public class AccessRequestOkMethodHandler implements StateAwareMethodListener<AccessRequestOkBody>
-{
- private static final Logger _logger = LoggerFactory.getLogger(AccessRequestOkMethodHandler.class);
-
- private static AccessRequestOkMethodHandler _handler = new AccessRequestOkMethodHandler();
-
- public static AccessRequestOkMethodHandler getInstance()
- {
- return _handler;
- }
-
- public void methodReceived(AMQStateManager stateManager, AccessRequestOkBody method, int channelId)
- throws AMQException
- {
- _logger.debug("AccessRequestOk method received");
- final AMQProtocolSession session = stateManager.getProtocolSession();
- session.setTicket(method.getTicket(), channelId);
-
- }
-}
+package org.apache.qpid.client.handler; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.qpid.framing.*; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.AMQNoConsumersException; +import org.apache.qpid.client.AMQNoRouteException; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQInvalidRoutingKeyException; +import org.apache.qpid.AMQChannelClosedException; +import org.apache.qpid.protocol.AMQConstant; + +public class AccessRequestOkMethodHandler implements StateAwareMethodListener<AccessRequestOkBody> +{ + private static final Logger _logger = LoggerFactory.getLogger(AccessRequestOkMethodHandler.class); + + private static AccessRequestOkMethodHandler _handler = new AccessRequestOkMethodHandler(); + + public static AccessRequestOkMethodHandler getInstance() + { + return _handler; + } + + public void methodReceived(AMQStateManager stateManager, AccessRequestOkBody method, int channelId) + throws AMQException + { + _logger.debug("AccessRequestOk method received"); + final AMQProtocolSession session = stateManager.getProtocolSession(); + session.setTicket(method.getTicket(), channelId); + + } +} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl.java index de976b05bd..afb7517a12 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl.java @@ -1,528 +1,528 @@ -/*
- *
- * 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.client.handler;
-
-import java.util.Map;
-import java.util.HashMap;
-
-import org.apache.qpid.framing.*;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.client.state.AMQStateManager;
-import org.apache.qpid.client.state.AMQMethodNotImplementedException;
-
-
-public class ClientMethodDispatcherImpl implements MethodDispatcher
-{
-
-
- private static final BasicCancelOkMethodHandler _basicCancelOkMethodHandler = BasicCancelOkMethodHandler.getInstance();
- private static final BasicDeliverMethodHandler _basicDeliverMethodHandler = BasicDeliverMethodHandler.getInstance();
- private static final BasicReturnMethodHandler _basicReturnMethodHandler = BasicReturnMethodHandler.getInstance();
- private static final ChannelCloseMethodHandler _channelCloseMethodHandler = ChannelCloseMethodHandler.getInstance();
- private static final ChannelFlowOkMethodHandler _channelFlowOkMethodHandler = ChannelFlowOkMethodHandler.getInstance();
- private static final ConnectionCloseMethodHandler _connectionCloseMethodHandler = ConnectionCloseMethodHandler.getInstance();
- private static final ConnectionOpenOkMethodHandler _connectionOpenOkMethodHandler = ConnectionOpenOkMethodHandler.getInstance();
- private static final ConnectionRedirectMethodHandler _connectionRedirectMethodHandler = ConnectionRedirectMethodHandler.getInstance();
- private static final ConnectionSecureMethodHandler _connectionSecureMethodHandler = ConnectionSecureMethodHandler.getInstance();
- private static final ConnectionStartMethodHandler _connectionStartMethodHandler = ConnectionStartMethodHandler.getInstance();
- private static final ConnectionTuneMethodHandler _connectionTuneMethodHandler = ConnectionTuneMethodHandler.getInstance();
- private static final ExchangeBoundOkMethodHandler _exchangeBoundOkMethodHandler = ExchangeBoundOkMethodHandler.getInstance();
- private static final QueueDeleteOkMethodHandler _queueDeleteOkMethodHandler = QueueDeleteOkMethodHandler.getInstance();
-
-
-
- private static interface DispatcherFactory
- {
- public ClientMethodDispatcherImpl createMethodDispatcher(AMQStateManager stateManager);
- }
-
- private static final Map<ProtocolVersion, DispatcherFactory> _dispatcherFactories =
- new HashMap<ProtocolVersion, DispatcherFactory>();
-
- static
- {
- _dispatcherFactories.put(ProtocolVersion.v8_0,
- new DispatcherFactory()
- {
- public ClientMethodDispatcherImpl createMethodDispatcher(AMQStateManager stateManager)
- {
- return new ClientMethodDispatcherImpl_8_0(stateManager);
- }
- });
-
- _dispatcherFactories.put(ProtocolVersion.v0_9,
- new DispatcherFactory()
- {
- public ClientMethodDispatcherImpl createMethodDispatcher(AMQStateManager stateManager)
- {
- return new ClientMethodDispatcherImpl_0_9(stateManager);
- }
- });
-
- }
-
-
- public static ClientMethodDispatcherImpl newMethodDispatcher(ProtocolVersion version, AMQStateManager stateManager)
- {
- DispatcherFactory factory = _dispatcherFactories.get(version);
- return factory.createMethodDispatcher(stateManager);
- }
-
-
-
-
- private AMQStateManager _stateManager;
-
- public ClientMethodDispatcherImpl(AMQStateManager stateManager)
- {
- _stateManager = stateManager;
- }
-
-
- public AMQStateManager getStateManager()
- {
- return _stateManager;
- }
-
- public boolean dispatchAccessRequestOk(AccessRequestOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchBasicCancelOk(BasicCancelOkBody body, int channelId) throws AMQException
- {
- _basicCancelOkMethodHandler.methodReceived(_stateManager,body,channelId);
- return true;
- }
-
- public boolean dispatchBasicConsumeOk(BasicConsumeOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchBasicDeliver(BasicDeliverBody body, int channelId) throws AMQException
- {
- _basicDeliverMethodHandler.methodReceived(_stateManager,body,channelId);
- return true;
- }
-
- public boolean dispatchBasicGetEmpty(BasicGetEmptyBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchBasicGetOk(BasicGetOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchBasicQosOk(BasicQosOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchBasicReturn(BasicReturnBody body, int channelId) throws AMQException
- {
- _basicReturnMethodHandler.methodReceived(_stateManager,body,channelId);
- return true;
- }
-
- public boolean dispatchChannelClose(ChannelCloseBody body, int channelId) throws AMQException
- {
- _channelCloseMethodHandler.methodReceived(_stateManager,body,channelId);
- return true;
- }
-
- public boolean dispatchChannelCloseOk(ChannelCloseOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchChannelFlow(ChannelFlowBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchChannelFlowOk(ChannelFlowOkBody body, int channelId) throws AMQException
- {
- _channelFlowOkMethodHandler.methodReceived(_stateManager,body,channelId);
- return true;
- }
-
- public boolean dispatchChannelOpenOk(ChannelOpenOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchConnectionClose(ConnectionCloseBody body, int channelId) throws AMQException
- {
- _connectionCloseMethodHandler.methodReceived(_stateManager,body,channelId);
- return true;
- }
-
- public boolean dispatchConnectionCloseOk(ConnectionCloseOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchConnectionOpenOk(ConnectionOpenOkBody body, int channelId) throws AMQException
- {
- _connectionOpenOkMethodHandler.methodReceived(_stateManager,body,channelId);
- return true;
- }
-
- public boolean dispatchConnectionRedirect(ConnectionRedirectBody body, int channelId) throws AMQException
- {
- _connectionRedirectMethodHandler.methodReceived(_stateManager,body,channelId);
- return true;
- }
-
- public boolean dispatchConnectionSecure(ConnectionSecureBody body, int channelId) throws AMQException
- {
- _connectionSecureMethodHandler.methodReceived(_stateManager,body,channelId);
- return true;
- }
-
- public boolean dispatchConnectionStart(ConnectionStartBody body, int channelId) throws AMQException
- {
- _connectionStartMethodHandler.methodReceived(_stateManager,body,channelId);
- return true;
- }
-
- public boolean dispatchConnectionTune(ConnectionTuneBody body, int channelId) throws AMQException
- {
- _connectionTuneMethodHandler.methodReceived(_stateManager,body,channelId);
- return true;
- }
-
- public boolean dispatchQueueDeleteOk(QueueDeleteOkBody body, int channelId) throws AMQException
- {
- _queueDeleteOkMethodHandler.methodReceived(_stateManager,body,channelId);
- return true;
- }
-
- public boolean dispatchQueuePurgeOk(QueuePurgeOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchStreamCancelOk(StreamCancelOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchStreamConsumeOk(StreamConsumeOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchAccessRequest(AccessRequestBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchBasicAck(BasicAckBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchBasicCancel(BasicCancelBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchBasicConsume(BasicConsumeBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchBasicGet(BasicGetBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchBasicPublish(BasicPublishBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchBasicQos(BasicQosBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchBasicRecover(BasicRecoverBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchBasicReject(BasicRejectBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchChannelOpen(ChannelOpenBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchConnectionOpen(ConnectionOpenBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchConnectionSecureOk(ConnectionSecureOkBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchConnectionStartOk(ConnectionStartOkBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchConnectionTuneOk(ConnectionTuneOkBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchDtxSelect(DtxSelectBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchDtxStart(DtxStartBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchExchangeBound(ExchangeBoundBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchExchangeDeclare(ExchangeDeclareBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchExchangeDelete(ExchangeDeleteBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchFileAck(FileAckBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchFileCancel(FileCancelBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchFileConsume(FileConsumeBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchFilePublish(FilePublishBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchFileQos(FileQosBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchFileReject(FileRejectBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchQueueBind(QueueBindBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchQueueDeclare(QueueDeclareBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchQueueDelete(QueueDeleteBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchQueuePurge(QueuePurgeBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchStreamCancel(StreamCancelBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchStreamConsume(StreamConsumeBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchStreamPublish(StreamPublishBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchStreamQos(StreamQosBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchTunnelRequest(TunnelRequestBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchTxCommit(TxCommitBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchTxRollback(TxRollbackBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchTxSelect(TxSelectBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchDtxSelectOk(DtxSelectOkBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchDtxStartOk(DtxStartOkBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchExchangeBoundOk(ExchangeBoundOkBody body, int channelId) throws AMQException
- {
- _exchangeBoundOkMethodHandler.methodReceived(_stateManager,body,channelId);
- return true;
- }
-
- public boolean dispatchExchangeDeclareOk(ExchangeDeclareOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchExchangeDeleteOk(ExchangeDeleteOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchFileCancelOk(FileCancelOkBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchFileConsumeOk(FileConsumeOkBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchFileDeliver(FileDeliverBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchFileOpen(FileOpenBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchFileOpenOk(FileOpenOkBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchFileQosOk(FileQosOkBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchFileReturn(FileReturnBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchFileStage(FileStageBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchQueueBindOk(QueueBindOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchQueueDeclareOk(QueueDeclareOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchStreamDeliver(StreamDeliverBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchStreamQosOk(StreamQosOkBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchStreamReturn(StreamReturnBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchTxCommitOk(TxCommitOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchTxRollbackOk(TxRollbackOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchTxSelectOk(TxSelectOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
-}
+/* + * + * 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.client.handler; + +import java.util.Map; +import java.util.HashMap; + +import org.apache.qpid.framing.*; +import org.apache.qpid.AMQException; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.AMQMethodNotImplementedException; + + +public class ClientMethodDispatcherImpl implements MethodDispatcher +{ + + + private static final BasicCancelOkMethodHandler _basicCancelOkMethodHandler = BasicCancelOkMethodHandler.getInstance(); + private static final BasicDeliverMethodHandler _basicDeliverMethodHandler = BasicDeliverMethodHandler.getInstance(); + private static final BasicReturnMethodHandler _basicReturnMethodHandler = BasicReturnMethodHandler.getInstance(); + private static final ChannelCloseMethodHandler _channelCloseMethodHandler = ChannelCloseMethodHandler.getInstance(); + private static final ChannelFlowOkMethodHandler _channelFlowOkMethodHandler = ChannelFlowOkMethodHandler.getInstance(); + private static final ConnectionCloseMethodHandler _connectionCloseMethodHandler = ConnectionCloseMethodHandler.getInstance(); + private static final ConnectionOpenOkMethodHandler _connectionOpenOkMethodHandler = ConnectionOpenOkMethodHandler.getInstance(); + private static final ConnectionRedirectMethodHandler _connectionRedirectMethodHandler = ConnectionRedirectMethodHandler.getInstance(); + private static final ConnectionSecureMethodHandler _connectionSecureMethodHandler = ConnectionSecureMethodHandler.getInstance(); + private static final ConnectionStartMethodHandler _connectionStartMethodHandler = ConnectionStartMethodHandler.getInstance(); + private static final ConnectionTuneMethodHandler _connectionTuneMethodHandler = ConnectionTuneMethodHandler.getInstance(); + private static final ExchangeBoundOkMethodHandler _exchangeBoundOkMethodHandler = ExchangeBoundOkMethodHandler.getInstance(); + private static final QueueDeleteOkMethodHandler _queueDeleteOkMethodHandler = QueueDeleteOkMethodHandler.getInstance(); + + + + private static interface DispatcherFactory + { + public ClientMethodDispatcherImpl createMethodDispatcher(AMQStateManager stateManager); + } + + private static final Map<ProtocolVersion, DispatcherFactory> _dispatcherFactories = + new HashMap<ProtocolVersion, DispatcherFactory>(); + + static + { + _dispatcherFactories.put(ProtocolVersion.v8_0, + new DispatcherFactory() + { + public ClientMethodDispatcherImpl createMethodDispatcher(AMQStateManager stateManager) + { + return new ClientMethodDispatcherImpl_8_0(stateManager); + } + }); + + _dispatcherFactories.put(ProtocolVersion.v0_9, + new DispatcherFactory() + { + public ClientMethodDispatcherImpl createMethodDispatcher(AMQStateManager stateManager) + { + return new ClientMethodDispatcherImpl_0_9(stateManager); + } + }); + + } + + + public static ClientMethodDispatcherImpl newMethodDispatcher(ProtocolVersion version, AMQStateManager stateManager) + { + DispatcherFactory factory = _dispatcherFactories.get(version); + return factory.createMethodDispatcher(stateManager); + } + + + + + private AMQStateManager _stateManager; + + public ClientMethodDispatcherImpl(AMQStateManager stateManager) + { + _stateManager = stateManager; + } + + + public AMQStateManager getStateManager() + { + return _stateManager; + } + + public boolean dispatchAccessRequestOk(AccessRequestOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchBasicCancelOk(BasicCancelOkBody body, int channelId) throws AMQException + { + _basicCancelOkMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchBasicConsumeOk(BasicConsumeOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchBasicDeliver(BasicDeliverBody body, int channelId) throws AMQException + { + _basicDeliverMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchBasicGetEmpty(BasicGetEmptyBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchBasicGetOk(BasicGetOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchBasicQosOk(BasicQosOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchBasicReturn(BasicReturnBody body, int channelId) throws AMQException + { + _basicReturnMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchChannelClose(ChannelCloseBody body, int channelId) throws AMQException + { + _channelCloseMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchChannelCloseOk(ChannelCloseOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelFlow(ChannelFlowBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelFlowOk(ChannelFlowOkBody body, int channelId) throws AMQException + { + _channelFlowOkMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchChannelOpenOk(ChannelOpenOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchConnectionClose(ConnectionCloseBody body, int channelId) throws AMQException + { + _connectionCloseMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchConnectionCloseOk(ConnectionCloseOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchConnectionOpenOk(ConnectionOpenOkBody body, int channelId) throws AMQException + { + _connectionOpenOkMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchConnectionRedirect(ConnectionRedirectBody body, int channelId) throws AMQException + { + _connectionRedirectMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchConnectionSecure(ConnectionSecureBody body, int channelId) throws AMQException + { + _connectionSecureMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchConnectionStart(ConnectionStartBody body, int channelId) throws AMQException + { + _connectionStartMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchConnectionTune(ConnectionTuneBody body, int channelId) throws AMQException + { + _connectionTuneMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchQueueDeleteOk(QueueDeleteOkBody body, int channelId) throws AMQException + { + _queueDeleteOkMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchQueuePurgeOk(QueuePurgeOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchStreamCancelOk(StreamCancelOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchStreamConsumeOk(StreamConsumeOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchAccessRequest(AccessRequestBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicAck(BasicAckBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicCancel(BasicCancelBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicConsume(BasicConsumeBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicGet(BasicGetBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicPublish(BasicPublishBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicQos(BasicQosBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicRecover(BasicRecoverBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicReject(BasicRejectBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchChannelOpen(ChannelOpenBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchConnectionOpen(ConnectionOpenBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchConnectionSecureOk(ConnectionSecureOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchConnectionStartOk(ConnectionStartOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchConnectionTuneOk(ConnectionTuneOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchDtxSelect(DtxSelectBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchDtxStart(DtxStartBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchExchangeBound(ExchangeBoundBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchExchangeDeclare(ExchangeDeclareBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchExchangeDelete(ExchangeDeleteBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileAck(FileAckBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileCancel(FileCancelBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileConsume(FileConsumeBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFilePublish(FilePublishBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileQos(FileQosBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileReject(FileRejectBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchQueueBind(QueueBindBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchQueueDeclare(QueueDeclareBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchQueueDelete(QueueDeleteBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchQueuePurge(QueuePurgeBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchStreamCancel(StreamCancelBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchStreamConsume(StreamConsumeBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchStreamPublish(StreamPublishBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchStreamQos(StreamQosBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchTunnelRequest(TunnelRequestBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchTxCommit(TxCommitBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchTxRollback(TxRollbackBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchTxSelect(TxSelectBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchDtxSelectOk(DtxSelectOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchDtxStartOk(DtxStartOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchExchangeBoundOk(ExchangeBoundOkBody body, int channelId) throws AMQException + { + _exchangeBoundOkMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchExchangeDeclareOk(ExchangeDeclareOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchExchangeDeleteOk(ExchangeDeleteOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchFileCancelOk(FileCancelOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileConsumeOk(FileConsumeOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileDeliver(FileDeliverBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileOpen(FileOpenBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileOpenOk(FileOpenOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileQosOk(FileQosOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileReturn(FileReturnBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileStage(FileStageBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchQueueBindOk(QueueBindOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchQueueDeclareOk(QueueDeclareOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchStreamDeliver(StreamDeliverBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchStreamQosOk(StreamQosOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchStreamReturn(StreamReturnBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchTxCommitOk(TxCommitOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTxRollbackOk(TxRollbackOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTxSelectOk(TxSelectOkBody body, int channelId) throws AMQException + { + return false; + } + +} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_9.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_9.java index ae6d5e8283..e235368357 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_9.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_9.java @@ -1,155 +1,155 @@ -/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.client.handler;
-
-import org.apache.qpid.framing.*;
-import org.apache.qpid.framing.amqp_0_9.MethodDispatcher_0_9;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.client.state.AMQStateManager;
-import org.apache.qpid.client.state.AMQMethodNotImplementedException;
-
-
-public class ClientMethodDispatcherImpl_0_9 extends ClientMethodDispatcherImpl implements MethodDispatcher_0_9
-{
- public ClientMethodDispatcherImpl_0_9(AMQStateManager stateManager)
- {
- super(stateManager);
- }
-
-
- public boolean dispatchBasicRecoverSyncOk(BasicRecoverSyncOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchBasicRecoverSync(BasicRecoverSyncBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchChannelOk(ChannelOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchChannelPing(ChannelPingBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchChannelPong(ChannelPongBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchChannelResume(ChannelResumeBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchMessageAppend(MessageAppendBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageCancel(MessageCancelBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchMessageCheckpoint(MessageCheckpointBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageClose(MessageCloseBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageConsume(MessageConsumeBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchMessageEmpty(MessageEmptyBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageGet(MessageGetBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchMessageOffset(MessageOffsetBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageOk(MessageOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageOpen(MessageOpenBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageQos(MessageQosBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchMessageRecover(MessageRecoverBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchMessageReject(MessageRejectBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageResume(MessageResumeBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchMessageTransfer(MessageTransferBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchQueueUnbind(QueueUnbindBody body, int channelId) throws AMQException
- {
- throw new AMQMethodNotImplementedException(body);
- }
-
- public boolean dispatchQueueUnbindOk(QueueUnbindOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
-
-}
+/* + * + * 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.client.handler; + +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.amqp_0_9.MethodDispatcher_0_9; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.AMQMethodNotImplementedException; + + +public class ClientMethodDispatcherImpl_0_9 extends ClientMethodDispatcherImpl implements MethodDispatcher_0_9 +{ + public ClientMethodDispatcherImpl_0_9(AMQStateManager stateManager) + { + super(stateManager); + } + + + public boolean dispatchBasicRecoverSyncOk(BasicRecoverSyncOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchBasicRecoverSync(BasicRecoverSyncBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchChannelOk(ChannelOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelPing(ChannelPingBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelPong(ChannelPongBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelResume(ChannelResumeBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchMessageAppend(MessageAppendBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageCancel(MessageCancelBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchMessageCheckpoint(MessageCheckpointBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageClose(MessageCloseBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageConsume(MessageConsumeBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchMessageEmpty(MessageEmptyBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageGet(MessageGetBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchMessageOffset(MessageOffsetBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageOk(MessageOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageOpen(MessageOpenBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageQos(MessageQosBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchMessageRecover(MessageRecoverBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchMessageReject(MessageRejectBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageResume(MessageResumeBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageTransfer(MessageTransferBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchQueueUnbind(QueueUnbindBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchQueueUnbindOk(QueueUnbindOkBody body, int channelId) throws AMQException + { + return false; + } + + +} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_8_0.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_8_0.java index 6bd6874cde..b0f003cd2d 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_8_0.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_8_0.java @@ -1,85 +1,85 @@ -/*
- *
- * 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.client.handler;
-
-import org.apache.qpid.framing.*;
-import org.apache.qpid.framing.amqp_8_0.MethodDispatcher_8_0;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.client.state.AMQStateManager;
-
-public class ClientMethodDispatcherImpl_8_0 extends ClientMethodDispatcherImpl implements MethodDispatcher_8_0
-{
- public ClientMethodDispatcherImpl_8_0(AMQStateManager stateManager)
- {
- super(stateManager);
- }
-
- public boolean dispatchBasicRecoverOk(BasicRecoverOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchChannelAlert(ChannelAlertBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchTestContent(TestContentBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchTestContentOk(TestContentOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchTestInteger(TestIntegerBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchTestIntegerOk(TestIntegerOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchTestString(TestStringBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchTestStringOk(TestStringOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchTestTable(TestTableBody body, int channelId) throws AMQException
- {
- return false;
- }
-
- public boolean dispatchTestTableOk(TestTableOkBody body, int channelId) throws AMQException
- {
- return false;
- }
-}
+/* + * + * 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.client.handler; + +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.amqp_8_0.MethodDispatcher_8_0; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.state.AMQStateManager; + +public class ClientMethodDispatcherImpl_8_0 extends ClientMethodDispatcherImpl implements MethodDispatcher_8_0 +{ + public ClientMethodDispatcherImpl_8_0(AMQStateManager stateManager) + { + super(stateManager); + } + + public boolean dispatchBasicRecoverOk(BasicRecoverOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelAlert(ChannelAlertBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestContent(TestContentBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestContentOk(TestContentOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestInteger(TestIntegerBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestIntegerOk(TestIntegerOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestString(TestStringBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestStringOk(TestStringOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestTable(TestTableBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestTableOk(TestTableOkBody body, int channelId) throws AMQException + { + return false; + } +} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java b/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java index 5904131122..9bdde01bf3 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java @@ -1,802 +1,802 @@ -/*
- * 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.client.message;
-
-import java.nio.charset.CharacterCodingException;
-import java.nio.charset.Charset;
-
-import javax.jms.JMSException;
-import javax.jms.MessageEOFException;
-import javax.jms.MessageFormatException;
-import javax.jms.MessageNotReadableException;
-import javax.jms.MessageNotWriteableException;
-
-import org.apache.mina.common.ByteBuffer;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.ContentHeaderBody;
-import org.apache.qpid.framing.BasicContentHeaderProperties;
-
-/**
- * @author Apache Software Foundation
- */
-public abstract class AbstractBytesTypedMessage extends AbstractBytesMessage
-{
-
- protected static final byte BOOLEAN_TYPE = (byte) 1;
-
- protected static final byte BYTE_TYPE = (byte) 2;
-
- protected static final byte BYTEARRAY_TYPE = (byte) 3;
-
- protected static final byte SHORT_TYPE = (byte) 4;
-
- protected static final byte CHAR_TYPE = (byte) 5;
-
- protected static final byte INT_TYPE = (byte) 6;
-
- protected static final byte LONG_TYPE = (byte) 7;
-
- protected static final byte FLOAT_TYPE = (byte) 8;
-
- protected static final byte DOUBLE_TYPE = (byte) 9;
-
- protected static final byte STRING_TYPE = (byte) 10;
-
- protected static final byte NULL_STRING_TYPE = (byte) 11;
-
- /**
- * This is set when reading a byte array. The readBytes(byte[]) method supports multiple calls to read
- * a byte array in multiple chunks, hence this is used to track how much is left to be read
- */
- private int _byteArrayRemaining = -1;
-
- AbstractBytesTypedMessage()
- {
- this(null);
- }
-
- /**
- * Construct a stream message with existing data.
- *
- * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is
- * set to auto expand
- */
- AbstractBytesTypedMessage(ByteBuffer data)
- {
- super(data); // this instanties a content header
- }
-
-
- AbstractBytesTypedMessage(long messageNbr, BasicContentHeaderProperties contentHeader, AMQShortString exchange,
- AMQShortString routingKey, ByteBuffer data) throws AMQException
- {
- super(messageNbr, contentHeader, exchange, routingKey, data);
- }
-
-
- protected byte readWireType() throws MessageFormatException, MessageEOFException,
- MessageNotReadableException
- {
- checkReadable();
- checkAvailable(1);
- return _data.get();
- }
-
- protected void writeTypeDiscriminator(byte type) throws MessageNotWriteableException
- {
- checkWritable();
- _data.put(type);
- _changedData = true;
- }
-
- protected boolean readBoolean() throws JMSException
- {
- int position = _data.position();
- byte wireType = readWireType();
- boolean result;
- try
- {
- switch (wireType)
- {
- case BOOLEAN_TYPE:
- checkAvailable(1);
- result = readBooleanImpl();
- break;
- case STRING_TYPE:
- checkAvailable(1);
- result = Boolean.parseBoolean(readStringImpl());
- break;
- default:
- _data.position(position);
- throw new MessageFormatException("Unable to convert " + wireType + " to a boolean");
- }
- return result;
- }
- catch (RuntimeException e)
- {
- _data.position(position);
- throw e;
- }
- }
-
- private boolean readBooleanImpl()
- {
- return _data.get() != 0;
- }
-
- protected byte readByte() throws JMSException
- {
- int position = _data.position();
- byte wireType = readWireType();
- byte result;
- try
- {
- switch (wireType)
- {
- case BYTE_TYPE:
- checkAvailable(1);
- result = readByteImpl();
- break;
- case STRING_TYPE:
- checkAvailable(1);
- result = Byte.parseByte(readStringImpl());
- break;
- default:
- _data.position(position);
- throw new MessageFormatException("Unable to convert " + wireType + " to a byte");
- }
- }
- catch (RuntimeException e)
- {
- _data.position(position);
- throw e;
- }
- return result;
- }
-
- private byte readByteImpl()
- {
- return _data.get();
- }
-
- protected short readShort() throws JMSException
- {
- int position = _data.position();
- byte wireType = readWireType();
- short result;
- try
- {
- switch (wireType)
- {
- case SHORT_TYPE:
- checkAvailable(2);
- result = readShortImpl();
- break;
- case STRING_TYPE:
- checkAvailable(1);
- result = Short.parseShort(readStringImpl());
- break;
- case BYTE_TYPE:
- checkAvailable(1);
- result = readByteImpl();
- break;
- default:
- _data.position(position);
- throw new MessageFormatException("Unable to convert " + wireType + " to a short");
- }
- }
- catch (RuntimeException e)
- {
- _data.position(position);
- throw e;
- }
- return result;
- }
-
- private short readShortImpl()
- {
- return _data.getShort();
- }
-
- /**
- * Note that this method reads a unicode character as two bytes from the stream
- *
- * @return the character read from the stream
- * @throws javax.jms.JMSException
- */
- protected char readChar() throws JMSException
- {
- int position = _data.position();
- byte wireType = readWireType();
- try
- {
- if(wireType == NULL_STRING_TYPE){
- throw new NullPointerException();
- }
-
- if (wireType != CHAR_TYPE)
- {
- _data.position(position);
- throw new MessageFormatException("Unable to convert " + wireType + " to a char");
- }
- else
- {
- checkAvailable(2);
- return readCharImpl();
- }
- }
- catch (RuntimeException e)
- {
- _data.position(position);
- throw e;
- }
- }
-
- private char readCharImpl()
- {
- return _data.getChar();
- }
-
- protected int readInt() throws JMSException
- {
- int position = _data.position();
- byte wireType = readWireType();
- int result;
- try
- {
- switch (wireType)
- {
- case INT_TYPE:
- checkAvailable(4);
- result = readIntImpl();
- break;
- case SHORT_TYPE:
- checkAvailable(2);
- result = readShortImpl();
- break;
- case STRING_TYPE:
- checkAvailable(1);
- result = Integer.parseInt(readStringImpl());
- break;
- case BYTE_TYPE:
- checkAvailable(1);
- result = readByteImpl();
- break;
- default:
- _data.position(position);
- throw new MessageFormatException("Unable to convert " + wireType + " to an int");
- }
- return result;
- }
- catch (RuntimeException e)
- {
- _data.position(position);
- throw e;
- }
- }
-
- protected int readIntImpl()
- {
- return _data.getInt();
- }
-
- protected long readLong() throws JMSException
- {
- int position = _data.position();
- byte wireType = readWireType();
- long result;
- try
- {
- switch (wireType)
- {
- case LONG_TYPE:
- checkAvailable(8);
- result = readLongImpl();
- break;
- case INT_TYPE:
- checkAvailable(4);
- result = readIntImpl();
- break;
- case SHORT_TYPE:
- checkAvailable(2);
- result = readShortImpl();
- break;
- case STRING_TYPE:
- checkAvailable(1);
- result = Long.parseLong(readStringImpl());
- break;
- case BYTE_TYPE:
- checkAvailable(1);
- result = readByteImpl();
- break;
- default:
- _data.position(position);
- throw new MessageFormatException("Unable to convert " + wireType + " to a long");
- }
- return result;
- }
- catch (RuntimeException e)
- {
- _data.position(position);
- throw e;
- }
- }
-
- private long readLongImpl()
- {
- return _data.getLong();
- }
-
- protected float readFloat() throws JMSException
- {
- int position = _data.position();
- byte wireType = readWireType();
- float result;
- try
- {
- switch (wireType)
- {
- case FLOAT_TYPE:
- checkAvailable(4);
- result = readFloatImpl();
- break;
- case STRING_TYPE:
- checkAvailable(1);
- result = Float.parseFloat(readStringImpl());
- break;
- default:
- _data.position(position);
- throw new MessageFormatException("Unable to convert " + wireType + " to a float");
- }
- return result;
- }
- catch (RuntimeException e)
- {
- _data.position(position);
- throw e;
- }
- }
-
- private float readFloatImpl()
- {
- return _data.getFloat();
- }
-
- protected double readDouble() throws JMSException
- {
- int position = _data.position();
- byte wireType = readWireType();
- double result;
- try
- {
- switch (wireType)
- {
- case DOUBLE_TYPE:
- checkAvailable(8);
- result = readDoubleImpl();
- break;
- case FLOAT_TYPE:
- checkAvailable(4);
- result = readFloatImpl();
- break;
- case STRING_TYPE:
- checkAvailable(1);
- result = Double.parseDouble(readStringImpl());
- break;
- default:
- _data.position(position);
- throw new MessageFormatException("Unable to convert " + wireType + " to a double");
- }
- return result;
- }
- catch (RuntimeException e)
- {
- _data.position(position);
- throw e;
- }
- }
-
- private double readDoubleImpl()
- {
- return _data.getDouble();
- }
-
- protected String readString() throws JMSException
- {
- int position = _data.position();
- byte wireType = readWireType();
- String result;
- try
- {
- switch (wireType)
- {
- case STRING_TYPE:
- checkAvailable(1);
- result = readStringImpl();
- break;
- case NULL_STRING_TYPE:
- result = null;
- throw new NullPointerException("data is null");
- case BOOLEAN_TYPE:
- checkAvailable(1);
- result = String.valueOf(readBooleanImpl());
- break;
- case LONG_TYPE:
- checkAvailable(8);
- result = String.valueOf(readLongImpl());
- break;
- case INT_TYPE:
- checkAvailable(4);
- result = String.valueOf(readIntImpl());
- break;
- case SHORT_TYPE:
- checkAvailable(2);
- result = String.valueOf(readShortImpl());
- break;
- case BYTE_TYPE:
- checkAvailable(1);
- result = String.valueOf(readByteImpl());
- break;
- case FLOAT_TYPE:
- checkAvailable(4);
- result = String.valueOf(readFloatImpl());
- break;
- case DOUBLE_TYPE:
- checkAvailable(8);
- result = String.valueOf(readDoubleImpl());
- break;
- case CHAR_TYPE:
- checkAvailable(2);
- result = String.valueOf(readCharImpl());
- break;
- default:
- _data.position(position);
- throw new MessageFormatException("Unable to convert " + wireType + " to a String");
- }
- return result;
- }
- catch (RuntimeException e)
- {
- _data.position(position);
- throw e;
- }
- }
-
- protected String readStringImpl() throws JMSException
- {
- try
- {
- return _data.getString(Charset.forName("UTF-8").newDecoder());
- }
- catch (CharacterCodingException e)
- {
- JMSException je = new JMSException("Error decoding byte stream as a UTF8 string: " + e);
- je.setLinkedException(e);
- throw je;
- }
- }
-
- protected int readBytes(byte[] bytes) throws JMSException
- {
- if (bytes == null)
- {
- throw new IllegalArgumentException("byte array must not be null");
- }
- checkReadable();
- // first call
- if (_byteArrayRemaining == -1)
- {
- // type discriminator checked separately so you get a MessageFormatException rather than
- // an EOF even in the case where both would be applicable
- checkAvailable(1);
- byte wireType = readWireType();
- if (wireType != BYTEARRAY_TYPE)
- {
- throw new MessageFormatException("Unable to convert " + wireType + " to a byte array");
- }
- checkAvailable(4);
- int size = _data.getInt();
- // length of -1 indicates null
- if (size == -1)
- {
- return -1;
- }
- else
- {
- if (size > _data.remaining())
- {
- throw new MessageEOFException("Byte array has stated length " + size + " but message only contains " +
- _data.remaining() + " bytes");
- }
- else
- {
- _byteArrayRemaining = size;
- }
- }
- }
- else if (_byteArrayRemaining == 0)
- {
- _byteArrayRemaining = -1;
- return -1;
- }
-
- int returnedSize = readBytesImpl(bytes);
- if (returnedSize < bytes.length)
- {
- _byteArrayRemaining = -1;
- }
- return returnedSize;
- }
-
- private int readBytesImpl(byte[] bytes)
- {
- int count = (_byteArrayRemaining >= bytes.length ? bytes.length : _byteArrayRemaining);
- _byteArrayRemaining -= count;
-
- if (count == 0)
- {
- return 0;
- }
- else
- {
- _data.get(bytes, 0, count);
- return count;
- }
- }
-
- protected Object readObject() throws JMSException
- {
- int position = _data.position();
- byte wireType = readWireType();
- Object result = null;
- try
- {
- switch (wireType)
- {
- case BOOLEAN_TYPE:
- checkAvailable(1);
- result = readBooleanImpl();
- break;
- case BYTE_TYPE:
- checkAvailable(1);
- result = readByteImpl();
- break;
- case BYTEARRAY_TYPE:
- checkAvailable(4);
- int size = _data.getInt();
- if (size == -1)
- {
- result = null;
- }
- else
- {
- _byteArrayRemaining = size;
- byte[] bytesResult = new byte[size];
- readBytesImpl(bytesResult);
- result = bytesResult;
- }
- break;
- case SHORT_TYPE:
- checkAvailable(2);
- result = readShortImpl();
- break;
- case CHAR_TYPE:
- checkAvailable(2);
- result = readCharImpl();
- break;
- case INT_TYPE:
- checkAvailable(4);
- result = readIntImpl();
- break;
- case LONG_TYPE:
- checkAvailable(8);
- result = readLongImpl();
- break;
- case FLOAT_TYPE:
- checkAvailable(4);
- result = readFloatImpl();
- break;
- case DOUBLE_TYPE:
- checkAvailable(8);
- result = readDoubleImpl();
- break;
- case NULL_STRING_TYPE:
- result = null;
- break;
- case STRING_TYPE:
- checkAvailable(1);
- result = readStringImpl();
- break;
- }
- return result;
- }
- catch (RuntimeException e)
- {
- _data.position(position);
- throw e;
- }
- }
-
- protected void writeBoolean(boolean b) throws JMSException
- {
- writeTypeDiscriminator(BOOLEAN_TYPE);
- _data.put(b ? (byte) 1 : (byte) 0);
- }
-
- protected void writeByte(byte b) throws JMSException
- {
- writeTypeDiscriminator(BYTE_TYPE);
- _data.put(b);
- }
-
- protected void writeShort(short i) throws JMSException
- {
- writeTypeDiscriminator(SHORT_TYPE);
- _data.putShort(i);
- }
-
- protected void writeChar(char c) throws JMSException
- {
- writeTypeDiscriminator(CHAR_TYPE);
- _data.putChar(c);
- }
-
- protected void writeInt(int i) throws JMSException
- {
- writeTypeDiscriminator(INT_TYPE);
- writeIntImpl(i);
- }
-
- protected void writeIntImpl(int i)
- {
- _data.putInt(i);
- }
-
- protected void writeLong(long l) throws JMSException
- {
- writeTypeDiscriminator(LONG_TYPE);
- _data.putLong(l);
- }
-
- protected void writeFloat(float v) throws JMSException
- {
- writeTypeDiscriminator(FLOAT_TYPE);
- _data.putFloat(v);
- }
-
- protected void writeDouble(double v) throws JMSException
- {
- writeTypeDiscriminator(DOUBLE_TYPE);
- _data.putDouble(v);
- }
-
- protected void writeString(String string) throws JMSException
- {
- if (string == null)
- {
- writeTypeDiscriminator(NULL_STRING_TYPE);
- }
- else
- {
- writeTypeDiscriminator(STRING_TYPE);
- try
- {
- writeStringImpl(string);
- }
- catch (CharacterCodingException e)
- {
- JMSException ex = new JMSException("Unable to encode string: " + e);
- ex.setLinkedException(e);
- throw ex;
- }
- }
- }
-
- protected void writeStringImpl(String string)
- throws CharacterCodingException
- {
- _data.putString(string, Charset.forName("UTF-8").newEncoder());
- // we must write the null terminator ourselves
- _data.put((byte) 0);
- }
-
- protected void writeBytes(byte[] bytes) throws JMSException
- {
- writeBytes(bytes, 0, bytes == null ? 0 : bytes.length);
- }
-
- protected void writeBytes(byte[] bytes, int offset, int length) throws JMSException
- {
- writeTypeDiscriminator(BYTEARRAY_TYPE);
- if (bytes == null)
- {
- _data.putInt(-1);
- }
- else
- {
- _data.putInt(length);
- _data.put(bytes, offset, length);
- }
- }
-
- protected void writeObject(Object object) throws JMSException
- {
- checkWritable();
- Class clazz;
-
- if (object == null)
- {
- // string handles the output of null values
- clazz = String.class;
- }
- else
- {
- clazz = object.getClass();
- }
-
- if (clazz == Byte.class)
- {
- writeByte((Byte) object);
- }
- else if (clazz == Boolean.class)
- {
- writeBoolean((Boolean) object);
- }
- else if (clazz == byte[].class)
- {
- writeBytes((byte[]) object);
- }
- else if (clazz == Short.class)
- {
- writeShort((Short) object);
- }
- else if (clazz == Character.class)
- {
- writeChar((Character) object);
- }
- else if (clazz == Integer.class)
- {
- writeInt((Integer) object);
- }
- else if (clazz == Long.class)
- {
- writeLong((Long) object);
- }
- else if (clazz == Float.class)
- {
- writeFloat((Float) object);
- }
- else if (clazz == Double.class)
- {
- writeDouble((Double) object);
- }
- else if (clazz == String.class)
- {
- writeString((String) object);
- }
- else
- {
- throw new MessageFormatException("Only primitives plus byte arrays and String are valid types");
- }
- }
-}
+/* + * 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.client.message; + +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; + +import javax.jms.JMSException; +import javax.jms.MessageEOFException; +import javax.jms.MessageFormatException; +import javax.jms.MessageNotReadableException; +import javax.jms.MessageNotWriteableException; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.BasicContentHeaderProperties; + +/** + * @author Apache Software Foundation + */ +public abstract class AbstractBytesTypedMessage extends AbstractBytesMessage +{ + + protected static final byte BOOLEAN_TYPE = (byte) 1; + + protected static final byte BYTE_TYPE = (byte) 2; + + protected static final byte BYTEARRAY_TYPE = (byte) 3; + + protected static final byte SHORT_TYPE = (byte) 4; + + protected static final byte CHAR_TYPE = (byte) 5; + + protected static final byte INT_TYPE = (byte) 6; + + protected static final byte LONG_TYPE = (byte) 7; + + protected static final byte FLOAT_TYPE = (byte) 8; + + protected static final byte DOUBLE_TYPE = (byte) 9; + + protected static final byte STRING_TYPE = (byte) 10; + + protected static final byte NULL_STRING_TYPE = (byte) 11; + + /** + * This is set when reading a byte array. The readBytes(byte[]) method supports multiple calls to read + * a byte array in multiple chunks, hence this is used to track how much is left to be read + */ + private int _byteArrayRemaining = -1; + + AbstractBytesTypedMessage() + { + this(null); + } + + /** + * Construct a stream message with existing data. + * + * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is + * set to auto expand + */ + AbstractBytesTypedMessage(ByteBuffer data) + { + super(data); // this instanties a content header + } + + + AbstractBytesTypedMessage(long messageNbr, BasicContentHeaderProperties contentHeader, AMQShortString exchange, + AMQShortString routingKey, ByteBuffer data) throws AMQException + { + super(messageNbr, contentHeader, exchange, routingKey, data); + } + + + protected byte readWireType() throws MessageFormatException, MessageEOFException, + MessageNotReadableException + { + checkReadable(); + checkAvailable(1); + return _data.get(); + } + + protected void writeTypeDiscriminator(byte type) throws MessageNotWriteableException + { + checkWritable(); + _data.put(type); + _changedData = true; + } + + protected boolean readBoolean() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + boolean result; + try + { + switch (wireType) + { + case BOOLEAN_TYPE: + checkAvailable(1); + result = readBooleanImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Boolean.parseBoolean(readStringImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a boolean"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + private boolean readBooleanImpl() + { + return _data.get() != 0; + } + + protected byte readByte() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + byte result; + try + { + switch (wireType) + { + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Byte.parseByte(readStringImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a byte"); + } + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + return result; + } + + private byte readByteImpl() + { + return _data.get(); + } + + protected short readShort() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + short result; + try + { + switch (wireType) + { + case SHORT_TYPE: + checkAvailable(2); + result = readShortImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Short.parseShort(readStringImpl()); + break; + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a short"); + } + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + return result; + } + + private short readShortImpl() + { + return _data.getShort(); + } + + /** + * Note that this method reads a unicode character as two bytes from the stream + * + * @return the character read from the stream + * @throws javax.jms.JMSException + */ + protected char readChar() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + try + { + if(wireType == NULL_STRING_TYPE){ + throw new NullPointerException(); + } + + if (wireType != CHAR_TYPE) + { + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a char"); + } + else + { + checkAvailable(2); + return readCharImpl(); + } + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + private char readCharImpl() + { + return _data.getChar(); + } + + protected int readInt() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + int result; + try + { + switch (wireType) + { + case INT_TYPE: + checkAvailable(4); + result = readIntImpl(); + break; + case SHORT_TYPE: + checkAvailable(2); + result = readShortImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Integer.parseInt(readStringImpl()); + break; + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to an int"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + protected int readIntImpl() + { + return _data.getInt(); + } + + protected long readLong() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + long result; + try + { + switch (wireType) + { + case LONG_TYPE: + checkAvailable(8); + result = readLongImpl(); + break; + case INT_TYPE: + checkAvailable(4); + result = readIntImpl(); + break; + case SHORT_TYPE: + checkAvailable(2); + result = readShortImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Long.parseLong(readStringImpl()); + break; + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a long"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + private long readLongImpl() + { + return _data.getLong(); + } + + protected float readFloat() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + float result; + try + { + switch (wireType) + { + case FLOAT_TYPE: + checkAvailable(4); + result = readFloatImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Float.parseFloat(readStringImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a float"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + private float readFloatImpl() + { + return _data.getFloat(); + } + + protected double readDouble() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + double result; + try + { + switch (wireType) + { + case DOUBLE_TYPE: + checkAvailable(8); + result = readDoubleImpl(); + break; + case FLOAT_TYPE: + checkAvailable(4); + result = readFloatImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Double.parseDouble(readStringImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a double"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + private double readDoubleImpl() + { + return _data.getDouble(); + } + + protected String readString() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + String result; + try + { + switch (wireType) + { + case STRING_TYPE: + checkAvailable(1); + result = readStringImpl(); + break; + case NULL_STRING_TYPE: + result = null; + throw new NullPointerException("data is null"); + case BOOLEAN_TYPE: + checkAvailable(1); + result = String.valueOf(readBooleanImpl()); + break; + case LONG_TYPE: + checkAvailable(8); + result = String.valueOf(readLongImpl()); + break; + case INT_TYPE: + checkAvailable(4); + result = String.valueOf(readIntImpl()); + break; + case SHORT_TYPE: + checkAvailable(2); + result = String.valueOf(readShortImpl()); + break; + case BYTE_TYPE: + checkAvailable(1); + result = String.valueOf(readByteImpl()); + break; + case FLOAT_TYPE: + checkAvailable(4); + result = String.valueOf(readFloatImpl()); + break; + case DOUBLE_TYPE: + checkAvailable(8); + result = String.valueOf(readDoubleImpl()); + break; + case CHAR_TYPE: + checkAvailable(2); + result = String.valueOf(readCharImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a String"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + protected String readStringImpl() throws JMSException + { + try + { + return _data.getString(Charset.forName("UTF-8").newDecoder()); + } + catch (CharacterCodingException e) + { + JMSException je = new JMSException("Error decoding byte stream as a UTF8 string: " + e); + je.setLinkedException(e); + throw je; + } + } + + protected int readBytes(byte[] bytes) throws JMSException + { + if (bytes == null) + { + throw new IllegalArgumentException("byte array must not be null"); + } + checkReadable(); + // first call + if (_byteArrayRemaining == -1) + { + // type discriminator checked separately so you get a MessageFormatException rather than + // an EOF even in the case where both would be applicable + checkAvailable(1); + byte wireType = readWireType(); + if (wireType != BYTEARRAY_TYPE) + { + throw new MessageFormatException("Unable to convert " + wireType + " to a byte array"); + } + checkAvailable(4); + int size = _data.getInt(); + // length of -1 indicates null + if (size == -1) + { + return -1; + } + else + { + if (size > _data.remaining()) + { + throw new MessageEOFException("Byte array has stated length " + size + " but message only contains " + + _data.remaining() + " bytes"); + } + else + { + _byteArrayRemaining = size; + } + } + } + else if (_byteArrayRemaining == 0) + { + _byteArrayRemaining = -1; + return -1; + } + + int returnedSize = readBytesImpl(bytes); + if (returnedSize < bytes.length) + { + _byteArrayRemaining = -1; + } + return returnedSize; + } + + private int readBytesImpl(byte[] bytes) + { + int count = (_byteArrayRemaining >= bytes.length ? bytes.length : _byteArrayRemaining); + _byteArrayRemaining -= count; + + if (count == 0) + { + return 0; + } + else + { + _data.get(bytes, 0, count); + return count; + } + } + + protected Object readObject() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + Object result = null; + try + { + switch (wireType) + { + case BOOLEAN_TYPE: + checkAvailable(1); + result = readBooleanImpl(); + break; + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + case BYTEARRAY_TYPE: + checkAvailable(4); + int size = _data.getInt(); + if (size == -1) + { + result = null; + } + else + { + _byteArrayRemaining = size; + byte[] bytesResult = new byte[size]; + readBytesImpl(bytesResult); + result = bytesResult; + } + break; + case SHORT_TYPE: + checkAvailable(2); + result = readShortImpl(); + break; + case CHAR_TYPE: + checkAvailable(2); + result = readCharImpl(); + break; + case INT_TYPE: + checkAvailable(4); + result = readIntImpl(); + break; + case LONG_TYPE: + checkAvailable(8); + result = readLongImpl(); + break; + case FLOAT_TYPE: + checkAvailable(4); + result = readFloatImpl(); + break; + case DOUBLE_TYPE: + checkAvailable(8); + result = readDoubleImpl(); + break; + case NULL_STRING_TYPE: + result = null; + break; + case STRING_TYPE: + checkAvailable(1); + result = readStringImpl(); + break; + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + protected void writeBoolean(boolean b) throws JMSException + { + writeTypeDiscriminator(BOOLEAN_TYPE); + _data.put(b ? (byte) 1 : (byte) 0); + } + + protected void writeByte(byte b) throws JMSException + { + writeTypeDiscriminator(BYTE_TYPE); + _data.put(b); + } + + protected void writeShort(short i) throws JMSException + { + writeTypeDiscriminator(SHORT_TYPE); + _data.putShort(i); + } + + protected void writeChar(char c) throws JMSException + { + writeTypeDiscriminator(CHAR_TYPE); + _data.putChar(c); + } + + protected void writeInt(int i) throws JMSException + { + writeTypeDiscriminator(INT_TYPE); + writeIntImpl(i); + } + + protected void writeIntImpl(int i) + { + _data.putInt(i); + } + + protected void writeLong(long l) throws JMSException + { + writeTypeDiscriminator(LONG_TYPE); + _data.putLong(l); + } + + protected void writeFloat(float v) throws JMSException + { + writeTypeDiscriminator(FLOAT_TYPE); + _data.putFloat(v); + } + + protected void writeDouble(double v) throws JMSException + { + writeTypeDiscriminator(DOUBLE_TYPE); + _data.putDouble(v); + } + + protected void writeString(String string) throws JMSException + { + if (string == null) + { + writeTypeDiscriminator(NULL_STRING_TYPE); + } + else + { + writeTypeDiscriminator(STRING_TYPE); + try + { + writeStringImpl(string); + } + catch (CharacterCodingException e) + { + JMSException ex = new JMSException("Unable to encode string: " + e); + ex.setLinkedException(e); + throw ex; + } + } + } + + protected void writeStringImpl(String string) + throws CharacterCodingException + { + _data.putString(string, Charset.forName("UTF-8").newEncoder()); + // we must write the null terminator ourselves + _data.put((byte) 0); + } + + protected void writeBytes(byte[] bytes) throws JMSException + { + writeBytes(bytes, 0, bytes == null ? 0 : bytes.length); + } + + protected void writeBytes(byte[] bytes, int offset, int length) throws JMSException + { + writeTypeDiscriminator(BYTEARRAY_TYPE); + if (bytes == null) + { + _data.putInt(-1); + } + else + { + _data.putInt(length); + _data.put(bytes, offset, length); + } + } + + protected void writeObject(Object object) throws JMSException + { + checkWritable(); + Class clazz; + + if (object == null) + { + // string handles the output of null values + clazz = String.class; + } + else + { + clazz = object.getClass(); + } + + if (clazz == Byte.class) + { + writeByte((Byte) object); + } + else if (clazz == Boolean.class) + { + writeBoolean((Boolean) object); + } + else if (clazz == byte[].class) + { + writeBytes((byte[]) object); + } + else if (clazz == Short.class) + { + writeShort((Short) object); + } + else if (clazz == Character.class) + { + writeChar((Character) object); + } + else if (clazz == Integer.class) + { + writeInt((Integer) object); + } + else if (clazz == Long.class) + { + writeLong((Long) object); + } + else if (clazz == Float.class) + { + writeFloat((Float) object); + } + else if (clazz == Double.class) + { + writeDouble((Double) object); + } + else if (clazz == String.class) + { + writeString((String) object); + } + else + { + throw new MessageFormatException("Only primitives plus byte arrays and String are valid types"); + } + } +} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java b/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java index 39b4e1e27b..fec0117a03 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java @@ -1,552 +1,552 @@ -/*
- *
- * 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.client.message;
-
-import java.util.Enumeration;
-
-import javax.jms.JMSException;
-import javax.jms.MessageFormatException;
-
-import org.apache.mina.common.ByteBuffer;
-import org.apache.qpid.AMQPInvalidClassException;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.FieldTable;
-
-
-public final class JMSHeaderAdapter
-{
- private final FieldTable _headers;
-
- public JMSHeaderAdapter(FieldTable headers)
- {
- _headers = headers;
- }
-
-
- public FieldTable getHeaders()
- {
- return _headers;
- }
-
- public boolean getBoolean(String string) throws JMSException
- {
- checkPropertyName(string);
- Boolean b = getHeaders().getBoolean(string);
-
- if (b == null)
- {
- if (getHeaders().containsKey(string))
- {
- Object str = getHeaders().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 boolean getBoolean(AMQShortString string) throws JMSException
- {
- checkPropertyName(string);
- Boolean b = getHeaders().getBoolean(string);
-
- if (b == null)
- {
- if (getHeaders().containsKey(string))
- {
- Object str = getHeaders().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
- {
- checkPropertyName(string);
- Character c = getHeaders().getCharacter(string);
-
- if (c == null)
- {
- if (getHeaders().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
- {
- return getBytes(new AMQShortString(string));
- }
-
- public byte[] getBytes(AMQShortString string) throws JMSException
- {
- checkPropertyName(string);
-
- byte[] bs = getHeaders().getBytes(string);
-
- if (bs == null)
- {
- throw new MessageFormatException("getBytes can't use " + string + " item.");
- }
- else
- {
- return bs;
- }
- }
-
- public byte getByte(String string) throws JMSException
- {
- checkPropertyName(string);
- Byte b = getHeaders().getByte(string);
- if (b == null)
- {
- if (getHeaders().containsKey(string))
- {
- Object str = getHeaders().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
- {
- checkPropertyName(string);
- Short s = getHeaders().getShort(string);
-
- if (s == null)
- {
- s = Short.valueOf(getByte(string));
- }
-
- return s;
- }
-
- public int getInteger(String string) throws JMSException
- {
- checkPropertyName(string);
- Integer i = getHeaders().getInteger(string);
-
- if (i == null)
- {
- i = Integer.valueOf(getShort(string));
- }
-
- return i;
- }
-
- public long getLong(String string) throws JMSException
- {
- checkPropertyName(string);
- Long l = getHeaders().getLong(string);
-
- if (l == null)
- {
- l = Long.valueOf(getInteger(string));
- }
-
- return l;
- }
-
- public float getFloat(String string) throws JMSException
- {
- checkPropertyName(string);
- Float f = getHeaders().getFloat(string);
-
- if (f == null)
- {
- if (getHeaders().containsKey(string))
- {
- Object str = getHeaders().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
- {
- checkPropertyName(string);
- Double d = getHeaders().getDouble(string);
-
- if (d == null)
- {
- d = Double.valueOf(getFloat(string));
- }
-
- return d;
- }
-
- public String getString(String string) throws JMSException
- {
- checkPropertyName(string);
- String s = getHeaders().getString(string);
-
- if (s == null)
- {
- if (getHeaders().containsKey(string))
- {
- Object o = getHeaders().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);
- }
- }
- }//else return s // null;
- }
-
- return s;
- }
-
- public Object getObject(String string) throws JMSException
- {
- checkPropertyName(string);
- return getHeaders().getObject(string);
- }
-
- public void setBoolean(AMQShortString string, boolean b) throws JMSException
- {
- checkPropertyName(string);
- getHeaders().setBoolean(string, b);
- }
-
- public void setBoolean(String string, boolean b) throws JMSException
- {
- checkPropertyName(string);
- getHeaders().setBoolean(string, b);
- }
-
- public void setChar(String string, char c) throws JMSException
- {
- checkPropertyName(string);
- getHeaders().setChar(string, c);
- }
-
- public Object setBytes(AMQShortString string, byte[] bytes)
- {
- checkPropertyName(string);
- return getHeaders().setBytes(string, bytes);
- }
-
- public Object setBytes(String string, byte[] bytes)
- {
- checkPropertyName(string);
- return getHeaders().setBytes(string, bytes);
- }
-
- public Object setBytes(String string, byte[] bytes, int start, int length)
- {
- checkPropertyName(string);
- return getHeaders().setBytes(string, bytes, start, length);
- }
-
- public void setByte(String string, byte b) throws JMSException
- {
- checkPropertyName(string);
- getHeaders().setByte(string, b);
- }
-
- public void setByte(AMQShortString string, byte b) throws JMSException
- {
- checkPropertyName(string);
- getHeaders().setByte(string, b);
- }
-
-
- public void setShort(String string, short i) throws JMSException
- {
- checkPropertyName(string);
- getHeaders().setShort(string, i);
- }
-
- public void setInteger(String string, int i) throws JMSException
- {
- checkPropertyName(string);
- getHeaders().setInteger(string, i);
- }
-
- public void setInteger(AMQShortString string, int i) throws JMSException
- {
- checkPropertyName(string);
- getHeaders().setInteger(string, i);
- }
-
- public void setLong(String string, long l) throws JMSException
- {
- checkPropertyName(string);
- getHeaders().setLong(string, l);
- }
-
- public void setFloat(String string, float v) throws JMSException
- {
- checkPropertyName(string);
- getHeaders().setFloat(string, v);
- }
-
- public void setDouble(String string, double v) throws JMSException
- {
- checkPropertyName(string);
- getHeaders().setDouble(string, v);
- }
-
- public void setString(String string, String string1) throws JMSException
- {
- checkPropertyName(string);
- getHeaders().setString(string, string1);
- }
-
- public void setString(AMQShortString string, String string1) throws JMSException
- {
- checkPropertyName(string);
- getHeaders().setString(string, string1);
- }
-
- public void setObject(String string, Object object) throws JMSException
- {
- checkPropertyName(string);
- try
- {
- getHeaders().setObject(string, object);
- }
- catch (AMQPInvalidClassException aice)
- {
- MessageFormatException mfe = new MessageFormatException("Only primatives are allowed object is:" + object.getClass());
- mfe.setLinkedException(aice);
- throw mfe;
- }
- }
-
- public boolean itemExists(String string) throws JMSException
- {
- checkPropertyName(string);
- return getHeaders().containsKey(string);
- }
-
- public Enumeration getPropertyNames()
- {
- return getHeaders().getPropertyNames();
- }
-
- public void clear()
- {
- getHeaders().clear();
- }
-
- public boolean propertyExists(AMQShortString propertyName)
- {
- checkPropertyName(propertyName);
- return getHeaders().propertyExists(propertyName);
- }
-
- public boolean propertyExists(String propertyName)
- {
- checkPropertyName(propertyName);
- return getHeaders().propertyExists(propertyName);
- }
-
- public Object put(Object key, Object value)
- {
- checkPropertyName(key.toString());
- return getHeaders().setObject(key.toString(), value);
- }
-
- public Object remove(AMQShortString propertyName)
- {
- checkPropertyName(propertyName);
- return getHeaders().remove(propertyName);
- }
-
- public Object remove(String propertyName)
- {
- checkPropertyName(propertyName);
- return getHeaders().remove(propertyName);
- }
-
- public boolean isEmpty()
- {
- return getHeaders().isEmpty();
- }
-
- public void writeToBuffer(ByteBuffer data)
- {
- getHeaders().writeToBuffer(data);
- }
-
- public Enumeration getMapNames()
- {
- return getPropertyNames();
- }
-
- protected static void checkPropertyName(CharSequence propertyName)
- {
- if (propertyName == null)
- {
- throw new IllegalArgumentException("Property name must not be null");
- }
- else if (propertyName.length() == 0)
- {
- throw new IllegalArgumentException("Property name must not be the empty string");
- }
-
- checkIdentiferFormat(propertyName);
- }
-
- protected static void checkIdentiferFormat(CharSequence 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");
- }
- }
-
- }
-}
+/* + * + * 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.client.message; + +import java.util.Enumeration; + +import javax.jms.JMSException; +import javax.jms.MessageFormatException; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQPInvalidClassException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; + + +public final class JMSHeaderAdapter +{ + private final FieldTable _headers; + + public JMSHeaderAdapter(FieldTable headers) + { + _headers = headers; + } + + + public FieldTable getHeaders() + { + return _headers; + } + + public boolean getBoolean(String string) throws JMSException + { + checkPropertyName(string); + Boolean b = getHeaders().getBoolean(string); + + if (b == null) + { + if (getHeaders().containsKey(string)) + { + Object str = getHeaders().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 boolean getBoolean(AMQShortString string) throws JMSException + { + checkPropertyName(string); + Boolean b = getHeaders().getBoolean(string); + + if (b == null) + { + if (getHeaders().containsKey(string)) + { + Object str = getHeaders().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 + { + checkPropertyName(string); + Character c = getHeaders().getCharacter(string); + + if (c == null) + { + if (getHeaders().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 + { + return getBytes(new AMQShortString(string)); + } + + public byte[] getBytes(AMQShortString string) throws JMSException + { + checkPropertyName(string); + + byte[] bs = getHeaders().getBytes(string); + + if (bs == null) + { + throw new MessageFormatException("getBytes can't use " + string + " item."); + } + else + { + return bs; + } + } + + public byte getByte(String string) throws JMSException + { + checkPropertyName(string); + Byte b = getHeaders().getByte(string); + if (b == null) + { + if (getHeaders().containsKey(string)) + { + Object str = getHeaders().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 + { + checkPropertyName(string); + Short s = getHeaders().getShort(string); + + if (s == null) + { + s = Short.valueOf(getByte(string)); + } + + return s; + } + + public int getInteger(String string) throws JMSException + { + checkPropertyName(string); + Integer i = getHeaders().getInteger(string); + + if (i == null) + { + i = Integer.valueOf(getShort(string)); + } + + return i; + } + + public long getLong(String string) throws JMSException + { + checkPropertyName(string); + Long l = getHeaders().getLong(string); + + if (l == null) + { + l = Long.valueOf(getInteger(string)); + } + + return l; + } + + public float getFloat(String string) throws JMSException + { + checkPropertyName(string); + Float f = getHeaders().getFloat(string); + + if (f == null) + { + if (getHeaders().containsKey(string)) + { + Object str = getHeaders().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 + { + checkPropertyName(string); + Double d = getHeaders().getDouble(string); + + if (d == null) + { + d = Double.valueOf(getFloat(string)); + } + + return d; + } + + public String getString(String string) throws JMSException + { + checkPropertyName(string); + String s = getHeaders().getString(string); + + if (s == null) + { + if (getHeaders().containsKey(string)) + { + Object o = getHeaders().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); + } + } + }//else return s // null; + } + + return s; + } + + public Object getObject(String string) throws JMSException + { + checkPropertyName(string); + return getHeaders().getObject(string); + } + + public void setBoolean(AMQShortString string, boolean b) throws JMSException + { + checkPropertyName(string); + getHeaders().setBoolean(string, b); + } + + public void setBoolean(String string, boolean b) throws JMSException + { + checkPropertyName(string); + getHeaders().setBoolean(string, b); + } + + public void setChar(String string, char c) throws JMSException + { + checkPropertyName(string); + getHeaders().setChar(string, c); + } + + public Object setBytes(AMQShortString string, byte[] bytes) + { + checkPropertyName(string); + return getHeaders().setBytes(string, bytes); + } + + public Object setBytes(String string, byte[] bytes) + { + checkPropertyName(string); + return getHeaders().setBytes(string, bytes); + } + + public Object setBytes(String string, byte[] bytes, int start, int length) + { + checkPropertyName(string); + return getHeaders().setBytes(string, bytes, start, length); + } + + public void setByte(String string, byte b) throws JMSException + { + checkPropertyName(string); + getHeaders().setByte(string, b); + } + + public void setByte(AMQShortString string, byte b) throws JMSException + { + checkPropertyName(string); + getHeaders().setByte(string, b); + } + + + public void setShort(String string, short i) throws JMSException + { + checkPropertyName(string); + getHeaders().setShort(string, i); + } + + public void setInteger(String string, int i) throws JMSException + { + checkPropertyName(string); + getHeaders().setInteger(string, i); + } + + public void setInteger(AMQShortString string, int i) throws JMSException + { + checkPropertyName(string); + getHeaders().setInteger(string, i); + } + + public void setLong(String string, long l) throws JMSException + { + checkPropertyName(string); + getHeaders().setLong(string, l); + } + + public void setFloat(String string, float v) throws JMSException + { + checkPropertyName(string); + getHeaders().setFloat(string, v); + } + + public void setDouble(String string, double v) throws JMSException + { + checkPropertyName(string); + getHeaders().setDouble(string, v); + } + + public void setString(String string, String string1) throws JMSException + { + checkPropertyName(string); + getHeaders().setString(string, string1); + } + + public void setString(AMQShortString string, String string1) throws JMSException + { + checkPropertyName(string); + getHeaders().setString(string, string1); + } + + public void setObject(String string, Object object) throws JMSException + { + checkPropertyName(string); + try + { + getHeaders().setObject(string, object); + } + catch (AMQPInvalidClassException aice) + { + MessageFormatException mfe = new MessageFormatException("Only primatives are allowed object is:" + object.getClass()); + mfe.setLinkedException(aice); + throw mfe; + } + } + + public boolean itemExists(String string) throws JMSException + { + checkPropertyName(string); + return getHeaders().containsKey(string); + } + + public Enumeration getPropertyNames() + { + return getHeaders().getPropertyNames(); + } + + public void clear() + { + getHeaders().clear(); + } + + public boolean propertyExists(AMQShortString propertyName) + { + checkPropertyName(propertyName); + return getHeaders().propertyExists(propertyName); + } + + public boolean propertyExists(String propertyName) + { + checkPropertyName(propertyName); + return getHeaders().propertyExists(propertyName); + } + + public Object put(Object key, Object value) + { + checkPropertyName(key.toString()); + return getHeaders().setObject(key.toString(), value); + } + + public Object remove(AMQShortString propertyName) + { + checkPropertyName(propertyName); + return getHeaders().remove(propertyName); + } + + public Object remove(String propertyName) + { + checkPropertyName(propertyName); + return getHeaders().remove(propertyName); + } + + public boolean isEmpty() + { + return getHeaders().isEmpty(); + } + + public void writeToBuffer(ByteBuffer data) + { + getHeaders().writeToBuffer(data); + } + + public Enumeration getMapNames() + { + return getPropertyNames(); + } + + protected static void checkPropertyName(CharSequence propertyName) + { + if (propertyName == null) + { + throw new IllegalArgumentException("Property name must not be null"); + } + else if (propertyName.length() == 0) + { + throw new IllegalArgumentException("Property name must not be the empty string"); + } + + checkIdentiferFormat(propertyName); + } + + protected static void checkIdentiferFormat(CharSequence 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"); + } + } + + } +} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQMethodNotImplementedException.java b/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQMethodNotImplementedException.java index 5185278eef..2c99b9a97b 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQMethodNotImplementedException.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQMethodNotImplementedException.java @@ -1,32 +1,32 @@ -/*
- *
- * 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.client.state;
-
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.AMQException;
-
-public class AMQMethodNotImplementedException extends AMQException
-{
- public AMQMethodNotImplementedException(AMQMethodBody body)
- {
- super(null, "Unexpected Method Received: " + body.getClass().getName(), null);
- }
-}
+/* + * + * 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.client.state; + +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.AMQException; + +public class AMQMethodNotImplementedException extends AMQException +{ + public AMQMethodNotImplementedException(AMQMethodBody body) + { + super(null, "Unexpected Method Received: " + body.getClass().getName(), null); + } +} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/jms/Message.java b/qpid/java/client/src/main/java/org/apache/qpid/jms/Message.java index 6752ee616f..e65f9ad2f4 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/jms/Message.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/jms/Message.java @@ -1,28 +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.JMSException;
-
-public interface Message extends javax.jms.Message
-{
- public void acknowledgeThis() throws JMSException;
-}
+/* + * + * 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.JMSException; + +public interface Message extends javax.jms.Message +{ + public void acknowledgeThis() throws JMSException; +} diff --git a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java index a5279a195b..1738db7239 100644 --- a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java +++ b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java @@ -1,104 +1,104 @@ -/*
- *
- * 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 org.apache.qpid.client.AMQConnection;
-import org.apache.qpid.client.AMQQueue;
-import org.apache.qpid.testutil.QpidTestCase;
-
-import javax.jms.Session;
-import javax.jms.QueueSession;
-import javax.jms.Queue;
-import javax.jms.QueueSender;
-import javax.jms.TextMessage;
-import javax.jms.InvalidDestinationException;
-
-public class InvalidDestinationTest extends QpidTestCase
-{
- private AMQConnection _connection;
-
- protected void setUp() throws Exception
- {
- super.setUp();
- _connection = (AMQConnection) getConnection("guest", "guest");
- }
-
- protected void tearDown() throws Exception
- {
- _connection.close();
- super.tearDown();
- }
-
-
-
- public void testInvalidDestination() throws Exception
- {
- Queue invalidDestination = new AMQQueue("amq.direct","unknownQ");
- AMQQueue validDestination = new AMQQueue("amq.direct","knownQ");
- QueueSession queueSession = _connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
-
- // This is the only easy way to create and bind a queue from the API :-(
- queueSession.createConsumer(validDestination);
-
- QueueSender sender = queueSession.createSender(invalidDestination);
- TextMessage msg = queueSession.createTextMessage("Hello");
- try
- {
- sender.send(msg);
- fail("Expected InvalidDestinationException");
- }
- catch (InvalidDestinationException ex)
- {
- // pass
- }
- sender.close();
-
- sender = queueSession.createSender(null);
- invalidDestination = new AMQQueue("amq.direct","unknownQ");
-
- try
- {
- sender.send(invalidDestination,msg);
- fail("Expected InvalidDestinationException");
- }
- catch (InvalidDestinationException ex)
- {
- // pass
- }
- sender.send(validDestination,msg);
- sender.close();
- validDestination = new AMQQueue("amq.direct","knownQ");
- sender = queueSession.createSender(validDestination);
- sender.send(msg);
-
-
-
-
- }
-
-
- public static junit.framework.Test suite()
- {
-
- return new junit.framework.TestSuite(InvalidDestinationTest.class);
- }
-}
+/* + * + * 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 org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.testutil.QpidTestCase; + +import javax.jms.Session; +import javax.jms.QueueSession; +import javax.jms.Queue; +import javax.jms.QueueSender; +import javax.jms.TextMessage; +import javax.jms.InvalidDestinationException; + +public class InvalidDestinationTest extends QpidTestCase +{ + private AMQConnection _connection; + + protected void setUp() throws Exception + { + super.setUp(); + _connection = (AMQConnection) getConnection("guest", "guest"); + } + + protected void tearDown() throws Exception + { + _connection.close(); + super.tearDown(); + } + + + + public void testInvalidDestination() throws Exception + { + Queue invalidDestination = new AMQQueue("amq.direct","unknownQ"); + AMQQueue validDestination = new AMQQueue("amq.direct","knownQ"); + QueueSession queueSession = _connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); + + // This is the only easy way to create and bind a queue from the API :-( + queueSession.createConsumer(validDestination); + + QueueSender sender = queueSession.createSender(invalidDestination); + TextMessage msg = queueSession.createTextMessage("Hello"); + try + { + sender.send(msg); + fail("Expected InvalidDestinationException"); + } + catch (InvalidDestinationException ex) + { + // pass + } + sender.close(); + + sender = queueSession.createSender(null); + invalidDestination = new AMQQueue("amq.direct","unknownQ"); + + try + { + sender.send(invalidDestination,msg); + fail("Expected InvalidDestinationException"); + } + catch (InvalidDestinationException ex) + { + // pass + } + sender.send(validDestination,msg); + sender.close(); + validDestination = new AMQQueue("amq.direct","knownQ"); + sender = queueSession.createSender(validDestination); + sender.send(msg); + + + + + } + + + public static junit.framework.Test suite() + { + + return new junit.framework.TestSuite(InvalidDestinationTest.class); + } +} diff --git a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java index 34197f2608..46b99fac8d 100644 --- a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java +++ b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java @@ -1,221 +1,221 @@ -/*
- *
- * 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.client.temporaryqueue;
-
-import javax.jms.Connection;
-import javax.jms.JMSException;
-import javax.jms.MessageConsumer;
-import javax.jms.MessageProducer;
-import javax.jms.Session;
-import javax.jms.TemporaryQueue;
-import javax.jms.TextMessage;
-import junit.framework.Assert;
-
-import org.apache.qpid.testutil.QpidTestCase;
-import org.apache.qpid.client.AMQQueue;
-import org.apache.qpid.client.transport.TransportConnection;
-
-import java.util.List;
-import java.util.LinkedList;
-
-public class TemporaryQueueTest extends QpidTestCase
-{
- protected void setUp() throws Exception
- {
- super.setUp();
- }
-
- protected void tearDown() throws Exception
- {
- super.tearDown();
- }
-
- protected Connection createConnection() throws Exception
- {
- return getConnection("guest", "guest");
- }
-
- 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 void tUniqueness() throws Exception
- {
- int numProcs = Runtime.getRuntime().availableProcessors();
- final int threadsProc = 5;
-
- runUniqueness(1, 10);
- runUniqueness(numProcs * threadsProc, 10);
- runUniqueness(numProcs * threadsProc, 100);
- runUniqueness(numProcs * threadsProc, 500);
- }
-
- void runUniqueness(int makers, int queues) throws Exception
- {
- Connection connection = createConnection();
-
- Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
-
- List<TempQueueMaker> tqList = new LinkedList<TempQueueMaker>();
-
- //Create Makers
- for (int m = 0; m < makers; m++)
- {
- tqList.add(new TempQueueMaker(session, queues));
- }
-
-
- List<Thread> threadList = new LinkedList<Thread>();
-
- //Create Makers
- for (TempQueueMaker maker : tqList)
- {
- threadList.add(new Thread(maker));
- }
-
- //Start threads
- for (Thread thread : threadList)
- {
- thread.start();
- }
-
- // Join Threads
- for (Thread thread : threadList)
- {
- try
- {
- thread.join();
- }
- catch (InterruptedException e)
- {
- fail("Couldn't correctly join threads");
- }
- }
-
-
- List<AMQQueue> list = new LinkedList<AMQQueue>();
-
- // Test values
- for (TempQueueMaker maker : tqList)
- {
- check(maker, list);
- }
-
- Assert.assertEquals("Not enough queues made.", makers * queues, list.size());
-
- connection.close();
- }
-
- private void check(TempQueueMaker tq, List<AMQQueue> list)
- {
- for (AMQQueue q : tq.getList())
- {
- if (list.contains(q))
- {
- fail(q + " already exists.");
- }
- else
- {
- list.add(q);
- }
- }
- }
-
-
- class TempQueueMaker implements Runnable
- {
- List<AMQQueue> _queues;
- Session _session;
- private int _count;
-
-
- TempQueueMaker(Session session, int queues) throws JMSException
- {
- _queues = new LinkedList<AMQQueue>();
-
- _count = queues;
-
- _session = session;
- }
-
- public void run()
- {
- int i = 0;
- try
- {
- for (; i < _count; i++)
- {
- _queues.add((AMQQueue) _session.createTemporaryQueue());
- }
- }
- catch (JMSException jmse)
- {
- //stop
- }
- }
-
- List<AMQQueue> getList()
- {
- return _queues;
- }
- }
-
-
- public static junit.framework.Test suite()
- {
- return new junit.framework.TestSuite(TemporaryQueueTest.class);
- }
-}
+/* + * + * 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.client.temporaryqueue; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TemporaryQueue; +import javax.jms.TextMessage; +import junit.framework.Assert; + +import org.apache.qpid.testutil.QpidTestCase; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.transport.TransportConnection; + +import java.util.List; +import java.util.LinkedList; + +public class TemporaryQueueTest extends QpidTestCase +{ + protected void setUp() throws Exception + { + super.setUp(); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + } + + protected Connection createConnection() throws Exception + { + return getConnection("guest", "guest"); + } + + 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 void tUniqueness() throws Exception + { + int numProcs = Runtime.getRuntime().availableProcessors(); + final int threadsProc = 5; + + runUniqueness(1, 10); + runUniqueness(numProcs * threadsProc, 10); + runUniqueness(numProcs * threadsProc, 100); + runUniqueness(numProcs * threadsProc, 500); + } + + void runUniqueness(int makers, int queues) throws Exception + { + Connection connection = createConnection(); + + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + List<TempQueueMaker> tqList = new LinkedList<TempQueueMaker>(); + + //Create Makers + for (int m = 0; m < makers; m++) + { + tqList.add(new TempQueueMaker(session, queues)); + } + + + List<Thread> threadList = new LinkedList<Thread>(); + + //Create Makers + for (TempQueueMaker maker : tqList) + { + threadList.add(new Thread(maker)); + } + + //Start threads + for (Thread thread : threadList) + { + thread.start(); + } + + // Join Threads + for (Thread thread : threadList) + { + try + { + thread.join(); + } + catch (InterruptedException e) + { + fail("Couldn't correctly join threads"); + } + } + + + List<AMQQueue> list = new LinkedList<AMQQueue>(); + + // Test values + for (TempQueueMaker maker : tqList) + { + check(maker, list); + } + + Assert.assertEquals("Not enough queues made.", makers * queues, list.size()); + + connection.close(); + } + + private void check(TempQueueMaker tq, List<AMQQueue> list) + { + for (AMQQueue q : tq.getList()) + { + if (list.contains(q)) + { + fail(q + " already exists."); + } + else + { + list.add(q); + } + } + } + + + class TempQueueMaker implements Runnable + { + List<AMQQueue> _queues; + Session _session; + private int _count; + + + TempQueueMaker(Session session, int queues) throws JMSException + { + _queues = new LinkedList<AMQQueue>(); + + _count = queues; + + _session = session; + } + + public void run() + { + int i = 0; + try + { + for (; i < _count; i++) + { + _queues.add((AMQQueue) _session.createTemporaryQueue()); + } + } + catch (JMSException jmse) + { + //stop + } + } + + List<AMQQueue> getList() + { + return _queues; + } + } + + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(TemporaryQueueTest.class); + } +} diff --git a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java index d25986d991..54b2ee95f4 100644 --- a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java +++ b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java @@ -1,144 +1,144 @@ -/*
- *
- * 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.close;
-
-import junit.framework.Assert;
-
-import org.apache.qpid.client.AMQConnection;
-import org.apache.qpid.client.transport.TransportConnection;
-import org.apache.qpid.testutil.QpidTestCase;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.qpid.junit.concurrency.TestRunnable;
-import org.apache.qpid.junit.concurrency.ThreadTestCoordinator;
-
-import javax.jms.Connection;
-import javax.jms.Message;
-import javax.jms.MessageListener;
-import javax.jms.Session;
-
-/**
- * This test forces the situation where a session is closed whilst a message consumer is still in its onMessage method.
- * Running in AUTO_ACK mode, the close call ought to wait until the onMessage method completes, and the ack is sent
- * before closing the connection.
- *
- * <p><table id="crc"><caption>CRC Card</caption> <tr><th> Responsibilities <th> Collaborations <tr><td> Check that
- * closing a connection whilst handling a message, blocks till completion of the handler. </table>
- */
-public class CloseBeforeAckTest extends QpidTestCase
-{
- private static final Logger log = LoggerFactory.getLogger(CloseBeforeAckTest.class);
-
- Connection connection;
- Session session;
- public static final String TEST_QUEUE_NAME = "TestQueue";
- private int TEST_COUNT = 25;
-
- class TestThread1 extends TestRunnable implements MessageListener
- {
- public void runWithExceptions() throws Exception
- {
- // Set this up to listen for message on the test session.
- session.createConsumer(session.createQueue(TEST_QUEUE_NAME)).setMessageListener(this);
- }
-
- public void onMessage(Message message)
- {
- // Give thread 2 permission to close the session.
- allow(new int[] { 1 });
-
- // Wait until thread 2 has closed the connection, or is blocked waiting for this to complete.
- waitFor(new int[] { 1 }, true);
- }
- }
-
- TestThread1 testThread1 = new TestThread1();
-
- TestRunnable testThread2 =
- new TestRunnable()
- {
- public void runWithExceptions() throws Exception
- {
- // Send a message to be picked up by thread 1.
- session.createProducer(null).send(session.createQueue(TEST_QUEUE_NAME),
- session.createTextMessage("Hi there thread 1!"));
-
- // Wait for thread 1 to pick up the message and give permission to continue.
- waitFor(new int[] { 0 }, false);
-
- // Close the connection.
- session.close();
-
- // Allow thread 1 to continue to completion, if it is erronously still waiting.
- allow(new int[] { 1 });
- }
- };
-
- public void testCloseBeforeAutoAck_QPID_397() throws Exception
- {
- // Create a session in auto acknowledge mode. This problem shows up in auto acknowledge if the client acks
- // message at the end of the onMessage method, after a close has been sent.
- session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
-
- ThreadTestCoordinator tt = new ThreadTestCoordinator(2);
-
- tt.addTestThread(testThread1, 0);
- tt.addTestThread(testThread2, 1);
- tt.setDeadlockTimeout(500);
- tt.run();
-
- String errorMessage = tt.joinAndRetrieveMessages();
-
- // Print any error messages or exceptions.
- log.debug(errorMessage);
-
- if (!tt.getExceptions().isEmpty())
- {
- for (Exception e : tt.getExceptions())
- {
- log.debug("Exception thrown during test thread: ", e);
- }
- }
-
- Assert.assertTrue(errorMessage, "".equals(errorMessage));
- }
-
- public void closeBeforeAutoAckManyTimes() throws Exception
- {
- for (int i = 0; i < TEST_COUNT; i++)
- {
- testCloseBeforeAutoAck_QPID_397();
- }
- }
-
- protected void setUp() throws Exception
- {
- super.setUp();
- connection = getConnection("guest", "guest");
- }
-
- protected void tearDown() throws Exception
- {
- super.tearDown();
- }
-}
+/* + * + * 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.close; + +import junit.framework.Assert; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.testutil.QpidTestCase; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.qpid.junit.concurrency.TestRunnable; +import org.apache.qpid.junit.concurrency.ThreadTestCoordinator; + +import javax.jms.Connection; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.Session; + +/** + * This test forces the situation where a session is closed whilst a message consumer is still in its onMessage method. + * Running in AUTO_ACK mode, the close call ought to wait until the onMessage method completes, and the ack is sent + * before closing the connection. + * + * <p><table id="crc"><caption>CRC Card</caption> <tr><th> Responsibilities <th> Collaborations <tr><td> Check that + * closing a connection whilst handling a message, blocks till completion of the handler. </table> + */ +public class CloseBeforeAckTest extends QpidTestCase +{ + private static final Logger log = LoggerFactory.getLogger(CloseBeforeAckTest.class); + + Connection connection; + Session session; + public static final String TEST_QUEUE_NAME = "TestQueue"; + private int TEST_COUNT = 25; + + class TestThread1 extends TestRunnable implements MessageListener + { + public void runWithExceptions() throws Exception + { + // Set this up to listen for message on the test session. + session.createConsumer(session.createQueue(TEST_QUEUE_NAME)).setMessageListener(this); + } + + public void onMessage(Message message) + { + // Give thread 2 permission to close the session. + allow(new int[] { 1 }); + + // Wait until thread 2 has closed the connection, or is blocked waiting for this to complete. + waitFor(new int[] { 1 }, true); + } + } + + TestThread1 testThread1 = new TestThread1(); + + TestRunnable testThread2 = + new TestRunnable() + { + public void runWithExceptions() throws Exception + { + // Send a message to be picked up by thread 1. + session.createProducer(null).send(session.createQueue(TEST_QUEUE_NAME), + session.createTextMessage("Hi there thread 1!")); + + // Wait for thread 1 to pick up the message and give permission to continue. + waitFor(new int[] { 0 }, false); + + // Close the connection. + session.close(); + + // Allow thread 1 to continue to completion, if it is erronously still waiting. + allow(new int[] { 1 }); + } + }; + + public void testCloseBeforeAutoAck_QPID_397() throws Exception + { + // Create a session in auto acknowledge mode. This problem shows up in auto acknowledge if the client acks + // message at the end of the onMessage method, after a close has been sent. + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + ThreadTestCoordinator tt = new ThreadTestCoordinator(2); + + tt.addTestThread(testThread1, 0); + tt.addTestThread(testThread2, 1); + tt.setDeadlockTimeout(500); + tt.run(); + + String errorMessage = tt.joinAndRetrieveMessages(); + + // Print any error messages or exceptions. + log.debug(errorMessage); + + if (!tt.getExceptions().isEmpty()) + { + for (Exception e : tt.getExceptions()) + { + log.debug("Exception thrown during test thread: ", e); + } + } + + Assert.assertTrue(errorMessage, "".equals(errorMessage)); + } + + public void closeBeforeAutoAckManyTimes() throws Exception + { + for (int i = 0; i < TEST_COUNT; i++) + { + testCloseBeforeAutoAck_QPID_397(); + } + } + + protected void setUp() throws Exception + { + super.setUp(); + connection = getConnection("guest", "guest"); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + } +} diff --git a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSDestinationTest.java b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSDestinationTest.java index 0b9d0bdc2d..131cbd5f68 100644 --- a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSDestinationTest.java +++ b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSDestinationTest.java @@ -1,89 +1,89 @@ -/*
- *
- * 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.message;
-
-import org.apache.qpid.client.AMQConnection;
-import org.apache.qpid.client.AMQQueue;
-import org.apache.qpid.client.AMQSession;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.testutil.QpidTestCase;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.jms.Connection;
-import javax.jms.MessageConsumer;
-import javax.jms.MessageProducer;
-import javax.jms.Queue;
-import javax.jms.Session;
-import javax.jms.TextMessage;
-
-/**
- * @author Apache Software Foundation
- */
-public class JMSDestinationTest extends QpidTestCase
-{
- private static final Logger _logger = LoggerFactory.getLogger(JMSDestinationTest.class);
-
-
- protected void setUp() throws Exception
- {
- super.setUp();
- }
-
- protected void tearDown() throws Exception
- {
- super.tearDown();
- }
-
- public void testJMSDestination() throws Exception
- {
- AMQConnection con = (AMQConnection) getConnection("guest", "guest");
- AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
- Queue queue =
- new AMQQueue(con.getDefaultQueueExchangeName(), new AMQShortString("someQ"), new AMQShortString("someQ"), false,
- true);
- MessageConsumer consumer = consumerSession.createConsumer(queue);
-
- Connection con2 = (AMQConnection) getConnection("guest", "guest");
- Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
- MessageProducer producer = producerSession.createProducer(queue);
-
- TextMessage sentMsg = producerSession.createTextMessage("hello");
- assertNull(sentMsg.getJMSDestination());
-
- producer.send(sentMsg);
-
- assertEquals(sentMsg.getJMSDestination(), queue);
-
- con2.close();
-
- con.start();
-
- TextMessage rm = (TextMessage) consumer.receive();
- assertNotNull(rm);
-
- assertEquals(rm.getJMSDestination(), queue);
- con.close();
- }
-
-}
+/* + * + * 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.message; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.testutil.QpidTestCase; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.Connection; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; + +/** + * @author Apache Software Foundation + */ +public class JMSDestinationTest extends QpidTestCase +{ + private static final Logger _logger = LoggerFactory.getLogger(JMSDestinationTest.class); + + + protected void setUp() throws Exception + { + super.setUp(); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + } + + public void testJMSDestination() throws Exception + { + AMQConnection con = (AMQConnection) getConnection("guest", "guest"); + AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Queue queue = + new AMQQueue(con.getDefaultQueueExchangeName(), new AMQShortString("someQ"), new AMQShortString("someQ"), false, + true); + MessageConsumer consumer = consumerSession.createConsumer(queue); + + Connection con2 = (AMQConnection) getConnection("guest", "guest"); + Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); + MessageProducer producer = producerSession.createProducer(queue); + + TextMessage sentMsg = producerSession.createTextMessage("hello"); + assertNull(sentMsg.getJMSDestination()); + + producer.send(sentMsg); + + assertEquals(sentMsg.getJMSDestination(), queue); + + con2.close(); + + con.start(); + + TextMessage rm = (TextMessage) consumer.receive(); + assertNotNull(rm); + + assertEquals(rm.getJMSDestination(), queue); + con.close(); + } + +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java b/qpid/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java index b31765ce66..0eefc03016 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java @@ -1,43 +1,43 @@ -/*
- *
- * 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;
-
-/**
- * AMQUnknownExchangeType represents coding error where unknown exchange type requested from exchange factory.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Represents unknown exchange type request.
- * <tr><td>
- *
- * @todo Not an AMQP exception as no status code.
- *
- * @todo Represent coding error, where unknown exchange type is requested by passing a string parameter. Use a type safe
- * enum for the exchange type, or replace with IllegalArgumentException. Should be runtime.
- */
-public class AMQUnknownExchangeType extends AMQException
-{
- public AMQUnknownExchangeType(String message, Throwable cause)
- {
- super(null, message, cause);
- }
-}
+/* + * + * 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; + +/** + * AMQUnknownExchangeType represents coding error where unknown exchange type requested from exchange factory. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represents unknown exchange type request. + * <tr><td> + * + * @todo Not an AMQP exception as no status code. + * + * @todo Represent coding error, where unknown exchange type is requested by passing a string parameter. Use a type safe + * enum for the exchange type, or replace with IllegalArgumentException. Should be runtime. + */ +public class AMQUnknownExchangeType extends AMQException +{ + public AMQUnknownExchangeType(String message, Throwable cause) + { + super(null, message, cause); + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java index 64af717342..ad7f36f790 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java @@ -1,96 +1,96 @@ -package org.apache.qpid.framing;
-
-/*
- *
- * 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.
- *
- */
-
-import org.apache.mina.common.ByteBuffer;
-import org.apache.qpid.AMQChannelException;
-import org.apache.qpid.AMQConnectionException;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.protocol.AMQConstant;
-import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
-
-public abstract class AMQMethodBodyImpl implements AMQMethodBody
-{
- public static final byte TYPE = 1;
-
- public AMQMethodBodyImpl()
- {
- }
-
- public byte getFrameType()
- {
- return TYPE;
- }
-
-
- /** unsigned short */
- abstract protected int getBodySize();
-
-
- public AMQFrame generateFrame(int channelId)
- {
- return new AMQFrame(channelId, this);
- }
-
- /**
- * Creates an AMQChannelException for the corresponding body type (a channel exception should include the class and
- * method ids of the body it resulted from).
- */
-
- /**
- * Convenience Method to create a channel not found exception
- *
- * @param channelId The channel id that is not found
- *
- * @return new AMQChannelException
- */
- public AMQChannelException getChannelNotFoundException(int channelId)
- {
- return getChannelException(AMQConstant.NOT_FOUND, "Channel not found for id:" + channelId);
- }
-
- public AMQChannelException getChannelException(AMQConstant code, String message)
- {
- return new AMQChannelException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), null);
- }
-
- public AMQChannelException getChannelException(AMQConstant code, String message, Throwable cause)
- {
- return new AMQChannelException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), cause);
- }
-
- public AMQConnectionException getConnectionException(AMQConstant code, String message)
- {
- return new AMQConnectionException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), null);
- }
-
- public AMQConnectionException getConnectionException(AMQConstant code, String message, Throwable cause)
- {
- return new AMQConnectionException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), cause);
- }
-
- public void handle(final int channelId, final AMQVersionAwareProtocolSession session) throws AMQException
- {
- session.methodFrameReceived(channelId, this);
- }
-
-}
+package org.apache.qpid.framing; + +/* + * + * 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. + * + */ + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQChannelException; +import org.apache.qpid.AMQConnectionException; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; + +public abstract class AMQMethodBodyImpl implements AMQMethodBody +{ + public static final byte TYPE = 1; + + public AMQMethodBodyImpl() + { + } + + public byte getFrameType() + { + return TYPE; + } + + + /** unsigned short */ + abstract protected int getBodySize(); + + + public AMQFrame generateFrame(int channelId) + { + return new AMQFrame(channelId, this); + } + + /** + * Creates an AMQChannelException for the corresponding body type (a channel exception should include the class and + * method ids of the body it resulted from). + */ + + /** + * Convenience Method to create a channel not found exception + * + * @param channelId The channel id that is not found + * + * @return new AMQChannelException + */ + public AMQChannelException getChannelNotFoundException(int channelId) + { + return getChannelException(AMQConstant.NOT_FOUND, "Channel not found for id:" + channelId); + } + + public AMQChannelException getChannelException(AMQConstant code, String message) + { + return new AMQChannelException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), null); + } + + public AMQChannelException getChannelException(AMQConstant code, String message, Throwable cause) + { + return new AMQChannelException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), cause); + } + + public AMQConnectionException getConnectionException(AMQConstant code, String message) + { + return new AMQConnectionException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), null); + } + + public AMQConnectionException getConnectionException(AMQConstant code, String message, Throwable cause) + { + return new AMQConnectionException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), cause); + } + + public void handle(final int channelId, final AMQVersionAwareProtocolSession session) throws AMQException + { + session.methodFrameReceived(channelId, this); + } + +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java index 0030742e94..0c61d9db3c 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java @@ -1,30 +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.framing;
-
-import org.apache.mina.common.ByteBuffer;
-
-
-public abstract interface AMQMethodBodyInstanceFactory
-{
- public AMQMethodBody newInstance(ByteBuffer buffer, long size) throws AMQFrameDecodingException;
-}
+/* + * + * 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; + + +public abstract interface AMQMethodBodyInstanceFactory +{ + public AMQMethodBody newInstance(ByteBuffer buffer, long size) throws AMQFrameDecodingException; +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodFactory.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodFactory.java index 4ffc9e0066..bfcc38ad60 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodFactory.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodFactory.java @@ -1,90 +1,90 @@ -/*
- *
- * 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;
-
-
-public interface AMQMethodFactory
-{
-
- // Connection Methods
-
- ConnectionCloseBody createConnectionClose();
-
- // Access Methods
-
- AccessRequestBody createAccessRequest(boolean active, boolean exclusive, boolean passive, boolean read, AMQShortString realm, boolean write);
-
-
- // Tx Methods
-
- TxSelectBody createTxSelect();
-
- TxCommitBody createTxCommit();
-
- TxRollbackBody createTxRollback();
-
- // Channel Methods
-
- ChannelOpenBody createChannelOpen();
-
- ChannelCloseBody createChannelClose(int replyCode, AMQShortString replyText);
-
- ChannelFlowBody createChannelFlow(boolean active);
-
-
- // Exchange Methods
-
-
- ExchangeBoundBody createExchangeBound(AMQShortString exchangeName,
- AMQShortString queueName,
- AMQShortString routingKey);
-
- ExchangeDeclareBody createExchangeDeclare(AMQShortString name, AMQShortString type, int ticket);
-
-
- // Queue Methods
-
- QueueDeclareBody createQueueDeclare(AMQShortString name, FieldTable arguments, boolean autoDelete, boolean durable, boolean exclusive, boolean passive, int ticket);
-
- QueueBindBody createQueueBind(AMQShortString queueName, AMQShortString exchangeName, AMQShortString routingKey, FieldTable arguments, int ticket);
-
- QueueDeleteBody createQueueDelete(AMQShortString queueName, boolean ifEmpty, boolean ifUnused, int ticket);
-
-
- // Message Methods
-
- // In different versions of the protocol we change the class used for message transfer
- // abstract this out so the appropriate methods are created
- AMQMethodBody createRecover(boolean requeue);
-
- AMQMethodBody createConsumer(AMQShortString tag, AMQShortString queueName, FieldTable arguments, boolean noAck, boolean exclusive, boolean noLocal, int ticket);
-
- AMQMethodBody createConsumerCancel(AMQShortString consumerTag);
-
- AMQMethodBody createAcknowledge(long deliveryTag, boolean multiple);
-
- AMQMethodBody createRejectBody(long deliveryTag, boolean requeue);
-
- AMQMethodBody createMessageQos(int prefetchCount, int prefetchSize);
-
-}
+/* + * + * 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; + + +public interface AMQMethodFactory +{ + + // Connection Methods + + ConnectionCloseBody createConnectionClose(); + + // Access Methods + + AccessRequestBody createAccessRequest(boolean active, boolean exclusive, boolean passive, boolean read, AMQShortString realm, boolean write); + + + // Tx Methods + + TxSelectBody createTxSelect(); + + TxCommitBody createTxCommit(); + + TxRollbackBody createTxRollback(); + + // Channel Methods + + ChannelOpenBody createChannelOpen(); + + ChannelCloseBody createChannelClose(int replyCode, AMQShortString replyText); + + ChannelFlowBody createChannelFlow(boolean active); + + + // Exchange Methods + + + ExchangeBoundBody createExchangeBound(AMQShortString exchangeName, + AMQShortString queueName, + AMQShortString routingKey); + + ExchangeDeclareBody createExchangeDeclare(AMQShortString name, AMQShortString type, int ticket); + + + // Queue Methods + + QueueDeclareBody createQueueDeclare(AMQShortString name, FieldTable arguments, boolean autoDelete, boolean durable, boolean exclusive, boolean passive, int ticket); + + QueueBindBody createQueueBind(AMQShortString queueName, AMQShortString exchangeName, AMQShortString routingKey, FieldTable arguments, int ticket); + + QueueDeleteBody createQueueDelete(AMQShortString queueName, boolean ifEmpty, boolean ifUnused, int ticket); + + + // Message Methods + + // In different versions of the protocol we change the class used for message transfer + // abstract this out so the appropriate methods are created + AMQMethodBody createRecover(boolean requeue); + + AMQMethodBody createConsumer(AMQShortString tag, AMQShortString queueName, FieldTable arguments, boolean noAck, boolean exclusive, boolean noLocal, int ticket); + + AMQMethodBody createConsumerCancel(AMQShortString consumerTag); + + AMQMethodBody createAcknowledge(long deliveryTag, boolean multiple); + + AMQMethodBody createRejectBody(long deliveryTag, boolean requeue); + + AMQMethodBody createMessageQos(int prefetchCount, int prefetchSize); + +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQType.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQType.java index 2c356d072c..14fb63da03 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQType.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQType.java @@ -1,795 +1,795 @@ -/*
- *
- * 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 java.math.BigDecimal;
-
-/**
- * AMQType is a type that represents the different possible AMQP field table types. It provides operations for each
- * of the types to perform tasks such as calculating the size of an instance of the type, converting types between AMQP
- * and Java native types, and reading and writing instances of AMQP types in binary formats to and from byte buffers.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Get the equivalent one byte identifier for a type.
- * <tr><td> Calculate the size of an instance of an AMQP parameter type. <td> {@link EncodingUtils}
- * <tr><td> Convert an instance of an AMQP parameter into a compatable Java object tagged with its AMQP type.
- * <td> {@link AMQTypedValue}
- * <tr><td> Write an instance of an AMQP parameter type to a byte buffer. <td> {@link EncodingUtils}
- * <tr><td> Read an instance of an AMQP parameter from a byte buffer. <td> {@link EncodingUtils}
- * </table>
- */
-public enum AMQType
-{
- LONG_STRING('S')
- {
- public int getEncodingSize(Object value)
- {
- return EncodingUtils.encodedLongStringLength((String) value);
- }
-
- public String toNativeValue(Object value)
- {
- if (value != null)
- {
- return value.toString();
- }
- else
- {
- throw new NullPointerException("Cannot convert: null to String.");
- }
- }
-
- public void writeValueImpl(Object value, ByteBuffer buffer)
- {
- EncodingUtils.writeLongStringBytes(buffer, (String) value);
- }
-
- public Object readValueFromBuffer(ByteBuffer buffer)
- {
- return EncodingUtils.readLongString(buffer);
- }
- },
-
- INTEGER('i')
- {
- public int getEncodingSize(Object value)
- {
- return EncodingUtils.unsignedIntegerLength();
- }
-
- public Long toNativeValue(Object value)
- {
- if (value instanceof Long)
- {
- return (Long) value;
- }
- else if (value instanceof Integer)
- {
- return ((Integer) value).longValue();
- }
- else if (value instanceof Short)
- {
- return ((Short) value).longValue();
- }
- else if (value instanceof Byte)
- {
- return ((Byte) value).longValue();
- }
- else if ((value instanceof String) || (value == null))
- {
- return Long.valueOf((String) value);
- }
- else
- {
- throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + ") to int.");
- }
- }
-
- public void writeValueImpl(Object value, ByteBuffer buffer)
- {
- EncodingUtils.writeUnsignedInteger(buffer, (Long) value);
- }
-
- public Object readValueFromBuffer(ByteBuffer buffer)
- {
- return EncodingUtils.readUnsignedInteger(buffer);
- }
- },
-
- DECIMAL('D')
- {
- public int getEncodingSize(Object value)
- {
- return EncodingUtils.encodedByteLength() + EncodingUtils.encodedIntegerLength();
- }
-
- public Object toNativeValue(Object value)
- {
- if (value instanceof BigDecimal)
- {
- return (BigDecimal) value;
- }
- else
- {
- throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
- + ") to BigDecimal.");
- }
- }
-
- public void writeValueImpl(Object value, ByteBuffer buffer)
- {
- BigDecimal bd = (BigDecimal) value;
-
- byte places = new Integer(bd.scale()).byteValue();
-
- int unscaled = bd.intValue();
-
- EncodingUtils.writeByte(buffer, places);
-
- EncodingUtils.writeInteger(buffer, unscaled);
- }
-
- public Object readValueFromBuffer(ByteBuffer buffer)
- {
- byte places = EncodingUtils.readByte(buffer);
-
- int unscaled = EncodingUtils.readInteger(buffer);
-
- BigDecimal bd = new BigDecimal(unscaled);
-
- return bd.setScale(places);
- }
- },
-
- TIMESTAMP('T')
- {
- public int getEncodingSize(Object value)
- {
- return EncodingUtils.encodedLongLength();
- }
-
- public Object toNativeValue(Object value)
- {
- if (value instanceof Long)
- {
- return (Long) value;
- }
- else
- {
- throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
- + ") to timestamp.");
- }
- }
-
- public void writeValueImpl(Object value, ByteBuffer buffer)
- {
- EncodingUtils.writeLong(buffer, (Long) value);
- }
-
- public Object readValueFromBuffer(ByteBuffer buffer)
- {
- return EncodingUtils.readLong(buffer);
- }
- },
-
- /**
- * Implements the field table type. The native value of a field table type will be an instance of
- * {@link FieldTable}, which itself may contain name/value pairs encoded as {@link AMQTypedValue}s.
- */
- FIELD_TABLE('F')
- {
- /**
- * Calculates the size of an instance of the type in bytes.
- *
- * @param value An instance of the type.
- *
- * @return The size of the instance of the type in bytes.
- */
- public int getEncodingSize(Object value)
- {
- // Ensure that the value is a FieldTable.
- if (!(value instanceof FieldTable))
- {
- throw new IllegalArgumentException("Value is not a FieldTable.");
- }
-
- FieldTable ftValue = (FieldTable) value;
-
- // Loop over all name/value pairs adding up size of each. FieldTable itself keeps track of its encoded
- // size as entries are added, so no need to loop over all explicitly.
- // EncodingUtils calculation of the encoded field table lenth, will include 4 bytes for its 'size' field.
- return EncodingUtils.encodedFieldTableLength(ftValue);
- }
-
- /**
- * Converts an instance of the type to an equivalent Java native representation.
- *
- * @param value An instance of the type.
- *
- * @return An equivalent Java native representation.
- */
- public Object toNativeValue(Object value)
- {
- // Ensure that the value is a FieldTable.
- if (!(value instanceof FieldTable))
- {
- throw new IllegalArgumentException("Value is not a FieldTable.");
- }
-
- return (FieldTable) value;
- }
-
- /**
- * Writes an instance of the type to a specified byte buffer.
- *
- * @param value An instance of the type.
- * @param buffer The byte buffer to write it to.
- */
- public void writeValueImpl(Object value, ByteBuffer buffer)
- {
- // Ensure that the value is a FieldTable.
- if (!(value instanceof FieldTable))
- {
- throw new IllegalArgumentException("Value is not a FieldTable.");
- }
-
- FieldTable ftValue = (FieldTable) value;
-
- // Loop over all name/values writing out into buffer.
- ftValue.writeToBuffer(buffer);
- }
-
- /**
- * Reads an instance of the type from a specified byte buffer.
- *
- * @param buffer The byte buffer to write it to.
- *
- * @return An instance of the type.
- */
- public Object readValueFromBuffer(ByteBuffer buffer)
- {
- try
- {
- // Read size of field table then all name/value pairs.
- return EncodingUtils.readFieldTable(buffer);
- }
- catch (AMQFrameDecodingException e)
- {
- throw new IllegalArgumentException("Unable to read field table from buffer.", e);
- }
- }
- },
-
- VOID('V')
- {
- public int getEncodingSize(Object value)
- {
- return 0;
- }
-
- public Object toNativeValue(Object value)
- {
- if (value == null)
- {
- return null;
- }
- else
- {
- throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
- + ") to null String.");
- }
- }
-
- public void writeValueImpl(Object value, ByteBuffer buffer)
- { }
-
- public Object readValueFromBuffer(ByteBuffer buffer)
- {
- return null;
- }
- },
-
- BINARY('x')
- {
- public int getEncodingSize(Object value)
- {
- return EncodingUtils.encodedLongstrLength((byte[]) value);
- }
-
- public Object toNativeValue(Object value)
- {
- if ((value instanceof byte[]) || (value == null))
- {
- return value;
- }
- else
- {
- throw new IllegalArgumentException("Value: " + value + " (" + value.getClass().getName()
- + ") cannot be converted to byte[]");
- }
- }
-
- public void writeValueImpl(Object value, ByteBuffer buffer)
- {
- EncodingUtils.writeLongstr(buffer, (byte[]) value);
- }
-
- public Object readValueFromBuffer(ByteBuffer buffer)
- {
- return EncodingUtils.readLongstr(buffer);
- }
- },
-
- ASCII_STRING('c')
- {
- public int getEncodingSize(Object value)
- {
- return EncodingUtils.encodedLongStringLength((String) value);
- }
-
- public String toNativeValue(Object value)
- {
- if (value != null)
- {
- return value.toString();
- }
- else
- {
- throw new NullPointerException("Cannot convert: null to String.");
- }
- }
-
- public void writeValueImpl(Object value, ByteBuffer buffer)
- {
- EncodingUtils.writeLongStringBytes(buffer, (String) value);
- }
-
- public Object readValueFromBuffer(ByteBuffer buffer)
- {
- return EncodingUtils.readLongString(buffer);
- }
- },
-
- WIDE_STRING('C')
- {
- public int getEncodingSize(Object value)
- {
- // FIXME: use proper charset encoder
- return EncodingUtils.encodedLongStringLength((String) value);
- }
-
- public String toNativeValue(Object value)
- {
- if (value != null)
- {
- return value.toString();
- }
- else
- {
- throw new NullPointerException("Cannot convert: null to String.");
- }
- }
-
- public void writeValueImpl(Object value, ByteBuffer buffer)
- {
- EncodingUtils.writeLongStringBytes(buffer, (String) value);
- }
-
- public Object readValueFromBuffer(ByteBuffer buffer)
- {
- return EncodingUtils.readLongString(buffer);
- }
- },
-
- BOOLEAN('t')
- {
- public int getEncodingSize(Object value)
- {
- return EncodingUtils.encodedBooleanLength();
- }
-
- public Object toNativeValue(Object value)
- {
- if (value instanceof Boolean)
- {
- return (Boolean) value;
- }
- else if ((value instanceof String) || (value == null))
- {
- return Boolean.valueOf((String) value);
- }
- else
- {
- throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
- + ") to boolean.");
- }
- }
-
- public void writeValueImpl(Object value, ByteBuffer buffer)
- {
- EncodingUtils.writeBoolean(buffer, (Boolean) value);
- }
-
- public Object readValueFromBuffer(ByteBuffer buffer)
- {
- return EncodingUtils.readBoolean(buffer);
- }
- },
-
- ASCII_CHARACTER('k')
- {
- public int getEncodingSize(Object value)
- {
- return EncodingUtils.encodedCharLength();
- }
-
- public Character toNativeValue(Object value)
- {
- if (value instanceof Character)
- {
- return (Character) value;
- }
- else if (value == null)
- {
- throw new NullPointerException("Cannot convert null into char");
- }
- else
- {
- throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
- + ") to char.");
- }
- }
-
- public void writeValueImpl(Object value, ByteBuffer buffer)
- {
- EncodingUtils.writeChar(buffer, (Character) value);
- }
-
- public Object readValueFromBuffer(ByteBuffer buffer)
- {
- return EncodingUtils.readChar(buffer);
- }
- },
-
- BYTE('b')
- {
- public int getEncodingSize(Object value)
- {
- return EncodingUtils.encodedByteLength();
- }
-
- public Byte toNativeValue(Object value)
- {
- if (value instanceof Byte)
- {
- return (Byte) value;
- }
- else if ((value instanceof String) || (value == null))
- {
- return Byte.valueOf((String) value);
- }
- else
- {
- throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
- + ") to byte.");
- }
- }
-
- public void writeValueImpl(Object value, ByteBuffer buffer)
- {
- EncodingUtils.writeByte(buffer, (Byte) value);
- }
-
- public Object readValueFromBuffer(ByteBuffer buffer)
- {
- return EncodingUtils.readByte(buffer);
- }
- },
-
- SHORT('s')
- {
- public int getEncodingSize(Object value)
- {
- return EncodingUtils.encodedShortLength();
- }
-
- public Short toNativeValue(Object value)
- {
- if (value instanceof Short)
- {
- return (Short) value;
- }
- else if (value instanceof Byte)
- {
- return ((Byte) value).shortValue();
- }
- else if ((value instanceof String) || (value == null))
- {
- return Short.valueOf((String) value);
- }
- else
- {
- throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
- + ") to short.");
- }
- }
-
- public void writeValueImpl(Object value, ByteBuffer buffer)
- {
- EncodingUtils.writeShort(buffer, (Short) value);
- }
-
- public Object readValueFromBuffer(ByteBuffer buffer)
- {
- return EncodingUtils.readShort(buffer);
- }
- },
-
- INT('I')
- {
- public int getEncodingSize(Object value)
- {
- return EncodingUtils.encodedIntegerLength();
- }
-
- public Integer toNativeValue(Object value)
- {
- if (value instanceof Integer)
- {
- return (Integer) value;
- }
- else if (value instanceof Short)
- {
- return ((Short) value).intValue();
- }
- else if (value instanceof Byte)
- {
- return ((Byte) value).intValue();
- }
- else if ((value instanceof String) || (value == null))
- {
- return Integer.valueOf((String) value);
- }
- else
- {
- throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + ") to int.");
- }
- }
-
- public void writeValueImpl(Object value, ByteBuffer buffer)
- {
- EncodingUtils.writeInteger(buffer, (Integer) value);
- }
-
- public Object readValueFromBuffer(ByteBuffer buffer)
- {
- return EncodingUtils.readInteger(buffer);
- }
- },
-
- LONG('l')
- {
- public int getEncodingSize(Object value)
- {
- return EncodingUtils.encodedLongLength();
- }
-
- public Object toNativeValue(Object value)
- {
- if (value instanceof Long)
- {
- return (Long) value;
- }
- else if (value instanceof Integer)
- {
- return ((Integer) value).longValue();
- }
- else if (value instanceof Short)
- {
- return ((Short) value).longValue();
- }
- else if (value instanceof Byte)
- {
- return ((Byte) value).longValue();
- }
- else if ((value instanceof String) || (value == null))
- {
- return Long.valueOf((String) value);
- }
- else
- {
- throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
- + ") to long.");
- }
- }
-
- public void writeValueImpl(Object value, ByteBuffer buffer)
- {
- EncodingUtils.writeLong(buffer, (Long) value);
- }
-
- public Object readValueFromBuffer(ByteBuffer buffer)
- {
- return EncodingUtils.readLong(buffer);
- }
- },
-
- FLOAT('f')
- {
- public int getEncodingSize(Object value)
- {
- return EncodingUtils.encodedFloatLength();
- }
-
- public Float toNativeValue(Object value)
- {
- if (value instanceof Float)
- {
- return (Float) value;
- }
- else if ((value instanceof String) || (value == null))
- {
- return Float.valueOf((String) value);
- }
- else
- {
- throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
- + ") to float.");
- }
- }
-
- public void writeValueImpl(Object value, ByteBuffer buffer)
- {
- EncodingUtils.writeFloat(buffer, (Float) value);
- }
-
- public Object readValueFromBuffer(ByteBuffer buffer)
- {
- return EncodingUtils.readFloat(buffer);
- }
- },
-
- DOUBLE('d')
- {
- public int getEncodingSize(Object value)
- {
- return EncodingUtils.encodedDoubleLength();
- }
-
- public Double toNativeValue(Object value)
- {
- if (value instanceof Double)
- {
- return (Double) value;
- }
- else if (value instanceof Float)
- {
- return ((Float) value).doubleValue();
- }
- else if ((value instanceof String) || (value == null))
- {
- return Double.valueOf((String) value);
- }
- else
- {
- throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
- + ") to double.");
- }
- }
-
- public void writeValueImpl(Object value, ByteBuffer buffer)
- {
- EncodingUtils.writeDouble(buffer, (Double) value);
- }
-
- public Object readValueFromBuffer(ByteBuffer buffer)
- {
- return EncodingUtils.readDouble(buffer);
- }
- };
-
- /** Holds the defined one byte identifier for the type. */
- private final byte _identifier;
-
- /**
- * Creates an instance of an AMQP type from its defined one byte identifier.
- *
- * @param identifier The one byte identifier for the type.
- */
- AMQType(char identifier)
- {
- _identifier = (byte) identifier;
- }
-
- /**
- * Extracts the byte identifier for the typ.
- *
- * @return The byte identifier for the typ.
- */
- public final byte identifier()
- {
- return _identifier;
- }
-
- /**
- * Calculates the size of an instance of the type in bytes.
- *
- * @param value An instance of the type.
- *
- * @return The size of the instance of the type in bytes.
- */
- public abstract int getEncodingSize(Object value);
-
- /**
- * Converts an instance of the type to an equivalent Java native representation.
- *
- * @param value An instance of the type.
- *
- * @return An equivalent Java native representation.
- */
- public abstract Object toNativeValue(Object value);
-
- /**
- * Converts an instance of the type to an equivalent Java native representation, packaged as an
- * {@link AMQTypedValue} tagged with its AMQP type.
- *
- * @param value An instance of the type.
- *
- * @return An equivalent Java native representation, tagged with its AMQP type.
- */
- public AMQTypedValue asTypedValue(Object value)
- {
- return new AMQTypedValue(this, toNativeValue(value));
- }
-
- /**
- * Writes an instance of the type to a specified byte buffer, preceded by its one byte identifier. As the type and
- * value are both written, this provides a fully encoded description of a parameters type and value.
- *
- * @param value An instance of the type.
- * @param buffer The byte buffer to write it to.
- */
- public void writeToBuffer(Object value, ByteBuffer buffer)
- {
- buffer.put(identifier());
- writeValueImpl(value, buffer);
- }
-
- /**
- * Writes an instance of the type to a specified byte buffer.
- *
- * @param value An instance of the type.
- * @param buffer The byte buffer to write it to.
- */
- abstract void writeValueImpl(Object value, ByteBuffer buffer);
-
- /**
- * Reads an instance of the type from a specified byte buffer.
- *
- * @param buffer The byte buffer to write it to.
- *
- * @return An instance of the type.
- */
- abstract Object readValueFromBuffer(ByteBuffer buffer);
-}
+/* + * + * 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 java.math.BigDecimal; + +/** + * AMQType is a type that represents the different possible AMQP field table types. It provides operations for each + * of the types to perform tasks such as calculating the size of an instance of the type, converting types between AMQP + * and Java native types, and reading and writing instances of AMQP types in binary formats to and from byte buffers. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Get the equivalent one byte identifier for a type. + * <tr><td> Calculate the size of an instance of an AMQP parameter type. <td> {@link EncodingUtils} + * <tr><td> Convert an instance of an AMQP parameter into a compatable Java object tagged with its AMQP type. + * <td> {@link AMQTypedValue} + * <tr><td> Write an instance of an AMQP parameter type to a byte buffer. <td> {@link EncodingUtils} + * <tr><td> Read an instance of an AMQP parameter from a byte buffer. <td> {@link EncodingUtils} + * </table> + */ +public enum AMQType +{ + LONG_STRING('S') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedLongStringLength((String) value); + } + + public String toNativeValue(Object value) + { + if (value != null) + { + return value.toString(); + } + else + { + throw new NullPointerException("Cannot convert: null to String."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeLongStringBytes(buffer, (String) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readLongString(buffer); + } + }, + + INTEGER('i') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.unsignedIntegerLength(); + } + + public Long toNativeValue(Object value) + { + if (value instanceof Long) + { + return (Long) value; + } + else if (value instanceof Integer) + { + return ((Integer) value).longValue(); + } + else if (value instanceof Short) + { + return ((Short) value).longValue(); + } + else if (value instanceof Byte) + { + return ((Byte) value).longValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Long.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + ") to int."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeUnsignedInteger(buffer, (Long) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readUnsignedInteger(buffer); + } + }, + + DECIMAL('D') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedByteLength() + EncodingUtils.encodedIntegerLength(); + } + + public Object toNativeValue(Object value) + { + if (value instanceof BigDecimal) + { + return (BigDecimal) value; + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to BigDecimal."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + BigDecimal bd = (BigDecimal) value; + + byte places = new Integer(bd.scale()).byteValue(); + + int unscaled = bd.intValue(); + + EncodingUtils.writeByte(buffer, places); + + EncodingUtils.writeInteger(buffer, unscaled); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + byte places = EncodingUtils.readByte(buffer); + + int unscaled = EncodingUtils.readInteger(buffer); + + BigDecimal bd = new BigDecimal(unscaled); + + return bd.setScale(places); + } + }, + + TIMESTAMP('T') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedLongLength(); + } + + public Object toNativeValue(Object value) + { + if (value instanceof Long) + { + return (Long) value; + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to timestamp."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeLong(buffer, (Long) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readLong(buffer); + } + }, + + /** + * Implements the field table type. The native value of a field table type will be an instance of + * {@link FieldTable}, which itself may contain name/value pairs encoded as {@link AMQTypedValue}s. + */ + FIELD_TABLE('F') + { + /** + * Calculates the size of an instance of the type in bytes. + * + * @param value An instance of the type. + * + * @return The size of the instance of the type in bytes. + */ + public int getEncodingSize(Object value) + { + // Ensure that the value is a FieldTable. + if (!(value instanceof FieldTable)) + { + throw new IllegalArgumentException("Value is not a FieldTable."); + } + + FieldTable ftValue = (FieldTable) value; + + // Loop over all name/value pairs adding up size of each. FieldTable itself keeps track of its encoded + // size as entries are added, so no need to loop over all explicitly. + // EncodingUtils calculation of the encoded field table lenth, will include 4 bytes for its 'size' field. + return EncodingUtils.encodedFieldTableLength(ftValue); + } + + /** + * Converts an instance of the type to an equivalent Java native representation. + * + * @param value An instance of the type. + * + * @return An equivalent Java native representation. + */ + public Object toNativeValue(Object value) + { + // Ensure that the value is a FieldTable. + if (!(value instanceof FieldTable)) + { + throw new IllegalArgumentException("Value is not a FieldTable."); + } + + return (FieldTable) value; + } + + /** + * Writes an instance of the type to a specified byte buffer. + * + * @param value An instance of the type. + * @param buffer The byte buffer to write it to. + */ + public void writeValueImpl(Object value, ByteBuffer buffer) + { + // Ensure that the value is a FieldTable. + if (!(value instanceof FieldTable)) + { + throw new IllegalArgumentException("Value is not a FieldTable."); + } + + FieldTable ftValue = (FieldTable) value; + + // Loop over all name/values writing out into buffer. + ftValue.writeToBuffer(buffer); + } + + /** + * Reads an instance of the type from a specified byte buffer. + * + * @param buffer The byte buffer to write it to. + * + * @return An instance of the type. + */ + public Object readValueFromBuffer(ByteBuffer buffer) + { + try + { + // Read size of field table then all name/value pairs. + return EncodingUtils.readFieldTable(buffer); + } + catch (AMQFrameDecodingException e) + { + throw new IllegalArgumentException("Unable to read field table from buffer.", e); + } + } + }, + + VOID('V') + { + public int getEncodingSize(Object value) + { + return 0; + } + + public Object toNativeValue(Object value) + { + if (value == null) + { + return null; + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to null String."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return null; + } + }, + + BINARY('x') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedLongstrLength((byte[]) value); + } + + public Object toNativeValue(Object value) + { + if ((value instanceof byte[]) || (value == null)) + { + return value; + } + else + { + throw new IllegalArgumentException("Value: " + value + " (" + value.getClass().getName() + + ") cannot be converted to byte[]"); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeLongstr(buffer, (byte[]) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readLongstr(buffer); + } + }, + + ASCII_STRING('c') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedLongStringLength((String) value); + } + + public String toNativeValue(Object value) + { + if (value != null) + { + return value.toString(); + } + else + { + throw new NullPointerException("Cannot convert: null to String."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeLongStringBytes(buffer, (String) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readLongString(buffer); + } + }, + + WIDE_STRING('C') + { + public int getEncodingSize(Object value) + { + // FIXME: use proper charset encoder + return EncodingUtils.encodedLongStringLength((String) value); + } + + public String toNativeValue(Object value) + { + if (value != null) + { + return value.toString(); + } + else + { + throw new NullPointerException("Cannot convert: null to String."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeLongStringBytes(buffer, (String) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readLongString(buffer); + } + }, + + BOOLEAN('t') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedBooleanLength(); + } + + public Object toNativeValue(Object value) + { + if (value instanceof Boolean) + { + return (Boolean) value; + } + else if ((value instanceof String) || (value == null)) + { + return Boolean.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to boolean."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeBoolean(buffer, (Boolean) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readBoolean(buffer); + } + }, + + ASCII_CHARACTER('k') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedCharLength(); + } + + public Character toNativeValue(Object value) + { + if (value instanceof Character) + { + return (Character) value; + } + else if (value == null) + { + throw new NullPointerException("Cannot convert null into char"); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to char."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeChar(buffer, (Character) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readChar(buffer); + } + }, + + BYTE('b') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedByteLength(); + } + + public Byte toNativeValue(Object value) + { + if (value instanceof Byte) + { + return (Byte) value; + } + else if ((value instanceof String) || (value == null)) + { + return Byte.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to byte."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeByte(buffer, (Byte) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readByte(buffer); + } + }, + + SHORT('s') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedShortLength(); + } + + public Short toNativeValue(Object value) + { + if (value instanceof Short) + { + return (Short) value; + } + else if (value instanceof Byte) + { + return ((Byte) value).shortValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Short.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to short."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeShort(buffer, (Short) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readShort(buffer); + } + }, + + INT('I') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedIntegerLength(); + } + + public Integer toNativeValue(Object value) + { + if (value instanceof Integer) + { + return (Integer) value; + } + else if (value instanceof Short) + { + return ((Short) value).intValue(); + } + else if (value instanceof Byte) + { + return ((Byte) value).intValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Integer.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + ") to int."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeInteger(buffer, (Integer) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readInteger(buffer); + } + }, + + LONG('l') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedLongLength(); + } + + public Object toNativeValue(Object value) + { + if (value instanceof Long) + { + return (Long) value; + } + else if (value instanceof Integer) + { + return ((Integer) value).longValue(); + } + else if (value instanceof Short) + { + return ((Short) value).longValue(); + } + else if (value instanceof Byte) + { + return ((Byte) value).longValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Long.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to long."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeLong(buffer, (Long) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readLong(buffer); + } + }, + + FLOAT('f') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedFloatLength(); + } + + public Float toNativeValue(Object value) + { + if (value instanceof Float) + { + return (Float) value; + } + else if ((value instanceof String) || (value == null)) + { + return Float.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to float."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeFloat(buffer, (Float) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readFloat(buffer); + } + }, + + DOUBLE('d') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedDoubleLength(); + } + + public Double toNativeValue(Object value) + { + if (value instanceof Double) + { + return (Double) value; + } + else if (value instanceof Float) + { + return ((Float) value).doubleValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Double.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to double."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeDouble(buffer, (Double) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readDouble(buffer); + } + }; + + /** Holds the defined one byte identifier for the type. */ + private final byte _identifier; + + /** + * Creates an instance of an AMQP type from its defined one byte identifier. + * + * @param identifier The one byte identifier for the type. + */ + AMQType(char identifier) + { + _identifier = (byte) identifier; + } + + /** + * Extracts the byte identifier for the typ. + * + * @return The byte identifier for the typ. + */ + public final byte identifier() + { + return _identifier; + } + + /** + * Calculates the size of an instance of the type in bytes. + * + * @param value An instance of the type. + * + * @return The size of the instance of the type in bytes. + */ + public abstract int getEncodingSize(Object value); + + /** + * Converts an instance of the type to an equivalent Java native representation. + * + * @param value An instance of the type. + * + * @return An equivalent Java native representation. + */ + public abstract Object toNativeValue(Object value); + + /** + * Converts an instance of the type to an equivalent Java native representation, packaged as an + * {@link AMQTypedValue} tagged with its AMQP type. + * + * @param value An instance of the type. + * + * @return An equivalent Java native representation, tagged with its AMQP type. + */ + public AMQTypedValue asTypedValue(Object value) + { + return new AMQTypedValue(this, toNativeValue(value)); + } + + /** + * Writes an instance of the type to a specified byte buffer, preceded by its one byte identifier. As the type and + * value are both written, this provides a fully encoded description of a parameters type and value. + * + * @param value An instance of the type. + * @param buffer The byte buffer to write it to. + */ + public void writeToBuffer(Object value, ByteBuffer buffer) + { + buffer.put(identifier()); + writeValueImpl(value, buffer); + } + + /** + * Writes an instance of the type to a specified byte buffer. + * + * @param value An instance of the type. + * @param buffer The byte buffer to write it to. + */ + abstract void writeValueImpl(Object value, ByteBuffer buffer); + + /** + * Reads an instance of the type from a specified byte buffer. + * + * @param buffer The byte buffer to write it to. + * + * @return An instance of the type. + */ + abstract Object readValueFromBuffer(ByteBuffer buffer); +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java index 1419dd75b1..a16e137466 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java @@ -1,48 +1,48 @@ -/*
- *
- * 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 java.util.HashMap;
-import java.util.Map;
-
-public class AMQTypeMap
-{
- public static Map<Byte, AMQType> _reverseTypeMap = new HashMap<Byte, AMQType>();
-
- static
- {
- for(AMQType type : AMQType.values())
- {
- _reverseTypeMap.put(type.identifier(), type);
- }
- }
-
- public static AMQType getType(Byte identifier)
- {
- AMQType result = _reverseTypeMap.get(identifier);
- if (result == null) {
- throw new IllegalArgumentException
- ("no such type code: " + Integer.toHexString(identifier.intValue()));
- }
- return result;
- }
-
-}
+/* + * + * 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 java.util.HashMap; +import java.util.Map; + +public class AMQTypeMap +{ + public static Map<Byte, AMQType> _reverseTypeMap = new HashMap<Byte, AMQType>(); + + static + { + for(AMQType type : AMQType.values()) + { + _reverseTypeMap.put(type.identifier(), type); + } + } + + public static AMQType getType(Byte identifier) + { + AMQType result = _reverseTypeMap.get(identifier); + if (result == null) { + throw new IllegalArgumentException + ("no such type code: " + Integer.toHexString(identifier.intValue())); + } + return result; + } + +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java index e5b1fad9a8..d6359baa0f 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java @@ -1,96 +1,96 @@ -/*
- *
- * 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;
-
-/**
- * AMQTypedValue combines together a native Java Object value, and an {@link AMQType}, as a fully typed AMQP parameter
- * value. It provides the ability to read and write fully typed parameters to and from byte buffers. It also provides
- * the ability to create such parameters from Java native value and a type tag or to extract the native value and type
- * from one.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Create a fully typed AMQP value from a native type and a type tag. <td> {@link AMQType}
- * <tr><td> Create a fully typed AMQP value from a binary representation in a byte buffer. <td> {@link AMQType}
- * <tr><td> Write a fully typed AMQP value to a binary representation in a byte buffer. <td> {@link AMQType}
- * <tr><td> Extract the type from a fully typed AMQP value.
- * <tr><td> Extract the value from a fully typed AMQP value.
- * </table>
- */
-public class AMQTypedValue
-{
- /** The type of the value. */
- private final AMQType _type;
-
- /** The Java native representation of the AMQP typed value. */
- private final Object _value;
-
- public AMQTypedValue(AMQType type, Object value)
- {
- if (type == null)
- {
- throw new NullPointerException("Cannot create a typed value with null type");
- }
-
- _type = type;
- _value = type.toNativeValue(value);
- }
-
- private AMQTypedValue(AMQType type, ByteBuffer buffer)
- {
- _type = type;
- _value = type.readValueFromBuffer(buffer);
- }
-
- public AMQType getType()
- {
- return _type;
- }
-
- public Object getValue()
- {
- return _value;
- }
-
- public void writeToBuffer(ByteBuffer buffer)
- {
- _type.writeToBuffer(_value, buffer);
- }
-
- public int getEncodingSize()
- {
- return _type.getEncodingSize(_value);
- }
-
- public static AMQTypedValue readFromBuffer(ByteBuffer buffer)
- {
- AMQType type = AMQTypeMap.getType(buffer.get());
-
- return new AMQTypedValue(type, buffer);
- }
-
- public String toString()
- {
- return "[" + getType() + ": " + getValue() + "]";
- }
-}
+/* + * + * 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; + +/** + * AMQTypedValue combines together a native Java Object value, and an {@link AMQType}, as a fully typed AMQP parameter + * value. It provides the ability to read and write fully typed parameters to and from byte buffers. It also provides + * the ability to create such parameters from Java native value and a type tag or to extract the native value and type + * from one. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Create a fully typed AMQP value from a native type and a type tag. <td> {@link AMQType} + * <tr><td> Create a fully typed AMQP value from a binary representation in a byte buffer. <td> {@link AMQType} + * <tr><td> Write a fully typed AMQP value to a binary representation in a byte buffer. <td> {@link AMQType} + * <tr><td> Extract the type from a fully typed AMQP value. + * <tr><td> Extract the value from a fully typed AMQP value. + * </table> + */ +public class AMQTypedValue +{ + /** The type of the value. */ + private final AMQType _type; + + /** The Java native representation of the AMQP typed value. */ + private final Object _value; + + public AMQTypedValue(AMQType type, Object value) + { + if (type == null) + { + throw new NullPointerException("Cannot create a typed value with null type"); + } + + _type = type; + _value = type.toNativeValue(value); + } + + private AMQTypedValue(AMQType type, ByteBuffer buffer) + { + _type = type; + _value = type.readValueFromBuffer(buffer); + } + + public AMQType getType() + { + return _type; + } + + public Object getValue() + { + return _value; + } + + public void writeToBuffer(ByteBuffer buffer) + { + _type.writeToBuffer(_value, buffer); + } + + public int getEncodingSize() + { + return _type.getEncodingSize(_value); + } + + public static AMQTypedValue readFromBuffer(ByteBuffer buffer) + { + AMQType type = AMQTypeMap.getType(buffer.get()); + + return new AMQTypedValue(type, buffer); + } + + public String toString() + { + return "[" + getType() + ": " + getValue() + "]"; + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java index 6a608a8bff..7162c37062 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java @@ -1,81 +1,81 @@ -/*
- *
- * 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;
-
-public interface CommonContentHeaderProperties extends ContentHeaderProperties
-{
- AMQShortString getContentType();
-
- void setContentType(AMQShortString contentType);
-
- FieldTable getHeaders();
-
- void setHeaders(FieldTable headers);
-
- byte getDeliveryMode();
-
- void setDeliveryMode(byte deliveryMode);
-
- byte getPriority();
-
- void setPriority(byte priority);
-
- AMQShortString getCorrelationId();
-
- void setCorrelationId(AMQShortString correlationId);
-
- AMQShortString getReplyTo();
-
- void setReplyTo(AMQShortString replyTo);
-
- long getExpiration();
-
- void setExpiration(long expiration);
-
- AMQShortString getMessageId();
-
- void setMessageId(AMQShortString messageId);
-
- long getTimestamp();
-
- void setTimestamp(long timestamp);
-
- AMQShortString getType();
-
- void setType(AMQShortString type);
-
- AMQShortString getUserId();
-
- void setUserId(AMQShortString userId);
-
- AMQShortString getAppId();
-
- void setAppId(AMQShortString appId);
-
- AMQShortString getClusterId();
-
- void setClusterId(AMQShortString clusterId);
-
- AMQShortString getEncoding();
-
- void setEncoding(AMQShortString encoding);
-}
+/* + * + * 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; + +public interface CommonContentHeaderProperties extends ContentHeaderProperties +{ + AMQShortString getContentType(); + + void setContentType(AMQShortString contentType); + + FieldTable getHeaders(); + + void setHeaders(FieldTable headers); + + byte getDeliveryMode(); + + void setDeliveryMode(byte deliveryMode); + + byte getPriority(); + + void setPriority(byte priority); + + AMQShortString getCorrelationId(); + + void setCorrelationId(AMQShortString correlationId); + + AMQShortString getReplyTo(); + + void setReplyTo(AMQShortString replyTo); + + long getExpiration(); + + void setExpiration(long expiration); + + AMQShortString getMessageId(); + + void setMessageId(AMQShortString messageId); + + long getTimestamp(); + + void setTimestamp(long timestamp); + + AMQShortString getType(); + + void setType(AMQShortString type); + + AMQShortString getUserId(); + + void setUserId(AMQShortString userId); + + AMQShortString getAppId(); + + void setAppId(AMQShortString appId); + + AMQShortString getClusterId(); + + void setClusterId(AMQShortString clusterId); + + AMQShortString getEncoding(); + + void setEncoding(AMQShortString encoding); +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java index f8cf3f3011..bd763599b0 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java @@ -1,98 +1,98 @@ -/*
- *
- * 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;
-
-public class SmallCompositeAMQDataBlock extends AMQDataBlock implements EncodableAMQDataBlock
-{
- private AMQDataBlock _firstFrame;
-
- private AMQDataBlock _block;
-
- public SmallCompositeAMQDataBlock(AMQDataBlock block)
- {
- _block = block;
- }
-
- /**
- * The encoded block will be logically first before the AMQDataBlocks which are encoded
- * into the buffer afterwards.
- * @param encodedBlock already-encoded data
- * @param block a block to be encoded.
- */
- public SmallCompositeAMQDataBlock(AMQDataBlock encodedBlock, AMQDataBlock block)
- {
- this(block);
- _firstFrame = encodedBlock;
- }
-
- public AMQDataBlock getBlock()
- {
- return _block;
- }
-
- public AMQDataBlock getFirstFrame()
- {
- return _firstFrame;
- }
-
- public long getSize()
- {
- long frameSize = _block.getSize();
-
- if (_firstFrame != null)
- {
-
- frameSize += _firstFrame.getSize();
- }
- return frameSize;
- }
-
- public void writePayload(ByteBuffer buffer)
- {
- if (_firstFrame != null)
- {
- _firstFrame.writePayload(buffer);
- }
- _block.writePayload(buffer);
-
- }
-
- public String toString()
- {
- if (_block == null)
- {
- return "No blocks contained in composite frame";
- }
- else
- {
- StringBuilder buf = new StringBuilder(this.getClass().getName());
- buf.append("{encodedBlock=").append(_firstFrame);
-
- buf.append(" _block=[").append(_block.toString()).append("]");
-
- buf.append("}");
- return buf.toString();
- }
- }
-}
+/* + * + * 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; + +public class SmallCompositeAMQDataBlock extends AMQDataBlock implements EncodableAMQDataBlock +{ + private AMQDataBlock _firstFrame; + + private AMQDataBlock _block; + + public SmallCompositeAMQDataBlock(AMQDataBlock block) + { + _block = block; + } + + /** + * The encoded block will be logically first before the AMQDataBlocks which are encoded + * into the buffer afterwards. + * @param encodedBlock already-encoded data + * @param block a block to be encoded. + */ + public SmallCompositeAMQDataBlock(AMQDataBlock encodedBlock, AMQDataBlock block) + { + this(block); + _firstFrame = encodedBlock; + } + + public AMQDataBlock getBlock() + { + return _block; + } + + public AMQDataBlock getFirstFrame() + { + return _firstFrame; + } + + public long getSize() + { + long frameSize = _block.getSize(); + + if (_firstFrame != null) + { + + frameSize += _firstFrame.getSize(); + } + return frameSize; + } + + public void writePayload(ByteBuffer buffer) + { + if (_firstFrame != null) + { + _firstFrame.writePayload(buffer); + } + _block.writePayload(buffer); + + } + + public String toString() + { + if (_block == null) + { + return "No blocks contained in composite frame"; + } + else + { + StringBuilder buf = new StringBuilder(this.getClass().getName()); + buf.append("{encodedBlock=").append(_firstFrame); + + buf.append(" _block=[").append(_block.toString()).append("]"); + + buf.append("}"); + return buf.toString(); + } + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java index 516d0c569c..76c154581d 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java @@ -1,198 +1,198 @@ -/*
- *
- * 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.framing.abstraction.ProtocolVersionMethodConverter;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class VersionSpecificRegistry
-{
- private static final Logger _log = LoggerFactory.getLogger(VersionSpecificRegistry.class);
-
- private final byte _protocolMajorVersion;
- private final byte _protocolMinorVersion;
-
- private static final int DEFAULT_MAX_CLASS_ID = 200;
- private static final int DEFAULT_MAX_METHOD_ID = 50;
-
- private AMQMethodBodyInstanceFactory[][] _registry = new AMQMethodBodyInstanceFactory[DEFAULT_MAX_CLASS_ID][];
-
- private ProtocolVersionMethodConverter _protocolVersionConverter;
-
- public VersionSpecificRegistry(byte major, byte minor)
- {
- _protocolMajorVersion = major;
- _protocolMinorVersion = minor;
-
- _protocolVersionConverter = loadProtocolVersionConverters(major, minor);
- }
-
- private static ProtocolVersionMethodConverter loadProtocolVersionConverters(byte protocolMajorVersion,
- byte protocolMinorVersion)
- {
- try
- {
- Class<ProtocolVersionMethodConverter> versionMethodConverterClass =
- (Class<ProtocolVersionMethodConverter>) Class.forName("org.apache.qpid.framing.MethodConverter_"
- + protocolMajorVersion + "_" + protocolMinorVersion);
-
- return versionMethodConverterClass.newInstance();
-
- }
- catch (ClassNotFoundException e)
- {
- _log.warn("Could not find protocol conversion classes for " + protocolMajorVersion + "-" + protocolMinorVersion);
- if (protocolMinorVersion != 0)
- {
- protocolMinorVersion--;
-
- return loadProtocolVersionConverters(protocolMajorVersion, protocolMinorVersion);
- }
- else if (protocolMajorVersion != 0)
- {
- protocolMajorVersion--;
-
- return loadProtocolVersionConverters(protocolMajorVersion, protocolMinorVersion);
- }
- else
- {
- return null;
- }
-
- }
- catch (IllegalAccessException e)
- {
- throw new IllegalStateException("Unable to load protocol version converter: ", e);
- }
- catch (InstantiationException e)
- {
- throw new IllegalStateException("Unable to load protocol version converter: ", e);
- }
- }
-
- public byte getProtocolMajorVersion()
- {
- return _protocolMajorVersion;
- }
-
- public byte getProtocolMinorVersion()
- {
- return _protocolMinorVersion;
- }
-
- public AMQMethodBodyInstanceFactory getMethodBody(final short classID, final short methodID)
- {
- try
- {
- return _registry[classID][methodID];
- }
- catch (IndexOutOfBoundsException e)
- {
- return null;
- }
- catch (NullPointerException e)
- {
- return null;
- }
- }
-
- public void registerMethod(final short classID, final short methodID, final AMQMethodBodyInstanceFactory instanceFactory)
- {
- if (_registry.length <= classID)
- {
- AMQMethodBodyInstanceFactory[][] oldRegistry = _registry;
- _registry = new AMQMethodBodyInstanceFactory[classID + 1][];
- System.arraycopy(oldRegistry, 0, _registry, 0, oldRegistry.length);
- }
-
- if (_registry[classID] == null)
- {
- _registry[classID] =
- new AMQMethodBodyInstanceFactory[(methodID > DEFAULT_MAX_METHOD_ID) ? (methodID + 1)
- : (DEFAULT_MAX_METHOD_ID + 1)];
- }
- else if (_registry[classID].length <= methodID)
- {
- AMQMethodBodyInstanceFactory[] oldMethods = _registry[classID];
- _registry[classID] = new AMQMethodBodyInstanceFactory[methodID + 1];
- System.arraycopy(oldMethods, 0, _registry[classID], 0, oldMethods.length);
- }
-
- _registry[classID][methodID] = instanceFactory;
-
- }
-
- public AMQMethodBody get(short classID, short methodID, ByteBuffer in, long size) throws AMQFrameDecodingException
- {
- AMQMethodBodyInstanceFactory bodyFactory;
- try
- {
- bodyFactory = _registry[classID][methodID];
- }
- catch (NullPointerException e)
- {
- throw new AMQFrameDecodingException(null, "Class " + classID + " unknown in AMQP version "
- + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID
- + " method " + methodID + ".", e);
- }
- catch (IndexOutOfBoundsException e)
- {
- if (classID >= _registry.length)
- {
- throw new AMQFrameDecodingException(null, "Class " + classID + " unknown in AMQP version "
- + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID
- + " method " + methodID + ".", e);
-
- }
- else
- {
- throw new AMQFrameDecodingException(null, "Method " + methodID + " unknown in AMQP version "
- + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID
- + " method " + methodID + ".", e);
-
- }
- }
-
- if (bodyFactory == null)
- {
- throw new AMQFrameDecodingException(null, "Method " + methodID + " unknown in AMQP version "
- + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID
- + " method " + methodID + ".", null);
- }
-
- return bodyFactory.newInstance( in, size);
-
- }
-
- public ProtocolVersionMethodConverter getProtocolVersionMethodConverter()
- {
- return _protocolVersionConverter;
- }
-
- public void configure()
- {
- _protocolVersionConverter.configure();
- }
-}
+/* + * + * 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.framing.abstraction.ProtocolVersionMethodConverter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class VersionSpecificRegistry +{ + private static final Logger _log = LoggerFactory.getLogger(VersionSpecificRegistry.class); + + private final byte _protocolMajorVersion; + private final byte _protocolMinorVersion; + + private static final int DEFAULT_MAX_CLASS_ID = 200; + private static final int DEFAULT_MAX_METHOD_ID = 50; + + private AMQMethodBodyInstanceFactory[][] _registry = new AMQMethodBodyInstanceFactory[DEFAULT_MAX_CLASS_ID][]; + + private ProtocolVersionMethodConverter _protocolVersionConverter; + + public VersionSpecificRegistry(byte major, byte minor) + { + _protocolMajorVersion = major; + _protocolMinorVersion = minor; + + _protocolVersionConverter = loadProtocolVersionConverters(major, minor); + } + + private static ProtocolVersionMethodConverter loadProtocolVersionConverters(byte protocolMajorVersion, + byte protocolMinorVersion) + { + try + { + Class<ProtocolVersionMethodConverter> versionMethodConverterClass = + (Class<ProtocolVersionMethodConverter>) Class.forName("org.apache.qpid.framing.MethodConverter_" + + protocolMajorVersion + "_" + protocolMinorVersion); + + return versionMethodConverterClass.newInstance(); + + } + catch (ClassNotFoundException e) + { + _log.warn("Could not find protocol conversion classes for " + protocolMajorVersion + "-" + protocolMinorVersion); + if (protocolMinorVersion != 0) + { + protocolMinorVersion--; + + return loadProtocolVersionConverters(protocolMajorVersion, protocolMinorVersion); + } + else if (protocolMajorVersion != 0) + { + protocolMajorVersion--; + + return loadProtocolVersionConverters(protocolMajorVersion, protocolMinorVersion); + } + else + { + return null; + } + + } + catch (IllegalAccessException e) + { + throw new IllegalStateException("Unable to load protocol version converter: ", e); + } + catch (InstantiationException e) + { + throw new IllegalStateException("Unable to load protocol version converter: ", e); + } + } + + public byte getProtocolMajorVersion() + { + return _protocolMajorVersion; + } + + public byte getProtocolMinorVersion() + { + return _protocolMinorVersion; + } + + public AMQMethodBodyInstanceFactory getMethodBody(final short classID, final short methodID) + { + try + { + return _registry[classID][methodID]; + } + catch (IndexOutOfBoundsException e) + { + return null; + } + catch (NullPointerException e) + { + return null; + } + } + + public void registerMethod(final short classID, final short methodID, final AMQMethodBodyInstanceFactory instanceFactory) + { + if (_registry.length <= classID) + { + AMQMethodBodyInstanceFactory[][] oldRegistry = _registry; + _registry = new AMQMethodBodyInstanceFactory[classID + 1][]; + System.arraycopy(oldRegistry, 0, _registry, 0, oldRegistry.length); + } + + if (_registry[classID] == null) + { + _registry[classID] = + new AMQMethodBodyInstanceFactory[(methodID > DEFAULT_MAX_METHOD_ID) ? (methodID + 1) + : (DEFAULT_MAX_METHOD_ID + 1)]; + } + else if (_registry[classID].length <= methodID) + { + AMQMethodBodyInstanceFactory[] oldMethods = _registry[classID]; + _registry[classID] = new AMQMethodBodyInstanceFactory[methodID + 1]; + System.arraycopy(oldMethods, 0, _registry[classID], 0, oldMethods.length); + } + + _registry[classID][methodID] = instanceFactory; + + } + + public AMQMethodBody get(short classID, short methodID, ByteBuffer in, long size) throws AMQFrameDecodingException + { + AMQMethodBodyInstanceFactory bodyFactory; + try + { + bodyFactory = _registry[classID][methodID]; + } + catch (NullPointerException e) + { + throw new AMQFrameDecodingException(null, "Class " + classID + " unknown in AMQP version " + + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID + + " method " + methodID + ".", e); + } + catch (IndexOutOfBoundsException e) + { + if (classID >= _registry.length) + { + throw new AMQFrameDecodingException(null, "Class " + classID + " unknown in AMQP version " + + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID + + " method " + methodID + ".", e); + + } + else + { + throw new AMQFrameDecodingException(null, "Method " + methodID + " unknown in AMQP version " + + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID + + " method " + methodID + ".", e); + + } + } + + if (bodyFactory == null) + { + throw new AMQFrameDecodingException(null, "Method " + methodID + " unknown in AMQP version " + + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID + + " method " + methodID + ".", null); + } + + return bodyFactory.newInstance( in, size); + + } + + public ProtocolVersionMethodConverter getProtocolVersionMethodConverter() + { + return _protocolVersionConverter; + } + + public void configure() + { + _protocolVersionConverter.configure(); + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java index 1c335f3036..1d7c05e9cc 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java @@ -1,47 +1,47 @@ -/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-package org.apache.qpid.framing.abstraction;
-
-public abstract class AbstractMethodConverter implements ProtocolVersionMethodConverter
-{
- private final byte _protocolMajorVersion;
-
-
- private final byte _protocolMinorVersion;
-
- public AbstractMethodConverter(byte major, byte minor)
- {
- _protocolMajorVersion = major;
- _protocolMinorVersion = minor;
- }
-
-
- public final byte getProtocolMajorVersion()
- {
- return _protocolMajorVersion;
- }
-
- public final byte getProtocolMinorVersion()
- {
- return _protocolMinorVersion;
- }
-}
+/* + * + * 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.abstraction; + +public abstract class AbstractMethodConverter implements ProtocolVersionMethodConverter +{ + private final byte _protocolMajorVersion; + + + private final byte _protocolMinorVersion; + + public AbstractMethodConverter(byte major, byte minor) + { + _protocolMajorVersion = major; + _protocolMinorVersion = minor; + } + + + public final byte getProtocolMajorVersion() + { + return _protocolMajorVersion; + } + + public final byte getProtocolMinorVersion() + { + return _protocolMinorVersion; + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java index 6312e478a8..0695349f76 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java @@ -1,32 +1,32 @@ -/*
- *
- * 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.abstraction;
-
-import org.apache.mina.common.ByteBuffer;
-
-public interface ContentChunk
-{
- int getSize();
- ByteBuffer getData();
-
- void reduceToFit();
-}
+/* + * + * 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.abstraction; + +import org.apache.mina.common.ByteBuffer; + +public interface ContentChunk +{ + int getSize(); + ByteBuffer getData(); + + void reduceToFit(); +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java index 49c28bb06b..a96bdcc171 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java @@ -1,38 +1,38 @@ -/*
- *
- * 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.abstraction;
-
-import org.apache.qpid.framing.AMQShortString;
-
-public interface MessagePublishInfo
-{
-
- public AMQShortString getExchange();
-
- public void setExchange(AMQShortString exchange);
-
- public boolean isImmediate();
-
- public boolean isMandatory();
-
- public AMQShortString getRoutingKey();
-
-}
+/* + * + * 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.abstraction; + +import org.apache.qpid.framing.AMQShortString; + +public interface MessagePublishInfo +{ + + public AMQShortString getExchange(); + + public void setExchange(AMQShortString exchange); + + public boolean isImmediate(); + + public boolean isMandatory(); + + public AMQShortString getRoutingKey(); + +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java index 42e2f7ad97..01d1a8a17b 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java @@ -1,32 +1,32 @@ -/*
- *
- * 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.abstraction;
-
-import org.apache.qpid.framing.AMQMethodBody;
-
-
-public interface MessagePublishInfoConverter
-{
- public MessagePublishInfo convertToInfo(AMQMethodBody body);
- public AMQMethodBody convertToBody(MessagePublishInfo info);
-
-}
+/* + * + * 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.abstraction; + +import org.apache.qpid.framing.AMQMethodBody; + + +public interface MessagePublishInfoConverter +{ + public MessagePublishInfo convertToInfo(AMQMethodBody body); + public AMQMethodBody convertToBody(MessagePublishInfo info); + +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java index 99588a0908..0a1cedc4e6 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java @@ -1,32 +1,32 @@ -/*
- *
- * 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.abstraction;
-
-import org.apache.qpid.framing.AMQBody;
-
-public interface ProtocolVersionMethodConverter extends MessagePublishInfoConverter
-{
- AMQBody convertToBody(ContentChunk contentBody);
- ContentChunk convertToContentChunk(AMQBody body);
-
- void configure();
-}
+/* + * + * 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.abstraction; + +import org.apache.qpid.framing.AMQBody; + +public interface ProtocolVersionMethodConverter extends MessagePublishInfoConverter +{ + AMQBody convertToBody(ContentChunk contentBody); + ContentChunk convertToContentChunk(AMQBody body); + + void configure(); +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java index 3c5cb74773..948f5baaf6 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java @@ -1,209 +1,209 @@ -/*
- *
- * 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.amqp_0_9;
-
-import org.apache.qpid.framing.EncodingUtils;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.FieldTable;
-import org.apache.qpid.framing.AMQFrameDecodingException;
-import org.apache.qpid.framing.Content;
-
-import org.apache.mina.common.ByteBuffer;
-
-public abstract class AMQMethodBody_0_9 extends org.apache.qpid.framing.AMQMethodBodyImpl
-{
-
- public byte getMajor()
- {
- return 0;
- }
-
- public byte getMinor()
- {
- return 9;
- }
-
- public int getSize()
- {
- return 2 + 2 + getBodySize();
- }
-
- public void writePayload(ByteBuffer buffer)
- {
- EncodingUtils.writeUnsignedShort(buffer, getClazz());
- EncodingUtils.writeUnsignedShort(buffer, getMethod());
- writeMethodPayload(buffer);
- }
-
-
- protected byte readByte(ByteBuffer buffer)
- {
- return buffer.get();
- }
-
- protected AMQShortString readAMQShortString(ByteBuffer buffer)
- {
- return EncodingUtils.readAMQShortString(buffer);
- }
-
- protected int getSizeOf(AMQShortString string)
- {
- return EncodingUtils.encodedShortStringLength(string);
- }
-
- protected void writeByte(ByteBuffer buffer, byte b)
- {
- buffer.put(b);
- }
-
- protected void writeAMQShortString(ByteBuffer buffer, AMQShortString string)
- {
- EncodingUtils.writeShortStringBytes(buffer, string);
- }
-
- protected int readInt(ByteBuffer buffer)
- {
- return buffer.getInt();
- }
-
- protected void writeInt(ByteBuffer buffer, int i)
- {
- buffer.putInt(i);
- }
-
- protected FieldTable readFieldTable(ByteBuffer buffer) throws AMQFrameDecodingException
- {
- return EncodingUtils.readFieldTable(buffer);
- }
-
- protected int getSizeOf(FieldTable table)
- {
- return EncodingUtils.encodedFieldTableLength(table); //To change body of created methods use File | Settings | File Templates.
- }
-
- protected void writeFieldTable(ByteBuffer buffer, FieldTable table)
- {
- EncodingUtils.writeFieldTableBytes(buffer, table);
- }
-
- protected long readLong(ByteBuffer buffer)
- {
- return buffer.getLong();
- }
-
- protected void writeLong(ByteBuffer buffer, long l)
- {
- buffer.putLong(l);
- }
-
- protected int getSizeOf(byte[] response)
- {
- return (response == null) ? 4 :response.length + 4;
- }
-
- protected void writeBytes(ByteBuffer buffer, byte[] data)
- {
- EncodingUtils.writeBytes(buffer,data);
- }
-
- protected byte[] readBytes(ByteBuffer buffer)
- {
- return EncodingUtils.readBytes(buffer);
- }
-
- protected short readShort(ByteBuffer buffer)
- {
- return EncodingUtils.readShort(buffer);
- }
-
- protected void writeShort(ByteBuffer buffer, short s)
- {
- EncodingUtils.writeShort(buffer, s);
- }
-
- protected Content readContent(ByteBuffer buffer)
- {
- return null; //To change body of created methods use File | Settings | File Templates.
- }
-
- protected int getSizeOf(Content body)
- {
- return 0; //To change body of created methods use File | Settings | File Templates.
- }
-
- protected void writeContent(ByteBuffer buffer, Content body)
- {
- //To change body of created methods use File | Settings | File Templates.
- }
-
- protected byte readBitfield(ByteBuffer buffer)
- {
- return readByte(buffer); //To change body of created methods use File | Settings | File Templates.
- }
-
- protected int readUnsignedShort(ByteBuffer buffer)
- {
- return buffer.getUnsignedShort(); //To change body of created methods use File | Settings | File Templates.
- }
-
- protected void writeBitfield(ByteBuffer buffer, byte bitfield0)
- {
- buffer.put(bitfield0);
- }
-
- protected void writeUnsignedShort(ByteBuffer buffer, int s)
- {
- EncodingUtils.writeUnsignedShort(buffer, s);
- }
-
- protected long readUnsignedInteger(ByteBuffer buffer)
- {
- return buffer.getUnsignedInt();
- }
- protected void writeUnsignedInteger(ByteBuffer buffer, long i)
- {
- EncodingUtils.writeUnsignedInteger(buffer, i);
- }
-
-
- protected short readUnsignedByte(ByteBuffer buffer)
- {
- return buffer.getUnsigned();
- }
-
- protected void writeUnsignedByte(ByteBuffer buffer, short unsignedByte)
- {
- EncodingUtils.writeUnsignedByte(buffer, unsignedByte);
- }
-
- protected long readTimestamp(ByteBuffer buffer)
- {
- return EncodingUtils.readTimestamp(buffer);
- }
-
- protected void writeTimestamp(ByteBuffer buffer, long t)
- {
- EncodingUtils.writeTimestamp(buffer, t);
- }
-
-
-}
+/* + * + * 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.amqp_0_9; + +import org.apache.qpid.framing.EncodingUtils; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.AMQFrameDecodingException; +import org.apache.qpid.framing.Content; + +import org.apache.mina.common.ByteBuffer; + +public abstract class AMQMethodBody_0_9 extends org.apache.qpid.framing.AMQMethodBodyImpl +{ + + public byte getMajor() + { + return 0; + } + + public byte getMinor() + { + return 9; + } + + public int getSize() + { + return 2 + 2 + getBodySize(); + } + + public void writePayload(ByteBuffer buffer) + { + EncodingUtils.writeUnsignedShort(buffer, getClazz()); + EncodingUtils.writeUnsignedShort(buffer, getMethod()); + writeMethodPayload(buffer); + } + + + protected byte readByte(ByteBuffer buffer) + { + return buffer.get(); + } + + protected AMQShortString readAMQShortString(ByteBuffer buffer) + { + return EncodingUtils.readAMQShortString(buffer); + } + + protected int getSizeOf(AMQShortString string) + { + return EncodingUtils.encodedShortStringLength(string); + } + + protected void writeByte(ByteBuffer buffer, byte b) + { + buffer.put(b); + } + + protected void writeAMQShortString(ByteBuffer buffer, AMQShortString string) + { + EncodingUtils.writeShortStringBytes(buffer, string); + } + + protected int readInt(ByteBuffer buffer) + { + return buffer.getInt(); + } + + protected void writeInt(ByteBuffer buffer, int i) + { + buffer.putInt(i); + } + + protected FieldTable readFieldTable(ByteBuffer buffer) throws AMQFrameDecodingException + { + return EncodingUtils.readFieldTable(buffer); + } + + protected int getSizeOf(FieldTable table) + { + return EncodingUtils.encodedFieldTableLength(table); //To change body of created methods use File | Settings | File Templates. + } + + protected void writeFieldTable(ByteBuffer buffer, FieldTable table) + { + EncodingUtils.writeFieldTableBytes(buffer, table); + } + + protected long readLong(ByteBuffer buffer) + { + return buffer.getLong(); + } + + protected void writeLong(ByteBuffer buffer, long l) + { + buffer.putLong(l); + } + + protected int getSizeOf(byte[] response) + { + return (response == null) ? 4 :response.length + 4; + } + + protected void writeBytes(ByteBuffer buffer, byte[] data) + { + EncodingUtils.writeBytes(buffer,data); + } + + protected byte[] readBytes(ByteBuffer buffer) + { + return EncodingUtils.readBytes(buffer); + } + + protected short readShort(ByteBuffer buffer) + { + return EncodingUtils.readShort(buffer); + } + + protected void writeShort(ByteBuffer buffer, short s) + { + EncodingUtils.writeShort(buffer, s); + } + + protected Content readContent(ByteBuffer buffer) + { + return null; //To change body of created methods use File | Settings | File Templates. + } + + protected int getSizeOf(Content body) + { + return 0; //To change body of created methods use File | Settings | File Templates. + } + + protected void writeContent(ByteBuffer buffer, Content body) + { + //To change body of created methods use File | Settings | File Templates. + } + + protected byte readBitfield(ByteBuffer buffer) + { + return readByte(buffer); //To change body of created methods use File | Settings | File Templates. + } + + protected int readUnsignedShort(ByteBuffer buffer) + { + return buffer.getUnsignedShort(); //To change body of created methods use File | Settings | File Templates. + } + + protected void writeBitfield(ByteBuffer buffer, byte bitfield0) + { + buffer.put(bitfield0); + } + + protected void writeUnsignedShort(ByteBuffer buffer, int s) + { + EncodingUtils.writeUnsignedShort(buffer, s); + } + + protected long readUnsignedInteger(ByteBuffer buffer) + { + return buffer.getUnsignedInt(); + } + protected void writeUnsignedInteger(ByteBuffer buffer, long i) + { + EncodingUtils.writeUnsignedInteger(buffer, i); + } + + + protected short readUnsignedByte(ByteBuffer buffer) + { + return buffer.getUnsigned(); + } + + protected void writeUnsignedByte(ByteBuffer buffer, short unsignedByte) + { + EncodingUtils.writeUnsignedByte(buffer, unsignedByte); + } + + protected long readTimestamp(ByteBuffer buffer) + { + return EncodingUtils.readTimestamp(buffer); + } + + protected void writeTimestamp(ByteBuffer buffer, long t) + { + EncodingUtils.writeTimestamp(buffer, t); + } + + +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java index 2fd4f70138..fd5195eb6b 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java @@ -1,172 +1,172 @@ -/*
- *
- * 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.amqp_0_9;
-
-import org.apache.mina.common.ByteBuffer;
-
-import org.apache.qpid.framing.abstraction.AbstractMethodConverter;
-import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter;
-import org.apache.qpid.framing.abstraction.ContentChunk;
-import org.apache.qpid.framing.abstraction.MessagePublishInfo;
-import org.apache.qpid.framing.*;
-import org.apache.qpid.framing.amqp_0_9.*;
-import org.apache.qpid.framing.amqp_0_9.BasicPublishBodyImpl;
-
-public class MethodConverter_0_9 extends AbstractMethodConverter implements ProtocolVersionMethodConverter
-{
- private int _basicPublishClassId;
- private int _basicPublishMethodId;
-
- public MethodConverter_0_9()
- {
- super((byte)0,(byte)9);
-
-
- }
-
- public AMQBody convertToBody(ContentChunk contentChunk)
- {
- if(contentChunk instanceof ContentChunk_0_9)
- {
- return ((ContentChunk_0_9)contentChunk).toBody();
- }
- else
- {
- return new ContentBody(contentChunk.getData());
- }
- }
-
- public ContentChunk convertToContentChunk(AMQBody body)
- {
- final ContentBody contentBodyChunk = (ContentBody) body;
-
- return new ContentChunk_0_9(contentBodyChunk);
-
- }
-
- public void configure()
- {
-
- _basicPublishClassId = org.apache.qpid.framing.amqp_0_9.BasicPublishBodyImpl.CLASS_ID;
- _basicPublishMethodId = BasicPublishBodyImpl.METHOD_ID;
-
- }
-
- public MessagePublishInfo convertToInfo(AMQMethodBody methodBody)
- {
- final BasicPublishBody publishBody = ((BasicPublishBody) methodBody);
-
- final AMQShortString exchange = publishBody.getExchange();
- final AMQShortString routingKey = publishBody.getRoutingKey();
-
- return new MethodConverter_0_9.MessagePublishInfoImpl(exchange,
- publishBody.getImmediate(),
- publishBody.getMandatory(),
- routingKey);
-
- }
-
- public AMQMethodBody convertToBody(MessagePublishInfo info)
- {
-
- return new BasicPublishBodyImpl(0,
- info.getExchange(),
- info.getRoutingKey(),
- info.isMandatory(),
- info.isImmediate()) ;
-
- }
-
- private static class MessagePublishInfoImpl implements MessagePublishInfo
- {
- private AMQShortString _exchange;
- private final boolean _immediate;
- private final boolean _mandatory;
- private final AMQShortString _routingKey;
-
- public MessagePublishInfoImpl(final AMQShortString exchange,
- final boolean immediate,
- final boolean mandatory,
- final AMQShortString routingKey)
- {
- _exchange = exchange;
- _immediate = immediate;
- _mandatory = mandatory;
- _routingKey = routingKey;
- }
-
- public AMQShortString getExchange()
- {
- return _exchange;
- }
-
- public void setExchange(AMQShortString exchange)
- {
- _exchange = exchange;
- }
-
- public boolean isImmediate()
- {
- return _immediate;
- }
-
- public boolean isMandatory()
- {
- return _mandatory;
- }
-
- public AMQShortString getRoutingKey()
- {
- return _routingKey;
- }
- }
-
- private static class ContentChunk_0_9 implements ContentChunk
- {
- private final ContentBody _contentBodyChunk;
-
- public ContentChunk_0_9(final ContentBody contentBodyChunk)
- {
- _contentBodyChunk = contentBodyChunk;
- }
-
- public int getSize()
- {
- return _contentBodyChunk.getSize();
- }
-
- public ByteBuffer getData()
- {
- return _contentBodyChunk.payload;
- }
-
- public void reduceToFit()
- {
- _contentBodyChunk.reduceBufferToFit();
- }
-
- public AMQBody toBody()
- {
- return _contentBodyChunk;
- }
- }
-}
+/* + * + * 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.amqp_0_9; + +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.framing.abstraction.AbstractMethodConverter; +import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.amqp_0_9.*; +import org.apache.qpid.framing.amqp_0_9.BasicPublishBodyImpl; + +public class MethodConverter_0_9 extends AbstractMethodConverter implements ProtocolVersionMethodConverter +{ + private int _basicPublishClassId; + private int _basicPublishMethodId; + + public MethodConverter_0_9() + { + super((byte)0,(byte)9); + + + } + + public AMQBody convertToBody(ContentChunk contentChunk) + { + if(contentChunk instanceof ContentChunk_0_9) + { + return ((ContentChunk_0_9)contentChunk).toBody(); + } + else + { + return new ContentBody(contentChunk.getData()); + } + } + + public ContentChunk convertToContentChunk(AMQBody body) + { + final ContentBody contentBodyChunk = (ContentBody) body; + + return new ContentChunk_0_9(contentBodyChunk); + + } + + public void configure() + { + + _basicPublishClassId = org.apache.qpid.framing.amqp_0_9.BasicPublishBodyImpl.CLASS_ID; + _basicPublishMethodId = BasicPublishBodyImpl.METHOD_ID; + + } + + public MessagePublishInfo convertToInfo(AMQMethodBody methodBody) + { + final BasicPublishBody publishBody = ((BasicPublishBody) methodBody); + + final AMQShortString exchange = publishBody.getExchange(); + final AMQShortString routingKey = publishBody.getRoutingKey(); + + return new MethodConverter_0_9.MessagePublishInfoImpl(exchange, + publishBody.getImmediate(), + publishBody.getMandatory(), + routingKey); + + } + + public AMQMethodBody convertToBody(MessagePublishInfo info) + { + + return new BasicPublishBodyImpl(0, + info.getExchange(), + info.getRoutingKey(), + info.isMandatory(), + info.isImmediate()) ; + + } + + private static class MessagePublishInfoImpl implements MessagePublishInfo + { + private AMQShortString _exchange; + private final boolean _immediate; + private final boolean _mandatory; + private final AMQShortString _routingKey; + + public MessagePublishInfoImpl(final AMQShortString exchange, + final boolean immediate, + final boolean mandatory, + final AMQShortString routingKey) + { + _exchange = exchange; + _immediate = immediate; + _mandatory = mandatory; + _routingKey = routingKey; + } + + public AMQShortString getExchange() + { + return _exchange; + } + + public void setExchange(AMQShortString exchange) + { + _exchange = exchange; + } + + public boolean isImmediate() + { + return _immediate; + } + + public boolean isMandatory() + { + return _mandatory; + } + + public AMQShortString getRoutingKey() + { + return _routingKey; + } + } + + private static class ContentChunk_0_9 implements ContentChunk + { + private final ContentBody _contentBodyChunk; + + public ContentChunk_0_9(final ContentBody contentBodyChunk) + { + _contentBodyChunk = contentBodyChunk; + } + + public int getSize() + { + return _contentBodyChunk.getSize(); + } + + public ByteBuffer getData() + { + return _contentBodyChunk.payload; + } + + public void reduceToFit() + { + _contentBodyChunk.reduceBufferToFit(); + } + + public AMQBody toBody() + { + return _contentBodyChunk; + } + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java index 2b7c9534a9..e9b4447140 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java @@ -1,209 +1,209 @@ -/*
- *
- * 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.amqp_8_0;
-
-import org.apache.qpid.framing.EncodingUtils;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.FieldTable;
-import org.apache.qpid.framing.AMQFrameDecodingException;
-import org.apache.qpid.framing.Content;
-
-import org.apache.mina.common.ByteBuffer;
-
-public abstract class AMQMethodBody_8_0 extends org.apache.qpid.framing.AMQMethodBodyImpl
-{
-
- public byte getMajor()
- {
- return 8;
- }
-
- public byte getMinor()
- {
- return 0;
- }
-
- public int getSize()
- {
- return 2 + 2 + getBodySize();
- }
-
- public void writePayload(ByteBuffer buffer)
- {
- EncodingUtils.writeUnsignedShort(buffer, getClazz());
- EncodingUtils.writeUnsignedShort(buffer, getMethod());
- writeMethodPayload(buffer);
- }
-
-
- protected byte readByte(ByteBuffer buffer)
- {
- return buffer.get();
- }
-
- protected AMQShortString readAMQShortString(ByteBuffer buffer)
- {
- return EncodingUtils.readAMQShortString(buffer);
- }
-
- protected int getSizeOf(AMQShortString string)
- {
- return EncodingUtils.encodedShortStringLength(string);
- }
-
- protected void writeByte(ByteBuffer buffer, byte b)
- {
- buffer.put(b);
- }
-
- protected void writeAMQShortString(ByteBuffer buffer, AMQShortString string)
- {
- EncodingUtils.writeShortStringBytes(buffer, string);
- }
-
- protected int readInt(ByteBuffer buffer)
- {
- return buffer.getInt();
- }
-
- protected void writeInt(ByteBuffer buffer, int i)
- {
- buffer.putInt(i);
- }
-
- protected FieldTable readFieldTable(ByteBuffer buffer) throws AMQFrameDecodingException
- {
- return EncodingUtils.readFieldTable(buffer);
- }
-
- protected int getSizeOf(FieldTable table)
- {
- return EncodingUtils.encodedFieldTableLength(table); //To change body of created methods use File | Settings | File Templates.
- }
-
- protected void writeFieldTable(ByteBuffer buffer, FieldTable table)
- {
- EncodingUtils.writeFieldTableBytes(buffer, table);
- }
-
- protected long readLong(ByteBuffer buffer)
- {
- return buffer.getLong();
- }
-
- protected void writeLong(ByteBuffer buffer, long l)
- {
- buffer.putLong(l);
- }
-
- protected int getSizeOf(byte[] response)
- {
- return (response == null) ? 4 : response.length + 4;
- }
-
- protected void writeBytes(ByteBuffer buffer, byte[] data)
- {
- EncodingUtils.writeBytes(buffer,data);
- }
-
- protected byte[] readBytes(ByteBuffer buffer)
- {
- return EncodingUtils.readBytes(buffer);
- }
-
- protected short readShort(ByteBuffer buffer)
- {
- return EncodingUtils.readShort(buffer);
- }
-
- protected void writeShort(ByteBuffer buffer, short s)
- {
- EncodingUtils.writeShort(buffer, s);
- }
-
- protected Content readContent(ByteBuffer buffer)
- {
- return null; //To change body of created methods use File | Settings | File Templates.
- }
-
- protected int getSizeOf(Content body)
- {
- return 0; //To change body of created methods use File | Settings | File Templates.
- }
-
- protected void writeContent(ByteBuffer buffer, Content body)
- {
- //To change body of created methods use File | Settings | File Templates.
- }
-
- protected byte readBitfield(ByteBuffer buffer)
- {
- return readByte(buffer); //To change body of created methods use File | Settings | File Templates.
- }
-
- protected int readUnsignedShort(ByteBuffer buffer)
- {
- return buffer.getUnsignedShort(); //To change body of created methods use File | Settings | File Templates.
- }
-
- protected void writeBitfield(ByteBuffer buffer, byte bitfield0)
- {
- buffer.put(bitfield0);
- }
-
- protected void writeUnsignedShort(ByteBuffer buffer, int s)
- {
- EncodingUtils.writeUnsignedShort(buffer, s);
- }
-
- protected long readUnsignedInteger(ByteBuffer buffer)
- {
- return buffer.getUnsignedInt();
- }
- protected void writeUnsignedInteger(ByteBuffer buffer, long i)
- {
- EncodingUtils.writeUnsignedInteger(buffer, i);
- }
-
-
- protected short readUnsignedByte(ByteBuffer buffer)
- {
- return buffer.getUnsigned();
- }
-
- protected void writeUnsignedByte(ByteBuffer buffer, short unsignedByte)
- {
- EncodingUtils.writeUnsignedByte(buffer, unsignedByte);
- }
-
- protected long readTimestamp(ByteBuffer buffer)
- {
- return EncodingUtils.readTimestamp(buffer);
- }
-
- protected void writeTimestamp(ByteBuffer buffer, long t)
- {
- EncodingUtils.writeTimestamp(buffer, t);
- }
-
-
-}
+/* + * + * 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.amqp_8_0; + +import org.apache.qpid.framing.EncodingUtils; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.AMQFrameDecodingException; +import org.apache.qpid.framing.Content; + +import org.apache.mina.common.ByteBuffer; + +public abstract class AMQMethodBody_8_0 extends org.apache.qpid.framing.AMQMethodBodyImpl +{ + + public byte getMajor() + { + return 8; + } + + public byte getMinor() + { + return 0; + } + + public int getSize() + { + return 2 + 2 + getBodySize(); + } + + public void writePayload(ByteBuffer buffer) + { + EncodingUtils.writeUnsignedShort(buffer, getClazz()); + EncodingUtils.writeUnsignedShort(buffer, getMethod()); + writeMethodPayload(buffer); + } + + + protected byte readByte(ByteBuffer buffer) + { + return buffer.get(); + } + + protected AMQShortString readAMQShortString(ByteBuffer buffer) + { + return EncodingUtils.readAMQShortString(buffer); + } + + protected int getSizeOf(AMQShortString string) + { + return EncodingUtils.encodedShortStringLength(string); + } + + protected void writeByte(ByteBuffer buffer, byte b) + { + buffer.put(b); + } + + protected void writeAMQShortString(ByteBuffer buffer, AMQShortString string) + { + EncodingUtils.writeShortStringBytes(buffer, string); + } + + protected int readInt(ByteBuffer buffer) + { + return buffer.getInt(); + } + + protected void writeInt(ByteBuffer buffer, int i) + { + buffer.putInt(i); + } + + protected FieldTable readFieldTable(ByteBuffer buffer) throws AMQFrameDecodingException + { + return EncodingUtils.readFieldTable(buffer); + } + + protected int getSizeOf(FieldTable table) + { + return EncodingUtils.encodedFieldTableLength(table); //To change body of created methods use File | Settings | File Templates. + } + + protected void writeFieldTable(ByteBuffer buffer, FieldTable table) + { + EncodingUtils.writeFieldTableBytes(buffer, table); + } + + protected long readLong(ByteBuffer buffer) + { + return buffer.getLong(); + } + + protected void writeLong(ByteBuffer buffer, long l) + { + buffer.putLong(l); + } + + protected int getSizeOf(byte[] response) + { + return (response == null) ? 4 : response.length + 4; + } + + protected void writeBytes(ByteBuffer buffer, byte[] data) + { + EncodingUtils.writeBytes(buffer,data); + } + + protected byte[] readBytes(ByteBuffer buffer) + { + return EncodingUtils.readBytes(buffer); + } + + protected short readShort(ByteBuffer buffer) + { + return EncodingUtils.readShort(buffer); + } + + protected void writeShort(ByteBuffer buffer, short s) + { + EncodingUtils.writeShort(buffer, s); + } + + protected Content readContent(ByteBuffer buffer) + { + return null; //To change body of created methods use File | Settings | File Templates. + } + + protected int getSizeOf(Content body) + { + return 0; //To change body of created methods use File | Settings | File Templates. + } + + protected void writeContent(ByteBuffer buffer, Content body) + { + //To change body of created methods use File | Settings | File Templates. + } + + protected byte readBitfield(ByteBuffer buffer) + { + return readByte(buffer); //To change body of created methods use File | Settings | File Templates. + } + + protected int readUnsignedShort(ByteBuffer buffer) + { + return buffer.getUnsignedShort(); //To change body of created methods use File | Settings | File Templates. + } + + protected void writeBitfield(ByteBuffer buffer, byte bitfield0) + { + buffer.put(bitfield0); + } + + protected void writeUnsignedShort(ByteBuffer buffer, int s) + { + EncodingUtils.writeUnsignedShort(buffer, s); + } + + protected long readUnsignedInteger(ByteBuffer buffer) + { + return buffer.getUnsignedInt(); + } + protected void writeUnsignedInteger(ByteBuffer buffer, long i) + { + EncodingUtils.writeUnsignedInteger(buffer, i); + } + + + protected short readUnsignedByte(ByteBuffer buffer) + { + return buffer.getUnsigned(); + } + + protected void writeUnsignedByte(ByteBuffer buffer, short unsignedByte) + { + EncodingUtils.writeUnsignedByte(buffer, unsignedByte); + } + + protected long readTimestamp(ByteBuffer buffer) + { + return EncodingUtils.readTimestamp(buffer); + } + + protected void writeTimestamp(ByteBuffer buffer, long t) + { + EncodingUtils.writeTimestamp(buffer, t); + } + + +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java index b1be49a350..299c655698 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java @@ -1,151 +1,151 @@ -/*
- *
- * 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.amqp_8_0;
-
-import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter;
-import org.apache.qpid.framing.abstraction.ContentChunk;
-import org.apache.qpid.framing.abstraction.MessagePublishInfo;
-import org.apache.qpid.framing.abstraction.AbstractMethodConverter;
-import org.apache.qpid.framing.amqp_8_0.BasicPublishBodyImpl;
-import org.apache.qpid.framing.*;
-
-import org.apache.mina.common.ByteBuffer;
-
-public class MethodConverter_8_0 extends AbstractMethodConverter implements ProtocolVersionMethodConverter
-{
- private int _basicPublishClassId;
- private int _basicPublishMethodId;
-
- public MethodConverter_8_0()
- {
- super((byte)8,(byte)0);
-
-
- }
-
- public AMQBody convertToBody(ContentChunk contentChunk)
- {
- return new ContentBody(contentChunk.getData());
- }
-
- public ContentChunk convertToContentChunk(AMQBody body)
- {
- final ContentBody contentBodyChunk = (ContentBody) body;
-
- return new ContentChunk()
- {
-
- public int getSize()
- {
- return contentBodyChunk.getSize();
- }
-
- public ByteBuffer getData()
- {
- return contentBodyChunk.payload;
- }
-
- public void reduceToFit()
- {
- contentBodyChunk.reduceBufferToFit();
- }
- };
-
- }
-
- public void configure()
- {
-
- _basicPublishClassId = BasicPublishBodyImpl.CLASS_ID;
- _basicPublishMethodId = BasicPublishBodyImpl.METHOD_ID;
-
- }
-
- public MessagePublishInfo convertToInfo(AMQMethodBody methodBody)
- {
- final BasicPublishBody publishBody = ((BasicPublishBody) methodBody);
-
- final AMQShortString exchange = publishBody.getExchange();
- final AMQShortString routingKey = publishBody.getRoutingKey();
-
- return new MessagePublishInfoImpl(exchange == null ? null : exchange.intern(),
- publishBody.getImmediate(),
- publishBody.getMandatory(),
- routingKey == null ? null : routingKey.intern());
-
- }
-
- public AMQMethodBody convertToBody(MessagePublishInfo info)
- {
-
- return new BasicPublishBodyImpl(0,
- info.getExchange(),
- info.getRoutingKey(),
- info.isMandatory(),
- info.isImmediate()) ;
-
- }
-
- private static class MessagePublishInfoImpl implements MessagePublishInfo
- {
- private AMQShortString _exchange;
- private final boolean _immediate;
- private final boolean _mandatory;
- private final AMQShortString _routingKey;
-
- public MessagePublishInfoImpl(final AMQShortString exchange,
- final boolean immediate,
- final boolean mandatory,
- final AMQShortString routingKey)
- {
- _exchange = exchange;
- _immediate = immediate;
- _mandatory = mandatory;
- _routingKey = routingKey;
- }
-
- public AMQShortString getExchange()
- {
- return _exchange;
- }
-
- public void setExchange(AMQShortString exchange)
- {
- _exchange = exchange;
- }
-
- public boolean isImmediate()
- {
- return _immediate;
- }
-
- public boolean isMandatory()
- {
- return _mandatory;
- }
-
- public AMQShortString getRoutingKey()
- {
- return _routingKey;
- }
- }
-}
+/* + * + * 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.amqp_8_0; + +import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.abstraction.AbstractMethodConverter; +import org.apache.qpid.framing.amqp_8_0.BasicPublishBodyImpl; +import org.apache.qpid.framing.*; + +import org.apache.mina.common.ByteBuffer; + +public class MethodConverter_8_0 extends AbstractMethodConverter implements ProtocolVersionMethodConverter +{ + private int _basicPublishClassId; + private int _basicPublishMethodId; + + public MethodConverter_8_0() + { + super((byte)8,(byte)0); + + + } + + public AMQBody convertToBody(ContentChunk contentChunk) + { + return new ContentBody(contentChunk.getData()); + } + + public ContentChunk convertToContentChunk(AMQBody body) + { + final ContentBody contentBodyChunk = (ContentBody) body; + + return new ContentChunk() + { + + public int getSize() + { + return contentBodyChunk.getSize(); + } + + public ByteBuffer getData() + { + return contentBodyChunk.payload; + } + + public void reduceToFit() + { + contentBodyChunk.reduceBufferToFit(); + } + }; + + } + + public void configure() + { + + _basicPublishClassId = BasicPublishBodyImpl.CLASS_ID; + _basicPublishMethodId = BasicPublishBodyImpl.METHOD_ID; + + } + + public MessagePublishInfo convertToInfo(AMQMethodBody methodBody) + { + final BasicPublishBody publishBody = ((BasicPublishBody) methodBody); + + final AMQShortString exchange = publishBody.getExchange(); + final AMQShortString routingKey = publishBody.getRoutingKey(); + + return new MessagePublishInfoImpl(exchange == null ? null : exchange.intern(), + publishBody.getImmediate(), + publishBody.getMandatory(), + routingKey == null ? null : routingKey.intern()); + + } + + public AMQMethodBody convertToBody(MessagePublishInfo info) + { + + return new BasicPublishBodyImpl(0, + info.getExchange(), + info.getRoutingKey(), + info.isMandatory(), + info.isImmediate()) ; + + } + + private static class MessagePublishInfoImpl implements MessagePublishInfo + { + private AMQShortString _exchange; + private final boolean _immediate; + private final boolean _mandatory; + private final AMQShortString _routingKey; + + public MessagePublishInfoImpl(final AMQShortString exchange, + final boolean immediate, + final boolean mandatory, + final AMQShortString routingKey) + { + _exchange = exchange; + _immediate = immediate; + _mandatory = mandatory; + _routingKey = routingKey; + } + + public AMQShortString getExchange() + { + return _exchange; + } + + public void setExchange(AMQShortString exchange) + { + _exchange = exchange; + } + + public boolean isImmediate() + { + return _immediate; + } + + public boolean isMandatory() + { + return _mandatory; + } + + public AMQShortString getRoutingKey() + { + return _routingKey; + } + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java b/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java index b56a05f725..59003225b7 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java @@ -1,57 +1,57 @@ -/*
- *
- * 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.protocol;
-
-import org.apache.qpid.framing.*;
-import org.apache.qpid.AMQException;
-
-/**
- * AMQVersionAwareProtocolSession is implemented by all AMQP session classes, that need to provide an awareness to
- * callers of the version of the AMQP protocol that they are able to work with.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities
- * <tr><td> Provide the method registry for a specific version of the AMQP.
- * </table>
- *
- * @todo Why is this a seperate interface to {@link ProtocolVersionAware}, could they be combined into a single
- * interface and one of them eliminated? Move getRegistry method to ProtocolVersionAware, make the sessions
- * implement AMQProtocolWriter directly and drop this interface.
- */
-public interface AMQVersionAwareProtocolSession extends AMQProtocolWriter, ProtocolVersionAware
-{
- /**
- * Gets the method registry for a specific version of the AMQP.
- *
- * @return The method registry for a specific version of the AMQP.
- */
-// public VersionSpecificRegistry getRegistry();
-
- MethodRegistry getMethodRegistry();
-
-
- public void methodFrameReceived(int channelId, AMQMethodBody body) throws AMQException;
- public void contentHeaderReceived(int channelId, ContentHeaderBody body) throws AMQException;
- public void contentBodyReceived(int channelId, ContentBody body) throws AMQException;
- public void heartbeatBodyReceived(int channelId, HeartbeatBody body) throws AMQException;
-
-
-}
+/* + * + * 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.protocol; + +import org.apache.qpid.framing.*; +import org.apache.qpid.AMQException; + +/** + * AMQVersionAwareProtocolSession is implemented by all AMQP session classes, that need to provide an awareness to + * callers of the version of the AMQP protocol that they are able to work with. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities + * <tr><td> Provide the method registry for a specific version of the AMQP. + * </table> + * + * @todo Why is this a seperate interface to {@link ProtocolVersionAware}, could they be combined into a single + * interface and one of them eliminated? Move getRegistry method to ProtocolVersionAware, make the sessions + * implement AMQProtocolWriter directly and drop this interface. + */ +public interface AMQVersionAwareProtocolSession extends AMQProtocolWriter, ProtocolVersionAware +{ + /** + * Gets the method registry for a specific version of the AMQP. + * + * @return The method registry for a specific version of the AMQP. + */ +// public VersionSpecificRegistry getRegistry(); + + MethodRegistry getMethodRegistry(); + + + public void methodFrameReceived(int channelId, AMQMethodBody body) throws AMQException; + public void contentHeaderReceived(int channelId, ContentHeaderBody body) throws AMQException; + public void contentBodyReceived(int channelId, ContentBody body) throws AMQException; + public void heartbeatBodyReceived(int channelId, HeartbeatBody body) throws AMQException; + + +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java b/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java index dea80cdcf4..56f950dd85 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java @@ -1,53 +1,53 @@ -/*
- *
- * 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.protocol;
-
-import org.apache.qpid.framing.ProtocolVersion;
-
-/**
- * ProtocolVersionAware is implemented by all AMQP handling classes, that need to provide an awareness to callers of
- * the version of the AMQP protocol that they are able to handle.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities
- * <tr><td> Report the major and minor AMQP version handled.
- * </table>
- */
-public interface ProtocolVersionAware
-{
- /**
- * @deprecated
- * Reports the AMQP minor version, that the implementer can handle.
- *
- * @return The AMQP minor version.
- */
- public byte getProtocolMinorVersion();
-
- /**
- * @deprecated
- * Reports the AMQP major version, that the implementer can handle.
- *
- * @return The AMQP major version.
- */
- public byte getProtocolMajorVersion();
-
- public ProtocolVersion getProtocolVersion();
-}
+/* + * + * 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.protocol; + +import org.apache.qpid.framing.ProtocolVersion; + +/** + * ProtocolVersionAware is implemented by all AMQP handling classes, that need to provide an awareness to callers of + * the version of the AMQP protocol that they are able to handle. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities + * <tr><td> Report the major and minor AMQP version handled. + * </table> + */ +public interface ProtocolVersionAware +{ + /** + * @deprecated + * Reports the AMQP minor version, that the implementer can handle. + * + * @return The AMQP minor version. + */ + public byte getProtocolMinorVersion(); + + /** + * @deprecated + * Reports the AMQP major version, that the implementer can handle. + * + * @return The AMQP major version. + */ + public byte getProtocolMajorVersion(); + + public ProtocolVersion getProtocolVersion(); +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java b/qpid/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java index dc73bce28f..baa68f87f9 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java @@ -1,689 +1,689 @@ -/*
- *
- * 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.util;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.regex.*;
-
-/**
- * CommandLineParser provides a utility for specifying the format of a command line and parsing command lines to ensure
- * that they fit their specified format. A command line is made up of flags and options, both may be refered to as
- * options. A flag is an option that does not take an argument (specifying it means it has the value 'true' and not
- * specifying it means it has the value 'false'). Options must take arguments but they can be set up with defaults so
- * that they take a default value when not set. Options may be mandatory in wich case it is an error not to specify
- * them on the command line. Flags are never mandatory because they are implicitly set to false when not specified.
- *
- * <p/>Some example command lines are:
- *
- * <ul>
- * <li>This one has two options that expect arguments:
- * <pre>
- * cruisecontrol -configfile cruisecontrol.xml -port 9000
- * </pre>
- * <li>This has one no-arg flag and two 'free' arguments:
- * <pre>
- * zip -r project.zip project/*
- * </pre>
- * <li>This one concatenates multiple flags into a single block with only one '-':
- * <pre>
- * jar -tvf mytar.tar
- * </pre>
- *
- * <p/>The parsing rules are:
- *
- * <ol>
- * <li>Flags may be combined after a single '-' because they never take arguments. Normally such flags are single letter
- * flags but this is only a convention and not enforced. Flags of more than one letter are usually specified on their own.
- * <li>Options expecting arguments must always be on their own.
- * <li>The argument to an option may be seperated from it by whitespace or appended directly onto the option.
- * <li>The argument to an option may never begin with a '-' character.
- * <li>All other arguments not beginning with a '-' character are free arguments that do not belong to any option.
- * <li>The second or later of a set of duplicate or repeated flags are ignored.
- * <li>Options are matched up to the shortest matching option. This is because of the possibility of having no space
- * between an option and its argument. This rules out the possibility of using two options where one is an opening
- * substring of the other. For example, the options "foo" and "foobar" cannot be used on the same command line because
- * it is not possible to distinguish the argument "-foobar" from being the "foobar" option or the "foo" option with
- * the "bar" argument.
- * </ol>
- *
- * <p/>By default, unknown options are simply ignored if specified on the command line. This behaviour may be changed
- * so that the parser reports all unknowns as errors by using the {@link #setErrorsOnUnknowns} method.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Accept a command line specification.
- * <tr><td> Parse a command line into properties, validating it against its specification.
- * <tr><td> Report all errors between a command line and its specification.
- * <tr><td> Provide a formatted usage string for a command line.
- * <tr><td> Provide a formatted options in force string for a command line.
- * <tr><td> Allow errors on unknowns behaviour to be turned on or off.
- * </table>
- */
-public class CommandLineParser
-{
- /** Holds a mapping from command line option names to detailed information about those options. */
- private Map<String, CommandLineOption> optionMap = new HashMap<String, CommandLineOption>();
-
- /** Holds a list of parsing errors. */
- private List<String> parsingErrors = new ArrayList<String>();
-
- /** Holds the regular expression matcher to match command line options with. */
- private Matcher optionMatcher = null;
-
- /** Holds the parsed command line properties after parsing. */
- private Properties parsedProperties = null;
-
- /** Flag used to indicate that errors should be created for unknown options. False by default. */
- private boolean errorsOnUnknowns = false;
-
- /**
- * Creates a command line options parser from a command line specification. This is passed to this constructor
- * as an array of arrays of strings. Each array of strings specifies the command line for a single option. A static
- * array may therefore easily be used to configure the command line parser in a single method call with an easily
- * readable format.
- *
- * <p/>Each array of strings must be 2, 3, 4 or 5 elements long. If any of the last three elements are missing they
- * are assumed to be null. The elements specify the following parameters:
- * <ol>
- * <li>The name of the option without the leading '-'. For example, "file". To specify the format of the 'free'
- * arguments use the option names "1", "2", ... and so on.
- * <li>The option comment. A line of text describing the usage of the option. For example, "The file to be processed."
- * <li>The options argument. This is a very short description of the argument to the option, often a single word
- * or a reminder as to the arguments format. When this element is null the option is a flag and does not
- * accept any arguments. For example, "filename" or "(unix | windows)" or null. The actual text specified
- * is only used to print in the usage message to remind the user of the usage of the option.
- * <li>The mandatory flag. When set to "true" an option must always be specified. Any other value, including null,
- * means that the option is mandatory. Flags are always mandatory (see class javadoc for explanation of why) so
- * this is ignored for flags.
- * <li>A regular expression describing the format that the argument must take. Ignored if null.
- * </ol>
- * <p/>An example call to this constructor is:
- *
- * <pre>
- * CommandLineParser commandLine = new CommandLineParser(
- * new String[][] {{"file", "The file to be processed. ", "filename", "true"},
- * {"dir", "Directory to store results in. Current dir used if not set.", "out dir"},
- * {"os", "Operating system EOL format to use.", "(windows | unix)", null, "windows\|unix"},
- * {"v", "Verbose mode. Prints information about the processing as it goes."},
- * {"1", "The processing command to run.", "command", "true", "add\|remove\|list"}});
- * </pre>
- *
- * @param config The configuration as an array of arrays of strings.
- */
- public CommandLineParser(String[][] config)
- {
- // Loop through all the command line option specifications creating details for each in the options map.
- for (int i = 0; i < config.length; i++)
- {
- String[] nextOptionSpec = config[i];
-
- addOption(nextOptionSpec[0], nextOptionSpec[1], (nextOptionSpec.length > 2) ? nextOptionSpec[2] : null,
- (nextOptionSpec.length > 3) ? ("true".equals(nextOptionSpec[3]) ? true : false) : false,
- (nextOptionSpec.length > 4) ? nextOptionSpec[4] : null);
- }
- }
-
- /**
- * Lists all the parsing errors from the most recent parsing in a string.
- *
- * @return All the parsing errors from the most recent parsing.
- */
- public String getErrors()
- {
- // Return the empty string if there are no errors.
- if (parsingErrors.isEmpty())
- {
- return "";
- }
-
- // Concatenate all the parsing errors together.
- String result = "";
-
- for (String s : parsingErrors)
- {
- result += s;
- }
-
- return result;
- }
-
- /**
- * Lists the properties set from the most recent parsing or an empty string if no parsing has been done yet.
- *
- * @return The properties set from the most recent parsing or an empty string if no parsing has been done yet.
- */
- public String getOptionsInForce()
- {
- // Check if there are no properties to report and return and empty string if so.
- if (parsedProperties == null)
- {
- return "";
- }
-
- // List all the properties.
- String result = "Options in force:\n";
-
- for (Map.Entry<Object, Object> property : parsedProperties.entrySet())
- {
- result += property.getKey() + " = " + property.getValue() + "\n";
- }
-
- return result;
- }
-
- /**
- * Generates a usage string consisting of the name of each option and each options argument description and
- * comment.
- *
- * @return A usage string for all the options.
- */
- public String getUsage()
- {
- String result = "Options:\n";
-
- // Print usage on each of the command line options.
- for (CommandLineOption optionInfo : optionMap.values())
- {
- result +=
- optionInfo.option + " " + ((optionInfo.argument != null) ? (optionInfo.argument + " ") : "")
- + optionInfo.comment + "\n";
- }
-
- return result;
- }
-
- /**
- * Control the behaviour of the errors on unkowns reporting. When turned on this reports all unkowns options
- * as errors. When turned off, all unknowns are simply ignored.
- *
- * @param errors The setting of the errors on unkown flag. True to turn it on.
- */
- public void setErrorsOnUnknowns(boolean errors)
- {
- errorsOnUnknowns = errors;
- }
-
- /**
- * Parses a set of command line arguments into a set of properties, keyed by the argument flag. The free arguments
- * are keyed by integers as strings starting at "1" and then "2", ... and so on.
- *
- * <p/>See the class level comment for a description of the parsing rules.
- *
- * @param args The command line arguments.
- *
- * @return The arguments as a set of properties.
- *
- * @throws IllegalArgumentException If the command line cannot be parsed against its specification. If this exception
- * is thrown a call to {@link #getErrors} will provide a diagnostic of the command
- * line errors.
- */
- public Properties parseCommandLine(String[] args) throws IllegalArgumentException
- {
- Properties options = new Properties();
-
- // Used to keep count of the current 'free' argument.
- int free = 1;
-
- // Used to indicate that the most recently parsed option is expecting arguments.
- boolean expectingArgs = false;
-
- // The option that is expecting arguments from the next element of the command line.
- String optionExpectingArgs = null;
-
- // Used to indicate that the most recently parsed option is a duplicate and should be ignored.
- boolean ignore = false;
-
- // Create the regular expression matcher for the command line options.
- String regexp = "^(";
- int optionsAdded = 0;
-
- for (Iterator<String> i = optionMap.keySet().iterator(); i.hasNext();)
- {
- String nextOption = i.next();
-
- // Check that the option is not a free argument definition.
- boolean notFree = false;
-
- try
- {
- Integer.parseInt(nextOption);
- }
- catch (NumberFormatException e)
- {
- notFree = true;
- }
-
- // Add the option to the regular expression matcher if it is not a free argument definition.
- if (notFree)
- {
- regexp += nextOption + (i.hasNext() ? "|" : "");
- optionsAdded++;
- }
- }
-
- // There has to be more that one option in the regular expression or else the compiler complains that the close
- // cannot be nullable if the '?' token is used to make the matched option string optional.
- regexp += ")" + ((optionsAdded > 0) ? "?" : "") + "(.*)";
- Pattern pattern = Pattern.compile(regexp);
-
- // Loop through all the command line arguments.
- for (int i = 0; i < args.length; i++)
- {
- // Check if the next command line argument begins with a '-' character and is therefore the start of
- // an option.
- if (args[i].startsWith("-"))
- {
- // Extract the value of the option without the leading '-'.
- String arg = args[i].substring(1);
-
- // Match up to the longest matching option.
- optionMatcher = pattern.matcher(arg);
- optionMatcher.matches();
-
- String matchedOption = optionMatcher.group(1);
-
- // Match any argument directly appended onto the longest matching option.
- String matchedArg = optionMatcher.group(2);
-
- // Check that a known option was matched.
- if ((matchedOption != null) && !"".equals(matchedOption))
- {
- // Get the command line option information for the matched option.
- CommandLineOption optionInfo = optionMap.get(matchedOption);
-
- // Check if this option is expecting arguments.
- if (optionInfo.expectsArgs)
- {
- // The option is expecting arguments so swallow the next command line argument as an
- // argument to this option.
- expectingArgs = true;
- optionExpectingArgs = matchedOption;
-
- // In the mean time set this options argument to the empty string in case no argument is ever
- // supplied.
- // options.put(matchedOption, "");
- }
-
- // Check if the option was matched on its own and is a flag in which case set that flag.
- if ("".equals(matchedArg) && !optionInfo.expectsArgs)
- {
- options.put(matchedOption, "true");
- }
- // The option was matched as a substring with its argument appended to it or is a flag that is
- // condensed together with other flags.
- else if (!"".equals(matchedArg))
- {
- // Check if the option is a flag and therefore is allowed to be condensed together
- // with other flags.
- if (!optionInfo.expectsArgs)
- {
- // Set the first matched flag.
- options.put(matchedOption, "true");
-
- // Repeat the longest matching process on the remainder but ensure that the remainder
- // consists only of flags as only flags may be condensed together in this fashion.
- do
- {
- // Match the remainder against the options.
- optionMatcher = pattern.matcher(matchedArg);
- optionMatcher.matches();
-
- matchedOption = optionMatcher.group(1);
- matchedArg = optionMatcher.group(2);
-
- // Check that an option was matched.
- if (matchedOption != null)
- {
- // Get the command line option information for the next matched option.
- optionInfo = optionMap.get(matchedOption);
-
- // Ensure that the next option is a flag or raise an error if not.
- if (optionInfo.expectsArgs == true)
- {
- parsingErrors.add("Option " + matchedOption + " cannot be combined with flags.\n");
- }
-
- options.put(matchedOption, "true");
- }
- // The remainder could not be matched against a flag it is either an unknown flag
- // or an illegal argument to a flag.
- else
- {
- parsingErrors.add("Illegal argument to a flag in the option " + arg + "\n");
-
- break;
- }
- }
- // Continue until the remainder of the argument has all been matched with flags.
- while (!"".equals(matchedArg));
- }
- // The option is expecting an argument, so store the unmatched portion against it
- // as its argument.
- else
- {
- // Check the arguments format is correct against any specified format.
- checkArgumentFormat(optionInfo, matchedArg);
-
- // Store the argument against its option (regardless of its format).
- options.put(matchedOption, matchedArg);
-
- // The argument to this flag has already been supplied to it. Do not swallow the
- // next command line argument as an argument to this flag.
- expectingArgs = false;
- }
- }
- }
- else // No matching option was found.
- {
- // Add this to the list of parsing errors if errors on unkowns is being used.
- if (errorsOnUnknowns)
- {
- parsingErrors.add("Option " + matchedOption + " is not a recognized option.\n");
- }
- }
- }
- // The command line argument did not being with a '-' so it is an argument to the previous flag or it
- // is a free argument.
- else
- {
- // Check if a previous flag is expecting to swallow this next argument as its argument.
- if (expectingArgs)
- {
- // Get the option info for the option waiting for arguments.
- CommandLineOption optionInfo = optionMap.get(optionExpectingArgs);
-
- // Check the arguments format is correct against any specified format.
- checkArgumentFormat(optionInfo, args[i]);
-
- // Store the argument against its option (regardless of its format).
- options.put(optionExpectingArgs, args[i]);
-
- // Clear the expecting args flag now that the argument has been swallowed.
- expectingArgs = false;
- optionExpectingArgs = null;
- }
- // This command line option is not an argument to any option. Add it to the set of 'free' options.
- else
- {
- // Get the option info for the free option, if there is any.
- CommandLineOption optionInfo = optionMap.get(Integer.toString(free));
-
- if (optionInfo != null)
- {
- // Check the arguments format is correct against any specified format.
- checkArgumentFormat(optionInfo, args[i]);
- }
-
- // Add to the list of free options.
- options.put(Integer.toString(free), args[i]);
-
- // Move on to the next free argument.
- free++;
- }
- }
- }
-
- // Scan through all the specified options to check that all mandatory options have been set and that all flags
- // that were not set are set to false in the set of properties.
- for (CommandLineOption optionInfo : optionMap.values())
- {
- // Check if this is a flag.
- if (!optionInfo.expectsArgs)
- {
- // Check if the flag is not set in the properties and set it to false if so.
- if (!options.containsKey(optionInfo.option))
- {
- options.put(optionInfo.option, "false");
- }
- }
- // Check if this is a mandatory option and was not set.
- else if (optionInfo.mandatory && !options.containsKey(optionInfo.option))
- {
- // Create an error for the missing option.
- parsingErrors.add("Option " + optionInfo.option + " is mandatory but not was not specified.\n");
- }
- }
-
- // Check if there were any errors.
- if (!parsingErrors.isEmpty())
- {
- // Throw an illegal argument exception to signify that there were parsing errors.
- throw new IllegalArgumentException();
- }
-
- // Convert any name/value pairs in the free arguments into properties in the parsed options.
- options = takeFreeArgsAsProperties(options, 1);
-
- parsedProperties = options;
-
- return options;
- }
-
- /**
- * If a command line has been parsed, calling this method sets all of its parsed options into the specified properties.
- */
- public void addCommandLineToProperties(Properties properties)
- {
- if (parsedProperties != null)
- {
- for (Object propKey : parsedProperties.keySet())
- {
- String name = (String) propKey;
- String value = parsedProperties.getProperty(name);
-
- properties.setProperty(name, value);
- }
- }
- }
-
- /**
- * Resets this command line parser after it has been used to parse a command line. This method will only need
- * to be called to use this parser a second time which is not likely seeing as a command line is usually only
- * specified once. However, it is exposed as a public method for the rare case where this may be done.
- *
- * <p/>Cleans the internal state of this parser, removing all stored errors and information about the options in
- * force.
- */
- public void reset()
- {
- parsingErrors = new ArrayList<String>();
- parsedProperties = null;
- }
-
- /**
- * Adds the option to list of available command line options.
- *
- * @param option The option to add as an available command line option.
- * @param comment A comment for the option.
- * @param argument The text that appears after the option in the usage string.
- * @param mandatory When true, indicates that this option is mandatory.
- * @param formatRegexp The format that the argument must take, defined as a regular expression.
- */
- protected void addOption(String option, String comment, String argument, boolean mandatory, String formatRegexp)
- {
- // Check if usage text has been set in which case this option is expecting arguments.
- boolean expectsArgs = ((argument == null) || argument.equals("")) ? false : true;
-
- // Add the option to the map of command line options.
- CommandLineOption opt = new CommandLineOption(option, expectsArgs, comment, argument, mandatory, formatRegexp);
- optionMap.put(option, opt);
- }
-
- /**
- * Converts the free arguments into property declarations. After parsing the command line the free arguments
- * are numbered from 1, such that the parsed properties contain values for the keys "1", "2", ... This method
- * converts any free arguments declared using the 'name=value' syntax into properties with key 'name', value
- * 'value'.
- *
- * <p/>For example the comand line:
- * <pre>
- * ... debug=true
- * </pre>
- *
- * <p/>After parsing has properties:
- * <pre>[[1, debug=true]]</pre>
- *
- * <p/>After applying this method the properties are:
- * <pre>[[1, debug=true], [debug, true]]</pre>
- *
- * @param properties The parsed command line properties.
- * @param from The free argument index to convert to properties from.
- *
- * @return The parsed command line properties, with free argument name value pairs too.
- */
- private Properties takeFreeArgsAsProperties(Properties properties, int from)
- {
- for (int i = from; true; i++)
- {
- String nextFreeArg = properties.getProperty(Integer.toString(i));
-
- // Terminate the loop once all free arguments have been consumed.
- if (nextFreeArg == null)
- {
- break;
- }
-
- // Split it on the =, strip any whitespace and set it as a system property.
- String[] nameValuePair = nextFreeArg.split("=");
-
- if (nameValuePair.length == 2)
- {
- properties.setProperty(nameValuePair[0], nameValuePair[1]);
- }
- }
-
- return properties;
- }
-
- /**
- * Checks the format of an argument to an option against its specified regular expression format if one has
- * been set. Any errors are added to the list of parsing errors.
- *
- * @param optionInfo The command line option information for the option which is havings its argument checked.
- * @param matchedArg The string argument to the option.
- */
- private void checkArgumentFormat(CommandLineOption optionInfo, String matchedArg)
- {
- // Check if this option enforces a format for its argument.
- if (optionInfo.argumentFormatRegexp != null)
- {
- Pattern pattern = Pattern.compile(optionInfo.argumentFormatRegexp);
- Matcher argumentMatcher = pattern.matcher(matchedArg);
-
- // Check if the argument does not meet its required format.
- if (!argumentMatcher.matches())
- {
- // Create an error for this badly formed argument.
- parsingErrors.add("The argument to option " + optionInfo.option + " does not meet its required format.\n");
- }
- }
- }
-
- /**
- * Extracts all name=value pairs from the command line, sets them all as system properties and also returns
- * a map of properties containing them.
- *
- * @param args The command line.
- * @param commandLine The command line parser.
- * @param properties The properties object to inject all parsed properties into (optional may be <tt>null</tt>).
- *
- * @return A set of properties containing all name=value pairs from the command line.
- */
- public static Properties processCommandLine(String[] args, CommandLineParser commandLine, Properties properties)
- {
- // Capture the command line arguments or display errors and correct usage and then exit.
- Properties options = null;
-
- try
- {
- options = commandLine.parseCommandLine(args);
-
- // Add all the trailing command line options (name=value pairs) to system properties. They may be picked up
- // from there.
- commandLine.addCommandLineToProperties(properties);
- }
- catch (IllegalArgumentException e)
- {
- System.out.println(commandLine.getErrors());
- System.out.println(commandLine.getUsage());
- System.exit(1);
- }
-
- return options;
- }
-
- /**
- * Holds information about a command line options. This includes what its name is, whether or not it is a flag,
- * whether or not it is mandatory, what its user comment is, what its argument reminder text is and what its
- * regular expression format is.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Hold details of a command line option.
- * </table>
- */
- protected class CommandLineOption
- {
- /** Holds the text for the flag to match this argument with. */
- public String option = null;
-
- /** Holds a string describing how to use this command line argument. */
- public String argument = null;
-
- /** Flag that determines whether or not this command line argument can take arguments. */
- public boolean expectsArgs = false;
-
- /** Holds a short comment describing what this command line argument is for. */
- public String comment = null;
-
- /** Flag that determines whether or not this is an mandatory command line argument. */
- public boolean mandatory = false;
-
- /** A regular expression describing what format the argument to this option muist have. */
- public String argumentFormatRegexp = null;
-
- /**
- * Create a command line option object that holds specific information about a command line option.
- *
- * @param option The text that matches the option.
- * @param expectsArgs Whether or not the option expects arguments. It is a flag if this is false.
- * @param comment A comment explaining how to use this option.
- * @param argument A short reminder of the format of the argument to this option/
- * @param mandatory Set to true if this option is mandatory.
- * @param formatRegexp The regular expression that the argument to this option must meet to be valid.
- */
- public CommandLineOption(String option, boolean expectsArgs, String comment, String argument, boolean mandatory,
- String formatRegexp)
- {
- this.option = option;
- this.expectsArgs = expectsArgs;
- this.comment = comment;
- this.argument = argument;
- this.mandatory = mandatory;
- this.argumentFormatRegexp = formatRegexp;
- }
- }
-}
+/* + * + * 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.util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.regex.*; + +/** + * CommandLineParser provides a utility for specifying the format of a command line and parsing command lines to ensure + * that they fit their specified format. A command line is made up of flags and options, both may be refered to as + * options. A flag is an option that does not take an argument (specifying it means it has the value 'true' and not + * specifying it means it has the value 'false'). Options must take arguments but they can be set up with defaults so + * that they take a default value when not set. Options may be mandatory in wich case it is an error not to specify + * them on the command line. Flags are never mandatory because they are implicitly set to false when not specified. + * + * <p/>Some example command lines are: + * + * <ul> + * <li>This one has two options that expect arguments: + * <pre> + * cruisecontrol -configfile cruisecontrol.xml -port 9000 + * </pre> + * <li>This has one no-arg flag and two 'free' arguments: + * <pre> + * zip -r project.zip project/* + * </pre> + * <li>This one concatenates multiple flags into a single block with only one '-': + * <pre> + * jar -tvf mytar.tar + * </pre> + * + * <p/>The parsing rules are: + * + * <ol> + * <li>Flags may be combined after a single '-' because they never take arguments. Normally such flags are single letter + * flags but this is only a convention and not enforced. Flags of more than one letter are usually specified on their own. + * <li>Options expecting arguments must always be on their own. + * <li>The argument to an option may be seperated from it by whitespace or appended directly onto the option. + * <li>The argument to an option may never begin with a '-' character. + * <li>All other arguments not beginning with a '-' character are free arguments that do not belong to any option. + * <li>The second or later of a set of duplicate or repeated flags are ignored. + * <li>Options are matched up to the shortest matching option. This is because of the possibility of having no space + * between an option and its argument. This rules out the possibility of using two options where one is an opening + * substring of the other. For example, the options "foo" and "foobar" cannot be used on the same command line because + * it is not possible to distinguish the argument "-foobar" from being the "foobar" option or the "foo" option with + * the "bar" argument. + * </ol> + * + * <p/>By default, unknown options are simply ignored if specified on the command line. This behaviour may be changed + * so that the parser reports all unknowns as errors by using the {@link #setErrorsOnUnknowns} method. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Accept a command line specification. + * <tr><td> Parse a command line into properties, validating it against its specification. + * <tr><td> Report all errors between a command line and its specification. + * <tr><td> Provide a formatted usage string for a command line. + * <tr><td> Provide a formatted options in force string for a command line. + * <tr><td> Allow errors on unknowns behaviour to be turned on or off. + * </table> + */ +public class CommandLineParser +{ + /** Holds a mapping from command line option names to detailed information about those options. */ + private Map<String, CommandLineOption> optionMap = new HashMap<String, CommandLineOption>(); + + /** Holds a list of parsing errors. */ + private List<String> parsingErrors = new ArrayList<String>(); + + /** Holds the regular expression matcher to match command line options with. */ + private Matcher optionMatcher = null; + + /** Holds the parsed command line properties after parsing. */ + private Properties parsedProperties = null; + + /** Flag used to indicate that errors should be created for unknown options. False by default. */ + private boolean errorsOnUnknowns = false; + + /** + * Creates a command line options parser from a command line specification. This is passed to this constructor + * as an array of arrays of strings. Each array of strings specifies the command line for a single option. A static + * array may therefore easily be used to configure the command line parser in a single method call with an easily + * readable format. + * + * <p/>Each array of strings must be 2, 3, 4 or 5 elements long. If any of the last three elements are missing they + * are assumed to be null. The elements specify the following parameters: + * <ol> + * <li>The name of the option without the leading '-'. For example, "file". To specify the format of the 'free' + * arguments use the option names "1", "2", ... and so on. + * <li>The option comment. A line of text describing the usage of the option. For example, "The file to be processed." + * <li>The options argument. This is a very short description of the argument to the option, often a single word + * or a reminder as to the arguments format. When this element is null the option is a flag and does not + * accept any arguments. For example, "filename" or "(unix | windows)" or null. The actual text specified + * is only used to print in the usage message to remind the user of the usage of the option. + * <li>The mandatory flag. When set to "true" an option must always be specified. Any other value, including null, + * means that the option is mandatory. Flags are always mandatory (see class javadoc for explanation of why) so + * this is ignored for flags. + * <li>A regular expression describing the format that the argument must take. Ignored if null. + * </ol> + * <p/>An example call to this constructor is: + * + * <pre> + * CommandLineParser commandLine = new CommandLineParser( + * new String[][] {{"file", "The file to be processed. ", "filename", "true"}, + * {"dir", "Directory to store results in. Current dir used if not set.", "out dir"}, + * {"os", "Operating system EOL format to use.", "(windows | unix)", null, "windows\|unix"}, + * {"v", "Verbose mode. Prints information about the processing as it goes."}, + * {"1", "The processing command to run.", "command", "true", "add\|remove\|list"}}); + * </pre> + * + * @param config The configuration as an array of arrays of strings. + */ + public CommandLineParser(String[][] config) + { + // Loop through all the command line option specifications creating details for each in the options map. + for (int i = 0; i < config.length; i++) + { + String[] nextOptionSpec = config[i]; + + addOption(nextOptionSpec[0], nextOptionSpec[1], (nextOptionSpec.length > 2) ? nextOptionSpec[2] : null, + (nextOptionSpec.length > 3) ? ("true".equals(nextOptionSpec[3]) ? true : false) : false, + (nextOptionSpec.length > 4) ? nextOptionSpec[4] : null); + } + } + + /** + * Lists all the parsing errors from the most recent parsing in a string. + * + * @return All the parsing errors from the most recent parsing. + */ + public String getErrors() + { + // Return the empty string if there are no errors. + if (parsingErrors.isEmpty()) + { + return ""; + } + + // Concatenate all the parsing errors together. + String result = ""; + + for (String s : parsingErrors) + { + result += s; + } + + return result; + } + + /** + * Lists the properties set from the most recent parsing or an empty string if no parsing has been done yet. + * + * @return The properties set from the most recent parsing or an empty string if no parsing has been done yet. + */ + public String getOptionsInForce() + { + // Check if there are no properties to report and return and empty string if so. + if (parsedProperties == null) + { + return ""; + } + + // List all the properties. + String result = "Options in force:\n"; + + for (Map.Entry<Object, Object> property : parsedProperties.entrySet()) + { + result += property.getKey() + " = " + property.getValue() + "\n"; + } + + return result; + } + + /** + * Generates a usage string consisting of the name of each option and each options argument description and + * comment. + * + * @return A usage string for all the options. + */ + public String getUsage() + { + String result = "Options:\n"; + + // Print usage on each of the command line options. + for (CommandLineOption optionInfo : optionMap.values()) + { + result += + optionInfo.option + " " + ((optionInfo.argument != null) ? (optionInfo.argument + " ") : "") + + optionInfo.comment + "\n"; + } + + return result; + } + + /** + * Control the behaviour of the errors on unkowns reporting. When turned on this reports all unkowns options + * as errors. When turned off, all unknowns are simply ignored. + * + * @param errors The setting of the errors on unkown flag. True to turn it on. + */ + public void setErrorsOnUnknowns(boolean errors) + { + errorsOnUnknowns = errors; + } + + /** + * Parses a set of command line arguments into a set of properties, keyed by the argument flag. The free arguments + * are keyed by integers as strings starting at "1" and then "2", ... and so on. + * + * <p/>See the class level comment for a description of the parsing rules. + * + * @param args The command line arguments. + * + * @return The arguments as a set of properties. + * + * @throws IllegalArgumentException If the command line cannot be parsed against its specification. If this exception + * is thrown a call to {@link #getErrors} will provide a diagnostic of the command + * line errors. + */ + public Properties parseCommandLine(String[] args) throws IllegalArgumentException + { + Properties options = new Properties(); + + // Used to keep count of the current 'free' argument. + int free = 1; + + // Used to indicate that the most recently parsed option is expecting arguments. + boolean expectingArgs = false; + + // The option that is expecting arguments from the next element of the command line. + String optionExpectingArgs = null; + + // Used to indicate that the most recently parsed option is a duplicate and should be ignored. + boolean ignore = false; + + // Create the regular expression matcher for the command line options. + String regexp = "^("; + int optionsAdded = 0; + + for (Iterator<String> i = optionMap.keySet().iterator(); i.hasNext();) + { + String nextOption = i.next(); + + // Check that the option is not a free argument definition. + boolean notFree = false; + + try + { + Integer.parseInt(nextOption); + } + catch (NumberFormatException e) + { + notFree = true; + } + + // Add the option to the regular expression matcher if it is not a free argument definition. + if (notFree) + { + regexp += nextOption + (i.hasNext() ? "|" : ""); + optionsAdded++; + } + } + + // There has to be more that one option in the regular expression or else the compiler complains that the close + // cannot be nullable if the '?' token is used to make the matched option string optional. + regexp += ")" + ((optionsAdded > 0) ? "?" : "") + "(.*)"; + Pattern pattern = Pattern.compile(regexp); + + // Loop through all the command line arguments. + for (int i = 0; i < args.length; i++) + { + // Check if the next command line argument begins with a '-' character and is therefore the start of + // an option. + if (args[i].startsWith("-")) + { + // Extract the value of the option without the leading '-'. + String arg = args[i].substring(1); + + // Match up to the longest matching option. + optionMatcher = pattern.matcher(arg); + optionMatcher.matches(); + + String matchedOption = optionMatcher.group(1); + + // Match any argument directly appended onto the longest matching option. + String matchedArg = optionMatcher.group(2); + + // Check that a known option was matched. + if ((matchedOption != null) && !"".equals(matchedOption)) + { + // Get the command line option information for the matched option. + CommandLineOption optionInfo = optionMap.get(matchedOption); + + // Check if this option is expecting arguments. + if (optionInfo.expectsArgs) + { + // The option is expecting arguments so swallow the next command line argument as an + // argument to this option. + expectingArgs = true; + optionExpectingArgs = matchedOption; + + // In the mean time set this options argument to the empty string in case no argument is ever + // supplied. + // options.put(matchedOption, ""); + } + + // Check if the option was matched on its own and is a flag in which case set that flag. + if ("".equals(matchedArg) && !optionInfo.expectsArgs) + { + options.put(matchedOption, "true"); + } + // The option was matched as a substring with its argument appended to it or is a flag that is + // condensed together with other flags. + else if (!"".equals(matchedArg)) + { + // Check if the option is a flag and therefore is allowed to be condensed together + // with other flags. + if (!optionInfo.expectsArgs) + { + // Set the first matched flag. + options.put(matchedOption, "true"); + + // Repeat the longest matching process on the remainder but ensure that the remainder + // consists only of flags as only flags may be condensed together in this fashion. + do + { + // Match the remainder against the options. + optionMatcher = pattern.matcher(matchedArg); + optionMatcher.matches(); + + matchedOption = optionMatcher.group(1); + matchedArg = optionMatcher.group(2); + + // Check that an option was matched. + if (matchedOption != null) + { + // Get the command line option information for the next matched option. + optionInfo = optionMap.get(matchedOption); + + // Ensure that the next option is a flag or raise an error if not. + if (optionInfo.expectsArgs == true) + { + parsingErrors.add("Option " + matchedOption + " cannot be combined with flags.\n"); + } + + options.put(matchedOption, "true"); + } + // The remainder could not be matched against a flag it is either an unknown flag + // or an illegal argument to a flag. + else + { + parsingErrors.add("Illegal argument to a flag in the option " + arg + "\n"); + + break; + } + } + // Continue until the remainder of the argument has all been matched with flags. + while (!"".equals(matchedArg)); + } + // The option is expecting an argument, so store the unmatched portion against it + // as its argument. + else + { + // Check the arguments format is correct against any specified format. + checkArgumentFormat(optionInfo, matchedArg); + + // Store the argument against its option (regardless of its format). + options.put(matchedOption, matchedArg); + + // The argument to this flag has already been supplied to it. Do not swallow the + // next command line argument as an argument to this flag. + expectingArgs = false; + } + } + } + else // No matching option was found. + { + // Add this to the list of parsing errors if errors on unkowns is being used. + if (errorsOnUnknowns) + { + parsingErrors.add("Option " + matchedOption + " is not a recognized option.\n"); + } + } + } + // The command line argument did not being with a '-' so it is an argument to the previous flag or it + // is a free argument. + else + { + // Check if a previous flag is expecting to swallow this next argument as its argument. + if (expectingArgs) + { + // Get the option info for the option waiting for arguments. + CommandLineOption optionInfo = optionMap.get(optionExpectingArgs); + + // Check the arguments format is correct against any specified format. + checkArgumentFormat(optionInfo, args[i]); + + // Store the argument against its option (regardless of its format). + options.put(optionExpectingArgs, args[i]); + + // Clear the expecting args flag now that the argument has been swallowed. + expectingArgs = false; + optionExpectingArgs = null; + } + // This command line option is not an argument to any option. Add it to the set of 'free' options. + else + { + // Get the option info for the free option, if there is any. + CommandLineOption optionInfo = optionMap.get(Integer.toString(free)); + + if (optionInfo != null) + { + // Check the arguments format is correct against any specified format. + checkArgumentFormat(optionInfo, args[i]); + } + + // Add to the list of free options. + options.put(Integer.toString(free), args[i]); + + // Move on to the next free argument. + free++; + } + } + } + + // Scan through all the specified options to check that all mandatory options have been set and that all flags + // that were not set are set to false in the set of properties. + for (CommandLineOption optionInfo : optionMap.values()) + { + // Check if this is a flag. + if (!optionInfo.expectsArgs) + { + // Check if the flag is not set in the properties and set it to false if so. + if (!options.containsKey(optionInfo.option)) + { + options.put(optionInfo.option, "false"); + } + } + // Check if this is a mandatory option and was not set. + else if (optionInfo.mandatory && !options.containsKey(optionInfo.option)) + { + // Create an error for the missing option. + parsingErrors.add("Option " + optionInfo.option + " is mandatory but not was not specified.\n"); + } + } + + // Check if there were any errors. + if (!parsingErrors.isEmpty()) + { + // Throw an illegal argument exception to signify that there were parsing errors. + throw new IllegalArgumentException(); + } + + // Convert any name/value pairs in the free arguments into properties in the parsed options. + options = takeFreeArgsAsProperties(options, 1); + + parsedProperties = options; + + return options; + } + + /** + * If a command line has been parsed, calling this method sets all of its parsed options into the specified properties. + */ + public void addCommandLineToProperties(Properties properties) + { + if (parsedProperties != null) + { + for (Object propKey : parsedProperties.keySet()) + { + String name = (String) propKey; + String value = parsedProperties.getProperty(name); + + properties.setProperty(name, value); + } + } + } + + /** + * Resets this command line parser after it has been used to parse a command line. This method will only need + * to be called to use this parser a second time which is not likely seeing as a command line is usually only + * specified once. However, it is exposed as a public method for the rare case where this may be done. + * + * <p/>Cleans the internal state of this parser, removing all stored errors and information about the options in + * force. + */ + public void reset() + { + parsingErrors = new ArrayList<String>(); + parsedProperties = null; + } + + /** + * Adds the option to list of available command line options. + * + * @param option The option to add as an available command line option. + * @param comment A comment for the option. + * @param argument The text that appears after the option in the usage string. + * @param mandatory When true, indicates that this option is mandatory. + * @param formatRegexp The format that the argument must take, defined as a regular expression. + */ + protected void addOption(String option, String comment, String argument, boolean mandatory, String formatRegexp) + { + // Check if usage text has been set in which case this option is expecting arguments. + boolean expectsArgs = ((argument == null) || argument.equals("")) ? false : true; + + // Add the option to the map of command line options. + CommandLineOption opt = new CommandLineOption(option, expectsArgs, comment, argument, mandatory, formatRegexp); + optionMap.put(option, opt); + } + + /** + * Converts the free arguments into property declarations. After parsing the command line the free arguments + * are numbered from 1, such that the parsed properties contain values for the keys "1", "2", ... This method + * converts any free arguments declared using the 'name=value' syntax into properties with key 'name', value + * 'value'. + * + * <p/>For example the comand line: + * <pre> + * ... debug=true + * </pre> + * + * <p/>After parsing has properties: + * <pre>[[1, debug=true]]</pre> + * + * <p/>After applying this method the properties are: + * <pre>[[1, debug=true], [debug, true]]</pre> + * + * @param properties The parsed command line properties. + * @param from The free argument index to convert to properties from. + * + * @return The parsed command line properties, with free argument name value pairs too. + */ + private Properties takeFreeArgsAsProperties(Properties properties, int from) + { + for (int i = from; true; i++) + { + String nextFreeArg = properties.getProperty(Integer.toString(i)); + + // Terminate the loop once all free arguments have been consumed. + if (nextFreeArg == null) + { + break; + } + + // Split it on the =, strip any whitespace and set it as a system property. + String[] nameValuePair = nextFreeArg.split("="); + + if (nameValuePair.length == 2) + { + properties.setProperty(nameValuePair[0], nameValuePair[1]); + } + } + + return properties; + } + + /** + * Checks the format of an argument to an option against its specified regular expression format if one has + * been set. Any errors are added to the list of parsing errors. + * + * @param optionInfo The command line option information for the option which is havings its argument checked. + * @param matchedArg The string argument to the option. + */ + private void checkArgumentFormat(CommandLineOption optionInfo, String matchedArg) + { + // Check if this option enforces a format for its argument. + if (optionInfo.argumentFormatRegexp != null) + { + Pattern pattern = Pattern.compile(optionInfo.argumentFormatRegexp); + Matcher argumentMatcher = pattern.matcher(matchedArg); + + // Check if the argument does not meet its required format. + if (!argumentMatcher.matches()) + { + // Create an error for this badly formed argument. + parsingErrors.add("The argument to option " + optionInfo.option + " does not meet its required format.\n"); + } + } + } + + /** + * Extracts all name=value pairs from the command line, sets them all as system properties and also returns + * a map of properties containing them. + * + * @param args The command line. + * @param commandLine The command line parser. + * @param properties The properties object to inject all parsed properties into (optional may be <tt>null</tt>). + * + * @return A set of properties containing all name=value pairs from the command line. + */ + public static Properties processCommandLine(String[] args, CommandLineParser commandLine, Properties properties) + { + // Capture the command line arguments or display errors and correct usage and then exit. + Properties options = null; + + try + { + options = commandLine.parseCommandLine(args); + + // Add all the trailing command line options (name=value pairs) to system properties. They may be picked up + // from there. + commandLine.addCommandLineToProperties(properties); + } + catch (IllegalArgumentException e) + { + System.out.println(commandLine.getErrors()); + System.out.println(commandLine.getUsage()); + System.exit(1); + } + + return options; + } + + /** + * Holds information about a command line options. This includes what its name is, whether or not it is a flag, + * whether or not it is mandatory, what its user comment is, what its argument reminder text is and what its + * regular expression format is. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Hold details of a command line option. + * </table> + */ + protected class CommandLineOption + { + /** Holds the text for the flag to match this argument with. */ + public String option = null; + + /** Holds a string describing how to use this command line argument. */ + public String argument = null; + + /** Flag that determines whether or not this command line argument can take arguments. */ + public boolean expectsArgs = false; + + /** Holds a short comment describing what this command line argument is for. */ + public String comment = null; + + /** Flag that determines whether or not this is an mandatory command line argument. */ + public boolean mandatory = false; + + /** A regular expression describing what format the argument to this option muist have. */ + public String argumentFormatRegexp = null; + + /** + * Create a command line option object that holds specific information about a command line option. + * + * @param option The text that matches the option. + * @param expectsArgs Whether or not the option expects arguments. It is a flag if this is false. + * @param comment A comment explaining how to use this option. + * @param argument A short reminder of the format of the argument to this option/ + * @param mandatory Set to true if this option is mandatory. + * @param formatRegexp The regular expression that the argument to this option must meet to be valid. + */ + public CommandLineOption(String option, boolean expectsArgs, String comment, String argument, boolean mandatory, + String formatRegexp) + { + this.option = option; + this.expectsArgs = expectsArgs; + this.comment = comment; + this.argument = argument; + this.mandatory = mandatory; + this.argumentFormatRegexp = formatRegexp; + } + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/util/FileUtils.java b/qpid/java/common/src/main/java/org/apache/qpid/util/FileUtils.java index 3b8ebc1666..7494745457 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/util/FileUtils.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/util/FileUtils.java @@ -1,195 +1,195 @@ -/*
- *
- * 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.util;
-
-import java.io.*;
-
-/**
- * FileUtils provides some simple helper methods for working with files. It follows the convention of wrapping all
- * checked exceptions as runtimes, so code using these methods is free of try-catch blocks but does not expect to
- * recover from errors.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Read a text file as a string.
- * <tr><td> Open a file or default resource as an input stream.
- * </table>
- */
-public class FileUtils
-{
- /**
- * Reads a text file as a string.
- *
- * @param filename The name of the file.
- *
- * @return The contents of the file.
- */
- public static String readFileAsString(String filename)
- {
- BufferedInputStream is = null;
-
- try
- {
- is = new BufferedInputStream(new FileInputStream(filename));
- }
- catch (FileNotFoundException e)
- {
- throw new RuntimeException(e);
- }
-
- return readStreamAsString(is);
- }
-
- /**
- * Reads a text file as a string.
- *
- * @param file The file.
- *
- * @return The contents of the file.
- */
- public static String readFileAsString(File file)
- {
- BufferedInputStream is = null;
-
- try
- {
- is = new BufferedInputStream(new FileInputStream(file));
- }
- catch (FileNotFoundException e)
- {
- throw new RuntimeException(e);
- }
-
- return readStreamAsString(is);
- }
-
- /**
- * Reads the contents of a reader, one line at a time until the end of stream is encountered, and returns all
- * together as a string.
- *
- * @param is The reader.
- *
- * @return The contents of the reader.
- */
- private static String readStreamAsString(BufferedInputStream is)
- {
- try
- {
- byte[] data = new byte[4096];
-
- StringBuffer inBuffer = new StringBuffer();
-
- String line;
- int read;
-
- while ((read = is.read(data)) != -1)
- {
- String s = new String(data, 0, read);
- inBuffer.append(s);
- }
-
- return inBuffer.toString();
- }
- catch (IOException e)
- {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Either opens the specified filename as an input stream, or uses the default resource loaded using the
- * specified class loader, if opening the file fails or no file name is specified.
- *
- * @param filename The name of the file to open.
- * @param defaultResource The name of the default resource on the classpath if the file cannot be opened.
- * @param cl The classloader to load the default resource with.
- *
- * @return An input stream for the file or resource, or null if one could not be opened.
- */
- public static InputStream openFileOrDefaultResource(String filename, String defaultResource, ClassLoader cl)
- {
- InputStream is = null;
-
- // Flag to indicate whether the default resource should be used. By default this is true, so that the default
- // is used when opening the file fails.
- boolean useDefault = true;
-
- // Try to open the file if one was specified.
- if (filename != null)
- {
- try
- {
- is = new BufferedInputStream(new FileInputStream(new File(filename)));
-
- // Clear the default flag because the file was succesfully opened.
- useDefault = false;
- }
- catch (FileNotFoundException e)
- {
- // Ignore this exception, the default will be used instead.
- }
- }
-
- // Load the default resource if a file was not specified, or if opening the file failed.
- if (useDefault)
- {
- is = cl.getResourceAsStream(defaultResource);
- }
-
- return is;
- }
-
- /**
- * Copies the specified source file to the specified destintaion file. If the destinationst file does not exist,
- * it is created.
- *
- * @param src The source file name.
- * @param dst The destination file name.
- */
- public static void copy(File src, File dst)
- {
- try
- {
- InputStream in = new FileInputStream(src);
- if (!dst.exists())
- {
- dst.createNewFile();
- }
-
- OutputStream out = new FileOutputStream(dst);
-
- // Transfer bytes from in to out
- byte[] buf = new byte[1024];
- int len;
- while ((len = in.read(buf)) > 0)
- {
- out.write(buf, 0, len);
- }
-
- in.close();
- out.close();
- }
- catch (IOException e)
- {
- throw new RuntimeException(e);
- }
- }
-}
+/* + * + * 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.util; + +import java.io.*; + +/** + * FileUtils provides some simple helper methods for working with files. It follows the convention of wrapping all + * checked exceptions as runtimes, so code using these methods is free of try-catch blocks but does not expect to + * recover from errors. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Read a text file as a string. + * <tr><td> Open a file or default resource as an input stream. + * </table> + */ +public class FileUtils +{ + /** + * Reads a text file as a string. + * + * @param filename The name of the file. + * + * @return The contents of the file. + */ + public static String readFileAsString(String filename) + { + BufferedInputStream is = null; + + try + { + is = new BufferedInputStream(new FileInputStream(filename)); + } + catch (FileNotFoundException e) + { + throw new RuntimeException(e); + } + + return readStreamAsString(is); + } + + /** + * Reads a text file as a string. + * + * @param file The file. + * + * @return The contents of the file. + */ + public static String readFileAsString(File file) + { + BufferedInputStream is = null; + + try + { + is = new BufferedInputStream(new FileInputStream(file)); + } + catch (FileNotFoundException e) + { + throw new RuntimeException(e); + } + + return readStreamAsString(is); + } + + /** + * Reads the contents of a reader, one line at a time until the end of stream is encountered, and returns all + * together as a string. + * + * @param is The reader. + * + * @return The contents of the reader. + */ + private static String readStreamAsString(BufferedInputStream is) + { + try + { + byte[] data = new byte[4096]; + + StringBuffer inBuffer = new StringBuffer(); + + String line; + int read; + + while ((read = is.read(data)) != -1) + { + String s = new String(data, 0, read); + inBuffer.append(s); + } + + return inBuffer.toString(); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + + /** + * Either opens the specified filename as an input stream, or uses the default resource loaded using the + * specified class loader, if opening the file fails or no file name is specified. + * + * @param filename The name of the file to open. + * @param defaultResource The name of the default resource on the classpath if the file cannot be opened. + * @param cl The classloader to load the default resource with. + * + * @return An input stream for the file or resource, or null if one could not be opened. + */ + public static InputStream openFileOrDefaultResource(String filename, String defaultResource, ClassLoader cl) + { + InputStream is = null; + + // Flag to indicate whether the default resource should be used. By default this is true, so that the default + // is used when opening the file fails. + boolean useDefault = true; + + // Try to open the file if one was specified. + if (filename != null) + { + try + { + is = new BufferedInputStream(new FileInputStream(new File(filename))); + + // Clear the default flag because the file was succesfully opened. + useDefault = false; + } + catch (FileNotFoundException e) + { + // Ignore this exception, the default will be used instead. + } + } + + // Load the default resource if a file was not specified, or if opening the file failed. + if (useDefault) + { + is = cl.getResourceAsStream(defaultResource); + } + + return is; + } + + /** + * Copies the specified source file to the specified destintaion file. If the destinationst file does not exist, + * it is created. + * + * @param src The source file name. + * @param dst The destination file name. + */ + public static void copy(File src, File dst) + { + try + { + InputStream in = new FileInputStream(src); + if (!dst.exists()) + { + dst.createNewFile(); + } + + OutputStream out = new FileOutputStream(dst); + + // Transfer bytes from in to out + byte[] buf = new byte[1024]; + int len; + while ((len = in.read(buf)) > 0) + { + out.write(buf, 0, len); + } + + in.close(); + out.close(); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java b/qpid/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java index 10f6a27293..4677713dc9 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java @@ -1,75 +1,75 @@ -/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.util;
-
-/**
- * Contains pretty printing convenienve methods for producing formatted logging output, mostly for debugging purposes.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * </table>
- *
- * @todo Drop this. There are already array pretty printing methods it java.utils.Arrays.
- */
-public class PrettyPrintingUtils
-{
- /**
- * Pretty prints an array of ints as a string.
- *
- * @param array The array to pretty print.
- *
- * @return The pretty printed string.
- */
- public static String printArray(int[] array)
- {
- String result = "[";
- for (int i = 0; i < array.length; i++)
- {
- result += array[i];
- result += (i < (array.length - 1)) ? ", " : "";
- }
-
- result += "]";
-
- return result;
- }
-
- /**
- * Pretty prints an array of strings as a string.
- *
- * @param array The array to pretty print.
- *
- * @return The pretty printed string.
- */
- public static String printArray(String[] array)
- {
- String result = "[";
- for (int i = 0; i < array.length; i++)
- {
- result += array[i];
- result += (i < (array.length - 1)) ? ", " : "";
- }
-
- result += "]";
-
- return result;
- }
-}
+/* + * + * 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.util; + +/** + * Contains pretty printing convenienve methods for producing formatted logging output, mostly for debugging purposes. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * </table> + * + * @todo Drop this. There are already array pretty printing methods it java.utils.Arrays. + */ +public class PrettyPrintingUtils +{ + /** + * Pretty prints an array of ints as a string. + * + * @param array The array to pretty print. + * + * @return The pretty printed string. + */ + public static String printArray(int[] array) + { + String result = "["; + for (int i = 0; i < array.length; i++) + { + result += array[i]; + result += (i < (array.length - 1)) ? ", " : ""; + } + + result += "]"; + + return result; + } + + /** + * Pretty prints an array of strings as a string. + * + * @param array The array to pretty print. + * + * @return The pretty printed string. + */ + public static String printArray(String[] array) + { + String result = "["; + for (int i = 0; i < array.length; i++) + { + result += array[i]; + result += (i < (array.length - 1)) ? ", " : ""; + } + + result += "]"; + + return result; + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java b/qpid/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java index 63cf6f252b..d90e3b1a17 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java @@ -1,200 +1,200 @@ -/*
- *
- * 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.util;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.Iterator;
-import java.util.Properties;
-
-/**
- * PropertiesHelper defines some static methods which are useful when working with properties
- * files.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Read properties from an input stream
- * <tr><td> Read properties from a file
- * <tr><td> Read properties from a URL
- * <tr><td> Read properties given a path to a file
- * <tr><td> Trim any whitespace from property values
- * </table>
- */
-public class PropertiesUtils
-{
- /** Used for logging. */
- private static final Logger log = LoggerFactory.getLogger(PropertiesUtils.class);
-
- /**
- * Get properties from an input stream.
- *
- * @param is The input stream.
- *
- * @return The properties loaded from the input stream.
- *
- * @throws IOException If the is an I/O error reading from the stream.
- */
- public static Properties getProperties(InputStream is) throws IOException
- {
- log.debug("getProperties(InputStream): called");
-
- // Create properties object laoded from input stream
- Properties properties = new Properties();
-
- properties.load(is);
-
- return properties;
- }
-
- /**
- * Get properties from a file.
- *
- * @param file The file.
- *
- * @return The properties loaded from the file.
- *
- * @throws IOException If there is an I/O error reading from the file.
- */
- public static Properties getProperties(File file) throws IOException
- {
- log.debug("getProperties(File): called");
-
- // Open the file as an input stream
- InputStream is = new FileInputStream(file);
-
- // Create properties object loaded from the stream
- Properties properties = getProperties(is);
-
- // Close the file
- is.close();
-
- return properties;
- }
-
- /**
- * Get properties from a url.
- *
- * @param url The URL.
- *
- * @return The properties loaded from the url.
- *
- * @throws IOException If there is an I/O error reading from the URL.
- */
- public static Properties getProperties(URL url) throws IOException
- {
- log.debug("getProperties(URL): called");
-
- // Open the URL as an input stream
- InputStream is = url.openStream();
-
- // Create properties object loaded from the stream
- Properties properties = getProperties(is);
-
- // Close the url
- is.close();
-
- return properties;
- }
-
- /**
- * Get properties from a path name. The path name may refer to either a file or a URL.
- *
- * @param pathname The path name.
- *
- * @return The properties loaded from the file or URL.
- *
- * @throws IOException If there is an I/O error reading from the URL or file named by the path.
- */
- public static Properties getProperties(String pathname) throws IOException
- {
- log.debug("getProperties(String): called");
-
- // Check that the path is not null
- if (pathname == null)
- {
- return null;
- }
-
- // Check if the path is a URL
- if (isURL(pathname))
- {
- // The path is a URL
- return getProperties(new URL(pathname));
- }
- else
- {
- // Assume the path is a file name
- return getProperties(new File(pathname));
- }
- }
-
- /**
- * Trims whitespace from property values. This method returns a new set of properties
- * the same as the properties specified as an argument but with any white space removed by
- * the {@link java.lang.String#trim} method.
- *
- * @param properties The properties to trim whitespace from.
- *
- * @return The white space trimmed properties.
- */
- public static Properties trim(Properties properties)
- {
- Properties trimmedProperties = new Properties();
-
- // Loop over all the properties
- for (Iterator i = properties.keySet().iterator(); i.hasNext();)
- {
- String next = (String) i.next();
- String nextValue = properties.getProperty(next);
-
- // Trim the value if it is not null
- if (nextValue != null)
- {
- nextValue.trim();
- }
-
- // Store the trimmed value in the trimmed properties
- trimmedProperties.setProperty(next, nextValue);
- }
-
- return trimmedProperties;
- }
-
- /**
- * Helper method. Guesses whether a string is a URL or not. A String is considered to be a url if it begins with
- * http:, ftp:, or uucp:.
- *
- * @param name The string to test for being a URL.
- *
- * @return True if the string is a URL and false if not.
- */
- private static boolean isURL(String name)
- {
- return (name.toLowerCase().startsWith("http:") || name.toLowerCase().startsWith("ftp:")
- || name.toLowerCase().startsWith("uucp:"));
- }
-}
+/* + * + * 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.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Iterator; +import java.util.Properties; + +/** + * PropertiesHelper defines some static methods which are useful when working with properties + * files. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Read properties from an input stream + * <tr><td> Read properties from a file + * <tr><td> Read properties from a URL + * <tr><td> Read properties given a path to a file + * <tr><td> Trim any whitespace from property values + * </table> + */ +public class PropertiesUtils +{ + /** Used for logging. */ + private static final Logger log = LoggerFactory.getLogger(PropertiesUtils.class); + + /** + * Get properties from an input stream. + * + * @param is The input stream. + * + * @return The properties loaded from the input stream. + * + * @throws IOException If the is an I/O error reading from the stream. + */ + public static Properties getProperties(InputStream is) throws IOException + { + log.debug("getProperties(InputStream): called"); + + // Create properties object laoded from input stream + Properties properties = new Properties(); + + properties.load(is); + + return properties; + } + + /** + * Get properties from a file. + * + * @param file The file. + * + * @return The properties loaded from the file. + * + * @throws IOException If there is an I/O error reading from the file. + */ + public static Properties getProperties(File file) throws IOException + { + log.debug("getProperties(File): called"); + + // Open the file as an input stream + InputStream is = new FileInputStream(file); + + // Create properties object loaded from the stream + Properties properties = getProperties(is); + + // Close the file + is.close(); + + return properties; + } + + /** + * Get properties from a url. + * + * @param url The URL. + * + * @return The properties loaded from the url. + * + * @throws IOException If there is an I/O error reading from the URL. + */ + public static Properties getProperties(URL url) throws IOException + { + log.debug("getProperties(URL): called"); + + // Open the URL as an input stream + InputStream is = url.openStream(); + + // Create properties object loaded from the stream + Properties properties = getProperties(is); + + // Close the url + is.close(); + + return properties; + } + + /** + * Get properties from a path name. The path name may refer to either a file or a URL. + * + * @param pathname The path name. + * + * @return The properties loaded from the file or URL. + * + * @throws IOException If there is an I/O error reading from the URL or file named by the path. + */ + public static Properties getProperties(String pathname) throws IOException + { + log.debug("getProperties(String): called"); + + // Check that the path is not null + if (pathname == null) + { + return null; + } + + // Check if the path is a URL + if (isURL(pathname)) + { + // The path is a URL + return getProperties(new URL(pathname)); + } + else + { + // Assume the path is a file name + return getProperties(new File(pathname)); + } + } + + /** + * Trims whitespace from property values. This method returns a new set of properties + * the same as the properties specified as an argument but with any white space removed by + * the {@link java.lang.String#trim} method. + * + * @param properties The properties to trim whitespace from. + * + * @return The white space trimmed properties. + */ + public static Properties trim(Properties properties) + { + Properties trimmedProperties = new Properties(); + + // Loop over all the properties + for (Iterator i = properties.keySet().iterator(); i.hasNext();) + { + String next = (String) i.next(); + String nextValue = properties.getProperty(next); + + // Trim the value if it is not null + if (nextValue != null) + { + nextValue.trim(); + } + + // Store the trimmed value in the trimmed properties + trimmedProperties.setProperty(next, nextValue); + } + + return trimmedProperties; + } + + /** + * Helper method. Guesses whether a string is a URL or not. A String is considered to be a url if it begins with + * http:, ftp:, or uucp:. + * + * @param name The string to test for being a URL. + * + * @return True if the string is a URL and false if not. + */ + private static boolean isURL(String name) + { + return (name.toLowerCase().startsWith("http:") || name.toLowerCase().startsWith("ftp:") + || name.toLowerCase().startsWith("uucp:")); + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/util/ReflectionUtils.java b/qpid/java/common/src/main/java/org/apache/qpid/util/ReflectionUtils.java index 495918911a..28fb2e0c8a 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/util/ReflectionUtils.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/util/ReflectionUtils.java @@ -1,228 +1,228 @@ -/*
- *
- * 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.util;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-/**
- * Provides helper methods for operating on classes and methods using reflection. Reflection methods tend to return
- * a lot of checked exception so writing code to use them can be tedious and harder to read, especially when such errors
- * are not expected to occur. This class always works with {@link ReflectionUtilsException}, which is a runtime exception,
- * to wrap the checked exceptions raised by the standard Java reflection methods. Code using it does not normally
- * expect these errors to occur, usually does not have a recovery mechanism for them when they do, but is cleaner,
- * quicker to write and easier to read in the majority of cases.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Look up Classes by name.
- * <tr><td> Instantiate Classes by no-arg constructor.
- * </table>
- */
-public class ReflectionUtils
-{
- /**
- * Gets the Class object for a named class.
- *
- * @param className The class to get the Class object for.
- *
- * @return The Class object for the named class.
- */
- public static Class<?> forName(String className)
- {
- try
- {
- return Class.forName(className);
- }
- catch (ClassNotFoundException e)
- {
- throw new ReflectionUtilsException("ClassNotFoundException whilst finding class.", e);
- }
- }
-
- /**
- * Creates an instance of a Class, instantiated through its no-args constructor.
- *
- * @param cls The Class to instantiate.
- * @param <T> The Class type.
- *
- * @return An instance of the class.
- */
- public static <T> T newInstance(Class<? extends T> cls)
- {
- try
- {
- return cls.newInstance();
- }
- catch (InstantiationException e)
- {
- throw new ReflectionUtilsException("InstantiationException whilst instantiating class.", e);
- }
- catch (IllegalAccessException e)
- {
- throw new ReflectionUtilsException("IllegalAccessException whilst instantiating class.", e);
- }
- }
-
- /**
- * Calls a named method on an object with a specified set of parameters, any Java access modifier are overridden.
- *
- * @param o The object to call.
- * @param method The method name to call.
- * @param params The parameters to pass.
- * @param paramClasses The argument types.
- *
- * @return The return value from the method call.
- */
- public static Object callMethodOverridingIllegalAccess(Object o, String method, Object[] params, Class[] paramClasses)
- {
- // Get the objects class.
- Class cls = o.getClass();
-
- // Get the classes of the parameters.
- /*Class[] paramClasses = new Class[params.length];
-
- for (int i = 0; i < params.length; i++)
- {
- paramClasses[i] = params[i].getClass();
- }*/
-
- try
- {
- // Try to find the matching method on the class.
- Method m = cls.getDeclaredMethod(method, paramClasses);
-
- // Make it accessible.
- m.setAccessible(true);
-
- // Invoke it with the parameters.
- return m.invoke(o, params);
- }
- catch (NoSuchMethodException e)
- {
- throw new ReflectionUtilsException("NoSuchMethodException.", e);
- }
- catch (IllegalAccessException e)
- {
- throw new ReflectionUtilsException("IllegalAccessException.", e);
- }
- catch (InvocationTargetException e)
- {
- throw new ReflectionUtilsException("InvocationTargetException", e);
- }
- }
-
- /**
- * Calls a named method on an object with a specified set of parameters.
- *
- * @param o The object to call.
- * @param method The method name to call.
- * @param params The parameters to pass.
- *
- * @return The return value from the method call.
- */
- public static Object callMethod(Object o, String method, Object[] params)
- {
- // Get the objects class.
- Class cls = o.getClass();
-
- // Get the classes of the parameters.
- Class[] paramClasses = new Class[params.length];
-
- for (int i = 0; i < params.length; i++)
- {
- paramClasses[i] = params[i].getClass();
- }
-
- try
- {
- // Try to find the matching method on the class.
- Method m = cls.getMethod(method, paramClasses);
-
- // Invoke it with the parameters.
- return m.invoke(o, params);
- }
- catch (NoSuchMethodException e)
- {
- throw new ReflectionUtilsException("NoSuchMethodException.", e);
- }
- catch (IllegalAccessException e)
- {
- throw new ReflectionUtilsException("IllegalAccessException", e);
- }
- catch (InvocationTargetException e)
- {
- throw new ReflectionUtilsException("InvocationTargetException", e);
- }
- }
-
- /**
- * Calls a constuctor witht the specified arguments.
- *
- * @param constructor The constructor.
- * @param args The arguments.
- * @param <T> The Class type.
- *
- * @return An instance of the class that the constructor is for.
- */
- public static <T> T newInstance(Constructor<T> constructor, Object[] args)
- {
- try
- {
- return constructor.newInstance(args);
- }
- catch (InstantiationException e)
- {
- throw new ReflectionUtilsException("InstantiationException", e);
- }
- catch (IllegalAccessException e)
- {
- throw new ReflectionUtilsException("IllegalAccessException", e);
- }
- catch (InvocationTargetException e)
- {
- throw new ReflectionUtilsException("InvocationTargetException", e);
- }
- }
-
- /**
- * Gets the constructor of a class that takes the specified set of arguments if any matches. If no matching
- * constructor is found then a runtime exception is raised.
- *
- * @param cls The class to get a constructor from.
- * @param args The arguments to match.
- * @param <T> The class type.
- *
- * @return The constructor.
- */
- public static <T> Constructor<T> getConstructor(Class<T> cls, Class[] args)
- {
- try
- {
- return cls.getConstructor(args);
- }
- catch (NoSuchMethodException e)
- {
- throw new ReflectionUtilsException("NoSuchMethodException", e);
- }
- }
-}
+/* + * + * 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.util; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * Provides helper methods for operating on classes and methods using reflection. Reflection methods tend to return + * a lot of checked exception so writing code to use them can be tedious and harder to read, especially when such errors + * are not expected to occur. This class always works with {@link ReflectionUtilsException}, which is a runtime exception, + * to wrap the checked exceptions raised by the standard Java reflection methods. Code using it does not normally + * expect these errors to occur, usually does not have a recovery mechanism for them when they do, but is cleaner, + * quicker to write and easier to read in the majority of cases. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Look up Classes by name. + * <tr><td> Instantiate Classes by no-arg constructor. + * </table> + */ +public class ReflectionUtils +{ + /** + * Gets the Class object for a named class. + * + * @param className The class to get the Class object for. + * + * @return The Class object for the named class. + */ + public static Class<?> forName(String className) + { + try + { + return Class.forName(className); + } + catch (ClassNotFoundException e) + { + throw new ReflectionUtilsException("ClassNotFoundException whilst finding class.", e); + } + } + + /** + * Creates an instance of a Class, instantiated through its no-args constructor. + * + * @param cls The Class to instantiate. + * @param <T> The Class type. + * + * @return An instance of the class. + */ + public static <T> T newInstance(Class<? extends T> cls) + { + try + { + return cls.newInstance(); + } + catch (InstantiationException e) + { + throw new ReflectionUtilsException("InstantiationException whilst instantiating class.", e); + } + catch (IllegalAccessException e) + { + throw new ReflectionUtilsException("IllegalAccessException whilst instantiating class.", e); + } + } + + /** + * Calls a named method on an object with a specified set of parameters, any Java access modifier are overridden. + * + * @param o The object to call. + * @param method The method name to call. + * @param params The parameters to pass. + * @param paramClasses The argument types. + * + * @return The return value from the method call. + */ + public static Object callMethodOverridingIllegalAccess(Object o, String method, Object[] params, Class[] paramClasses) + { + // Get the objects class. + Class cls = o.getClass(); + + // Get the classes of the parameters. + /*Class[] paramClasses = new Class[params.length]; + + for (int i = 0; i < params.length; i++) + { + paramClasses[i] = params[i].getClass(); + }*/ + + try + { + // Try to find the matching method on the class. + Method m = cls.getDeclaredMethod(method, paramClasses); + + // Make it accessible. + m.setAccessible(true); + + // Invoke it with the parameters. + return m.invoke(o, params); + } + catch (NoSuchMethodException e) + { + throw new ReflectionUtilsException("NoSuchMethodException.", e); + } + catch (IllegalAccessException e) + { + throw new ReflectionUtilsException("IllegalAccessException.", e); + } + catch (InvocationTargetException e) + { + throw new ReflectionUtilsException("InvocationTargetException", e); + } + } + + /** + * Calls a named method on an object with a specified set of parameters. + * + * @param o The object to call. + * @param method The method name to call. + * @param params The parameters to pass. + * + * @return The return value from the method call. + */ + public static Object callMethod(Object o, String method, Object[] params) + { + // Get the objects class. + Class cls = o.getClass(); + + // Get the classes of the parameters. + Class[] paramClasses = new Class[params.length]; + + for (int i = 0; i < params.length; i++) + { + paramClasses[i] = params[i].getClass(); + } + + try + { + // Try to find the matching method on the class. + Method m = cls.getMethod(method, paramClasses); + + // Invoke it with the parameters. + return m.invoke(o, params); + } + catch (NoSuchMethodException e) + { + throw new ReflectionUtilsException("NoSuchMethodException.", e); + } + catch (IllegalAccessException e) + { + throw new ReflectionUtilsException("IllegalAccessException", e); + } + catch (InvocationTargetException e) + { + throw new ReflectionUtilsException("InvocationTargetException", e); + } + } + + /** + * Calls a constuctor witht the specified arguments. + * + * @param constructor The constructor. + * @param args The arguments. + * @param <T> The Class type. + * + * @return An instance of the class that the constructor is for. + */ + public static <T> T newInstance(Constructor<T> constructor, Object[] args) + { + try + { + return constructor.newInstance(args); + } + catch (InstantiationException e) + { + throw new ReflectionUtilsException("InstantiationException", e); + } + catch (IllegalAccessException e) + { + throw new ReflectionUtilsException("IllegalAccessException", e); + } + catch (InvocationTargetException e) + { + throw new ReflectionUtilsException("InvocationTargetException", e); + } + } + + /** + * Gets the constructor of a class that takes the specified set of arguments if any matches. If no matching + * constructor is found then a runtime exception is raised. + * + * @param cls The class to get a constructor from. + * @param args The arguments to match. + * @param <T> The class type. + * + * @return The constructor. + */ + public static <T> Constructor<T> getConstructor(Class<T> cls, Class[] args) + { + try + { + return cls.getConstructor(args); + } + catch (NoSuchMethodException e) + { + throw new ReflectionUtilsException("NoSuchMethodException", e); + } + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/util/ReflectionUtilsException.java b/qpid/java/common/src/main/java/org/apache/qpid/util/ReflectionUtilsException.java index 20499641ac..c2862a755b 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/util/ReflectionUtilsException.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/util/ReflectionUtilsException.java @@ -1,44 +1,44 @@ -/*
- *
- * 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.util;
-
-/**
- * Wraps a checked exception that occurs when {@link ReflectionUtils} encounters checked exceptions using standard
- * Java reflection methods.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Wrap a checked reflection exception.
- * </table>
- */
-public class ReflectionUtilsException extends RuntimeException
-{
- /**
- * Creates a runtime reflection exception, from a checked one.
- *
- * @param message The message.
- * @param cause The causing exception.
- */
- public ReflectionUtilsException(String message, Throwable cause)
- {
- super(message, cause);
- }
-}
+/* + * + * 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.util; + +/** + * Wraps a checked exception that occurs when {@link ReflectionUtils} encounters checked exceptions using standard + * Java reflection methods. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Wrap a checked reflection exception. + * </table> + */ +public class ReflectionUtilsException extends RuntimeException +{ + /** + * Creates a runtime reflection exception, from a checked one. + * + * @param message The message. + * @param cause The causing exception. + */ + public ReflectionUtilsException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/AlreadyUnblockedException.java b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/AlreadyUnblockedException.java index 4acea0e2ec..ef43d1c8a8 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/AlreadyUnblockedException.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/AlreadyUnblockedException.java @@ -1,13 +1,13 @@ -package org.apache.qpid.util.concurrent;
-
-/**
- * Used to signal that a data element and its producer cannot be requeued or sent an error message when using a
- * {@link BatchSynchQueue} because the producer has already been unblocked by an unblocking take on the queue.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Signal that an unblocking take has already occurred.
- * </table>
- */
-public class AlreadyUnblockedException extends RuntimeException
-{ }
+package org.apache.qpid.util.concurrent; + +/** + * Used to signal that a data element and its producer cannot be requeued or sent an error message when using a + * {@link BatchSynchQueue} because the producer has already been unblocked by an unblocking take on the queue. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Signal that an unblocking take has already occurred. + * </table> + */ +public class AlreadyUnblockedException extends RuntimeException +{ } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueue.java b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueue.java index cf2abfffb8..261eecb561 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueue.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueue.java @@ -1,101 +1,101 @@ -package org.apache.qpid.util.concurrent;
-
-import java.util.Collection;
-import java.util.concurrent.BlockingQueue;
-
-/**
- * BatchSynchQueue is an abstraction of the classic producer/consumer buffer pattern for thread interaction. In this
- * pattern threads can deposit data onto a buffer whilst other threads take data from the buffer and perform usefull
- * work with it. A BatchSynchQueue adds to this the possibility that producers can be blocked until their data is
- * consumed or until a consumer chooses to release the producer some time after consuming the data from the queue.
- *
- * <p>There are a number of possible advantages to using this technique when compared with having the producers
- * processing their own data:
- *
- * <ul>
- * <li>Data may be deposited asynchronously in the buffer allowing the producers to continue running.</li>
- * <li>Data may be deposited synchronously in the buffer so that producers wait until their data has been processed
- * before being allowed to continue.</li>
- * <li>Variable rates of production/consumption can be smoothed over by the buffer as it provides space in memory to
- * hold data between production and consumption.</li>
- * <li>Consumers may be able to batch data as they consume it leading to more efficient consumption over
- * individual data item consumption where latency associated with the consume operation can be ammortized.
- * For example, it may be possibly to ammortize the cost of a disk seek over many producers.</li>
- * <li>Data from seperate threads can be combined together in the buffer, providing a convenient way of spreading work
- * amongst many workers and gathering the results together again.</li>
- * <li>Different types of queue can be used to hold the buffer, resulting in different processing orders. For example,
- * lifo, fifo, priority heap, etc.</li>
- * </ul>
- *
- * <p/>The asynchronous type of producer/consumer buffers is already well supported by the java.util.concurrent package
- * (in Java 5) and there is also a synchronous queue implementation available there too. This interface extends the
- * blocking queue with some more methods for controlling a synchronous blocking queue. In particular it adds additional
- * take methods that can be used to take data from a queue without releasing producers, so that consumers have an
- * opportunity to confirm correct processing of the data before producers are released. It also adds a put method with
- * exceptions so that consumers can signal exception cases back to producers where there are errors in the data.
- *
- * <p/>This type of queue is usefull in situations where consumers can obtain an efficiency gain by batching data
- * from many threads but where synchronous handling of that data is neccessary because producers need to know that
- * their data has been processed before they continue. For example, sending a bundle of messages together, or writing
- * many records to disk at once, may result in improved performance but the originators of the messages or disk records
- * need confirmation that their data has really been sent or saved to disk.
- *
- * <p/>The consumer can put an element back onto the queue or send an error message to the elements producer using the
- * {@link SynchRecord} interface.
- *
- * <p/>The {@link #take()}, {@link #drainTo(java.util.Collection<? super E>)} and
- * {@link #drainTo(java.util.Collection<? super E>, int)} methods from {@link BlockingQueue} should behave as if they
- * have been called with unblock set to false. That is they take elements from the queue but leave the producers
- * blocked. These methods do not return collections of {@link SynchRecord}s so they do not supply an interface through
- * which errors or re-queuings can be applied. If these methods are used then the consumer must succesfully process
- * all the records it takes.
- *
- * <p/>The {@link #put} method should silently swallow any exceptions that consumers attempt to return to the caller.
- * In order to handle exceptions the {@link #tryPut} method must be used.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Handle synchronous puts, with possible exceptions.
- * <tr><td> Allow consumers to take many records from a queue in a batch.
- * <tr><td> Allow consumers to decide when to unblock synchronous producers.
- * </table>
- */
-public interface BatchSynchQueue<E> extends BlockingQueue<E>
-{
- /**
- * Tries a synchronous put into the queue. If a consumer encounters an exception condition whilst processing the
- * data that is put, then this is returned to the caller wrapped inside a {@link SynchException}.
- *
- * @param e The data element to put into the queue.
- *
- * @throws InterruptedException If the thread is interrupted whilst waiting to write to the queue or whilst waiting
- * on its entry in the queue being consumed.
- * @throws SynchException If a consumer encounters an error whilst processing the data element.
- */
- public void tryPut(E e) throws InterruptedException, SynchException;
-
- /**
- * Takes all available data items from the queue or blocks until some become available. The returned items
- * are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their
- * producers, where the producers are still blocked.
- *
- * @param c The collection to drain the data items into.
- * @param unblock If set to <tt>true</tt> the producers for the taken items will be immediately unblocked.
- *
- * @return A count of the number of elements that were drained from the queue.
- */
- public SynchRef drainTo(Collection<SynchRecord<E>> c, boolean unblock);
-
- /**
- * Takes up to maxElements available data items from the queue or blocks until some become available. The returned
- * items are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their
- * producers, where the producers are still blocked.
- *
- * @param c The collection to drain the data items into.
- * @param maxElements The maximum number of elements to drain.
- * @param unblock If set to <tt>true</tt> the producers for the taken items will be immediately unblocked.
- *
- * @return A count of the number of elements that were drained from the queue.
- */
- public SynchRef drainTo(Collection<SynchRecord<E>> c, int maxElements, boolean unblock);
-}
+package org.apache.qpid.util.concurrent; + +import java.util.Collection; +import java.util.concurrent.BlockingQueue; + +/** + * BatchSynchQueue is an abstraction of the classic producer/consumer buffer pattern for thread interaction. In this + * pattern threads can deposit data onto a buffer whilst other threads take data from the buffer and perform usefull + * work with it. A BatchSynchQueue adds to this the possibility that producers can be blocked until their data is + * consumed or until a consumer chooses to release the producer some time after consuming the data from the queue. + * + * <p>There are a number of possible advantages to using this technique when compared with having the producers + * processing their own data: + * + * <ul> + * <li>Data may be deposited asynchronously in the buffer allowing the producers to continue running.</li> + * <li>Data may be deposited synchronously in the buffer so that producers wait until their data has been processed + * before being allowed to continue.</li> + * <li>Variable rates of production/consumption can be smoothed over by the buffer as it provides space in memory to + * hold data between production and consumption.</li> + * <li>Consumers may be able to batch data as they consume it leading to more efficient consumption over + * individual data item consumption where latency associated with the consume operation can be ammortized. + * For example, it may be possibly to ammortize the cost of a disk seek over many producers.</li> + * <li>Data from seperate threads can be combined together in the buffer, providing a convenient way of spreading work + * amongst many workers and gathering the results together again.</li> + * <li>Different types of queue can be used to hold the buffer, resulting in different processing orders. For example, + * lifo, fifo, priority heap, etc.</li> + * </ul> + * + * <p/>The asynchronous type of producer/consumer buffers is already well supported by the java.util.concurrent package + * (in Java 5) and there is also a synchronous queue implementation available there too. This interface extends the + * blocking queue with some more methods for controlling a synchronous blocking queue. In particular it adds additional + * take methods that can be used to take data from a queue without releasing producers, so that consumers have an + * opportunity to confirm correct processing of the data before producers are released. It also adds a put method with + * exceptions so that consumers can signal exception cases back to producers where there are errors in the data. + * + * <p/>This type of queue is usefull in situations where consumers can obtain an efficiency gain by batching data + * from many threads but where synchronous handling of that data is neccessary because producers need to know that + * their data has been processed before they continue. For example, sending a bundle of messages together, or writing + * many records to disk at once, may result in improved performance but the originators of the messages or disk records + * need confirmation that their data has really been sent or saved to disk. + * + * <p/>The consumer can put an element back onto the queue or send an error message to the elements producer using the + * {@link SynchRecord} interface. + * + * <p/>The {@link #take()}, {@link #drainTo(java.util.Collection<? super E>)} and + * {@link #drainTo(java.util.Collection<? super E>, int)} methods from {@link BlockingQueue} should behave as if they + * have been called with unblock set to false. That is they take elements from the queue but leave the producers + * blocked. These methods do not return collections of {@link SynchRecord}s so they do not supply an interface through + * which errors or re-queuings can be applied. If these methods are used then the consumer must succesfully process + * all the records it takes. + * + * <p/>The {@link #put} method should silently swallow any exceptions that consumers attempt to return to the caller. + * In order to handle exceptions the {@link #tryPut} method must be used. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Handle synchronous puts, with possible exceptions. + * <tr><td> Allow consumers to take many records from a queue in a batch. + * <tr><td> Allow consumers to decide when to unblock synchronous producers. + * </table> + */ +public interface BatchSynchQueue<E> extends BlockingQueue<E> +{ + /** + * Tries a synchronous put into the queue. If a consumer encounters an exception condition whilst processing the + * data that is put, then this is returned to the caller wrapped inside a {@link SynchException}. + * + * @param e The data element to put into the queue. + * + * @throws InterruptedException If the thread is interrupted whilst waiting to write to the queue or whilst waiting + * on its entry in the queue being consumed. + * @throws SynchException If a consumer encounters an error whilst processing the data element. + */ + public void tryPut(E e) throws InterruptedException, SynchException; + + /** + * Takes all available data items from the queue or blocks until some become available. The returned items + * are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their + * producers, where the producers are still blocked. + * + * @param c The collection to drain the data items into. + * @param unblock If set to <tt>true</tt> the producers for the taken items will be immediately unblocked. + * + * @return A count of the number of elements that were drained from the queue. + */ + public SynchRef drainTo(Collection<SynchRecord<E>> c, boolean unblock); + + /** + * Takes up to maxElements available data items from the queue or blocks until some become available. The returned + * items are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their + * producers, where the producers are still blocked. + * + * @param c The collection to drain the data items into. + * @param maxElements The maximum number of elements to drain. + * @param unblock If set to <tt>true</tt> the producers for the taken items will be immediately unblocked. + * + * @return A count of the number of elements that were drained from the queue. + */ + public SynchRef drainTo(Collection<SynchRecord<E>> c, int maxElements, boolean unblock); +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueueBase.java b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueueBase.java index 47eff4be19..d1c1abd285 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueueBase.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueueBase.java @@ -1,813 +1,813 @@ -package org.apache.qpid.util.concurrent;
-
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.ReentrantLock;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-
-/**
- * Synchronous/Asynchronous puts. Asynchronous is easiest, just wait till can write to queue and deposit data.
- * Synchronous is harder. Deposit data, but then must wait until deposited element/elements are taken before being
- * allowed to unblock and continue. Consumer needs some options here too. Can just get the data from the buffer and
- * allow any producers unblocked as a result to continue, or can get data but continue blocking while the data is
- * processed before sending a message to do the unblocking. Synch/Asynch mode to be controlled by a switch.
- * Unblocking/not unblocking during consumer processing to be controlled by the consumers calls.
- *
- * <p/>Implementing sub-classes only need to supply an implementation of a queue to produce a valid concrete
- * implementation of this. This queue is only accessed through the methods {@link #insert}, {@link #extract},
- * {@link #getBufferCapacity()}, {@link #peekAtBufferHead()}. An implementation can override these methods to implement
- * the buffer other than by a queue, for example, by using an array.
- *
- * <p/>Normal queue methods to work asynchronously.
- * <p/>Put, take and drain methods from the BlockingQueue interface work synchronously but unblock producers immediately
- * when their data is taken.
- * <p/>The additional put, take and drain methods from the BatchSynchQueue interface work synchronously and provide the
- * option to keep producers blocked until the consumer decides to release them.
- *
- * <p/>Removed take method that keeps producers blocked as it is pointless. Essentially it reduces this class to
- * synchronous processing of individual data items, which negates the point of the hand-off design. The efficiency
- * gain of the hand off design comes in being able to batch consume requests, ammortizing latency (such as caused by io)
- * accross many producers. The only advantage of the single blocking take method is that it did take advantage of the
- * queue ordering, which ma be usefull, for example to apply a priority ordering amongst producers. This is also an
- * advantage over the java.util.concurrent.SynchronousQueue which doesn't have a backing queue which can be used to
- * apply orderings. If a single item take is really needed can just use the drainTo method with a maximum of one item.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * </table>
- */
-public abstract class BatchSynchQueueBase<E> extends AbstractQueue<E> implements BatchSynchQueue<E>
-{
- /** Used for logging. */
- private static final Logger log = LoggerFactory.getLogger(BatchSynchQueueBase.class);
-
- /** Holds a reference to the queue implementation that holds the buffer. */
- Queue<SynchRecordImpl<E>> buffer;
-
- /** Holds the number of items in the queue */
- private int count;
-
- /** Main lock guarding all access */
- private ReentrantLock lock;
-
- /** Condition for waiting takes */
- private Condition notEmpty;
-
- /** Condition for waiting puts */
- private Condition notFull;
-
- /**
- * Creates a batch synch queue without fair thread scheduling.
- */
- public BatchSynchQueueBase()
- {
- this(false);
- }
-
- /**
- * Ensures that the underlying buffer implementation is created.
- *
- * @param fair <tt>true</tt> if fairness is to be applied to threads waiting to access the buffer.
- */
- public BatchSynchQueueBase(boolean fair)
- {
- buffer = this.createQueue();
-
- // Create the buffer lock with the fairness flag set accordingly.
- lock = new ReentrantLock(fair);
-
- // Create the non-empty and non-full condition monitors on the buffer lock.
- notEmpty = lock.newCondition();
- notFull = lock.newCondition();
- }
-
- /**
- * Returns an iterator over the elements contained in this collection.
- *
- * @return An iterator over the elements contained in this collection.
- */
- public Iterator<E> iterator()
- {
- throw new RuntimeException("Not implemented.");
- }
-
- /**
- * Returns the number of elements in this collection. If the collection contains more than
- * <tt>Integer.MAX_VALUE</tt> elements, returns <tt>Integer.MAX_VALUE</tt>.
- *
- * @return The number of elements in this collection.
- */
- public int size()
- {
- final ReentrantLock lock = this.lock;
- lock.lock();
-
- try
- {
- return count;
- }
- finally
- {
- lock.unlock();
- }
- }
-
- /**
- * Inserts the specified element into this queue, if possible. When using queues that may impose insertion
- * restrictions (for example capacity bounds), method <tt>offer</tt> is generally preferable to method
- * {@link java.util.Collection#add}, which can fail to insert an element only by throwing an exception.
- *
- * @param e The element to insert.
- *
- * @return <tt>true</tt> if it was possible to add the element to this queue, else <tt>false</tt>
- */
- public boolean offer(E e)
- {
- if (e == null)
- {
- throw new NullPointerException();
- }
-
- final ReentrantLock lock = this.lock;
- lock.lock();
-
- try
- {
- return insert(e, false);
- }
- finally
- {
- lock.unlock();
- }
- }
-
- /**
- * Inserts the specified element into this queue, waiting if necessary up to the specified wait time for space to
- * become available.
- *
- * @param e The element to add.
- * @param timeout How long to wait before giving up, in units of <tt>unit</tt>
- * @param unit A <tt>TimeUnit</tt> determining how to interpret the <tt>timeout</tt> parameter.
- *
- * @return <tt>true</tt> if successful, or <tt>false</tt> if the specified waiting time elapses before space is
- * available.
- *
- * @throws InterruptedException If interrupted while waiting.
- * @throws NullPointerException If the specified element is <tt>null</tt>.
- */
- public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException
- {
- if (e == null)
- {
- throw new NullPointerException();
- }
-
- final ReentrantLock lock = this.lock;
- lock.lockInterruptibly();
-
- long nanos = unit.toNanos(timeout);
-
- try
- {
- do
- {
- if (insert(e, false))
- {
- return true;
- }
-
- try
- {
- nanos = notFull.awaitNanos(nanos);
- }
- catch (InterruptedException ie)
- {
- notFull.signal(); // propagate to non-interrupted thread
- throw ie;
- }
- }
- while (nanos > 0);
-
- return false;
- }
- finally
- {
- lock.unlock();
- }
- }
-
- /**
- * Retrieves and removes the head of this queue, or <tt>null</tt> if this queue is empty.
- *
- * @return The head of this queue, or <tt>null</tt> if this queue is empty.
- */
- public E poll()
- {
- final ReentrantLock lock = this.lock;
-
- lock.lock();
- try
- {
- if (count == 0)
- {
- return null;
- }
-
- E x = extract(true, true).getElement();
-
- return x;
- }
- finally
- {
- lock.unlock();
- }
- }
-
- /**
- * Retrieves and removes the head of this queue, waiting if necessary up to the specified wait time if no elements
- * are present on this queue.
- *
- * @param timeout How long to wait before giving up, in units of <tt>unit</tt>.
- * @param unit A <tt>TimeUnit</tt> determining how to interpret the <tt>timeout</tt> parameter.
- *
- * @return The head of this queue, or <tt>null</tt> if the specified waiting time elapses before an element is present.
- *
- * @throws InterruptedException If interrupted while waiting.
- */
- public E poll(long timeout, TimeUnit unit) throws InterruptedException
- {
- final ReentrantLock lock = this.lock;
- lock.lockInterruptibly();
- try
- {
- long nanos = unit.toNanos(timeout);
-
- do
- {
- if (count != 0)
- {
- E x = extract(true, true).getElement();
-
- return x;
- }
-
- try
- {
- nanos = notEmpty.awaitNanos(nanos);
- }
- catch (InterruptedException ie)
- {
- notEmpty.signal(); // propagate to non-interrupted thread
- throw ie;
- }
- }
- while (nanos > 0);
-
- return null;
- }
- finally
- {
- lock.unlock();
- }
- }
-
- /**
- * Retrieves, but does not remove, the head of this queue, returning <tt>null</tt> if this queue is empty.
- *
- * @return The head of this queue, or <tt>null</tt> if this queue is empty.
- */
- public E peek()
- {
- final ReentrantLock lock = this.lock;
- lock.lock();
-
- try
- {
- return peekAtBufferHead();
- }
- finally
- {
- lock.unlock();
- }
- }
-
- /**
- * Returns the number of elements that this queue can ideally (in the absence of memory or resource constraints)
- * accept without blocking, or <tt>Integer.MAX_VALUE</tt> if there is no intrinsic limit.
- *
- * <p>Note that you <em>cannot</em> always tell if an attempt to <tt>add</tt> an element will succeed by
- * inspecting <tt>remainingCapacity</tt> because it may be the case that another thread is about to <tt>put</tt>
- * or <tt>take</tt> an element.
- *
- * @return The remaining capacity.
- */
- public int remainingCapacity()
- {
- final ReentrantLock lock = this.lock;
- lock.lock();
-
- try
- {
- return getBufferCapacity() - count;
- }
- finally
- {
- lock.unlock();
- }
- }
-
- /**
- * Adds the specified element to this queue, waiting if necessary for space to become available.
- *
- * <p/>This method delegated to {@link #tryPut} which can raise {@link SynchException}s. If any are raised
- * this method silently ignores them. Use the {@link #tryPut} method directly if you want to catch these
- * exceptions.
- *
- * @param e The element to add.
- *
- * @throws InterruptedException If interrupted while waiting.
- */
- public void put(E e) throws InterruptedException
- {
- try
- {
- tryPut(e);
- }
- catch (SynchException ex)
- {
- // This exception is deliberately ignored. See the method comment for information about this.
- }
- }
-
- /**
- * Tries a synchronous put into the queue. If a consumer encounters an exception condition whilst processing the
- * data that is put, then this is returned to the caller wrapped inside a {@link SynchException}.
- *
- * @param e The data element to put into the queue. Cannot be null.
- *
- * @throws InterruptedException If the thread is interrupted whilst waiting to write to the queue or whilst waiting
- * on its entry in the queue being consumed.
- * @throws SynchException If a consumer encounters an error whilst processing the data element.
- */
- public void tryPut(E e) throws InterruptedException, SynchException
- {
- if (e == null)
- {
- throw new NullPointerException();
- }
-
- // final Queue<E> items = this.buffer;
- final ReentrantLock lock = this.lock;
- lock.lockInterruptibly();
-
- try
- {
- while (count == getBufferCapacity())
- {
- // Release the lock and wait until the queue is not full.
- notFull.await();
- }
- }
- catch (InterruptedException ie)
- {
- notFull.signal(); // propagate to non-interrupted thread
- throw ie;
- }
-
- // There is room in the queue so insert must succeed. Insert into the queu, release the lock and block
- // the producer until its data is taken.
- insert(e, true);
- }
-
- /**
- * Retrieves and removes the head of this queue, waiting if no elements are present on this queue.
- * Any producer that has its data element taken by this call will be immediately unblocked. To keep the
- * producer blocked whilst taking just a single item, use the
- * {@link #drainTo(java.util.Collection<org.apache.qpid.util.concurrent.SynchRecord<E>>, int, boolean)}
- * method. There is no take method to do that because there is not usually any advantage in a synchronous hand
- * off design that consumes data one item at a time. It is normal to consume data in chunks to ammortize consumption
- * latencies accross many producers where possible.
- *
- * @return The head of this queue.
- *
- * @throws InterruptedException if interrupted while waiting.
- */
- public E take() throws InterruptedException
- {
- final ReentrantLock lock = this.lock;
- lock.lockInterruptibly();
-
- try
- {
- try
- {
- while (count == 0)
- {
- // Release the lock and wait until the queue becomes non-empty.
- notEmpty.await();
- }
- }
- catch (InterruptedException ie)
- {
- notEmpty.signal(); // propagate to non-interrupted thread
- throw ie;
- }
-
- // There is data in the queue so extraction must succeed. Notify any waiting threads that the queue is
- // not full, and unblock the producer that owns the data item that is taken.
- E x = extract(true, true).getElement();
-
- return x;
- }
- finally
- {
- lock.unlock();
- }
- }
-
- /**
- * Removes all available elements from this queue and adds them into the given collection. This operation may be
- * more efficient than repeatedly polling this queue. A failure encountered while attempting to <tt>add</tt> elements
- * to collection <tt>c</tt> may result in elements being in neither, either or both collections when the associated
- * exception is thrown. Attempts to drain a queue to itself result in <tt>IllegalArgumentException</tt>. Further,
- * the behavior of this operation is undefined if the specified collection is modified while the operation is in
- * progress.
- *
- * @param objects The collection to transfer elements into.
- *
- * @return The number of elements transferred.
- *
- * @throws NullPointerException If objects is null.
- * @throws IllegalArgumentException If objects is this queue.
- */
- public int drainTo(Collection<? super E> objects)
- {
- return drainTo(objects, -1);
- }
-
- /**
- * Removes at most the given number of available elements from this queue and adds them into the given collection.
- * A failure encountered while attempting to <tt>add</tt> elements to collection <tt>c</tt> may result in elements
- * being in neither, either or both collections when the associated exception is thrown. Attempts to drain a queue
- * to itself result in <tt>IllegalArgumentException</tt>. Further, the behavior of this operation is undefined if
- * the specified collection is modified while the operation is in progress.
- *
- * @param objects The collection to transfer elements into.
- * @param maxElements The maximum number of elements to transfer. If this is -1 then that is interpreted as meaning
- * all elements.
- *
- * @return The number of elements transferred.
- *
- * @throws NullPointerException If c is null.
- * @throws IllegalArgumentException If c is this queue.
- */
- public int drainTo(Collection<? super E> objects, int maxElements)
- {
- if (objects == null)
- {
- throw new NullPointerException();
- }
-
- if (objects == this)
- {
- throw new IllegalArgumentException();
- }
-
- // final Queue<E> items = this.buffer;
- final ReentrantLock lock = this.lock;
- lock.lock();
-
- try
- {
- int n = 0;
-
- for (int max = ((maxElements >= count) || (maxElements < 0)) ? count : maxElements; n < max; n++)
- {
- // Take items from the queue, do unblock the producers, but don't send not full signals yet.
- objects.add(extract(true, false).getElement());
- }
-
- if (n > 0)
- {
- // count -= n;
- notFull.signalAll();
- }
-
- return n;
- }
- finally
- {
- lock.unlock();
- }
- }
-
- /**
- * Takes all available data items from the queue or blocks until some become available. The returned items
- * are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their
- * producers, where the producers are still blocked.
- *
- * @param c The collection to drain the data items into.
- * @param unblock If set to <tt>true</tt> the producers for the taken items will be immediately unblocked.
- *
- * @return A count of the number of elements that were drained from the queue.
- */
- public SynchRef drainTo(Collection<SynchRecord<E>> c, boolean unblock)
- {
- return drainTo(c, -1, unblock);
- }
-
- /**
- * Takes up to maxElements available data items from the queue or blocks until some become available. The returned
- * items are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their
- * producers, where the producers are still blocked.
- *
- * @param coll The collection to drain the data items into.
- * @param maxElements The maximum number of elements to drain.
- * @param unblock If set to <tt>true</tt> the producers for the taken items will be immediately unblocked.
- *
- * @return A count of the number of elements that were drained from the queue.
- */
- public SynchRef drainTo(Collection<SynchRecord<E>> coll, int maxElements, boolean unblock)
- {
- if (coll == null)
- {
- throw new NullPointerException();
- }
-
- // final Queue<E> items = this.buffer;
- final ReentrantLock lock = this.lock;
- lock.lock();
-
- try
- {
- int n = 0;
-
- for (int max = ((maxElements >= count) || (maxElements < 0)) ? count : maxElements; n < max; n++)
- {
- // Extract the next record from the queue, don't signall the not full condition yet and release
- // producers depending on whether the caller wants to or not.
- coll.add(extract(false, unblock));
- }
-
- if (n > 0)
- {
- // count -= n;
- notFull.signalAll();
- }
-
- return new SynchRefImpl(n, coll);
- }
- finally
- {
- lock.unlock();
- }
- }
-
- /**
- * This abstract method should be overriden to return an empty queue. Different implementations of producer
- * consumer buffers can control the order in which data is accessed using different queue implementations.
- * This method allows the type of queue to be abstracted out of this class and to be supplied by concrete
- * implementations.
- *
- * @return An empty queue.
- */
- protected abstract <T> Queue<T> createQueue();
-
- /**
- * Insert element into the queue, then possibly signal that the queue is not empty and block the producer
- * on the element until permission to procede is given.
- *
- * <p/>If the producer is to be blocked then the lock must be released first, otherwise no other process
- * will be able to get access to the queue. Hence, unlock and block are always set together.
- *
- * <p/>Call only when holding the global lock.
- *
- * @param unlockAndBlock <tt>true</tt>If the global queue lock should be released and the producer should be blocked.
- *
- * @return <tt>true</tt> if the operation succeeded, <tt>false</tt> otherwise. If the result is <tt>true</tt> this
- * method may not return straight away, but only after the producer is unblocked by having its data
- * consumed if the unlockAndBlock flag is set. In the false case the method will return straight away, no
- * matter what value the unlockAndBlock flag has, leaving the global lock on.
- */
- protected boolean insert(E x, boolean unlockAndBlock)
- {
- // Create a new record for the data item.
- SynchRecordImpl<E> record = new SynchRecordImpl<E>(x);
-
- boolean result = buffer.offer(record);
-
- if (result)
- {
- count++;
-
- // Tell any waiting consumers that the queue is not empty.
- notEmpty.signal();
-
- if (unlockAndBlock)
- {
- // Allow other threads to read/write the queue.
- lock.unlock();
-
- // Wait until a consumer takes this data item.
- record.waitForConsumer();
- }
-
- return true;
- }
- else
- {
- return false;
- }
- }
-
- /**
- * Extract element at current take position, advance, and signal.
- *
- * <p/>Call only when holding lock.
- */
- protected SynchRecordImpl<E> extract(boolean unblock, boolean signal)
- {
- SynchRecordImpl<E> result = buffer.remove();
- count--;
-
- if (signal)
- {
- notFull.signal();
- }
-
- if (unblock)
- {
- result.releaseImmediately();
- }
-
- return result;
- }
-
- /**
- * Get the capacity of the buffer. If the buffer has no maximum capacity then Integer.MAX_VALUE is returned.
- *
- * <p/>Call only when holding lock.
- *
- * @return The maximum capacity of the buffer.
- */
- protected int getBufferCapacity()
- {
- if (buffer instanceof Capacity)
- {
- return ((Capacity) buffer).getCapacity();
- }
- else
- {
- return Integer.MAX_VALUE;
- }
- }
-
- /**
- * Return the head element from the buffer.
- *
- * <p/>Call only when holding lock.
- *
- * @return The head element from the buffer.
- */
- protected E peekAtBufferHead()
- {
- return buffer.peek().getElement();
- }
-
- public class SynchRefImpl implements SynchRef
- {
- /** Holds the number of synch records associated with this reference. */
- int numRecords;
-
- /** Holds a reference to the collection of synch records managed by this. */
- Collection<SynchRecord<E>> records;
-
- public SynchRefImpl(int n, Collection<SynchRecord<E>> records)
- {
- this.numRecords = n;
- this.records = records;
- }
-
- public int getNumRecords()
- {
- return numRecords;
- }
-
- /**
- * Any producers that have had their data elements taken from the queue but have not been unblocked are unblocked
- * when this method is called. The exception to this is producers that have had their data put back onto the queue
- * by a consumer. Producers that have had exceptions for their data items registered by consumers will be unblocked
- * but will not return from their put call normally, but with an exception instead.
- */
- public void unblockProducers()
- {
- log.debug("public void unblockProducers(): called");
-
- if (records != null)
- {
- for (SynchRecord<E> record : records)
- {
- // This call takes account of items that have already been released, are to be requeued or are in
- // error.
- record.releaseImmediately();
- }
- }
-
- records = null;
- }
- }
-
- /**
- * A SynchRecordImpl is used by a {@link BatchSynchQueue} to pair together a producer with its data. This allows
- * the producer of data to be identified so that it can be unblocked when its data is consumed or sent errors when
- * its data cannot be consumed.
- */
- public class SynchRecordImpl<E> implements SynchRecord<E>
- {
- /** A boolean latch that determines when the producer for this data item will be allowed to continue. */
- BooleanLatch latch = new BooleanLatch();
-
- /** The data element associated with this item. */
- E element;
-
- /**
- * Create a new synch record.
- *
- * @param e The data element that the record encapsulates.
- */
- public SynchRecordImpl(E e)
- {
- // Keep the data element.
- element = e;
- }
-
- /**
- * Waits until the producer is given permission to proceded by a consumer.
- */
- public void waitForConsumer()
- {
- latch.await();
- }
-
- /**
- * Gets the data element contained by this record.
- *
- * @return The data element contained by this record.
- */
- public E getElement()
- {
- return element;
- }
-
- /**
- * Immediately releases the producer of this data record. Consumers can bring the synchronization time of
- * producers to a minimum by using this method to release them at the earliest possible moment when batch
- * consuming records from sychronized producers.
- */
- public void releaseImmediately()
- {
- // Check that the record has not already been released, is in error or is to be requeued.
- latch.signal();
-
- // Propagate errors to the producer.
-
- // Requeue items to be requeued.
- }
-
- /**
- * Tells the synch queue to put this element back onto the queue instead of releasing its producer.
- * The element is not requeued immediately but upon calling the {@link SynchRef#unblockProducers()} method or
- * the {@link #releaseImmediately()} method.
- *
- * <p/>This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this
- * element has already been unblocked.
- */
- public void reQueue()
- {
- throw new RuntimeException("Not implemented.");
- }
-
- /**
- * Tells the synch queue to raise an exception with this elements producer. The exception is not raised
- * immediately but upon calling the {@link SynchRef#unblockProducers()} method or the
- * {@link #releaseImmediately()} method. The exception will be wrapped in a {@link SynchException} before it is
- * raised on the producer.
- *
- * <p/>This method is unusual in that it accepts an exception as an argument. This is non-standard but is used
- * because the exception is to be passed onto a different thread.
- *
- * <p/>This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this
- * element has already been unblocked.
- *
- * @param e The exception to raise on the producer.
- */
- public void inError(Exception e)
- {
- throw new RuntimeException("Not implemented.");
- }
- }
-}
+package org.apache.qpid.util.concurrent; + +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * Synchronous/Asynchronous puts. Asynchronous is easiest, just wait till can write to queue and deposit data. + * Synchronous is harder. Deposit data, but then must wait until deposited element/elements are taken before being + * allowed to unblock and continue. Consumer needs some options here too. Can just get the data from the buffer and + * allow any producers unblocked as a result to continue, or can get data but continue blocking while the data is + * processed before sending a message to do the unblocking. Synch/Asynch mode to be controlled by a switch. + * Unblocking/not unblocking during consumer processing to be controlled by the consumers calls. + * + * <p/>Implementing sub-classes only need to supply an implementation of a queue to produce a valid concrete + * implementation of this. This queue is only accessed through the methods {@link #insert}, {@link #extract}, + * {@link #getBufferCapacity()}, {@link #peekAtBufferHead()}. An implementation can override these methods to implement + * the buffer other than by a queue, for example, by using an array. + * + * <p/>Normal queue methods to work asynchronously. + * <p/>Put, take and drain methods from the BlockingQueue interface work synchronously but unblock producers immediately + * when their data is taken. + * <p/>The additional put, take and drain methods from the BatchSynchQueue interface work synchronously and provide the + * option to keep producers blocked until the consumer decides to release them. + * + * <p/>Removed take method that keeps producers blocked as it is pointless. Essentially it reduces this class to + * synchronous processing of individual data items, which negates the point of the hand-off design. The efficiency + * gain of the hand off design comes in being able to batch consume requests, ammortizing latency (such as caused by io) + * accross many producers. The only advantage of the single blocking take method is that it did take advantage of the + * queue ordering, which ma be usefull, for example to apply a priority ordering amongst producers. This is also an + * advantage over the java.util.concurrent.SynchronousQueue which doesn't have a backing queue which can be used to + * apply orderings. If a single item take is really needed can just use the drainTo method with a maximum of one item. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * </table> + */ +public abstract class BatchSynchQueueBase<E> extends AbstractQueue<E> implements BatchSynchQueue<E> +{ + /** Used for logging. */ + private static final Logger log = LoggerFactory.getLogger(BatchSynchQueueBase.class); + + /** Holds a reference to the queue implementation that holds the buffer. */ + Queue<SynchRecordImpl<E>> buffer; + + /** Holds the number of items in the queue */ + private int count; + + /** Main lock guarding all access */ + private ReentrantLock lock; + + /** Condition for waiting takes */ + private Condition notEmpty; + + /** Condition for waiting puts */ + private Condition notFull; + + /** + * Creates a batch synch queue without fair thread scheduling. + */ + public BatchSynchQueueBase() + { + this(false); + } + + /** + * Ensures that the underlying buffer implementation is created. + * + * @param fair <tt>true</tt> if fairness is to be applied to threads waiting to access the buffer. + */ + public BatchSynchQueueBase(boolean fair) + { + buffer = this.createQueue(); + + // Create the buffer lock with the fairness flag set accordingly. + lock = new ReentrantLock(fair); + + // Create the non-empty and non-full condition monitors on the buffer lock. + notEmpty = lock.newCondition(); + notFull = lock.newCondition(); + } + + /** + * Returns an iterator over the elements contained in this collection. + * + * @return An iterator over the elements contained in this collection. + */ + public Iterator<E> iterator() + { + throw new RuntimeException("Not implemented."); + } + + /** + * Returns the number of elements in this collection. If the collection contains more than + * <tt>Integer.MAX_VALUE</tt> elements, returns <tt>Integer.MAX_VALUE</tt>. + * + * @return The number of elements in this collection. + */ + public int size() + { + final ReentrantLock lock = this.lock; + lock.lock(); + + try + { + return count; + } + finally + { + lock.unlock(); + } + } + + /** + * Inserts the specified element into this queue, if possible. When using queues that may impose insertion + * restrictions (for example capacity bounds), method <tt>offer</tt> is generally preferable to method + * {@link java.util.Collection#add}, which can fail to insert an element only by throwing an exception. + * + * @param e The element to insert. + * + * @return <tt>true</tt> if it was possible to add the element to this queue, else <tt>false</tt> + */ + public boolean offer(E e) + { + if (e == null) + { + throw new NullPointerException(); + } + + final ReentrantLock lock = this.lock; + lock.lock(); + + try + { + return insert(e, false); + } + finally + { + lock.unlock(); + } + } + + /** + * Inserts the specified element into this queue, waiting if necessary up to the specified wait time for space to + * become available. + * + * @param e The element to add. + * @param timeout How long to wait before giving up, in units of <tt>unit</tt> + * @param unit A <tt>TimeUnit</tt> determining how to interpret the <tt>timeout</tt> parameter. + * + * @return <tt>true</tt> if successful, or <tt>false</tt> if the specified waiting time elapses before space is + * available. + * + * @throws InterruptedException If interrupted while waiting. + * @throws NullPointerException If the specified element is <tt>null</tt>. + */ + public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException + { + if (e == null) + { + throw new NullPointerException(); + } + + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + + long nanos = unit.toNanos(timeout); + + try + { + do + { + if (insert(e, false)) + { + return true; + } + + try + { + nanos = notFull.awaitNanos(nanos); + } + catch (InterruptedException ie) + { + notFull.signal(); // propagate to non-interrupted thread + throw ie; + } + } + while (nanos > 0); + + return false; + } + finally + { + lock.unlock(); + } + } + + /** + * Retrieves and removes the head of this queue, or <tt>null</tt> if this queue is empty. + * + * @return The head of this queue, or <tt>null</tt> if this queue is empty. + */ + public E poll() + { + final ReentrantLock lock = this.lock; + + lock.lock(); + try + { + if (count == 0) + { + return null; + } + + E x = extract(true, true).getElement(); + + return x; + } + finally + { + lock.unlock(); + } + } + + /** + * Retrieves and removes the head of this queue, waiting if necessary up to the specified wait time if no elements + * are present on this queue. + * + * @param timeout How long to wait before giving up, in units of <tt>unit</tt>. + * @param unit A <tt>TimeUnit</tt> determining how to interpret the <tt>timeout</tt> parameter. + * + * @return The head of this queue, or <tt>null</tt> if the specified waiting time elapses before an element is present. + * + * @throws InterruptedException If interrupted while waiting. + */ + public E poll(long timeout, TimeUnit unit) throws InterruptedException + { + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + try + { + long nanos = unit.toNanos(timeout); + + do + { + if (count != 0) + { + E x = extract(true, true).getElement(); + + return x; + } + + try + { + nanos = notEmpty.awaitNanos(nanos); + } + catch (InterruptedException ie) + { + notEmpty.signal(); // propagate to non-interrupted thread + throw ie; + } + } + while (nanos > 0); + + return null; + } + finally + { + lock.unlock(); + } + } + + /** + * Retrieves, but does not remove, the head of this queue, returning <tt>null</tt> if this queue is empty. + * + * @return The head of this queue, or <tt>null</tt> if this queue is empty. + */ + public E peek() + { + final ReentrantLock lock = this.lock; + lock.lock(); + + try + { + return peekAtBufferHead(); + } + finally + { + lock.unlock(); + } + } + + /** + * Returns the number of elements that this queue can ideally (in the absence of memory or resource constraints) + * accept without blocking, or <tt>Integer.MAX_VALUE</tt> if there is no intrinsic limit. + * + * <p>Note that you <em>cannot</em> always tell if an attempt to <tt>add</tt> an element will succeed by + * inspecting <tt>remainingCapacity</tt> because it may be the case that another thread is about to <tt>put</tt> + * or <tt>take</tt> an element. + * + * @return The remaining capacity. + */ + public int remainingCapacity() + { + final ReentrantLock lock = this.lock; + lock.lock(); + + try + { + return getBufferCapacity() - count; + } + finally + { + lock.unlock(); + } + } + + /** + * Adds the specified element to this queue, waiting if necessary for space to become available. + * + * <p/>This method delegated to {@link #tryPut} which can raise {@link SynchException}s. If any are raised + * this method silently ignores them. Use the {@link #tryPut} method directly if you want to catch these + * exceptions. + * + * @param e The element to add. + * + * @throws InterruptedException If interrupted while waiting. + */ + public void put(E e) throws InterruptedException + { + try + { + tryPut(e); + } + catch (SynchException ex) + { + // This exception is deliberately ignored. See the method comment for information about this. + } + } + + /** + * Tries a synchronous put into the queue. If a consumer encounters an exception condition whilst processing the + * data that is put, then this is returned to the caller wrapped inside a {@link SynchException}. + * + * @param e The data element to put into the queue. Cannot be null. + * + * @throws InterruptedException If the thread is interrupted whilst waiting to write to the queue or whilst waiting + * on its entry in the queue being consumed. + * @throws SynchException If a consumer encounters an error whilst processing the data element. + */ + public void tryPut(E e) throws InterruptedException, SynchException + { + if (e == null) + { + throw new NullPointerException(); + } + + // final Queue<E> items = this.buffer; + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + + try + { + while (count == getBufferCapacity()) + { + // Release the lock and wait until the queue is not full. + notFull.await(); + } + } + catch (InterruptedException ie) + { + notFull.signal(); // propagate to non-interrupted thread + throw ie; + } + + // There is room in the queue so insert must succeed. Insert into the queu, release the lock and block + // the producer until its data is taken. + insert(e, true); + } + + /** + * Retrieves and removes the head of this queue, waiting if no elements are present on this queue. + * Any producer that has its data element taken by this call will be immediately unblocked. To keep the + * producer blocked whilst taking just a single item, use the + * {@link #drainTo(java.util.Collection<org.apache.qpid.util.concurrent.SynchRecord<E>>, int, boolean)} + * method. There is no take method to do that because there is not usually any advantage in a synchronous hand + * off design that consumes data one item at a time. It is normal to consume data in chunks to ammortize consumption + * latencies accross many producers where possible. + * + * @return The head of this queue. + * + * @throws InterruptedException if interrupted while waiting. + */ + public E take() throws InterruptedException + { + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + + try + { + try + { + while (count == 0) + { + // Release the lock and wait until the queue becomes non-empty. + notEmpty.await(); + } + } + catch (InterruptedException ie) + { + notEmpty.signal(); // propagate to non-interrupted thread + throw ie; + } + + // There is data in the queue so extraction must succeed. Notify any waiting threads that the queue is + // not full, and unblock the producer that owns the data item that is taken. + E x = extract(true, true).getElement(); + + return x; + } + finally + { + lock.unlock(); + } + } + + /** + * Removes all available elements from this queue and adds them into the given collection. This operation may be + * more efficient than repeatedly polling this queue. A failure encountered while attempting to <tt>add</tt> elements + * to collection <tt>c</tt> may result in elements being in neither, either or both collections when the associated + * exception is thrown. Attempts to drain a queue to itself result in <tt>IllegalArgumentException</tt>. Further, + * the behavior of this operation is undefined if the specified collection is modified while the operation is in + * progress. + * + * @param objects The collection to transfer elements into. + * + * @return The number of elements transferred. + * + * @throws NullPointerException If objects is null. + * @throws IllegalArgumentException If objects is this queue. + */ + public int drainTo(Collection<? super E> objects) + { + return drainTo(objects, -1); + } + + /** + * Removes at most the given number of available elements from this queue and adds them into the given collection. + * A failure encountered while attempting to <tt>add</tt> elements to collection <tt>c</tt> may result in elements + * being in neither, either or both collections when the associated exception is thrown. Attempts to drain a queue + * to itself result in <tt>IllegalArgumentException</tt>. Further, the behavior of this operation is undefined if + * the specified collection is modified while the operation is in progress. + * + * @param objects The collection to transfer elements into. + * @param maxElements The maximum number of elements to transfer. If this is -1 then that is interpreted as meaning + * all elements. + * + * @return The number of elements transferred. + * + * @throws NullPointerException If c is null. + * @throws IllegalArgumentException If c is this queue. + */ + public int drainTo(Collection<? super E> objects, int maxElements) + { + if (objects == null) + { + throw new NullPointerException(); + } + + if (objects == this) + { + throw new IllegalArgumentException(); + } + + // final Queue<E> items = this.buffer; + final ReentrantLock lock = this.lock; + lock.lock(); + + try + { + int n = 0; + + for (int max = ((maxElements >= count) || (maxElements < 0)) ? count : maxElements; n < max; n++) + { + // Take items from the queue, do unblock the producers, but don't send not full signals yet. + objects.add(extract(true, false).getElement()); + } + + if (n > 0) + { + // count -= n; + notFull.signalAll(); + } + + return n; + } + finally + { + lock.unlock(); + } + } + + /** + * Takes all available data items from the queue or blocks until some become available. The returned items + * are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their + * producers, where the producers are still blocked. + * + * @param c The collection to drain the data items into. + * @param unblock If set to <tt>true</tt> the producers for the taken items will be immediately unblocked. + * + * @return A count of the number of elements that were drained from the queue. + */ + public SynchRef drainTo(Collection<SynchRecord<E>> c, boolean unblock) + { + return drainTo(c, -1, unblock); + } + + /** + * Takes up to maxElements available data items from the queue or blocks until some become available. The returned + * items are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their + * producers, where the producers are still blocked. + * + * @param coll The collection to drain the data items into. + * @param maxElements The maximum number of elements to drain. + * @param unblock If set to <tt>true</tt> the producers for the taken items will be immediately unblocked. + * + * @return A count of the number of elements that were drained from the queue. + */ + public SynchRef drainTo(Collection<SynchRecord<E>> coll, int maxElements, boolean unblock) + { + if (coll == null) + { + throw new NullPointerException(); + } + + // final Queue<E> items = this.buffer; + final ReentrantLock lock = this.lock; + lock.lock(); + + try + { + int n = 0; + + for (int max = ((maxElements >= count) || (maxElements < 0)) ? count : maxElements; n < max; n++) + { + // Extract the next record from the queue, don't signall the not full condition yet and release + // producers depending on whether the caller wants to or not. + coll.add(extract(false, unblock)); + } + + if (n > 0) + { + // count -= n; + notFull.signalAll(); + } + + return new SynchRefImpl(n, coll); + } + finally + { + lock.unlock(); + } + } + + /** + * This abstract method should be overriden to return an empty queue. Different implementations of producer + * consumer buffers can control the order in which data is accessed using different queue implementations. + * This method allows the type of queue to be abstracted out of this class and to be supplied by concrete + * implementations. + * + * @return An empty queue. + */ + protected abstract <T> Queue<T> createQueue(); + + /** + * Insert element into the queue, then possibly signal that the queue is not empty and block the producer + * on the element until permission to procede is given. + * + * <p/>If the producer is to be blocked then the lock must be released first, otherwise no other process + * will be able to get access to the queue. Hence, unlock and block are always set together. + * + * <p/>Call only when holding the global lock. + * + * @param unlockAndBlock <tt>true</tt>If the global queue lock should be released and the producer should be blocked. + * + * @return <tt>true</tt> if the operation succeeded, <tt>false</tt> otherwise. If the result is <tt>true</tt> this + * method may not return straight away, but only after the producer is unblocked by having its data + * consumed if the unlockAndBlock flag is set. In the false case the method will return straight away, no + * matter what value the unlockAndBlock flag has, leaving the global lock on. + */ + protected boolean insert(E x, boolean unlockAndBlock) + { + // Create a new record for the data item. + SynchRecordImpl<E> record = new SynchRecordImpl<E>(x); + + boolean result = buffer.offer(record); + + if (result) + { + count++; + + // Tell any waiting consumers that the queue is not empty. + notEmpty.signal(); + + if (unlockAndBlock) + { + // Allow other threads to read/write the queue. + lock.unlock(); + + // Wait until a consumer takes this data item. + record.waitForConsumer(); + } + + return true; + } + else + { + return false; + } + } + + /** + * Extract element at current take position, advance, and signal. + * + * <p/>Call only when holding lock. + */ + protected SynchRecordImpl<E> extract(boolean unblock, boolean signal) + { + SynchRecordImpl<E> result = buffer.remove(); + count--; + + if (signal) + { + notFull.signal(); + } + + if (unblock) + { + result.releaseImmediately(); + } + + return result; + } + + /** + * Get the capacity of the buffer. If the buffer has no maximum capacity then Integer.MAX_VALUE is returned. + * + * <p/>Call only when holding lock. + * + * @return The maximum capacity of the buffer. + */ + protected int getBufferCapacity() + { + if (buffer instanceof Capacity) + { + return ((Capacity) buffer).getCapacity(); + } + else + { + return Integer.MAX_VALUE; + } + } + + /** + * Return the head element from the buffer. + * + * <p/>Call only when holding lock. + * + * @return The head element from the buffer. + */ + protected E peekAtBufferHead() + { + return buffer.peek().getElement(); + } + + public class SynchRefImpl implements SynchRef + { + /** Holds the number of synch records associated with this reference. */ + int numRecords; + + /** Holds a reference to the collection of synch records managed by this. */ + Collection<SynchRecord<E>> records; + + public SynchRefImpl(int n, Collection<SynchRecord<E>> records) + { + this.numRecords = n; + this.records = records; + } + + public int getNumRecords() + { + return numRecords; + } + + /** + * Any producers that have had their data elements taken from the queue but have not been unblocked are unblocked + * when this method is called. The exception to this is producers that have had their data put back onto the queue + * by a consumer. Producers that have had exceptions for their data items registered by consumers will be unblocked + * but will not return from their put call normally, but with an exception instead. + */ + public void unblockProducers() + { + log.debug("public void unblockProducers(): called"); + + if (records != null) + { + for (SynchRecord<E> record : records) + { + // This call takes account of items that have already been released, are to be requeued or are in + // error. + record.releaseImmediately(); + } + } + + records = null; + } + } + + /** + * A SynchRecordImpl is used by a {@link BatchSynchQueue} to pair together a producer with its data. This allows + * the producer of data to be identified so that it can be unblocked when its data is consumed or sent errors when + * its data cannot be consumed. + */ + public class SynchRecordImpl<E> implements SynchRecord<E> + { + /** A boolean latch that determines when the producer for this data item will be allowed to continue. */ + BooleanLatch latch = new BooleanLatch(); + + /** The data element associated with this item. */ + E element; + + /** + * Create a new synch record. + * + * @param e The data element that the record encapsulates. + */ + public SynchRecordImpl(E e) + { + // Keep the data element. + element = e; + } + + /** + * Waits until the producer is given permission to proceded by a consumer. + */ + public void waitForConsumer() + { + latch.await(); + } + + /** + * Gets the data element contained by this record. + * + * @return The data element contained by this record. + */ + public E getElement() + { + return element; + } + + /** + * Immediately releases the producer of this data record. Consumers can bring the synchronization time of + * producers to a minimum by using this method to release them at the earliest possible moment when batch + * consuming records from sychronized producers. + */ + public void releaseImmediately() + { + // Check that the record has not already been released, is in error or is to be requeued. + latch.signal(); + + // Propagate errors to the producer. + + // Requeue items to be requeued. + } + + /** + * Tells the synch queue to put this element back onto the queue instead of releasing its producer. + * The element is not requeued immediately but upon calling the {@link SynchRef#unblockProducers()} method or + * the {@link #releaseImmediately()} method. + * + * <p/>This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this + * element has already been unblocked. + */ + public void reQueue() + { + throw new RuntimeException("Not implemented."); + } + + /** + * Tells the synch queue to raise an exception with this elements producer. The exception is not raised + * immediately but upon calling the {@link SynchRef#unblockProducers()} method or the + * {@link #releaseImmediately()} method. The exception will be wrapped in a {@link SynchException} before it is + * raised on the producer. + * + * <p/>This method is unusual in that it accepts an exception as an argument. This is non-standard but is used + * because the exception is to be passed onto a different thread. + * + * <p/>This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this + * element has already been unblocked. + * + * @param e The exception to raise on the producer. + */ + public void inError(Exception e) + { + throw new RuntimeException("Not implemented."); + } + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BooleanLatch.java b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BooleanLatch.java index 2a5b0d0c3e..391ca686c9 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BooleanLatch.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BooleanLatch.java @@ -1,107 +1,107 @@ -package org.apache.qpid.util.concurrent;
-
-import java.util.concurrent.locks.AbstractQueuedSynchronizer;
-
-/**
- * A BooleanLatch is like a set of traffic lights, where threads can wait at a red light until another thread gives
- * the green light. When threads arrive at the latch it is initially red. They queue up until the green signal is
- * given, at which point they can all acquire the latch in shared mode and continue to run concurrently. Once the latch
- * is signalled it cannot be reset to red again.
- *
- * <p/> The latch uses a {@link java.util.concurrent.locks.AbstractQueuedSynchronizer} to implement its synchronization.
- * This has two internal states, 0 which means that the latch is blocked, and 1 which means that the latch is open.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Block threads until a go signal is given.
- * </table>
- *
- * @todo Might be better to use a countdown latch to count down from 1. Its await method can throw interrupted
- * exception which makes the possibility of interruption more explicit, and provides a reminder to recheck the
- * latch condition before continuing.
- */
-public class BooleanLatch
-{
- /** Holds the synchronizer that provides the thread queueing synchronization. */
- private final Sync sync = new Sync();
-
- /**
- * Tests whether or not the latch has been signalled, that is to say that, the light is green.
- *
- * <p/>This method is non-blocking.
- *
- * @return <tt>true</tt> if the latch may be acquired; the light is green.
- */
- public boolean isSignalled()
- {
- return sync.isSignalled();
- }
-
- /**
- * Waits on the latch until the signal is given and the light is green. If the light is already green then the
- * latch will be acquired and the thread will not have to wait.
- *
- * <p/>This method will block until the go signal is given or the thread is otherwise interrupted. Before carrying
- * out any processing threads that return from this method should confirm that the go signal has really been given
- * on this latch by calling the {@link #isSignalled()} method.
- */
- public void await()
- {
- sync.acquireShared(1);
- }
-
- /**
- * Releases any threads currently waiting on the latch. This flips the light to green allowing any threads that
- * were waiting for this condition to now run.
- *
- * <p/>This method is non-blocking.
- */
- public void signal()
- {
- sync.releaseShared(1);
- }
-
- /**
- * Implements a thread queued synchronizer. The internal state 0 means that the queue is blocked and the internl
- * state 1 means that the queue is released and that all waiting threads can acquire the synchronizer in shared
- * mode.
- */
- private static class Sync extends AbstractQueuedSynchronizer
- {
- /**
- * Attempts to acquire this synchronizer in shared mode. It may be acquired once it has been released.
- *
- * @param ignore This parameter is ignored.
- *
- * @return 1 if the shared acquisition succeeds and -1 if it fails.
- */
- protected int tryAcquireShared(int ignore)
- {
- return isSignalled() ? 1 : -1;
- }
-
- /**
- * Releases the synchronizer, setting its internal state to 1.
- *
- * @param ignore This parameter is ignored.
- *
- * @return <tt>true</tt> always.
- */
- protected boolean tryReleaseShared(int ignore)
- {
- setState(1);
-
- return true;
- }
-
- /**
- * Tests if the synchronizer is signalled. It is signalled when its internal state it 1.
- *
- * @return <tt>true</tt> if the internal state is 1, <tt>false</tt> otherwise.
- */
- boolean isSignalled()
- {
- return getState() != 0;
- }
- }
-}
+package org.apache.qpid.util.concurrent; + +import java.util.concurrent.locks.AbstractQueuedSynchronizer; + +/** + * A BooleanLatch is like a set of traffic lights, where threads can wait at a red light until another thread gives + * the green light. When threads arrive at the latch it is initially red. They queue up until the green signal is + * given, at which point they can all acquire the latch in shared mode and continue to run concurrently. Once the latch + * is signalled it cannot be reset to red again. + * + * <p/> The latch uses a {@link java.util.concurrent.locks.AbstractQueuedSynchronizer} to implement its synchronization. + * This has two internal states, 0 which means that the latch is blocked, and 1 which means that the latch is open. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Block threads until a go signal is given. + * </table> + * + * @todo Might be better to use a countdown latch to count down from 1. Its await method can throw interrupted + * exception which makes the possibility of interruption more explicit, and provides a reminder to recheck the + * latch condition before continuing. + */ +public class BooleanLatch +{ + /** Holds the synchronizer that provides the thread queueing synchronization. */ + private final Sync sync = new Sync(); + + /** + * Tests whether or not the latch has been signalled, that is to say that, the light is green. + * + * <p/>This method is non-blocking. + * + * @return <tt>true</tt> if the latch may be acquired; the light is green. + */ + public boolean isSignalled() + { + return sync.isSignalled(); + } + + /** + * Waits on the latch until the signal is given and the light is green. If the light is already green then the + * latch will be acquired and the thread will not have to wait. + * + * <p/>This method will block until the go signal is given or the thread is otherwise interrupted. Before carrying + * out any processing threads that return from this method should confirm that the go signal has really been given + * on this latch by calling the {@link #isSignalled()} method. + */ + public void await() + { + sync.acquireShared(1); + } + + /** + * Releases any threads currently waiting on the latch. This flips the light to green allowing any threads that + * were waiting for this condition to now run. + * + * <p/>This method is non-blocking. + */ + public void signal() + { + sync.releaseShared(1); + } + + /** + * Implements a thread queued synchronizer. The internal state 0 means that the queue is blocked and the internl + * state 1 means that the queue is released and that all waiting threads can acquire the synchronizer in shared + * mode. + */ + private static class Sync extends AbstractQueuedSynchronizer + { + /** + * Attempts to acquire this synchronizer in shared mode. It may be acquired once it has been released. + * + * @param ignore This parameter is ignored. + * + * @return 1 if the shared acquisition succeeds and -1 if it fails. + */ + protected int tryAcquireShared(int ignore) + { + return isSignalled() ? 1 : -1; + } + + /** + * Releases the synchronizer, setting its internal state to 1. + * + * @param ignore This parameter is ignored. + * + * @return <tt>true</tt> always. + */ + protected boolean tryReleaseShared(int ignore) + { + setState(1); + + return true; + } + + /** + * Tests if the synchronizer is signalled. It is signalled when its internal state it 1. + * + * @return <tt>true</tt> if the internal state is 1, <tt>false</tt> otherwise. + */ + boolean isSignalled() + { + return getState() != 0; + } + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/Capacity.java b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/Capacity.java index 2b4a5f28a9..e317c84971 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/Capacity.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/Capacity.java @@ -1,14 +1,14 @@ -package org.apache.qpid.util.concurrent;
-
-/**
- * An interface exposed by data structures that have a maximum capacity.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Report the maximum capacity.
- * </table>
- */
-public interface Capacity
-{
- public int getCapacity();
-}
+package org.apache.qpid.util.concurrent; + +/** + * An interface exposed by data structures that have a maximum capacity. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Report the maximum capacity. + * </table> + */ +public interface Capacity +{ + public int getCapacity(); +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchBuffer.java b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchBuffer.java index 8f682ec462..49020803d7 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchBuffer.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchBuffer.java @@ -1,29 +1,29 @@ -package org.apache.qpid.util.concurrent;
-
-import java.util.Queue;
-
-/**
- * SynchBuffer completes the {@link BatchSynchQueueBase} abstract class by providing an implementation of the underlying
- * queue as an array. This uses FIFO ordering for the queue but restricts the maximum size of the queue to a fixed
- * amount. It also has the advantage that, as the buffer does not grow and shrink dynamically, memory for the buffer
- * is allocated up front and does not create garbage during the operation of the queue.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Provide array based FIFO queue to create a batch synched queue around.
- * </table>
- *
- * @todo Write an array based buffer implementation that implements Queue.
- */
-public class SynchBuffer<E> extends BatchSynchQueueBase<E>
-{
- /**
- * Returns an empty queue, implemented as an array.
- *
- * @return An empty queue, implemented as an array.
- */
- protected <T> Queue<T> createQueue()
- {
- throw new RuntimeException("Not implemented.");
- }
-}
+package org.apache.qpid.util.concurrent; + +import java.util.Queue; + +/** + * SynchBuffer completes the {@link BatchSynchQueueBase} abstract class by providing an implementation of the underlying + * queue as an array. This uses FIFO ordering for the queue but restricts the maximum size of the queue to a fixed + * amount. It also has the advantage that, as the buffer does not grow and shrink dynamically, memory for the buffer + * is allocated up front and does not create garbage during the operation of the queue. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Provide array based FIFO queue to create a batch synched queue around. + * </table> + * + * @todo Write an array based buffer implementation that implements Queue. + */ +public class SynchBuffer<E> extends BatchSynchQueueBase<E> +{ + /** + * Returns an empty queue, implemented as an array. + * + * @return An empty queue, implemented as an array. + */ + protected <T> Queue<T> createQueue() + { + throw new RuntimeException("Not implemented."); + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchException.java b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchException.java index c6edff6320..77b60f2b72 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchException.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchException.java @@ -1,31 +1,31 @@ -package org.apache.qpid.util.concurrent;
-
-/**
- * SynchException is used to encapsulate exceptions with the data elements that caused them in order to send exceptions
- * back from the consumers of a {@link BatchSynchQueue} to producers. The underlying exception should be retrieved from
- * the {@link #getCause} method.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Encapsulate a data element and exception.
- * </table>
- */
-public class SynchException extends Exception
-{
- /** Holds the data element that is in error. */
- Object element;
-
- /**
- * Creates a new BaseApplicationException object.
- *
- * @param message The exception message.
- * @param cause The underlying throwable cause. This may be null.
- */
- public SynchException(String message, Throwable cause, Object element)
- {
- super(message, cause);
-
- // Keep the data element that was in error.
- this.element = element;
- }
-}
+package org.apache.qpid.util.concurrent; + +/** + * SynchException is used to encapsulate exceptions with the data elements that caused them in order to send exceptions + * back from the consumers of a {@link BatchSynchQueue} to producers. The underlying exception should be retrieved from + * the {@link #getCause} method. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Encapsulate a data element and exception. + * </table> + */ +public class SynchException extends Exception +{ + /** Holds the data element that is in error. */ + Object element; + + /** + * Creates a new BaseApplicationException object. + * + * @param message The exception message. + * @param cause The underlying throwable cause. This may be null. + */ + public SynchException(String message, Throwable cause, Object element) + { + super(message, cause); + + // Keep the data element that was in error. + this.element = element; + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchQueue.java b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchQueue.java index df3f2b849a..9d15c211f6 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchQueue.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchQueue.java @@ -1,27 +1,27 @@ -package org.apache.qpid.util.concurrent;
-
-import java.util.LinkedList;
-import java.util.Queue;
-
-/**
- * SynchQueue completes the {@link BatchSynchQueueBase} abstract class by providing an implementation of the underlying
- * queue as a linked list. This uses FIFO ordering for the queue and allows the queue to grow to accomodate more
- * elements as needed.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Provide linked list FIFO queue to create a batch synched queue around.
- * </table>
- */
-public class SynchQueue<E> extends BatchSynchQueueBase<E>
-{
- /**
- * Returns an empty queue, implemented as a linked list.
- *
- * @return An empty queue, implemented as a linked list.
- */
- protected <T> Queue<T> createQueue()
- {
- return new LinkedList<T>();
- }
-}
+package org.apache.qpid.util.concurrent; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * SynchQueue completes the {@link BatchSynchQueueBase} abstract class by providing an implementation of the underlying + * queue as a linked list. This uses FIFO ordering for the queue and allows the queue to grow to accomodate more + * elements as needed. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Provide linked list FIFO queue to create a batch synched queue around. + * </table> + */ +public class SynchQueue<E> extends BatchSynchQueueBase<E> +{ + /** + * Returns an empty queue, implemented as a linked list. + * + * @return An empty queue, implemented as a linked list. + */ + protected <T> Queue<T> createQueue() + { + return new LinkedList<T>(); + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRecord.java b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRecord.java index cacef472d6..5e002100c2 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRecord.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRecord.java @@ -1,53 +1,53 @@ -package org.apache.qpid.util.concurrent;
-
-/**
- * SynchRecord associates a data item from a {@link BatchSynchQueue} with its producer. This enables the data item data
- * item to be put back on the queue without unblocking its producer, or to send exceptions to the producer.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Get the underlying data element.
- * <tr><td> Put the data element back on the queue without unblocking its producer.
- * <tr><td> Send and exception to the data elements producer.
- * </table>
- */
-public interface SynchRecord<E>
-{
- /**
- * Gets the data element contained by this record.
- *
- * @return The data element contained by this record.
- */
- public E getElement();
-
- /**
- * Tells the synch queue to put this element back onto the queue instead of releasing its producer.
- * The element is not requeued immediately but upon calling the {@link SynchRef#unblockProducers()} method.
- *
- * <p/>This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this element
- * has already been unblocked.
- */
- public void reQueue();
-
- /**
- * Immediately releases the producer of this data record. Consumers can bring the synchronization time of
- * producers to a minimum by using this method to release them at the earliest possible moment when batch
- * consuming records from sychronized producers.
- */
- public void releaseImmediately();
-
- /**
- * Tells the synch queue to raise an exception with this elements producer. The exception is not raised immediately
- * but upon calling the {@link SynchRef#unblockProducers()} method. The exception will be wrapped in a
- * {@link SynchException} before it is raised on the producer.
- *
- * <p/>This method is unusual in that it accepts an exception as an argument. This is non-standard but is used
- * because the exception is to be passed onto a different thread.
- *
- * <p/>This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this element
- * has already been unblocked.
- *
- * @param e The exception to raise on the producer.
- */
- public void inError(Exception e);
-}
+package org.apache.qpid.util.concurrent; + +/** + * SynchRecord associates a data item from a {@link BatchSynchQueue} with its producer. This enables the data item data + * item to be put back on the queue without unblocking its producer, or to send exceptions to the producer. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Get the underlying data element. + * <tr><td> Put the data element back on the queue without unblocking its producer. + * <tr><td> Send and exception to the data elements producer. + * </table> + */ +public interface SynchRecord<E> +{ + /** + * Gets the data element contained by this record. + * + * @return The data element contained by this record. + */ + public E getElement(); + + /** + * Tells the synch queue to put this element back onto the queue instead of releasing its producer. + * The element is not requeued immediately but upon calling the {@link SynchRef#unblockProducers()} method. + * + * <p/>This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this element + * has already been unblocked. + */ + public void reQueue(); + + /** + * Immediately releases the producer of this data record. Consumers can bring the synchronization time of + * producers to a minimum by using this method to release them at the earliest possible moment when batch + * consuming records from sychronized producers. + */ + public void releaseImmediately(); + + /** + * Tells the synch queue to raise an exception with this elements producer. The exception is not raised immediately + * but upon calling the {@link SynchRef#unblockProducers()} method. The exception will be wrapped in a + * {@link SynchException} before it is raised on the producer. + * + * <p/>This method is unusual in that it accepts an exception as an argument. This is non-standard but is used + * because the exception is to be passed onto a different thread. + * + * <p/>This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this element + * has already been unblocked. + * + * @param e The exception to raise on the producer. + */ + public void inError(Exception e); +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRef.java b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRef.java index c044ed0a60..a75f7b766d 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRef.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRef.java @@ -1,30 +1,30 @@ -package org.apache.qpid.util.concurrent;
-
-/**
- * A SynchRef is an interface which is returned from the synchronous take and drain methods of {@link BatchSynchQueue},
- * allowing call-backs to be made against the synchronizing strucutre. It allows the consumer to communicate when it
- * wants producers that have their data taken to be unblocked.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities
- * <tr><td> Report number of records returned by a taking operation.
- * <tr><td> Provide call-back to release producers of taken records.
- * </table>
- */
-public interface SynchRef
-{
- /**
- * Reports the number of records taken by the take or drain operation.
- *
- * @return The number of records taken by the take or drain operation.
- */
- public int getNumRecords();
-
- /**
- * Any producers that have had their data elements taken from the queue but have not been unblocked are
- * unblocked when this method is called. The exception to this is producers that have had their data put back
- * onto the queue by a consumer. Producers that have had exceptions for their data items registered by consumers
- * will be unblocked but will not return from their put call normally, but with an exception instead.
- */
- public void unblockProducers();
-}
+package org.apache.qpid.util.concurrent; + +/** + * A SynchRef is an interface which is returned from the synchronous take and drain methods of {@link BatchSynchQueue}, + * allowing call-backs to be made against the synchronizing strucutre. It allows the consumer to communicate when it + * wants producers that have their data taken to be unblocked. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities + * <tr><td> Report number of records returned by a taking operation. + * <tr><td> Provide call-back to release producers of taken records. + * </table> + */ +public interface SynchRef +{ + /** + * Reports the number of records taken by the take or drain operation. + * + * @return The number of records taken by the take or drain operation. + */ + public int getNumRecords(); + + /** + * Any producers that have had their data elements taken from the queue but have not been unblocked are + * unblocked when this method is called. The exception to this is producers that have had their data put back + * onto the queue by a consumer. Producers that have had exceptions for their data items registered by consumers + * will be unblocked but will not return from their put call normally, but with an exception instead. + */ + public void unblockProducers(); +} diff --git a/qpid/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java b/qpid/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java index 66fb3252df..942901f1c0 100644 --- a/qpid/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java +++ b/qpid/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java @@ -1,554 +1,554 @@ -/*
- *
- * 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.util;
-
-import junit.framework.*;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Properties;
-
-/**
- * Unit tests the {@link CommandLineParser} class.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Check that parsing a single flag works ok.
- * <tr><td> Check that parsing multiple flags condensed together works ok.
- * <tr><td> Check that parsing an option with a space between it and its argument works ok.
- * <tr><td> Check that parsing an option with no space between it and its argument works ok.
- * <tr><td> Check that parsing an option with specific argument format works ok.
- * <tr><td> Check that parsing an option with specific argument format fails on bad argument.
- * <tr><td> Check that parsing a flag condensed together with an option fails.
- * <tr><td> Check that parsing a free argument works ok.
- * <tr><td> Check that parsing a free argument with specific format works ok.
- * <tr><td> Check that parsing a free argument with specific format fails on bad argument.
- * <tr><td> Check that parsing a mandatory option works ok.
- * <tr><td> Check that parsing a mandatory free argument works ok.
- * <tr><td> Check that parsing a mandatory option fails when no option is set.
- * <tr><td> Check that parsing a mandatory free argument fails when no argument is specified.
- * <tr><td> Check that parsing an unknown option works when unknowns not errors.
- * <tr><td> Check that parsing an unknown flag fails when unknowns are to be reported as errors.
- * <tr><td> Check that parsing an unknown option fails when unknowns are to be reported as errors.
- * <tr><td> Check that get errors returns a string on errors.
- * <tr><td> Check that get errors returns an empty string on no errors.
- * <tr><td> Check that get usage returns a string.
- * <tr><td> Check that get options in force returns an empty string before parsing.
- * <tr><td> Check that get options in force return a non-empty string after parsing.
- * </table>
- */
-public class CommandLineParserTest extends TestCase
-{
- private static final Logger log = LoggerFactory.getLogger(CommandLineParserTest.class);
-
- public CommandLineParserTest(String name)
- {
- super(name);
- }
-
- /**
- * Compile all the tests for the default test implementation of a traversable state into a test suite.
- */
- public static Test suite()
- {
- // Build a new test suite
- TestSuite suite = new TestSuite("CommandLineParser Tests");
-
- // Add all the tests defined in this class (using the default constructor)
- suite.addTestSuite(CommandLineParserTest.class);
-
- return suite;
- }
-
- /** Check that get errors returns an empty string on no errors. */
- public void testGetErrorsReturnsEmptyStringOnNoErrors() throws Exception
- {
- // Create a command line parser for some flags and options.
- CommandLineParser parser =
- new CommandLineParser(
- new String[][]
- {
- { "t1", "Test Flag 1." },
- { "t2", "Test Option 2.", "test" },
- { "t3", "Test Option 3.", "test", "true" },
- { "t4", "Test Option 4.", "test", null, "^test$" }
- });
-
- // Do some legal parsing.
- parser.parseCommandLine(new String[] { "-t1", "-t2test", "-t3test", "-t4test" });
-
- // Check that the get errors message returns an empty string.
- assertTrue("The errors method did not return an empty string.", "".equals(parser.getErrors()));
- }
-
- /** Check that get errors returns a string on errors. */
- public void testGetErrorsReturnsStringOnErrors() throws Exception
- {
- // Create a command line parser for some flags and options.
- CommandLineParser parser =
- new CommandLineParser(
- new String[][]
- {
- { "t1", "Test Flag 1." },
- { "t2", "Test Option 2.", "test" },
- { "t3", "Test Option 3.", "test", "true" },
- { "t4", "Test Option 4.", "test", null, "^test$" }
- });
-
- try
- {
- // Do some illegal parsing.
- parser.parseCommandLine(new String[] { "-t1", "-t1t2test", "-t4fail" });
- }
- catch (IllegalArgumentException e)
- { }
-
- // Check that the get errors message returns a string.
- assertTrue("The errors method returned an empty string.",
- !((parser.getErrors() == null) || "".equals(parser.getErrors())));
-
- }
-
- /** Check that get options in force returns an empty string before parsing. */
- public void testGetOptionsInForceReturnsEmptyStringBeforeParsing() throws Exception
- {
- // Create a command line parser for some flags and options.
- CommandLineParser parser =
- new CommandLineParser(
- new String[][]
- {
- { "t1", "Test Flag 1." },
- { "t2", "Test Option 2.", "test" },
- { "t3", "Test Option 3.", "test", "true" },
- { "t4", "Test Option 4.", "test", null, "^test$" }
- });
-
- // Check that the options in force method returns an empty string.
- assertTrue("The options in force method did not return an empty string.", "".equals(parser.getOptionsInForce()));
- }
-
- /** Check that get options in force return a non-empty string after parsing. */
- public void testGetOptionsInForceReturnsNonEmptyStringAfterParsing() throws Exception
- {
- // Create a command line parser for some flags and options.
- CommandLineParser parser =
- new CommandLineParser(
- new String[][]
- {
- { "t1", "Test Flag 1." },
- { "t2", "Test Option 2.", "test" },
- { "t3", "Test Option 3.", "test", "true" },
- { "t4", "Test Option 4.", "test", null, "^test$" }
- });
-
- // Do some parsing.
- parser.parseCommandLine(new String[] { "-t1", "-t2test", "-t3test", "-t4test" });
-
- // Check that the options in force method returns a string.
- assertTrue("The options in force method did not return a non empty string.",
- !((parser.getOptionsInForce() == null) || "".equals(parser.getOptionsInForce())));
- }
-
- /** Check that get usage returns a string. */
- public void testGetUsageReturnsString() throws Exception
- {
- // Create a command line parser for some flags and options.
- CommandLineParser parser =
- new CommandLineParser(
- new String[][]
- {
- { "t1", "Test Flag 1." },
- { "t2", "Test Option 2.", "test" },
- { "t3", "Test Option 3.", "test", "true" },
- { "t4", "Test Option 4.", "test", null, "^test$" }
- });
-
- // Check that the usage method returns a string.
- assertTrue("The usage method did not return a non empty string.",
- !((parser.getUsage() == null) || "".equals(parser.getUsage())));
- }
-
- /** Check that parsing multiple flags condensed together works ok. */
- public void testParseCondensedFlagsOk() throws Exception
- {
- // Create a command line parser for multiple flags.
- CommandLineParser parser =
- new CommandLineParser(
- new String[][]
- {
- { "t1", "Test Flag 1." },
- { "t2", "Test Flag 2." },
- { "t3", "Test Flag 3." }
- });
-
- // Parse a command line with the flags set and condensed together.
- Properties testProps = parser.parseCommandLine(new String[] { "-t1t2t3" });
-
- // Check that the flags were set in the parsed properties.
- assertTrue("The t1 flag was not \"true\", it was: " + testProps.get("t1"), "true".equals(testProps.get("t1")));
- assertTrue("The t2 flag was not \"true\", it was: " + testProps.get("t2"), "true".equals(testProps.get("t2")));
- assertTrue("The t3 flag was not \"true\", it was: " + testProps.get("t3"), "true".equals(testProps.get("t3")));
- }
-
- /** Check that parsing a flag condensed together with an option fails. */
- public void testParseFlagCondensedWithOptionFails() throws Exception
- {
- // Create a command line parser for a flag and an option.
- CommandLineParser parser =
- new CommandLineParser(new String[][]
- {
- { "t1", "Test Flag 1." },
- { "t2", "Test Option 2.", "test" }
- });
-
- // Check that the parser reports an error.
- boolean testPassed = false;
-
- try
- {
- // Parse a command line with the flag and option condensed together.
- Properties testProps = parser.parseCommandLine(new String[] { "-t1t2" });
- }
- catch (IllegalArgumentException e)
- {
- testPassed = true;
- }
-
- assertTrue("IllegalArgumentException not thrown when a flag and option are condensed together.", testPassed);
- }
-
- /** Check that parsing a free argument with specific format fails on bad argument. */
- public void testParseFormattedFreeArgumentFailsBadArgument() throws Exception
- {
- // Create a command line parser for a formatted free argument.
- CommandLineParser parser =
- new CommandLineParser(new String[][]
- {
- { "1", "Test Free Argument.", "test", null, "^test$" }
- });
-
- // Check that the parser signals an error for a badly formatted argument.
- boolean testPassed = false;
-
- try
- {
- // Parse a command line with this option set incorrectly.
- Properties testProps = parser.parseCommandLine(new String[] { "fail" });
- }
- catch (IllegalArgumentException e)
- {
- testPassed = true;
- }
-
- assertTrue("IllegalArgumentException not thrown when a badly formatted argument was set.", testPassed);
- }
-
- /** Check that parsing a free argument with specific format works ok. */
- public void testParseFormattedFreeArgumentOk() throws Exception
- {
- // Create a command line parser for a formatted free argument.
- CommandLineParser parser =
- new CommandLineParser(new String[][]
- {
- { "1", "Test Free Argument.", "test", null, "^test$" }
- });
-
- // Parse a command line with this argument set correctly.
- Properties testProps = parser.parseCommandLine(new String[] { "test" });
-
- // Check that the resultant properties contains the correctly parsed option.
- assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"),
- "test".equals(testProps.get("1")));
- }
-
- /** Check that parsing an option with specific argument format fails on bad argument. */
- public void testParseFormattedOptionArgumentFailsBadArgument() throws Exception
- {
- // Create a command line parser for a formatted option.
- CommandLineParser parser = new CommandLineParser(new String[][]
- {
- { "t", "Test Option.", "test", null, "^test$" }
- });
-
- // Check that the parser signals an error for a badly formatted argument.
- boolean testPassed = false;
-
- try
- {
- // Parse a command line with this option set incorrectly.
- Properties testProps = parser.parseCommandLine(new String[] { "-t", "fail" });
- }
- catch (IllegalArgumentException e)
- {
- testPassed = true;
- }
-
- assertTrue("IllegalArgumentException not thrown when a badly formatted argument was set.", testPassed);
- }
-
- /** Check that parsing an option with specific argument format works ok. */
- public void testParseFormattedOptionArgumentOk() throws Exception
- {
- // Create a command line parser for a formatted option.
- CommandLineParser parser = new CommandLineParser(new String[][]
- {
- { "t", "Test Option.", "test", null, "^test$" }
- });
-
- // Parse a command line with this option set correctly.
- Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" });
-
- // Check that the resultant properties contains the correctly parsed option.
- assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"),
- "test".equals(testProps.get("t")));
- }
-
- /** Check that parsing a free argument works ok. */
- public void testParseFreeArgumentOk() throws Exception
- {
- // Create a command line parser for a free argument.
- CommandLineParser parser = new CommandLineParser(new String[][]
- {
- { "1", "Test Free Argument.", "test" }
- });
-
- // Parse a command line with this argument set.
- Properties testProps = parser.parseCommandLine(new String[] { "test" });
-
- // Check that the resultant properties contains the correctly parsed option.
- assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"),
- "test".equals(testProps.get("1")));
- }
-
- /** Check that parsing a mandatory option works ok. */
- public void testParseMandatoryOptionOk() throws Exception
- {
- // Create a command line parser for a mandatory option.
- CommandLineParser parser = new CommandLineParser(new String[][]
- {
- { "t", "Test Option.", "test", "true" }
- });
-
- // Parse a command line with this option set correctly.
- Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" });
-
- // Check that the resultant properties contains the correctly parsed option.
- assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"),
- "test".equals(testProps.get("t")));
- }
-
- /** Check that parsing a mandatory free argument works ok. */
- public void testParseMandatoryFreeArgumentOk() throws Exception
- {
- // Create a command line parser for a mandatory free argument.
- CommandLineParser parser = new CommandLineParser(new String[][]
- {
- { "1", "Test Option.", "test", "true" }
- });
-
- // Parse a command line with this argument set.
- Properties testProps = parser.parseCommandLine(new String[] { "test" });
-
- // Check that the resultant properties contains the correctly parsed option.
- assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"),
- "test".equals(testProps.get("1")));
- }
-
- /** Check that parsing a mandatory free argument fails when no argument is specified. */
- public void testParseManadatoryFreeArgumentFailsNoArgument() throws Exception
- {
- // Create a command line parser for a mandatory free argument.
- CommandLineParser parser = new CommandLineParser(new String[][]
- {
- { "1", "Test Option.", "test", "true" }
- });
-
- // Check that parsing fails when this mandatory free argument is missing.
- boolean testPassed = false;
-
- try
- {
- // Parse a command line with this free argument not set.
- Properties testProps = parser.parseCommandLine(new String[] {});
- }
- catch (IllegalArgumentException e)
- {
- testPassed = true;
- }
-
- // Check that the resultant properties contains the correctly parsed option.
- assertTrue("IllegalArgumentException not thrown for a missing mandatory option.", testPassed);
- }
-
- /** Check that parsing a mandatory option fails when no option is set. */
- public void testParseMandatoryFailsNoOption() throws Exception
- {
- // Create a command line parser for a mandatory option.
- CommandLineParser parser = new CommandLineParser(new String[][]
- {
- { "t", "Test Option.", "test", "true" }
- });
-
- // Check that parsing fails when this mandatory option is missing.
- boolean testPassed = false;
-
- try
- {
- // Parse a command line with this option not set.
- Properties testProps = parser.parseCommandLine(new String[] {});
- }
- catch (IllegalArgumentException e)
- {
- testPassed = true;
- }
-
- // Check that the resultant properties contains the correctly parsed option.
- assertTrue("IllegalArgumentException not thrown for a missing mandatory option.", testPassed);
- }
-
- /** Check that parsing an option with no space between it and its argument works ok. */
- public void testParseOptionWithNoSpaceOk() throws Exception
- {
- // Create a command line parser for an option.
- CommandLineParser parser = new CommandLineParser(new String[][]
- {
- { "t", "Test Option.", "test" }
- });
-
- // Parse a command line with this option set with no space.
- Properties testProps = parser.parseCommandLine(new String[] { "-ttest" });
-
- // Check that the resultant properties contains the correctly parsed option.
- assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"),
- "test".equals(testProps.get("t")));
- }
-
- /** Check that parsing an option with a space between it and its argument works ok. */
- public void testParseOptionWithSpaceOk() throws Exception
- {
- // Create a command line parser for an option.
- CommandLineParser parser = new CommandLineParser(new String[][]
- {
- { "t", "Test Option.", "test" }
- });
-
- // Parse a command line with this option set with a space.
- Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" });
-
- // Check that the resultant properties contains the correctly parsed option.
- assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"),
- "test".equals(testProps.get("t")));
- }
-
- /** Check that parsing a single flag works ok. */
- public void testParseSingleFlagOk() throws Exception
- {
- // Create a command line parser for a single flag.
- CommandLineParser parser = new CommandLineParser(new String[][]
- {
- { "t", "Test Flag." }
- });
-
- // Parse a command line with the single flag set.
- Properties testProps = parser.parseCommandLine(new String[] { "-t" });
-
- // Check that the flag is set in the parsed properties.
- assertTrue("The t flag was not \"true\", it was: " + testProps.get("t"), "true".equals(testProps.get("t")));
-
- // Reset the parser.
- parser.reset();
-
- // Parse a command line with the single flag not set.
- testProps = parser.parseCommandLine(new String[] {});
-
- // Check that the flag is cleared in the parsed properties.
- assertTrue("The t flag was not \"false\", it was: " + testProps.get("t"), "false".equals(testProps.get("t")));
- }
-
- /** Check that parsing an unknown option works when unknowns not errors. */
- public void testParseUnknownOptionOk() throws Exception
- {
- // Create a command line parser for no flags or options
- CommandLineParser parser = new CommandLineParser(new String[][] {});
-
- // Check that parsing does not fail on an unknown flag.
- try
- {
- parser.parseCommandLine(new String[] { "-t" });
- }
- catch (IllegalArgumentException e)
- {
- fail("The parser threw an IllegalArgumentException on an unknown flag when errors on unkowns is off.");
- }
- }
-
- /** Check that parsing an unknown flag fails when unknowns are to be reported as errors. */
- public void testParseUnknownFlagFailsWhenUnknownsAreErrors() throws Exception
- {
- // Create a command line parser for no flags or options
- CommandLineParser parser = new CommandLineParser(new String[][] {});
-
- // Turn on fail on unknowns mode.
- parser.setErrorsOnUnknowns(true);
-
- // Check that parsing fails on an unknown flag.
- boolean testPassed = false;
-
- try
- {
- parser.parseCommandLine(new String[] { "-t" });
- }
- catch (IllegalArgumentException e)
- {
- testPassed = true;
- }
-
- assertTrue("IllegalArgumentException not thrown for an unknown flag when errors on unknowns mode is on.",
- testPassed);
- }
-
- /** Check that parsing an unknown option fails when unknowns are to be reported as errors. */
- public void testParseUnknownOptionFailsWhenUnknownsAreErrors() throws Exception
- {
- // Create a command line parser for no flags or options
- CommandLineParser parser = new CommandLineParser(new String[][] {});
-
- // Turn on fail on unknowns mode.
- parser.setErrorsOnUnknowns(true);
-
- // Check that parsing fails on an unknown flag.
- boolean testPassed = false;
-
- try
- {
- parser.parseCommandLine(new String[] { "-t", "test" });
- }
- catch (IllegalArgumentException e)
- {
- testPassed = true;
- }
-
- assertTrue("IllegalArgumentException not thrown for an unknown option when errors on unknowns mode is on.",
- testPassed);
- }
-}
+/* + * + * 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.util; + +import junit.framework.*; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Properties; + +/** + * Unit tests the {@link CommandLineParser} class. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Check that parsing a single flag works ok. + * <tr><td> Check that parsing multiple flags condensed together works ok. + * <tr><td> Check that parsing an option with a space between it and its argument works ok. + * <tr><td> Check that parsing an option with no space between it and its argument works ok. + * <tr><td> Check that parsing an option with specific argument format works ok. + * <tr><td> Check that parsing an option with specific argument format fails on bad argument. + * <tr><td> Check that parsing a flag condensed together with an option fails. + * <tr><td> Check that parsing a free argument works ok. + * <tr><td> Check that parsing a free argument with specific format works ok. + * <tr><td> Check that parsing a free argument with specific format fails on bad argument. + * <tr><td> Check that parsing a mandatory option works ok. + * <tr><td> Check that parsing a mandatory free argument works ok. + * <tr><td> Check that parsing a mandatory option fails when no option is set. + * <tr><td> Check that parsing a mandatory free argument fails when no argument is specified. + * <tr><td> Check that parsing an unknown option works when unknowns not errors. + * <tr><td> Check that parsing an unknown flag fails when unknowns are to be reported as errors. + * <tr><td> Check that parsing an unknown option fails when unknowns are to be reported as errors. + * <tr><td> Check that get errors returns a string on errors. + * <tr><td> Check that get errors returns an empty string on no errors. + * <tr><td> Check that get usage returns a string. + * <tr><td> Check that get options in force returns an empty string before parsing. + * <tr><td> Check that get options in force return a non-empty string after parsing. + * </table> + */ +public class CommandLineParserTest extends TestCase +{ + private static final Logger log = LoggerFactory.getLogger(CommandLineParserTest.class); + + public CommandLineParserTest(String name) + { + super(name); + } + + /** + * Compile all the tests for the default test implementation of a traversable state into a test suite. + */ + public static Test suite() + { + // Build a new test suite + TestSuite suite = new TestSuite("CommandLineParser Tests"); + + // Add all the tests defined in this class (using the default constructor) + suite.addTestSuite(CommandLineParserTest.class); + + return suite; + } + + /** Check that get errors returns an empty string on no errors. */ + public void testGetErrorsReturnsEmptyStringOnNoErrors() throws Exception + { + // Create a command line parser for some flags and options. + CommandLineParser parser = + new CommandLineParser( + new String[][] + { + { "t1", "Test Flag 1." }, + { "t2", "Test Option 2.", "test" }, + { "t3", "Test Option 3.", "test", "true" }, + { "t4", "Test Option 4.", "test", null, "^test$" } + }); + + // Do some legal parsing. + parser.parseCommandLine(new String[] { "-t1", "-t2test", "-t3test", "-t4test" }); + + // Check that the get errors message returns an empty string. + assertTrue("The errors method did not return an empty string.", "".equals(parser.getErrors())); + } + + /** Check that get errors returns a string on errors. */ + public void testGetErrorsReturnsStringOnErrors() throws Exception + { + // Create a command line parser for some flags and options. + CommandLineParser parser = + new CommandLineParser( + new String[][] + { + { "t1", "Test Flag 1." }, + { "t2", "Test Option 2.", "test" }, + { "t3", "Test Option 3.", "test", "true" }, + { "t4", "Test Option 4.", "test", null, "^test$" } + }); + + try + { + // Do some illegal parsing. + parser.parseCommandLine(new String[] { "-t1", "-t1t2test", "-t4fail" }); + } + catch (IllegalArgumentException e) + { } + + // Check that the get errors message returns a string. + assertTrue("The errors method returned an empty string.", + !((parser.getErrors() == null) || "".equals(parser.getErrors()))); + + } + + /** Check that get options in force returns an empty string before parsing. */ + public void testGetOptionsInForceReturnsEmptyStringBeforeParsing() throws Exception + { + // Create a command line parser for some flags and options. + CommandLineParser parser = + new CommandLineParser( + new String[][] + { + { "t1", "Test Flag 1." }, + { "t2", "Test Option 2.", "test" }, + { "t3", "Test Option 3.", "test", "true" }, + { "t4", "Test Option 4.", "test", null, "^test$" } + }); + + // Check that the options in force method returns an empty string. + assertTrue("The options in force method did not return an empty string.", "".equals(parser.getOptionsInForce())); + } + + /** Check that get options in force return a non-empty string after parsing. */ + public void testGetOptionsInForceReturnsNonEmptyStringAfterParsing() throws Exception + { + // Create a command line parser for some flags and options. + CommandLineParser parser = + new CommandLineParser( + new String[][] + { + { "t1", "Test Flag 1." }, + { "t2", "Test Option 2.", "test" }, + { "t3", "Test Option 3.", "test", "true" }, + { "t4", "Test Option 4.", "test", null, "^test$" } + }); + + // Do some parsing. + parser.parseCommandLine(new String[] { "-t1", "-t2test", "-t3test", "-t4test" }); + + // Check that the options in force method returns a string. + assertTrue("The options in force method did not return a non empty string.", + !((parser.getOptionsInForce() == null) || "".equals(parser.getOptionsInForce()))); + } + + /** Check that get usage returns a string. */ + public void testGetUsageReturnsString() throws Exception + { + // Create a command line parser for some flags and options. + CommandLineParser parser = + new CommandLineParser( + new String[][] + { + { "t1", "Test Flag 1." }, + { "t2", "Test Option 2.", "test" }, + { "t3", "Test Option 3.", "test", "true" }, + { "t4", "Test Option 4.", "test", null, "^test$" } + }); + + // Check that the usage method returns a string. + assertTrue("The usage method did not return a non empty string.", + !((parser.getUsage() == null) || "".equals(parser.getUsage()))); + } + + /** Check that parsing multiple flags condensed together works ok. */ + public void testParseCondensedFlagsOk() throws Exception + { + // Create a command line parser for multiple flags. + CommandLineParser parser = + new CommandLineParser( + new String[][] + { + { "t1", "Test Flag 1." }, + { "t2", "Test Flag 2." }, + { "t3", "Test Flag 3." } + }); + + // Parse a command line with the flags set and condensed together. + Properties testProps = parser.parseCommandLine(new String[] { "-t1t2t3" }); + + // Check that the flags were set in the parsed properties. + assertTrue("The t1 flag was not \"true\", it was: " + testProps.get("t1"), "true".equals(testProps.get("t1"))); + assertTrue("The t2 flag was not \"true\", it was: " + testProps.get("t2"), "true".equals(testProps.get("t2"))); + assertTrue("The t3 flag was not \"true\", it was: " + testProps.get("t3"), "true".equals(testProps.get("t3"))); + } + + /** Check that parsing a flag condensed together with an option fails. */ + public void testParseFlagCondensedWithOptionFails() throws Exception + { + // Create a command line parser for a flag and an option. + CommandLineParser parser = + new CommandLineParser(new String[][] + { + { "t1", "Test Flag 1." }, + { "t2", "Test Option 2.", "test" } + }); + + // Check that the parser reports an error. + boolean testPassed = false; + + try + { + // Parse a command line with the flag and option condensed together. + Properties testProps = parser.parseCommandLine(new String[] { "-t1t2" }); + } + catch (IllegalArgumentException e) + { + testPassed = true; + } + + assertTrue("IllegalArgumentException not thrown when a flag and option are condensed together.", testPassed); + } + + /** Check that parsing a free argument with specific format fails on bad argument. */ + public void testParseFormattedFreeArgumentFailsBadArgument() throws Exception + { + // Create a command line parser for a formatted free argument. + CommandLineParser parser = + new CommandLineParser(new String[][] + { + { "1", "Test Free Argument.", "test", null, "^test$" } + }); + + // Check that the parser signals an error for a badly formatted argument. + boolean testPassed = false; + + try + { + // Parse a command line with this option set incorrectly. + Properties testProps = parser.parseCommandLine(new String[] { "fail" }); + } + catch (IllegalArgumentException e) + { + testPassed = true; + } + + assertTrue("IllegalArgumentException not thrown when a badly formatted argument was set.", testPassed); + } + + /** Check that parsing a free argument with specific format works ok. */ + public void testParseFormattedFreeArgumentOk() throws Exception + { + // Create a command line parser for a formatted free argument. + CommandLineParser parser = + new CommandLineParser(new String[][] + { + { "1", "Test Free Argument.", "test", null, "^test$" } + }); + + // Parse a command line with this argument set correctly. + Properties testProps = parser.parseCommandLine(new String[] { "test" }); + + // Check that the resultant properties contains the correctly parsed option. + assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"), + "test".equals(testProps.get("1"))); + } + + /** Check that parsing an option with specific argument format fails on bad argument. */ + public void testParseFormattedOptionArgumentFailsBadArgument() throws Exception + { + // Create a command line parser for a formatted option. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "t", "Test Option.", "test", null, "^test$" } + }); + + // Check that the parser signals an error for a badly formatted argument. + boolean testPassed = false; + + try + { + // Parse a command line with this option set incorrectly. + Properties testProps = parser.parseCommandLine(new String[] { "-t", "fail" }); + } + catch (IllegalArgumentException e) + { + testPassed = true; + } + + assertTrue("IllegalArgumentException not thrown when a badly formatted argument was set.", testPassed); + } + + /** Check that parsing an option with specific argument format works ok. */ + public void testParseFormattedOptionArgumentOk() throws Exception + { + // Create a command line parser for a formatted option. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "t", "Test Option.", "test", null, "^test$" } + }); + + // Parse a command line with this option set correctly. + Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" }); + + // Check that the resultant properties contains the correctly parsed option. + assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"), + "test".equals(testProps.get("t"))); + } + + /** Check that parsing a free argument works ok. */ + public void testParseFreeArgumentOk() throws Exception + { + // Create a command line parser for a free argument. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "1", "Test Free Argument.", "test" } + }); + + // Parse a command line with this argument set. + Properties testProps = parser.parseCommandLine(new String[] { "test" }); + + // Check that the resultant properties contains the correctly parsed option. + assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"), + "test".equals(testProps.get("1"))); + } + + /** Check that parsing a mandatory option works ok. */ + public void testParseMandatoryOptionOk() throws Exception + { + // Create a command line parser for a mandatory option. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "t", "Test Option.", "test", "true" } + }); + + // Parse a command line with this option set correctly. + Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" }); + + // Check that the resultant properties contains the correctly parsed option. + assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"), + "test".equals(testProps.get("t"))); + } + + /** Check that parsing a mandatory free argument works ok. */ + public void testParseMandatoryFreeArgumentOk() throws Exception + { + // Create a command line parser for a mandatory free argument. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "1", "Test Option.", "test", "true" } + }); + + // Parse a command line with this argument set. + Properties testProps = parser.parseCommandLine(new String[] { "test" }); + + // Check that the resultant properties contains the correctly parsed option. + assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"), + "test".equals(testProps.get("1"))); + } + + /** Check that parsing a mandatory free argument fails when no argument is specified. */ + public void testParseManadatoryFreeArgumentFailsNoArgument() throws Exception + { + // Create a command line parser for a mandatory free argument. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "1", "Test Option.", "test", "true" } + }); + + // Check that parsing fails when this mandatory free argument is missing. + boolean testPassed = false; + + try + { + // Parse a command line with this free argument not set. + Properties testProps = parser.parseCommandLine(new String[] {}); + } + catch (IllegalArgumentException e) + { + testPassed = true; + } + + // Check that the resultant properties contains the correctly parsed option. + assertTrue("IllegalArgumentException not thrown for a missing mandatory option.", testPassed); + } + + /** Check that parsing a mandatory option fails when no option is set. */ + public void testParseMandatoryFailsNoOption() throws Exception + { + // Create a command line parser for a mandatory option. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "t", "Test Option.", "test", "true" } + }); + + // Check that parsing fails when this mandatory option is missing. + boolean testPassed = false; + + try + { + // Parse a command line with this option not set. + Properties testProps = parser.parseCommandLine(new String[] {}); + } + catch (IllegalArgumentException e) + { + testPassed = true; + } + + // Check that the resultant properties contains the correctly parsed option. + assertTrue("IllegalArgumentException not thrown for a missing mandatory option.", testPassed); + } + + /** Check that parsing an option with no space between it and its argument works ok. */ + public void testParseOptionWithNoSpaceOk() throws Exception + { + // Create a command line parser for an option. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "t", "Test Option.", "test" } + }); + + // Parse a command line with this option set with no space. + Properties testProps = parser.parseCommandLine(new String[] { "-ttest" }); + + // Check that the resultant properties contains the correctly parsed option. + assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"), + "test".equals(testProps.get("t"))); + } + + /** Check that parsing an option with a space between it and its argument works ok. */ + public void testParseOptionWithSpaceOk() throws Exception + { + // Create a command line parser for an option. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "t", "Test Option.", "test" } + }); + + // Parse a command line with this option set with a space. + Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" }); + + // Check that the resultant properties contains the correctly parsed option. + assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"), + "test".equals(testProps.get("t"))); + } + + /** Check that parsing a single flag works ok. */ + public void testParseSingleFlagOk() throws Exception + { + // Create a command line parser for a single flag. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "t", "Test Flag." } + }); + + // Parse a command line with the single flag set. + Properties testProps = parser.parseCommandLine(new String[] { "-t" }); + + // Check that the flag is set in the parsed properties. + assertTrue("The t flag was not \"true\", it was: " + testProps.get("t"), "true".equals(testProps.get("t"))); + + // Reset the parser. + parser.reset(); + + // Parse a command line with the single flag not set. + testProps = parser.parseCommandLine(new String[] {}); + + // Check that the flag is cleared in the parsed properties. + assertTrue("The t flag was not \"false\", it was: " + testProps.get("t"), "false".equals(testProps.get("t"))); + } + + /** Check that parsing an unknown option works when unknowns not errors. */ + public void testParseUnknownOptionOk() throws Exception + { + // Create a command line parser for no flags or options + CommandLineParser parser = new CommandLineParser(new String[][] {}); + + // Check that parsing does not fail on an unknown flag. + try + { + parser.parseCommandLine(new String[] { "-t" }); + } + catch (IllegalArgumentException e) + { + fail("The parser threw an IllegalArgumentException on an unknown flag when errors on unkowns is off."); + } + } + + /** Check that parsing an unknown flag fails when unknowns are to be reported as errors. */ + public void testParseUnknownFlagFailsWhenUnknownsAreErrors() throws Exception + { + // Create a command line parser for no flags or options + CommandLineParser parser = new CommandLineParser(new String[][] {}); + + // Turn on fail on unknowns mode. + parser.setErrorsOnUnknowns(true); + + // Check that parsing fails on an unknown flag. + boolean testPassed = false; + + try + { + parser.parseCommandLine(new String[] { "-t" }); + } + catch (IllegalArgumentException e) + { + testPassed = true; + } + + assertTrue("IllegalArgumentException not thrown for an unknown flag when errors on unknowns mode is on.", + testPassed); + } + + /** Check that parsing an unknown option fails when unknowns are to be reported as errors. */ + public void testParseUnknownOptionFailsWhenUnknownsAreErrors() throws Exception + { + // Create a command line parser for no flags or options + CommandLineParser parser = new CommandLineParser(new String[][] {}); + + // Turn on fail on unknowns mode. + parser.setErrorsOnUnknowns(true); + + // Check that parsing fails on an unknown flag. + boolean testPassed = false; + + try + { + parser.parseCommandLine(new String[] { "-t", "test" }); + } + catch (IllegalArgumentException e) + { + testPassed = true; + } + + assertTrue("IllegalArgumentException not thrown for an unknown option when errors on unknowns mode is on.", + testPassed); + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java index 45bbfcd148..db17c7aacc 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java @@ -1,135 +1,135 @@ -/*
- *
- * 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.interop.clienttestcases;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest;
-
-import javax.jms.JMSException;
-import javax.jms.Message;
-import javax.jms.Session;
-
-/**
- * Implements tet case 1, dummy run. This test case sends no test messages, it exists to confirm that the test harness
- * is interacting with the coordinator correctly.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Supply the name of the test case that this implements.
- * <tr><td> Accept/Reject invites based on test parameters.
- * <tr><td> Adapt to assigned roles.
- * <tr><td> Perform test case actions.
- * <tr><td> Generate test reports.
- * </table>
- */
-public class TestCase1DummyRun implements TestClientControlledTest
-{
- /** Used for debugging. */
- private static final Logger log = Logger.getLogger(TestCase1DummyRun.class);
-
- /**
- * Should provide the name of the test case that this class implements. The exact names are defined in the
- * interop testing spec.
- *
- * @return The name of the test case that this implements.
- */
- public String getName()
- {
- log.debug("public String getName(): called");
-
- return "TC1_DummyRun";
- }
-
- /**
- * Determines whether the test invite that matched this test case is acceptable.
- *
- * @param inviteMessage The invitation to accept or reject.
- *
- * @return <tt>true</tt> to accept the invitation, <tt>false</tt> to reject it.
- *
- * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
- */
- public boolean acceptInvite(Message inviteMessage) throws JMSException
- {
- log.debug("public boolean acceptInvite(Message inviteMessage): called");
-
- // Test parameters don't matter, accept all invites.
- return true;
- }
-
- /**
- * Assigns the role to be played by this test case. The test parameters are fully specified in the
- * assignment message. When this method return the test case will be ready to execute.
- *
- * @param role The role to be played; sender or receivers.
- * @param assignRoleMessage The role assingment message, contains the full test parameters.
- *
- * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
- */
- public void assignRole(Roles role, Message assignRoleMessage) throws JMSException
- {
- log.debug("public void assignRole(Roles role, Message assignRoleMessage): called");
-
- // Do nothing, both roles are the same.
- }
-
- /**
- * Performs the test case actions. Returning from here, indicates that the sending role has completed its test.
- *
- * @param numMessages The number of test messages to send.
- */
- public void start(int numMessages)
- {
- log.debug("public void start(): called");
-
- // Do nothing.
- }
-
- /**
- * Gets a report on the actions performed by the test case in its assigned role.
- *
- * @param session The controlSession to create the report message in.
- *
- * @return The report message.
- *
- * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through.
- */
- public Message getReport(Session session) throws JMSException
- {
- log.debug("public Message getReport(Session controlSession): called");
-
- // Generate a dummy report, the coordinator expects a report but doesn't care what it is.
- return session.createTextMessage("Dummy Run, Ok.");
- }
-
- /**
- * Handles incoming test messages. Does nothing.
- *
- * @param message The incoming test message.
- */
- public void onMessage(Message message)
- {
- log.debug("public void onMessage(Message message = " + message + "): called");
-
- // Ignore any messages.
- }
-}
+/* + * + * 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.interop.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; + +/** + * Implements tet case 1, dummy run. This test case sends no test messages, it exists to confirm that the test harness + * is interacting with the coordinator correctly. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Supply the name of the test case that this implements. + * <tr><td> Accept/Reject invites based on test parameters. + * <tr><td> Adapt to assigned roles. + * <tr><td> Perform test case actions. + * <tr><td> Generate test reports. + * </table> + */ +public class TestCase1DummyRun implements TestClientControlledTest +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase1DummyRun.class); + + /** + * Should provide the name of the test case that this class implements. The exact names are defined in the + * interop testing spec. + * + * @return The name of the test case that this implements. + */ + public String getName() + { + log.debug("public String getName(): called"); + + return "TC1_DummyRun"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return <tt>true</tt> to accept the invitation, <tt>false</tt> to reject it. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage): called"); + + // Test parameters don't matter, accept all invites. + return true; + } + + /** + * Assigns the role to be played by this test case. The test parameters are fully specified in the + * assignment message. When this method return the test case will be ready to execute. + * + * @param role The role to be played; sender or receivers. + * @param assignRoleMessage The role assingment message, contains the full test parameters. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void assignRole(Roles role, Message assignRoleMessage) throws JMSException + { + log.debug("public void assignRole(Roles role, Message assignRoleMessage): called"); + + // Do nothing, both roles are the same. + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + */ + public void start(int numMessages) + { + log.debug("public void start(): called"); + + // Do nothing. + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession to create the report message in. + * + * @return The report message. + * + * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. + */ + public Message getReport(Session session) throws JMSException + { + log.debug("public Message getReport(Session controlSession): called"); + + // Generate a dummy report, the coordinator expects a report but doesn't care what it is. + return session.createTextMessage("Dummy Run, Ok."); + } + + /** + * Handles incoming test messages. Does nothing. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Ignore any messages. + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java index 1d30ff7ca6..36d3cce7f7 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java @@ -1,209 +1,209 @@ -/*
- *
- * 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.interop.clienttestcases;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.test.framework.TestUtils;
-import org.apache.qpid.test.framework.distributedtesting.TestClient;
-import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest;
-
-import javax.jms.*;
-
-/**
- * Implements test case 2, basic P2P. Sends/received a specified number of messages to a specified route on the
- * default direct exchange. Produces reports on the actual number of messages sent/received.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Supply the name of the test case that this implements.
- * <tr><td> Accept/Reject invites based on test parameters.
- * <tr><td> Adapt to assigned roles.
- * <tr><td> Send required number of test messages.
- * <tr><td> Generate test reports.
- * </table>
- */
-public class TestCase2BasicP2P implements TestClientControlledTest, MessageListener
-{
- /** Used for debugging. */
- private static final Logger log = Logger.getLogger(TestCase2BasicP2P.class);
-
- /** Holds the count of test messages received. */
- private int messageCount;
-
- /** The role to be played by the test. */
- private Roles role;
-
- /** The number of test messages to send. */
- private int numMessages;
-
- /** The connection to send the test messages on. */
- private Connection connection;
-
- /** The controlSession to send the test messages on. */
- private Session session;
-
- /** The producer to send the test messages with. */
- MessageProducer producer;
-
- /**
- * Should provide the name of the test case that this class implements. The exact names are defined in the
- * interop testing spec.
- *
- * @return The name of the test case that this implements.
- */
- public String getName()
- {
- log.debug("public String getName(): called");
-
- return "TC2_BasicP2P";
- }
-
- /**
- * Determines whether the test invite that matched this test case is acceptable.
- *
- * @param inviteMessage The invitation to accept or reject.
- *
- * @return <tt>true</tt> to accept the invitation, <tt>false</tt> to reject it.
- *
- * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
- */
- public boolean acceptInvite(Message inviteMessage) throws JMSException
- {
- log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called");
-
- // All invites are acceptable.
- return true;
- }
-
- /**
- * Assigns the role to be played by this test case. The test parameters are fully specified in the
- * assignment message. When this method return the test case will be ready to execute.
- *
- * @param role The role to be played; sender or receivers.
- *
- * @param assignRoleMessage The role assingment message, contains the full test parameters.
- *
- * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
- */
- public void assignRole(Roles role, Message assignRoleMessage) throws JMSException
- {
- log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage
- + "): called");
-
- // Reset the message count for a new test.
- messageCount = 0;
-
- // Take note of the role to be played.
- this.role = role;
-
- // Create a new connection to pass the test messages on.
- connection = TestUtils.createConnection(TestClient.testContextProperties);
- session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
-
- // Extract and retain the test parameters.
- numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES");
- Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME"));
-
- log.debug("numMessages = " + numMessages);
- log.debug("sendDestination = " + sendDestination);
- log.debug("role = " + role);
-
- switch (role)
- {
- // Check if the sender role is being assigned, and set up a message producer if so.
- case SENDER:
- producer = session.createProducer(sendDestination);
- break;
-
- // Otherwise the receivers role is being assigned, so set this up to listen for messages.
- case RECEIVER:
- MessageConsumer consumer = session.createConsumer(sendDestination);
- consumer.setMessageListener(this);
- break;
- }
-
- connection.start();
- }
-
- /**
- * Performs the test case actions. Returning from here, indicates that the sending role has completed its test.
- *
- * @param numMessages The number of test messages to send.
- *
- * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
- */
- public void start(int numMessages) throws JMSException
- {
- log.debug("public void start(): called");
-
- // Check that the sender role is being performed.
- if (role.equals(Roles.SENDER))
- {
- Message testMessage = session.createTextMessage("test");
-
- for (int i = 0; i < this.numMessages; i++)
- {
- producer.send(testMessage);
-
- // Increment the message count.
- messageCount++;
- }
- }
- }
-
- /**
- * Gets a report on the actions performed by the test case in its assigned role.
- *
- * @param session The controlSession to create the report message in.
- *
- * @return The report message.
- *
- * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through.
- */
- public Message getReport(Session session) throws JMSException
- {
- log.debug("public Message getReport(Session controlSession): called");
-
- // Close the test connection.
- connection.close();
-
- // Generate a report message containing the count of the number of messages passed.
- Message report = session.createMessage();
- report.setStringProperty("CONTROL_TYPE", "REPORT");
- report.setIntProperty("MESSAGE_COUNT", messageCount);
-
- return report;
- }
-
- /**
- * Counts incoming test messages.
- *
- * @param message The incoming test message.
- */
- public void onMessage(Message message)
- {
- log.debug("public void onMessage(Message message = " + message + "): called");
-
- // Increment the message count.
- messageCount++;
- }
-}
+/* + * + * 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.interop.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.*; + +/** + * Implements test case 2, basic P2P. Sends/received a specified number of messages to a specified route on the + * default direct exchange. Produces reports on the actual number of messages sent/received. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Supply the name of the test case that this implements. + * <tr><td> Accept/Reject invites based on test parameters. + * <tr><td> Adapt to assigned roles. + * <tr><td> Send required number of test messages. + * <tr><td> Generate test reports. + * </table> + */ +public class TestCase2BasicP2P implements TestClientControlledTest, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase2BasicP2P.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The connection to send the test messages on. */ + private Connection connection; + + /** The controlSession to send the test messages on. */ + private Session session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * Should provide the name of the test case that this class implements. The exact names are defined in the + * interop testing spec. + * + * @return The name of the test case that this implements. + */ + public String getName() + { + log.debug("public String getName(): called"); + + return "TC2_BasicP2P"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return <tt>true</tt> to accept the invitation, <tt>false</tt> to reject it. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // All invites are acceptable. + return true; + } + + /** + * Assigns the role to be played by this test case. The test parameters are fully specified in the + * assignment message. When this method return the test case will be ready to execute. + * + * @param role The role to be played; sender or receivers. + * + * @param assignRoleMessage The role assingment message, contains the full test parameters. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void assignRole(Roles role, Message assignRoleMessage) throws JMSException + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Create a new connection to pass the test messages on. + connection = TestUtils.createConnection(TestClient.testContextProperties); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); + Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); + + log.debug("numMessages = " + numMessages); + log.debug("sendDestination = " + sendDestination); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a message producer if so. + case SENDER: + producer = session.createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages. + case RECEIVER: + MessageConsumer consumer = session.createConsumer(sendDestination); + consumer.setMessageListener(this); + break; + } + + connection.start(); + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = session.createTextMessage("test"); + + for (int i = 0; i < this.numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession to create the report message in. + * + * @return The report message. + * + * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. + */ + public Message getReport(Session session) throws JMSException + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connection. + connection.close(); + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java index 622ddc2f64..205472716b 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java @@ -1,239 +1,239 @@ -/*
- *
- * 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.interop.clienttestcases;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.test.framework.TestUtils;
-import org.apache.qpid.test.framework.distributedtesting.TestClient;
-import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest;
-
-import javax.jms.*;
-
-/**
- * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the
- * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of
- * messages sent/received.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Supply the name of the test case that this implements.
- * <tr><td> Accept/Reject invites based on test parameters.
- * <tr><td> Adapt to assigned roles.
- * <tr><td> Send required number of test messages using pub/sub.
- * <tr><td> Generate test reports.
- * </table>
- */
-public class TestCase3BasicPubSub implements TestClientControlledTest, MessageListener
-{
- /** Used for debugging. */
- private static final Logger log = Logger.getLogger(TestCase3BasicPubSub.class);
-
- /** Holds the count of test messages received. */
- private int messageCount;
-
- /** The role to be played by the test. */
- private Roles role;
-
- /** The number of test messages to send. */
- private int numMessages;
-
- /** The connections to send/receive the test messages on. */
- private Connection[] connection;
-
- /** The sessions to send/receive the test messages on. */
- private Session[] session;
-
- /** The producer to send the test messages with. */
- MessageProducer producer;
-
- /**
- * Should provide the name of the test case that this class implements. The exact names are defined in the
- * interop testing spec.
- *
- * @return The name of the test case that this implements.
- */
- public String getName()
- {
- log.debug("public String getName(): called");
-
- return "TC3_BasicPubSub";
- }
-
- /**
- * Determines whether the test invite that matched this test case is acceptable.
- *
- * @param inviteMessage The invitation to accept or reject.
- *
- * @return <tt>true</tt> to accept the invitation, <tt>false</tt> to reject it.
- *
- * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through.
- */
- public boolean acceptInvite(Message inviteMessage) throws JMSException
- {
- log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called");
-
- // All invites are acceptable.
- return true;
- }
-
- /**
- * Assigns the role to be played by this test case. The test parameters are fully specified in the
- * assignment message. When this method return the test case will be ready to execute.
- *
- * @param role The role to be played; sender or receivers.
- *
- * @param assignRoleMessage The role assingment message, contains the full test parameters.
- *
- * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
- */
- public void assignRole(Roles role, Message assignRoleMessage) throws JMSException
- {
- log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage
- + "): called");
-
- // Reset the message count for a new test.
- messageCount = 0;
-
- // Take note of the role to be played.
- this.role = role;
-
- // Extract and retain the test parameters.
- numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES");
- int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS");
- String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY");
-
- log.debug("numMessages = " + numMessages);
- log.debug("numReceivers = " + numReceivers);
- log.debug("sendKey = " + sendKey);
- log.debug("role = " + role);
-
- switch (role)
- {
- // Check if the sender role is being assigned, and set up a single message producer if so.
- case SENDER:
- // Create a new connection to pass the test messages on.
- connection = new Connection[1];
- session = new Session[1];
-
- connection[0] = TestUtils.createConnection(TestClient.testContextProperties);
- session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE);
-
- // Extract and retain the test parameters.
- Destination sendDestination = session[0].createTopic(sendKey);
-
- producer = session[0].createProducer(sendDestination);
- break;
-
- // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number
- // of receivers connections.
- case RECEIVER:
- // Create the required number of receivers connections.
- connection = new Connection[numReceivers];
- session = new Session[numReceivers];
-
- for (int i = 0; i < numReceivers; i++)
- {
- connection[i] = TestUtils.createConnection(TestClient.testContextProperties);
- session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE);
-
- sendDestination = session[i].createTopic(sendKey);
-
- MessageConsumer consumer = session[i].createConsumer(sendDestination);
- consumer.setMessageListener(this);
- }
-
- break;
- }
-
- // Start all the connection dispatcher threads running.
- for (Connection conn : connection)
- {
- conn.start();
- }
- }
-
- /**
- * Performs the test case actions. Returning from here, indicates that the sending role has completed its test.
- *
- * @param numMessages The number of test messages to send.
- *
- * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
- */
- public void start(int numMessages) throws JMSException
- {
- log.debug("public void start(): called");
-
- // Check that the sender role is being performed.
- if (role.equals(Roles.SENDER))
- {
- Message testMessage = session[0].createTextMessage("test");
-
- for (int i = 0; i < this.numMessages; i++)
- {
- producer.send(testMessage);
-
- // Increment the message count.
- messageCount++;
- }
- }
- }
-
- /**
- * Gets a report on the actions performed by the test case in its assigned role.
- *
- * @param session The controlSession to create the report message in.
- *
- * @return The report message.
- *
- * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through.
- */
- public Message getReport(Session session) throws JMSException
- {
- log.debug("public Message getReport(Session controlSession): called");
-
- // Close the test connections.
- for (Connection conn : connection)
- {
- conn.close();
- }
-
- // Generate a report message containing the count of the number of messages passed.
- Message report = session.createMessage();
- report.setStringProperty("CONTROL_TYPE", "REPORT");
- report.setIntProperty("MESSAGE_COUNT", messageCount);
-
- return report;
- }
-
- /**
- * Counts incoming test messages.
- *
- * @param message The incoming test message.
- */
- public void onMessage(Message message)
- {
- log.debug("public void onMessage(Message message = " + message + "): called");
-
- // Increment the message count.
- messageCount++;
- }
-}
+/* + * + * 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.interop.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.*; + +/** + * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the + * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of + * messages sent/received. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Supply the name of the test case that this implements. + * <tr><td> Accept/Reject invites based on test parameters. + * <tr><td> Adapt to assigned roles. + * <tr><td> Send required number of test messages using pub/sub. + * <tr><td> Generate test reports. + * </table> + */ +public class TestCase3BasicPubSub implements TestClientControlledTest, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase3BasicPubSub.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The connections to send/receive the test messages on. */ + private Connection[] connection; + + /** The sessions to send/receive the test messages on. */ + private Session[] session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * Should provide the name of the test case that this class implements. The exact names are defined in the + * interop testing spec. + * + * @return The name of the test case that this implements. + */ + public String getName() + { + log.debug("public String getName(): called"); + + return "TC3_BasicPubSub"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return <tt>true</tt> to accept the invitation, <tt>false</tt> to reject it. + * + * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // All invites are acceptable. + return true; + } + + /** + * Assigns the role to be played by this test case. The test parameters are fully specified in the + * assignment message. When this method return the test case will be ready to execute. + * + * @param role The role to be played; sender or receivers. + * + * @param assignRoleMessage The role assingment message, contains the full test parameters. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void assignRole(Roles role, Message assignRoleMessage) throws JMSException + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); + int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); + String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); + + log.debug("numMessages = " + numMessages); + log.debug("numReceivers = " + numReceivers); + log.debug("sendKey = " + sendKey); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + // Create a new connection to pass the test messages on. + connection = new Connection[1]; + session = new Session[1]; + + connection[0] = TestUtils.createConnection(TestClient.testContextProperties); + session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + Destination sendDestination = session[0].createTopic(sendKey); + + producer = session[0].createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number + // of receivers connections. + case RECEIVER: + // Create the required number of receivers connections. + connection = new Connection[numReceivers]; + session = new Session[numReceivers]; + + for (int i = 0; i < numReceivers; i++) + { + connection[i] = TestUtils.createConnection(TestClient.testContextProperties); + session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); + + sendDestination = session[i].createTopic(sendKey); + + MessageConsumer consumer = session[i].createConsumer(sendDestination); + consumer.setMessageListener(this); + } + + break; + } + + // Start all the connection dispatcher threads running. + for (Connection conn : connection) + { + conn.start(); + } + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = session[0].createTextMessage("test"); + + for (int i = 0; i < this.numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession to create the report message in. + * + * @return The report message. + * + * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. + */ + public Message getReport(Session session) throws JMSException + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connections. + for (Connection conn : connection) + { + conn.close(); + } + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java index 0388c56678..3730233264 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java @@ -1,214 +1,214 @@ -/*
- *
- * 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.interop.clienttestcases;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.test.framework.TestUtils;
-import org.apache.qpid.test.framework.distributedtesting.TestClient;
-import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest;
-
-import javax.jms.*;
-
-/**
- * Implements test case 4, P2P messages with message size. Sends/received a specified number of messages to a specified
- * route on the default direct exchange, of a specified size. Produces reports on the actual number of messages
- * sent/received.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Supply the name of the test case that this implements.
- * <tr><td> Accept/Reject invites based on test parameters.
- * <tr><td> Adapt to assigned roles.
- * <tr><td> Send required number of test messages.
- * <tr><td> Generate test reports.
- * </table>
- */
-public class TestCase4P2PMessageSize implements TestClientControlledTest, MessageListener
-{
- /** Used for debugging. */
- private static final Logger log = Logger.getLogger(TestCase4P2PMessageSize.class);
-
- /** Holds the count of test messages received. */
- private int messageCount;
-
- /** The role to be played by the test. */
- private Roles role;
-
- /** The number of test messages to send. */
- private int numMessages;
-
- /** The size of the test messages to send. */
- private int messageSize;
-
- /** The connection to send the test messages on. */
- private Connection connection;
-
- /** The controlSession to send the test messages on. */
- private Session session;
-
- /** The producer to send the test messages with. */
- MessageProducer producer;
-
- /**
- * Should provide the name of the test case that this class implements. The exact names are defined in the
- * interop testing spec.
- *
- * @return The name of the test case that this implements.
- */
- public String getName()
- {
- log.debug("public String getName(): called");
-
- return "TC4_P2PMessageSize";
- }
-
- /**
- * Determines whether the test invite that matched this test case is acceptable.
- *
- * @param inviteMessage The invitation to accept or reject.
- *
- * @return <tt>true</tt> to accept the invitation, <tt>false</tt> to reject it.
- *
- * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
- */
- public boolean acceptInvite(Message inviteMessage) throws JMSException
- {
- log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called");
-
- // All invites are acceptable.
- return true;
- }
-
- /**
- * Assigns the role to be played by this test case. The test parameters are fully specified in the
- * assignment message. When this method return the test case will be ready to execute.
- *
- * @param role The role to be played; sender or receivers.
- *
- * @param assignRoleMessage The role assingment message, contains the full test parameters.
- *
- * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
- */
- public void assignRole(Roles role, Message assignRoleMessage) throws JMSException
- {
- log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage
- + "): called");
-
- // Reset the message count for a new test.
- messageCount = 0;
-
- // Take note of the role to be played.
- this.role = role;
-
- // Create a new connection to pass the test messages on.
- connection = TestUtils.createConnection(TestClient.testContextProperties);
- session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
-
- // Extract and retain the test parameters.
- numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES");
- messageSize = assignRoleMessage.getIntProperty("messageSize");
- Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME"));
-
- log.debug("numMessages = " + numMessages);
- log.debug("sendDestination = " + sendDestination);
- log.debug("role = " + role);
-
- switch (role)
- {
- // Check if the sender role is being assigned, and set up a message producer if so.
- case SENDER:
- producer = session.createProducer(sendDestination);
- break;
-
- // Otherwise the receivers role is being assigned, so set this up to listen for messages.
- case RECEIVER:
- MessageConsumer consumer = session.createConsumer(sendDestination);
- consumer.setMessageListener(this);
- break;
- }
-
- connection.start();
- }
-
- /**
- * Performs the test case actions. Returning from here, indicates that the sending role has completed its test.
- *
- * @param numMessages The number of test messages to send.
- *
- * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
- */
- public void start(int numMessages) throws JMSException
- {
- log.debug("public void start(): called");
-
- // Check that the sender role is being performed.
- if (role.equals(Roles.SENDER))
- {
- Message testMessage = TestUtils.createTestMessageOfSize(session, messageSize);
-
- for (int i = 0; i < this.numMessages; i++)
- {
- producer.send(testMessage);
-
- // Increment the message count.
- messageCount++;
- }
- }
- }
-
- /**
- * Gets a report on the actions performed by the test case in its assigned role.
- *
- * @param session The controlSession to create the report message in.
- *
- * @return The report message.
- *
- * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through.
- */
- public Message getReport(Session session) throws JMSException
- {
- log.debug("public Message getReport(Session controlSession): called");
-
- // Close the test connection.
- connection.close();
-
- // Generate a report message containing the count of the number of messages passed.
- Message report = session.createMessage();
- report.setStringProperty("CONTROL_TYPE", "REPORT");
- report.setIntProperty("MESSAGE_COUNT", messageCount);
-
- return report;
- }
-
- /**
- * Counts incoming test messages.
- *
- * @param message The incoming test message.
- */
- public void onMessage(Message message)
- {
- log.debug("public void onMessage(Message message = " + message + "): called");
-
- // Increment the message count.
- messageCount++;
- }
-}
+/* + * + * 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.interop.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.*; + +/** + * Implements test case 4, P2P messages with message size. Sends/received a specified number of messages to a specified + * route on the default direct exchange, of a specified size. Produces reports on the actual number of messages + * sent/received. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Supply the name of the test case that this implements. + * <tr><td> Accept/Reject invites based on test parameters. + * <tr><td> Adapt to assigned roles. + * <tr><td> Send required number of test messages. + * <tr><td> Generate test reports. + * </table> + */ +public class TestCase4P2PMessageSize implements TestClientControlledTest, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase4P2PMessageSize.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The size of the test messages to send. */ + private int messageSize; + + /** The connection to send the test messages on. */ + private Connection connection; + + /** The controlSession to send the test messages on. */ + private Session session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * Should provide the name of the test case that this class implements. The exact names are defined in the + * interop testing spec. + * + * @return The name of the test case that this implements. + */ + public String getName() + { + log.debug("public String getName(): called"); + + return "TC4_P2PMessageSize"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return <tt>true</tt> to accept the invitation, <tt>false</tt> to reject it. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // All invites are acceptable. + return true; + } + + /** + * Assigns the role to be played by this test case. The test parameters are fully specified in the + * assignment message. When this method return the test case will be ready to execute. + * + * @param role The role to be played; sender or receivers. + * + * @param assignRoleMessage The role assingment message, contains the full test parameters. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void assignRole(Roles role, Message assignRoleMessage) throws JMSException + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Create a new connection to pass the test messages on. + connection = TestUtils.createConnection(TestClient.testContextProperties); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); + messageSize = assignRoleMessage.getIntProperty("messageSize"); + Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); + + log.debug("numMessages = " + numMessages); + log.debug("sendDestination = " + sendDestination); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a message producer if so. + case SENDER: + producer = session.createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages. + case RECEIVER: + MessageConsumer consumer = session.createConsumer(sendDestination); + consumer.setMessageListener(this); + break; + } + + connection.start(); + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = TestUtils.createTestMessageOfSize(session, messageSize); + + for (int i = 0; i < this.numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession to create the report message in. + * + * @return The report message. + * + * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. + */ + public Message getReport(Session session) throws JMSException + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connection. + connection.close(); + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java index ab59e16ab3..f601712bc9 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java @@ -1,243 +1,243 @@ -/*
- *
- * 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.interop.clienttestcases;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.test.framework.TestUtils;
-import org.apache.qpid.test.framework.distributedtesting.TestClient;
-import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest;
-
-import javax.jms.*;
-
-/**
- * Implements test case 5, pub/sub with message size. Sends/received a specified number of messages to a specified route
- * on the default topic exchange, using the specified number of receivers connections, and the specified message size.
- * Produces reports on the actual number of messages sent/received.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Supply the name of the test case that this implements.
- * <tr><td> Accept/Reject invites based on test parameters.
- * <tr><td> Adapt to assigned roles.
- * <tr><td> Send required number of test messages using pub/sub.
- * <tr><td> Generate test reports.
- * </table>
- */
-public class TestCase5PubSubMessageSize implements TestClientControlledTest, MessageListener
-{
- /** Used for debugging. */
- private static final Logger log = Logger.getLogger(TestCase5PubSubMessageSize.class);
-
- /** Holds the count of test messages received. */
- private int messageCount;
-
- /** The role to be played by the test. */
- private Roles role;
-
- /** The number of test messages to send. */
- private int numMessages;
-
- /** The size of the test messages to send. */
- private int messageSize;
-
- /** The connections to send/receive the test messages on. */
- private Connection[] connection;
-
- /** The sessions to send/receive the test messages on. */
- private Session[] session;
-
- /** The producer to send the test messages with. */
- MessageProducer producer;
-
- /**
- * Should provide the name of the test case that this class implements. The exact names are defined in the
- * interop testing spec.
- *
- * @return The name of the test case that this implements.
- */
- public String getName()
- {
- log.debug("public String getName(): called");
-
- return "TC5_PubSubMessageSize";
- }
-
- /**
- * Determines whether the test invite that matched this test case is acceptable.
- *
- * @param inviteMessage The invitation to accept or reject.
- *
- * @return <tt>true</tt> to accept the invitation, <tt>false</tt> to reject it.
- *
- * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through.
- */
- public boolean acceptInvite(Message inviteMessage) throws JMSException
- {
- log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called");
-
- // All invites are acceptable.
- return true;
- }
-
- /**
- * Assigns the role to be played by this test case. The test parameters are fully specified in the
- * assignment message. When this method return the test case will be ready to execute.
- *
- * @param role The role to be played; sender or receivers.
- *
- * @param assignRoleMessage The role assingment message, contains the full test parameters.
- *
- * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through.
- */
- public void assignRole(Roles role, Message assignRoleMessage) throws JMSException
- {
- log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage
- + "): called");
-
- // Reset the message count for a new test.
- messageCount = 0;
-
- // Take note of the role to be played.
- this.role = role;
-
- // Extract and retain the test parameters.
- numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES");
- messageSize = assignRoleMessage.getIntProperty("messageSize");
- int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS");
- String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY");
-
- log.debug("numMessages = " + numMessages);
- log.debug("numReceivers = " + numReceivers);
- log.debug("sendKey = " + sendKey);
- log.debug("role = " + role);
-
- switch (role)
- {
- // Check if the sender role is being assigned, and set up a single message producer if so.
- case SENDER:
- // Create a new connection to pass the test messages on.
- connection = new Connection[1];
- session = new Session[1];
-
- connection[0] = TestUtils.createConnection(TestClient.testContextProperties);
- session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE);
-
- // Extract and retain the test parameters.
- Destination sendDestination = session[0].createTopic(sendKey);
-
- producer = session[0].createProducer(sendDestination);
- break;
-
- // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number
- // of receivers connections.
- case RECEIVER:
- // Create the required number of receivers connections.
- connection = new Connection[numReceivers];
- session = new Session[numReceivers];
-
- for (int i = 0; i < numReceivers; i++)
- {
- connection[i] = TestUtils.createConnection(TestClient.testContextProperties);
- session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE);
-
- sendDestination = session[i].createTopic(sendKey);
-
- MessageConsumer consumer = session[i].createConsumer(sendDestination);
- consumer.setMessageListener(this);
- }
-
- break;
- }
-
- // Start all the connection dispatcher threads running.
- for (Connection conn : connection)
- {
- conn.start();
- }
- }
-
- /**
- * Performs the test case actions. Returning from here, indicates that the sending role has completed its test.
- *
- * @param numMessages The number of test messages to send.
- *
- * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
- */
- public void start(int numMessages) throws JMSException
- {
- log.debug("public void start(): called");
-
- // Check that the sender role is being performed.
- if (role.equals(Roles.SENDER))
- {
- Message testMessage = TestUtils.createTestMessageOfSize(session[0], messageSize);
-
- for (int i = 0; i < this.numMessages; i++)
- {
- producer.send(testMessage);
-
- // Increment the message count.
- messageCount++;
- }
- }
- }
-
- /**
- * Gets a report on the actions performed by the test case in its assigned role.
- *
- * @param session The controlSession to create the report message in.
- *
- * @return The report message.
- *
- * @throws javax.jms.JMSException Any JMSExceptions resulting from creating the report are allowed to fall through.
- */
- public Message getReport(Session session) throws JMSException
- {
- log.debug("public Message getReport(Session controlSession): called");
-
- // Close the test connections.
- for (Connection conn : connection)
- {
- conn.close();
- }
-
- // Generate a report message containing the count of the number of messages passed.
- Message report = session.createMessage();
- report.setStringProperty("CONTROL_TYPE", "REPORT");
- report.setIntProperty("MESSAGE_COUNT", messageCount);
-
- return report;
- }
-
- /**
- * Counts incoming test messages.
- *
- * @param message The incoming test message.
- */
- public void onMessage(Message message)
- {
- log.debug("public void onMessage(Message message = " + message + "): called");
-
- // Increment the message count.
- messageCount++;
- }
-}
+/* + * + * 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.interop.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.*; + +/** + * Implements test case 5, pub/sub with message size. Sends/received a specified number of messages to a specified route + * on the default topic exchange, using the specified number of receivers connections, and the specified message size. + * Produces reports on the actual number of messages sent/received. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Supply the name of the test case that this implements. + * <tr><td> Accept/Reject invites based on test parameters. + * <tr><td> Adapt to assigned roles. + * <tr><td> Send required number of test messages using pub/sub. + * <tr><td> Generate test reports. + * </table> + */ +public class TestCase5PubSubMessageSize implements TestClientControlledTest, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase5PubSubMessageSize.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The size of the test messages to send. */ + private int messageSize; + + /** The connections to send/receive the test messages on. */ + private Connection[] connection; + + /** The sessions to send/receive the test messages on. */ + private Session[] session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * Should provide the name of the test case that this class implements. The exact names are defined in the + * interop testing spec. + * + * @return The name of the test case that this implements. + */ + public String getName() + { + log.debug("public String getName(): called"); + + return "TC5_PubSubMessageSize"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return <tt>true</tt> to accept the invitation, <tt>false</tt> to reject it. + * + * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // All invites are acceptable. + return true; + } + + /** + * Assigns the role to be played by this test case. The test parameters are fully specified in the + * assignment message. When this method return the test case will be ready to execute. + * + * @param role The role to be played; sender or receivers. + * + * @param assignRoleMessage The role assingment message, contains the full test parameters. + * + * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void assignRole(Roles role, Message assignRoleMessage) throws JMSException + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); + messageSize = assignRoleMessage.getIntProperty("messageSize"); + int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); + String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); + + log.debug("numMessages = " + numMessages); + log.debug("numReceivers = " + numReceivers); + log.debug("sendKey = " + sendKey); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + // Create a new connection to pass the test messages on. + connection = new Connection[1]; + session = new Session[1]; + + connection[0] = TestUtils.createConnection(TestClient.testContextProperties); + session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + Destination sendDestination = session[0].createTopic(sendKey); + + producer = session[0].createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number + // of receivers connections. + case RECEIVER: + // Create the required number of receivers connections. + connection = new Connection[numReceivers]; + session = new Session[numReceivers]; + + for (int i = 0; i < numReceivers; i++) + { + connection[i] = TestUtils.createConnection(TestClient.testContextProperties); + session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); + + sendDestination = session[i].createTopic(sendKey); + + MessageConsumer consumer = session[i].createConsumer(sendDestination); + consumer.setMessageListener(this); + } + + break; + } + + // Start all the connection dispatcher threads running. + for (Connection conn : connection) + { + conn.start(); + } + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = TestUtils.createTestMessageOfSize(session[0], messageSize); + + for (int i = 0; i < this.numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession to create the report message in. + * + * @return The report message. + * + * @throws javax.jms.JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. + */ + public Message getReport(Session session) throws JMSException + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connections. + for (Connection conn : connection) + { + conn.close(); + } + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java index 60cd9f47f3..a2e4a00aa6 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java @@ -1,84 +1,84 @@ -/*
- *
- * 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.interop.testcases;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.test.framework.FrameworkBaseCase;
-
-import java.util.Properties;
-
-/**
- * Coordinates test case 1, from the interop test specification. This test connects up the sender and receivers roles,
- * and gets some dummy test reports from them, in order to check that the test framework itself is operational.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Exercises the interop testing framework without actually sending any test messages.
- * <td> {@link FrameworkBaseCase}
- * </table>
- */
-public class InteropTestCase1DummyRun extends FrameworkBaseCase
-{
- /** Used for debugging. */
- private static final Logger log = Logger.getLogger(InteropTestCase1DummyRun.class);
-
- /**
- * Creates a new coordinating test case with the specified name.
- *
- * @param name The test case name.
- */
- public InteropTestCase1DummyRun(String name)
- {
- super(name);
- }
-
- /**
- * Performs the basic P2P test case, "Test Case 2" in the specification.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testDummyRun() throws Exception
- {
- log.debug("public void testDummyRun(): called");
-
- Properties testConfig = new Properties();
- testConfig.put("TEST_NAME", "TC1_DummyRun");
-
- /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig);
-
- // Compare sender and receivers reports.
- // Assert.assertEquals("Expected to get 2 dummy reports.", 2, reports.length);
- }
-
- /**
- * Should provide a translation from the junit method name of a test to its test case name as defined in the
- * interop testing specification. For example the method "testP2P" might map onto the interop test case name
- * "TC2_BasicP2P".
- *
- * @param methodName The name of the JUnit test method.
- * @return The name of the corresponding interop test case.
- */
- public String getTestCaseNameForTestMethod(String methodName)
- {
- return "TC1_DummyRun";
- }
-}
+/* + * + * 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.interop.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + * Coordinates test case 1, from the interop test specification. This test connects up the sender and receivers roles, + * and gets some dummy test reports from them, in order to check that the test framework itself is operational. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Exercises the interop testing framework without actually sending any test messages. + * <td> {@link FrameworkBaseCase} + * </table> + */ +public class InteropTestCase1DummyRun extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase1DummyRun.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase1DummyRun(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testDummyRun() throws Exception + { + log.debug("public void testDummyRun(): called"); + + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "TC1_DummyRun"); + + /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + // Assert.assertEquals("Expected to get 2 dummy reports.", 2, reports.length); + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC1_DummyRun"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java index 8983249daa..6d6515f1fd 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java @@ -1,90 +1,90 @@ -/*
- *
- * 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.interop.testcases;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.test.framework.FrameworkBaseCase;
-
-import java.util.Properties;
-
-/**
- * Implements test case 2, from the interop test specification. This test sets up the TC2_BasicP2P test for 50
- * messages. It checks that the sender and receivers reports both indicate that all the test messages were transmitted
- * successfully.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Setup p2p test parameters and compare with test output. <td> {@link FrameworkBaseCase}
- * </table>
- */
-public class InteropTestCase2BasicP2P extends FrameworkBaseCase
-{
- /** Used for debugging. */
- private static final Logger log = Logger.getLogger(InteropTestCase2BasicP2P.class);
-
- /**
- * Creates a new coordinating test case with the specified name.
- *
- * @param name The test case name.
- */
- public InteropTestCase2BasicP2P(String name)
- {
- super(name);
- }
-
- /**
- * Performs the basic P2P test case, "Test Case 2" in the specification.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testBasicP2P() throws Exception
- {
- log.debug("public void testBasicP2P(): called");
-
- Properties testConfig = new Properties();
- testConfig.setProperty("TEST_NAME", "TC2_BasicP2P");
- testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue");
- testConfig.put("P2P_NUM_MESSAGES", 50);
-
- /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig);
-
- // Compare sender and receivers reports.
- /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT");
- int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT");
-
- Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent);
- Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived);*/
- }
-
- /**
- * Should provide a translation from the junit method name of a test to its test case name as defined in the
- * interop testing specification. For example the method "testP2P" might map onto the interop test case name
- * "TC2_BasicP2P".
- *
- * @param methodName The name of the JUnit test method.
- * @return The name of the corresponding interop test case.
- */
- public String getTestCaseNameForTestMethod(String methodName)
- {
- return "TC2_BasicP2P";
- }
-}
+/* + * + * 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.interop.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + * Implements test case 2, from the interop test specification. This test sets up the TC2_BasicP2P test for 50 + * messages. It checks that the sender and receivers reports both indicate that all the test messages were transmitted + * successfully. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Setup p2p test parameters and compare with test output. <td> {@link FrameworkBaseCase} + * </table> + */ +public class InteropTestCase2BasicP2P extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase2BasicP2P.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase2BasicP2P(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testBasicP2P() throws Exception + { + log.debug("public void testBasicP2P(): called"); + + Properties testConfig = new Properties(); + testConfig.setProperty("TEST_NAME", "TC2_BasicP2P"); + testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); + testConfig.put("P2P_NUM_MESSAGES", 50); + + /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); + Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived);*/ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC2_BasicP2P"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java index 6e87c3e3ee..2faca91e73 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java @@ -1,88 +1,88 @@ -/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.interop.testcases;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.test.framework.FrameworkBaseCase;
-
-import java.util.Properties;
-
-/**
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Setup pub/sub test parameters and compare with test output. <td> {@link FrameworkBaseCase}
- * </table>
- */
-public class InteropTestCase3BasicPubSub extends FrameworkBaseCase
-{
- /** Used for debugging. */
- private static final Logger log = Logger.getLogger(InteropTestCase3BasicPubSub.class);
-
- /**
- * Creates a new coordinating test case with the specified name.
- *
- * @param name The test case name.
- */
- public InteropTestCase3BasicPubSub(String name)
- {
- super(name);
- }
-
- /**
- * Performs the basic P2P test case, "Test Case 2" in the specification.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testBasicPubSub() throws Exception
- {
- log.debug("public void testBasicPubSub(): called");
-
- Properties testConfig = new Properties();
- testConfig.put("TEST_NAME", "TC3_BasicPubSub");
- testConfig.put("PUBSUB_KEY", "tc3route");
- testConfig.put("PUBSUB_NUM_MESSAGES", 10);
- testConfig.put("PUBSUB_NUM_RECEIVERS", 5);
-
- /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig);
-
- // Compare sender and receivers reports.
- /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT");
- int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT");
-
- Assert.assertEquals("The requested number of messages were not sent.", 10, messagesSent);
- Assert.assertEquals("Received messages did not match up to num sent * num receivers.", messagesSent * 5,
- messagesReceived);*/
- }
-
- /**
- * Should provide a translation from the junit method name of a test to its test case name as defined in the
- * interop testing specification. For example the method "testP2P" might map onto the interop test case name
- * "TC2_BasicP2P".
- *
- * @param methodName The name of the JUnit test method.
- * @return The name of the corresponding interop test case.
- */
- public String getTestCaseNameForTestMethod(String methodName)
- {
- return "TC3_BasicPubSub";
- }
-}
+/* + * + * 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.interop.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Setup pub/sub test parameters and compare with test output. <td> {@link FrameworkBaseCase} + * </table> + */ +public class InteropTestCase3BasicPubSub extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase3BasicPubSub.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase3BasicPubSub(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testBasicPubSub() throws Exception + { + log.debug("public void testBasicPubSub(): called"); + + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "TC3_BasicPubSub"); + testConfig.put("PUBSUB_KEY", "tc3route"); + testConfig.put("PUBSUB_NUM_MESSAGES", 10); + testConfig.put("PUBSUB_NUM_RECEIVERS", 5); + + /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 10, messagesSent); + Assert.assertEquals("Received messages did not match up to num sent * num receivers.", messagesSent * 5, + messagesReceived);*/ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC3_BasicPubSub"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java index c3f450ec42..2d64f2b805 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java @@ -1,193 +1,193 @@ -/*
- *
- * 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.interop.testcases;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.test.framework.FrameworkBaseCase;
-
-import java.util.Properties;
-
-/**
- * Implements test case 4, from the interop test specification. This test sets up the TC2_P2PMessageSize test for 50
- * messages, and a variety of message sizes. It checks that the sender and receivers reports both indicate that all
- * the test messages were transmitted successfully.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Setup p2p test parameters and compare with test output. <td> {@link FrameworkBaseCase}
- * </table>
- */
-public class InteropTestCase4P2PMessageSize extends FrameworkBaseCase
-{
- /** Used for debugging. */
- private static final Logger log = Logger.getLogger(InteropTestCase4P2PMessageSize.class);
-
- /**
- * Creates a new coordinating test case with the specified name.
- *
- * @param name The test case name.
- */
- public InteropTestCase4P2PMessageSize(String name)
- {
- super(name);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 0K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testP2PMessageSize0K() throws Exception
- {
- runTestForMessagesOfSize(0);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 63K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testP2PMessageSize63K() throws Exception
- {
- runTestForMessagesOfSize(63 * 1024);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 64K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testP2PMessageSize64K() throws Exception
- {
- runTestForMessagesOfSize(64 * 1024);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 65K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testP2PMessageSize65K() throws Exception
- {
- runTestForMessagesOfSize(65 * 1024);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 127K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testP2PMessageSize127K() throws Exception
- {
- runTestForMessagesOfSize(127 * 1024);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 128K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testP2PMessageSize128K() throws Exception
- {
- runTestForMessagesOfSize(128 * 1024);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 129K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testP2PMessageSize129K() throws Exception
- {
- runTestForMessagesOfSize(129 * 1024);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 255K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testP2PMessageSize255K() throws Exception
- {
- runTestForMessagesOfSize(255 * 1024);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 256K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testP2PMessageSize256K() throws Exception
- {
- runTestForMessagesOfSize(256 * 1024);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 257K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testP2PMessageSize257K() throws Exception
- {
- runTestForMessagesOfSize(257 * 1024);
- }
-
- /**
- * Sends 50 test messages of the specified size, and asserts that all were received.
- *
- * @param size The size of the messages to send in bytes.
- */
- private void runTestForMessagesOfSize(int size)
- {
- Properties testConfig = new Properties();
- testConfig.setProperty("TEST_NAME", "TC4_P2PMessageSize");
- testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue");
- testConfig.put("P2P_NUM_MESSAGES", 50);
- testConfig.put("messageSize", size);
-
- /*Message[] reports =*/
- getCircuitFactory().sequenceTest(null, null, testConfig);
-
- // Compare sender and receivers reports.
- /*
- int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT");
- int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT");
-
- Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent);
- Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived);
- */
- }
-
- /**
- * Should provide a translation from the junit method name of a test to its test case name as defined in the
- * interop testing specification. For example the method "testP2P" might map onto the interop test case name
- * "TC2_BasicP2P".
- *
- * @param methodName The name of the JUnit test method.
- *
- * @return The name of the corresponding interop test case.
- */
- public String getTestCaseNameForTestMethod(String methodName)
- {
- return "TC4_P2PMessageSize";
- }
-}
+/* + * + * 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.interop.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + * Implements test case 4, from the interop test specification. This test sets up the TC2_P2PMessageSize test for 50 + * messages, and a variety of message sizes. It checks that the sender and receivers reports both indicate that all + * the test messages were transmitted successfully. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Setup p2p test parameters and compare with test output. <td> {@link FrameworkBaseCase} + * </table> + */ +public class InteropTestCase4P2PMessageSize extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase4P2PMessageSize.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase4P2PMessageSize(String name) + { + super(name); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 0K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize0K() throws Exception + { + runTestForMessagesOfSize(0); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 63K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize63K() throws Exception + { + runTestForMessagesOfSize(63 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 64K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize64K() throws Exception + { + runTestForMessagesOfSize(64 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 65K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize65K() throws Exception + { + runTestForMessagesOfSize(65 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 127K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize127K() throws Exception + { + runTestForMessagesOfSize(127 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 128K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize128K() throws Exception + { + runTestForMessagesOfSize(128 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 129K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize129K() throws Exception + { + runTestForMessagesOfSize(129 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 255K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize255K() throws Exception + { + runTestForMessagesOfSize(255 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 256K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize256K() throws Exception + { + runTestForMessagesOfSize(256 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 257K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize257K() throws Exception + { + runTestForMessagesOfSize(257 * 1024); + } + + /** + * Sends 50 test messages of the specified size, and asserts that all were received. + * + * @param size The size of the messages to send in bytes. + */ + private void runTestForMessagesOfSize(int size) + { + Properties testConfig = new Properties(); + testConfig.setProperty("TEST_NAME", "TC4_P2PMessageSize"); + testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); + testConfig.put("P2P_NUM_MESSAGES", 50); + testConfig.put("messageSize", size); + + /*Message[] reports =*/ + getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /* + int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); + Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived); + */ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC4_P2PMessageSize"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java index 86a0a60ea4..23d33fc115 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java @@ -1,193 +1,193 @@ -/*
- *
- * 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.interop.testcases;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.test.framework.FrameworkBaseCase;
-
-import java.util.Properties;
-
-/**
- * Implements test case 5, from the interop test specification. This test sets up the TC2_PubSubMessageSize test for 10
- * messages, sent to 5 consumers, and a variety of message sizes. It checks that the sender and receivers reports both
- * indicate that all the test messages were transmitted successfully.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Setup pub/sub test parameters and compare with test output. <td> {@link FrameworkBaseCase}
- * </table>
- */
-public class InteropTestCase5PubSubMessageSize extends FrameworkBaseCase
-{
- /** Used for debugging. */
- private static final Logger log = Logger.getLogger(InteropTestCase5PubSubMessageSize.class);
-
- /**
- * Creates a new coordinating test case with the specified name.
- *
- * @param name The test case name.
- */
- public InteropTestCase5PubSubMessageSize(String name)
- {
- super(name);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 0K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testPubSubMessageSize0K() throws Exception
- {
- runTestForMessagesOfSize(0);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 63K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testPubSubMessageSize63K() throws Exception
- {
- runTestForMessagesOfSize(63 * 1024);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 64K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testPubSubMessageSize64K() throws Exception
- {
- runTestForMessagesOfSize(64 * 1024);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 65K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testPubSubMessageSize65K() throws Exception
- {
- runTestForMessagesOfSize(65 * 1024);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 127K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testPubSubMessageSize127K() throws Exception
- {
- runTestForMessagesOfSize(127 * 1024);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 128K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testPubSubMessageSize128K() throws Exception
- {
- runTestForMessagesOfSize(128 * 1024);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 129K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testPubSubMessageSize129K() throws Exception
- {
- runTestForMessagesOfSize(129 * 1024);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 255K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testPubSubMessageSize255K() throws Exception
- {
- runTestForMessagesOfSize(255 * 1024);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 256K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testPubSubMessageSize256K() throws Exception
- {
- runTestForMessagesOfSize(256 * 1024);
- }
-
- /**
- * Performs the P2P message test case, "Test Case 4" in the specification, for messages 257K in size.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testPubSubMessageSize257K() throws Exception
- {
- runTestForMessagesOfSize(257 * 1024);
- }
-
- /**
- * Sends 50 test messages of the specified size, and asserts that all were received.
- *
- * @param size The size of the messages to send in bytes.
- */
- private void runTestForMessagesOfSize(int size)
- {
- Properties testConfig = new Properties();
- testConfig.put("TEST_NAME", "TC5_PubSubMessageSize");
- testConfig.put("PUBSUB_KEY", "tc3route");
- testConfig.put("PUBSUB_NUM_MESSAGES", 10);
- testConfig.put("PUBSUB_NUM_RECEIVERS", 5);
- testConfig.put("messageSize", size);
-
- /*Message[] reports =*/
- getCircuitFactory().sequenceTest(null, null, testConfig);
-
- // Compare sender and receivers reports.
- /*
- int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT");
- int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT");
-
- Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent);
- Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived);
- */
- }
-
- /**
- * Should provide a translation from the junit method name of a test to its test case name as defined in the
- * interop testing specification. For example the method "testP2P" might map onto the interop test case name
- * "TC2_BasicP2P".
- *
- * @param methodName The name of the JUnit test method.
- * @return The name of the corresponding interop test case.
- */
- public String getTestCaseNameForTestMethod(String methodName)
- {
- return "TC5_PubSubMessageSize";
- }
-}
+/* + * + * 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.interop.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + * Implements test case 5, from the interop test specification. This test sets up the TC2_PubSubMessageSize test for 10 + * messages, sent to 5 consumers, and a variety of message sizes. It checks that the sender and receivers reports both + * indicate that all the test messages were transmitted successfully. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Setup pub/sub test parameters and compare with test output. <td> {@link FrameworkBaseCase} + * </table> + */ +public class InteropTestCase5PubSubMessageSize extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase5PubSubMessageSize.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase5PubSubMessageSize(String name) + { + super(name); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 0K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize0K() throws Exception + { + runTestForMessagesOfSize(0); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 63K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize63K() throws Exception + { + runTestForMessagesOfSize(63 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 64K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize64K() throws Exception + { + runTestForMessagesOfSize(64 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 65K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize65K() throws Exception + { + runTestForMessagesOfSize(65 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 127K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize127K() throws Exception + { + runTestForMessagesOfSize(127 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 128K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize128K() throws Exception + { + runTestForMessagesOfSize(128 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 129K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize129K() throws Exception + { + runTestForMessagesOfSize(129 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 255K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize255K() throws Exception + { + runTestForMessagesOfSize(255 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 256K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize256K() throws Exception + { + runTestForMessagesOfSize(256 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 257K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize257K() throws Exception + { + runTestForMessagesOfSize(257 * 1024); + } + + /** + * Sends 50 test messages of the specified size, and asserts that all were received. + * + * @param size The size of the messages to send in bytes. + */ + private void runTestForMessagesOfSize(int size) + { + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "TC5_PubSubMessageSize"); + testConfig.put("PUBSUB_KEY", "tc3route"); + testConfig.put("PUBSUB_NUM_MESSAGES", 10); + testConfig.put("PUBSUB_NUM_RECEIVERS", 5); + testConfig.put("messageSize", size); + + /*Message[] reports =*/ + getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /* + int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); + Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived); + */ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC5_PubSubMessageSize"; + } +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java index 5ed502287d..63e2c75509 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java @@ -1,906 +1,906 @@ -/*
- *
- * 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.sustained;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.client.AMQNoConsumersException;
-import org.apache.qpid.client.AMQNoRouteException;
-import org.apache.qpid.test.framework.distributedtesting.TestClient;
-import org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub;
-import org.apache.qpid.test.framework.TestUtils;
-
-import javax.jms.Connection;
-import javax.jms.Destination;
-import javax.jms.ExceptionListener;
-import javax.jms.JMSException;
-import javax.jms.Message;
-import javax.jms.MessageConsumer;
-import javax.jms.MessageListener;
-import javax.jms.MessageProducer;
-import javax.jms.Session;
-import javax.jms.TextMessage;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.concurrent.CountDownLatch;
-
-/**
- * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the
- * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of
- * messages sent/received.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Supply the name of the test case that this implements.
- * <tr><td> Accept/Reject invites based on test parameters.
- * <tr><td> Adapt to assigned roles.
- * <tr><td> Send required number of test messages using pub/sub. <tr><td> Generate test reports.
- * </table>
- */
-public class SustainedClientTestCase extends TestCase3BasicPubSub implements ExceptionListener, MessageListener
-{
- /** Used for debugging. */
- private static final Logger log = Logger.getLogger(SustainedClientTestCase.class);
-
- /** Used to log to the console. */
- private static final Logger console = Logger.getLogger("SustainedTest");
-
- /** The role to be played by the test. */
- private Roles role;
-
- /** The number of receivers connection to use. */
- private int numReceivers;
-
- /** The routing key to send them to on the default direct exchange. */
- private Destination sendDestination;
-
- /** The routing key to send updates to on the default direct exchange. */
- private Destination sendUpdateDestination;
-
- /** The connections to send/receive the test messages on. */
- private Connection[] connection;
-
- /** The sessions to send/receive the test messages on. */
- private Session[] session;
-
- /** The producer to send the test messages with. */
- MessageProducer producer;
-
- /** Adapter that adjusts the send rate based on the updates from clients. */
- SustainedRateAdapter _rateAdapter;
-
- /** */
- int _batchSize;
-
- private static final long TEN_MILLI_SEC = 10000000;
- private static final int DEBUG_LOG_UPATE_INTERVAL = 10;
- private static final int LOG_UPATE_INTERVAL = 10;
- private static final boolean SLEEP_PER_MESSAGE = Boolean.getBoolean("sleepPerMessage");
-
- /**
- * Should provide the name of the test case that this class implements. The exact names are defined in the interop
- * testing spec.
- *
- * @return The name of the test case that this implements.
- */
- public String getName()
- {
- log.debug("public String getName(): called");
-
- return "Perf_SustainedPubSub";
- }
-
- /**
- * Assigns the role to be played by this test case. The test parameters are fully specified in the assignment
- * message. When this method return the test case will be ready to execute.
- *
- * @param role The role to be played; sender or receivers.
- * @param assignRoleMessage The role assingment message, contains the full test parameters.
- *
- * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
- */
- public void assignRole(Roles role, Message assignRoleMessage) throws JMSException
- {
- log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage
- + "): called");
-
- // Take note of the role to be played.
- this.role = role;
-
- // Extract and retain the test parameters.
- numReceivers = assignRoleMessage.getIntProperty("SUSTAINED_NUM_RECEIVERS");
- _batchSize = assignRoleMessage.getIntProperty("SUSTAINED_UPDATE_INTERVAL");
- String sendKey = assignRoleMessage.getStringProperty("SUSTAINED_KEY");
- String sendUpdateKey = assignRoleMessage.getStringProperty("SUSTAINED_UPDATE_KEY");
- int ackMode = assignRoleMessage.getIntProperty("ACKNOWLEDGE_MODE");
- String clientName = assignRoleMessage.getStringProperty("CLIENT_NAME");
-
- if (log.isDebugEnabled())
- {
- log.debug("numReceivers = " + numReceivers);
- log.debug("_batchSize = " + _batchSize);
- log.debug("ackMode = " + ackMode);
- log.debug("sendKey = " + sendKey);
- log.debug("sendUpdateKey = " + sendUpdateKey);
- log.debug("role = " + role);
- }
-
- switch (role)
- {
- // Check if the sender role is being assigned, and set up a single message producer if so.
- case SENDER:
- console.info("Creating Sender");
- // Create a new connection to pass the test messages on.
- connection = new Connection[1];
- session = new Session[1];
-
- connection[0] = TestUtils.createConnection(TestClient.testContextProperties);
- session[0] = connection[0].createSession(false, ackMode);
-
- // Extract and retain the test parameters.
- sendDestination = session[0].createTopic(sendKey);
-
- connection[0].setExceptionListener(this);
-
- producer = session[0].createProducer(sendDestination);
-
- sendUpdateDestination = session[0].createTopic(sendUpdateKey);
- MessageConsumer updateConsumer = session[0].createConsumer(sendUpdateDestination);
-
- _rateAdapter = new SustainedRateAdapter(this);
- updateConsumer.setMessageListener(_rateAdapter);
-
- break;
-
- // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number
- // of receivers connections.
- case RECEIVER:
- console.info("Creating Receiver");
- // Create the required number of receivers connections.
- connection = new Connection[numReceivers];
- session = new Session[numReceivers];
-
- for (int i = 0; i < numReceivers; i++)
- {
- connection[i] = TestUtils.createConnection(TestClient.testContextProperties);
- session[i] = connection[i].createSession(false, ackMode);
-
- sendDestination = session[i].createTopic(sendKey);
-
- sendUpdateDestination = session[i].createTopic(sendUpdateKey);
-
- MessageConsumer consumer = session[i].createConsumer(sendDestination);
-
- consumer.setMessageListener(new SustainedListener(clientName + "-" + i, _batchSize, session[i],
- sendUpdateDestination));
- }
-
- break;
- }
-
- // Start all the connection dispatcher threads running.
- for (int i = 0; i < connection.length; i++)
- {
- connection[i].start();
- }
- }
-
- /** Performs the test case actions.
- * @param numMessages*/
- public void start(int numMessages) throws JMSException
- {
- log.debug("public void start(): called");
-
- // Check that the sender role is being performed.
- switch (role)
- {
- // Check if the sender role is being assigned, and set up a single message producer if so.
- case SENDER:
- _rateAdapter.run();
- break;
- case RECEIVER:
-
- }
-
- // return from here when you have finished the test.. this will signal the controller and
- }
-
- public void terminate() throws JMSException, InterruptedException
- {
- if (_rateAdapter != null)
- {
- _rateAdapter.stop();
- }
- }
-
- /**
- * Gets a report on the actions performed by the test case in its assigned role.
- *
- * @param session The controlSession to create the report message in.
- *
- * @return The report message.
- *
- * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through.
- */
- public Message getReport(Session session) throws JMSException
- {
- log.debug("public Message getReport(Session controlSession): called");
-
- // Close the test connections.
- for (int i = 0; i < connection.length; i++)
- {
- connection[i].close();
- }
-
- Message report = session.createMessage();
- report.setStringProperty("CONTROL_TYPE", "REPORT");
-
- return report;
- }
-
- public void onException(JMSException jmsException)
- {
- Exception linked = jmsException.getLinkedException();
-
- if (linked != null)
- {
- if (log.isDebugEnabled())
- {
- log.debug("Linked Exception:" + linked);
- }
-
- if ((linked instanceof AMQNoRouteException) || (linked instanceof AMQNoConsumersException))
- {
- if (log.isDebugEnabled())
- {
- if (linked instanceof AMQNoConsumersException)
- {
- log.warn("No clients currently available for message:"
- + ((AMQNoConsumersException) linked).getUndeliveredMessage());
- }
- else
- {
- log.warn("No route for message");
- }
- }
-
- // Tell the rate adapter that there are no clients ready yet
- _rateAdapter.NO_CLIENTS = true;
- }
- }
- else
- {
- log.warn("Exception:" + linked);
- }
- }
-
- /**
- * Inner class that listens for messages and sends a report for the time taken between receiving the 'start' and
- * 'end' messages.
- */
- class SustainedListener implements MessageListener
- {
- /** Number of messages received */
- private long _received = 0;
- /** The number of messages in the batch */
- private int _batchSize = 0;
- /** Record of the when the 'start' messagse was sen */
- private Long _startTime;
- /** Message producer to use to send reports */
- MessageProducer _updater;
- /** Session to create the report message on */
- Session _session;
- /** Record of the client ID used for this SustainedListnener */
- String _client;
-
- /**
- * Main Constructor
- *
- * @param clientname The _client id used to identify this connection.
- * @param batchSize The number of messages that are to be sent per batch. Note: This is not used to
- * control the interval between sending reports.
- * @param session The controlSession used for communication.
- * @param sendDestination The destination that update reports should be sent to.
- *
- * @throws JMSException My occur if creatingthe Producer fails
- */
- public SustainedListener(String clientname, int batchSize, Session session, Destination sendDestination)
- throws JMSException
- {
- _batchSize = batchSize;
- _client = clientname;
- _session = session;
- _updater = session.createProducer(sendDestination);
- }
-
- public void onMessage(Message message)
- {
- if (log.isDebugEnabled())
- {
- log.debug("Message " + _received + "received in listener");
- }
-
- if (message instanceof TextMessage)
- {
- try
- {
- _received++;
- if (((TextMessage) message).getText().equals("start"))
- {
- log.debug("Starting Batch");
- _startTime = System.nanoTime();
- }
- else if (((TextMessage) message).getText().equals("end"))
- {
- if (_startTime != null)
- {
- long currentTime = System.nanoTime();
- sendStatus(currentTime - _startTime, _received, message.getIntProperty("BATCH"));
- log.debug("End Batch");
- }
- }
- }
- catch (JMSException e)
- {
- // ignore error
- }
- }
-
- }
-
- /**
- * sendStatus creates and sends the report back to the publisher
- *
- * @param time taken for the the last batch
- * @param received Total number of messages received.
- * @param batchNumber the batch number
- * @throws JMSException if an error occurs during the send
- */
- private void sendStatus(long time, long received, int batchNumber) throws JMSException
- {
- Message updateMessage = _session.createTextMessage("update");
- updateMessage.setStringProperty("CLIENT_ID", ":" + _client);
- updateMessage.setStringProperty("CONTROL_TYPE", "UPDATE");
- updateMessage.setLongProperty("RECEIVED", received);
- updateMessage.setIntProperty("BATCH", batchNumber);
- updateMessage.setLongProperty("DURATION", time);
-
- if (log.isInfoEnabled())
- {
- log.info("**** SENDING [" + batchNumber + "]**** " + "CLIENT_ID:" + _client + " RECEIVED:" + received
- + " BATCH:" + batchNumber + " DURATION:" + time);
- }
-
- // Output on the main console.info the details of this batch
- if ((batchNumber % 10) == 0)
- {
- console.info("Sending Report [" + batchNumber + "] " + "CLIENT_ID:" + _client + " RECEIVED:" + received
- + " BATCH:" + batchNumber + " DURATION:" + time);
- }
-
- _updater.send(updateMessage);
- }
- }
-
- /**
- * This class is used here to adjust the _delay value which in turn is used to control the number of messages/second
- * that are sent through the test system.
- *
- * By keeping a record of the messages recevied and the average time taken to process the batch size can be
- * calculated and so the delay can be adjusted to maintain that rate.
- *
- * Given that delays of < 10ms can be rounded up the delay is only used between messages if the _delay > 10ms * no
- * messages in the batch. Otherwise the delay is used at the end of the batch.
- */
- class SustainedRateAdapter implements MessageListener, Runnable
- {
- private SustainedClientTestCase _client;
- private long _batchVariance = Integer.getInteger("batchVariance", 3); // no. batches to allow drifting
- private long _timeVariance = TEN_MILLI_SEC * 5; // no. nanos between send and report delay (10ms)
- private volatile long _delay; // in nanos
- private long _sent;
- private Map<String, Long> _slowClients = new HashMap<String, Long>();
- private static final long PAUSE_SLEEP = TEN_MILLI_SEC / 1000; // 10 ms
- private static final long NO_CLIENT_SLEEP = 1000; // 1s
- private volatile boolean NO_CLIENTS = true;
- private int _delayShifting;
- private final int REPORTS_WITHOUT_CHANGE = Integer.getInteger("stableReportCount", 5);
- private boolean _warmedup = false;
- private static final long EXPECTED_TIME_PER_BATCH = 100000L;
- private int _warmUpBatches = Integer.getInteger("warmUpBatches", 10);
-
- SustainedRateAdapter(SustainedClientTestCase client)
- {
- _client = client;
- }
-
- public void onMessage(Message message)
- {
- if (log.isDebugEnabled())
- {
- log.debug("SustainedRateAdapter onMessage(Message message = " + message + "): called");
- }
-
- try
- {
- String controlType = message.getStringProperty("CONTROL_TYPE");
-
- // Check if the message is a test invite.
- if ("UPDATE".equals(controlType))
- {
- NO_CLIENTS = false;
- long duration = message.getLongProperty("DURATION");
- long totalReceived = message.getLongProperty("RECEIVED");
- String client = message.getStringProperty("CLIENT_ID");
- int batchNumber = message.getIntProperty("BATCH");
-
- if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0))
- {
- log.info("Update Report: CLIENT_ID:" + client + " RECEIVED:" + totalReceived + " Recevied BATCH:"
- + batchNumber + " DURATION:" + duration);
- }
-
- recordSlow(client, totalReceived, batchNumber);
-
- adjustDelay(client, batchNumber, duration);
-
- // Warm up completes when:
- // we haven't warmed up
- // and the number of batches sent to each client is at least half of the required warmup batches
- if (!_warmedup && (batchNumber >= _warmUpBatches))
- {
- _warmedup = true;
- _warmup.countDown();
-
- }
- }
- }
- catch (JMSException e)
- {
- //
- }
- }
-
- CountDownLatch _warmup = new CountDownLatch(1);
-
- int _numBatches = 10000;
-
- // long[] _timings = new long[_numBatches];
- private boolean _running = true;
-
- public void run()
- {
- console.info("Warming up");
-
- doBatch(_warmUpBatches);
-
- try
- {
- // wait for warmup to complete.
- _warmup.await();
-
- // set delay to the average length of the batches
- _delay = _totalDuration / _warmUpBatches / delays.size();
-
- console.info("Warmup complete delay set : " + _delay + " based on _totalDuration: " + _totalDuration
- + " over no. batches: " + _warmUpBatches + " with client count: " + delays.size());
-
- _totalDuration = 0L;
- _totalReceived = 0L;
- _sent = 0L;
- }
- catch (InterruptedException e)
- {
- //
- }
-
- doBatch(_numBatches);
-
- }
-
- private void doBatch(int batchSize) // long[] timings,
- {
- TextMessage testMessage = null;
- try
- {
- testMessage = _client.session[0].createTextMessage("start");
-
- for (int batch = 0; batch <= batchSize; batch++)
- // while (_running)
- {
- long start = System.nanoTime();
-
- testMessage.setText("start");
- testMessage.setIntProperty("BATCH", batch);
-
- _client.producer.send(testMessage);
- _rateAdapter.sentMessage();
-
- testMessage.setText("test");
- // start at 2 so start and end count as part of batch
- for (int m = 2; m < _batchSize; m++)
- {
- _client.producer.send(testMessage);
- _rateAdapter.sentMessage();
- }
-
- testMessage.setText("end");
- _client.producer.send(testMessage);
- _rateAdapter.sentMessage();
-
- long end = System.nanoTime();
-
- long sendtime = end - start;
-
- if (log.isDebugEnabled())
- {
- log.info("Sent batch[" + batch + "](" + _batchSize + ") in " + sendtime); // timings[batch]);
- }
-
- if ((batch % LOG_UPATE_INTERVAL) == 0)
- {
- console.info("Sent Batch[" + batch + "](" + _batchSize + ")" + status());
- }
-
- _rateAdapter.sleepBatch();
-
- }
- }
- catch (JMSException e)
- {
- console.error("Runner ended");
- }
- }
-
- private String status()
- {
- return " TotalDuration: " + _totalDuration + " for " + delays.size() + " consumers" + " Delay is " + _delay
- + " resulting in "
- + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch"));
- }
-
- private void sleepBatch()
- {
- if (checkForSlowClients())
- { // if there werwe slow clients we have already slept so don't sleep anymore again.
- return;
- }
-
- if (!SLEEP_PER_MESSAGE)
- {
- // per batch sleep.. if sleep is to small to spread over the batch.
- if (_delay <= (TEN_MILLI_SEC * _batchSize))
- {
- sleepLong(_delay);
- }
- else
- {
- log.info("Not sleeping _delay > ten*batch is:" + _delay);
- }
- }
- }
-
- public void stop()
- {
- _running = false;
- }
-
- Map<String, Long> delays = new HashMap<String, Long>();
- Long _totalReceived = 0L;
- Long _totalDuration = 0L;
- int _skipUpdate = 0;
-
- /**
- * Adjust the delay for sending messages based on this update from the client
- *
- * @param client The client that send this update
- * @param duration The time taken for the last batch of messagse
- * @param batchNumber The reported batchnumber from the client
- */
- private void adjustDelay(String client, int batchNumber, long duration)
- {
- // Retrieve the current total time taken for this client.
- Long currentTime = delays.get(client);
-
- // Add the new duration time to this client
- if (currentTime == null)
- {
- currentTime = duration;
- }
- else
- {
- currentTime += duration;
- }
-
- delays.put(client, currentTime);
-
- long batchesSent = _sent / _batchSize;
-
- // ensure we don't divide by zero
- if (batchesSent == 0)
- {
- batchesSent = 1L;
- }
-
- _totalReceived += _batchSize;
- _totalDuration += duration;
-
- // calculate average duration accross clients per batch
- long averageDuration = _totalDuration / delays.size() / batchesSent;
-
- // calculate the difference between current send delay and average report delay
- long diff = (duration) - averageDuration;
-
- if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0))
- {
- log.info("TotalDuration:" + _totalDuration + " for " + delays.size() + " consumers." + " on batch: "
- + batchesSent + " received batch: " + batchNumber + " Batch Duration: " + duration + " Average: "
- + averageDuration + " so diff: " + diff + " for : " + client + " Delay is " + _delay + " resulting in "
- + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch")));
- }
-
- // if the averageDuration differs from the current by more than the specified variane then adjust delay.
- if (Math.abs(diff) > _timeVariance)
- {
-
- // if the the _delay is larger than the required duration to send report
- // speed up
- if (diff > TEN_MILLI_SEC)
- {
- _delay -= TEN_MILLI_SEC;
-
- if (_delay < 0)
- {
- _delay = 0;
- log.info("Reset _delay to 0");
- delayStable();
- }
- else
- {
- delayChanged();
- }
-
- }
- else if (diff < 0) // diff < 0 diff cannot be 0 as it is > _timeVariance
- {
- // the report took longer
- _delay += TEN_MILLI_SEC;
- delayChanged();
- }
- }
- else
- {
- delayStable();
- }
-
- // If we have a consumer that is behind with the batches.
- if ((batchesSent - batchNumber) > _batchVariance)
- {
- log.debug("Increasing _delay as sending more than receiving");
-
- _delay += 2 * TEN_MILLI_SEC;
- delayChanged();
- }
-
- }
-
- /** Reset the number of iterations before we say the delay has stabilised. */
- private void delayChanged()
- {
- _delayShifting = REPORTS_WITHOUT_CHANGE;
- }
-
- /**
- * Record the fact that delay has stabilised If delay has stablised for REPORTS_WITHOUT_CHANGE then it will
- * output Delay stabilised
- */
- private void delayStable()
- {
- _delayShifting--;
-
- if (_delayShifting < 0)
- {
- _delayShifting = 0;
- console.debug("Delay stabilised:" + _delay);
- }
- }
-
- /**
- * Checks that the client has received enough messages. If the client has fallen behind then they are put in the
- * _slowClients lists which will increase the delay.
- *
- * @param client The client identifier to check
- * @param received the number of messages received by that client
- * @param batchNumber
- */
- private void recordSlow(String client, long received, int batchNumber)
- {
- if (Math.abs(batchNumber - (_sent / _batchSize)) > _batchVariance)
- {
- _slowClients.put(client, received);
- }
- else
- {
- _slowClients.remove(client);
- }
- }
-
- /** Incrment the number of sent messages and then sleep, if required. */
- public void sentMessage()
- {
-
- _sent++;
-
- if (_delay > (TEN_MILLI_SEC * _batchSize))
- {
- long batchDelay = _delay / _batchSize;
- // less than 10ms sleep doesn't always work.
- // _delay is in nano seconds
- // if (batchDelay < (TEN_MILLI_SEC))
- // {
- // sleep(0, (int) batchDelay);
- // }
- // else
- {
- // if (batchDelay < 30000000000L)
- {
- sleepLong(batchDelay);
- }
- }
- }
- else
- {
- if (SLEEP_PER_MESSAGE && (_delay > 0))
- {
- sleepLong(_delay / _batchSize);
- }
- }
- }
-
- /**
- * Check at the end of each batch and pause sending messages to allow slow clients to catch up.
- *
- * @return true if there were slow clients that caught up.
- */
- private boolean checkForSlowClients()
- {
- // This will allways be true as we are running this at the end of each batchSize
- // if (_sent % _batchSize == 0)
- {
- // Cause test to pause when we have slow
- if (!_slowClients.isEmpty() || NO_CLIENTS)
- {
-
- while (!_slowClients.isEmpty())
- {
- if (log.isInfoEnabled() && ((_sent / _batchSize % DEBUG_LOG_UPATE_INTERVAL) == 0))
- {
- String clients = "";
- Iterator it = _slowClients.keySet().iterator();
- while (it.hasNext())
- {
- clients += it.next();
- if (it.hasNext())
- {
- clients += ", ";
- }
- }
-
- log.info("Pausing for slow clients:" + clients);
- }
-
- if (console.isDebugEnabled() && ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0))
- {
- console.debug(_slowClients.size() + " slow clients.");
- }
-
- sleep(PAUSE_SLEEP);
- }
-
- if (NO_CLIENTS)
- {
- sleep(NO_CLIENT_SLEEP);
- }
-
- log.debug("Continuing");
-
- return true;
- }
- else
- {
- if ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0)
- {
- console.info("Total Delay :" + _delay + " "
- + ((_delayShifting == 0) ? "Stablised" : ("Not Stablised(" + _delayShifting + ")")));
- }
- }
-
- }
-
- return false;
- }
-
- /**
- * Sleep normally takes micro-seconds this allows the use of a nano-second value.
- *
- * @param delay nanoseconds to sleep for.
- */
- private void sleepLong(long delay)
- {
- sleep(delay / 1000000, (int) (delay % 1000000));
- }
-
- /**
- * Sleep for the specified micro-seconds.
- * @param sleep microseconds to sleep for.
- */
- private void sleep(long sleep)
- {
- sleep(sleep, 0);
- }
-
- /**
- * Perform the sleep , swallowing any InteruptException.
- *
- * NOTE: If a sleep request is > 10s then reset only sleep for 5s
- *
- * @param milli to sleep for
- * @param nano sub miliseconds to sleep for
- */
- private void sleep(long milli, int nano)
- {
- try
- {
- log.debug("Sleep:" + milli + ":" + nano);
- if (milli > 10000)
- {
-
- if (_delay == milli)
- {
- _totalDuration = _totalReceived / _batchSize * EXPECTED_TIME_PER_BATCH;
- log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000)
- + "s. Reset _totalDuration:" + _totalDuration);
- }
- else
- {
- log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) + "s");
- }
-
- milli = 5000;
- }
-
- Thread.sleep(milli, nano);
- }
- catch (InterruptedException e)
- {
- //
- }
- }
-
- public void setClient(SustainedClientTestCase client)
- {
- _client = client;
- }
- }
-
-}
+/* + * + * 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.sustained; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQNoConsumersException; +import org.apache.qpid.client.AMQNoRouteException; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub; +import org.apache.qpid.test.framework.TestUtils; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +/** + * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the + * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of + * messages sent/received. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Supply the name of the test case that this implements. + * <tr><td> Accept/Reject invites based on test parameters. + * <tr><td> Adapt to assigned roles. + * <tr><td> Send required number of test messages using pub/sub. <tr><td> Generate test reports. + * </table> + */ +public class SustainedClientTestCase extends TestCase3BasicPubSub implements ExceptionListener, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(SustainedClientTestCase.class); + + /** Used to log to the console. */ + private static final Logger console = Logger.getLogger("SustainedTest"); + + /** The role to be played by the test. */ + private Roles role; + + /** The number of receivers connection to use. */ + private int numReceivers; + + /** The routing key to send them to on the default direct exchange. */ + private Destination sendDestination; + + /** The routing key to send updates to on the default direct exchange. */ + private Destination sendUpdateDestination; + + /** The connections to send/receive the test messages on. */ + private Connection[] connection; + + /** The sessions to send/receive the test messages on. */ + private Session[] session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** Adapter that adjusts the send rate based on the updates from clients. */ + SustainedRateAdapter _rateAdapter; + + /** */ + int _batchSize; + + private static final long TEN_MILLI_SEC = 10000000; + private static final int DEBUG_LOG_UPATE_INTERVAL = 10; + private static final int LOG_UPATE_INTERVAL = 10; + private static final boolean SLEEP_PER_MESSAGE = Boolean.getBoolean("sleepPerMessage"); + + /** + * Should provide the name of the test case that this class implements. The exact names are defined in the interop + * testing spec. + * + * @return The name of the test case that this implements. + */ + public String getName() + { + log.debug("public String getName(): called"); + + return "Perf_SustainedPubSub"; + } + + /** + * Assigns the role to be played by this test case. The test parameters are fully specified in the assignment + * message. When this method return the test case will be ready to execute. + * + * @param role The role to be played; sender or receivers. + * @param assignRoleMessage The role assingment message, contains the full test parameters. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void assignRole(Roles role, Message assignRoleMessage) throws JMSException + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Take note of the role to be played. + this.role = role; + + // Extract and retain the test parameters. + numReceivers = assignRoleMessage.getIntProperty("SUSTAINED_NUM_RECEIVERS"); + _batchSize = assignRoleMessage.getIntProperty("SUSTAINED_UPDATE_INTERVAL"); + String sendKey = assignRoleMessage.getStringProperty("SUSTAINED_KEY"); + String sendUpdateKey = assignRoleMessage.getStringProperty("SUSTAINED_UPDATE_KEY"); + int ackMode = assignRoleMessage.getIntProperty("ACKNOWLEDGE_MODE"); + String clientName = assignRoleMessage.getStringProperty("CLIENT_NAME"); + + if (log.isDebugEnabled()) + { + log.debug("numReceivers = " + numReceivers); + log.debug("_batchSize = " + _batchSize); + log.debug("ackMode = " + ackMode); + log.debug("sendKey = " + sendKey); + log.debug("sendUpdateKey = " + sendUpdateKey); + log.debug("role = " + role); + } + + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + console.info("Creating Sender"); + // Create a new connection to pass the test messages on. + connection = new Connection[1]; + session = new Session[1]; + + connection[0] = TestUtils.createConnection(TestClient.testContextProperties); + session[0] = connection[0].createSession(false, ackMode); + + // Extract and retain the test parameters. + sendDestination = session[0].createTopic(sendKey); + + connection[0].setExceptionListener(this); + + producer = session[0].createProducer(sendDestination); + + sendUpdateDestination = session[0].createTopic(sendUpdateKey); + MessageConsumer updateConsumer = session[0].createConsumer(sendUpdateDestination); + + _rateAdapter = new SustainedRateAdapter(this); + updateConsumer.setMessageListener(_rateAdapter); + + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number + // of receivers connections. + case RECEIVER: + console.info("Creating Receiver"); + // Create the required number of receivers connections. + connection = new Connection[numReceivers]; + session = new Session[numReceivers]; + + for (int i = 0; i < numReceivers; i++) + { + connection[i] = TestUtils.createConnection(TestClient.testContextProperties); + session[i] = connection[i].createSession(false, ackMode); + + sendDestination = session[i].createTopic(sendKey); + + sendUpdateDestination = session[i].createTopic(sendUpdateKey); + + MessageConsumer consumer = session[i].createConsumer(sendDestination); + + consumer.setMessageListener(new SustainedListener(clientName + "-" + i, _batchSize, session[i], + sendUpdateDestination)); + } + + break; + } + + // Start all the connection dispatcher threads running. + for (int i = 0; i < connection.length; i++) + { + connection[i].start(); + } + } + + /** Performs the test case actions. + * @param numMessages*/ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + _rateAdapter.run(); + break; + case RECEIVER: + + } + + // return from here when you have finished the test.. this will signal the controller and + } + + public void terminate() throws JMSException, InterruptedException + { + if (_rateAdapter != null) + { + _rateAdapter.stop(); + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession to create the report message in. + * + * @return The report message. + * + * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. + */ + public Message getReport(Session session) throws JMSException + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connections. + for (int i = 0; i < connection.length; i++) + { + connection[i].close(); + } + + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + + return report; + } + + public void onException(JMSException jmsException) + { + Exception linked = jmsException.getLinkedException(); + + if (linked != null) + { + if (log.isDebugEnabled()) + { + log.debug("Linked Exception:" + linked); + } + + if ((linked instanceof AMQNoRouteException) || (linked instanceof AMQNoConsumersException)) + { + if (log.isDebugEnabled()) + { + if (linked instanceof AMQNoConsumersException) + { + log.warn("No clients currently available for message:" + + ((AMQNoConsumersException) linked).getUndeliveredMessage()); + } + else + { + log.warn("No route for message"); + } + } + + // Tell the rate adapter that there are no clients ready yet + _rateAdapter.NO_CLIENTS = true; + } + } + else + { + log.warn("Exception:" + linked); + } + } + + /** + * Inner class that listens for messages and sends a report for the time taken between receiving the 'start' and + * 'end' messages. + */ + class SustainedListener implements MessageListener + { + /** Number of messages received */ + private long _received = 0; + /** The number of messages in the batch */ + private int _batchSize = 0; + /** Record of the when the 'start' messagse was sen */ + private Long _startTime; + /** Message producer to use to send reports */ + MessageProducer _updater; + /** Session to create the report message on */ + Session _session; + /** Record of the client ID used for this SustainedListnener */ + String _client; + + /** + * Main Constructor + * + * @param clientname The _client id used to identify this connection. + * @param batchSize The number of messages that are to be sent per batch. Note: This is not used to + * control the interval between sending reports. + * @param session The controlSession used for communication. + * @param sendDestination The destination that update reports should be sent to. + * + * @throws JMSException My occur if creatingthe Producer fails + */ + public SustainedListener(String clientname, int batchSize, Session session, Destination sendDestination) + throws JMSException + { + _batchSize = batchSize; + _client = clientname; + _session = session; + _updater = session.createProducer(sendDestination); + } + + public void onMessage(Message message) + { + if (log.isDebugEnabled()) + { + log.debug("Message " + _received + "received in listener"); + } + + if (message instanceof TextMessage) + { + try + { + _received++; + if (((TextMessage) message).getText().equals("start")) + { + log.debug("Starting Batch"); + _startTime = System.nanoTime(); + } + else if (((TextMessage) message).getText().equals("end")) + { + if (_startTime != null) + { + long currentTime = System.nanoTime(); + sendStatus(currentTime - _startTime, _received, message.getIntProperty("BATCH")); + log.debug("End Batch"); + } + } + } + catch (JMSException e) + { + // ignore error + } + } + + } + + /** + * sendStatus creates and sends the report back to the publisher + * + * @param time taken for the the last batch + * @param received Total number of messages received. + * @param batchNumber the batch number + * @throws JMSException if an error occurs during the send + */ + private void sendStatus(long time, long received, int batchNumber) throws JMSException + { + Message updateMessage = _session.createTextMessage("update"); + updateMessage.setStringProperty("CLIENT_ID", ":" + _client); + updateMessage.setStringProperty("CONTROL_TYPE", "UPDATE"); + updateMessage.setLongProperty("RECEIVED", received); + updateMessage.setIntProperty("BATCH", batchNumber); + updateMessage.setLongProperty("DURATION", time); + + if (log.isInfoEnabled()) + { + log.info("**** SENDING [" + batchNumber + "]**** " + "CLIENT_ID:" + _client + " RECEIVED:" + received + + " BATCH:" + batchNumber + " DURATION:" + time); + } + + // Output on the main console.info the details of this batch + if ((batchNumber % 10) == 0) + { + console.info("Sending Report [" + batchNumber + "] " + "CLIENT_ID:" + _client + " RECEIVED:" + received + + " BATCH:" + batchNumber + " DURATION:" + time); + } + + _updater.send(updateMessage); + } + } + + /** + * This class is used here to adjust the _delay value which in turn is used to control the number of messages/second + * that are sent through the test system. + * + * By keeping a record of the messages recevied and the average time taken to process the batch size can be + * calculated and so the delay can be adjusted to maintain that rate. + * + * Given that delays of < 10ms can be rounded up the delay is only used between messages if the _delay > 10ms * no + * messages in the batch. Otherwise the delay is used at the end of the batch. + */ + class SustainedRateAdapter implements MessageListener, Runnable + { + private SustainedClientTestCase _client; + private long _batchVariance = Integer.getInteger("batchVariance", 3); // no. batches to allow drifting + private long _timeVariance = TEN_MILLI_SEC * 5; // no. nanos between send and report delay (10ms) + private volatile long _delay; // in nanos + private long _sent; + private Map<String, Long> _slowClients = new HashMap<String, Long>(); + private static final long PAUSE_SLEEP = TEN_MILLI_SEC / 1000; // 10 ms + private static final long NO_CLIENT_SLEEP = 1000; // 1s + private volatile boolean NO_CLIENTS = true; + private int _delayShifting; + private final int REPORTS_WITHOUT_CHANGE = Integer.getInteger("stableReportCount", 5); + private boolean _warmedup = false; + private static final long EXPECTED_TIME_PER_BATCH = 100000L; + private int _warmUpBatches = Integer.getInteger("warmUpBatches", 10); + + SustainedRateAdapter(SustainedClientTestCase client) + { + _client = client; + } + + public void onMessage(Message message) + { + if (log.isDebugEnabled()) + { + log.debug("SustainedRateAdapter onMessage(Message message = " + message + "): called"); + } + + try + { + String controlType = message.getStringProperty("CONTROL_TYPE"); + + // Check if the message is a test invite. + if ("UPDATE".equals(controlType)) + { + NO_CLIENTS = false; + long duration = message.getLongProperty("DURATION"); + long totalReceived = message.getLongProperty("RECEIVED"); + String client = message.getStringProperty("CLIENT_ID"); + int batchNumber = message.getIntProperty("BATCH"); + + if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) + { + log.info("Update Report: CLIENT_ID:" + client + " RECEIVED:" + totalReceived + " Recevied BATCH:" + + batchNumber + " DURATION:" + duration); + } + + recordSlow(client, totalReceived, batchNumber); + + adjustDelay(client, batchNumber, duration); + + // Warm up completes when: + // we haven't warmed up + // and the number of batches sent to each client is at least half of the required warmup batches + if (!_warmedup && (batchNumber >= _warmUpBatches)) + { + _warmedup = true; + _warmup.countDown(); + + } + } + } + catch (JMSException e) + { + // + } + } + + CountDownLatch _warmup = new CountDownLatch(1); + + int _numBatches = 10000; + + // long[] _timings = new long[_numBatches]; + private boolean _running = true; + + public void run() + { + console.info("Warming up"); + + doBatch(_warmUpBatches); + + try + { + // wait for warmup to complete. + _warmup.await(); + + // set delay to the average length of the batches + _delay = _totalDuration / _warmUpBatches / delays.size(); + + console.info("Warmup complete delay set : " + _delay + " based on _totalDuration: " + _totalDuration + + " over no. batches: " + _warmUpBatches + " with client count: " + delays.size()); + + _totalDuration = 0L; + _totalReceived = 0L; + _sent = 0L; + } + catch (InterruptedException e) + { + // + } + + doBatch(_numBatches); + + } + + private void doBatch(int batchSize) // long[] timings, + { + TextMessage testMessage = null; + try + { + testMessage = _client.session[0].createTextMessage("start"); + + for (int batch = 0; batch <= batchSize; batch++) + // while (_running) + { + long start = System.nanoTime(); + + testMessage.setText("start"); + testMessage.setIntProperty("BATCH", batch); + + _client.producer.send(testMessage); + _rateAdapter.sentMessage(); + + testMessage.setText("test"); + // start at 2 so start and end count as part of batch + for (int m = 2; m < _batchSize; m++) + { + _client.producer.send(testMessage); + _rateAdapter.sentMessage(); + } + + testMessage.setText("end"); + _client.producer.send(testMessage); + _rateAdapter.sentMessage(); + + long end = System.nanoTime(); + + long sendtime = end - start; + + if (log.isDebugEnabled()) + { + log.info("Sent batch[" + batch + "](" + _batchSize + ") in " + sendtime); // timings[batch]); + } + + if ((batch % LOG_UPATE_INTERVAL) == 0) + { + console.info("Sent Batch[" + batch + "](" + _batchSize + ")" + status()); + } + + _rateAdapter.sleepBatch(); + + } + } + catch (JMSException e) + { + console.error("Runner ended"); + } + } + + private String status() + { + return " TotalDuration: " + _totalDuration + " for " + delays.size() + " consumers" + " Delay is " + _delay + + " resulting in " + + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch")); + } + + private void sleepBatch() + { + if (checkForSlowClients()) + { // if there werwe slow clients we have already slept so don't sleep anymore again. + return; + } + + if (!SLEEP_PER_MESSAGE) + { + // per batch sleep.. if sleep is to small to spread over the batch. + if (_delay <= (TEN_MILLI_SEC * _batchSize)) + { + sleepLong(_delay); + } + else + { + log.info("Not sleeping _delay > ten*batch is:" + _delay); + } + } + } + + public void stop() + { + _running = false; + } + + Map<String, Long> delays = new HashMap<String, Long>(); + Long _totalReceived = 0L; + Long _totalDuration = 0L; + int _skipUpdate = 0; + + /** + * Adjust the delay for sending messages based on this update from the client + * + * @param client The client that send this update + * @param duration The time taken for the last batch of messagse + * @param batchNumber The reported batchnumber from the client + */ + private void adjustDelay(String client, int batchNumber, long duration) + { + // Retrieve the current total time taken for this client. + Long currentTime = delays.get(client); + + // Add the new duration time to this client + if (currentTime == null) + { + currentTime = duration; + } + else + { + currentTime += duration; + } + + delays.put(client, currentTime); + + long batchesSent = _sent / _batchSize; + + // ensure we don't divide by zero + if (batchesSent == 0) + { + batchesSent = 1L; + } + + _totalReceived += _batchSize; + _totalDuration += duration; + + // calculate average duration accross clients per batch + long averageDuration = _totalDuration / delays.size() / batchesSent; + + // calculate the difference between current send delay and average report delay + long diff = (duration) - averageDuration; + + if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) + { + log.info("TotalDuration:" + _totalDuration + " for " + delays.size() + " consumers." + " on batch: " + + batchesSent + " received batch: " + batchNumber + " Batch Duration: " + duration + " Average: " + + averageDuration + " so diff: " + diff + " for : " + client + " Delay is " + _delay + " resulting in " + + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch"))); + } + + // if the averageDuration differs from the current by more than the specified variane then adjust delay. + if (Math.abs(diff) > _timeVariance) + { + + // if the the _delay is larger than the required duration to send report + // speed up + if (diff > TEN_MILLI_SEC) + { + _delay -= TEN_MILLI_SEC; + + if (_delay < 0) + { + _delay = 0; + log.info("Reset _delay to 0"); + delayStable(); + } + else + { + delayChanged(); + } + + } + else if (diff < 0) // diff < 0 diff cannot be 0 as it is > _timeVariance + { + // the report took longer + _delay += TEN_MILLI_SEC; + delayChanged(); + } + } + else + { + delayStable(); + } + + // If we have a consumer that is behind with the batches. + if ((batchesSent - batchNumber) > _batchVariance) + { + log.debug("Increasing _delay as sending more than receiving"); + + _delay += 2 * TEN_MILLI_SEC; + delayChanged(); + } + + } + + /** Reset the number of iterations before we say the delay has stabilised. */ + private void delayChanged() + { + _delayShifting = REPORTS_WITHOUT_CHANGE; + } + + /** + * Record the fact that delay has stabilised If delay has stablised for REPORTS_WITHOUT_CHANGE then it will + * output Delay stabilised + */ + private void delayStable() + { + _delayShifting--; + + if (_delayShifting < 0) + { + _delayShifting = 0; + console.debug("Delay stabilised:" + _delay); + } + } + + /** + * Checks that the client has received enough messages. If the client has fallen behind then they are put in the + * _slowClients lists which will increase the delay. + * + * @param client The client identifier to check + * @param received the number of messages received by that client + * @param batchNumber + */ + private void recordSlow(String client, long received, int batchNumber) + { + if (Math.abs(batchNumber - (_sent / _batchSize)) > _batchVariance) + { + _slowClients.put(client, received); + } + else + { + _slowClients.remove(client); + } + } + + /** Incrment the number of sent messages and then sleep, if required. */ + public void sentMessage() + { + + _sent++; + + if (_delay > (TEN_MILLI_SEC * _batchSize)) + { + long batchDelay = _delay / _batchSize; + // less than 10ms sleep doesn't always work. + // _delay is in nano seconds + // if (batchDelay < (TEN_MILLI_SEC)) + // { + // sleep(0, (int) batchDelay); + // } + // else + { + // if (batchDelay < 30000000000L) + { + sleepLong(batchDelay); + } + } + } + else + { + if (SLEEP_PER_MESSAGE && (_delay > 0)) + { + sleepLong(_delay / _batchSize); + } + } + } + + /** + * Check at the end of each batch and pause sending messages to allow slow clients to catch up. + * + * @return true if there were slow clients that caught up. + */ + private boolean checkForSlowClients() + { + // This will allways be true as we are running this at the end of each batchSize + // if (_sent % _batchSize == 0) + { + // Cause test to pause when we have slow + if (!_slowClients.isEmpty() || NO_CLIENTS) + { + + while (!_slowClients.isEmpty()) + { + if (log.isInfoEnabled() && ((_sent / _batchSize % DEBUG_LOG_UPATE_INTERVAL) == 0)) + { + String clients = ""; + Iterator it = _slowClients.keySet().iterator(); + while (it.hasNext()) + { + clients += it.next(); + if (it.hasNext()) + { + clients += ", "; + } + } + + log.info("Pausing for slow clients:" + clients); + } + + if (console.isDebugEnabled() && ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0)) + { + console.debug(_slowClients.size() + " slow clients."); + } + + sleep(PAUSE_SLEEP); + } + + if (NO_CLIENTS) + { + sleep(NO_CLIENT_SLEEP); + } + + log.debug("Continuing"); + + return true; + } + else + { + if ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0) + { + console.info("Total Delay :" + _delay + " " + + ((_delayShifting == 0) ? "Stablised" : ("Not Stablised(" + _delayShifting + ")"))); + } + } + + } + + return false; + } + + /** + * Sleep normally takes micro-seconds this allows the use of a nano-second value. + * + * @param delay nanoseconds to sleep for. + */ + private void sleepLong(long delay) + { + sleep(delay / 1000000, (int) (delay % 1000000)); + } + + /** + * Sleep for the specified micro-seconds. + * @param sleep microseconds to sleep for. + */ + private void sleep(long sleep) + { + sleep(sleep, 0); + } + + /** + * Perform the sleep , swallowing any InteruptException. + * + * NOTE: If a sleep request is > 10s then reset only sleep for 5s + * + * @param milli to sleep for + * @param nano sub miliseconds to sleep for + */ + private void sleep(long milli, int nano) + { + try + { + log.debug("Sleep:" + milli + ":" + nano); + if (milli > 10000) + { + + if (_delay == milli) + { + _totalDuration = _totalReceived / _batchSize * EXPECTED_TIME_PER_BATCH; + log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) + + "s. Reset _totalDuration:" + _totalDuration); + } + else + { + log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) + "s"); + } + + milli = 5000; + } + + Thread.sleep(milli, nano); + } + catch (InterruptedException e) + { + // + } + } + + public void setClient(SustainedClientTestCase client) + { + _client = client; + } + } + +} diff --git a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java index 5979a459ec..0077b4727a 100644 --- a/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java +++ b/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java @@ -1,126 +1,126 @@ -/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.sustained;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.client.AMQSession;
-import org.apache.qpid.test.framework.DropInTest;
-import org.apache.qpid.test.framework.FrameworkBaseCase;
-
-import javax.jms.JMSException;
-import javax.jms.Message;
-
-import java.util.Properties;
-
-/**
- * SustainedTestCase is a {@link FrameworkBaseCase} that runs the "Perf_SustainedPubSub" test case. This consists of one
- * test client sending, and several receiving, and attempts to find the highest rate at which messages can be broadcast
- * to the receivers. It is also a {@link DropInTest} to which more test clients may be added during a test run.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td>
- * </table>
- */
-public class SustainedTestCase extends FrameworkBaseCase implements DropInTest
-{
- /** Used for debugging. */
- Logger log = Logger.getLogger(SustainedTestCase.class);
-
- /** Holds the root name of the topic on which to send the test messages. */
- private static final String SUSTAINED_KEY = "Perf_SustainedPubSub";
-
- /**
- * Creates a new coordinating test case with the specified name.
- *
- * @param name The test case name.
- */
- public SustainedTestCase(String name)
- {
- super(name);
- }
-
- /**
- * Performs a single test run of the sustained test.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- public void testBasicPubSub() throws Exception
- {
- log.debug("public void testSinglePubSubCycle(): called");
-
- Properties testConfig = new Properties();
- testConfig.put("TEST_NAME", "Perf_SustainedPubSub");
- testConfig.put("SUSTAINED_KEY", SUSTAINED_KEY);
- testConfig.put("SUSTAINED_NUM_RECEIVERS", Integer.getInteger("numReceives", 2));
- testConfig.put("SUSTAINED_UPDATE_INTERVAL", Integer.getInteger("batchSize", 1000));
- testConfig.put("SUSTAINED_UPDATE_KEY", SUSTAINED_KEY + ".UPDATE");
- testConfig.put("ACKNOWLEDGE_MODE", Integer.getInteger("ackMode", AMQSession.AUTO_ACKNOWLEDGE));
-
- log.info("Created Config: " + testConfig.entrySet().toArray());
-
- getCircuitFactory().sequenceTest(null, null, testConfig);
- }
-
- /**
- * Accepts a late joining client into this test case. The client will be enlisted with a control message
- * with the 'CONTROL_TYPE' field set to the value 'LATEJOIN'. It should also provide values for the fields:
- *
- * <p/><table>
- * <tr><td> CLIENT_NAME <td> A unique name for the new client.
- * <tr><td> CLIENT_PRIVATE_CONTROL_KEY <td> The key for the route on which the client receives its control messages.
- * </table>
- *
- * @param message The late joiners join message.
- *
- * @throws JMSException Any JMS Exception are allowed to fall through, indicating that the join failed.
- */
- public void lateJoin(Message message) throws JMSException
- {
- throw new RuntimeException("Not implemented.");
- /*
- // Extract the joining clients details from its join request message.
- TestClientDetails clientDetails = new TestClientDetails();
- clientDetails.clientName = message.getStringProperty("CLIENT_NAME");
- clientDetails.privateControlKey = message.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY");
-
- // Register the joining client, but do block for confirmation as cannot do a synchronous receivers during this
- // method call, as it may have been called from an 'onMessage' method.
- assignReceiverRole(clientDetails, new Properties(), false);
- */
- }
-
- /**
- * Should provide a translation from the junit method name of a test to its test case name as known to the test
- * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test
- * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case
- * name "TC2_BasicP2P".
- *
- * @param methodName The name of the JUnit test method.
- *
- * @return The name of the corresponding interop test case.
- */
- public String getTestCaseNameForTestMethod(String methodName)
- {
- return "Perf_SustainedPubSub";
- }
-}
+/* + * + * 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.sustained; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.test.framework.DropInTest; +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import javax.jms.JMSException; +import javax.jms.Message; + +import java.util.Properties; + +/** + * SustainedTestCase is a {@link FrameworkBaseCase} that runs the "Perf_SustainedPubSub" test case. This consists of one + * test client sending, and several receiving, and attempts to find the highest rate at which messages can be broadcast + * to the receivers. It is also a {@link DropInTest} to which more test clients may be added during a test run. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> + * </table> + */ +public class SustainedTestCase extends FrameworkBaseCase implements DropInTest +{ + /** Used for debugging. */ + Logger log = Logger.getLogger(SustainedTestCase.class); + + /** Holds the root name of the topic on which to send the test messages. */ + private static final String SUSTAINED_KEY = "Perf_SustainedPubSub"; + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public SustainedTestCase(String name) + { + super(name); + } + + /** + * Performs a single test run of the sustained test. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testBasicPubSub() throws Exception + { + log.debug("public void testSinglePubSubCycle(): called"); + + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "Perf_SustainedPubSub"); + testConfig.put("SUSTAINED_KEY", SUSTAINED_KEY); + testConfig.put("SUSTAINED_NUM_RECEIVERS", Integer.getInteger("numReceives", 2)); + testConfig.put("SUSTAINED_UPDATE_INTERVAL", Integer.getInteger("batchSize", 1000)); + testConfig.put("SUSTAINED_UPDATE_KEY", SUSTAINED_KEY + ".UPDATE"); + testConfig.put("ACKNOWLEDGE_MODE", Integer.getInteger("ackMode", AMQSession.AUTO_ACKNOWLEDGE)); + + log.info("Created Config: " + testConfig.entrySet().toArray()); + + getCircuitFactory().sequenceTest(null, null, testConfig); + } + + /** + * Accepts a late joining client into this test case. The client will be enlisted with a control message + * with the 'CONTROL_TYPE' field set to the value 'LATEJOIN'. It should also provide values for the fields: + * + * <p/><table> + * <tr><td> CLIENT_NAME <td> A unique name for the new client. + * <tr><td> CLIENT_PRIVATE_CONTROL_KEY <td> The key for the route on which the client receives its control messages. + * </table> + * + * @param message The late joiners join message. + * + * @throws JMSException Any JMS Exception are allowed to fall through, indicating that the join failed. + */ + public void lateJoin(Message message) throws JMSException + { + throw new RuntimeException("Not implemented."); + /* + // Extract the joining clients details from its join request message. + TestClientDetails clientDetails = new TestClientDetails(); + clientDetails.clientName = message.getStringProperty("CLIENT_NAME"); + clientDetails.privateControlKey = message.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY"); + + // Register the joining client, but do block for confirmation as cannot do a synchronous receivers during this + // method call, as it may have been called from an 'onMessage' method. + assignReceiverRole(clientDetails, new Properties(), false); + */ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as known to the test + * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test + * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case + * name "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "Perf_SustainedPubSub"; + } +} diff --git a/qpid/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/IsolatedClassLoader.java b/qpid/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/IsolatedClassLoader.java index dc74590f60..fdae005f00 100644 --- a/qpid/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/IsolatedClassLoader.java +++ b/qpid/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/IsolatedClassLoader.java @@ -1,113 +1,113 @@ -/*
- *
- * 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.junit.maven;
-
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * </table>
- *
- * @author Rupert Smith
- *
- * @noinspection CustomClassloader
- */
-public class IsolatedClassLoader extends URLClassLoader
-{
-
- private static final URL[] EMPTY_URL_ARRAY = new URL[0];
- private ClassLoader parent = ClassLoader.getSystemClassLoader();
-
- private Set urls = new HashSet();
-
- private boolean childDelegation = true;
-
- public IsolatedClassLoader()
- {
- super(EMPTY_URL_ARRAY, null);
- }
-
- public IsolatedClassLoader(ClassLoader parent, boolean childDelegation)
- {
- super(EMPTY_URL_ARRAY, parent);
-
- this.childDelegation = childDelegation;
- }
-
- public IsolatedClassLoader(ClassLoader parent)
- {
- super(EMPTY_URL_ARRAY, parent);
- }
-
- public void addURL(URL url)
- {
- // avoid duplicates
- if (!urls.contains(url))
- {
- super.addURL(url);
- urls.add(url);
- }
- }
-
- public synchronized Class loadClass(String name) throws ClassNotFoundException
- {
- Class c;
-
- if (childDelegation)
- {
- c = findLoadedClass(name);
-
- ClassNotFoundException ex = null;
-
- if (c == null)
- {
- try
- {
- c = findClass(name);
- }
- catch (ClassNotFoundException e)
- {
- ex = e;
-
- if (parent != null)
- {
- c = parent.loadClass(name);
- }
- }
- }
-
- if (c == null)
- {
- throw ex;
- }
- }
- else
- {
- c = super.loadClass(name);
- }
-
- return c;
- }
-}
+/* + * + * 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.junit.maven; + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashSet; +import java.util.Set; + +/** + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * </table> + * + * @author Rupert Smith + * + * @noinspection CustomClassloader + */ +public class IsolatedClassLoader extends URLClassLoader +{ + + private static final URL[] EMPTY_URL_ARRAY = new URL[0]; + private ClassLoader parent = ClassLoader.getSystemClassLoader(); + + private Set urls = new HashSet(); + + private boolean childDelegation = true; + + public IsolatedClassLoader() + { + super(EMPTY_URL_ARRAY, null); + } + + public IsolatedClassLoader(ClassLoader parent, boolean childDelegation) + { + super(EMPTY_URL_ARRAY, parent); + + this.childDelegation = childDelegation; + } + + public IsolatedClassLoader(ClassLoader parent) + { + super(EMPTY_URL_ARRAY, parent); + } + + public void addURL(URL url) + { + // avoid duplicates + if (!urls.contains(url)) + { + super.addURL(url); + urls.add(url); + } + } + + public synchronized Class loadClass(String name) throws ClassNotFoundException + { + Class c; + + if (childDelegation) + { + c = findLoadedClass(name); + + ClassNotFoundException ex = null; + + if (c == null) + { + try + { + c = findClass(name); + } + catch (ClassNotFoundException e) + { + ex = e; + + if (parent != null) + { + c = parent.loadClass(name); + } + } + } + + if (c == null) + { + throw ex; + } + } + else + { + c = super.loadClass(name); + } + + return c; + } +} diff --git a/qpid/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestRunnerMojo.java b/qpid/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestRunnerMojo.java index 442529b609..36e594d9dc 100644 --- a/qpid/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestRunnerMojo.java +++ b/qpid/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestRunnerMojo.java @@ -1,274 +1,274 @@ -/*
- *
- * 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.junit.maven;
-
-import org.apache.maven.plugin.AbstractMojo;
-
-import java.io.File;
-import java.lang.reflect.Method;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.*;
-
-/**
- * TKTestRunnerMojo is a JUnit test runner plugin for Maven 2. It is intended to be compatible with the surefire
- * plugin (though not all features of that are yet implemented), with some extended capabilities.
- *
- * <p/>This plugin adds the ability to use different JUnit test runners, and to pass arbitrary options to them.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * </table>
- *
- * @author Rupert Smith
- *
- * @goal tktest
- * @phase test
- * @requiresDependencyResolution test
- */
-public class TKTestRunnerMojo extends AbstractMojo
-{
- private static final BitSet UNRESERVED = new BitSet(256);
-
- /**
- * Set this to 'true' to bypass unit tests entirely. Its use is NOT RECOMMENDED, but quite convenient on occasion.
- *
- * @parameter expression="${maven.test.skip}"
- */
- private boolean skip;
-
- /**
- * The TKTest runner command lines. There are passed directly to the TKTestRunner main method.
- *
- * @parameter
- */
- private Map<String, String> commands = new LinkedHashMap<String, String>();
-
- /**
- * The base directory of the project being tested. This can be obtained in your unit test by
- * System.getProperty("basedir").
- *
- * @parameter expression="${basedir}"
- * @required
- */
- private File basedir;
-
- /**
- * The directory containing generated classes of the project being tested.
- *
- * @parameter expression="${project.build.outputDirectory}"
- * @required
- */
- private File classesDirectory;
-
- /**
- * The directory containing generated test classes of the project being tested.
- *
- * @parameter expression="${project.build.testOutputDirectory}"
- * @required
- */
- private File testClassesDirectory;
-
- /**
- * The classpath elements of the project being tested.
- *
- * @parameter expression="${project.testClasspathElements}"
- * @required
- * @readonly
- */
- private List classpathElements;
-
- /**
- * List of System properties to pass to the tests.
- *
- * @parameter
- */
- private Properties systemProperties;
-
- /**
- * Map of of plugin artifacts.
- *
- * @parameter expression="${plugin.artifactMap}"
- * @required
- * @readonly
- */
- private Map pluginArtifactMap;
-
- /**
- * Map of of project artifacts.
- *
- * @parameter expression="${project.artifactMap}"
- * @required
- * @readonly
- */
- private Map projectArtifactMap;
-
- /**
- * Option to specify the forking mode. Can be "never" (default), "once" or "always".
- * "none" and "pertest" are also accepted for backwards compatibility.
- *
- * @parameter expression="${forkMode}" default-value="once"
- */
- private String forkMode;
-
- /**
- * Option to specify the jvm (or path to the java executable) to use with
- * the forking options. For the default we will assume that java is in the path.
- *
- * @parameter expression="${jvm}"
- * default-value="java"
- */
- private String jvm;
-
- /**
- * The test runner to use.
- *
- * @parameter
- */
- private String testrunner;
-
- /**
- * The additional properties to append to the test runner invocation command line.
- *
- * @parameter
- */
- private Properties testrunnerproperties;
-
- /**
- * The options to pass to all test runner invocation command lines.
- *
- * @parameter
- */
- private String[] testrunneroptions;
-
- /**
- * Implementation of the tktest goal.
- */
- public void execute()
- {
- // Skip these tests if test skipping is turned on.
- if (skip)
- {
- getLog().info("Skipping Tests.");
-
- return;
- }
-
- // Log out the classpath if debugging is on.
- if (getLog().isDebugEnabled())
- {
- getLog().info("Test Classpath :");
-
- for (Object classpathElement1 : classpathElements)
- {
- String classpathElement = (String) classpathElement1;
- getLog().info(" " + classpathElement);
- }
- }
-
- try
- {
- // Create a class loader to load the test runner with. This also gets set as the context loader for this
- // thread, so that all subsequent class loading activity by the test runner or the test code, has the
- // test classes available to it. The system loader is set up for the maven build, which is why a seperate
- // loader needs to be created; in order to inject the test dependencies into it.
- ClassLoader runnerClassLoader = createClassLoader(classpathElements, ClassLoader.getSystemClassLoader(), true);
- Thread.currentThread().setContextClassLoader(runnerClassLoader);
-
- // Load the test runner implementation that will be used to run the tests.
- if ((testrunner == null) || "".equals(testrunner))
- {
- testrunner = "org.apache.qpid.junit.extensions.TKTestRunner";
- }
-
- Class testRunnerClass = Class.forName(testrunner, false, runnerClassLoader);
- Method run = testRunnerClass.getMethod("main", String[].class);
-
- // Concatenate all of the options to pass on the command line to the test runner.
- String preOptions = "";
-
- for (String option : testrunneroptions)
- {
- preOptions += option + " ";
- }
-
- // Concatenate all of the additional properties as name=value pairs on the command line.
- String nvPairs = "";
-
- if (testrunnerproperties != null)
- {
- for (Object objKey : testrunnerproperties.keySet())
- {
- String key = (String) objKey;
- String value = testrunnerproperties.getProperty(key);
-
- nvPairs = key + "=" + value + " ";
- }
- }
-
- // Pass each of the test runner command lines in turn to the toolkit test runner.
- // The command line is made up of the options, the command specific command line, then the trailing
- // name=value pairs.
- for (String testName : commands.keySet())
- {
- String commandLine = preOptions + " " + commands.get(testName) + " " + nvPairs;
- getLog().info("commandLine = " + commandLine);
-
- // Tokenize the command line on white space, into an array of string components.
- String[] tokenizedCommandLine = commandLine.split("\\s+");
-
- // Run the tests.
- run.invoke(testRunnerClass, new Object[] { tokenizedCommandLine });
- }
- }
- catch (Exception e)
- {
- getLog().error("There was an exception: " + e.getMessage(), e);
- }
- }
-
- private static ClassLoader createClassLoader(List classPathUrls, ClassLoader parent, boolean childDelegation)
- throws MalformedURLException
- {
- List urls = new ArrayList();
-
- for (Iterator i = classPathUrls.iterator(); i.hasNext();)
- {
- String url = (String) i.next();
-
- if (url != null)
- {
- File f = new File(url);
- urls.add(f.toURL());
- }
- }
-
- IsolatedClassLoader classLoader = new IsolatedClassLoader(parent, childDelegation);
-
- for (Iterator iter = urls.iterator(); iter.hasNext();)
- {
- URL url = (URL) iter.next();
- classLoader.addURL(url);
- }
-
- return classLoader;
- }
-}
+/* + * + * 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.junit.maven; + +import org.apache.maven.plugin.AbstractMojo; + +import java.io.File; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.*; + +/** + * TKTestRunnerMojo is a JUnit test runner plugin for Maven 2. It is intended to be compatible with the surefire + * plugin (though not all features of that are yet implemented), with some extended capabilities. + * + * <p/>This plugin adds the ability to use different JUnit test runners, and to pass arbitrary options to them. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * </table> + * + * @author Rupert Smith + * + * @goal tktest + * @phase test + * @requiresDependencyResolution test + */ +public class TKTestRunnerMojo extends AbstractMojo +{ + private static final BitSet UNRESERVED = new BitSet(256); + + /** + * Set this to 'true' to bypass unit tests entirely. Its use is NOT RECOMMENDED, but quite convenient on occasion. + * + * @parameter expression="${maven.test.skip}" + */ + private boolean skip; + + /** + * The TKTest runner command lines. There are passed directly to the TKTestRunner main method. + * + * @parameter + */ + private Map<String, String> commands = new LinkedHashMap<String, String>(); + + /** + * The base directory of the project being tested. This can be obtained in your unit test by + * System.getProperty("basedir"). + * + * @parameter expression="${basedir}" + * @required + */ + private File basedir; + + /** + * The directory containing generated classes of the project being tested. + * + * @parameter expression="${project.build.outputDirectory}" + * @required + */ + private File classesDirectory; + + /** + * The directory containing generated test classes of the project being tested. + * + * @parameter expression="${project.build.testOutputDirectory}" + * @required + */ + private File testClassesDirectory; + + /** + * The classpath elements of the project being tested. + * + * @parameter expression="${project.testClasspathElements}" + * @required + * @readonly + */ + private List classpathElements; + + /** + * List of System properties to pass to the tests. + * + * @parameter + */ + private Properties systemProperties; + + /** + * Map of of plugin artifacts. + * + * @parameter expression="${plugin.artifactMap}" + * @required + * @readonly + */ + private Map pluginArtifactMap; + + /** + * Map of of project artifacts. + * + * @parameter expression="${project.artifactMap}" + * @required + * @readonly + */ + private Map projectArtifactMap; + + /** + * Option to specify the forking mode. Can be "never" (default), "once" or "always". + * "none" and "pertest" are also accepted for backwards compatibility. + * + * @parameter expression="${forkMode}" default-value="once" + */ + private String forkMode; + + /** + * Option to specify the jvm (or path to the java executable) to use with + * the forking options. For the default we will assume that java is in the path. + * + * @parameter expression="${jvm}" + * default-value="java" + */ + private String jvm; + + /** + * The test runner to use. + * + * @parameter + */ + private String testrunner; + + /** + * The additional properties to append to the test runner invocation command line. + * + * @parameter + */ + private Properties testrunnerproperties; + + /** + * The options to pass to all test runner invocation command lines. + * + * @parameter + */ + private String[] testrunneroptions; + + /** + * Implementation of the tktest goal. + */ + public void execute() + { + // Skip these tests if test skipping is turned on. + if (skip) + { + getLog().info("Skipping Tests."); + + return; + } + + // Log out the classpath if debugging is on. + if (getLog().isDebugEnabled()) + { + getLog().info("Test Classpath :"); + + for (Object classpathElement1 : classpathElements) + { + String classpathElement = (String) classpathElement1; + getLog().info(" " + classpathElement); + } + } + + try + { + // Create a class loader to load the test runner with. This also gets set as the context loader for this + // thread, so that all subsequent class loading activity by the test runner or the test code, has the + // test classes available to it. The system loader is set up for the maven build, which is why a seperate + // loader needs to be created; in order to inject the test dependencies into it. + ClassLoader runnerClassLoader = createClassLoader(classpathElements, ClassLoader.getSystemClassLoader(), true); + Thread.currentThread().setContextClassLoader(runnerClassLoader); + + // Load the test runner implementation that will be used to run the tests. + if ((testrunner == null) || "".equals(testrunner)) + { + testrunner = "org.apache.qpid.junit.extensions.TKTestRunner"; + } + + Class testRunnerClass = Class.forName(testrunner, false, runnerClassLoader); + Method run = testRunnerClass.getMethod("main", String[].class); + + // Concatenate all of the options to pass on the command line to the test runner. + String preOptions = ""; + + for (String option : testrunneroptions) + { + preOptions += option + " "; + } + + // Concatenate all of the additional properties as name=value pairs on the command line. + String nvPairs = ""; + + if (testrunnerproperties != null) + { + for (Object objKey : testrunnerproperties.keySet()) + { + String key = (String) objKey; + String value = testrunnerproperties.getProperty(key); + + nvPairs = key + "=" + value + " "; + } + } + + // Pass each of the test runner command lines in turn to the toolkit test runner. + // The command line is made up of the options, the command specific command line, then the trailing + // name=value pairs. + for (String testName : commands.keySet()) + { + String commandLine = preOptions + " " + commands.get(testName) + " " + nvPairs; + getLog().info("commandLine = " + commandLine); + + // Tokenize the command line on white space, into an array of string components. + String[] tokenizedCommandLine = commandLine.split("\\s+"); + + // Run the tests. + run.invoke(testRunnerClass, new Object[] { tokenizedCommandLine }); + } + } + catch (Exception e) + { + getLog().error("There was an exception: " + e.getMessage(), e); + } + } + + private static ClassLoader createClassLoader(List classPathUrls, ClassLoader parent, boolean childDelegation) + throws MalformedURLException + { + List urls = new ArrayList(); + + for (Iterator i = classPathUrls.iterator(); i.hasNext();) + { + String url = (String) i.next(); + + if (url != null) + { + File f = new File(url); + urls.add(f.toURL()); + } + } + + IsolatedClassLoader classLoader = new IsolatedClassLoader(parent, childDelegation); + + for (Iterator iter = urls.iterator(); iter.hasNext();) + { + URL url = (URL) iter.next(); + classLoader.addURL(url); + } + + return classLoader; + } +} diff --git a/qpid/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestScriptGenMojo.java b/qpid/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestScriptGenMojo.java index 5c7669e069..adbe527e5e 100644 --- a/qpid/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestScriptGenMojo.java +++ b/qpid/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestScriptGenMojo.java @@ -1,148 +1,148 @@ -/*
- *
- * 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.junit.maven;
-
-import org.apache.maven.plugin.AbstractMojo;
-import org.apache.maven.plugin.MojoExecutionException;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.Writer;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Properties;
-
-/**
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * </table>
- *
- * @author Rupert Smith
- * @goal tkscriptgen
- * @phase test
- * @execute phase="test"
- * @requiresDependencyResolution test
- */
-public class TKTestScriptGenMojo extends AbstractMojo
-{
- private static final String _scriptLanguage = "#!/bin/bash\n\n";
-
- private static final String _javaOptArgParser =
- "# Parse arguements taking all - prefixed args as JAVA_OPTS\n" + "for arg in \"$@\"; do\n"
- + " if [[ $arg == -java:* ]]; then\n" + " JAVA_OPTS=\"${JAVA_OPTS}-`echo $arg|cut -d ':' -f 2` \"\n"
- + " else\n" + " ARGS=\"${ARGS}$arg \"\n" + " fi\n" + "done\n\n";
-
- /**
- * Where to write out the scripts.
- *
- * @parameter
- */
- private String scriptOutDirectory;
-
- /**
- * The all-in-one test jar location.
- *
- * @parameter
- */
- private String testJar;
-
- /**
- * The system properties to pass to java runtime.
- *
- * @parameter
- */
- private Properties systemproperties;
-
- /**
- * The TKTest runner command lines. There are passed directly to the TKTestRunner main method.
- *
- * @parameter
- */
- private Map<String, String> commands = new LinkedHashMap<String, String>();
-
- /**
- * Implementation of the tkscriptgen goal.
- *
- * @throws MojoExecutionException
- */
- public void execute() throws MojoExecutionException
- {
- // Turn each of the test runner command lines into a script.
- for (String testName : commands.keySet())
- {
- String testOptions = commands.get(testName);
- String commandLine = "java ";
-
- String logdir = null;
-
- for (Object key : systemproperties.keySet())
- {
- String keyString = (String) key;
- String value = systemproperties.getProperty(keyString);
-
- if (keyString.equals("logdir"))
- {
- logdir = value;
- }
- else
- {
- if (keyString.startsWith("-X"))
- {
- commandLine += keyString + value + " ";
- }
- else
- {
- commandLine += "-D" + keyString + "=" + value + " ";
- }
- }
- }
-
- commandLine +=
- "${JAVA_OPTS} -cp " + testJar + " org.apache.qpid.junit.extensions.TKTestRunner " + testOptions + " ${ARGS}";
-
- getLog().info("Generating Script for test: " + testName);
- getLog().debug(commandLine);
-
- String fileName = scriptOutDirectory + "/" + testName + ".sh";
-
- try
- {
- File scriptFile = new File(fileName);
- Writer scriptWriter = new FileWriter(scriptFile);
- scriptWriter.write(_scriptLanguage);
- scriptWriter.write(_javaOptArgParser);
- if (logdir != null)
- {
- scriptWriter.write("mkdir -p " + logdir + "\n");
- }
-
- scriptWriter.write(commandLine);
- scriptWriter.flush();
- scriptWriter.close();
- }
- catch (IOException e)
- {
- getLog().error("Failed to write: " + fileName);
- }
- }
- }
-}
+/* + * + * 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.junit.maven; + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Properties; + +/** + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * </table> + * + * @author Rupert Smith + * @goal tkscriptgen + * @phase test + * @execute phase="test" + * @requiresDependencyResolution test + */ +public class TKTestScriptGenMojo extends AbstractMojo +{ + private static final String _scriptLanguage = "#!/bin/bash\n\n"; + + private static final String _javaOptArgParser = + "# Parse arguements taking all - prefixed args as JAVA_OPTS\n" + "for arg in \"$@\"; do\n" + + " if [[ $arg == -java:* ]]; then\n" + " JAVA_OPTS=\"${JAVA_OPTS}-`echo $arg|cut -d ':' -f 2` \"\n" + + " else\n" + " ARGS=\"${ARGS}$arg \"\n" + " fi\n" + "done\n\n"; + + /** + * Where to write out the scripts. + * + * @parameter + */ + private String scriptOutDirectory; + + /** + * The all-in-one test jar location. + * + * @parameter + */ + private String testJar; + + /** + * The system properties to pass to java runtime. + * + * @parameter + */ + private Properties systemproperties; + + /** + * The TKTest runner command lines. There are passed directly to the TKTestRunner main method. + * + * @parameter + */ + private Map<String, String> commands = new LinkedHashMap<String, String>(); + + /** + * Implementation of the tkscriptgen goal. + * + * @throws MojoExecutionException + */ + public void execute() throws MojoExecutionException + { + // Turn each of the test runner command lines into a script. + for (String testName : commands.keySet()) + { + String testOptions = commands.get(testName); + String commandLine = "java "; + + String logdir = null; + + for (Object key : systemproperties.keySet()) + { + String keyString = (String) key; + String value = systemproperties.getProperty(keyString); + + if (keyString.equals("logdir")) + { + logdir = value; + } + else + { + if (keyString.startsWith("-X")) + { + commandLine += keyString + value + " "; + } + else + { + commandLine += "-D" + keyString + "=" + value + " "; + } + } + } + + commandLine += + "${JAVA_OPTS} -cp " + testJar + " org.apache.qpid.junit.extensions.TKTestRunner " + testOptions + " ${ARGS}"; + + getLog().info("Generating Script for test: " + testName); + getLog().debug(commandLine); + + String fileName = scriptOutDirectory + "/" + testName + ".sh"; + + try + { + File scriptFile = new File(fileName); + Writer scriptWriter = new FileWriter(scriptFile); + scriptWriter.write(_scriptLanguage); + scriptWriter.write(_javaOptArgParser); + if (logdir != null) + { + scriptWriter.write("mkdir -p " + logdir + "\n"); + } + + scriptWriter.write(commandLine); + scriptWriter.flush(); + scriptWriter.close(); + } + catch (IOException e) + { + getLog().error("Failed to write: " + fileName); + } + } + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/DefaultThreadFactory.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/DefaultThreadFactory.java index 6c88d019c4..8fb0a6a90e 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/DefaultThreadFactory.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/DefaultThreadFactory.java @@ -1,48 +1,48 @@ -/*
- *
- * 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.junit.concurrency;
-
-import java.util.concurrent.ThreadFactory;
-
-/**
- * Implements a default thread factory.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Create default threads with no specialization.
- * </table>
- *
- * @author Rupert Smith
- */
-public class DefaultThreadFactory implements ThreadFactory
-{
- /**
- * Constructs a new <tt>Thread</tt>.
- *
- * @param r A runnable to be executed by new thread instance.
- *
- * @return The constructed thread.
- */
- public Thread newThread(Runnable r)
- {
- return new Thread(r);
- }
-}
+/* + * + * 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.junit.concurrency; + +import java.util.concurrent.ThreadFactory; + +/** + * Implements a default thread factory. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Create default threads with no specialization. + * </table> + * + * @author Rupert Smith + */ +public class DefaultThreadFactory implements ThreadFactory +{ + /** + * Constructs a new <tt>Thread</tt>. + * + * @param r A runnable to be executed by new thread instance. + * + * @return The constructed thread. + */ + public Thread newThread(Runnable r) + { + return new Thread(r); + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/PossibleDeadlockException.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/PossibleDeadlockException.java index 0bb07a4557..3bbfc2d502 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/PossibleDeadlockException.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/PossibleDeadlockException.java @@ -1,46 +1,46 @@ -/*
- *
- * 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.junit.concurrency;
-
-/**
- * PossibleDeadlockException is used to signal that two test threads being executed by a {@link ThreadTestCoordinator}
- * may be in a state of deadlock because they are mutually blocking each other or one is waiting on the other and the
- * other has been blocked elsewhere for longer than a specified timeout.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Signal a possible state of deadlock between coordinated test threads.
- * </table>
- *
- * @author Rupert Smith
- */
-public class PossibleDeadlockException extends RuntimeException
-{
- /**
- * Create a new possible deadlock execption.
- *
- * @param message The exception message.
- */
- public PossibleDeadlockException(String message)
- {
- super(message);
- }
-}
+/* + * + * 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.junit.concurrency; + +/** + * PossibleDeadlockException is used to signal that two test threads being executed by a {@link ThreadTestCoordinator} + * may be in a state of deadlock because they are mutually blocking each other or one is waiting on the other and the + * other has been blocked elsewhere for longer than a specified timeout. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Signal a possible state of deadlock between coordinated test threads. + * </table> + * + * @author Rupert Smith + */ +public class PossibleDeadlockException extends RuntimeException +{ + /** + * Create a new possible deadlock execption. + * + * @param message The exception message. + */ + public PossibleDeadlockException(String message) + { + super(message); + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/TestRunnable.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/TestRunnable.java index 5bf0c430cd..02e776a4ea 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/TestRunnable.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/TestRunnable.java @@ -1,239 +1,239 @@ -/*
- *
- * 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.junit.concurrency;
-
-/**
- * TestRunnable is an extension of java.util.Runnable that adds some features to make it easier to coordinate the
- * activities of threads in such a way as to expose bugs in multi threaded code.
- *
- * <p/>Sometimes several threads will run in a particular order so that a bug is not revealed. Other times the ordering
- * of the threads will expose a bug. Such bugs can be hard to replicate as the exact execution ordering of threads is not
- * usually controlled. This class adds some methods that allow threads to synchronize other threads, either allowing them
- * to run, or waiting for them to allow this thread to run. It also provides convenience methods to gather error messages
- * and exceptions from threads, which will often be reported in unit testing code.
- *
- * <p/>Coordination between threads is handled by the {@link ThreadTestCoordinator}. It is called through the convenience
- * methods {@link #allow} and {@link #waitFor}. Threads to be coordinated must be set up with the coordinator and assigned
- * integer ids. It is then possible to call the coordinator with an array of thread ids requesting that those threads
- * be allowed to continue, or to wait until one of them allows this thread to continue. The otherwise non-deterministic
- * execution order of threads can be controlled into a carefully determined sequence using these methods in order
- * to reproduce race conditions, dead locks, live locks, dirty reads, phantom reads, non repeatable reads and so on.
- *
- * <p/>When waiting for another thread to give a signal to continue it is sometimes the case that the other thread has
- * become blocked by the code under test. For example in testing for a dirty read (for example in database code),
- * thread 1 lets thread 2 perform a write but not commit it, then thread 2 lets thread 1 run and attempt to perform a
- * dirty read on its uncommitted write. Transaction synchronization code being tested against the possibility of a dirty
- * write may make use of snapshots in which case both threads should be able to read and write without blocking. It may
- * make use of explicit keys in which case thread 2 may become blocked on its write attempt because thread 1 holds a
- * read lock and it must wait until thread 1 completes its transaction before it can acquire this lock. The
- * {@link #waitFor} method accepts a boolean parameter to indicate that threads being blocked (other than on the
- * coordinator) can be interpreted the same as if the thread explicitly allows the thread calling waitFor to continue.
- * Using this technique a dirty read test could be written that works against either the snapshot or the locking
- * implementation, allowing both approaches to pass the test yet arranging for multiple threads to run against the
- * implementation in such a way that a potential dirty read bug is exposed.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Wait for another thread to allow this one to continue.
- * <tr><td> Allow another thread to continue.
- * <tr><td> Accumulate error messages.
- * <tr><td> Record exceptions from thread run.
- * <tr><td> Maintain link to thread coordinator.
- * <tr><td> Explicitly mark a thread with an integer id.
- * <tr><td> Maintian a flag to indicate whether or not this thread is waiting on the coordinator.
- * </table>
- *
- * @todo The allow then waitFor operations are very often used as a pair. So create a method allowAndWait that combines
- * them into a single method call.
- *
- * @author Rupert Smith
- */
-public abstract class TestRunnable implements Runnable
-{
- /** Holds a reference to the thread coordinator. */
- private ThreadTestCoordinator coordinator;
-
- /** Holds the explicit integer id of this thread. */
- private int id;
-
- /** Used to indicate that this thread is waiting on the coordinator and not elsewhere. */
- private boolean waitingOnCoordinator = false;
-
- /** Used to accumulate error messsages. */
- private String errorMessage = "";
-
- /** Holds the Java thread object that this is running under. */
- private Thread thisThread;
-
- /** Used to hold any exceptions resulting from the run method. */
- private Exception runException = null;
-
- /**
- * Implementations override this to perform coordinated thread sequencing.
- *
- * @throws Exception Any exception raised by the implementation will be caught by the default {@link #run()}
- * implementation for later querying by the {@link #getException()} method.
- */
- public abstract void runWithExceptions() throws Exception;
-
- /**
- * Provides a default implementation of the run method that allows exceptions to be thrown and keeps a record
- * of those exceptions. Defers to the {@link #runWithExceptions()} method to provide the thread body implementation
- * and catches any exceptions thrown by it.
- */
- public void run()
- {
- try
- {
- runWithExceptions();
- }
- catch (Exception e)
- {
- this.runException = e;
- }
- }
-
- /**
- * Attempt to consume an allow event from one of the specified threads and blocks until such an event occurrs.
- *
- * @param threads The set of threads that can allow this one to continue.
- * @param otherWaitIsAllow If set to <tt>true</tt> if the threads being waited on are blocked other than on
- * the coordinator itself then this is to be interpreted as allowing this thread to
- * continue.
- *
- * @return If the <tt>otherWaitIsAllow</tt> flag is set, then <tt>true</tt> is returned when the thread being waited on is found
- * to be blocked outside of the thread test coordinator. <tt>false</tt> under all other conditions.
- */
- protected boolean waitFor(int[] threads, boolean otherWaitIsAllow)
- {
- return coordinator.consumeAllowEvent(threads, otherWaitIsAllow, id, this);
- }
-
- /**
- * Produces allow events on each of the specified threads.
- *
- * @param threads The set of threads that are to be allowed to continue.
- */
- protected void allow(int[] threads)
- {
- coordinator.produceAllowEvents(threads, id, this);
- }
-
- /**
- * Keeps the error message for later reporting by the coordinator.
- *
- * @param message The error message to keep.
- */
- protected void addErrorMessage(String message)
- {
- errorMessage += message;
- }
-
- /**
- * Sets the coordinator for this thread.
- *
- * @param coordinator The coordinator for this thread.
- */
- void setCoordinator(ThreadTestCoordinator coordinator)
- {
- this.coordinator = coordinator;
- }
-
- /**
- * Reports whether or not this thread is waiting on the coordinator.
- *
- * @return <tt>If this thread is waiting on the coordinator.
- */
- boolean isWaitingOnCoordinator()
- {
- return waitingOnCoordinator;
- }
-
- /**
- * Sets the value of the waiting on coordinator flag.
- *
- * @param waiting The value of the waiting on coordinator flag.
- */
- void setWaitingOnCoordinator(boolean waiting)
- {
- waitingOnCoordinator = waiting;
- }
-
- /**
- * Sets up the explicit int id for this thread.
- *
- * @param id The integer id.
- */
- void setId(int id)
- {
- this.id = id;
- }
-
- /**
- * Reports any accumulated error messages.
- *
- * @return Any accumulated error messages.
- */
- String getErrorMessage()
- {
- return errorMessage;
- }
-
- /**
- * Reports any exception thrown by the {@link #runWithExceptions} method.
- *
- * @return Any exception thrown by the {@link #runWithExceptions} method.
- */
- Exception getException()
- {
- return runException;
- }
-
- /**
- * Sets the Java thread under which this runs.
- *
- * @param thread The Java thread under which this runs.
- */
- void setThread(Thread thread)
- {
- thisThread = thread;
- }
-
- /**
- * Gets the Java thread under which this runs.
- *
- * @return The Java thread under which this runs.
- */
- Thread getThread()
- {
- return thisThread;
- }
-
- /**
- * Provides a string summary of this test threads status.
- *
- * @return Summarizes this threads status.
- */
- public String toString()
- {
- return "id = " + id + ", waitingOnCoordinator = " + waitingOnCoordinator;
- }
-}
+/* + * + * 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.junit.concurrency; + +/** + * TestRunnable is an extension of java.util.Runnable that adds some features to make it easier to coordinate the + * activities of threads in such a way as to expose bugs in multi threaded code. + * + * <p/>Sometimes several threads will run in a particular order so that a bug is not revealed. Other times the ordering + * of the threads will expose a bug. Such bugs can be hard to replicate as the exact execution ordering of threads is not + * usually controlled. This class adds some methods that allow threads to synchronize other threads, either allowing them + * to run, or waiting for them to allow this thread to run. It also provides convenience methods to gather error messages + * and exceptions from threads, which will often be reported in unit testing code. + * + * <p/>Coordination between threads is handled by the {@link ThreadTestCoordinator}. It is called through the convenience + * methods {@link #allow} and {@link #waitFor}. Threads to be coordinated must be set up with the coordinator and assigned + * integer ids. It is then possible to call the coordinator with an array of thread ids requesting that those threads + * be allowed to continue, or to wait until one of them allows this thread to continue. The otherwise non-deterministic + * execution order of threads can be controlled into a carefully determined sequence using these methods in order + * to reproduce race conditions, dead locks, live locks, dirty reads, phantom reads, non repeatable reads and so on. + * + * <p/>When waiting for another thread to give a signal to continue it is sometimes the case that the other thread has + * become blocked by the code under test. For example in testing for a dirty read (for example in database code), + * thread 1 lets thread 2 perform a write but not commit it, then thread 2 lets thread 1 run and attempt to perform a + * dirty read on its uncommitted write. Transaction synchronization code being tested against the possibility of a dirty + * write may make use of snapshots in which case both threads should be able to read and write without blocking. It may + * make use of explicit keys in which case thread 2 may become blocked on its write attempt because thread 1 holds a + * read lock and it must wait until thread 1 completes its transaction before it can acquire this lock. The + * {@link #waitFor} method accepts a boolean parameter to indicate that threads being blocked (other than on the + * coordinator) can be interpreted the same as if the thread explicitly allows the thread calling waitFor to continue. + * Using this technique a dirty read test could be written that works against either the snapshot or the locking + * implementation, allowing both approaches to pass the test yet arranging for multiple threads to run against the + * implementation in such a way that a potential dirty read bug is exposed. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Wait for another thread to allow this one to continue. + * <tr><td> Allow another thread to continue. + * <tr><td> Accumulate error messages. + * <tr><td> Record exceptions from thread run. + * <tr><td> Maintain link to thread coordinator. + * <tr><td> Explicitly mark a thread with an integer id. + * <tr><td> Maintian a flag to indicate whether or not this thread is waiting on the coordinator. + * </table> + * + * @todo The allow then waitFor operations are very often used as a pair. So create a method allowAndWait that combines + * them into a single method call. + * + * @author Rupert Smith + */ +public abstract class TestRunnable implements Runnable +{ + /** Holds a reference to the thread coordinator. */ + private ThreadTestCoordinator coordinator; + + /** Holds the explicit integer id of this thread. */ + private int id; + + /** Used to indicate that this thread is waiting on the coordinator and not elsewhere. */ + private boolean waitingOnCoordinator = false; + + /** Used to accumulate error messsages. */ + private String errorMessage = ""; + + /** Holds the Java thread object that this is running under. */ + private Thread thisThread; + + /** Used to hold any exceptions resulting from the run method. */ + private Exception runException = null; + + /** + * Implementations override this to perform coordinated thread sequencing. + * + * @throws Exception Any exception raised by the implementation will be caught by the default {@link #run()} + * implementation for later querying by the {@link #getException()} method. + */ + public abstract void runWithExceptions() throws Exception; + + /** + * Provides a default implementation of the run method that allows exceptions to be thrown and keeps a record + * of those exceptions. Defers to the {@link #runWithExceptions()} method to provide the thread body implementation + * and catches any exceptions thrown by it. + */ + public void run() + { + try + { + runWithExceptions(); + } + catch (Exception e) + { + this.runException = e; + } + } + + /** + * Attempt to consume an allow event from one of the specified threads and blocks until such an event occurrs. + * + * @param threads The set of threads that can allow this one to continue. + * @param otherWaitIsAllow If set to <tt>true</tt> if the threads being waited on are blocked other than on + * the coordinator itself then this is to be interpreted as allowing this thread to + * continue. + * + * @return If the <tt>otherWaitIsAllow</tt> flag is set, then <tt>true</tt> is returned when the thread being waited on is found + * to be blocked outside of the thread test coordinator. <tt>false</tt> under all other conditions. + */ + protected boolean waitFor(int[] threads, boolean otherWaitIsAllow) + { + return coordinator.consumeAllowEvent(threads, otherWaitIsAllow, id, this); + } + + /** + * Produces allow events on each of the specified threads. + * + * @param threads The set of threads that are to be allowed to continue. + */ + protected void allow(int[] threads) + { + coordinator.produceAllowEvents(threads, id, this); + } + + /** + * Keeps the error message for later reporting by the coordinator. + * + * @param message The error message to keep. + */ + protected void addErrorMessage(String message) + { + errorMessage += message; + } + + /** + * Sets the coordinator for this thread. + * + * @param coordinator The coordinator for this thread. + */ + void setCoordinator(ThreadTestCoordinator coordinator) + { + this.coordinator = coordinator; + } + + /** + * Reports whether or not this thread is waiting on the coordinator. + * + * @return <tt>If this thread is waiting on the coordinator. + */ + boolean isWaitingOnCoordinator() + { + return waitingOnCoordinator; + } + + /** + * Sets the value of the waiting on coordinator flag. + * + * @param waiting The value of the waiting on coordinator flag. + */ + void setWaitingOnCoordinator(boolean waiting) + { + waitingOnCoordinator = waiting; + } + + /** + * Sets up the explicit int id for this thread. + * + * @param id The integer id. + */ + void setId(int id) + { + this.id = id; + } + + /** + * Reports any accumulated error messages. + * + * @return Any accumulated error messages. + */ + String getErrorMessage() + { + return errorMessage; + } + + /** + * Reports any exception thrown by the {@link #runWithExceptions} method. + * + * @return Any exception thrown by the {@link #runWithExceptions} method. + */ + Exception getException() + { + return runException; + } + + /** + * Sets the Java thread under which this runs. + * + * @param thread The Java thread under which this runs. + */ + void setThread(Thread thread) + { + thisThread = thread; + } + + /** + * Gets the Java thread under which this runs. + * + * @return The Java thread under which this runs. + */ + Thread getThread() + { + return thisThread; + } + + /** + * Provides a string summary of this test threads status. + * + * @return Summarizes this threads status. + */ + public String toString() + { + return "id = " + id + ", waitingOnCoordinator = " + waitingOnCoordinator; + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestCoordinator.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestCoordinator.java index 0be0fe37dc..605c35feed 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestCoordinator.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestCoordinator.java @@ -1,485 +1,485 @@ -/*
- *
- * 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.junit.concurrency;
-
-import org.apache.log4j.Logger;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.concurrent.ThreadFactory;
-
-/**
- * ThreadTestCoordinator provides an array of binary latches that allows threads to wait for other threads or to send
- * them a signal that allows them to continue running or to wait for another thread to signal them. The binary latch
- * array is always a square array, allowing one latch from and to every thread. Upon accepting an allow signal from one
- * sender the latches for all senders for a are cleared. This class is always used in conjunction with
- * {@link TestRunnable} for writing concurrent test code that coordinates multi-threaded activity in order to reproduce
- * concurrency bugs.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Accept test threads to coordinate.
- * <tr><td> Allow test threads to send 'allow to continue' signals.
- * <tr><td> Allow test threads to wait on this coordinator for 'allow to continue' signals.
- * <tr><td> Report error messages from test threads.
- * <tr><td> Report exceptions from test threads.
- * <tr><td> Provide method to wait until all test threads have completed.
- * </table>
- *
- * @todo This code was hacked together as a bit of an experiment, because I wasn't sure if this idea would work. It has
- * proved extremely usefull. Some documentation for this needs to be written to explain it better.
- *
- * @todo Consider how deadlock detection will be handled. If all threads are blocking on the coordinator, waiting for
- * each other, they are deadlocked and there is something wrong with the test code that put them in that
- * situation. If they are all blocked elsewhere, they may be deadlocked, or could just be waiting on some
- * external event. A timeout should be used. Timeout is already implemented, just need to sanity check how
- * this is working and document it.
- *
- * @todo Consider how livelock detection could be implemented? LockFree data structures might cause live locks. I
- * guess a longish timeout is the only thing that can be done for that.
- *
- * @todo Only course grained synchronous at the method class level can be obtained. This is because test code can
- * only insert synchronization points between method calls it makes. So this code will not be usefull for
- * checking sequences of events within methods, unless the code under test is explicitly instrumented for it.
- * It might be possible to instrument code by using labels, and then use the debugger/profiler interface to
- * put breakpoints on the labels and use them as synchronization points. Not perfect, but at the unused labels
- * can be left in the code, without altering its behaviour.
- *
- * @author Rupert Smith
- */
-public class ThreadTestCoordinator
-{
- /** Used for logging. */
- private static final Logger log = Logger.getLogger(ThreadTestCoordinator.class);
-
- /** Keeps track of the test threads by their ids. */
- private TestRunnable[] testThreads; // = new TestRunnable[2];
-
- /** An explicit thread monitor for the coordinator. Threads wait on the coordinator whilst waiting for events. */
- private final Object coordinatorLock = new Object();
-
- /** A set of monitors for each test thread. */
- private Object[] locks;
-
- /** The binary latch array, this is always a square array allowing one event from and to every thread. */
- private boolean[][] allowEvents;
-
- /** Keeps track of the number of threads being coordinated. */
- private int threadCount = 0;
-
- /** Accumulates any exceptions resulting from the threads run methods. */
- private Collection<Exception> exceptions = new ArrayList<Exception>();
-
- /**
- * Holds the deadlock timeout after which threads are given a runtime exception to signal that a potential
- * deadlock may be happening.
- */
- private long deadlockTimeout = 1000 * 1000000;
-
- /** Holds the factory to create test thread with. */
- private ThreadFactory threadFactory;
-
- /**
- * Creates a new test thread coordinator. The number of threads to run must be specified here.
- *
- * @param numThreads The number of threads to run.
- */
- public ThreadTestCoordinator(int numThreads)
- {
- this.threadCount = numThreads;
-
- // Create an array big enough to hold all the test threads.
- testThreads = new TestRunnable[threadCount];
-
- // Use the default thread factory, as none specified.
- threadFactory = new DefaultThreadFactory();
- }
-
- /**
- * Creates a new test thread coordinator with a specific thread factory. The number of threads to run must be
- * specified here.
- *
- * @param numThreads The number of threads to run.
- * @param threadFactory The factory to use to create the test threads.
- */
- public ThreadTestCoordinator(int numThreads, ThreadFactory threadFactory)
- {
- this.threadCount = numThreads;
-
- // Create an array big enough to hold all the test threads.
- testThreads = new TestRunnable[threadCount];
-
- // Use the specified thread factory.
- this.threadFactory = threadFactory;
- }
-
- /**
- * Adds a thread to this coordinator and assigns an id to it. The ids must be numbered sequentially from 0 and
- * it is up to the caller to do this.
- *
- * @param runnable The test thread.
- * @param id The explicit id to assign to the test thread.
- */
- public void addTestThread(TestRunnable runnable, int id)
- {
- testThreads[id] = runnable;
- runnable.setCoordinator(this);
- runnable.setId(id);
- }
-
- /**
- * Starts all the coordinated threads running.
- */
- public void run()
- {
- // Create the monitors for each thread.
- locks = new Object[threadCount];
-
- // Create an appropriately sized event queue to allow one event from and to each thread.
- allowEvents = new boolean[threadCount][threadCount];
-
- // Initialize the monitors and clear the event queues.
- for (int i = 0; i < locks.length; i++)
- {
- locks[i] = new Object();
-
- for (int j = 0; j < locks.length; j++)
- {
- allowEvents[i][j] = false;
- }
- }
-
- // Start all the threads running.
- for (TestRunnable nextRunnable : testThreads)
- {
- // Create a Java thread for the test thread.
- Thread newThread = threadFactory.newThread(nextRunnable);
- nextRunnable.setThread(newThread);
-
- // Start it running.
- newThread.start();
- }
- }
-
- /**
- * Waits until all the test threads have completed and returns any accumulated error messages from them. Any
- * exceptions thrown by their run methods are also kept at this point.
- *
- * @return The accumulated error messages from all the threads concatenated together.
- */
- public String joinAndRetrieveMessages()
- {
- // Create an empty error message.
- String errorMessage = "";
-
- // Join all the test threads.
- for (TestRunnable r : testThreads)
- {
- Thread t = r.getThread();
-
- try
- {
- t.join();
- }
- catch (InterruptedException e)
- { }
-
- // Add any accumulated error messages to the return value.
- errorMessage += r.getErrorMessage();
-
- // Keep any exceptions resulting from the threads run method.
- Exception e = r.getException();
-
- if (e != null)
- {
- exceptions.add(e);
- }
- }
-
- return errorMessage;
- }
-
- /**
- * Reports any accumulated exceptions from the test threads run methods. This method must be called after
- * {@link #joinAndRetrieveMessages}.
- *
- * @return Any accumulated exceptions from the test threads run methods. This method must be called after
- */
- public Collection<Exception> getExceptions()
- {
- return exceptions;
- }
-
- /**
- * Sets a timeout to break out of potential deadlocks. If all threads are waiting for other threads to send
- * them continue events for longer than this timeout then the threads are all terminated.
- *
- * @param millis The minimum time to allow to pass before breaking out of any potential deadlocks.
- *
- * @todo This has not been implemented yet. If a potential deadlock happens then the joinAndRetrieveMessages
- * method should throw a PotentialDeadlockException.
- */
- public void setDeadlockTimeout(long millis)
- {
- deadlockTimeout = millis * 1000000;
- }
-
- /**
- * Creates a set of 'allow to continue' events on the event queues of the specified threads.
- *
- * @param threads The set of threads to allow to continue.
- * @param callerId The explicit id of the calling test thread.
- * @param caller The calling test thread.
- */
- void produceAllowEvents(int[] threads, int callerId, TestRunnable caller)
- {
- // Generate some debugging messages. Very usefull to know how thread synchronization is progressing.
- String message = "Thread " + callerId + " is allowing threads [ ";
-
- for (int j = 0; j < threads.length; j++)
- {
- message += threads[j] + ((j < (threads.length - 1)) ? ", " : "");
- }
-
- message += " ] to continue.";
- log.debug(message);
-
- // For each allow event, synchronize on the threads lock then set the event flag to true.
- for (int id : threads)
- {
- // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
- // being blocked at this time.
- caller.setWaitingOnCoordinator(true);
-
- synchronized (locks[id])
- {
- // Release the wating on coordinator flag now that this thread is running again.
- caller.setWaitingOnCoordinator(false);
-
- // Send the allow to continue event to the receiving thread.
- allowEvents[id][callerId] = true;
- }
- }
-
- // Wake up any threads waiting on the coordinator lock to recheck their event queues.
- // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
- // being blocked at this time.
- caller.setWaitingOnCoordinator(true);
-
- synchronized (coordinatorLock)
- {
- // Release the wating on coordinator flag now that this thread is running again.
- caller.setWaitingOnCoordinator(false);
- coordinatorLock.notifyAll();
- }
- }
-
- /**
- * Consumes an 'allow to continue' from one of the specified threads or waits until one is available or in some
- * cases if one of the specified threads is blocked elsewhere to accept that as an 'allow to continue' event.
- *
- * @param threads The set of threads to accept an allow to continue event from.
- * @param otherWaitIsAllow Whether or not to accept threads being blocked elsewhere as permission to continue.
- * @param callerId The explicit id of the calling test thread.
- * @param caller The calling test thread.
- *
- * @return If the <tt>otherWaitIsAllow</tt> flag is set, then <tt>true</tt> is returned when the thread being waited on is found
- * to be blocked outside of the thread test coordinator. <tt>false</tt> under all other conditions.
- */
- boolean consumeAllowEvent(int[] threads, boolean otherWaitIsAllow, int callerId, TestRunnable caller)
- {
- // Generate some debugging messages. Very usefull to know how thread synchronization is progressing.
- String message = "Thread " + callerId + " is requesting threads [ ";
-
- // Record the time at which this method was called. Will be used for breaking out of potential deadlocks.
- long startTime = System.nanoTime();
-
- for (int j = 0; j < threads.length; j++)
- {
- message += threads[j] + ((j < (threads.length - 1)) ? ", " : "");
- }
-
- message += " ] to allow it to continue.";
- log.debug(message);
-
- // Loop until an allow to continue event is received.
- while (true)
- {
- // Look at all the allowing thread to see if one has created an event for consumption.
- for (int allowerId : threads)
- {
- // Get the threads lock for the event to consume.
- // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
- // being blocked at this time.
- caller.setWaitingOnCoordinator(true);
-
- synchronized (locks[callerId])
- {
- // Release the wating on coordinator flag now that this thread is running again.
- caller.setWaitingOnCoordinator(false);
-
- // Check if there is an event on the queue from the allowing thread to this one.
- if (allowEvents[callerId][allowerId])
- {
- log.debug("Found an allow event, thread " + allowerId + ", is allowing thread " + callerId
- + ", to continue.");
-
- // Consume all the allow events for this thread.
- /*for (int i = 0; i < allowEvents[callerId].length; i++)
- {
- allowEvents[callerId][i] = false;
- }*/
-
- // Consume just the event from the allower to the consumer, leaving other pending allow events alone.
- allowEvents[callerId][allowerId] = false;
-
- return false;
- }
- }
- }
-
- // If waiting elsewhere is to be interpreted as an 'allow to continue' event, then look at the thread status
- // for the threads being waited on to see if any are blocked on other resources.
- if (otherWaitIsAllow)
- {
- log.debug("Other wait is to be interpreted as an allow event.");
-
- // Look at all the potential allower threads.
- for (int allowerId : threads)
- {
- // Get the Java thread state for the allowing thread.
- Thread threadToTest = testThreads[allowerId].getThread();
- Thread.State state = threadToTest.getState();
-
- // Check if the thread is blocked and so a potential candidate for releasing this one.
- if ((state == Thread.State.BLOCKED) || (state == Thread.State.WAITING)
- || (state == Thread.State.TIMED_WAITING))
- {
- log.debug("Found an allower thread, id = " + allowerId + ", that is blocked or wating.");
-
- // Check that the allower thread is not waiting on the coordinator lock or any of the
- // individual thread locks. It must be waiting or blocked on another monitor.
- TestRunnable allowingRunnable = testThreads[allowerId];
- boolean isWaitingOnCoordinator = allowingRunnable.isWaitingOnCoordinator();
-
- if (!isWaitingOnCoordinator)
- {
- log.debug("The allower thread, id = " + allowerId
- + ", is blocked or waiting other than on the coordinator.");
-
- // Get the threads lock for the event to consume.
- caller.setWaitingOnCoordinator(true);
-
- synchronized (locks[callerId])
- {
- caller.setWaitingOnCoordinator(false);
-
- // Consume all the allow events for this thread.
- for (int i = 0; i < allowEvents[callerId].length; i++)
- {
- allowEvents[callerId][i] = false;
- }
-
- return true;
- }
- }
- else
- {
- log.debug("The waiting allower thread, " + allowerId
- + ", is waiting on the coordinator so does not allow thread " + callerId + " to continue.");
- }
- }
- }
- }
-
- // Keep waiting until an 'allow to continue' event can be consumed.
- try
- {
- // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
- // being blocked at this time.
- caller.setWaitingOnCoordinator(true);
-
- synchronized (coordinatorLock)
- {
- // Release the wating on coordinator flag now that this thread is running again.
- caller.setWaitingOnCoordinator(false);
-
- log.debug("Thread " + callerId + " is waiting on coordinator lock for more allow events.");
-
- // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
- // being blocked at this time.
- caller.setWaitingOnCoordinator(true);
- coordinatorLock.wait(10);
- }
- }
- catch (InterruptedException e)
- { }
-
- // Release the waiting on coordinator flag now that this thread is running again.
- caller.setWaitingOnCoordinator(false);
-
- // Check if this thread has been waiting for longer than the deadlock timeout and raise a possible
- // deadlock exception if so.
- long waitTime = System.nanoTime() - startTime;
- log.debug("Thread " + callerId + " has been waiting for " + (waitTime / 1000000) + " milliseconds.");
-
- if (waitTime > deadlockTimeout)
- {
- // Throw a possible deadlock exception.
- throw new PossibleDeadlockException("Possible deadlock due to timeout with state:\n" + this);
- }
-
- log.debug("Thread " + callerId + " has woken up, was waiting for more allow events to become available.");
- }
- }
-
- /**
- * Pretty prints the state of the thread test coordinator, for debugging purposes.
- *
- * @return Pretty printed state of the thread test coordinator.
- */
- public String toString()
- {
- String result = "[";
-
- for (int i = 0; i < allowEvents.length; i++)
- {
- for (int j = 0; j < allowEvents[i].length; j++)
- {
- result += allowEvents[i][j];
-
- result += (j < (allowEvents[i].length - 1)) ? ", " : "";
- }
-
- result += (i < (allowEvents.length - 1)) ? ",\n " : "";
- }
-
- result += "]";
-
- for (int i = 0; i < testThreads.length; i++)
- {
- result += "thread[" + i + "] = " + testThreads[i].toString();
- }
-
- return result;
- }
-
-}
+/* + * + * 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.junit.concurrency; + +import org.apache.log4j.Logger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.ThreadFactory; + +/** + * ThreadTestCoordinator provides an array of binary latches that allows threads to wait for other threads or to send + * them a signal that allows them to continue running or to wait for another thread to signal them. The binary latch + * array is always a square array, allowing one latch from and to every thread. Upon accepting an allow signal from one + * sender the latches for all senders for a are cleared. This class is always used in conjunction with + * {@link TestRunnable} for writing concurrent test code that coordinates multi-threaded activity in order to reproduce + * concurrency bugs. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Accept test threads to coordinate. + * <tr><td> Allow test threads to send 'allow to continue' signals. + * <tr><td> Allow test threads to wait on this coordinator for 'allow to continue' signals. + * <tr><td> Report error messages from test threads. + * <tr><td> Report exceptions from test threads. + * <tr><td> Provide method to wait until all test threads have completed. + * </table> + * + * @todo This code was hacked together as a bit of an experiment, because I wasn't sure if this idea would work. It has + * proved extremely usefull. Some documentation for this needs to be written to explain it better. + * + * @todo Consider how deadlock detection will be handled. If all threads are blocking on the coordinator, waiting for + * each other, they are deadlocked and there is something wrong with the test code that put them in that + * situation. If they are all blocked elsewhere, they may be deadlocked, or could just be waiting on some + * external event. A timeout should be used. Timeout is already implemented, just need to sanity check how + * this is working and document it. + * + * @todo Consider how livelock detection could be implemented? LockFree data structures might cause live locks. I + * guess a longish timeout is the only thing that can be done for that. + * + * @todo Only course grained synchronous at the method class level can be obtained. This is because test code can + * only insert synchronization points between method calls it makes. So this code will not be usefull for + * checking sequences of events within methods, unless the code under test is explicitly instrumented for it. + * It might be possible to instrument code by using labels, and then use the debugger/profiler interface to + * put breakpoints on the labels and use them as synchronization points. Not perfect, but at the unused labels + * can be left in the code, without altering its behaviour. + * + * @author Rupert Smith + */ +public class ThreadTestCoordinator +{ + /** Used for logging. */ + private static final Logger log = Logger.getLogger(ThreadTestCoordinator.class); + + /** Keeps track of the test threads by their ids. */ + private TestRunnable[] testThreads; // = new TestRunnable[2]; + + /** An explicit thread monitor for the coordinator. Threads wait on the coordinator whilst waiting for events. */ + private final Object coordinatorLock = new Object(); + + /** A set of monitors for each test thread. */ + private Object[] locks; + + /** The binary latch array, this is always a square array allowing one event from and to every thread. */ + private boolean[][] allowEvents; + + /** Keeps track of the number of threads being coordinated. */ + private int threadCount = 0; + + /** Accumulates any exceptions resulting from the threads run methods. */ + private Collection<Exception> exceptions = new ArrayList<Exception>(); + + /** + * Holds the deadlock timeout after which threads are given a runtime exception to signal that a potential + * deadlock may be happening. + */ + private long deadlockTimeout = 1000 * 1000000; + + /** Holds the factory to create test thread with. */ + private ThreadFactory threadFactory; + + /** + * Creates a new test thread coordinator. The number of threads to run must be specified here. + * + * @param numThreads The number of threads to run. + */ + public ThreadTestCoordinator(int numThreads) + { + this.threadCount = numThreads; + + // Create an array big enough to hold all the test threads. + testThreads = new TestRunnable[threadCount]; + + // Use the default thread factory, as none specified. + threadFactory = new DefaultThreadFactory(); + } + + /** + * Creates a new test thread coordinator with a specific thread factory. The number of threads to run must be + * specified here. + * + * @param numThreads The number of threads to run. + * @param threadFactory The factory to use to create the test threads. + */ + public ThreadTestCoordinator(int numThreads, ThreadFactory threadFactory) + { + this.threadCount = numThreads; + + // Create an array big enough to hold all the test threads. + testThreads = new TestRunnable[threadCount]; + + // Use the specified thread factory. + this.threadFactory = threadFactory; + } + + /** + * Adds a thread to this coordinator and assigns an id to it. The ids must be numbered sequentially from 0 and + * it is up to the caller to do this. + * + * @param runnable The test thread. + * @param id The explicit id to assign to the test thread. + */ + public void addTestThread(TestRunnable runnable, int id) + { + testThreads[id] = runnable; + runnable.setCoordinator(this); + runnable.setId(id); + } + + /** + * Starts all the coordinated threads running. + */ + public void run() + { + // Create the monitors for each thread. + locks = new Object[threadCount]; + + // Create an appropriately sized event queue to allow one event from and to each thread. + allowEvents = new boolean[threadCount][threadCount]; + + // Initialize the monitors and clear the event queues. + for (int i = 0; i < locks.length; i++) + { + locks[i] = new Object(); + + for (int j = 0; j < locks.length; j++) + { + allowEvents[i][j] = false; + } + } + + // Start all the threads running. + for (TestRunnable nextRunnable : testThreads) + { + // Create a Java thread for the test thread. + Thread newThread = threadFactory.newThread(nextRunnable); + nextRunnable.setThread(newThread); + + // Start it running. + newThread.start(); + } + } + + /** + * Waits until all the test threads have completed and returns any accumulated error messages from them. Any + * exceptions thrown by their run methods are also kept at this point. + * + * @return The accumulated error messages from all the threads concatenated together. + */ + public String joinAndRetrieveMessages() + { + // Create an empty error message. + String errorMessage = ""; + + // Join all the test threads. + for (TestRunnable r : testThreads) + { + Thread t = r.getThread(); + + try + { + t.join(); + } + catch (InterruptedException e) + { } + + // Add any accumulated error messages to the return value. + errorMessage += r.getErrorMessage(); + + // Keep any exceptions resulting from the threads run method. + Exception e = r.getException(); + + if (e != null) + { + exceptions.add(e); + } + } + + return errorMessage; + } + + /** + * Reports any accumulated exceptions from the test threads run methods. This method must be called after + * {@link #joinAndRetrieveMessages}. + * + * @return Any accumulated exceptions from the test threads run methods. This method must be called after + */ + public Collection<Exception> getExceptions() + { + return exceptions; + } + + /** + * Sets a timeout to break out of potential deadlocks. If all threads are waiting for other threads to send + * them continue events for longer than this timeout then the threads are all terminated. + * + * @param millis The minimum time to allow to pass before breaking out of any potential deadlocks. + * + * @todo This has not been implemented yet. If a potential deadlock happens then the joinAndRetrieveMessages + * method should throw a PotentialDeadlockException. + */ + public void setDeadlockTimeout(long millis) + { + deadlockTimeout = millis * 1000000; + } + + /** + * Creates a set of 'allow to continue' events on the event queues of the specified threads. + * + * @param threads The set of threads to allow to continue. + * @param callerId The explicit id of the calling test thread. + * @param caller The calling test thread. + */ + void produceAllowEvents(int[] threads, int callerId, TestRunnable caller) + { + // Generate some debugging messages. Very usefull to know how thread synchronization is progressing. + String message = "Thread " + callerId + " is allowing threads [ "; + + for (int j = 0; j < threads.length; j++) + { + message += threads[j] + ((j < (threads.length - 1)) ? ", " : ""); + } + + message += " ] to continue."; + log.debug(message); + + // For each allow event, synchronize on the threads lock then set the event flag to true. + for (int id : threads) + { + // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for + // being blocked at this time. + caller.setWaitingOnCoordinator(true); + + synchronized (locks[id]) + { + // Release the wating on coordinator flag now that this thread is running again. + caller.setWaitingOnCoordinator(false); + + // Send the allow to continue event to the receiving thread. + allowEvents[id][callerId] = true; + } + } + + // Wake up any threads waiting on the coordinator lock to recheck their event queues. + // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for + // being blocked at this time. + caller.setWaitingOnCoordinator(true); + + synchronized (coordinatorLock) + { + // Release the wating on coordinator flag now that this thread is running again. + caller.setWaitingOnCoordinator(false); + coordinatorLock.notifyAll(); + } + } + + /** + * Consumes an 'allow to continue' from one of the specified threads or waits until one is available or in some + * cases if one of the specified threads is blocked elsewhere to accept that as an 'allow to continue' event. + * + * @param threads The set of threads to accept an allow to continue event from. + * @param otherWaitIsAllow Whether or not to accept threads being blocked elsewhere as permission to continue. + * @param callerId The explicit id of the calling test thread. + * @param caller The calling test thread. + * + * @return If the <tt>otherWaitIsAllow</tt> flag is set, then <tt>true</tt> is returned when the thread being waited on is found + * to be blocked outside of the thread test coordinator. <tt>false</tt> under all other conditions. + */ + boolean consumeAllowEvent(int[] threads, boolean otherWaitIsAllow, int callerId, TestRunnable caller) + { + // Generate some debugging messages. Very usefull to know how thread synchronization is progressing. + String message = "Thread " + callerId + " is requesting threads [ "; + + // Record the time at which this method was called. Will be used for breaking out of potential deadlocks. + long startTime = System.nanoTime(); + + for (int j = 0; j < threads.length; j++) + { + message += threads[j] + ((j < (threads.length - 1)) ? ", " : ""); + } + + message += " ] to allow it to continue."; + log.debug(message); + + // Loop until an allow to continue event is received. + while (true) + { + // Look at all the allowing thread to see if one has created an event for consumption. + for (int allowerId : threads) + { + // Get the threads lock for the event to consume. + // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for + // being blocked at this time. + caller.setWaitingOnCoordinator(true); + + synchronized (locks[callerId]) + { + // Release the wating on coordinator flag now that this thread is running again. + caller.setWaitingOnCoordinator(false); + + // Check if there is an event on the queue from the allowing thread to this one. + if (allowEvents[callerId][allowerId]) + { + log.debug("Found an allow event, thread " + allowerId + ", is allowing thread " + callerId + + ", to continue."); + + // Consume all the allow events for this thread. + /*for (int i = 0; i < allowEvents[callerId].length; i++) + { + allowEvents[callerId][i] = false; + }*/ + + // Consume just the event from the allower to the consumer, leaving other pending allow events alone. + allowEvents[callerId][allowerId] = false; + + return false; + } + } + } + + // If waiting elsewhere is to be interpreted as an 'allow to continue' event, then look at the thread status + // for the threads being waited on to see if any are blocked on other resources. + if (otherWaitIsAllow) + { + log.debug("Other wait is to be interpreted as an allow event."); + + // Look at all the potential allower threads. + for (int allowerId : threads) + { + // Get the Java thread state for the allowing thread. + Thread threadToTest = testThreads[allowerId].getThread(); + Thread.State state = threadToTest.getState(); + + // Check if the thread is blocked and so a potential candidate for releasing this one. + if ((state == Thread.State.BLOCKED) || (state == Thread.State.WAITING) + || (state == Thread.State.TIMED_WAITING)) + { + log.debug("Found an allower thread, id = " + allowerId + ", that is blocked or wating."); + + // Check that the allower thread is not waiting on the coordinator lock or any of the + // individual thread locks. It must be waiting or blocked on another monitor. + TestRunnable allowingRunnable = testThreads[allowerId]; + boolean isWaitingOnCoordinator = allowingRunnable.isWaitingOnCoordinator(); + + if (!isWaitingOnCoordinator) + { + log.debug("The allower thread, id = " + allowerId + + ", is blocked or waiting other than on the coordinator."); + + // Get the threads lock for the event to consume. + caller.setWaitingOnCoordinator(true); + + synchronized (locks[callerId]) + { + caller.setWaitingOnCoordinator(false); + + // Consume all the allow events for this thread. + for (int i = 0; i < allowEvents[callerId].length; i++) + { + allowEvents[callerId][i] = false; + } + + return true; + } + } + else + { + log.debug("The waiting allower thread, " + allowerId + + ", is waiting on the coordinator so does not allow thread " + callerId + " to continue."); + } + } + } + } + + // Keep waiting until an 'allow to continue' event can be consumed. + try + { + // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for + // being blocked at this time. + caller.setWaitingOnCoordinator(true); + + synchronized (coordinatorLock) + { + // Release the wating on coordinator flag now that this thread is running again. + caller.setWaitingOnCoordinator(false); + + log.debug("Thread " + callerId + " is waiting on coordinator lock for more allow events."); + + // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for + // being blocked at this time. + caller.setWaitingOnCoordinator(true); + coordinatorLock.wait(10); + } + } + catch (InterruptedException e) + { } + + // Release the waiting on coordinator flag now that this thread is running again. + caller.setWaitingOnCoordinator(false); + + // Check if this thread has been waiting for longer than the deadlock timeout and raise a possible + // deadlock exception if so. + long waitTime = System.nanoTime() - startTime; + log.debug("Thread " + callerId + " has been waiting for " + (waitTime / 1000000) + " milliseconds."); + + if (waitTime > deadlockTimeout) + { + // Throw a possible deadlock exception. + throw new PossibleDeadlockException("Possible deadlock due to timeout with state:\n" + this); + } + + log.debug("Thread " + callerId + " has woken up, was waiting for more allow events to become available."); + } + } + + /** + * Pretty prints the state of the thread test coordinator, for debugging purposes. + * + * @return Pretty printed state of the thread test coordinator. + */ + public String toString() + { + String result = "["; + + for (int i = 0; i < allowEvents.length; i++) + { + for (int j = 0; j < allowEvents[i].length; j++) + { + result += allowEvents[i][j]; + + result += (j < (allowEvents[i].length - 1)) ? ", " : ""; + } + + result += (i < (allowEvents.length - 1)) ? ",\n " : ""; + } + + result += "]"; + + for (int i = 0; i < testThreads.length; i++) + { + result += "thread[" + i + "] = " + testThreads[i].toString(); + } + + return result; + } + +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestExample.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestExample.java index ef177a4255..b9865f2e22 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestExample.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestExample.java @@ -1,145 +1,145 @@ -/*
- *
- * 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.junit.concurrency;
-
-import org.apache.log4j.Logger;
-
-/**
- * An example to illustrate the use of the {@link ThreadTestCoordinator} and {@link TestRunnable}s.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Demo multi-threaded testing.
- * </table>
- *
- * @author Rupert Smith
- */
-public class ThreadTestExample
-{
- /** Used for logging. */
- private static final Logger log = Logger.getLogger(ThreadTestExample.class);
-
- /** Test thread 1. */
- TestRunnable testThread1 =
- new TestRunnable()
- {
- public void runWithExceptions() throws Exception
- {
- log.debug("public void run(): called");
- log.info("in testThread0, block 1");
-
- // Wait for t2 to allow t1 to continue.
- allow(new int[] { 1 });
- waitFor(new int[] { 1 }, false);
-
- log.info("in testThread0, block 2");
-
- // Wait for t2 to allow t1 to continue. T2 is allowed to be blocked elsewhere than giving explicit
- // permission to allow t1 to continue.
- allow(new int[] { 1 });
- waitFor(new int[] { 1 }, true);
-
- log.info("in testThread0, block 3");
-
- // Release thread 2 from waiting on the shared lock.
- synchronized (sharedLock)
- {
- sharedLock.notifyAll();
- }
-
- allow(new int[] { 1 });
- }
- };
-
- /** A shared lock between the test threads. */
- final Object sharedLock = new Object();
-
- /** Test thread 2. */
- TestRunnable testThread2 =
- new TestRunnable()
- {
- public void runWithExceptions() throws Exception
- {
- log.debug("public void run(): called");
- log.info("in testThread1, block 1");
-
- // Wait for t1 to allow t2 to continue.
- allow(new int[] { 0 });
- waitFor(new int[] { 0 }, false);
-
- log.info("in testThread1, block 2");
-
- // Wait on another resource. T1 should accept this as permission to continue.
- try
- {
- synchronized (sharedLock)
- {
- log.debug("in testThread1, waiting on shared lock.");
- sharedLock.wait();
- }
- }
- catch (InterruptedException e)
- {
- // Bail-out with a runtime if this happens.
- throw new RuntimeException("Interrupted whilst waiting for shared lock.", e);
- }
-
- log.info("in testThread1, finished waiting on shared lock.");
-
- // allow(new int[] { 0 });
-
- // Wait for t1 to allow t2 to continue.
- waitFor(new int[] { 0 }, false);
-
- log.info("in testThread1, block 3");
-
- allow(new int[] { 0 });
- }
- };
-
- /**
- * Executes the test threads with coordination.
- *
- * @param args Ignored.
- */
- public void main(String[] args)
- {
- ThreadTestCoordinator tt = new ThreadTestCoordinator(2);
-
- tt.addTestThread(testThread1, 0);
- tt.addTestThread(testThread2, 1);
- tt.setDeadlockTimeout(500);
- tt.run();
-
- String errorMessage = tt.joinAndRetrieveMessages();
-
- // Print any error messages or exceptions.
- log.info(errorMessage);
-
- if (!tt.getExceptions().isEmpty())
- {
- for (Exception e : tt.getExceptions())
- {
- log.warn("Exception thrown during test thread: ", e);
- }
- }
- }
-}
+/* + * + * 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.junit.concurrency; + +import org.apache.log4j.Logger; + +/** + * An example to illustrate the use of the {@link ThreadTestCoordinator} and {@link TestRunnable}s. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Demo multi-threaded testing. + * </table> + * + * @author Rupert Smith + */ +public class ThreadTestExample +{ + /** Used for logging. */ + private static final Logger log = Logger.getLogger(ThreadTestExample.class); + + /** Test thread 1. */ + TestRunnable testThread1 = + new TestRunnable() + { + public void runWithExceptions() throws Exception + { + log.debug("public void run(): called"); + log.info("in testThread0, block 1"); + + // Wait for t2 to allow t1 to continue. + allow(new int[] { 1 }); + waitFor(new int[] { 1 }, false); + + log.info("in testThread0, block 2"); + + // Wait for t2 to allow t1 to continue. T2 is allowed to be blocked elsewhere than giving explicit + // permission to allow t1 to continue. + allow(new int[] { 1 }); + waitFor(new int[] { 1 }, true); + + log.info("in testThread0, block 3"); + + // Release thread 2 from waiting on the shared lock. + synchronized (sharedLock) + { + sharedLock.notifyAll(); + } + + allow(new int[] { 1 }); + } + }; + + /** A shared lock between the test threads. */ + final Object sharedLock = new Object(); + + /** Test thread 2. */ + TestRunnable testThread2 = + new TestRunnable() + { + public void runWithExceptions() throws Exception + { + log.debug("public void run(): called"); + log.info("in testThread1, block 1"); + + // Wait for t1 to allow t2 to continue. + allow(new int[] { 0 }); + waitFor(new int[] { 0 }, false); + + log.info("in testThread1, block 2"); + + // Wait on another resource. T1 should accept this as permission to continue. + try + { + synchronized (sharedLock) + { + log.debug("in testThread1, waiting on shared lock."); + sharedLock.wait(); + } + } + catch (InterruptedException e) + { + // Bail-out with a runtime if this happens. + throw new RuntimeException("Interrupted whilst waiting for shared lock.", e); + } + + log.info("in testThread1, finished waiting on shared lock."); + + // allow(new int[] { 0 }); + + // Wait for t1 to allow t2 to continue. + waitFor(new int[] { 0 }, false); + + log.info("in testThread1, block 3"); + + allow(new int[] { 0 }); + } + }; + + /** + * Executes the test threads with coordination. + * + * @param args Ignored. + */ + public void main(String[] args) + { + ThreadTestCoordinator tt = new ThreadTestCoordinator(2); + + tt.addTestThread(testThread1, 0); + tt.addTestThread(testThread2, 1); + tt.setDeadlockTimeout(500); + tt.run(); + + String errorMessage = tt.joinAndRetrieveMessages(); + + // Print any error messages or exceptions. + log.info(errorMessage); + + if (!tt.getExceptions().isEmpty()) + { + for (Exception e : tt.getExceptions()) + { + log.warn("Exception thrown during test thread: ", e); + } + } + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestCase.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestCase.java index 03e465695e..58a7f60f3c 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestCase.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestCase.java @@ -1,303 +1,303 @@ -/*
- *
- * 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.junit.extensions;
-
-import junit.framework.TestCase;
-
-import org.apache.log4j.Logger;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-
-/**
- * AsymptoticTestCase is an extension of TestCase for writing unit tests to analyze asymptotic time and space behaviour.
- *
- * <p>ParameterizedTestCases allow tests to be defined which have test methods that take a single int argument. Normal
- * JUnit test methods do not take any arguments. This int argument can be interpreted in any way by the test but it is
- * intended to denote the 'size' of the test to be run. For example, when testing the performance of a data structure
- * for different numbers of data elements held in the data structure the int parameter should be interpreted as the
- * number of elements. Test timings for different numbers of elements can then be captured and the asymptotic behaviour
- * of the data structure with respect to time analyzed. Any non-parameterized tests defined in extensions of this class
- * will also be run.
- *
- * <p>TestCases derived from this class may also define tear down methods to clean up their memory usage. This is
- * intended to be used in conjunction with memory listeners that report the amount of memory a test uses. The idea is
- * to write a test that allocates memory in the main test method in such a way that it leaves that memory still
- * allocated at the end of the test. The amount of memory used can then be measured before calling the tear down method
- * to clean it up. In the data structure example above, a test will allocate as many elements as are requested by the
- * int parameter and deallocate them in the tear down method. In this way memory readings for different numbers of
- * elements can be captured and the asymptotic behaviour of the data structure with respect to space analyzed.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Store the current int parameter value. <td> {@link TKTestResult} and see {@link AsymptoticTestDecorator} too.
- * <tr><td> Invoke parameterized test methods.
- * </table>
- *
- * @todo If possible try to move the code that invokes the test and setup/teardown methods into {@link TKTestResult} or
- * {@link AsymptoticTestDecorator} rather than this class. This would mean that tests don't have to extend this
- * class to do time and space performance analysis, these methods could be added to any JUnit TestCase class
- * instead. This would be an improvement because existing unit tests wouldn't have to extend a different class to
- * work with this extension, and also tests that extend other junit extension classes could have parameterized
- * and tear down methods too.
- *
- * @author Rupert Smith
- */
-public class AsymptoticTestCase extends TestCase implements InstrumentedTest
-{
- /** Used for logging. */
- private static final Logger log = Logger.getLogger(AsymptoticTestCase.class);
-
- /** The name of the test case. */
- private String testCaseName;
-
- /** Thread local for holding measurements on a per thread basis. */
- ThreadLocal<TestMeasurements> threadLocalMeasurement =
- new ThreadLocal<TestMeasurements>()
- {
- /**
- * Sets up a default set test measurements (zeroed, apart from the size param which defaults to 1).
- *
- * @return A set of default test measurements.
- */
- protected synchronized TestMeasurements initialValue()
- {
- return new TestMeasurements();
- }
- };
-
- /**
- * Constructs a test case with the given name.
- *
- * @param name The name of the test.
- */
- public AsymptoticTestCase(String name)
- {
- super(name);
-
- log.debug("public AsymptoticTestCase(String " + name + "): called");
- testCaseName = name;
- }
-
- /**
- * Gets the current value of the integer parameter to be passed to the parameterized test.
- *
- * @return The current value of the integer parameter.
- */
- public int getN()
- {
- log.debug("public int getN(): called");
- int n = threadLocalMeasurement.get().n;
-
- log.debug("return: " + n);
-
- return n;
- }
-
- /**
- * Sets the current value of the integer parameter to be passed to the parameterized test.
- *
- * @param n The new current value of the integer parameter.
- */
- public void setN(int n)
- {
- log.debug("public void setN(int " + n + "): called");
- threadLocalMeasurement.get().n = n;
- }
-
- /**
- * Reports how long the test took to run.
- *
- * @return The time in milliseconds that the test took to run.
- */
- public long getTestTime()
- {
- log.debug("public long getTestTime(): called");
- long startTime = threadLocalMeasurement.get().startTime;
- long endTime = threadLocalMeasurement.get().endTime;
- long testTime = endTime - startTime;
-
- log.debug("return: " + testTime);
-
- return testTime;
- }
-
- /**
- * Reports the memory usage at the start of the test.
- *
- * @return The memory usage at the start of the test.
- */
- public long getTestStartMemory()
- {
- // log.debug("public long getTestStartMemory(): called");
- long startMem = threadLocalMeasurement.get().startMem;
-
- // log.debug("return: " + startMem);
-
- return startMem;
- }
-
- /**
- * Reports the memory usage at the end of the test.
- *
- * @return The memory usage at the end of the test.
- */
- public long getTestEndMemory()
- {
- // log.debug("public long getTestEndMemory(): called");
- long endMem = threadLocalMeasurement.get().endMem;
-
- // log.debug("return: " + endMem);
- return endMem;
- }
-
- /**
- * Resets the instrumentation values to zero, and nulls any references to held measurements so that the memory
- * can be reclaimed.
- */
- public void reset()
- {
- log.debug("public void reset(): called");
- threadLocalMeasurement.remove();
- }
-
- /**
- * Runs the test method for this test case.
- *
- * @throws Throwable Any Throwables from the test methods invoked are allowed to fall through.
- */
- protected void runTest() throws Throwable
- {
- log.debug("protected void runTest(): called");
-
- // Check that a test name has been set. This is used to define which method to run.
- assertNotNull(testCaseName);
- log.debug("testCaseName = " + testCaseName);
-
- // Try to get the method with matching name.
- Method runMethod = null;
- boolean isParameterized = false;
-
- // Check if a parameterized test method is available.
- try
- {
- // Use getMethod to get all public inherited methods. getDeclaredMethods returns all
- // methods of this class but excludes the inherited ones.
- runMethod = getClass().getMethod(testCaseName, int.class);
- isParameterized = true;
- }
- catch (NoSuchMethodException e)
- {
- // log.debug("Parameterized method \"" + testCaseName + "\" not found.");
- // Set run method to null (it already will be but...) to indicate that no parameterized method
- // version could be found.
- runMethod = null;
- }
-
- // If no parameterized method is available, try and get the unparameterized method.
- if (runMethod == null)
- {
- try
- {
- runMethod = getClass().getMethod(testCaseName);
- isParameterized = false;
-
- }
- catch (NoSuchMethodException e)
- {
- fail("Method \"" + testCaseName + "\" not found.");
- }
- }
-
- // Check that the method is publicly accessable.
- if (!Modifier.isPublic(runMethod.getModifiers()))
- {
- fail("Method \"" + testCaseName + "\" should be public.");
- }
-
- // Try to execute the method, passing it the current int parameter value. Allow any invocation exceptions or
- // resulting exceptions from the method to fall through.
- try
- {
- Integer paramN = getN();
- log.debug("paramN = " + paramN);
-
- // Calculate parameters for parameterized tests so new does not get called during memory measurement.
- Object[] params = new Object[] { paramN };
-
- // Take the test start memory and start time.
- threadLocalMeasurement.get().startMem = 0; // SizeOf.getUsedMemory();
-
- threadLocalMeasurement.get().startTime = System.nanoTime();
-
- if (isParameterized)
- {
- runMethod.invoke(this, params);
- }
- else
- {
- runMethod.invoke(this);
- }
- }
- catch (InvocationTargetException e)
- {
- e.fillInStackTrace();
- throw e.getTargetException();
- }
- catch (IllegalAccessException e)
- {
- e.fillInStackTrace();
- throw e;
- }
- finally
- {
- // Take the test end memory and end time and calculate how long it took to run.
- long endTime = System.nanoTime();
- threadLocalMeasurement.get().endTime = endTime;
- log.debug("startTime = " + threadLocalMeasurement.get().startTime + ", endTime = " + endTime + ", testTime = "
- + getTestTime());
-
- threadLocalMeasurement.get().endMem = 0; // SizeOf.getUsedMemory();
- }
- }
-
- /**
- * The test parameters, encapsulated as a unit for attaching on a per thread basis.
- */
- private static class TestMeasurements
- {
- /** Holds the current value of the integer parameter to run tests on. */
- public int n = 1;
-
- /** Holds the test start memory. */
- public long startTime = 0;
-
- /** Holds the test end memory. */
- public long endTime = 0;
-
- /** Holds the test start memory. */
- public long startMem = 0;
-
- /** Holds the test end memory. */
- public long endMem = 0;
- }
-}
+/* + * + * 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.junit.extensions; + +import junit.framework.TestCase; + +import org.apache.log4j.Logger; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * AsymptoticTestCase is an extension of TestCase for writing unit tests to analyze asymptotic time and space behaviour. + * + * <p>ParameterizedTestCases allow tests to be defined which have test methods that take a single int argument. Normal + * JUnit test methods do not take any arguments. This int argument can be interpreted in any way by the test but it is + * intended to denote the 'size' of the test to be run. For example, when testing the performance of a data structure + * for different numbers of data elements held in the data structure the int parameter should be interpreted as the + * number of elements. Test timings for different numbers of elements can then be captured and the asymptotic behaviour + * of the data structure with respect to time analyzed. Any non-parameterized tests defined in extensions of this class + * will also be run. + * + * <p>TestCases derived from this class may also define tear down methods to clean up their memory usage. This is + * intended to be used in conjunction with memory listeners that report the amount of memory a test uses. The idea is + * to write a test that allocates memory in the main test method in such a way that it leaves that memory still + * allocated at the end of the test. The amount of memory used can then be measured before calling the tear down method + * to clean it up. In the data structure example above, a test will allocate as many elements as are requested by the + * int parameter and deallocate them in the tear down method. In this way memory readings for different numbers of + * elements can be captured and the asymptotic behaviour of the data structure with respect to space analyzed. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Store the current int parameter value. <td> {@link TKTestResult} and see {@link AsymptoticTestDecorator} too. + * <tr><td> Invoke parameterized test methods. + * </table> + * + * @todo If possible try to move the code that invokes the test and setup/teardown methods into {@link TKTestResult} or + * {@link AsymptoticTestDecorator} rather than this class. This would mean that tests don't have to extend this + * class to do time and space performance analysis, these methods could be added to any JUnit TestCase class + * instead. This would be an improvement because existing unit tests wouldn't have to extend a different class to + * work with this extension, and also tests that extend other junit extension classes could have parameterized + * and tear down methods too. + * + * @author Rupert Smith + */ +public class AsymptoticTestCase extends TestCase implements InstrumentedTest +{ + /** Used for logging. */ + private static final Logger log = Logger.getLogger(AsymptoticTestCase.class); + + /** The name of the test case. */ + private String testCaseName; + + /** Thread local for holding measurements on a per thread basis. */ + ThreadLocal<TestMeasurements> threadLocalMeasurement = + new ThreadLocal<TestMeasurements>() + { + /** + * Sets up a default set test measurements (zeroed, apart from the size param which defaults to 1). + * + * @return A set of default test measurements. + */ + protected synchronized TestMeasurements initialValue() + { + return new TestMeasurements(); + } + }; + + /** + * Constructs a test case with the given name. + * + * @param name The name of the test. + */ + public AsymptoticTestCase(String name) + { + super(name); + + log.debug("public AsymptoticTestCase(String " + name + "): called"); + testCaseName = name; + } + + /** + * Gets the current value of the integer parameter to be passed to the parameterized test. + * + * @return The current value of the integer parameter. + */ + public int getN() + { + log.debug("public int getN(): called"); + int n = threadLocalMeasurement.get().n; + + log.debug("return: " + n); + + return n; + } + + /** + * Sets the current value of the integer parameter to be passed to the parameterized test. + * + * @param n The new current value of the integer parameter. + */ + public void setN(int n) + { + log.debug("public void setN(int " + n + "): called"); + threadLocalMeasurement.get().n = n; + } + + /** + * Reports how long the test took to run. + * + * @return The time in milliseconds that the test took to run. + */ + public long getTestTime() + { + log.debug("public long getTestTime(): called"); + long startTime = threadLocalMeasurement.get().startTime; + long endTime = threadLocalMeasurement.get().endTime; + long testTime = endTime - startTime; + + log.debug("return: " + testTime); + + return testTime; + } + + /** + * Reports the memory usage at the start of the test. + * + * @return The memory usage at the start of the test. + */ + public long getTestStartMemory() + { + // log.debug("public long getTestStartMemory(): called"); + long startMem = threadLocalMeasurement.get().startMem; + + // log.debug("return: " + startMem); + + return startMem; + } + + /** + * Reports the memory usage at the end of the test. + * + * @return The memory usage at the end of the test. + */ + public long getTestEndMemory() + { + // log.debug("public long getTestEndMemory(): called"); + long endMem = threadLocalMeasurement.get().endMem; + + // log.debug("return: " + endMem); + return endMem; + } + + /** + * Resets the instrumentation values to zero, and nulls any references to held measurements so that the memory + * can be reclaimed. + */ + public void reset() + { + log.debug("public void reset(): called"); + threadLocalMeasurement.remove(); + } + + /** + * Runs the test method for this test case. + * + * @throws Throwable Any Throwables from the test methods invoked are allowed to fall through. + */ + protected void runTest() throws Throwable + { + log.debug("protected void runTest(): called"); + + // Check that a test name has been set. This is used to define which method to run. + assertNotNull(testCaseName); + log.debug("testCaseName = " + testCaseName); + + // Try to get the method with matching name. + Method runMethod = null; + boolean isParameterized = false; + + // Check if a parameterized test method is available. + try + { + // Use getMethod to get all public inherited methods. getDeclaredMethods returns all + // methods of this class but excludes the inherited ones. + runMethod = getClass().getMethod(testCaseName, int.class); + isParameterized = true; + } + catch (NoSuchMethodException e) + { + // log.debug("Parameterized method \"" + testCaseName + "\" not found."); + // Set run method to null (it already will be but...) to indicate that no parameterized method + // version could be found. + runMethod = null; + } + + // If no parameterized method is available, try and get the unparameterized method. + if (runMethod == null) + { + try + { + runMethod = getClass().getMethod(testCaseName); + isParameterized = false; + + } + catch (NoSuchMethodException e) + { + fail("Method \"" + testCaseName + "\" not found."); + } + } + + // Check that the method is publicly accessable. + if (!Modifier.isPublic(runMethod.getModifiers())) + { + fail("Method \"" + testCaseName + "\" should be public."); + } + + // Try to execute the method, passing it the current int parameter value. Allow any invocation exceptions or + // resulting exceptions from the method to fall through. + try + { + Integer paramN = getN(); + log.debug("paramN = " + paramN); + + // Calculate parameters for parameterized tests so new does not get called during memory measurement. + Object[] params = new Object[] { paramN }; + + // Take the test start memory and start time. + threadLocalMeasurement.get().startMem = 0; // SizeOf.getUsedMemory(); + + threadLocalMeasurement.get().startTime = System.nanoTime(); + + if (isParameterized) + { + runMethod.invoke(this, params); + } + else + { + runMethod.invoke(this); + } + } + catch (InvocationTargetException e) + { + e.fillInStackTrace(); + throw e.getTargetException(); + } + catch (IllegalAccessException e) + { + e.fillInStackTrace(); + throw e; + } + finally + { + // Take the test end memory and end time and calculate how long it took to run. + long endTime = System.nanoTime(); + threadLocalMeasurement.get().endTime = endTime; + log.debug("startTime = " + threadLocalMeasurement.get().startTime + ", endTime = " + endTime + ", testTime = " + + getTestTime()); + + threadLocalMeasurement.get().endMem = 0; // SizeOf.getUsedMemory(); + } + } + + /** + * The test parameters, encapsulated as a unit for attaching on a per thread basis. + */ + private static class TestMeasurements + { + /** Holds the current value of the integer parameter to run tests on. */ + public int n = 1; + + /** Holds the test start memory. */ + public long startTime = 0; + + /** Holds the test end memory. */ + public long endTime = 0; + + /** Holds the test start memory. */ + public long startMem = 0; + + /** Holds the test end memory. */ + public long endMem = 0; + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestDecorator.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestDecorator.java index e99f904331..4faa58688f 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestDecorator.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestDecorator.java @@ -1,170 +1,170 @@ -/*
- *
- * 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.junit.extensions;
-
-import junit.framework.TestResult;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.junit.extensions.util.MathUtils;
-
-/**
- * A Decorator that runs a test repeatedly on an increasing int parameter, or for a fixed number of repeats. If both
- * a set of integer parameters and a repeat count are specified, then each test is run for the repeat count at each
- * integer parameter.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Repeat a test for each of a set of integer parameters. <td> {@link TKTestResult}
- * <tr><td> Repeat a test multiple times.
- * <tr><td>
- * </table>
- *
- * @author Rupert Smith
- */
-public class AsymptoticTestDecorator extends WrappedSuiteTestDecorator
-{
- /** Used for logging. */
- private static final Logger log = Logger.getLogger(AsymptoticTestDecorator.class);
-
- /** The int size parameters to run the test with. */
- private int[] params;
-
- /** The number of times the whole test should be repeated. */
- private int repeat;
-
- /**
- * Creates an asymptotic test decorator that wraps a test with repeats and a set of integer 'size' paramters
- * to call the test with.
- *
- * @param test The test to wrap.
- * @param params The integer 'size' parameters.
- * @param repeat The number of times to repeat the test.
- */
- public AsymptoticTestDecorator(WrappedSuiteTestDecorator test, int[] params, int repeat)
- {
- super(test);
-
- log.debug("public AsymptoticTestDecorator(Test \"" + test + "\", int[] "
- + ((params == null) ? null : MathUtils.printArray(params)) + ", int " + repeat + "): called");
-
- this.params = params;
- this.repeat = repeat;
- }
-
- /**
- * Creates a new AsymptoticTestDecorator object.
- *
- * @param test The test to decorate.
- * @param start The starting asymptotic integer parameter value.
- * @param end The ending asymptotic integer parameter value.
- * @param step The increment size to move from the start to end values by.
- * @param repeat The number of times to repeat the test at each step of the cycle.
- */
- public AsymptoticTestDecorator(WrappedSuiteTestDecorator test, int start, int end, int step, int repeat)
- {
- super(test);
-
- if (start < 0)
- {
- throw new IllegalArgumentException("Start must be >= 0");
- }
-
- if (end < start)
- {
- throw new IllegalArgumentException("End must be >= start");
- }
-
- if (step < 1)
- {
- throw new IllegalArgumentException("Step must be >= 1");
- }
-
- if (repeat < 1)
- {
- throw new IllegalArgumentException("Repeat must be >= 1");
- }
-
- // Generate the sequence.
- params = new int[((end - start) / step) + 1];
- int i = 0;
- for (int n = start; n <= end; n += step)
- {
- params[i++] = n;
- }
-
- this.repeat = repeat;
- }
-
- /**
- * Runs the test repeatedly for each value of the int parameter specified and for the correct number of test
- * repeats.
- *
- * @param result The test result object that the tests will indicate their results to. This is also used
- * to pass the int parameter from this class to the decorated test class.
- */
- public void run(TestResult result)
- {
- log.debug("public void run(TestResult result): called");
-
- if (!(result instanceof TKTestResult))
- {
- throw new IllegalArgumentException("AsymptoticTestDecorator only works with TKTestResult");
- }
-
- // Cast the test result into a TKTestResult to place the current parameter into.
- TKTestResult tkResult = (TKTestResult) result;
-
- log.debug("params = " + ((params == null) ? null : MathUtils.printArray(params)));
- log.debug("repeat = " + repeat);
-
- for (int n : params)
- {
- for (int j = 0; j < repeat; j++)
- {
- log.debug("n = " + n);
-
- // Set the integer parameter in the TKTestResult to be passed to the tests.
- tkResult.setN(n);
-
- if (tkResult.shouldStop())
- {
- log.debug("tkResult.shouldStop = " + true);
-
- break;
- }
-
- log.debug("Calling super#run");
- super.run(tkResult);
- }
- }
- }
-
- /**
- * Prints out the name of this test with the string "(parameterized)" appended onto it for debugging purposes.
- *
- * @return The name of this test with the string "(parameterized)" appended onto it.
- */
- public String toString()
- {
- return super.toString() + "(parameterized)";
- }
-}
+/* + * + * 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.junit.extensions; + +import junit.framework.TestResult; + +import org.apache.log4j.Logger; + +import org.apache.qpid.junit.extensions.util.MathUtils; + +/** + * A Decorator that runs a test repeatedly on an increasing int parameter, or for a fixed number of repeats. If both + * a set of integer parameters and a repeat count are specified, then each test is run for the repeat count at each + * integer parameter. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Repeat a test for each of a set of integer parameters. <td> {@link TKTestResult} + * <tr><td> Repeat a test multiple times. + * <tr><td> + * </table> + * + * @author Rupert Smith + */ +public class AsymptoticTestDecorator extends WrappedSuiteTestDecorator +{ + /** Used for logging. */ + private static final Logger log = Logger.getLogger(AsymptoticTestDecorator.class); + + /** The int size parameters to run the test with. */ + private int[] params; + + /** The number of times the whole test should be repeated. */ + private int repeat; + + /** + * Creates an asymptotic test decorator that wraps a test with repeats and a set of integer 'size' paramters + * to call the test with. + * + * @param test The test to wrap. + * @param params The integer 'size' parameters. + * @param repeat The number of times to repeat the test. + */ + public AsymptoticTestDecorator(WrappedSuiteTestDecorator test, int[] params, int repeat) + { + super(test); + + log.debug("public AsymptoticTestDecorator(Test \"" + test + "\", int[] " + + ((params == null) ? null : MathUtils.printArray(params)) + ", int " + repeat + "): called"); + + this.params = params; + this.repeat = repeat; + } + + /** + * Creates a new AsymptoticTestDecorator object. + * + * @param test The test to decorate. + * @param start The starting asymptotic integer parameter value. + * @param end The ending asymptotic integer parameter value. + * @param step The increment size to move from the start to end values by. + * @param repeat The number of times to repeat the test at each step of the cycle. + */ + public AsymptoticTestDecorator(WrappedSuiteTestDecorator test, int start, int end, int step, int repeat) + { + super(test); + + if (start < 0) + { + throw new IllegalArgumentException("Start must be >= 0"); + } + + if (end < start) + { + throw new IllegalArgumentException("End must be >= start"); + } + + if (step < 1) + { + throw new IllegalArgumentException("Step must be >= 1"); + } + + if (repeat < 1) + { + throw new IllegalArgumentException("Repeat must be >= 1"); + } + + // Generate the sequence. + params = new int[((end - start) / step) + 1]; + int i = 0; + for (int n = start; n <= end; n += step) + { + params[i++] = n; + } + + this.repeat = repeat; + } + + /** + * Runs the test repeatedly for each value of the int parameter specified and for the correct number of test + * repeats. + * + * @param result The test result object that the tests will indicate their results to. This is also used + * to pass the int parameter from this class to the decorated test class. + */ + public void run(TestResult result) + { + log.debug("public void run(TestResult result): called"); + + if (!(result instanceof TKTestResult)) + { + throw new IllegalArgumentException("AsymptoticTestDecorator only works with TKTestResult"); + } + + // Cast the test result into a TKTestResult to place the current parameter into. + TKTestResult tkResult = (TKTestResult) result; + + log.debug("params = " + ((params == null) ? null : MathUtils.printArray(params))); + log.debug("repeat = " + repeat); + + for (int n : params) + { + for (int j = 0; j < repeat; j++) + { + log.debug("n = " + n); + + // Set the integer parameter in the TKTestResult to be passed to the tests. + tkResult.setN(n); + + if (tkResult.shouldStop()) + { + log.debug("tkResult.shouldStop = " + true); + + break; + } + + log.debug("Calling super#run"); + super.run(tkResult); + } + } + } + + /** + * Prints out the name of this test with the string "(parameterized)" appended onto it for debugging purposes. + * + * @return The name of this test with the string "(parameterized)" appended onto it. + */ + public String toString() + { + return super.toString() + "(parameterized)"; + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BaseThrottle.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BaseThrottle.java index e8e203f0a3..61d5746421 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BaseThrottle.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BaseThrottle.java @@ -1,98 +1,98 @@ -/*
- *
- * 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.junit.extensions;
-
-/**
- * Provides a base implementation of the non-waiting throttle checking method, using the system nano timer.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Check against a throttle speed without waiting.
- * </table>
- *
- * @author Rupert Smith
- */
-public abstract class BaseThrottle implements Throttle
-{
- /** Holds the length of a single cycle in nano seconds. */
- protected long cycleTimeNanos;
-
- /** Holds the time of the last succesfull call to the check method. */
- private long lastCheckTimeNanos;
-
- /** Flag used to detect the first call to the {@link #checkThrottle()} method. */
- boolean firstCheckCall = true;
-
- /**
- * Flag used to detect the first call to the {@link #throttle()} method. Zero or negative start time cannot be
- * relied on to detect this as System.nanoTime can return zero or negative values.
- */
- boolean firstCall = true;
-
- /**
- * Specifies the throttling rate in operations per second. This must be called with with a value, the inverse
- * of which is a measurement in nano seconds, such that the number of nano seconds do not overflow a long integer.
- * The value must also be larger than zero.
- *
- * @param hertz The throttling rate in cycles per second.
- */
- public void setRate(float hertz)
- {
- // Check that the argument is above zero.
- if (hertz <= 0.0f)
- {
- throw new IllegalArgumentException("The throttle rate must be above zero.");
- }
-
- // Calculate the cycle time.
- cycleTimeNanos = (long) (1000000000f / hertz);
-
- // Reset the first pass flag.
- firstCall = false;
- firstCheckCall = false;
- }
-
- /**
- * Checks but does not enforce the throttle rate. When this method is called, it checks if a length of time greater
- * than that equal to the inverse of the throttling rate has passed since it was last called and returned <tt>true</tt>
- *
- * @return <tt>true</tt> if a length of time greater than that equal to the inverse of the throttling rate has
- * passed since this method was last called and returned <tt>true</tt>, <tt>false</tt> otherwise. The very
- * first time this method is called on a throttle, it returns <tt>true</tt> as the base case to the above
- * self-referential definition.
- */
- public boolean checkThrottle()
- {
- long now = System.nanoTime();
-
- if ((now > (cycleTimeNanos + lastCheckTimeNanos)) || firstCheckCall)
- {
- firstCheckCall = false;
- lastCheckTimeNanos = now;
-
- return true;
- }
- else
- {
- return false;
- }
- }
-}
+/* + * + * 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.junit.extensions; + +/** + * Provides a base implementation of the non-waiting throttle checking method, using the system nano timer. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Check against a throttle speed without waiting. + * </table> + * + * @author Rupert Smith + */ +public abstract class BaseThrottle implements Throttle +{ + /** Holds the length of a single cycle in nano seconds. */ + protected long cycleTimeNanos; + + /** Holds the time of the last succesfull call to the check method. */ + private long lastCheckTimeNanos; + + /** Flag used to detect the first call to the {@link #checkThrottle()} method. */ + boolean firstCheckCall = true; + + /** + * Flag used to detect the first call to the {@link #throttle()} method. Zero or negative start time cannot be + * relied on to detect this as System.nanoTime can return zero or negative values. + */ + boolean firstCall = true; + + /** + * Specifies the throttling rate in operations per second. This must be called with with a value, the inverse + * of which is a measurement in nano seconds, such that the number of nano seconds do not overflow a long integer. + * The value must also be larger than zero. + * + * @param hertz The throttling rate in cycles per second. + */ + public void setRate(float hertz) + { + // Check that the argument is above zero. + if (hertz <= 0.0f) + { + throw new IllegalArgumentException("The throttle rate must be above zero."); + } + + // Calculate the cycle time. + cycleTimeNanos = (long) (1000000000f / hertz); + + // Reset the first pass flag. + firstCall = false; + firstCheckCall = false; + } + + /** + * Checks but does not enforce the throttle rate. When this method is called, it checks if a length of time greater + * than that equal to the inverse of the throttling rate has passed since it was last called and returned <tt>true</tt> + * + * @return <tt>true</tt> if a length of time greater than that equal to the inverse of the throttling rate has + * passed since this method was last called and returned <tt>true</tt>, <tt>false</tt> otherwise. The very + * first time this method is called on a throttle, it returns <tt>true</tt> as the base case to the above + * self-referential definition. + */ + public boolean checkThrottle() + { + long now = System.nanoTime(); + + if ((now > (cycleTimeNanos + lastCheckTimeNanos)) || firstCheckCall) + { + firstCheckCall = false; + lastCheckTimeNanos = now; + + return true; + } + else + { + return false; + } + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BatchedThrottle.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BatchedThrottle.java index 1d00fcf3b6..241e7aa2b7 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BatchedThrottle.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BatchedThrottle.java @@ -1,94 +1,94 @@ -/*
- *
- * 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.junit.extensions;
-
-/**
- * BatchedThrottle is a {@link SleepThrottle} that uses batching to achieve much higher throttling rates than a sleep
- * throttle can. Sleep throttle has difficulties once the rate gets above a few hundred hertz, because the JVM cannot
- * generate timed pauses that are that short. BatchedThrottle gets around this by only inserting pauses once every so
- * many calls to the {@link #throttle()} method, and using a sleep throttle run at a lower rate. The rate for the sleep
- * throttle is chosen so that it remains under 100hz. The final throttling rate of this throttle is equal to the batch
- * size times the rate of the underlying sleep throttle.
- *
- * <p/>The batching calculation involves taking the log to the base 100 of the desired rate and rounding this to
- * an integer. The batch size is always an exact power of 100 because of the rounding. The rate for an underlying
- * sleep throttle is then chosen appropriately.
- *
- * <p/>In practice, the accuracy of a BacthedThrottle skews off but can sometimes even be reasonable up to ten thousand
- * hertz compared with 100 Hz for a {@link SleepThrottle}.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Accept throttling rate in operations per second.
- * <tr><td> Inject short pauses, occasionaly, to fill out processing cycles to a specified rate.
- * <tr><td> Check against a throttle speed without waiting.
- * </table>
- *
- * @todo Should always round the log base 100 down to the nearest integer?
- *
- * @author Rupert Smith
- */
-public class BatchedThrottle extends BaseThrottle
-{
- /** Holds the batch size. */
- int batchSize;
-
- /** The call count within the current batch. */
- long callCount;
-
- /** Holds a sleep throttle configured to run at the batched rate. */
- private Throttle batchRateThrottle = new SleepThrottle();
-
- /**
- * Specifies the throttling rate in operations per second.
- *
- * @param hertz The throttling rate in cycles per second.
- */
- public void setRate(float hertz)
- {
- // Pass the rate unaltered down to the base implementation, for the check method.
- super.setRate(hertz);
-
- // Log base 10 over 2 is used here to get a feel for what power of 100 the total rate is.
- // As the total rate goes up the powers of 100 the batch size goes up by powers of 100 to keep the
- // throttle rate in the range 1 to 100.
- int x = (int) (Math.log10(hertz) / 2);
- batchSize = (int) Math.pow(100, x);
- float throttleRate = hertz / batchSize;
-
- // Reset the call count.
- callCount = 0;
-
- // Set the sleep throttle wrapped implementation at a rate within its abilities.
- batchRateThrottle.setRate(throttleRate);
- }
-
- /**
- * Throttle calls to this method to the rate specified by the {@link #setRate(float)} method.
- */
- public void throttle()
- {
- if ((callCount++ % batchSize) == 0)
- {
- batchRateThrottle.throttle();
- }
- }
-}
+/* + * + * 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.junit.extensions; + +/** + * BatchedThrottle is a {@link SleepThrottle} that uses batching to achieve much higher throttling rates than a sleep + * throttle can. Sleep throttle has difficulties once the rate gets above a few hundred hertz, because the JVM cannot + * generate timed pauses that are that short. BatchedThrottle gets around this by only inserting pauses once every so + * many calls to the {@link #throttle()} method, and using a sleep throttle run at a lower rate. The rate for the sleep + * throttle is chosen so that it remains under 100hz. The final throttling rate of this throttle is equal to the batch + * size times the rate of the underlying sleep throttle. + * + * <p/>The batching calculation involves taking the log to the base 100 of the desired rate and rounding this to + * an integer. The batch size is always an exact power of 100 because of the rounding. The rate for an underlying + * sleep throttle is then chosen appropriately. + * + * <p/>In practice, the accuracy of a BacthedThrottle skews off but can sometimes even be reasonable up to ten thousand + * hertz compared with 100 Hz for a {@link SleepThrottle}. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Accept throttling rate in operations per second. + * <tr><td> Inject short pauses, occasionaly, to fill out processing cycles to a specified rate. + * <tr><td> Check against a throttle speed without waiting. + * </table> + * + * @todo Should always round the log base 100 down to the nearest integer? + * + * @author Rupert Smith + */ +public class BatchedThrottle extends BaseThrottle +{ + /** Holds the batch size. */ + int batchSize; + + /** The call count within the current batch. */ + long callCount; + + /** Holds a sleep throttle configured to run at the batched rate. */ + private Throttle batchRateThrottle = new SleepThrottle(); + + /** + * Specifies the throttling rate in operations per second. + * + * @param hertz The throttling rate in cycles per second. + */ + public void setRate(float hertz) + { + // Pass the rate unaltered down to the base implementation, for the check method. + super.setRate(hertz); + + // Log base 10 over 2 is used here to get a feel for what power of 100 the total rate is. + // As the total rate goes up the powers of 100 the batch size goes up by powers of 100 to keep the + // throttle rate in the range 1 to 100. + int x = (int) (Math.log10(hertz) / 2); + batchSize = (int) Math.pow(100, x); + float throttleRate = hertz / batchSize; + + // Reset the call count. + callCount = 0; + + // Set the sleep throttle wrapped implementation at a rate within its abilities. + batchRateThrottle.setRate(throttleRate); + } + + /** + * Throttle calls to this method to the rate specified by the {@link #setRate(float)} method. + */ + public void throttle() + { + if ((callCount++ % batchSize) == 0) + { + batchRateThrottle.throttle(); + } + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/DurationTestDecorator.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/DurationTestDecorator.java index fe1e044e67..1c1c146361 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/DurationTestDecorator.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/DurationTestDecorator.java @@ -1,199 +1,199 @@ -/*
- *
- * 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.junit.extensions;
-
-import junit.framework.Test;
-import junit.framework.TestResult;
-
-import org.apache.log4j.Logger;
-
-import java.util.Timer;
-import java.util.TimerTask;
-
-/**
- * A test decorator that runs a test repeatedly until a specified length of time has passed.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Repeatedly run a test for a fixed length of time.
- * </table>
- *
- * @todo The count of the number of tests run is an important number to keep. Also num passed/error/failed is also
- * important to record. What to do with these numbers? They are already logged to the test listeners.
- *
- * @todo The duration test runner wraps on top of size, repeat or thread wrappers, need a way for it to tell
- * TKTestResult when the duration is up, so that it can terminate any repeats in progress. It should end
- * as soon as possible once the test method exits.
- *
- * @author Rupert Smith
- */
-public class DurationTestDecorator extends WrappedSuiteTestDecorator implements ShutdownHookable
-{
- /** Used for logging. */
- private static final Logger log = Logger.getLogger(DurationTestDecorator.class);
-
- /** The test to run. */
- private Test test;
-
- /** The length of time to run the test for. */
- private long duration;
-
- /** Flag set by the shutdown hook. This decorator will not start any new tests when this is set. */
- private boolean shutdown = false;
-
- /**
- * Creates an active test with default multiplier (1).
- *
- * @param test The target test.
- */
- public DurationTestDecorator(WrappedSuiteTestDecorator test)
- {
- super(test);
- this.test = test;
- }
-
- /**
- * Creates active test with default multiplier (1).
- *
- * @param test The target test.
- * @param duration The duration in milliseconds.
- */
- public DurationTestDecorator(WrappedSuiteTestDecorator test, long duration)
- {
- super(test);
-
- // log.debug("public DurationTestDecorator(Test \"" + test + "\", long " + duration + "): called");
-
- this.test = test;
- this.duration = duration;
- }
-
- /**
- * Runs the test repeatedly for the fixed duration.
- *
- * @param testResult The the results object to monitor the test results with.
- */
- public void run(TestResult testResult)
- {
- log.debug("public void run(TestResult testResult): called");
-
- // Cast the test result to expose it as a TKTestResult if the test is running under the TKTestRunner.
- TKTestResult tkTestResult = null;
-
- if (testResult instanceof TKTestResult)
- {
- tkTestResult = (TKTestResult) testResult;
- }
-
- // Work out when the test should end.
- long now = System.nanoTime();
- long end = (duration * 1000000) + now;
-
- // If running under the TKTestRunner, set up a timer to notify the test framework when the test reaches its
- // completion time.
- Timer durationTimer = null;
-
- if (tkTestResult != null)
- {
- log.debug("Creating duration timer.");
-
- durationTimer = new Timer();
- durationTimer.schedule(new DurationTimerTask((TKTestResult) testResult), duration);
- }
-
- // Run the test until the duration times out or the shutdown flag is set. The test method may not exit until
- // interrupted in some cases, in which case the timer will do the interrupting.
- while ((now < end) && !shutdown)
- {
- test.run(testResult);
-
- now = System.nanoTime();
- }
-
- // Clean up any timer that was used.
- if (durationTimer != null)
- {
- log.debug("Cancelling duration timer.");
-
- durationTimer.cancel();
- }
- }
-
- /**
- * Supplies the shutdown hook. This shutdown hook does not call {@link TKTestResult#shutdownNow()} because the
- * {@link ScaledTestDecorator} already takes care of that.
- *
- * @return The shut down hook.
- */
- public Thread getShutdownHook()
- {
- return new Thread(new Runnable()
- {
- public void run()
- {
- // log.debug("DurationTestDecorator::ShutdownHook: called");
-
- // Set the shutdown flag so that no new tests are started.
- shutdown = true;
- }
- });
- }
-
- /**
- * DurationTimerTask is a timer task that is configured, upon expiry of its timer, to invoke
- * {@link TKTestResult#shutdownNow()}, for the test result object on which it is set. It also sets
- * the {@link DurationTestDecorator#shutdown} flag to indicate that no new tests should be run.
- *
- * <p/>The test loop implemented by DurationTestDecorator checks that the duration has not expired, on each
- * test case that it runs. However, it is possible to write test cases that never return until explicitly
- * interrupted by the test framework. This timer task exists to notify the test framework
- */
- private class DurationTimerTask extends TimerTask
- {
- /** Used for debugging purposes. */
- private final Logger log = Logger.getLogger(DurationTimerTask.class);
-
- /** Holds the test result for the test to which a duration limit is being applied. */
- TKTestResult testResult;
-
- /**
- * Creates a duration limit timer which will notify the specified test result when the duration has
- * expired.
- *
- * @param testResult The test result to notify upon expiry of the test duration.
- */
- public DurationTimerTask(TKTestResult testResult)
- {
- this.testResult = testResult;
- }
-
- /**
- * The action to be performed by this timer task.
- */
- public void run()
- {
- log.debug("public void run(): called");
-
- shutdown = true;
- testResult.shutdownNow();
- }
- }
-}
+/* + * + * 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.junit.extensions; + +import junit.framework.Test; +import junit.framework.TestResult; + +import org.apache.log4j.Logger; + +import java.util.Timer; +import java.util.TimerTask; + +/** + * A test decorator that runs a test repeatedly until a specified length of time has passed. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Repeatedly run a test for a fixed length of time. + * </table> + * + * @todo The count of the number of tests run is an important number to keep. Also num passed/error/failed is also + * important to record. What to do with these numbers? They are already logged to the test listeners. + * + * @todo The duration test runner wraps on top of size, repeat or thread wrappers, need a way for it to tell + * TKTestResult when the duration is up, so that it can terminate any repeats in progress. It should end + * as soon as possible once the test method exits. + * + * @author Rupert Smith + */ +public class DurationTestDecorator extends WrappedSuiteTestDecorator implements ShutdownHookable +{ + /** Used for logging. */ + private static final Logger log = Logger.getLogger(DurationTestDecorator.class); + + /** The test to run. */ + private Test test; + + /** The length of time to run the test for. */ + private long duration; + + /** Flag set by the shutdown hook. This decorator will not start any new tests when this is set. */ + private boolean shutdown = false; + + /** + * Creates an active test with default multiplier (1). + * + * @param test The target test. + */ + public DurationTestDecorator(WrappedSuiteTestDecorator test) + { + super(test); + this.test = test; + } + + /** + * Creates active test with default multiplier (1). + * + * @param test The target test. + * @param duration The duration in milliseconds. + */ + public DurationTestDecorator(WrappedSuiteTestDecorator test, long duration) + { + super(test); + + // log.debug("public DurationTestDecorator(Test \"" + test + "\", long " + duration + "): called"); + + this.test = test; + this.duration = duration; + } + + /** + * Runs the test repeatedly for the fixed duration. + * + * @param testResult The the results object to monitor the test results with. + */ + public void run(TestResult testResult) + { + log.debug("public void run(TestResult testResult): called"); + + // Cast the test result to expose it as a TKTestResult if the test is running under the TKTestRunner. + TKTestResult tkTestResult = null; + + if (testResult instanceof TKTestResult) + { + tkTestResult = (TKTestResult) testResult; + } + + // Work out when the test should end. + long now = System.nanoTime(); + long end = (duration * 1000000) + now; + + // If running under the TKTestRunner, set up a timer to notify the test framework when the test reaches its + // completion time. + Timer durationTimer = null; + + if (tkTestResult != null) + { + log.debug("Creating duration timer."); + + durationTimer = new Timer(); + durationTimer.schedule(new DurationTimerTask((TKTestResult) testResult), duration); + } + + // Run the test until the duration times out or the shutdown flag is set. The test method may not exit until + // interrupted in some cases, in which case the timer will do the interrupting. + while ((now < end) && !shutdown) + { + test.run(testResult); + + now = System.nanoTime(); + } + + // Clean up any timer that was used. + if (durationTimer != null) + { + log.debug("Cancelling duration timer."); + + durationTimer.cancel(); + } + } + + /** + * Supplies the shutdown hook. This shutdown hook does not call {@link TKTestResult#shutdownNow()} because the + * {@link ScaledTestDecorator} already takes care of that. + * + * @return The shut down hook. + */ + public Thread getShutdownHook() + { + return new Thread(new Runnable() + { + public void run() + { + // log.debug("DurationTestDecorator::ShutdownHook: called"); + + // Set the shutdown flag so that no new tests are started. + shutdown = true; + } + }); + } + + /** + * DurationTimerTask is a timer task that is configured, upon expiry of its timer, to invoke + * {@link TKTestResult#shutdownNow()}, for the test result object on which it is set. It also sets + * the {@link DurationTestDecorator#shutdown} flag to indicate that no new tests should be run. + * + * <p/>The test loop implemented by DurationTestDecorator checks that the duration has not expired, on each + * test case that it runs. However, it is possible to write test cases that never return until explicitly + * interrupted by the test framework. This timer task exists to notify the test framework + */ + private class DurationTimerTask extends TimerTask + { + /** Used for debugging purposes. */ + private final Logger log = Logger.getLogger(DurationTimerTask.class); + + /** Holds the test result for the test to which a duration limit is being applied. */ + TKTestResult testResult; + + /** + * Creates a duration limit timer which will notify the specified test result when the duration has + * expired. + * + * @param testResult The test result to notify upon expiry of the test duration. + */ + public DurationTimerTask(TKTestResult testResult) + { + this.testResult = testResult; + } + + /** + * The action to be performed by this timer task. + */ + public void run() + { + log.debug("public void run(): called"); + + shutdown = true; + testResult.shutdownNow(); + } + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/InstrumentedTest.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/InstrumentedTest.java index ed792fcd5a..0804757dce 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/InstrumentedTest.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/InstrumentedTest.java @@ -1,66 +1,66 @@ -/*
- *
- * 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.junit.extensions;
-
-import junit.framework.Test;
-
-/**
- * An InstrumentedTest is one which can supply some additional instrumentation on top of the pass/fail/error behaviour
- * of normal junit tests. Tests implementing this interface must additionally supply information about how long they
- * took to run and how much memory they used.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities
- * <tr><td> Report test run time.
- * <tr><td> Report test memory usage.
- * </table>
- *
- * @author Rupert Smith
- */
-public interface InstrumentedTest extends Test
-{
- /**
- * Reports how long the test took to run.
- *
- * @return The time in milliseconds that the test took to run.
- */
- public long getTestTime();
-
- /**
- * Reports the memory usage at the start of the test.
- *
- * @return The memory usage at the start of the test.
- */
- public long getTestStartMemory();
-
- /**
- * Reports the memory usage at the end of the test.
- *
- * @return The memory usage at the end of the test.
- */
- public long getTestEndMemory();
-
- /**
- * Resets the instrumentation values to zero, and nulls any references to held measurements so that the memory
- * can be reclaimed.
- */
- public void reset();
-}
+/* + * + * 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.junit.extensions; + +import junit.framework.Test; + +/** + * An InstrumentedTest is one which can supply some additional instrumentation on top of the pass/fail/error behaviour + * of normal junit tests. Tests implementing this interface must additionally supply information about how long they + * took to run and how much memory they used. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities + * <tr><td> Report test run time. + * <tr><td> Report test memory usage. + * </table> + * + * @author Rupert Smith + */ +public interface InstrumentedTest extends Test +{ + /** + * Reports how long the test took to run. + * + * @return The time in milliseconds that the test took to run. + */ + public long getTestTime(); + + /** + * Reports the memory usage at the start of the test. + * + * @return The memory usage at the start of the test. + */ + public long getTestStartMemory(); + + /** + * Reports the memory usage at the end of the test. + * + * @return The memory usage at the end of the test. + */ + public long getTestEndMemory(); + + /** + * Resets the instrumentation values to zero, and nulls any references to held measurements so that the memory + * can be reclaimed. + */ + public void reset(); +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/NullResultPrinter.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/NullResultPrinter.java index 2ffbcb5bb8..6727f6f152 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/NullResultPrinter.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/NullResultPrinter.java @@ -1,92 +1,92 @@ -/*
- *
- * 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.junit.extensions;
-
-import junit.framework.AssertionFailedError;
-import junit.framework.Test;
-
-import junit.textui.ResultPrinter;
-
-import java.io.PrintStream;
-
-/**
- * A ResultPrinter that prints nothing. This exists, in order to provide a replacement to JUnit's ResultPrinter, which
- * is refered to directly by JUnit code, rather that as an abstracted TestListener. JUnit's text ui TestRunner must
- * have a ResultPrinter. This provides an implementation of it that prints nothing, so that a better mechanism can
- * be used for providing feedback to the console instead.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td>
- * </table>
- *
- * @todo See todo in TKTestRunner about completely replacing the test ui runner. Doing things like this in order to
- * extend JUnit is not nice, and there needs to be a better way to do it. Delete this class and use a listener
- * instead.
- *
- * @author Rupert Smith
- */
-public class NullResultPrinter extends ResultPrinter
-{
- /**
- * Builds a fake ResultPrinter that prints nothing.
- *
- * @param writer The writer to send output to.
- */
- public NullResultPrinter(PrintStream writer)
- {
- super(writer);
- }
-
- /**
- * Does nothing.
- *
- * @param test Ignored.
- * @param t Ignored.
- */
- public void addError(Test test, Throwable t)
- { }
-
- /**
- * Does nothing.
- *
- * @param test Ignored.
- * @param t Ignored.
- */
- public void addFailure(Test test, AssertionFailedError t)
- { }
-
- /**
- * Does nothing.
- *
- * @param test Ignored.
- */
- public void endTest(Test test)
- { }
-
- /**
- * Does nothing.
- *
- * @param test Ignored.
- */
- public void startTest(Test test)
- { }
-}
+/* + * + * 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.junit.extensions; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; + +import junit.textui.ResultPrinter; + +import java.io.PrintStream; + +/** + * A ResultPrinter that prints nothing. This exists, in order to provide a replacement to JUnit's ResultPrinter, which + * is refered to directly by JUnit code, rather that as an abstracted TestListener. JUnit's text ui TestRunner must + * have a ResultPrinter. This provides an implementation of it that prints nothing, so that a better mechanism can + * be used for providing feedback to the console instead. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> + * </table> + * + * @todo See todo in TKTestRunner about completely replacing the test ui runner. Doing things like this in order to + * extend JUnit is not nice, and there needs to be a better way to do it. Delete this class and use a listener + * instead. + * + * @author Rupert Smith + */ +public class NullResultPrinter extends ResultPrinter +{ + /** + * Builds a fake ResultPrinter that prints nothing. + * + * @param writer The writer to send output to. + */ + public NullResultPrinter(PrintStream writer) + { + super(writer); + } + + /** + * Does nothing. + * + * @param test Ignored. + * @param t Ignored. + */ + public void addError(Test test, Throwable t) + { } + + /** + * Does nothing. + * + * @param test Ignored. + * @param t Ignored. + */ + public void addFailure(Test test, AssertionFailedError t) + { } + + /** + * Does nothing. + * + * @param test Ignored. + */ + public void endTest(Test test) + { } + + /** + * Does nothing. + * + * @param test Ignored. + */ + public void startTest(Test test) + { } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ParameterVariationTestDecorator.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ParameterVariationTestDecorator.java index 60ec156354..2c207635c7 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ParameterVariationTestDecorator.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ParameterVariationTestDecorator.java @@ -1,172 +1,172 @@ -/*
- *
- * 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.junit.extensions;
-
-import junit.framework.TestResult;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.junit.extensions.util.MathUtils;
-
-/**
- * ParameterVariationTestDecorator is a test decorator that runs a test repeatedly under all permutations of its
- * test parameters.
- *
- * a set of integer parameters and a repeat count are specified, then each test is run for the repeat count at each
- * integer parameter.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Repeat a test for each of a set of integer parameters. <td> {@link org.apache.qpid.junit.extensions.TKTestResult}
- * <tr><td> Repeat a test multiple times.
- * <tr><td>
- * </table>
- *
- * @author Rupert Smith
- */
-public class ParameterVariationTestDecorator extends WrappedSuiteTestDecorator
-{
- /** Used for logging. */
- private static final Logger log = Logger.getLogger(ParameterVariationTestDecorator.class);
-
- /** The int size parameters to run the test with. */
- private int[] params;
-
- /** The number of times the whole test should be repeated. */
- private int repeat;
-
- /**
- * Creates an asymptotic test decorator that wraps a test with repeats and a set of integer 'size' paramters
- * to call the test with.
- *
- * @param test The test to wrap.
- * @param params The integer 'size' parameters.
- * @param repeat The number of times to repeat the test.
- */
- public ParameterVariationTestDecorator(WrappedSuiteTestDecorator test, int[] params, int repeat)
- {
- super(test);
-
- log.debug("public AsymptoticTestDecorator(Test \"" + test + "\", int[] "
- + ((params == null) ? null : MathUtils.printArray(params)) + ", int " + repeat + "): called");
-
- this.params = params;
- this.repeat = repeat;
- }
-
- /**
- * Creates a new AsymptoticTestDecorator object.
- *
- * @param test The test to decorate.
- * @param start The starting asymptotic integer parameter value.
- * @param end The ending asymptotic integer parameter value.
- * @param step The increment size to move from the start to end values by.
- * @param repeat The number of times to repeat the test at each step of the cycle.
- */
- public ParameterVariationTestDecorator(WrappedSuiteTestDecorator test, int start, int end, int step, int repeat)
- {
- super(test);
-
- if (start < 0)
- {
- throw new IllegalArgumentException("Start must be >= 0");
- }
-
- if (end < start)
- {
- throw new IllegalArgumentException("End must be >= start");
- }
-
- if (step < 1)
- {
- throw new IllegalArgumentException("Step must be >= 1");
- }
-
- if (repeat < 1)
- {
- throw new IllegalArgumentException("Repeat must be >= 1");
- }
-
- // Generate the sequence.
- params = new int[((end - start) / step) + 1];
- int i = 0;
- for (int n = start; n <= end; n += step)
- {
- params[i++] = n;
- }
-
- this.repeat = repeat;
- }
-
- /**
- * Runs the test repeatedly for each value of the int parameter specified and for the correct number of test
- * repeats.
- *
- * @param result The test result object that the tests will indicate their results to. This is also used
- * to pass the int parameter from this class to the decorated test class.
- */
- public void run(TestResult result)
- {
- log.debug("public void run(TestResult result): called");
-
- if (!(result instanceof TKTestResult))
- {
- throw new IllegalArgumentException("AsymptoticTestDecorator only works with TKTestResult");
- }
-
- // Cast the test result into a TKTestResult to place the current parameter into.
- TKTestResult tkResult = (TKTestResult) result;
-
- log.debug("params = " + ((params == null) ? null : MathUtils.printArray(params)));
- log.debug("repeat = " + repeat);
-
- for (int n : params)
- {
- for (int j = 0; j < repeat; j++)
- {
- log.debug("n = " + n);
-
- // Set the integer parameter in the TKTestResult to be passed to the tests.
- tkResult.setN(n);
-
- if (tkResult.shouldStop())
- {
- log.debug("tkResult.shouldStop = " + true);
-
- break;
- }
-
- log.debug("Calling super#run");
- super.run(tkResult);
- }
- }
- }
-
- /**
- * Prints out the name of this test with the string "(parameterized)" appended onto it for debugging purposes.
- *
- * @return The name of this test with the string "(parameterized)" appended onto it.
- */
- public String toString()
- {
- return super.toString() + "(parameterized)";
- }
-}
+/* + * + * 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.junit.extensions; + +import junit.framework.TestResult; + +import org.apache.log4j.Logger; + +import org.apache.qpid.junit.extensions.util.MathUtils; + +/** + * ParameterVariationTestDecorator is a test decorator that runs a test repeatedly under all permutations of its + * test parameters. + * + * a set of integer parameters and a repeat count are specified, then each test is run for the repeat count at each + * integer parameter. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Repeat a test for each of a set of integer parameters. <td> {@link org.apache.qpid.junit.extensions.TKTestResult} + * <tr><td> Repeat a test multiple times. + * <tr><td> + * </table> + * + * @author Rupert Smith + */ +public class ParameterVariationTestDecorator extends WrappedSuiteTestDecorator +{ + /** Used for logging. */ + private static final Logger log = Logger.getLogger(ParameterVariationTestDecorator.class); + + /** The int size parameters to run the test with. */ + private int[] params; + + /** The number of times the whole test should be repeated. */ + private int repeat; + + /** + * Creates an asymptotic test decorator that wraps a test with repeats and a set of integer 'size' paramters + * to call the test with. + * + * @param test The test to wrap. + * @param params The integer 'size' parameters. + * @param repeat The number of times to repeat the test. + */ + public ParameterVariationTestDecorator(WrappedSuiteTestDecorator test, int[] params, int repeat) + { + super(test); + + log.debug("public AsymptoticTestDecorator(Test \"" + test + "\", int[] " + + ((params == null) ? null : MathUtils.printArray(params)) + ", int " + repeat + "): called"); + + this.params = params; + this.repeat = repeat; + } + + /** + * Creates a new AsymptoticTestDecorator object. + * + * @param test The test to decorate. + * @param start The starting asymptotic integer parameter value. + * @param end The ending asymptotic integer parameter value. + * @param step The increment size to move from the start to end values by. + * @param repeat The number of times to repeat the test at each step of the cycle. + */ + public ParameterVariationTestDecorator(WrappedSuiteTestDecorator test, int start, int end, int step, int repeat) + { + super(test); + + if (start < 0) + { + throw new IllegalArgumentException("Start must be >= 0"); + } + + if (end < start) + { + throw new IllegalArgumentException("End must be >= start"); + } + + if (step < 1) + { + throw new IllegalArgumentException("Step must be >= 1"); + } + + if (repeat < 1) + { + throw new IllegalArgumentException("Repeat must be >= 1"); + } + + // Generate the sequence. + params = new int[((end - start) / step) + 1]; + int i = 0; + for (int n = start; n <= end; n += step) + { + params[i++] = n; + } + + this.repeat = repeat; + } + + /** + * Runs the test repeatedly for each value of the int parameter specified and for the correct number of test + * repeats. + * + * @param result The test result object that the tests will indicate their results to. This is also used + * to pass the int parameter from this class to the decorated test class. + */ + public void run(TestResult result) + { + log.debug("public void run(TestResult result): called"); + + if (!(result instanceof TKTestResult)) + { + throw new IllegalArgumentException("AsymptoticTestDecorator only works with TKTestResult"); + } + + // Cast the test result into a TKTestResult to place the current parameter into. + TKTestResult tkResult = (TKTestResult) result; + + log.debug("params = " + ((params == null) ? null : MathUtils.printArray(params))); + log.debug("repeat = " + repeat); + + for (int n : params) + { + for (int j = 0; j < repeat; j++) + { + log.debug("n = " + n); + + // Set the integer parameter in the TKTestResult to be passed to the tests. + tkResult.setN(n); + + if (tkResult.shouldStop()) + { + log.debug("tkResult.shouldStop = " + true); + + break; + } + + log.debug("Calling super#run"); + super.run(tkResult); + } + } + } + + /** + * Prints out the name of this test with the string "(parameterized)" appended onto it for debugging purposes. + * + * @return The name of this test with the string "(parameterized)" appended onto it. + */ + public String toString() + { + return super.toString() + "(parameterized)"; + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ScaledTestDecorator.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ScaledTestDecorator.java index f5e3e1a758..e0af22cfb7 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ScaledTestDecorator.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ScaledTestDecorator.java @@ -1,375 +1,375 @@ -/*
- *
- * 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.junit.extensions;
-
-import junit.framework.Test;
-import junit.framework.TestResult;
-
-import java.util.concurrent.BrokenBarrierException;
-import java.util.concurrent.CyclicBarrier;
-
-/**
- * A test decorator that runs a test many times simultaneously in many threads.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Clone a test run into many threads and run them simultaneously.
- * <tr><td> Inform the test results of the start and end of each concurrent test batch. <td> {@link TKTestResult}
- * <tr><td> Inform the test results of the concurrency level. <td> {@link TKTestResult}
- * </table>
- *
- * @author Rupert Smith
- */
-public class ScaledTestDecorator extends WrappedSuiteTestDecorator implements ShutdownHookable // TestDecorator
-{
- /** Used for logging. */
- // private static final Logger log = Logger.getLogger(ScaledTestDecorator.class);
-
- /** Determines how long to wait for tests to cleanly exit on shutdown. */
- private static final long SHUTDOWN_PAUSE = 3000;
-
- /**
- * The stress levels or numbers of simultaneous threads to run the test in. The test is repeated at each of
- * the concurrency levels specified here. Defaults to 1 thread.
- */
- private int[] threads = new int[] { 1 };
-
- /** Used to hold the number of tests currently being run in parallel. */
- private int concurrencyLevel;
-
- /** The test to run. */
- private WrappedSuiteTestDecorator test;
-
- /**
- * Used to hold the current {@link TKTestResult} for the tests currently being run. This is made available so that
- * the shutdown hook can ask it to cleanly end the current tests in the event of a shutdown.
- */
- private TKTestResult currentTestResult;
-
- /** Flag set by the shutdown hook. This decorator will not start any new tests when this is set. */
- private boolean shutdown = false;
-
- /**
- * Creates an active test with default multiplier (1).
- *
- * @param test The target test.
- */
- public ScaledTestDecorator(WrappedSuiteTestDecorator test)
- {
- super(test);
- this.test = test;
- }
-
- /**
- * Creates a concurrently scaled test with the specified number of threads.
- *
- * @param test The target test.
- * @param numThreads The stress level.
- */
- public ScaledTestDecorator(WrappedSuiteTestDecorator test, int numThreads)
- {
- this(test, new int[] { numThreads });
- }
-
- /**
- * Creates a concurrently scaled test with the specified thread levels, the test is repeated at each level.
- *
- * @param test The target test.
- * @param threads The concurrency levels.
- */
- public ScaledTestDecorator(WrappedSuiteTestDecorator test, int[] threads)
- {
- super(test);
-
- /*log.debug("public ScaledTestDecorator(WrappedSuiteTestDecorator test = \"" + test + "\", int[] threads = "
- + MathUtils.printArray(threads) + "): called");*/
-
- this.test = test;
- this.threads = threads;
- }
-
- /**
- * Runs the test simultaneously in at the specified concurrency levels.
- *
- * @param testResult The results object to monitor the test results with.
- */
- public void run(TestResult testResult)
- {
- // log.debug("public void run(TestResult testResult = " + testResult + "): called");
-
- // Loop through all of the specified concurrent levels for the test, provided shutdown has not been called.
- for (int i = 0; (i < threads.length) && !shutdown; i++)
- {
- // Get the number of threads for this run.
- int numThreads = threads[i];
-
- // Create test thread handlers for all the threads.
- TestThreadHandler[] threadHandlers = new TestThreadHandler[numThreads];
-
- // Create a cyclic barrier for the test threads to synch their setups and teardowns on.
- CyclicBarrier barrier = new CyclicBarrier(numThreads);
-
- // Set up the test thread handlers to output results to the same test results object.
- for (int j = 0; j < numThreads; j++)
- {
- threadHandlers[j] = new TestThreadHandler(testResult, test, barrier);
- }
-
- // Ensure the concurrency level statistic is set up correctly.
- concurrencyLevel = numThreads;
-
- // Begin batch.
- if (testResult instanceof TKTestResult)
- {
- TKTestResult tkResult = (TKTestResult) testResult;
- // tkResult.notifyStartBatch();
- tkResult.setConcurrencyLevel(numThreads);
-
- // Set the test result for the currently running tests, so that the shutdown hook can call it if necessary.
- currentTestResult = tkResult;
- }
-
- // Run all the tests and wait for them all to finish.
- executeAndWaitForRunnables(threadHandlers);
-
- // Clear the test result for the currently running tests.
- currentTestResult = null;
-
- // End batch.
- if (testResult instanceof TKTestResult)
- {
- TKTestResult tkResult = (TKTestResult) testResult;
- tkResult.notifyEndBatch();
- }
-
- // Clear up all the test threads, they hold references to their associated TestResult object and Test object,
- // which may prevent them from being garbage collected as the TestResult and Test objects are long lived.
- for (int j = 0; j < numThreads; j++)
- {
- threadHandlers[j].testResult = null;
- threadHandlers[j].test = null;
- threadHandlers[j] = null;
- }
- }
- }
-
- /**
- * Reports the number of tests that the scaled decorator is currently running concurrently.
- *
- * @return The number of tests that the scaled decorator is currently running concurrently.
- */
- public int getConcurrencyLevel()
- {
- return concurrencyLevel;
- }
-
- /**
- * Executes all of the specifed runnable using the thread pool and waits for them all to complete.
- *
- * @param runnables The set of runnables to execute concurrently.
- */
- private void executeAndWaitForRunnables(Runnable[] runnables)
- {
- int numThreads = runnables.length;
-
- // Used to keep track of the test threads in order to know when they have all completed.
- Thread[] threads = new Thread[numThreads];
-
- // Create all the test threads.
- for (int j = 0; j < numThreads; j++)
- {
- threads[j] = new Thread(runnables[j]);
- }
-
- // Start all the test threads.
- for (int j = 0; j < numThreads; j++)
- {
- threads[j].start();
- }
-
- // Wait for all the test threads to complete.
- for (int j = 0; j < numThreads; j++)
- {
- try
- {
- threads[j].join();
- }
- catch (InterruptedException e)
- {
- // Restore the interrupted state of the thread.
- Thread.currentThread().interrupt();
- }
- }
- }
-
- /**
- * Supplies the shut-down hook.
- *
- * @return The shut-down hook.
- */
- public Thread getShutdownHook()
- {
- return new Thread(new Runnable()
- {
- public void run()
- {
- // log.debug("ScaledTestDecorator::ShutdownHook: called");
-
- // Set the shutdown flag so that no new tests are started.
- shutdown = true;
-
- // Check if tests are currently running, and ask them to complete as soon as possible. Allow
- // a short pause for this to happen.
- TKTestResult testResult = currentTestResult;
-
- if (testResult != null)
- {
- // log.debug("There is a test result currently running tests, asking it to terminate ASAP.");
- testResult.shutdownNow();
-
- try
- {
- Thread.sleep(SHUTDOWN_PAUSE);
- }
- catch (InterruptedException e)
- {
- // Restore the interrupted state of the thread.
- Thread.currentThread().interrupt();
- }
- }
- }
- });
- }
-
- /**
- * Prints a string summarizing this test decorator, mainly for debugging purposes.
- *
- * @return String representation for debugging purposes.
- */
- public String toString()
- {
- return "ScaledTestDecorator: [ test = " + test + ", concurrencyLevel = " + concurrencyLevel + " ]";
- }
-
- /**
- * TestThreadHandler is a runnable used to execute a test in. This is static to avoid implicit 'this' reference to
- * the longer lived ScaledTestDecorator class. The scaled test decorator may execute many repeats but creates fresh
- * handlers for each one. It re-uses the threads in a pool but does not re-use these handlers.
- */
- private static class TestThreadHandler implements Runnable
- {
- /** The test result object for the test to be run with. */
- TestResult testResult;
-
- /** The test to run. */
- WrappedSuiteTestDecorator test;
-
- /** Holds the cyclic barrier to synchronize on the end of the setups and before the tear downs. */
- CyclicBarrier barrier;
-
- /**
- * Creates a new TestThreadHandler object.
- *
- * @param testResult The test result object for the test to be run with.
- * @param test The test to run in a sperate thread.
- * @param barrier The barrier implementation to use to synchronize per-thread setup completion and test
- * completion before moving on through the setup, test, teardown phases. The barrier should
- * be configured for the number of test threads.
- */
- TestThreadHandler(TestResult testResult, WrappedSuiteTestDecorator test, CyclicBarrier barrier)
- {
- this.testResult = testResult;
- this.test = test;
- this.barrier = barrier;
- }
-
- /**
- * Runs the test associated with this pool.
- */
- public void run()
- {
- try
- {
- // Call setup on all underlying tests in the suite that are thread aware.
- for (Test childTest : test.getAllUnderlyingTests())
- {
- // Check that the test is concurrency aware, so provides a setup method to call.
- if (childTest instanceof TestThreadAware)
- {
- // Call the tests per thread setup.
- TestThreadAware setupTest = (TestThreadAware) childTest;
- setupTest.threadSetUp();
- }
- }
-
- // Wait until all test threads have completed their setups.
- barrier.await();
-
- // Start timing the test batch, only after thread setups have completed.
- if (testResult instanceof TKTestResult)
- {
- ((TKTestResult) testResult).notifyStartBatch();
- }
-
- // Run the tests.
- test.run(testResult);
-
- // Wait unitl all test threads have completed their tests.
- barrier.await();
-
- // Call tear down on all underlying tests in the suite that are thread aware.
- for (Test childTest : test.getAllUnderlyingTests())
- {
- // Check that the test is concurrency aware, so provides a teardown method to call.
- if (childTest instanceof TestThreadAware)
- {
- // Call the tests per thread tear down.
- TestThreadAware setupTest = (TestThreadAware) childTest;
- setupTest.threadTearDown();
- }
- }
- }
- catch (InterruptedException e)
- {
- // Restore the interrupted state of the thread.
- Thread.currentThread().interrupt();
- }
- catch (BrokenBarrierException e)
- {
- // Set the interrupted state on the thread. The BrokenBarrierException may be caused where one thread
- // waiting for the barrier is interrupted, causing the remaining threads correctly waiting on the
- // barrier to fail. This condition is expected during test interruptions, and the response to it is to
- // interrupt all the other threads running in the same scaled test.
- Thread.currentThread().interrupt();
- }
- }
-
- /**
- * Prints the name of the test for debugging purposes.
- *
- * @return The name of the test.
- */
- public String toString()
- {
- return "ScaledTestDecorator: [test = \"" + test + "\"]";
- }
- }
-}
+/* + * + * 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.junit.extensions; + +import junit.framework.Test; +import junit.framework.TestResult; + +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; + +/** + * A test decorator that runs a test many times simultaneously in many threads. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Clone a test run into many threads and run them simultaneously. + * <tr><td> Inform the test results of the start and end of each concurrent test batch. <td> {@link TKTestResult} + * <tr><td> Inform the test results of the concurrency level. <td> {@link TKTestResult} + * </table> + * + * @author Rupert Smith + */ +public class ScaledTestDecorator extends WrappedSuiteTestDecorator implements ShutdownHookable // TestDecorator +{ + /** Used for logging. */ + // private static final Logger log = Logger.getLogger(ScaledTestDecorator.class); + + /** Determines how long to wait for tests to cleanly exit on shutdown. */ + private static final long SHUTDOWN_PAUSE = 3000; + + /** + * The stress levels or numbers of simultaneous threads to run the test in. The test is repeated at each of + * the concurrency levels specified here. Defaults to 1 thread. + */ + private int[] threads = new int[] { 1 }; + + /** Used to hold the number of tests currently being run in parallel. */ + private int concurrencyLevel; + + /** The test to run. */ + private WrappedSuiteTestDecorator test; + + /** + * Used to hold the current {@link TKTestResult} for the tests currently being run. This is made available so that + * the shutdown hook can ask it to cleanly end the current tests in the event of a shutdown. + */ + private TKTestResult currentTestResult; + + /** Flag set by the shutdown hook. This decorator will not start any new tests when this is set. */ + private boolean shutdown = false; + + /** + * Creates an active test with default multiplier (1). + * + * @param test The target test. + */ + public ScaledTestDecorator(WrappedSuiteTestDecorator test) + { + super(test); + this.test = test; + } + + /** + * Creates a concurrently scaled test with the specified number of threads. + * + * @param test The target test. + * @param numThreads The stress level. + */ + public ScaledTestDecorator(WrappedSuiteTestDecorator test, int numThreads) + { + this(test, new int[] { numThreads }); + } + + /** + * Creates a concurrently scaled test with the specified thread levels, the test is repeated at each level. + * + * @param test The target test. + * @param threads The concurrency levels. + */ + public ScaledTestDecorator(WrappedSuiteTestDecorator test, int[] threads) + { + super(test); + + /*log.debug("public ScaledTestDecorator(WrappedSuiteTestDecorator test = \"" + test + "\", int[] threads = " + + MathUtils.printArray(threads) + "): called");*/ + + this.test = test; + this.threads = threads; + } + + /** + * Runs the test simultaneously in at the specified concurrency levels. + * + * @param testResult The results object to monitor the test results with. + */ + public void run(TestResult testResult) + { + // log.debug("public void run(TestResult testResult = " + testResult + "): called"); + + // Loop through all of the specified concurrent levels for the test, provided shutdown has not been called. + for (int i = 0; (i < threads.length) && !shutdown; i++) + { + // Get the number of threads for this run. + int numThreads = threads[i]; + + // Create test thread handlers for all the threads. + TestThreadHandler[] threadHandlers = new TestThreadHandler[numThreads]; + + // Create a cyclic barrier for the test threads to synch their setups and teardowns on. + CyclicBarrier barrier = new CyclicBarrier(numThreads); + + // Set up the test thread handlers to output results to the same test results object. + for (int j = 0; j < numThreads; j++) + { + threadHandlers[j] = new TestThreadHandler(testResult, test, barrier); + } + + // Ensure the concurrency level statistic is set up correctly. + concurrencyLevel = numThreads; + + // Begin batch. + if (testResult instanceof TKTestResult) + { + TKTestResult tkResult = (TKTestResult) testResult; + // tkResult.notifyStartBatch(); + tkResult.setConcurrencyLevel(numThreads); + + // Set the test result for the currently running tests, so that the shutdown hook can call it if necessary. + currentTestResult = tkResult; + } + + // Run all the tests and wait for them all to finish. + executeAndWaitForRunnables(threadHandlers); + + // Clear the test result for the currently running tests. + currentTestResult = null; + + // End batch. + if (testResult instanceof TKTestResult) + { + TKTestResult tkResult = (TKTestResult) testResult; + tkResult.notifyEndBatch(); + } + + // Clear up all the test threads, they hold references to their associated TestResult object and Test object, + // which may prevent them from being garbage collected as the TestResult and Test objects are long lived. + for (int j = 0; j < numThreads; j++) + { + threadHandlers[j].testResult = null; + threadHandlers[j].test = null; + threadHandlers[j] = null; + } + } + } + + /** + * Reports the number of tests that the scaled decorator is currently running concurrently. + * + * @return The number of tests that the scaled decorator is currently running concurrently. + */ + public int getConcurrencyLevel() + { + return concurrencyLevel; + } + + /** + * Executes all of the specifed runnable using the thread pool and waits for them all to complete. + * + * @param runnables The set of runnables to execute concurrently. + */ + private void executeAndWaitForRunnables(Runnable[] runnables) + { + int numThreads = runnables.length; + + // Used to keep track of the test threads in order to know when they have all completed. + Thread[] threads = new Thread[numThreads]; + + // Create all the test threads. + for (int j = 0; j < numThreads; j++) + { + threads[j] = new Thread(runnables[j]); + } + + // Start all the test threads. + for (int j = 0; j < numThreads; j++) + { + threads[j].start(); + } + + // Wait for all the test threads to complete. + for (int j = 0; j < numThreads; j++) + { + try + { + threads[j].join(); + } + catch (InterruptedException e) + { + // Restore the interrupted state of the thread. + Thread.currentThread().interrupt(); + } + } + } + + /** + * Supplies the shut-down hook. + * + * @return The shut-down hook. + */ + public Thread getShutdownHook() + { + return new Thread(new Runnable() + { + public void run() + { + // log.debug("ScaledTestDecorator::ShutdownHook: called"); + + // Set the shutdown flag so that no new tests are started. + shutdown = true; + + // Check if tests are currently running, and ask them to complete as soon as possible. Allow + // a short pause for this to happen. + TKTestResult testResult = currentTestResult; + + if (testResult != null) + { + // log.debug("There is a test result currently running tests, asking it to terminate ASAP."); + testResult.shutdownNow(); + + try + { + Thread.sleep(SHUTDOWN_PAUSE); + } + catch (InterruptedException e) + { + // Restore the interrupted state of the thread. + Thread.currentThread().interrupt(); + } + } + } + }); + } + + /** + * Prints a string summarizing this test decorator, mainly for debugging purposes. + * + * @return String representation for debugging purposes. + */ + public String toString() + { + return "ScaledTestDecorator: [ test = " + test + ", concurrencyLevel = " + concurrencyLevel + " ]"; + } + + /** + * TestThreadHandler is a runnable used to execute a test in. This is static to avoid implicit 'this' reference to + * the longer lived ScaledTestDecorator class. The scaled test decorator may execute many repeats but creates fresh + * handlers for each one. It re-uses the threads in a pool but does not re-use these handlers. + */ + private static class TestThreadHandler implements Runnable + { + /** The test result object for the test to be run with. */ + TestResult testResult; + + /** The test to run. */ + WrappedSuiteTestDecorator test; + + /** Holds the cyclic barrier to synchronize on the end of the setups and before the tear downs. */ + CyclicBarrier barrier; + + /** + * Creates a new TestThreadHandler object. + * + * @param testResult The test result object for the test to be run with. + * @param test The test to run in a sperate thread. + * @param barrier The barrier implementation to use to synchronize per-thread setup completion and test + * completion before moving on through the setup, test, teardown phases. The barrier should + * be configured for the number of test threads. + */ + TestThreadHandler(TestResult testResult, WrappedSuiteTestDecorator test, CyclicBarrier barrier) + { + this.testResult = testResult; + this.test = test; + this.barrier = barrier; + } + + /** + * Runs the test associated with this pool. + */ + public void run() + { + try + { + // Call setup on all underlying tests in the suite that are thread aware. + for (Test childTest : test.getAllUnderlyingTests()) + { + // Check that the test is concurrency aware, so provides a setup method to call. + if (childTest instanceof TestThreadAware) + { + // Call the tests per thread setup. + TestThreadAware setupTest = (TestThreadAware) childTest; + setupTest.threadSetUp(); + } + } + + // Wait until all test threads have completed their setups. + barrier.await(); + + // Start timing the test batch, only after thread setups have completed. + if (testResult instanceof TKTestResult) + { + ((TKTestResult) testResult).notifyStartBatch(); + } + + // Run the tests. + test.run(testResult); + + // Wait unitl all test threads have completed their tests. + barrier.await(); + + // Call tear down on all underlying tests in the suite that are thread aware. + for (Test childTest : test.getAllUnderlyingTests()) + { + // Check that the test is concurrency aware, so provides a teardown method to call. + if (childTest instanceof TestThreadAware) + { + // Call the tests per thread tear down. + TestThreadAware setupTest = (TestThreadAware) childTest; + setupTest.threadTearDown(); + } + } + } + catch (InterruptedException e) + { + // Restore the interrupted state of the thread. + Thread.currentThread().interrupt(); + } + catch (BrokenBarrierException e) + { + // Set the interrupted state on the thread. The BrokenBarrierException may be caused where one thread + // waiting for the barrier is interrupted, causing the remaining threads correctly waiting on the + // barrier to fail. This condition is expected during test interruptions, and the response to it is to + // interrupt all the other threads running in the same scaled test. + Thread.currentThread().interrupt(); + } + } + + /** + * Prints the name of the test for debugging purposes. + * + * @return The name of the test. + */ + public String toString() + { + return "ScaledTestDecorator: [test = \"" + test + "\"]"; + } + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskAware.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskAware.java index 0e8e1879b6..e462145d7d 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskAware.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskAware.java @@ -1,55 +1,55 @@ -/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.junit.extensions;
-
-/**
- * SetupTaskAware is an interface that tests that can accept injectable setup tasks may implement. Typically this
- * is used by configurable decorator stack to inject setup tasks into tests. It is then up to the test case to run
- * the tasks in the setup or threadSetup methods as it chooses.
- *
- * <p/>Set up tasks should be chained so that they are executed in the order that they are applied. Tear down tasks
- * should be chained so that they are executed in the reverse order to which they are applied. That way the set up and
- * tear down tasks act as a 'task' stack, with nested setups and tear downs.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities.
- * <tr><td> Handle injection of set up tasks.
- * <tr><td> Handle injection of tear down tasks.
- * </table>
- *
- * @author Rupert Smith
- */
-public interface SetupTaskAware
-{
- /**
- * Adds the specified task to the tests setup.
- *
- * @param task The task to add to the tests setup.
- */
- public void chainSetupTask(Runnable task);
-
- /**
- * Adds the specified task to the tests tear down.
- *
- * @param task The task to add to the tests tear down.
- */
- public void chainTearDownTask(Runnable task);
-}
+/* + * + * 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.junit.extensions; + +/** + * SetupTaskAware is an interface that tests that can accept injectable setup tasks may implement. Typically this + * is used by configurable decorator stack to inject setup tasks into tests. It is then up to the test case to run + * the tasks in the setup or threadSetup methods as it chooses. + * + * <p/>Set up tasks should be chained so that they are executed in the order that they are applied. Tear down tasks + * should be chained so that they are executed in the reverse order to which they are applied. That way the set up and + * tear down tasks act as a 'task' stack, with nested setups and tear downs. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities. + * <tr><td> Handle injection of set up tasks. + * <tr><td> Handle injection of tear down tasks. + * </table> + * + * @author Rupert Smith + */ +public interface SetupTaskAware +{ + /** + * Adds the specified task to the tests setup. + * + * @param task The task to add to the tests setup. + */ + public void chainSetupTask(Runnable task); + + /** + * Adds the specified task to the tests tear down. + * + * @param task The task to add to the tests tear down. + */ + public void chainTearDownTask(Runnable task); +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskHandler.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskHandler.java index 00736c59e5..b91ce41ad3 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskHandler.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskHandler.java @@ -1,92 +1,92 @@ -/*
- *
- * 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.junit.extensions;
-
-import org.apache.qpid.junit.extensions.util.StackQueue;
-
-import java.util.LinkedList;
-import java.util.Queue;
-
-/**
- * SetupTaskHandler implements a task stack. It can be used, by delegation, as a base implementation for tests that want
- * to have configurable setup/teardown task stacks. Typically it is up to the test implementation to decide whether the
- * stack is executed in the setup/teardown methods or in the threadSetup/threadTeaddown methods.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Handle injection of set up tasks.
- * <tr><td> Handle injection of tear down tasks.
- * <tr><td> Run set up tasks in chain order.
- * <tr><td> Run tear down tasks in reverse chain order.
- * </table>
- *
- * @author Rupert Smith
- */
-public class SetupTaskHandler implements SetupTaskAware
-{
- /** Holds the set up tasks. */
- Queue<Runnable> setups = new LinkedList<Runnable>();
-
- /** Holds the tear down tasks. */
- Queue<Runnable> teardowns = new StackQueue<Runnable>();
-
- /**
- * Adds the specified task to the tests setup.
- *
- * @param task The task to add to the tests setup.
- */
- public void chainSetupTask(Runnable task)
- {
- setups.offer(task);
- }
-
- /**
- * Adds the specified task to the tests tear down.
- *
- * @param task The task to add to the tests tear down.
- */
- public void chainTearDownTask(Runnable task)
- {
- teardowns.offer(task);
- }
-
- /**
- * Runs the set up tasks in the order that they way chained.
- */
- public void runSetupTasks()
- {
- while (!setups.isEmpty())
- {
- setups.remove().run();
- }
- }
-
- /**
- * Runs the tear down tasks in the reverse of the order in which they were chained.
- */
- public void runTearDownTasks()
- {
- while (!teardowns.isEmpty())
- {
- teardowns.remove().run();
- }
- }
-}
+/* + * + * 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.junit.extensions; + +import org.apache.qpid.junit.extensions.util.StackQueue; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * SetupTaskHandler implements a task stack. It can be used, by delegation, as a base implementation for tests that want + * to have configurable setup/teardown task stacks. Typically it is up to the test implementation to decide whether the + * stack is executed in the setup/teardown methods or in the threadSetup/threadTeaddown methods. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Handle injection of set up tasks. + * <tr><td> Handle injection of tear down tasks. + * <tr><td> Run set up tasks in chain order. + * <tr><td> Run tear down tasks in reverse chain order. + * </table> + * + * @author Rupert Smith + */ +public class SetupTaskHandler implements SetupTaskAware +{ + /** Holds the set up tasks. */ + Queue<Runnable> setups = new LinkedList<Runnable>(); + + /** Holds the tear down tasks. */ + Queue<Runnable> teardowns = new StackQueue<Runnable>(); + + /** + * Adds the specified task to the tests setup. + * + * @param task The task to add to the tests setup. + */ + public void chainSetupTask(Runnable task) + { + setups.offer(task); + } + + /** + * Adds the specified task to the tests tear down. + * + * @param task The task to add to the tests tear down. + */ + public void chainTearDownTask(Runnable task) + { + teardowns.offer(task); + } + + /** + * Runs the set up tasks in the order that they way chained. + */ + public void runSetupTasks() + { + while (!setups.isEmpty()) + { + setups.remove().run(); + } + } + + /** + * Runs the tear down tasks in the reverse of the order in which they were chained. + */ + public void runTearDownTasks() + { + while (!teardowns.isEmpty()) + { + teardowns.remove().run(); + } + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ShutdownHookable.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ShutdownHookable.java index 344d7abf82..dc6aa3c291 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ShutdownHookable.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ShutdownHookable.java @@ -1,42 +1,42 @@ -/*
- *
- * 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.junit.extensions;
-
-/**
- * Defines an interface that classes which supply shutdown hooks implement. Code that creates these classes can check
- * if they supply a shutdown hook and register these hooks when the obejct are created.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities
- * <tr><td> Supply a shutdown hook.
- * </table>
- *
- * @author Rupert Smith
- */
-public interface ShutdownHookable
-{
- /**
- * Supplies the shutdown hook.
- *
- * @return The shut down hook.
- */
- public Thread getShutdownHook();
-}
+/* + * + * 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.junit.extensions; + +/** + * Defines an interface that classes which supply shutdown hooks implement. Code that creates these classes can check + * if they supply a shutdown hook and register these hooks when the obejct are created. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities + * <tr><td> Supply a shutdown hook. + * </table> + * + * @author Rupert Smith + */ +public interface ShutdownHookable +{ + /** + * Supplies the shutdown hook. + * + * @return The shut down hook. + */ + public Thread getShutdownHook(); +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SleepThrottle.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SleepThrottle.java index f7e350b1c7..2dc4c0e272 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SleepThrottle.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SleepThrottle.java @@ -1,81 +1,81 @@ -/*
- *
- * 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.junit.extensions;
-
-/**
- * SleepThrottle is a Throttle implementation that generates short pauses using the thread sleep methods. As the pauses
- * get shorter, this technique gets more innacurate. In practice, around 100 Hz is the cap rate for accuracy.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Accept throttling rate in operations per second.
- * <tr><td> Inject short pauses to fill out processing cycles to a specified rate.
- * <tr><td> Check against a throttle speed without waiting.
- * </table>
- *
- * @author Rupert Smith
- */
-public class SleepThrottle extends BaseThrottle implements Throttle
-{
- /** Holds the time of the last call to the throttle method in nano seconds. */
- private long lastTimeNanos;
-
- /**
- * This method can only be called at the rate set by the {@link #setRate} method, if it is called faster than this
- * it will inject short pauses to restrict the call rate to that rate.
- */
- public void throttle()
- {
- // Get the current time in nanos.
- long currentTimeNanos = System.nanoTime();
-
- // Don't introduce any pause on the first call.
- if (!firstCall)
- {
- // Check if there is any time left in the cycle since the last call to this method and introduce a short pause
- // to fill that time if there is.
- long remainingTimeNanos = cycleTimeNanos - (currentTimeNanos - lastTimeNanos);
-
- if (remainingTimeNanos > 0)
- {
- long milliPause = remainingTimeNanos / 1000000;
- int nanoPause = (int) (remainingTimeNanos % 1000000);
-
- try
- {
- Thread.sleep(milliPause, nanoPause);
- }
- catch (InterruptedException e)
- {
- // Restore the interrupted thread, in-case the caller is checking for it.
- Thread.currentThread().interrupt();
- }
- }
- }
- else
- {
- firstCall = false;
- }
-
- // Update the last time stamp.
- lastTimeNanos = System.nanoTime();
- }
-}
+/* + * + * 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.junit.extensions; + +/** + * SleepThrottle is a Throttle implementation that generates short pauses using the thread sleep methods. As the pauses + * get shorter, this technique gets more innacurate. In practice, around 100 Hz is the cap rate for accuracy. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Accept throttling rate in operations per second. + * <tr><td> Inject short pauses to fill out processing cycles to a specified rate. + * <tr><td> Check against a throttle speed without waiting. + * </table> + * + * @author Rupert Smith + */ +public class SleepThrottle extends BaseThrottle implements Throttle +{ + /** Holds the time of the last call to the throttle method in nano seconds. */ + private long lastTimeNanos; + + /** + * This method can only be called at the rate set by the {@link #setRate} method, if it is called faster than this + * it will inject short pauses to restrict the call rate to that rate. + */ + public void throttle() + { + // Get the current time in nanos. + long currentTimeNanos = System.nanoTime(); + + // Don't introduce any pause on the first call. + if (!firstCall) + { + // Check if there is any time left in the cycle since the last call to this method and introduce a short pause + // to fill that time if there is. + long remainingTimeNanos = cycleTimeNanos - (currentTimeNanos - lastTimeNanos); + + if (remainingTimeNanos > 0) + { + long milliPause = remainingTimeNanos / 1000000; + int nanoPause = (int) (remainingTimeNanos % 1000000); + + try + { + Thread.sleep(milliPause, nanoPause); + } + catch (InterruptedException e) + { + // Restore the interrupted thread, in-case the caller is checking for it. + Thread.currentThread().interrupt(); + } + } + } + else + { + firstCall = false; + } + + // Update the last time stamp. + lastTimeNanos = System.nanoTime(); + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestResult.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestResult.java index c9bcf3eb66..ae497c671b 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestResult.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestResult.java @@ -1,625 +1,625 @@ -/*
- *
- * 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.junit.extensions;
-
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestResult;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.junit.extensions.listeners.TKTestListener;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Properties;
-
-/**
- * TKTestResult extends TestResult in order to calculate test timings, to pass the variable integer parameter for
- * parameterized test cases to those test cases and to introduce an optional delay before test starts. Interested
- * {@link TKTestListener}s may be attached to this and will be informed of all relevant test statistics.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Calculate test timings.
- * <tr><td> Inform timing listeners of timings.
- * <tr><td> Inform memory listeners of memory readings.
- * <tr><td> Inform parameters listeners of parameters.
- * <tr><td> Pass the integer parameter to parameterized test cases.
- * <tr><td> Provide verbose test information on test start and end.
- * </table>
- *
- * @todo Move the verbose test information on test start/end into a test listener instead. It confuses the intention
- * of this class. Could also move the delay into a listener but that seems less appropriate as it would be a
- * side-effecting listener. Delay and timing calculation are fundamental enough to this class.
- *
- * @todo The need for this class to act as a place-holder for the integer parameter for parameterized test cases is
- * because this behaviour has been factored out into a test decorator class, see {@link AsymptoticTestDecorator}.
- * The {@link AsymptoticTestDecorator#run} method takes a TestResult as an argument and cannot easily get to the
- * {@link AsymptoticTestCase} class other than through this class. The option of using this class as a place hold
- * for this value was chosen. Alternatively this class could provide a method for decorators to access the
- * underlying test case through and then leave the setting of this parameter to the decorator which is a more
- * natural home for this behaviour. It would also provide a more general framework for decorators.
- *
- * @todo The memory usage may need to be moved in closer to the test method invocation so that as little code as possible
- * exists between it and the test or the results may be obscured. In fact it certainly does as the teardown method
- * is getting called first. Wouldn't be a bad idea to move the timing code in closer too.
- *
- * @todo Get rid of the delay logic. Will be replaced by throttle control.
- *
- * @author Rupert Smith
- */
-public class TKTestResult extends TestResult
-{
- /** Used for logging. */
- private static final Logger log = Logger.getLogger(TKTestResult.class);
-
- /** The delay between two tests. */
- private int delay = 0;
-
- /**
- * This flag indicates that the #completeTest method of the timing controller has been called. Once this has
- * been called once, the end test event for the whole test method should be ignored because tests have taken
- * charge of outputing their own timings.
- */
- private boolean completeTestUsed = false;
-
- /**
- * Thread locals to hold test start time for non-instrumented tests. (Instrumented tests hold their own
- * measurement data).
- */
- // private Hashtable threadStartTimeMap = new Hashtable();
- private ThreadLocal<ThreadLocalSettings> threadLocals = new ThreadLocal<ThreadLocalSettings>();
-
- /** Used to hold the current integer parameter to pass to parameterized tests. This defaults to 1. */
- private int n = 1;
-
- /** The timing listeners. */
- private Collection<TKTestListener> tkListeners;
-
- /** The test case name. */
- private String testCaseName;
-
- /** Used to hold the current concurrency level, set by the {@link ScaledTestDecorator}. */
- private int concurrencyLevel = 1;
-
- /** Flag used to indicate that this test result should attempt to complete its current tests as soon as possible. */
- private boolean shutdownNow = false;
-
- /** Holds the parametes that the test is run with. */
- private Properties testParameters;
-
- /**
- * Creates a new TKTestResult object.
- *
- * @param delay A delay in milliseconds to introduce before every test start.
- * @param testCaseName The name of the test case that this is the TestResult object for.
- */
- public TKTestResult(int delay, String testCaseName)
- {
- super();
-
- /*log.debug("public TKTestResult(PrintStream writer, int " + delay + ", boolean " + verbose + ", String "
- + testCaseName + "): called");*/
-
- // Keep all the parameters that this is created with.
- this.delay = delay;
- this.testCaseName = testCaseName;
- }
-
- /**
- * Callback method use to inform this test result that a test will be started. Waits for the configured delay time
- * if one has been set, starts the timer, then delegates to the super class implementation.
- *
- * @param test The test to be started.
- */
- public void startTest(Test test)
- {
- // log.debug("public void startTest(Test test): called");
-
- // If a delay time has been specified then wait for that length of time.
- if (this.delay > 0)
- {
- try
- {
- Thread.sleep(delay);
- }
- catch (InterruptedException e)
- {
- // Ignore, but restore the interrupted flag.
- Thread.currentThread().interrupt();
- }
- }
-
- // Create the thread local settings for the test.
- ThreadLocalSettings threadLocalSettings = new ThreadLocalSettings();
- threadLocals.set(threadLocalSettings);
-
- // Record the test start time against this thread for calculating the test timing. (Consider using ThreadLocal
- // instead?)
- Long startTime = System.nanoTime();
- threadLocalSettings.startTime = startTime;
- // log.debug("startTime = " + startTime);
-
- // Check if the test is timing controller aware, in which case set up a new timing controller and hold it
- // in the thread local settings.
- if (test instanceof TimingControllerAware)
- {
- TimingControllerAware controllerAware = (TimingControllerAware) test;
- TimingControllerImpl controller =
- new TimingControllerImpl(this, test, startTime, Thread.currentThread().getId());
- controllerAware.setTimingController(controller);
-
- threadLocalSettings.timingController = controller;
- }
-
- // Delegate to the super method to notify test event listeners.
- super.startTest(test);
- }
-
- /**
- * Callback method use to inform this result that a test was completed. This calculates how long the test took
- * to run, then delegates to the super class implementation.
- *
- * @param test The test that has ended.
- */
- public void endTest(Test test)
- {
- // log.debug("public void endTest(Test test): called");
-
- long runTime = 0;
-
- // Recover the thread local settings.
- ThreadLocalSettings threadLocalSettings = threadLocals.get();
-
- // Check if the test is an instrumented test and get the timing information from the instrumentation as this
- // will be more accurate.
- if (test instanceof InstrumentedTest)
- {
- InstrumentedTest iTest = (InstrumentedTest) test;
-
- // Calculate the test run time.
- runTime = iTest.getTestTime();
- // log.debug("runTime = " + runTime);
-
- // Calculate the test memory usage.
- long startMem = iTest.getTestStartMemory();
- long endMem = iTest.getTestEndMemory();
-
- // log.debug("startMem = " + startMem);
- // log.debug("endMem = " + endMem);
-
- // Inform any memory listeners of the test memory.
- if (tkListeners != null)
- {
- for (TKTestListener memoryListener : tkListeners)
- {
- memoryListener.memoryUsed(test, startMem, endMem, null);
- }
- }
- }
- else
- {
- // Calculate the test run time.
- long endTime = System.nanoTime();
- Long startTime = threadLocalSettings.startTime;
- runTime = endTime - startTime;
- // log.debug("runTime = " + runTime);
-
- threadLocals.remove();
- }
-
- // Output end test stats. This is only done when the tests have not used the timing controller to output
- // mutiple timings.
- if (!completeTestUsed)
- {
- // Check if the test is an asymptotic test case and get its int parameter if so.
- if (test instanceof AsymptoticTestCase)
- {
- AsymptoticTestCase pTest = (AsymptoticTestCase) test;
-
- // Set the parameter.
- int paramValue = pTest.getN();
-
- // Inform any parameter listeners of the test parameter.
- if (tkListeners != null)
- {
- for (TKTestListener parameterListener : tkListeners)
- {
- parameterListener.parameterValue(test, paramValue, null);
- }
- }
- }
-
- // Inform any timing listeners of the test timing and concurrency level.
- if (tkListeners != null)
- {
- for (TKTestListener tkListener : tkListeners)
- {
- TKTestListener next = tkListener;
-
- next.timing(test, runTime, null);
- next.concurrencyLevel(test, concurrencyLevel, null);
- }
- }
-
- // Call the super method to notify test event listeners of the end event.
- super.endTest(test);
- }
- }
-
- /**
- * Gets the integer parameter to pass to parameterized test cases.
- *
- * @return The value of the integer parameter.
- */
- public int getN()
- {
- return n;
- }
-
- /**
- * Sets the integer parameter to pass to parameterized test cases.
- *
- * @param n The new value of the integer parameter.
- */
- public void setN(int n)
- {
- // log.debug("public void setN(int " + n + "): called");
-
- this.n = n;
- }
-
- /**
- * Adds a timing listener to pass all timing events to.
- *
- * @param listener The timing listener to register.
- */
- public void addTKTestListener(TKTestListener listener)
- {
- // Create the collection to hold the timing listeners if it does not already exist.
- if (tkListeners == null)
- {
- tkListeners = new ArrayList<TKTestListener>();
- }
-
- // Keep the new timing listener.
- tkListeners.add(listener);
- }
-
- /**
- * Called by the test runner to notify this that a new test batch is being begun. This method forwards this
- * notification to all batch listeners.
- */
- public void notifyStartBatch()
- {
- if (tkListeners != null)
- {
- for (TKTestListener batchListener : tkListeners)
- {
- batchListener.startBatch();
- }
- }
- }
-
- /**
- * Called by the test runner to notify this that the current test batch has been ended. This method forwards this
- * notification to all batch listener.
- */
- public void notifyEndBatch()
- {
- // log.debug("public void notifyEndBatch(): called");
-
- if (tkListeners != null)
- {
- for (TKTestListener batchListener : tkListeners)
- {
- batchListener.endBatch(testParameters);
- }
- }
- }
-
- /**
- * Called by the test runner to notify this of the properties that the test is using.
- *
- * @param properties The tests set/read properties.
- */
- public void notifyTestProperties(Properties properties)
- {
- // log.debug("public void notifyTestProperties(Properties properties): called");
-
- this.testParameters = properties;
-
- /*
- if (tkListeners != null)
- {
- for (TKTestListener batchListener : tkListeners)
- {
- batchListener.properties(properties);
- }
- }
- */
- }
-
- /**
- * Intercepts the execution of a test case to pass the variable integer parameter to a test if it is a parameterized
- * test case.
- *
- * @param test The test to run.
- */
- protected void run(final TestCase test)
- {
- // log.debug("protected void run(final TestCase test): called");
-
- // Check if the test case is a parameterized test and set its integer parameter if so.
- if (test instanceof AsymptoticTestCase)
- {
- AsymptoticTestCase pTest = (AsymptoticTestCase) test;
-
- // Set up the integer parameter.
- pTest.setN(n);
- }
-
- // Delegate to the super method to run the test.
- super.run(test);
- }
-
- /**
- * Helper method that generats a String of verbose information about a test. This includes the thread name, test
- * class name and test method name.
- *
- * @param test The test to generate the info string for.
- *
- * @return Returns a string with the thread name, test class name and test method name.
- */
- protected String getTestInfo(Test test)
- {
- // log.debug("protected String getTestInfo(Test test): called");
-
- return "[" + Thread.currentThread().getName() + "@" + test.getClass().getName() + "."
- + ((test instanceof TestCase) ? ((TestCase) test).getName() : "") + "]";
- }
-
- /**
- * Sets the concurrency level to pass into the test result.
- *
- * @param concurrencyLevel The concurrency level the tests are running out.
- */
- public void setConcurrencyLevel(int concurrencyLevel)
- {
- this.concurrencyLevel = concurrencyLevel;
- }
-
- /**
- * Tells this test result that it should stop running tests. Once this method has been called this test result
- * will not start any new tests, and any tests that use the timing controller will be passed interrupted exceptions,
- * to indicate that they should end immediately. Usually the caller of this method will introduce a short wait
- * to allow an opporunity for running tests to complete, before forcing the shutdown of the JVM.
- */
- public void shutdownNow()
- {
- log.debug("public void shutdownNow(): called on " + this);
-
- shutdownNow = true;
- }
-
- /**
- * Prints a string summary of this class, mainly for debugging purposes.
- *
- * @return A string summary of this class, mainly for debugging purposes.
- */
- public String toString()
- {
- return "TKTestResult@" + Integer.toString(hashCode(), 16) + ": [ testCaseName = " + testCaseName + ", n = " + n
- + ", tkListeners = " + tkListeners + " ]";
- }
-
- /**
- * Holds things that need to be kept on a per thread basis for each test invocation, such as the test start
- * time and its timing controller.
- */
- private static class ThreadLocalSettings
- {
- /** Holds the test start time. */
- Long startTime;
-
- /** Holds the test threads timing controller. */
- TimingController timingController;
- }
-
- /**
- * Provides an implementation of the {@link TimingController} interface that timing aware tests can use to call
- * back to reset timers, and register additional test timings.
- */
- private static class TimingControllerImpl implements TimingController
- {
- /** Holds an explicit reference to the test TKTestResult that created this. */
- TKTestResult testResult;
-
- /** Holds a reference to the test that this is the timing controller for. */
- Test test;
-
- /** Holds the start time for this timing controller. This gets reset to now on each completed test. */
- long startTime;
-
- /**
- * Holds the thread id of the thread that started the test, so that this controller may be called from other
- * threads but still identify itself correctly to {@link TKTestListener}s as being associated with the
- * thread that called the test method.
- */
- long threadId;
-
- /**
- * Creates a timing controller on a specified TKTestResult and a test.
- *
- * @param testResult The TKTestResult that this controller interacts with.
- * @param test The test that this is the timing controller for.
- * @param startTime The test start time in nanoseconds.
- * @param threadId The thread id of the thread that is calling the test method.
- */
- public TimingControllerImpl(TKTestResult testResult, Test test, long startTime, long threadId)
- {
- this.testResult = testResult;
- this.test = test;
- this.startTime = startTime;
- this.threadId = threadId;
- }
-
- /**
- * Gets the timing controller associated with the current test thread. Tests that use timing controller should
- * always get the timing controller from this method in the same thread that called the setUp, tearDown or test
- * method. The controller returned by this method may be called from any thread because it remembers the thread
- * id of the original test thread.
- *
- * @return The timing controller associated with the current test thread.
- */
- public TimingController getControllerForCurrentThread()
- {
- // Recover the thread local settings and extract the timing controller from them.
- ThreadLocalSettings threadLocalSettings = testResult.threadLocals.get();
-
- return threadLocalSettings.timingController;
- }
-
- /**
- * Not implemented yet.
- *
- * @return Nothing.
- */
- public long suspend()
- {
- throw new RuntimeException("Method not implemented.");
- }
-
- /**
- * Not implemented yet.
- *
- * @return Nothing.
- */
- public long resume()
- {
- throw new RuntimeException("Method not implemented.");
- }
-
- /**
- * Resets the timer start time to now.
- *
- * @return The new value of the start time.
- */
- public long restart()
- {
- startTime = System.nanoTime();
-
- return startTime;
- }
-
- /**
- * Register an additional pass/fail for the current test. The test result is assumed to apply to a test of
- * 'size' parmeter 1. Use the {@link #completeTest(boolean, int)} method to register timings with parameters.
- *
- * @param testPassed Whether or not this timing is for a test pass or fail.
- *
- * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to
- * indicate to the test method that it should stop immediately.
- */
- public void completeTest(boolean testPassed) throws InterruptedException
- {
- completeTest(testPassed, 1);
- }
-
- /**
- * Register an additional pass/fail for the current test. The test result is applies to a test of the specified
- * 'size' parmeter.
- *
- * @param testPassed Whether or not this timing is for a test pass or fail.
- * @param param The test parameter size for parameterized tests.
- *
- * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to
- * indicate to the test method that it should stop immediately.
- */
- public void completeTest(boolean testPassed, int param) throws InterruptedException
- {
- /*log.debug("public long completeTest(boolean testPassed = " + testPassed + ", int param = " + param
- + "): called");*/
-
- // Calculate the test run time.
- long endTime = System.nanoTime();
- long runTime = endTime - startTime;
- // log.debug("runTime = " + runTime);
-
- // Reset the test start time to now, to reset the timer for the next result.
- startTime = endTime;
-
- completeTest(testPassed, param, runTime);
- }
-
- /**
- * Register an additional pass/fail for the current test. The test result is applies to a test of the specified
- * 'size' parmeter and allows the caller to sepecify the timing to log.
- *
- * @param testPassed Whether or not this timing is for a test pass or fail.
- * @param param The test parameter size for parameterized tests.
- * @param timeNanos The time in nano-seconds to log the test result with.
- *
- * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to
- * indicate to the test method that it should stop immediately.
- */
- public void completeTest(boolean testPassed, int param, long timeNanos) throws InterruptedException
- {
- log.debug("public void completeTest(boolean testPassed, int param, long timeNanos): called");
- log.debug("testResult = " + testResult);
-
- // Tell the test result that completeTest has been used, so to not register end test events for the whole
- // test method.
- testResult.completeTestUsed = true;
-
- // Inform any timing listeners of the test timings and parameters and send an end test notification using
- // the thread id of the thread that started the test.
- if (testResult.tkListeners != null)
- {
- for (TKTestListener listener : testResult.tkListeners)
- {
- listener.reset(test, threadId);
- listener.timing(test, timeNanos, threadId);
- listener.parameterValue(test, param, threadId);
- listener.concurrencyLevel(test, testResult.concurrencyLevel, threadId);
-
- if (!testPassed)
- {
- listener.addFailure(test, null, threadId);
- }
-
- listener.endTest(test, threadId);
- }
- }
-
- // log.debug("testResult.shutdownNow = " + testResult.shutdownNow);
-
- // Check if the test runner has been asked to shutdown and raise an interuppted exception if so.
- if (testResult.shutdownNow)
- {
- // log.debug("The shutdown flag is set.");
-
- throw new InterruptedException("Attempting clean shutdown by suspending current test.");
- }
- }
- }
-}
+/* + * + * 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.junit.extensions; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestResult; + +import org.apache.log4j.Logger; + +import org.apache.qpid.junit.extensions.listeners.TKTestListener; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Properties; + +/** + * TKTestResult extends TestResult in order to calculate test timings, to pass the variable integer parameter for + * parameterized test cases to those test cases and to introduce an optional delay before test starts. Interested + * {@link TKTestListener}s may be attached to this and will be informed of all relevant test statistics. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Calculate test timings. + * <tr><td> Inform timing listeners of timings. + * <tr><td> Inform memory listeners of memory readings. + * <tr><td> Inform parameters listeners of parameters. + * <tr><td> Pass the integer parameter to parameterized test cases. + * <tr><td> Provide verbose test information on test start and end. + * </table> + * + * @todo Move the verbose test information on test start/end into a test listener instead. It confuses the intention + * of this class. Could also move the delay into a listener but that seems less appropriate as it would be a + * side-effecting listener. Delay and timing calculation are fundamental enough to this class. + * + * @todo The need for this class to act as a place-holder for the integer parameter for parameterized test cases is + * because this behaviour has been factored out into a test decorator class, see {@link AsymptoticTestDecorator}. + * The {@link AsymptoticTestDecorator#run} method takes a TestResult as an argument and cannot easily get to the + * {@link AsymptoticTestCase} class other than through this class. The option of using this class as a place hold + * for this value was chosen. Alternatively this class could provide a method for decorators to access the + * underlying test case through and then leave the setting of this parameter to the decorator which is a more + * natural home for this behaviour. It would also provide a more general framework for decorators. + * + * @todo The memory usage may need to be moved in closer to the test method invocation so that as little code as possible + * exists between it and the test or the results may be obscured. In fact it certainly does as the teardown method + * is getting called first. Wouldn't be a bad idea to move the timing code in closer too. + * + * @todo Get rid of the delay logic. Will be replaced by throttle control. + * + * @author Rupert Smith + */ +public class TKTestResult extends TestResult +{ + /** Used for logging. */ + private static final Logger log = Logger.getLogger(TKTestResult.class); + + /** The delay between two tests. */ + private int delay = 0; + + /** + * This flag indicates that the #completeTest method of the timing controller has been called. Once this has + * been called once, the end test event for the whole test method should be ignored because tests have taken + * charge of outputing their own timings. + */ + private boolean completeTestUsed = false; + + /** + * Thread locals to hold test start time for non-instrumented tests. (Instrumented tests hold their own + * measurement data). + */ + // private Hashtable threadStartTimeMap = new Hashtable(); + private ThreadLocal<ThreadLocalSettings> threadLocals = new ThreadLocal<ThreadLocalSettings>(); + + /** Used to hold the current integer parameter to pass to parameterized tests. This defaults to 1. */ + private int n = 1; + + /** The timing listeners. */ + private Collection<TKTestListener> tkListeners; + + /** The test case name. */ + private String testCaseName; + + /** Used to hold the current concurrency level, set by the {@link ScaledTestDecorator}. */ + private int concurrencyLevel = 1; + + /** Flag used to indicate that this test result should attempt to complete its current tests as soon as possible. */ + private boolean shutdownNow = false; + + /** Holds the parametes that the test is run with. */ + private Properties testParameters; + + /** + * Creates a new TKTestResult object. + * + * @param delay A delay in milliseconds to introduce before every test start. + * @param testCaseName The name of the test case that this is the TestResult object for. + */ + public TKTestResult(int delay, String testCaseName) + { + super(); + + /*log.debug("public TKTestResult(PrintStream writer, int " + delay + ", boolean " + verbose + ", String " + + testCaseName + "): called");*/ + + // Keep all the parameters that this is created with. + this.delay = delay; + this.testCaseName = testCaseName; + } + + /** + * Callback method use to inform this test result that a test will be started. Waits for the configured delay time + * if one has been set, starts the timer, then delegates to the super class implementation. + * + * @param test The test to be started. + */ + public void startTest(Test test) + { + // log.debug("public void startTest(Test test): called"); + + // If a delay time has been specified then wait for that length of time. + if (this.delay > 0) + { + try + { + Thread.sleep(delay); + } + catch (InterruptedException e) + { + // Ignore, but restore the interrupted flag. + Thread.currentThread().interrupt(); + } + } + + // Create the thread local settings for the test. + ThreadLocalSettings threadLocalSettings = new ThreadLocalSettings(); + threadLocals.set(threadLocalSettings); + + // Record the test start time against this thread for calculating the test timing. (Consider using ThreadLocal + // instead?) + Long startTime = System.nanoTime(); + threadLocalSettings.startTime = startTime; + // log.debug("startTime = " + startTime); + + // Check if the test is timing controller aware, in which case set up a new timing controller and hold it + // in the thread local settings. + if (test instanceof TimingControllerAware) + { + TimingControllerAware controllerAware = (TimingControllerAware) test; + TimingControllerImpl controller = + new TimingControllerImpl(this, test, startTime, Thread.currentThread().getId()); + controllerAware.setTimingController(controller); + + threadLocalSettings.timingController = controller; + } + + // Delegate to the super method to notify test event listeners. + super.startTest(test); + } + + /** + * Callback method use to inform this result that a test was completed. This calculates how long the test took + * to run, then delegates to the super class implementation. + * + * @param test The test that has ended. + */ + public void endTest(Test test) + { + // log.debug("public void endTest(Test test): called"); + + long runTime = 0; + + // Recover the thread local settings. + ThreadLocalSettings threadLocalSettings = threadLocals.get(); + + // Check if the test is an instrumented test and get the timing information from the instrumentation as this + // will be more accurate. + if (test instanceof InstrumentedTest) + { + InstrumentedTest iTest = (InstrumentedTest) test; + + // Calculate the test run time. + runTime = iTest.getTestTime(); + // log.debug("runTime = " + runTime); + + // Calculate the test memory usage. + long startMem = iTest.getTestStartMemory(); + long endMem = iTest.getTestEndMemory(); + + // log.debug("startMem = " + startMem); + // log.debug("endMem = " + endMem); + + // Inform any memory listeners of the test memory. + if (tkListeners != null) + { + for (TKTestListener memoryListener : tkListeners) + { + memoryListener.memoryUsed(test, startMem, endMem, null); + } + } + } + else + { + // Calculate the test run time. + long endTime = System.nanoTime(); + Long startTime = threadLocalSettings.startTime; + runTime = endTime - startTime; + // log.debug("runTime = " + runTime); + + threadLocals.remove(); + } + + // Output end test stats. This is only done when the tests have not used the timing controller to output + // mutiple timings. + if (!completeTestUsed) + { + // Check if the test is an asymptotic test case and get its int parameter if so. + if (test instanceof AsymptoticTestCase) + { + AsymptoticTestCase pTest = (AsymptoticTestCase) test; + + // Set the parameter. + int paramValue = pTest.getN(); + + // Inform any parameter listeners of the test parameter. + if (tkListeners != null) + { + for (TKTestListener parameterListener : tkListeners) + { + parameterListener.parameterValue(test, paramValue, null); + } + } + } + + // Inform any timing listeners of the test timing and concurrency level. + if (tkListeners != null) + { + for (TKTestListener tkListener : tkListeners) + { + TKTestListener next = tkListener; + + next.timing(test, runTime, null); + next.concurrencyLevel(test, concurrencyLevel, null); + } + } + + // Call the super method to notify test event listeners of the end event. + super.endTest(test); + } + } + + /** + * Gets the integer parameter to pass to parameterized test cases. + * + * @return The value of the integer parameter. + */ + public int getN() + { + return n; + } + + /** + * Sets the integer parameter to pass to parameterized test cases. + * + * @param n The new value of the integer parameter. + */ + public void setN(int n) + { + // log.debug("public void setN(int " + n + "): called"); + + this.n = n; + } + + /** + * Adds a timing listener to pass all timing events to. + * + * @param listener The timing listener to register. + */ + public void addTKTestListener(TKTestListener listener) + { + // Create the collection to hold the timing listeners if it does not already exist. + if (tkListeners == null) + { + tkListeners = new ArrayList<TKTestListener>(); + } + + // Keep the new timing listener. + tkListeners.add(listener); + } + + /** + * Called by the test runner to notify this that a new test batch is being begun. This method forwards this + * notification to all batch listeners. + */ + public void notifyStartBatch() + { + if (tkListeners != null) + { + for (TKTestListener batchListener : tkListeners) + { + batchListener.startBatch(); + } + } + } + + /** + * Called by the test runner to notify this that the current test batch has been ended. This method forwards this + * notification to all batch listener. + */ + public void notifyEndBatch() + { + // log.debug("public void notifyEndBatch(): called"); + + if (tkListeners != null) + { + for (TKTestListener batchListener : tkListeners) + { + batchListener.endBatch(testParameters); + } + } + } + + /** + * Called by the test runner to notify this of the properties that the test is using. + * + * @param properties The tests set/read properties. + */ + public void notifyTestProperties(Properties properties) + { + // log.debug("public void notifyTestProperties(Properties properties): called"); + + this.testParameters = properties; + + /* + if (tkListeners != null) + { + for (TKTestListener batchListener : tkListeners) + { + batchListener.properties(properties); + } + } + */ + } + + /** + * Intercepts the execution of a test case to pass the variable integer parameter to a test if it is a parameterized + * test case. + * + * @param test The test to run. + */ + protected void run(final TestCase test) + { + // log.debug("protected void run(final TestCase test): called"); + + // Check if the test case is a parameterized test and set its integer parameter if so. + if (test instanceof AsymptoticTestCase) + { + AsymptoticTestCase pTest = (AsymptoticTestCase) test; + + // Set up the integer parameter. + pTest.setN(n); + } + + // Delegate to the super method to run the test. + super.run(test); + } + + /** + * Helper method that generats a String of verbose information about a test. This includes the thread name, test + * class name and test method name. + * + * @param test The test to generate the info string for. + * + * @return Returns a string with the thread name, test class name and test method name. + */ + protected String getTestInfo(Test test) + { + // log.debug("protected String getTestInfo(Test test): called"); + + return "[" + Thread.currentThread().getName() + "@" + test.getClass().getName() + "." + + ((test instanceof TestCase) ? ((TestCase) test).getName() : "") + "]"; + } + + /** + * Sets the concurrency level to pass into the test result. + * + * @param concurrencyLevel The concurrency level the tests are running out. + */ + public void setConcurrencyLevel(int concurrencyLevel) + { + this.concurrencyLevel = concurrencyLevel; + } + + /** + * Tells this test result that it should stop running tests. Once this method has been called this test result + * will not start any new tests, and any tests that use the timing controller will be passed interrupted exceptions, + * to indicate that they should end immediately. Usually the caller of this method will introduce a short wait + * to allow an opporunity for running tests to complete, before forcing the shutdown of the JVM. + */ + public void shutdownNow() + { + log.debug("public void shutdownNow(): called on " + this); + + shutdownNow = true; + } + + /** + * Prints a string summary of this class, mainly for debugging purposes. + * + * @return A string summary of this class, mainly for debugging purposes. + */ + public String toString() + { + return "TKTestResult@" + Integer.toString(hashCode(), 16) + ": [ testCaseName = " + testCaseName + ", n = " + n + + ", tkListeners = " + tkListeners + " ]"; + } + + /** + * Holds things that need to be kept on a per thread basis for each test invocation, such as the test start + * time and its timing controller. + */ + private static class ThreadLocalSettings + { + /** Holds the test start time. */ + Long startTime; + + /** Holds the test threads timing controller. */ + TimingController timingController; + } + + /** + * Provides an implementation of the {@link TimingController} interface that timing aware tests can use to call + * back to reset timers, and register additional test timings. + */ + private static class TimingControllerImpl implements TimingController + { + /** Holds an explicit reference to the test TKTestResult that created this. */ + TKTestResult testResult; + + /** Holds a reference to the test that this is the timing controller for. */ + Test test; + + /** Holds the start time for this timing controller. This gets reset to now on each completed test. */ + long startTime; + + /** + * Holds the thread id of the thread that started the test, so that this controller may be called from other + * threads but still identify itself correctly to {@link TKTestListener}s as being associated with the + * thread that called the test method. + */ + long threadId; + + /** + * Creates a timing controller on a specified TKTestResult and a test. + * + * @param testResult The TKTestResult that this controller interacts with. + * @param test The test that this is the timing controller for. + * @param startTime The test start time in nanoseconds. + * @param threadId The thread id of the thread that is calling the test method. + */ + public TimingControllerImpl(TKTestResult testResult, Test test, long startTime, long threadId) + { + this.testResult = testResult; + this.test = test; + this.startTime = startTime; + this.threadId = threadId; + } + + /** + * Gets the timing controller associated with the current test thread. Tests that use timing controller should + * always get the timing controller from this method in the same thread that called the setUp, tearDown or test + * method. The controller returned by this method may be called from any thread because it remembers the thread + * id of the original test thread. + * + * @return The timing controller associated with the current test thread. + */ + public TimingController getControllerForCurrentThread() + { + // Recover the thread local settings and extract the timing controller from them. + ThreadLocalSettings threadLocalSettings = testResult.threadLocals.get(); + + return threadLocalSettings.timingController; + } + + /** + * Not implemented yet. + * + * @return Nothing. + */ + public long suspend() + { + throw new RuntimeException("Method not implemented."); + } + + /** + * Not implemented yet. + * + * @return Nothing. + */ + public long resume() + { + throw new RuntimeException("Method not implemented."); + } + + /** + * Resets the timer start time to now. + * + * @return The new value of the start time. + */ + public long restart() + { + startTime = System.nanoTime(); + + return startTime; + } + + /** + * Register an additional pass/fail for the current test. The test result is assumed to apply to a test of + * 'size' parmeter 1. Use the {@link #completeTest(boolean, int)} method to register timings with parameters. + * + * @param testPassed Whether or not this timing is for a test pass or fail. + * + * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to + * indicate to the test method that it should stop immediately. + */ + public void completeTest(boolean testPassed) throws InterruptedException + { + completeTest(testPassed, 1); + } + + /** + * Register an additional pass/fail for the current test. The test result is applies to a test of the specified + * 'size' parmeter. + * + * @param testPassed Whether or not this timing is for a test pass or fail. + * @param param The test parameter size for parameterized tests. + * + * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to + * indicate to the test method that it should stop immediately. + */ + public void completeTest(boolean testPassed, int param) throws InterruptedException + { + /*log.debug("public long completeTest(boolean testPassed = " + testPassed + ", int param = " + param + + "): called");*/ + + // Calculate the test run time. + long endTime = System.nanoTime(); + long runTime = endTime - startTime; + // log.debug("runTime = " + runTime); + + // Reset the test start time to now, to reset the timer for the next result. + startTime = endTime; + + completeTest(testPassed, param, runTime); + } + + /** + * Register an additional pass/fail for the current test. The test result is applies to a test of the specified + * 'size' parmeter and allows the caller to sepecify the timing to log. + * + * @param testPassed Whether or not this timing is for a test pass or fail. + * @param param The test parameter size for parameterized tests. + * @param timeNanos The time in nano-seconds to log the test result with. + * + * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to + * indicate to the test method that it should stop immediately. + */ + public void completeTest(boolean testPassed, int param, long timeNanos) throws InterruptedException + { + log.debug("public void completeTest(boolean testPassed, int param, long timeNanos): called"); + log.debug("testResult = " + testResult); + + // Tell the test result that completeTest has been used, so to not register end test events for the whole + // test method. + testResult.completeTestUsed = true; + + // Inform any timing listeners of the test timings and parameters and send an end test notification using + // the thread id of the thread that started the test. + if (testResult.tkListeners != null) + { + for (TKTestListener listener : testResult.tkListeners) + { + listener.reset(test, threadId); + listener.timing(test, timeNanos, threadId); + listener.parameterValue(test, param, threadId); + listener.concurrencyLevel(test, testResult.concurrencyLevel, threadId); + + if (!testPassed) + { + listener.addFailure(test, null, threadId); + } + + listener.endTest(test, threadId); + } + } + + // log.debug("testResult.shutdownNow = " + testResult.shutdownNow); + + // Check if the test runner has been asked to shutdown and raise an interuppted exception if so. + if (testResult.shutdownNow) + { + // log.debug("The shutdown flag is set."); + + throw new InterruptedException("Attempting clean shutdown by suspending current test."); + } + } + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestRunner.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestRunner.java index 7955a2e2e9..671d33feed 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestRunner.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestRunner.java @@ -1,694 +1,694 @@ -/*
- *
- * 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.junit.extensions;
-
-import junit.framework.Test;
-import junit.framework.TestResult;
-import junit.framework.TestSuite;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.junit.extensions.listeners.CSVTestListener;
-import org.apache.qpid.junit.extensions.listeners.ConsoleTestListener;
-import org.apache.qpid.junit.extensions.listeners.XMLTestListener;
-import org.apache.qpid.junit.extensions.util.CommandLineParser;
-import org.apache.qpid.junit.extensions.util.MathUtils;
-import org.apache.qpid.junit.extensions.util.ParsedProperties;
-import org.apache.qpid.junit.extensions.util.TestContextProperties;
-
-import java.io.*;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * TKTestRunner extends {@link junit.textui.TestRunner} with the ability to run tests multiple times, to execute a test
- * simultaneously using many threads, to put a delay between test runs and adds support for tests that take integer
- * parameters that can be 'stepped' through on multiple test runs. These features can be accessed by using this class
- * as an entry point and passing command line arguments to specify which features to use:
- *
- * <pre>
- * -w ms The number of milliseconds between invocations of test cases.
- * -c pattern The number of tests to run concurrently.
- * -r num The number of times to repeat each test.
- * -d duration The length of time to run the tests for.
- * -t name The name of the test case to execute.
- * -s pattern The size parameter to run tests with.
- * -o dir The name of the directory to output test timings to.
- * --csv Output test results in CSV format.
- * --xml Output test results in XML format.
- * </pre>
- *
- * <p/>This command line may also have trailing 'name=value' parameters added to it. All of these values are added
- * to the test context properties and passed to the test, which can access them by name.
- *
- * <p/>The pattern arguments are of the form [lowest(: ...)(: highest)](:sample=s)(:exp), where round brackets
- * enclose optional values. Using this pattern form it is possible to specify a single value, a range of values divided
- * into s samples, a range of values divided into s samples but distributed exponentially, or a fixed set of samples.
- *
- * <p/>The duration arguments are of the form (dD)(hH)(mM)(sS), where round brackets enclose optional values. At least
- * one of the optional values must be present.
- *
- * <p/>When specifying optional test parameters on the command line, in 'name=value' format, it is also possible to use
- * the format 'name=[value1:value2:value3:...]', to specify multiple values for a parameter. All permutations of all
- * parameters with multiple values will be created and tested. If the values are numerical, it is also possible to use
- * the sequence generation patterns instead of fully specifying all of the values.
- *
- * <p/>Here are some examples:
- *
- * <p/><table>
- * <tr><td><pre> -c [10:20:30:40:50] </pre><td> Runs the test with 10,20,...,50 threads.
- * <tr><td><pre> -s [1:100]:samples=10 </pre>
- * <td> Runs the test with ten different size parameters evenly spaced between 1 and 100.
- * <tr><td><pre> -s [1:1000000]:samples=10:exp </pre>
- * <td> Runs the test with ten different size parameters exponentially spaced between 1 and 1000000.
- * <tr><td><pre> -r 10 </pre><td> Runs each test ten times.
- * <tr><td><pre> -d 10H </pre><td> Runs the test repeatedly for 10 hours.
- * <tr><td><pre> -d 1M, -r 10 </pre>
- * <td> Runs the test repeatedly for 1 minute but only takes a timing sample every 10 test runs.
- * <tr><td><pre> -r 10, -c [1:5:10:50], -s [100:1000:10000] </pre>
- * <td> Runs 12 test cycles (4 concurrency samples * 3 size sample), with 10 repeats each. In total the test
- * will be run 199 times (3 + 15 + 30 + 150)
- * <tr><td><pre> cache=true </pre><td> Passes the 'cache' parameter with value 'true' to the test.
- * <tr><td><pre> cache=[true:false] </pre><td> Runs the test with the 'cache' parameter set to 'true' and 'false'.
- * <tr><td><pre> cacheSize=[1000:1000000],samples=4,exp </pre>
- * <td> Runs the test with the 'cache' parameter set to a series of exponentially increasing sizes.
- * </table>
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Create the test configuration specified by the command line parameters.
- * </table>
- *
- * @todo Verify that the output directory exists or can be created.
- *
- * @todo Verify that the specific named test case to execute exists.
- *
- * @todo Drop the delay parameter as it is being replaced by throttling.
- *
- * @todo Completely replace the test ui test runner, instead of having TKTestRunner inherit from it, its just not
- * good code to extend.
- *
- * @author Rupert Smith
- */
-public class TKTestRunner extends TestRunnerImprovedErrorHandling
-{
- /** Used for debugging. */
- private static final Logger log = Logger.getLogger(TKTestRunner.class);
-
- /** Used for displaying information on the console. */
- // private static final Logger console = Logger.getLogger("CONSOLE." + TKTestRunner.class.getName());
-
- /** Used for generating the timestamp when naming output files. */
- protected static final DateFormat TIME_STAMP_FORMAT = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss");
-
- /** Number of times to rerun the test. */
- protected Integer repetitions = 1;
-
- /** The length of time to run the tests for. */
- protected Long duration;
-
- /** Number of threads running the tests. */
- protected int[] threads;
-
- /** Delay in ms to wait between two test cases. */
- protected int delay = 0;
-
- /** The parameter values to pass to parameterized tests. */
- protected int[] params;
-
- /** Name of the single test case to execute. */
- protected String testCaseName = null;
-
- /** Name of the test class. */
- protected String testClassName = null;
-
- /** Name of the test run. */
- protected String testRunName = null;
-
- /** Directory to output XML reports into, if specified. */
- protected String reportDir = null;
-
- /** Flag that indicates the CSV results listener should be used to output results. */
- protected boolean csvResults;
-
- /** Flag that indiciates the XML results listener should be used to output results. */
- protected boolean xmlResults;
-
- /**
- * Holds the name of the class of the test currently being run. Ideally passed into the {@link #createTestResult}
- * method, but as the signature is already fixed for this, the current value gets pushed here as a member variable.
- */
- protected String currentTestClassName;
-
- /** Holds the test results object, which is reponsible for instrumenting tests/threads to record results. */
- protected TKTestResult result;
-
- /** Holds a list of factories for instantiating optional user specified test decorators. */
- protected List<TestDecoratorFactory> decoratorFactories;
-
- /**
- * Constructs a TKTestRunner using System.out for all the output.
- *
- * @param repetitions The number of times to repeat the test, or test batch size.
- * @param duration The length of time to run the tests for. -1 means no duration has been set.
- * @param threads The concurrency levels to ramp up to.
- * @param delay A delay in milliseconds between test runs.
- * @param params The sets of 'size' parameters to pass to test.
- * @param testCaseName The name of the test case to run.
- * @param reportDir The directory to output the test results to.
- * @param runName The name of the test run; used to name the output file.
- * @param csvResults <tt>true</tt> if the CSV results listener should be attached.
- * @param xmlResults <tt>true</tt> if the XML results listener should be attached.
- * @param decoratorFactories List of factories for user specified decorators.
- */
- public TKTestRunner(Integer repetitions, Long duration, int[] threads, int delay, int[] params, String testCaseName,
- String reportDir, String runName, boolean csvResults, boolean xmlResults,
- List<TestDecoratorFactory> decoratorFactories)
- {
- super(new NullResultPrinter(System.out));
-
- log.debug("public TKTestRunner(): called");
-
- // Keep all the test parameters.
- this.repetitions = repetitions;
- this.duration = duration;
- this.threads = threads;
- this.delay = delay;
- this.params = params;
- this.testCaseName = testCaseName;
- this.reportDir = reportDir;
- this.testRunName = runName;
- this.csvResults = csvResults;
- this.xmlResults = xmlResults;
- this.decoratorFactories = decoratorFactories;
- }
-
- /**
- * The entry point for the toolkit test runner.
- *
- * @param args The command line arguments.
- */
- public static void main(String[] args)
- {
- // Use the command line parser to evaluate the command line.
- CommandLineParser commandLine =
- new CommandLineParser(
- new String[][]
- {
- { "w", "The number of milliseconds between invocations of test cases.", "ms", "false" },
- { "c", "The number of tests to run concurrently.", "num", "false", MathUtils.SEQUENCE_REGEXP },
- { "r", "The number of times to repeat each test.", "num", "false" },
- { "d", "The length of time to run the tests for.", "duration", "false", MathUtils.DURATION_REGEXP },
- { "f", "The maximum rate to call the tests at.", "frequency", "false", "^([1-9][0-9]*)/([1-9][0-9]*)$" },
- { "s", "The size parameter to run tests with.", "size", "false", MathUtils.SEQUENCE_REGEXP },
- { "t", "The name of the test case to execute.", "name", "false" },
- { "o", "The name of the directory to output test timings to.", "dir", "false" },
- { "n", "A name for this test run, used to name the output file.", "name", "true" },
- {
- "X:decorators", "A list of additional test decorators to wrap the tests in.",
- "\"class.name[:class.name]*\"", "false"
- },
- { "1", "Test class.", "class", "true" },
- { "-csv", "Output test results in CSV format.", null, "false" },
- { "-xml", "Output test results in XML format.", null, "false" }
- });
-
- // Capture the command line arguments or display errors and correct usage and then exit.
- ParsedProperties options = null;
-
- try
- {
- options = new ParsedProperties(commandLine.parseCommandLine(args));
- }
- catch (IllegalArgumentException e)
- {
- System.out.println(commandLine.getErrors());
- System.out.println(commandLine.getUsage());
- System.exit(FAILURE_EXIT);
- }
-
- // Extract the command line options.
- Integer delay = options.getPropertyAsInteger("w");
- String threadsString = options.getProperty("c");
- Integer repetitions = options.getPropertyAsInteger("r");
- String durationString = options.getProperty("d");
- String paramsString = options.getProperty("s");
- String testCaseName = options.getProperty("t");
- String reportDir = options.getProperty("o");
- String testRunName = options.getProperty("n");
- String decorators = options.getProperty("X:decorators");
- String testClassName = options.getProperty("1");
- boolean csvResults = options.getPropertyAsBoolean("-csv");
- boolean xmlResults = options.getPropertyAsBoolean("-xml");
-
- int[] threads = (threadsString == null) ? null : MathUtils.parseSequence(threadsString);
- int[] params = (paramsString == null) ? null : MathUtils.parseSequence(paramsString);
- Long duration = (durationString == null) ? null : MathUtils.parseDuration(durationString);
-
- // The test run name defaults to the test class name unless a value was specified for it.
- testRunName = (testRunName == null) ? testClassName : testRunName;
-
- // Add all the command line options and trailing settings to test context properties. Tests may pick up
- // overridden values from there, and these values will be logged in the test results, for analysis and
- // to make tests repeatable.
- commandLine.addTrailingPairsToProperties(TestContextProperties.getInstance());
- commandLine.addOptionsToProperties(TestContextProperties.getInstance());
-
- // Create and start the test runner.
- try
- {
- // Create a list of test decorator factories for use specified decorators to be applied.
- List<TestDecoratorFactory> decoratorFactories = parseDecorators(decorators);
-
- TKTestRunner testRunner =
- new TKTestRunner(repetitions, duration, threads, (delay == null) ? 0 : delay, params, testCaseName,
- reportDir, testRunName, csvResults, xmlResults, decoratorFactories);
-
- TestResult testResult = testRunner.start(testClassName);
-
- if (!testResult.wasSuccessful())
- {
- System.exit(FAILURE_EXIT);
- }
- }
- catch (Exception e)
- {
- System.err.println(e.getMessage());
- e.printStackTrace(new PrintStream(System.err));
- System.exit(EXCEPTION_EXIT);
- }
- }
-
- /**
- * Parses a list of test decorators, in the form "class.name[:class.name]*", and creates factories for those
- * TestDecorator classes , and returns a list of the factories. This list of factories will be in the same
- * order as specified in the string. The factories can be used to succesively wrap tests in layers of
- * decorators, as decorators themselves implement the 'Test' interface.
- *
- * <p/>If the string fails to parse, or if any of the decorators specified in it are cannot be loaded, or are not
- * TestDecorators, a runtime exception with a suitable error message will be thrown. The factories themselves
- * throw runtimes if the constructor method calls on the decorators fail.
- *
- * @param decorators The decorators list to be parsed.
- *
- * @return A list of instantiated decorators.
- */
- protected static List<TestDecoratorFactory> parseDecorators(String decorators)
- {
- List<TestDecoratorFactory> result = new LinkedList<TestDecoratorFactory>();
- String toParse = decorators;
-
- // Check that the decorators string is not null or empty, returning an empty list of decorator factories it
- // it is.
- if ((decorators == null) || "".equals(decorators))
- {
- return result;
- }
-
- // Strip any leading and trailing quotes from the string.
- if (toParse.charAt(0) == '\"')
- {
- toParse = toParse.substring(1, toParse.length() - 1);
- }
-
- if (toParse.charAt(toParse.length() - 1) == '\"')
- {
- toParse = toParse.substring(0, toParse.length() - 2);
- }
-
- // Instantiate all decorators.
- for (String decoratorClassName : toParse.split(":"))
- {
- try
- {
- Class decoratorClass = Class.forName(decoratorClassName);
- final Constructor decoratorConstructor = decoratorClass.getConstructor(WrappedSuiteTestDecorator.class);
-
- // Check that the decorator is an instance of WrappedSuiteTestDecorator.
- if (!WrappedSuiteTestDecorator.class.isAssignableFrom(decoratorClass))
- {
- throw new RuntimeException("The decorator class " + decoratorClassName
- + " is not a sub-class of WrappedSuiteTestDecorator, which it needs to be.");
- }
-
- result.add(new TestDecoratorFactory()
- {
- public WrappedSuiteTestDecorator decorateTest(Test test)
- {
- try
- {
- return (WrappedSuiteTestDecorator) decoratorConstructor.newInstance(test);
- }
- catch (InstantiationException e)
- {
- throw new RuntimeException(
- "The decorator class " + decoratorConstructor.getDeclaringClass().getName()
- + " cannot be instantiated.", e);
- }
- catch (IllegalAccessException e)
- {
- throw new RuntimeException(
- "The decorator class " + decoratorConstructor.getDeclaringClass().getName()
- + " does not have a publicly accessable constructor.", e);
- }
- catch (InvocationTargetException e)
- {
- throw new RuntimeException(
- "The decorator class " + decoratorConstructor.getDeclaringClass().getName()
- + " cannot be invoked.", e);
- }
- }
- });
- }
- catch (ClassNotFoundException e)
- {
- throw new RuntimeException("The decorator class " + decoratorClassName + " could not be found.", e);
- }
- catch (NoSuchMethodException e)
- {
- throw new RuntimeException("The decorator class " + decoratorClassName
- + " does not have a constructor that accepts a single 'WrappedSuiteTestDecorator' argument.", e);
- }
- }
-
- return result;
- }
-
- /**
- * TestDecoratorFactory is a factory for creating test decorators from tests.
- */
- protected interface TestDecoratorFactory
- {
- /**
- * Decorates the specified test with a new decorator.
- *
- * @param test The test to decorate.
- *
- * @return The decorated test.
- */
- public WrappedSuiteTestDecorator decorateTest(Test test);
- }
-
- /**
- * Runs a test or suite of tests, using the super class implemenation. This method wraps the test to be run
- * in any test decorators needed to add in the configured toolkits enhanced junit functionality.
- *
- * @param test The test to run.
- * @param wait Undocumented. Nothing in the JUnit javadocs to say what this is for.
- *
- * @return The results of the test run.
- */
- public TestResult doRun(Test test, boolean wait)
- {
- log.debug("public TestResult doRun(Test \"" + test + "\", boolean " + wait + "): called");
-
- // Wrap the tests in decorators for duration, scaling, repetition, parameterization etc.
- WrappedSuiteTestDecorator targetTest = decorateTests(test);
-
- // Delegate to the super method to run the decorated tests.
- log.debug("About to call super.doRun");
-
- TestResult result = super.doRun(targetTest, wait);
- log.debug("super.doRun returned.");
-
- /*if (result instanceof TKTestResult)
- {
- TKTestResult tkResult = (TKTestResult) result;
-
- tkResult.notifyEndBatch();
- }*/
-
- return result;
- }
-
- /**
- * Applies test decorators to the tests for parameterization, duration, scaling and repetition.
- *
- * @param test The test to decorat.
- *
- * @return The decorated test.
- */
- protected WrappedSuiteTestDecorator decorateTests(Test test)
- {
- log.debug("params = " + ((params == null) ? null : MathUtils.printArray(params)));
- log.debug("repetitions = " + repetitions);
- log.debug("threads = " + ((threads == null) ? null : MathUtils.printArray(threads)));
- log.debug("duration = " + duration);
-
- // Wrap all tests in the test suite with WrappedSuiteTestDecorators. This is quite ugly and a bit baffling,
- // but the reason it is done is because the JUnit implementation of TestDecorator has some bugs in it.
- WrappedSuiteTestDecorator targetTest = null;
-
- if (test instanceof TestSuite)
- {
- log.debug("targetTest is a TestSuite");
-
- TestSuite suite = (TestSuite) test;
-
- int numTests = suite.countTestCases();
- log.debug("There are " + numTests + " in the suite.");
-
- for (int i = 0; i < numTests; i++)
- {
- Test nextTest = suite.testAt(i);
- log.debug("suite.testAt(" + i + ") = " + nextTest);
-
- if (nextTest instanceof TimingControllerAware)
- {
- log.debug("nextTest is TimingControllerAware");
- }
-
- if (nextTest instanceof TestThreadAware)
- {
- log.debug("nextTest is TestThreadAware");
- }
- }
-
- targetTest = new WrappedSuiteTestDecorator(suite);
- log.debug("Wrapped with a WrappedSuiteTestDecorator.");
- }
- // If the test has already been wrapped, no need to do it again.
- else if (test instanceof WrappedSuiteTestDecorator)
- {
- targetTest = (WrappedSuiteTestDecorator) test;
- }
-
- // If size parameter values have been set, then wrap the test in an asymptotic test decorator.
- if (params != null)
- {
- targetTest = new AsymptoticTestDecorator(targetTest, params, (repetitions == null) ? 1 : repetitions);
- log.debug("Wrapped with asymptotic test decorator.");
- log.debug("targetTest = " + targetTest);
- }
-
- // If no size parameters are set but the repitions parameter is, then wrap the test in an asymptotic test decorator.
- else if ((repetitions != null) && (repetitions > 1))
- {
- targetTest = new AsymptoticTestDecorator(targetTest, new int[] { 1 }, repetitions);
- log.debug("Wrapped with asymptotic test decorator.");
- log.debug("targetTest = " + targetTest);
- }
-
- // Apply any optional user specified decorators.
- targetTest = applyOptionalUserDecorators(targetTest);
-
- // If a test run duration has been set then wrap the test in a duration test decorator. This will wrap on
- // top of size, repeat or concurrency wrappings already applied.
- if (duration != null)
- {
- DurationTestDecorator durationTest = new DurationTestDecorator(targetTest, duration);
- targetTest = durationTest;
-
- log.debug("Wrapped with duration test decorator.");
- log.debug("targetTest = " + targetTest);
-
- registerShutdownHook(durationTest);
- }
-
- // ParameterVariationTestDecorator...
-
- // If a test thread concurrency level is set then wrap the test in a scaled test decorator. This will wrap on
- // top of size scaling or repetition wrappings.
- ScaledTestDecorator scaledDecorator;
-
- if ((threads != null) && ((threads.length > 1) || (MathUtils.maxInArray(threads) > 1)))
- {
- scaledDecorator = new ScaledTestDecorator(targetTest, threads);
- targetTest = scaledDecorator;
- log.debug("Wrapped with scaled test decorator.");
- log.debug("targetTest = " + targetTest);
- }
- else
- {
- scaledDecorator = new ScaledTestDecorator(targetTest, new int[] { 1 });
- targetTest = scaledDecorator;
- log.debug("Wrapped with scaled test decorator with default of 1 thread.");
- log.debug("targetTest = " + targetTest);
- }
-
- // Register the scaled test decorators shutdown hook.
- registerShutdownHook(scaledDecorator);
-
- return targetTest;
- }
-
- /**
- * If there were any user specified test decorators on the command line, this method instantiates them and wraps
- * the test in them, from inner-most to outer-most in the order in which the decorators were supplied on the
- * command line.
- *
- * @param targetTest The test to wrap.
- *
- * @return A wrapped test.
- */
- protected WrappedSuiteTestDecorator applyOptionalUserDecorators(WrappedSuiteTestDecorator targetTest)
- {
- // If there are user defined test decorators apply them in order now.
- for (TestDecoratorFactory factory : decoratorFactories)
- {
- targetTest = factory.decorateTest(targetTest);
- }
-
- return targetTest;
- }
-
- /**
- * Creates the TestResult object to be used for test runs. See {@link TKTestResult} for more information and the
- * enhanced test result class that this uses.
- *
- * @return An instance of the enhanced test result object, {@link TKTestResult}.
- */
- protected TestResult createTestResult()
- {
- log.debug("protected TestResult createTestResult(): called");
-
- TKTestResult result = new TKTestResult(delay, testCaseName);
-
- // Check if a directory to output reports to has been specified and attach test listeners if so.
- if (reportDir != null)
- {
- // Create the report directory if it does not already exist.
- File reportDirFile = new File(reportDir);
-
- if (!reportDirFile.exists())
- {
- reportDirFile.mkdir();
- }
-
- // Create the results file (make the name of this configurable as a command line parameter).
- Writer timingsWriter;
-
- // Always set up a console feedback listener.
- ConsoleTestListener feedbackListener = new ConsoleTestListener();
- result.addListener(feedbackListener);
- result.addTKTestListener(feedbackListener);
-
- // Set up an XML results listener to output the timings to the results file, if requested on the command line.
- if (xmlResults)
- {
- try
- {
- File timingsFile = new File(reportDirFile, "TEST-" + currentTestClassName + ".xml");
- timingsWriter = new BufferedWriter(new FileWriter(timingsFile), 20000);
- }
- catch (IOException e)
- {
- throw new RuntimeException("Unable to create the log file to write test results to: " + e, e);
- }
-
- XMLTestListener listener = new XMLTestListener(timingsWriter, currentTestClassName);
- result.addListener(listener);
- result.addTKTestListener(listener);
-
- registerShutdownHook(listener);
- }
-
- // Set up an CSV results listener to output the timings to the results file, if requested on the command line.
- if (csvResults)
- {
- try
- {
- File timingsFile =
- new File(reportDirFile, testRunName + "-" + TIME_STAMP_FORMAT.format(new Date()) + "-timings.csv");
- timingsWriter = new BufferedWriter(new FileWriter(timingsFile), 20000);
- }
- catch (IOException e)
- {
- throw new RuntimeException("Unable to create the log file to write test results to: " + e, e);
- }
-
- CSVTestListener listener = new CSVTestListener(timingsWriter);
- result.addListener(listener);
- result.addTKTestListener(listener);
-
- // Register the results listeners shutdown hook to flush its data if the test framework is shutdown
- // prematurely.
- registerShutdownHook(listener);
- }
-
- // Register the results listeners shutdown hook to flush its data if the test framework is shutdown
- // prematurely.
- // registerShutdownHook(listener);
-
- // Record the start time of the batch.
- // result.notifyStartBatch();
-
- // At this point in time the test class has been instantiated, giving it an opportunity to read its parameters.
- // Inform any test listers of the test properties.
- result.notifyTestProperties(TestContextProperties.getAccessedProps());
- }
-
- return result;
- }
-
- /**
- * Registers the shutdown hook of a {@link ShutdownHookable}.
- *
- * @param hookable The hookable to register.
- */
- protected void registerShutdownHook(ShutdownHookable hookable)
- {
- Runtime.getRuntime().addShutdownHook(hookable.getShutdownHook());
- }
-
- /**
- * Initializes the test runner with the provided command line arguments and and starts the test run.
- *
- * @param testClassName The fully qualified name of the test class to run.
- *
- * @return The test results.
- *
- * @throws Exception Any exceptions from running the tests are allowed to fall through.
- */
- protected TestResult start(String testClassName) throws Exception
- {
- // Record the current test class, so that the test results can be output to a file incorporating this name.
- this.currentTestClassName = testClassName;
-
- // Delegate to the super method to run the tests.
- return super.start(new String[] { testClassName });
- }
-}
+/* + * + * 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.junit.extensions; + +import junit.framework.Test; +import junit.framework.TestResult; +import junit.framework.TestSuite; + +import org.apache.log4j.Logger; + +import org.apache.qpid.junit.extensions.listeners.CSVTestListener; +import org.apache.qpid.junit.extensions.listeners.ConsoleTestListener; +import org.apache.qpid.junit.extensions.listeners.XMLTestListener; +import org.apache.qpid.junit.extensions.util.CommandLineParser; +import org.apache.qpid.junit.extensions.util.MathUtils; +import org.apache.qpid.junit.extensions.util.ParsedProperties; +import org.apache.qpid.junit.extensions.util.TestContextProperties; + +import java.io.*; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +/** + * TKTestRunner extends {@link junit.textui.TestRunner} with the ability to run tests multiple times, to execute a test + * simultaneously using many threads, to put a delay between test runs and adds support for tests that take integer + * parameters that can be 'stepped' through on multiple test runs. These features can be accessed by using this class + * as an entry point and passing command line arguments to specify which features to use: + * + * <pre> + * -w ms The number of milliseconds between invocations of test cases. + * -c pattern The number of tests to run concurrently. + * -r num The number of times to repeat each test. + * -d duration The length of time to run the tests for. + * -t name The name of the test case to execute. + * -s pattern The size parameter to run tests with. + * -o dir The name of the directory to output test timings to. + * --csv Output test results in CSV format. + * --xml Output test results in XML format. + * </pre> + * + * <p/>This command line may also have trailing 'name=value' parameters added to it. All of these values are added + * to the test context properties and passed to the test, which can access them by name. + * + * <p/>The pattern arguments are of the form [lowest(: ...)(: highest)](:sample=s)(:exp), where round brackets + * enclose optional values. Using this pattern form it is possible to specify a single value, a range of values divided + * into s samples, a range of values divided into s samples but distributed exponentially, or a fixed set of samples. + * + * <p/>The duration arguments are of the form (dD)(hH)(mM)(sS), where round brackets enclose optional values. At least + * one of the optional values must be present. + * + * <p/>When specifying optional test parameters on the command line, in 'name=value' format, it is also possible to use + * the format 'name=[value1:value2:value3:...]', to specify multiple values for a parameter. All permutations of all + * parameters with multiple values will be created and tested. If the values are numerical, it is also possible to use + * the sequence generation patterns instead of fully specifying all of the values. + * + * <p/>Here are some examples: + * + * <p/><table> + * <tr><td><pre> -c [10:20:30:40:50] </pre><td> Runs the test with 10,20,...,50 threads. + * <tr><td><pre> -s [1:100]:samples=10 </pre> + * <td> Runs the test with ten different size parameters evenly spaced between 1 and 100. + * <tr><td><pre> -s [1:1000000]:samples=10:exp </pre> + * <td> Runs the test with ten different size parameters exponentially spaced between 1 and 1000000. + * <tr><td><pre> -r 10 </pre><td> Runs each test ten times. + * <tr><td><pre> -d 10H </pre><td> Runs the test repeatedly for 10 hours. + * <tr><td><pre> -d 1M, -r 10 </pre> + * <td> Runs the test repeatedly for 1 minute but only takes a timing sample every 10 test runs. + * <tr><td><pre> -r 10, -c [1:5:10:50], -s [100:1000:10000] </pre> + * <td> Runs 12 test cycles (4 concurrency samples * 3 size sample), with 10 repeats each. In total the test + * will be run 199 times (3 + 15 + 30 + 150) + * <tr><td><pre> cache=true </pre><td> Passes the 'cache' parameter with value 'true' to the test. + * <tr><td><pre> cache=[true:false] </pre><td> Runs the test with the 'cache' parameter set to 'true' and 'false'. + * <tr><td><pre> cacheSize=[1000:1000000],samples=4,exp </pre> + * <td> Runs the test with the 'cache' parameter set to a series of exponentially increasing sizes. + * </table> + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Create the test configuration specified by the command line parameters. + * </table> + * + * @todo Verify that the output directory exists or can be created. + * + * @todo Verify that the specific named test case to execute exists. + * + * @todo Drop the delay parameter as it is being replaced by throttling. + * + * @todo Completely replace the test ui test runner, instead of having TKTestRunner inherit from it, its just not + * good code to extend. + * + * @author Rupert Smith + */ +public class TKTestRunner extends TestRunnerImprovedErrorHandling +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TKTestRunner.class); + + /** Used for displaying information on the console. */ + // private static final Logger console = Logger.getLogger("CONSOLE." + TKTestRunner.class.getName()); + + /** Used for generating the timestamp when naming output files. */ + protected static final DateFormat TIME_STAMP_FORMAT = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss"); + + /** Number of times to rerun the test. */ + protected Integer repetitions = 1; + + /** The length of time to run the tests for. */ + protected Long duration; + + /** Number of threads running the tests. */ + protected int[] threads; + + /** Delay in ms to wait between two test cases. */ + protected int delay = 0; + + /** The parameter values to pass to parameterized tests. */ + protected int[] params; + + /** Name of the single test case to execute. */ + protected String testCaseName = null; + + /** Name of the test class. */ + protected String testClassName = null; + + /** Name of the test run. */ + protected String testRunName = null; + + /** Directory to output XML reports into, if specified. */ + protected String reportDir = null; + + /** Flag that indicates the CSV results listener should be used to output results. */ + protected boolean csvResults; + + /** Flag that indiciates the XML results listener should be used to output results. */ + protected boolean xmlResults; + + /** + * Holds the name of the class of the test currently being run. Ideally passed into the {@link #createTestResult} + * method, but as the signature is already fixed for this, the current value gets pushed here as a member variable. + */ + protected String currentTestClassName; + + /** Holds the test results object, which is reponsible for instrumenting tests/threads to record results. */ + protected TKTestResult result; + + /** Holds a list of factories for instantiating optional user specified test decorators. */ + protected List<TestDecoratorFactory> decoratorFactories; + + /** + * Constructs a TKTestRunner using System.out for all the output. + * + * @param repetitions The number of times to repeat the test, or test batch size. + * @param duration The length of time to run the tests for. -1 means no duration has been set. + * @param threads The concurrency levels to ramp up to. + * @param delay A delay in milliseconds between test runs. + * @param params The sets of 'size' parameters to pass to test. + * @param testCaseName The name of the test case to run. + * @param reportDir The directory to output the test results to. + * @param runName The name of the test run; used to name the output file. + * @param csvResults <tt>true</tt> if the CSV results listener should be attached. + * @param xmlResults <tt>true</tt> if the XML results listener should be attached. + * @param decoratorFactories List of factories for user specified decorators. + */ + public TKTestRunner(Integer repetitions, Long duration, int[] threads, int delay, int[] params, String testCaseName, + String reportDir, String runName, boolean csvResults, boolean xmlResults, + List<TestDecoratorFactory> decoratorFactories) + { + super(new NullResultPrinter(System.out)); + + log.debug("public TKTestRunner(): called"); + + // Keep all the test parameters. + this.repetitions = repetitions; + this.duration = duration; + this.threads = threads; + this.delay = delay; + this.params = params; + this.testCaseName = testCaseName; + this.reportDir = reportDir; + this.testRunName = runName; + this.csvResults = csvResults; + this.xmlResults = xmlResults; + this.decoratorFactories = decoratorFactories; + } + + /** + * The entry point for the toolkit test runner. + * + * @param args The command line arguments. + */ + public static void main(String[] args) + { + // Use the command line parser to evaluate the command line. + CommandLineParser commandLine = + new CommandLineParser( + new String[][] + { + { "w", "The number of milliseconds between invocations of test cases.", "ms", "false" }, + { "c", "The number of tests to run concurrently.", "num", "false", MathUtils.SEQUENCE_REGEXP }, + { "r", "The number of times to repeat each test.", "num", "false" }, + { "d", "The length of time to run the tests for.", "duration", "false", MathUtils.DURATION_REGEXP }, + { "f", "The maximum rate to call the tests at.", "frequency", "false", "^([1-9][0-9]*)/([1-9][0-9]*)$" }, + { "s", "The size parameter to run tests with.", "size", "false", MathUtils.SEQUENCE_REGEXP }, + { "t", "The name of the test case to execute.", "name", "false" }, + { "o", "The name of the directory to output test timings to.", "dir", "false" }, + { "n", "A name for this test run, used to name the output file.", "name", "true" }, + { + "X:decorators", "A list of additional test decorators to wrap the tests in.", + "\"class.name[:class.name]*\"", "false" + }, + { "1", "Test class.", "class", "true" }, + { "-csv", "Output test results in CSV format.", null, "false" }, + { "-xml", "Output test results in XML format.", null, "false" } + }); + + // Capture the command line arguments or display errors and correct usage and then exit. + ParsedProperties options = null; + + try + { + options = new ParsedProperties(commandLine.parseCommandLine(args)); + } + catch (IllegalArgumentException e) + { + System.out.println(commandLine.getErrors()); + System.out.println(commandLine.getUsage()); + System.exit(FAILURE_EXIT); + } + + // Extract the command line options. + Integer delay = options.getPropertyAsInteger("w"); + String threadsString = options.getProperty("c"); + Integer repetitions = options.getPropertyAsInteger("r"); + String durationString = options.getProperty("d"); + String paramsString = options.getProperty("s"); + String testCaseName = options.getProperty("t"); + String reportDir = options.getProperty("o"); + String testRunName = options.getProperty("n"); + String decorators = options.getProperty("X:decorators"); + String testClassName = options.getProperty("1"); + boolean csvResults = options.getPropertyAsBoolean("-csv"); + boolean xmlResults = options.getPropertyAsBoolean("-xml"); + + int[] threads = (threadsString == null) ? null : MathUtils.parseSequence(threadsString); + int[] params = (paramsString == null) ? null : MathUtils.parseSequence(paramsString); + Long duration = (durationString == null) ? null : MathUtils.parseDuration(durationString); + + // The test run name defaults to the test class name unless a value was specified for it. + testRunName = (testRunName == null) ? testClassName : testRunName; + + // Add all the command line options and trailing settings to test context properties. Tests may pick up + // overridden values from there, and these values will be logged in the test results, for analysis and + // to make tests repeatable. + commandLine.addTrailingPairsToProperties(TestContextProperties.getInstance()); + commandLine.addOptionsToProperties(TestContextProperties.getInstance()); + + // Create and start the test runner. + try + { + // Create a list of test decorator factories for use specified decorators to be applied. + List<TestDecoratorFactory> decoratorFactories = parseDecorators(decorators); + + TKTestRunner testRunner = + new TKTestRunner(repetitions, duration, threads, (delay == null) ? 0 : delay, params, testCaseName, + reportDir, testRunName, csvResults, xmlResults, decoratorFactories); + + TestResult testResult = testRunner.start(testClassName); + + if (!testResult.wasSuccessful()) + { + System.exit(FAILURE_EXIT); + } + } + catch (Exception e) + { + System.err.println(e.getMessage()); + e.printStackTrace(new PrintStream(System.err)); + System.exit(EXCEPTION_EXIT); + } + } + + /** + * Parses a list of test decorators, in the form "class.name[:class.name]*", and creates factories for those + * TestDecorator classes , and returns a list of the factories. This list of factories will be in the same + * order as specified in the string. The factories can be used to succesively wrap tests in layers of + * decorators, as decorators themselves implement the 'Test' interface. + * + * <p/>If the string fails to parse, or if any of the decorators specified in it are cannot be loaded, or are not + * TestDecorators, a runtime exception with a suitable error message will be thrown. The factories themselves + * throw runtimes if the constructor method calls on the decorators fail. + * + * @param decorators The decorators list to be parsed. + * + * @return A list of instantiated decorators. + */ + protected static List<TestDecoratorFactory> parseDecorators(String decorators) + { + List<TestDecoratorFactory> result = new LinkedList<TestDecoratorFactory>(); + String toParse = decorators; + + // Check that the decorators string is not null or empty, returning an empty list of decorator factories it + // it is. + if ((decorators == null) || "".equals(decorators)) + { + return result; + } + + // Strip any leading and trailing quotes from the string. + if (toParse.charAt(0) == '\"') + { + toParse = toParse.substring(1, toParse.length() - 1); + } + + if (toParse.charAt(toParse.length() - 1) == '\"') + { + toParse = toParse.substring(0, toParse.length() - 2); + } + + // Instantiate all decorators. + for (String decoratorClassName : toParse.split(":")) + { + try + { + Class decoratorClass = Class.forName(decoratorClassName); + final Constructor decoratorConstructor = decoratorClass.getConstructor(WrappedSuiteTestDecorator.class); + + // Check that the decorator is an instance of WrappedSuiteTestDecorator. + if (!WrappedSuiteTestDecorator.class.isAssignableFrom(decoratorClass)) + { + throw new RuntimeException("The decorator class " + decoratorClassName + + " is not a sub-class of WrappedSuiteTestDecorator, which it needs to be."); + } + + result.add(new TestDecoratorFactory() + { + public WrappedSuiteTestDecorator decorateTest(Test test) + { + try + { + return (WrappedSuiteTestDecorator) decoratorConstructor.newInstance(test); + } + catch (InstantiationException e) + { + throw new RuntimeException( + "The decorator class " + decoratorConstructor.getDeclaringClass().getName() + + " cannot be instantiated.", e); + } + catch (IllegalAccessException e) + { + throw new RuntimeException( + "The decorator class " + decoratorConstructor.getDeclaringClass().getName() + + " does not have a publicly accessable constructor.", e); + } + catch (InvocationTargetException e) + { + throw new RuntimeException( + "The decorator class " + decoratorConstructor.getDeclaringClass().getName() + + " cannot be invoked.", e); + } + } + }); + } + catch (ClassNotFoundException e) + { + throw new RuntimeException("The decorator class " + decoratorClassName + " could not be found.", e); + } + catch (NoSuchMethodException e) + { + throw new RuntimeException("The decorator class " + decoratorClassName + + " does not have a constructor that accepts a single 'WrappedSuiteTestDecorator' argument.", e); + } + } + + return result; + } + + /** + * TestDecoratorFactory is a factory for creating test decorators from tests. + */ + protected interface TestDecoratorFactory + { + /** + * Decorates the specified test with a new decorator. + * + * @param test The test to decorate. + * + * @return The decorated test. + */ + public WrappedSuiteTestDecorator decorateTest(Test test); + } + + /** + * Runs a test or suite of tests, using the super class implemenation. This method wraps the test to be run + * in any test decorators needed to add in the configured toolkits enhanced junit functionality. + * + * @param test The test to run. + * @param wait Undocumented. Nothing in the JUnit javadocs to say what this is for. + * + * @return The results of the test run. + */ + public TestResult doRun(Test test, boolean wait) + { + log.debug("public TestResult doRun(Test \"" + test + "\", boolean " + wait + "): called"); + + // Wrap the tests in decorators for duration, scaling, repetition, parameterization etc. + WrappedSuiteTestDecorator targetTest = decorateTests(test); + + // Delegate to the super method to run the decorated tests. + log.debug("About to call super.doRun"); + + TestResult result = super.doRun(targetTest, wait); + log.debug("super.doRun returned."); + + /*if (result instanceof TKTestResult) + { + TKTestResult tkResult = (TKTestResult) result; + + tkResult.notifyEndBatch(); + }*/ + + return result; + } + + /** + * Applies test decorators to the tests for parameterization, duration, scaling and repetition. + * + * @param test The test to decorat. + * + * @return The decorated test. + */ + protected WrappedSuiteTestDecorator decorateTests(Test test) + { + log.debug("params = " + ((params == null) ? null : MathUtils.printArray(params))); + log.debug("repetitions = " + repetitions); + log.debug("threads = " + ((threads == null) ? null : MathUtils.printArray(threads))); + log.debug("duration = " + duration); + + // Wrap all tests in the test suite with WrappedSuiteTestDecorators. This is quite ugly and a bit baffling, + // but the reason it is done is because the JUnit implementation of TestDecorator has some bugs in it. + WrappedSuiteTestDecorator targetTest = null; + + if (test instanceof TestSuite) + { + log.debug("targetTest is a TestSuite"); + + TestSuite suite = (TestSuite) test; + + int numTests = suite.countTestCases(); + log.debug("There are " + numTests + " in the suite."); + + for (int i = 0; i < numTests; i++) + { + Test nextTest = suite.testAt(i); + log.debug("suite.testAt(" + i + ") = " + nextTest); + + if (nextTest instanceof TimingControllerAware) + { + log.debug("nextTest is TimingControllerAware"); + } + + if (nextTest instanceof TestThreadAware) + { + log.debug("nextTest is TestThreadAware"); + } + } + + targetTest = new WrappedSuiteTestDecorator(suite); + log.debug("Wrapped with a WrappedSuiteTestDecorator."); + } + // If the test has already been wrapped, no need to do it again. + else if (test instanceof WrappedSuiteTestDecorator) + { + targetTest = (WrappedSuiteTestDecorator) test; + } + + // If size parameter values have been set, then wrap the test in an asymptotic test decorator. + if (params != null) + { + targetTest = new AsymptoticTestDecorator(targetTest, params, (repetitions == null) ? 1 : repetitions); + log.debug("Wrapped with asymptotic test decorator."); + log.debug("targetTest = " + targetTest); + } + + // If no size parameters are set but the repitions parameter is, then wrap the test in an asymptotic test decorator. + else if ((repetitions != null) && (repetitions > 1)) + { + targetTest = new AsymptoticTestDecorator(targetTest, new int[] { 1 }, repetitions); + log.debug("Wrapped with asymptotic test decorator."); + log.debug("targetTest = " + targetTest); + } + + // Apply any optional user specified decorators. + targetTest = applyOptionalUserDecorators(targetTest); + + // If a test run duration has been set then wrap the test in a duration test decorator. This will wrap on + // top of size, repeat or concurrency wrappings already applied. + if (duration != null) + { + DurationTestDecorator durationTest = new DurationTestDecorator(targetTest, duration); + targetTest = durationTest; + + log.debug("Wrapped with duration test decorator."); + log.debug("targetTest = " + targetTest); + + registerShutdownHook(durationTest); + } + + // ParameterVariationTestDecorator... + + // If a test thread concurrency level is set then wrap the test in a scaled test decorator. This will wrap on + // top of size scaling or repetition wrappings. + ScaledTestDecorator scaledDecorator; + + if ((threads != null) && ((threads.length > 1) || (MathUtils.maxInArray(threads) > 1))) + { + scaledDecorator = new ScaledTestDecorator(targetTest, threads); + targetTest = scaledDecorator; + log.debug("Wrapped with scaled test decorator."); + log.debug("targetTest = " + targetTest); + } + else + { + scaledDecorator = new ScaledTestDecorator(targetTest, new int[] { 1 }); + targetTest = scaledDecorator; + log.debug("Wrapped with scaled test decorator with default of 1 thread."); + log.debug("targetTest = " + targetTest); + } + + // Register the scaled test decorators shutdown hook. + registerShutdownHook(scaledDecorator); + + return targetTest; + } + + /** + * If there were any user specified test decorators on the command line, this method instantiates them and wraps + * the test in them, from inner-most to outer-most in the order in which the decorators were supplied on the + * command line. + * + * @param targetTest The test to wrap. + * + * @return A wrapped test. + */ + protected WrappedSuiteTestDecorator applyOptionalUserDecorators(WrappedSuiteTestDecorator targetTest) + { + // If there are user defined test decorators apply them in order now. + for (TestDecoratorFactory factory : decoratorFactories) + { + targetTest = factory.decorateTest(targetTest); + } + + return targetTest; + } + + /** + * Creates the TestResult object to be used for test runs. See {@link TKTestResult} for more information and the + * enhanced test result class that this uses. + * + * @return An instance of the enhanced test result object, {@link TKTestResult}. + */ + protected TestResult createTestResult() + { + log.debug("protected TestResult createTestResult(): called"); + + TKTestResult result = new TKTestResult(delay, testCaseName); + + // Check if a directory to output reports to has been specified and attach test listeners if so. + if (reportDir != null) + { + // Create the report directory if it does not already exist. + File reportDirFile = new File(reportDir); + + if (!reportDirFile.exists()) + { + reportDirFile.mkdir(); + } + + // Create the results file (make the name of this configurable as a command line parameter). + Writer timingsWriter; + + // Always set up a console feedback listener. + ConsoleTestListener feedbackListener = new ConsoleTestListener(); + result.addListener(feedbackListener); + result.addTKTestListener(feedbackListener); + + // Set up an XML results listener to output the timings to the results file, if requested on the command line. + if (xmlResults) + { + try + { + File timingsFile = new File(reportDirFile, "TEST-" + currentTestClassName + ".xml"); + timingsWriter = new BufferedWriter(new FileWriter(timingsFile), 20000); + } + catch (IOException e) + { + throw new RuntimeException("Unable to create the log file to write test results to: " + e, e); + } + + XMLTestListener listener = new XMLTestListener(timingsWriter, currentTestClassName); + result.addListener(listener); + result.addTKTestListener(listener); + + registerShutdownHook(listener); + } + + // Set up an CSV results listener to output the timings to the results file, if requested on the command line. + if (csvResults) + { + try + { + File timingsFile = + new File(reportDirFile, testRunName + "-" + TIME_STAMP_FORMAT.format(new Date()) + "-timings.csv"); + timingsWriter = new BufferedWriter(new FileWriter(timingsFile), 20000); + } + catch (IOException e) + { + throw new RuntimeException("Unable to create the log file to write test results to: " + e, e); + } + + CSVTestListener listener = new CSVTestListener(timingsWriter); + result.addListener(listener); + result.addTKTestListener(listener); + + // Register the results listeners shutdown hook to flush its data if the test framework is shutdown + // prematurely. + registerShutdownHook(listener); + } + + // Register the results listeners shutdown hook to flush its data if the test framework is shutdown + // prematurely. + // registerShutdownHook(listener); + + // Record the start time of the batch. + // result.notifyStartBatch(); + + // At this point in time the test class has been instantiated, giving it an opportunity to read its parameters. + // Inform any test listers of the test properties. + result.notifyTestProperties(TestContextProperties.getAccessedProps()); + } + + return result; + } + + /** + * Registers the shutdown hook of a {@link ShutdownHookable}. + * + * @param hookable The hookable to register. + */ + protected void registerShutdownHook(ShutdownHookable hookable) + { + Runtime.getRuntime().addShutdownHook(hookable.getShutdownHook()); + } + + /** + * Initializes the test runner with the provided command line arguments and and starts the test run. + * + * @param testClassName The fully qualified name of the test class to run. + * + * @return The test results. + * + * @throws Exception Any exceptions from running the tests are allowed to fall through. + */ + protected TestResult start(String testClassName) throws Exception + { + // Record the current test class, so that the test results can be output to a file incorporating this name. + this.currentTestClassName = testClassName; + + // Delegate to the super method to run the tests. + return super.start(new String[] { testClassName }); + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestRunnerImprovedErrorHandling.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestRunnerImprovedErrorHandling.java index 9b4a8707db..edd79b3697 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestRunnerImprovedErrorHandling.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestRunnerImprovedErrorHandling.java @@ -1,131 +1,131 @@ -/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.junit.extensions;
-
-import junit.framework.Test;
-import junit.framework.TestResult;
-
-import junit.runner.Version;
-
-import junit.textui.ResultPrinter;
-import junit.textui.TestRunner;
-
-import org.apache.log4j.Logger;
-
-import java.io.PrintStream;
-
-/**
- * The {@link junit.textui.TestRunner} does not provide very good error handling. It does not wrap exceptions and
- * does not print out stack traces, losing valuable error tracing information. This class overrides methods in it
- * in order to improve their error handling. The {@link TKTestRunner} is then built on top of this.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * </table>
- *
- * @author Rupert Smith
- */
-public class TestRunnerImprovedErrorHandling extends TestRunner
-{
- /** Used for logging. */
- Logger log = Logger.getLogger(TestRunnerImprovedErrorHandling.class);
-
- /**
- * Delegates to the super constructor.
- */
- public TestRunnerImprovedErrorHandling()
- {
- super();
- }
-
- /**
- * Delegates to the super constructor.
- *
- * @param printStream The location to write test results to.
- */
- public TestRunnerImprovedErrorHandling(PrintStream printStream)
- {
- super(printStream);
- }
-
- /**
- * Delegates to the super constructor.
- *
- * @param resultPrinter The location to write test results to.
- */
- public TestRunnerImprovedErrorHandling(ResultPrinter resultPrinter)
- {
- super(resultPrinter);
- }
-
- /**
- * Starts a test run. Analyzes the command line arguments
- * and runs the given test suite.
- *
- * @param args The command line arguments.
- *
- * @return The test results.
- *
- * @throws Exception Any exceptions falling through the tests are wrapped in Exception and rethrown.
- */
- protected TestResult start(String[] args) throws Exception
- {
- String testCase = "";
- boolean wait = false;
-
- for (int i = 0; i < args.length; i++)
- {
- if (args[i].equals("-wait"))
- {
- wait = true;
- }
- else if (args[i].equals("-c"))
- {
- testCase = extractClassName(args[++i]);
- }
- else if (args[i].equals("-v"))
- {
- System.err.println("JUnit " + Version.id() + " by Kent Beck and Erich Gamma");
- }
- else
- {
- testCase = args[i];
- }
- }
-
- if (testCase.equals(""))
- {
- throw new Exception("Usage: TestRunner [-wait] testCaseName, where name is the name of the TestCase class");
- }
-
- try
- {
- Test suite = getTest(testCase);
-
- return doRun(suite, wait);
- }
- catch (Exception e)
- {
- log.warn("Got exception whilst creating and running test suite.", e);
- throw new Exception("Could not create and run the test suite.", e);
- }
- }
-}
+/* + * + * 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.junit.extensions; + +import junit.framework.Test; +import junit.framework.TestResult; + +import junit.runner.Version; + +import junit.textui.ResultPrinter; +import junit.textui.TestRunner; + +import org.apache.log4j.Logger; + +import java.io.PrintStream; + +/** + * The {@link junit.textui.TestRunner} does not provide very good error handling. It does not wrap exceptions and + * does not print out stack traces, losing valuable error tracing information. This class overrides methods in it + * in order to improve their error handling. The {@link TKTestRunner} is then built on top of this. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * </table> + * + * @author Rupert Smith + */ +public class TestRunnerImprovedErrorHandling extends TestRunner +{ + /** Used for logging. */ + Logger log = Logger.getLogger(TestRunnerImprovedErrorHandling.class); + + /** + * Delegates to the super constructor. + */ + public TestRunnerImprovedErrorHandling() + { + super(); + } + + /** + * Delegates to the super constructor. + * + * @param printStream The location to write test results to. + */ + public TestRunnerImprovedErrorHandling(PrintStream printStream) + { + super(printStream); + } + + /** + * Delegates to the super constructor. + * + * @param resultPrinter The location to write test results to. + */ + public TestRunnerImprovedErrorHandling(ResultPrinter resultPrinter) + { + super(resultPrinter); + } + + /** + * Starts a test run. Analyzes the command line arguments + * and runs the given test suite. + * + * @param args The command line arguments. + * + * @return The test results. + * + * @throws Exception Any exceptions falling through the tests are wrapped in Exception and rethrown. + */ + protected TestResult start(String[] args) throws Exception + { + String testCase = ""; + boolean wait = false; + + for (int i = 0; i < args.length; i++) + { + if (args[i].equals("-wait")) + { + wait = true; + } + else if (args[i].equals("-c")) + { + testCase = extractClassName(args[++i]); + } + else if (args[i].equals("-v")) + { + System.err.println("JUnit " + Version.id() + " by Kent Beck and Erich Gamma"); + } + else + { + testCase = args[i]; + } + } + + if (testCase.equals("")) + { + throw new Exception("Usage: TestRunner [-wait] testCaseName, where name is the name of the TestCase class"); + } + + try + { + Test suite = getTest(testCase); + + return doRun(suite, wait); + } + catch (Exception e) + { + log.warn("Got exception whilst creating and running test suite.", e); + throw new Exception("Could not create and run the test suite.", e); + } + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestThreadAware.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestThreadAware.java index aaa773260d..d7de2822a2 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestThreadAware.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestThreadAware.java @@ -1,49 +1,49 @@ -/*
- *
- * 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.junit.extensions;
-
-/**
- * This interface can be implemented by tests that want to know if they are being run concurrently. It provides
- * lifecycle notification events to tell the test implementation when test threads are being created and destroyed.
- * This can assist tests in creating and destroying resources that exist over the life of a test thread. A single
- * test thread can excute the same test many times, and often it is convenient to keep resources, for example network
- * connections, open over many test calls.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities
- * <tr><td> Set up per thread test fixtures.
- * <tr><td> Clean up per thread test fixtures.
- * </table>
- *
- * @author Rupert Smith
- */
-public interface TestThreadAware
-{
- /**
- * Called when a test thread is created.
- */
- public void threadSetUp();
-
- /**
- * Called when a test thread is destroyed.
- */
- public void threadTearDown();
-}
+/* + * + * 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.junit.extensions; + +/** + * This interface can be implemented by tests that want to know if they are being run concurrently. It provides + * lifecycle notification events to tell the test implementation when test threads are being created and destroyed. + * This can assist tests in creating and destroying resources that exist over the life of a test thread. A single + * test thread can excute the same test many times, and often it is convenient to keep resources, for example network + * connections, open over many test calls. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities + * <tr><td> Set up per thread test fixtures. + * <tr><td> Clean up per thread test fixtures. + * </table> + * + * @author Rupert Smith + */ +public interface TestThreadAware +{ + /** + * Called when a test thread is created. + */ + public void threadSetUp(); + + /** + * Called when a test thread is destroyed. + */ + public void threadTearDown(); +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/Throttle.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/Throttle.java index 955e47c25b..1ea8e8e2be 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/Throttle.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/Throttle.java @@ -1,73 +1,73 @@ -/*
- *
- * 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.junit.extensions;
-
-/**
- * Throttle is an interface that supplies a {@link #throttle} method, that can only be called at the rate specified
- * in a call to the {@link #setRate} method. This can be used to restict processing to run at a certain number
- * of operations per second.
- *
- * <p/>Throttle also supplies a method to check the throttle rate, without waiting. This could be used to update a user
- * interface every time an event occurs, but only up to a maximum rate. For example, as elements are added to a list,
- * a count of elements is updated for the user to see, but only up to a maximum rate of ten updates a second, as updating
- * faster than that slows the processing of element-by-element additions to the list unnecessarily.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities
- * <tr><td> Accept throttling rate in operations per second.
- * <tr><td> Inject short pauses to fill-out processing cycles to a specified rate.
- * <tr><td> Check against a throttle speed without waiting.
- * </table>
- *
- * @author Rupert Smith
- */
-public interface Throttle
-{
- /**
- * Specifies the throttling rate in operations per second. This must be called with with a value, the inverse
- * of which is a measurement in nano seconds, such that the number of nano seconds do not overflow a long integer.
- * The value must also be larger than zero.
- *
- * @param hertz The throttling rate in cycles per second.
- */
- public void setRate(float hertz);
-
- /**
- * This method can only be called at the rate set by the {@link #setRate} method, if it is called faster than this
- * it will inject short pauses to restrict the call rate to that rate.
- *
- * <p/>If the thread executing this method is interrupted, it must ensure that the threads interrupt thread
- * remains set upon exit from the method. This method does not expose InterruptedException, to indicate interruption
- * of the throttle during a timed wait. It may be changed so that it does.
- */
- public void throttle();
-
- /**
- * Checks but does not enforce the throttle rate. When this method is called, it checks if a length of time greater
- * than that equal to the inverse of the throttling rate has passed since it was last called and returned <tt>true</tt>
- *
- * @return <tt>true</tt> if a length of time greater than that equal to the inverse of the throttling rate has
- * passed since this method was last called and returned <tt>true</tt>, <tt>false</tt> otherwise. The very
- * first time this method is called on a throttle, it returns <tt>true</tt> as the base case to the above
- * self-referential definition.
- */
- public boolean checkThrottle();
-}
+/* + * + * 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.junit.extensions; + +/** + * Throttle is an interface that supplies a {@link #throttle} method, that can only be called at the rate specified + * in a call to the {@link #setRate} method. This can be used to restict processing to run at a certain number + * of operations per second. + * + * <p/>Throttle also supplies a method to check the throttle rate, without waiting. This could be used to update a user + * interface every time an event occurs, but only up to a maximum rate. For example, as elements are added to a list, + * a count of elements is updated for the user to see, but only up to a maximum rate of ten updates a second, as updating + * faster than that slows the processing of element-by-element additions to the list unnecessarily. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities + * <tr><td> Accept throttling rate in operations per second. + * <tr><td> Inject short pauses to fill-out processing cycles to a specified rate. + * <tr><td> Check against a throttle speed without waiting. + * </table> + * + * @author Rupert Smith + */ +public interface Throttle +{ + /** + * Specifies the throttling rate in operations per second. This must be called with with a value, the inverse + * of which is a measurement in nano seconds, such that the number of nano seconds do not overflow a long integer. + * The value must also be larger than zero. + * + * @param hertz The throttling rate in cycles per second. + */ + public void setRate(float hertz); + + /** + * This method can only be called at the rate set by the {@link #setRate} method, if it is called faster than this + * it will inject short pauses to restrict the call rate to that rate. + * + * <p/>If the thread executing this method is interrupted, it must ensure that the threads interrupt thread + * remains set upon exit from the method. This method does not expose InterruptedException, to indicate interruption + * of the throttle during a timed wait. It may be changed so that it does. + */ + public void throttle(); + + /** + * Checks but does not enforce the throttle rate. When this method is called, it checks if a length of time greater + * than that equal to the inverse of the throttling rate has passed since it was last called and returned <tt>true</tt> + * + * @return <tt>true</tt> if a length of time greater than that equal to the inverse of the throttling rate has + * passed since this method was last called and returned <tt>true</tt>, <tt>false</tt> otherwise. The very + * first time this method is called on a throttle, it returns <tt>true</tt> as the base case to the above + * self-referential definition. + */ + public boolean checkThrottle(); +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingController.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingController.java index 7b5763f1de..b69df84045 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingController.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingController.java @@ -1,175 +1,175 @@ -/*
- *
- * 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.junit.extensions;
-
-/**
- * A TimingController is a interface that a test that is aware of the fact that it is being timed can use to manage
- * the timer. Using this interface tests can suspend and resume test timers. This is usefull if you want to exclude
- * some expensive preparation from being timed as part of a test. In general when timing tests to measure the
- * performance of code, you should try to set up data in the #setUp where possible, or as static members in the test
- * class. This is not always convenient, this interface gives you a way to suspend and resume, or event completely
- * restart test timers, to get accurate measurements.
- *
- * <p/>The interface can also be used to register multiple test pass/fails and timings from a single test method.
- * In some cases it is easier to write tests in this way. For example a concurrent and asynchronous test may make
- * many asynchronous requests and then wait for replies to all its requests. Writing such a test with one send/reply
- * per test method and trying to scale up using many threads will quickly run into limitations if more than about
- * 100 asynchronous calls need to be made at once. A better way to write such a test is as a single method that sends
- * many (perhaps thousands or millions) and waits for replies in two threads, one for send, one for replies. It can
- * then log pass/fails and timings on each individual reply as they come back in, even though the test has been written
- * to send thousands of requests per test method in order to do volume testing.
- *
- * <p/>If when the {@link #completeTest(boolean)} is called, the test runner decides that testing should stop (perhaps
- * because a duration test has expired), it throws an InterruptedException to indicate that the test method should stop
- * immediately. The test method can do this by allowing this exception to fall through, if no other clean-up handling
- * is necessary, or it can simply return as soon as it possibly can. The test runner will still call the tearDown
- * method in the usual way when this happens.
- *
- * <p/>Below are some examples of how this can be used. Not how checking that the timing controller is really available
- * rather than assuming it is, means that the test can run as an ordinary JUnit test under the default test runners. In
- * general code should be written to take advantage of the extended capabilities of junit toolkit, without assuming they
- * are going to be run under its test runner.
- *
- * <pre>
- * public class MyTest extends TestCase implements TimingControllerAware {
- * ...
- *
- * timingUtils = this.getTimingController();
- *
- * // Do expensive data preparation here...
- *
- * if (timingUtils != null)
- * timingUtils.restart();
- * </pre>
- *
- * <pre>
- * public class MyTest extends TestCase implements TimingControllerAware {
- * ...
- *
- * public void myVolumeTest(int size) {
- *
- * timingUtils = this.getTimingController();
- *
- * boolean stopNow = false;
- *
- * // In Sender thread.
- * for(int i = 0; !stopNow && i < size; i++)
- * // Send request i.
- * ...
- *
- * // In Receiver thread.
- * onReceive(Object o) {
- * try {
- * // Check o is as expected.
- * if (....)
- * {
- * if (timingUtils != null)
- * timingUtils.completeTest(true);
- * }
- * else
- * {
- * if (timingUtils != null)
- * timingUtils.completeTest(false);
- * }
- * } catch (InterruptedException e) {
- * stopNow = true;
- * return;
- * }
- * }
- * </pre>
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities
- * <tr><td> Allow test timers to be suspended, restarted or reset.
- * <tr><td> Allow tests to register multiple pass/fails and timings.
- * </table>
- *
- * @author Rupert Smith
- */
-public interface TimingController
-{
- /**
- * Gets the timing controller associated with the current test thread. Tests that use timing controller should
- * always get the timing controller from this method in the same thread that called the setUp, tearDown or test
- * method. The controller returned by this method may be called from any thread because it remembers the thread
- * id of the original test thread.
- *
- * @return The timing controller associated with the current test thread.
- */
- public TimingController getControllerForCurrentThread();
-
- /**
- * Suspends the test timer.
- *
- * @return The current time in nanoseconds.
- */
- public long suspend();
-
- /**
- * Allows the test timer to continue running after a suspend.
- *
- * @return The current time in nanoseconds.
- */
- public long resume();
-
- /**
- * Completely restarts the test timer from zero.
- *
- * @return The current time in nanoseconds.
- */
- public long restart();
-
- /**
- * Register an additional pass/fail for the current test. The test result is assumed to apply to a test of
- * 'size' parmeter 1. Use the {@link #completeTest(boolean, int)} method to register timings with parameters.
- *
- * @param testPassed Whether or not this timing is for a test pass or fail.
- *
- * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to
- * indicate to the test method that it should stop immediately.
- */
- public void completeTest(boolean testPassed) throws InterruptedException;
-
- /**
- * Register an additional pass/fail for the current test. The test result is applies to a test of the specified
- * 'size' parmeter.
- *
- * @param testPassed Whether or not this timing is for a test pass or fail.
- * @param param The test parameter size for parameterized tests.
- *
- * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to
- * indicate to the test method that it should stop immediately.
- */
- public void completeTest(boolean testPassed, int param) throws InterruptedException;
-
- /**
- * Register an additional pass/fail for the current test. The test result is applies to a test of the specified
- * 'size' parmeter and allows the caller to sepecify the timing to log.
- *
- * @param testPassed Whether or not this timing is for a test pass or fail.
- * @param param The test parameter size for parameterized tests.
- * @param timeNanos The time in nano seconds to log the test result with.
- *
- * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to
- * indicate to the test method that it should stop immediately.
- */
- public void completeTest(boolean testPassed, int param, long timeNanos) throws InterruptedException;
-}
+/* + * + * 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.junit.extensions; + +/** + * A TimingController is a interface that a test that is aware of the fact that it is being timed can use to manage + * the timer. Using this interface tests can suspend and resume test timers. This is usefull if you want to exclude + * some expensive preparation from being timed as part of a test. In general when timing tests to measure the + * performance of code, you should try to set up data in the #setUp where possible, or as static members in the test + * class. This is not always convenient, this interface gives you a way to suspend and resume, or event completely + * restart test timers, to get accurate measurements. + * + * <p/>The interface can also be used to register multiple test pass/fails and timings from a single test method. + * In some cases it is easier to write tests in this way. For example a concurrent and asynchronous test may make + * many asynchronous requests and then wait for replies to all its requests. Writing such a test with one send/reply + * per test method and trying to scale up using many threads will quickly run into limitations if more than about + * 100 asynchronous calls need to be made at once. A better way to write such a test is as a single method that sends + * many (perhaps thousands or millions) and waits for replies in two threads, one for send, one for replies. It can + * then log pass/fails and timings on each individual reply as they come back in, even though the test has been written + * to send thousands of requests per test method in order to do volume testing. + * + * <p/>If when the {@link #completeTest(boolean)} is called, the test runner decides that testing should stop (perhaps + * because a duration test has expired), it throws an InterruptedException to indicate that the test method should stop + * immediately. The test method can do this by allowing this exception to fall through, if no other clean-up handling + * is necessary, or it can simply return as soon as it possibly can. The test runner will still call the tearDown + * method in the usual way when this happens. + * + * <p/>Below are some examples of how this can be used. Not how checking that the timing controller is really available + * rather than assuming it is, means that the test can run as an ordinary JUnit test under the default test runners. In + * general code should be written to take advantage of the extended capabilities of junit toolkit, without assuming they + * are going to be run under its test runner. + * + * <pre> + * public class MyTest extends TestCase implements TimingControllerAware { + * ... + * + * timingUtils = this.getTimingController(); + * + * // Do expensive data preparation here... + * + * if (timingUtils != null) + * timingUtils.restart(); + * </pre> + * + * <pre> + * public class MyTest extends TestCase implements TimingControllerAware { + * ... + * + * public void myVolumeTest(int size) { + * + * timingUtils = this.getTimingController(); + * + * boolean stopNow = false; + * + * // In Sender thread. + * for(int i = 0; !stopNow && i < size; i++) + * // Send request i. + * ... + * + * // In Receiver thread. + * onReceive(Object o) { + * try { + * // Check o is as expected. + * if (....) + * { + * if (timingUtils != null) + * timingUtils.completeTest(true); + * } + * else + * { + * if (timingUtils != null) + * timingUtils.completeTest(false); + * } + * } catch (InterruptedException e) { + * stopNow = true; + * return; + * } + * } + * </pre> + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities + * <tr><td> Allow test timers to be suspended, restarted or reset. + * <tr><td> Allow tests to register multiple pass/fails and timings. + * </table> + * + * @author Rupert Smith + */ +public interface TimingController +{ + /** + * Gets the timing controller associated with the current test thread. Tests that use timing controller should + * always get the timing controller from this method in the same thread that called the setUp, tearDown or test + * method. The controller returned by this method may be called from any thread because it remembers the thread + * id of the original test thread. + * + * @return The timing controller associated with the current test thread. + */ + public TimingController getControllerForCurrentThread(); + + /** + * Suspends the test timer. + * + * @return The current time in nanoseconds. + */ + public long suspend(); + + /** + * Allows the test timer to continue running after a suspend. + * + * @return The current time in nanoseconds. + */ + public long resume(); + + /** + * Completely restarts the test timer from zero. + * + * @return The current time in nanoseconds. + */ + public long restart(); + + /** + * Register an additional pass/fail for the current test. The test result is assumed to apply to a test of + * 'size' parmeter 1. Use the {@link #completeTest(boolean, int)} method to register timings with parameters. + * + * @param testPassed Whether or not this timing is for a test pass or fail. + * + * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to + * indicate to the test method that it should stop immediately. + */ + public void completeTest(boolean testPassed) throws InterruptedException; + + /** + * Register an additional pass/fail for the current test. The test result is applies to a test of the specified + * 'size' parmeter. + * + * @param testPassed Whether or not this timing is for a test pass or fail. + * @param param The test parameter size for parameterized tests. + * + * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to + * indicate to the test method that it should stop immediately. + */ + public void completeTest(boolean testPassed, int param) throws InterruptedException; + + /** + * Register an additional pass/fail for the current test. The test result is applies to a test of the specified + * 'size' parmeter and allows the caller to sepecify the timing to log. + * + * @param testPassed Whether or not this timing is for a test pass or fail. + * @param param The test parameter size for parameterized tests. + * @param timeNanos The time in nano seconds to log the test result with. + * + * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to + * indicate to the test method that it should stop immediately. + */ + public void completeTest(boolean testPassed, int param, long timeNanos) throws InterruptedException; +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingControllerAware.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingControllerAware.java index 1ccdc7dbad..11db87e073 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingControllerAware.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingControllerAware.java @@ -1,43 +1,43 @@ -/*
- *
- * 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.junit.extensions;
-
-/**
- * TimingControllerAware is an interface that tests that manipulate the timing controller should implement. It enables
- * the TK test runner to set the test up with a handle on the timing controller which the test can use to call back
- * to the test runner to manage the timers.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities
- * <tr><td> Provide timing controller insertion point for tests.
- * </table>
- *
- * @author Rupert Smith
- */
-public interface TimingControllerAware
-{
- /**
- * Used by test runners that can supply a {@link TimingController} to set the controller on an aware test.
- *
- * @param controller The timing controller.
- */
- public void setTimingController(TimingController controller);
-}
+/* + * + * 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.junit.extensions; + +/** + * TimingControllerAware is an interface that tests that manipulate the timing controller should implement. It enables + * the TK test runner to set the test up with a handle on the timing controller which the test can use to call back + * to the test runner to manage the timers. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities + * <tr><td> Provide timing controller insertion point for tests. + * </table> + * + * @author Rupert Smith + */ +public interface TimingControllerAware +{ + /** + * Used by test runners that can supply a {@link TimingController} to set the controller on an aware test. + * + * @param controller The timing controller. + */ + public void setTimingController(TimingController controller); +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/WrappedSuiteTestDecorator.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/WrappedSuiteTestDecorator.java index 7a1e537b1c..d5690fc24a 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/WrappedSuiteTestDecorator.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/WrappedSuiteTestDecorator.java @@ -1,134 +1,134 @@ -/*
- *
- * 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.junit.extensions;
-
-import junit.extensions.TestDecorator;
-
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
-import org.apache.log4j.Logger;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * WrappedSuiteTestDecorator is a test decorator that wraps a test suite, or another wrapped suite, but provides the
- * same functionality for the {@link junit.extensions.TestDecorator#countTestCases()} and {@link TestSuite#testAt(int)}
- * methods as the underlying suite. It returns the values that these methods provide, to enable classes using decorated
- * tests to drill down to the underlying tests in the suite. That is to say that it indexes and reports the number of
- * distinct tests in the suite, not the number of test runs that would result from, for example, wrapping the suite in a
- * repeating decorator.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities
- * <tr><td> Provide access to the underlying tests in a suite.
- * </table>
- *
- * @author Rupert Smith
- */
-public class WrappedSuiteTestDecorator extends TestDecorator
-{
- /** Used for logging. */
- private static Logger log = Logger.getLogger(WrappedSuiteTestDecorator.class);
-
- /** Holds the test suite that this supplies access to. */
- protected Test suite;
-
- /**
- * Creates a wrappred suite test decorator from a test suite.
- *
- * @param suite The test suite.
- */
- public WrappedSuiteTestDecorator(TestSuite suite)
- {
- super(suite);
- this.suite = suite;
- }
-
- /**
- * Creates a wrapped suite test decorator from another one.
- *
- * @param suite The test suite.
- */
- public WrappedSuiteTestDecorator(WrappedSuiteTestDecorator suite)
- {
- super(suite);
- this.suite = suite;
- }
-
- /**
- * Returns the test count of the wrapped suite.
- *
- * @return The test count of the wrapped suite.
- */
- public int countTestCases()
- {
- return suite.countTestCases();
- }
-
- /**
- * Gets the ith test from the test suite.
- *
- * @param i The index of the test within the suite to get.
- *
- * @return The test with the specified index.
- */
- public Test testAt(int i)
- {
- log.debug("public Test testAt(int i = " + i + "): called");
-
- if (suite instanceof WrappedSuiteTestDecorator)
- {
- return ((WrappedSuiteTestDecorator) suite).testAt(i);
- }
- else if (suite instanceof TestSuite)
- {
- return ((TestSuite) suite).testAt(i);
- }
-
- // This should never happen.
- return null;
- }
-
- /**
- * Gets all the tests from the underlying test suite.
- *
- * @return All the tests from the underlying test suite.
- */
- public Collection<Test> getAllUnderlyingTests()
- {
- log.debug("public Collection<Test> getAllUnderlyingTests(): called");
-
- List<Test> tests = new ArrayList<Test>();
-
- int numTests = countTestCases();
- log.debug("numTests = " + numTests);
-
- for (int i = 0; i < numTests; i++)
- {
- tests.add(testAt(i));
- }
-
- return tests;
- }
-}
+/* + * + * 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.junit.extensions; + +import junit.extensions.TestDecorator; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.apache.log4j.Logger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * WrappedSuiteTestDecorator is a test decorator that wraps a test suite, or another wrapped suite, but provides the + * same functionality for the {@link junit.extensions.TestDecorator#countTestCases()} and {@link TestSuite#testAt(int)} + * methods as the underlying suite. It returns the values that these methods provide, to enable classes using decorated + * tests to drill down to the underlying tests in the suite. That is to say that it indexes and reports the number of + * distinct tests in the suite, not the number of test runs that would result from, for example, wrapping the suite in a + * repeating decorator. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities + * <tr><td> Provide access to the underlying tests in a suite. + * </table> + * + * @author Rupert Smith + */ +public class WrappedSuiteTestDecorator extends TestDecorator +{ + /** Used for logging. */ + private static Logger log = Logger.getLogger(WrappedSuiteTestDecorator.class); + + /** Holds the test suite that this supplies access to. */ + protected Test suite; + + /** + * Creates a wrappred suite test decorator from a test suite. + * + * @param suite The test suite. + */ + public WrappedSuiteTestDecorator(TestSuite suite) + { + super(suite); + this.suite = suite; + } + + /** + * Creates a wrapped suite test decorator from another one. + * + * @param suite The test suite. + */ + public WrappedSuiteTestDecorator(WrappedSuiteTestDecorator suite) + { + super(suite); + this.suite = suite; + } + + /** + * Returns the test count of the wrapped suite. + * + * @return The test count of the wrapped suite. + */ + public int countTestCases() + { + return suite.countTestCases(); + } + + /** + * Gets the ith test from the test suite. + * + * @param i The index of the test within the suite to get. + * + * @return The test with the specified index. + */ + public Test testAt(int i) + { + log.debug("public Test testAt(int i = " + i + "): called"); + + if (suite instanceof WrappedSuiteTestDecorator) + { + return ((WrappedSuiteTestDecorator) suite).testAt(i); + } + else if (suite instanceof TestSuite) + { + return ((TestSuite) suite).testAt(i); + } + + // This should never happen. + return null; + } + + /** + * Gets all the tests from the underlying test suite. + * + * @return All the tests from the underlying test suite. + */ + public Collection<Test> getAllUnderlyingTests() + { + log.debug("public Collection<Test> getAllUnderlyingTests(): called"); + + List<Test> tests = new ArrayList<Test>(); + + int numTests = countTestCases(); + log.debug("numTests = " + numTests); + + for (int i = 0; i < numTests; i++) + { + tests.add(testAt(i)); + } + + return tests; + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/CSVTestListener.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/CSVTestListener.java index a771e08cf7..f93212e0c5 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/CSVTestListener.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/CSVTestListener.java @@ -1,532 +1,532 @@ -/*
- *
- * 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.junit.extensions.listeners;
-
-import junit.framework.AssertionFailedError;
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestListener;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.junit.extensions.ShutdownHookable;
-import org.apache.qpid.junit.extensions.util.TestContextProperties;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.TreeSet;
-
-/**
- * CSVTestListener is both a test listener, a timings listener, a memory listener and a parameter listener. It listens for test completion events and
- * then writes out all the data that it has listened to into a '.csv' (comma seperated values) file.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Listen to test events; start, end, fail, error.
- * <tr><td> Listen to test timings.
- * <tr><td> Listen to test memory usage.
- * <tr><td> Listen to parameterized test parameters.
- * <tr><td> Output all test data to a CSV file.
- * </table>
- *
- * @author Rupert Smith
- *
- * @todo Write an XML output class. Write a transform to convert it into an HTML page with timings as graphs.
- */
-public class CSVTestListener implements TestListener, TKTestListener, ShutdownHookable
-{
- /** Used for logging. */
- private static final Logger log = Logger.getLogger(CSVTestListener.class);
-
- /** The timings file writer. */
- private Writer timingsWriter;
-
- /**
- * Map for holding results on a per thread basis as they come in. A ThreadLocal is not used as sometimes an
- * explicit thread id must be used, where notifications come from different threads than the ones that called
- * the test method.
- */
- Map<Long, TestResult> threadLocalResults = Collections.synchronizedMap(new HashMap<Long, TestResult>());
-
- /** Used to record the start time of a complete test run, for outputing statistics at the end of the test run. */
- private long batchStartTime;
-
- /** Used to record the number of errors accross a complete test run. */
- private int numError;
-
- /** Used to record the number of failures accross a complete test run. */
- private int numFailed;
-
- /** Used to record the number of passes accross a complete test run. */
- private int numPassed;
-
- /** Used to record the total tests run accross a complete test run. Always equal to passes + errors + fails. */
- private int totalTests;
-
- /** Used to recrod the current concurrency level for the test batch. */
- private int concurrencyLevel;
-
- /**
- * Used to record the total 'size' of the tests run, this is the number run times the average value of the test
- * size parameters.
- */
- private int totalSize;
-
- /**
- * Used to record the summation of all of the individual test timgings. Note that total time and summed time
- * are unlikely to be in agreement, exception for a single threaded test (with no setup time). Total time is
- * the time taken to run all the tests, summed time is the added up time that each individual test took. So if
- * two tests run in parallel and take one second each, total time will be one seconds, summed time will be two
- * seconds.
- */
- private long summedTime;
-
- /** Flag to indicate when batch has been started but not ended to ensure end batch stats are output only once. */
- private boolean batchStarted = false;
-
- /**
- * Creates a new CSVTestListener object.
- *
- * @param writer A writer where this CSV listener should write out its output to.
- */
- public CSVTestListener(Writer writer)
- {
- // log.debug("public CSVTestListener(Writer writer): called");
-
- // Keep the writer.
- this.timingsWriter = writer;
- }
-
- /**
- * Resets the test results to the default state of time zero, memory usage zero, test passed.
- *
- * @param test The test to resest any results for.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void reset(Test test, Long threadId)
- {
- // log.debug("public void reset(Test test = \"" + test + "\", Long threadId = " + threadId + "): called");
-
- TestResult r =
- (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
-
- r.testTime = 0L;
- r.testStartMem = 0L;
- r.testEndMem = 0L;
- r.testState = "Pass";
- r.testParam = 0;
- }
-
- /**
- * Called when a test results in an error.
- *
- * @param test The test which is in error.
- * @param t Any Throwable raised by the test in error.
- */
- public void addError(Test test, Throwable t)
- {
- // log.debug("public void addError(Test test, Throwable t): called");
-
- TestResult r = threadLocalResults.get(Thread.currentThread().getId());
- r.testState = "Error";
- }
-
- /**
- * Called when a test results in a failure.
- *
- * @param test The test which failed.
- * @param t The AssertionFailedError that encapsulates the test failure.
- */
- public void addFailure(Test test, AssertionFailedError t)
- {
- // log.debug("public void addFailure(Test \"" + test + "\", AssertionFailedError t): called");
-
- TestResult r = threadLocalResults.get(Thread.currentThread().getId());
- r.testState = "Failure";
- }
-
- /**
- * Called when a test completes to mark it as a test fail. This method should be used when registering a
- * failure from a different thread than the one that started the test.
- *
- * @param test The test which failed.
- * @param e The assertion that failed the test.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void addFailure(Test test, AssertionFailedError e, Long threadId)
- {
- // log.debug("public void addFailure(Test test = \"" + test + "\", AssertionFailedError e, Long threadId = " + threadId
- // + "): called");
-
- TestResult r =
- (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
-
- r.testState = "Failure";
- }
-
- /**
- * Called when a test completes. Success, failure and errors.
- *
- * @param test The test which completed.
- */
- public void endTest(Test test)
- {
- // log.debug("public void endTest(Test \"" + test + "\"): called");
-
- TestResult r = threadLocalResults.get(Thread.currentThread().getId());
-
- writeTestResults(r, test);
-
- // Clear all the test results for the thread.
- threadLocalResults.remove(Thread.currentThread().getId());
- }
-
- /**
- * Called when a test starts.
- *
- * @param test The test wich has started.
- */
- public void startTest(Test test)
- {
- // log.debug("public void startTest(Test \"" + test + "\"): called");
-
- // Initialize the thread local test results.
- threadLocalResults.put(Thread.currentThread().getId(), new TestResult());
- }
-
- /**
- * Should be called every time a test completes with the run time of that test.
- *
- * @param test The name of the test.
- * @param nanos The run time of the test in nanoseconds.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void timing(Test test, long nanos, Long threadId)
- {
- // log.debug("public void timing(String \"" + test + "\", long " + nanos + "): called");
-
- TestResult r =
- (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
-
- r.testTime = nanos;
- summedTime += nanos;
- }
-
- /**
- * Should be called every time a test completed with the amount of memory used before and after the test was run.
- *
- * @param test The test which memory was measured for.
- * @param memStart The total JVM memory used before the test was run.
- * @param memEnd The total JVM memory used after the test was run.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void memoryUsed(Test test, long memStart, long memEnd, Long threadId)
- {
- // log.debug("public void memoryUsed(Test \"" + test + "\", long " + memStart + ", long " + memEnd + ", Long "
- // + threadId + "): called");
-
- TestResult r =
- (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
-
- r.testStartMem = memStart;
- r.testEndMem = memEnd;
- }
-
- /**
- * Should be called every time a parameterized test completed with the int value of its test parameter.
- *
- * @param test The test which memory was measured for.
- * @param parameter The int parameter value.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void parameterValue(Test test, int parameter, Long threadId)
- {
- // log.debug("public void parameterValue(Test test = \"" + test + "\", int parameter = " + parameter + "): called");
-
- TestResult r =
- (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
-
- r.testParam = parameter;
- totalSize += parameter;
- }
-
- /**
- * Should be called every time a test completes with the current number of test threads running. This should not
- * change within a test batch, therefore it is safe to take this as a batch level property value too.
- *
- * @param test The test for which the measurement is being generated.
- * @param threads The number of tests being run concurrently.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void concurrencyLevel(Test test, int threads, Long threadId)
- {
- // log.debug("public void concurrencyLevel(Test test = \"" + test + "\", int threads = " + threads + "): called");
-
- TestResult r =
- (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
-
- r.testConcurrency = threads;
- concurrencyLevel = threads;
-
- }
-
- /**
- * Called when a test completes. Success, failure and errors. This method should be used when registering an
- * end test from a different thread than the one that started the test.
- *
- * @param test The test which completed.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void endTest(Test test, Long threadId)
- {
- // log.debug("public void endTest(Test test = \"" + test + "\", Long threadId " + threadId + "): called");
-
- TestResult r =
- (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
-
- writeTestResults(r, test);
- }
-
- /**
- * Takes a time stamp for the beginning of the batch and resets stats counted for the batch.
- */
- public synchronized void startBatch()
- {
- numError = 0;
- numFailed = 0;
- numPassed = 0;
- totalTests = 0;
- totalSize = 0;
- batchStartTime = System.nanoTime();
- summedTime = 0;
- batchStarted = true;
-
- // Write out the column headers for the batch.
- writeColumnHeaders();
- }
-
- /**
- * Takes a time stamp for the end of the batch to calculate the total run time.
- * Write this and other stats out to the tail of the csv file.
- *
- * @param parameters The optional test parameters, may be null.
- */
- public synchronized void endBatch(Properties parameters)
- {
- boolean noParams = (parameters == null) || (parameters.size() == 0);
-
- // Check that a batch has been started but not ended.
- if (batchStarted)
- {
- long batchEndTime = System.nanoTime();
- float totalTimeMillis = ((float) (batchEndTime - batchStartTime)) / 1000000f;
- float summedTimeMillis = ((float) summedTime) / 1000000f;
-
- // Write the stats for the batch out.
- try
- {
- synchronized (this.getClass())
- {
- timingsWriter.write("Total Tests:, " + totalTests + ", ");
- timingsWriter.write("Total Passed:, " + numPassed + ", ");
- timingsWriter.write("Total Failed:, " + numFailed + ", ");
- timingsWriter.write("Total Error:, " + numError + ", ");
- timingsWriter.write("Total Size:, " + totalSize + ", ");
- timingsWriter.write("Summed Time:, " + summedTimeMillis + ", ");
- timingsWriter.write("Concurrency Level:, " + concurrencyLevel + ", ");
- timingsWriter.write("Total Time:, " + totalTimeMillis + ", ");
- timingsWriter.write("Test Throughput:, " + (((float) totalTests) / totalTimeMillis) + ", ");
- timingsWriter.write("Test * Size Throughput:, " + (((float) totalSize) / totalTimeMillis)
- + (noParams ? "\n\n" : ", "));
-
- // Write out the test parameters if there are any specified.
- if (!noParams)
- {
- properties(parameters);
- }
-
- timingsWriter.flush();
- }
- }
- catch (IOException e)
- {
- throw new RuntimeException("Unable to write out end batch statistics: " + e, e);
- }
- }
-
- // Reset the batch started flag to ensure stats are only output once.
- batchStarted = false;
- }
-
- /**
- * Notifies listeners of the tests read/set properties.
- *
- * @param properties The tests read/set properties.
- */
- public void properties(Properties properties)
- {
- // log.debug("public void properties(Properties properties): called");
-
- // Write the properties out to the results file.
- try
- {
- synchronized (this.getClass())
- {
- Set keySet = new TreeSet(properties.keySet());
-
- // timingsWriter.write("\n");
-
- for (Object key : keySet)
- {
- timingsWriter.write(key + " = , " + properties.getProperty((String) key) + ", ");
- }
-
- timingsWriter.write("\n\n");
- // timingsWriter.flush();
- }
- }
- catch (IOException e)
- {
- throw new RuntimeException("Unable to write out test parameters: " + e, e);
- }
-
- // Write out the column headers after the properties.
- // writeColumnHeaders();
- }
-
- /**
- * Writes out and flushes the column headers for raw test data.
- */
- private void writeColumnHeaders()
- {
- // Write the column headers for the CSV file. Any IO exceptions are ignored.
- try
- {
- timingsWriter.write("Class, ");
- timingsWriter.write("Method, ");
- timingsWriter.write("Thread, ");
- timingsWriter.write("Test Outcome, ");
- timingsWriter.write("Time (milliseconds), ");
- timingsWriter.write("Memory Used (bytes), ");
- timingsWriter.write("Concurrency level, ");
- timingsWriter.write("Test Size\n");
-
- timingsWriter.flush();
- }
- catch (IOException e)
- {
- throw new RuntimeException("Unable to write out column headers: " + e, e);
- }
- }
-
- /**
- * Writes out the test results for the specified test. This outputs a single line of results to the csv file.
- *
- * @param r The test results to write out.
- * @param test The test to write them out for.
- */
- private void writeTestResults(TestResult r, Test test)
- {
- // Update the running stats for this batch.
- if ("Error".equals(r.testState))
- {
- numError++;
- }
- else if ("Failure".equals(r.testState))
- {
- numFailed++;
- }
- else if ("Pass".equals(r.testState))
- {
- numPassed++;
- }
-
- totalTests++;
-
- // Write the test name and thread information plus all instrumenation a line of the CSV ouput. Any IO
- // exceptions are ignored.
- try
- {
- synchronized (this.getClass())
- {
- timingsWriter.write(test.getClass().getName() + ", ");
- timingsWriter.write(((test instanceof TestCase) ? ((TestCase) test).getName() : "") + ", ");
- timingsWriter.write(Thread.currentThread().getName() + ", ");
- timingsWriter.write(r.testState + ", ");
- timingsWriter.write((((float) r.testTime) / 1000000f) + ", ");
- timingsWriter.write((r.testEndMem - r.testStartMem) + ", ");
- timingsWriter.write(r.testConcurrency + ", ");
- timingsWriter.write(r.testParam + "\n");
- }
- }
- catch (IOException e)
- {
- throw new RuntimeException("Unable to write out test results: " + e, e);
- }
- }
-
- /**
- * Supplies the shutdown hook. This attempts to flush the results in the event of the test runner being prematurely
- * suspended before the end of the current test batch.
- *
- * @return The shut down hook.
- */
- public Thread getShutdownHook()
- {
- return new Thread(new Runnable()
- {
- public void run()
- {
- log.debug("CSVTestListener::ShutdownHook: called");
-
- // Complete the current test batch stats.
- endBatch(TestContextProperties.getInstance());
- }
- });
- }
-
- /** Captures test results packaged into a single object, so that it can be set up as a thread local. */
- private static class TestResult
- {
- /** Used to hold the test timing. */
- public long testTime;
-
- /** Used to hold the test start memory usage. */
- public long testStartMem;
-
- /** Used to hold the test end memory usage. */
- public long testEndMem;
-
- /** Used to hold the test pass/fail/error state. */
- public String testState = "Pass";
-
- /** Used to hold the test parameter value. */
- public int testParam;
-
- /** Used to hold the concurrency level under which the test was run. */
- public int testConcurrency;
- }
-}
+/* + * + * 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.junit.extensions.listeners; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestListener; + +import org.apache.log4j.Logger; + +import org.apache.qpid.junit.extensions.ShutdownHookable; +import org.apache.qpid.junit.extensions.util.TestContextProperties; + +import java.io.IOException; +import java.io.Writer; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.TreeSet; + +/** + * CSVTestListener is both a test listener, a timings listener, a memory listener and a parameter listener. It listens for test completion events and + * then writes out all the data that it has listened to into a '.csv' (comma seperated values) file. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Listen to test events; start, end, fail, error. + * <tr><td> Listen to test timings. + * <tr><td> Listen to test memory usage. + * <tr><td> Listen to parameterized test parameters. + * <tr><td> Output all test data to a CSV file. + * </table> + * + * @author Rupert Smith + * + * @todo Write an XML output class. Write a transform to convert it into an HTML page with timings as graphs. + */ +public class CSVTestListener implements TestListener, TKTestListener, ShutdownHookable +{ + /** Used for logging. */ + private static final Logger log = Logger.getLogger(CSVTestListener.class); + + /** The timings file writer. */ + private Writer timingsWriter; + + /** + * Map for holding results on a per thread basis as they come in. A ThreadLocal is not used as sometimes an + * explicit thread id must be used, where notifications come from different threads than the ones that called + * the test method. + */ + Map<Long, TestResult> threadLocalResults = Collections.synchronizedMap(new HashMap<Long, TestResult>()); + + /** Used to record the start time of a complete test run, for outputing statistics at the end of the test run. */ + private long batchStartTime; + + /** Used to record the number of errors accross a complete test run. */ + private int numError; + + /** Used to record the number of failures accross a complete test run. */ + private int numFailed; + + /** Used to record the number of passes accross a complete test run. */ + private int numPassed; + + /** Used to record the total tests run accross a complete test run. Always equal to passes + errors + fails. */ + private int totalTests; + + /** Used to recrod the current concurrency level for the test batch. */ + private int concurrencyLevel; + + /** + * Used to record the total 'size' of the tests run, this is the number run times the average value of the test + * size parameters. + */ + private int totalSize; + + /** + * Used to record the summation of all of the individual test timgings. Note that total time and summed time + * are unlikely to be in agreement, exception for a single threaded test (with no setup time). Total time is + * the time taken to run all the tests, summed time is the added up time that each individual test took. So if + * two tests run in parallel and take one second each, total time will be one seconds, summed time will be two + * seconds. + */ + private long summedTime; + + /** Flag to indicate when batch has been started but not ended to ensure end batch stats are output only once. */ + private boolean batchStarted = false; + + /** + * Creates a new CSVTestListener object. + * + * @param writer A writer where this CSV listener should write out its output to. + */ + public CSVTestListener(Writer writer) + { + // log.debug("public CSVTestListener(Writer writer): called"); + + // Keep the writer. + this.timingsWriter = writer; + } + + /** + * Resets the test results to the default state of time zero, memory usage zero, test passed. + * + * @param test The test to resest any results for. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void reset(Test test, Long threadId) + { + // log.debug("public void reset(Test test = \"" + test + "\", Long threadId = " + threadId + "): called"); + + TestResult r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + + r.testTime = 0L; + r.testStartMem = 0L; + r.testEndMem = 0L; + r.testState = "Pass"; + r.testParam = 0; + } + + /** + * Called when a test results in an error. + * + * @param test The test which is in error. + * @param t Any Throwable raised by the test in error. + */ + public void addError(Test test, Throwable t) + { + // log.debug("public void addError(Test test, Throwable t): called"); + + TestResult r = threadLocalResults.get(Thread.currentThread().getId()); + r.testState = "Error"; + } + + /** + * Called when a test results in a failure. + * + * @param test The test which failed. + * @param t The AssertionFailedError that encapsulates the test failure. + */ + public void addFailure(Test test, AssertionFailedError t) + { + // log.debug("public void addFailure(Test \"" + test + "\", AssertionFailedError t): called"); + + TestResult r = threadLocalResults.get(Thread.currentThread().getId()); + r.testState = "Failure"; + } + + /** + * Called when a test completes to mark it as a test fail. This method should be used when registering a + * failure from a different thread than the one that started the test. + * + * @param test The test which failed. + * @param e The assertion that failed the test. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void addFailure(Test test, AssertionFailedError e, Long threadId) + { + // log.debug("public void addFailure(Test test = \"" + test + "\", AssertionFailedError e, Long threadId = " + threadId + // + "): called"); + + TestResult r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + + r.testState = "Failure"; + } + + /** + * Called when a test completes. Success, failure and errors. + * + * @param test The test which completed. + */ + public void endTest(Test test) + { + // log.debug("public void endTest(Test \"" + test + "\"): called"); + + TestResult r = threadLocalResults.get(Thread.currentThread().getId()); + + writeTestResults(r, test); + + // Clear all the test results for the thread. + threadLocalResults.remove(Thread.currentThread().getId()); + } + + /** + * Called when a test starts. + * + * @param test The test wich has started. + */ + public void startTest(Test test) + { + // log.debug("public void startTest(Test \"" + test + "\"): called"); + + // Initialize the thread local test results. + threadLocalResults.put(Thread.currentThread().getId(), new TestResult()); + } + + /** + * Should be called every time a test completes with the run time of that test. + * + * @param test The name of the test. + * @param nanos The run time of the test in nanoseconds. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void timing(Test test, long nanos, Long threadId) + { + // log.debug("public void timing(String \"" + test + "\", long " + nanos + "): called"); + + TestResult r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + + r.testTime = nanos; + summedTime += nanos; + } + + /** + * Should be called every time a test completed with the amount of memory used before and after the test was run. + * + * @param test The test which memory was measured for. + * @param memStart The total JVM memory used before the test was run. + * @param memEnd The total JVM memory used after the test was run. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void memoryUsed(Test test, long memStart, long memEnd, Long threadId) + { + // log.debug("public void memoryUsed(Test \"" + test + "\", long " + memStart + ", long " + memEnd + ", Long " + // + threadId + "): called"); + + TestResult r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + + r.testStartMem = memStart; + r.testEndMem = memEnd; + } + + /** + * Should be called every time a parameterized test completed with the int value of its test parameter. + * + * @param test The test which memory was measured for. + * @param parameter The int parameter value. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void parameterValue(Test test, int parameter, Long threadId) + { + // log.debug("public void parameterValue(Test test = \"" + test + "\", int parameter = " + parameter + "): called"); + + TestResult r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + + r.testParam = parameter; + totalSize += parameter; + } + + /** + * Should be called every time a test completes with the current number of test threads running. This should not + * change within a test batch, therefore it is safe to take this as a batch level property value too. + * + * @param test The test for which the measurement is being generated. + * @param threads The number of tests being run concurrently. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void concurrencyLevel(Test test, int threads, Long threadId) + { + // log.debug("public void concurrencyLevel(Test test = \"" + test + "\", int threads = " + threads + "): called"); + + TestResult r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + + r.testConcurrency = threads; + concurrencyLevel = threads; + + } + + /** + * Called when a test completes. Success, failure and errors. This method should be used when registering an + * end test from a different thread than the one that started the test. + * + * @param test The test which completed. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void endTest(Test test, Long threadId) + { + // log.debug("public void endTest(Test test = \"" + test + "\", Long threadId " + threadId + "): called"); + + TestResult r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + + writeTestResults(r, test); + } + + /** + * Takes a time stamp for the beginning of the batch and resets stats counted for the batch. + */ + public synchronized void startBatch() + { + numError = 0; + numFailed = 0; + numPassed = 0; + totalTests = 0; + totalSize = 0; + batchStartTime = System.nanoTime(); + summedTime = 0; + batchStarted = true; + + // Write out the column headers for the batch. + writeColumnHeaders(); + } + + /** + * Takes a time stamp for the end of the batch to calculate the total run time. + * Write this and other stats out to the tail of the csv file. + * + * @param parameters The optional test parameters, may be null. + */ + public synchronized void endBatch(Properties parameters) + { + boolean noParams = (parameters == null) || (parameters.size() == 0); + + // Check that a batch has been started but not ended. + if (batchStarted) + { + long batchEndTime = System.nanoTime(); + float totalTimeMillis = ((float) (batchEndTime - batchStartTime)) / 1000000f; + float summedTimeMillis = ((float) summedTime) / 1000000f; + + // Write the stats for the batch out. + try + { + synchronized (this.getClass()) + { + timingsWriter.write("Total Tests:, " + totalTests + ", "); + timingsWriter.write("Total Passed:, " + numPassed + ", "); + timingsWriter.write("Total Failed:, " + numFailed + ", "); + timingsWriter.write("Total Error:, " + numError + ", "); + timingsWriter.write("Total Size:, " + totalSize + ", "); + timingsWriter.write("Summed Time:, " + summedTimeMillis + ", "); + timingsWriter.write("Concurrency Level:, " + concurrencyLevel + ", "); + timingsWriter.write("Total Time:, " + totalTimeMillis + ", "); + timingsWriter.write("Test Throughput:, " + (((float) totalTests) / totalTimeMillis) + ", "); + timingsWriter.write("Test * Size Throughput:, " + (((float) totalSize) / totalTimeMillis) + + (noParams ? "\n\n" : ", ")); + + // Write out the test parameters if there are any specified. + if (!noParams) + { + properties(parameters); + } + + timingsWriter.flush(); + } + } + catch (IOException e) + { + throw new RuntimeException("Unable to write out end batch statistics: " + e, e); + } + } + + // Reset the batch started flag to ensure stats are only output once. + batchStarted = false; + } + + /** + * Notifies listeners of the tests read/set properties. + * + * @param properties The tests read/set properties. + */ + public void properties(Properties properties) + { + // log.debug("public void properties(Properties properties): called"); + + // Write the properties out to the results file. + try + { + synchronized (this.getClass()) + { + Set keySet = new TreeSet(properties.keySet()); + + // timingsWriter.write("\n"); + + for (Object key : keySet) + { + timingsWriter.write(key + " = , " + properties.getProperty((String) key) + ", "); + } + + timingsWriter.write("\n\n"); + // timingsWriter.flush(); + } + } + catch (IOException e) + { + throw new RuntimeException("Unable to write out test parameters: " + e, e); + } + + // Write out the column headers after the properties. + // writeColumnHeaders(); + } + + /** + * Writes out and flushes the column headers for raw test data. + */ + private void writeColumnHeaders() + { + // Write the column headers for the CSV file. Any IO exceptions are ignored. + try + { + timingsWriter.write("Class, "); + timingsWriter.write("Method, "); + timingsWriter.write("Thread, "); + timingsWriter.write("Test Outcome, "); + timingsWriter.write("Time (milliseconds), "); + timingsWriter.write("Memory Used (bytes), "); + timingsWriter.write("Concurrency level, "); + timingsWriter.write("Test Size\n"); + + timingsWriter.flush(); + } + catch (IOException e) + { + throw new RuntimeException("Unable to write out column headers: " + e, e); + } + } + + /** + * Writes out the test results for the specified test. This outputs a single line of results to the csv file. + * + * @param r The test results to write out. + * @param test The test to write them out for. + */ + private void writeTestResults(TestResult r, Test test) + { + // Update the running stats for this batch. + if ("Error".equals(r.testState)) + { + numError++; + } + else if ("Failure".equals(r.testState)) + { + numFailed++; + } + else if ("Pass".equals(r.testState)) + { + numPassed++; + } + + totalTests++; + + // Write the test name and thread information plus all instrumenation a line of the CSV ouput. Any IO + // exceptions are ignored. + try + { + synchronized (this.getClass()) + { + timingsWriter.write(test.getClass().getName() + ", "); + timingsWriter.write(((test instanceof TestCase) ? ((TestCase) test).getName() : "") + ", "); + timingsWriter.write(Thread.currentThread().getName() + ", "); + timingsWriter.write(r.testState + ", "); + timingsWriter.write((((float) r.testTime) / 1000000f) + ", "); + timingsWriter.write((r.testEndMem - r.testStartMem) + ", "); + timingsWriter.write(r.testConcurrency + ", "); + timingsWriter.write(r.testParam + "\n"); + } + } + catch (IOException e) + { + throw new RuntimeException("Unable to write out test results: " + e, e); + } + } + + /** + * Supplies the shutdown hook. This attempts to flush the results in the event of the test runner being prematurely + * suspended before the end of the current test batch. + * + * @return The shut down hook. + */ + public Thread getShutdownHook() + { + return new Thread(new Runnable() + { + public void run() + { + log.debug("CSVTestListener::ShutdownHook: called"); + + // Complete the current test batch stats. + endBatch(TestContextProperties.getInstance()); + } + }); + } + + /** Captures test results packaged into a single object, so that it can be set up as a thread local. */ + private static class TestResult + { + /** Used to hold the test timing. */ + public long testTime; + + /** Used to hold the test start memory usage. */ + public long testStartMem; + + /** Used to hold the test end memory usage. */ + public long testEndMem; + + /** Used to hold the test pass/fail/error state. */ + public String testState = "Pass"; + + /** Used to hold the test parameter value. */ + public int testParam; + + /** Used to hold the concurrency level under which the test was run. */ + public int testConcurrency; + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/ConsoleTestListener.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/ConsoleTestListener.java index 5c328a8814..2955fba2bd 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/ConsoleTestListener.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/ConsoleTestListener.java @@ -1,264 +1,264 @@ -/*
- *
- * 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.junit.extensions.listeners;
-
-import junit.framework.AssertionFailedError;
-import junit.framework.Test;
-import junit.framework.TestListener;
-
-import org.apache.qpid.junit.extensions.SleepThrottle;
-import org.apache.qpid.junit.extensions.Throttle;
-
-import java.util.Properties;
-
-/**
- * ConsoleTestListener provides feedback to the console, as test timings are taken, by drawing a '.', or an 'E', or an
- * 'F', for each test that passes, is in error or fails. It does this for every test result registered with the framework,
- * not just on the completion of each test method as the JUnit one does. It also uses a throttle to cap the rate of
- * dot drawing, as exessively high rates can degrade test performance without providing much usefull feedback to the user.
- * Unlike the JUnit dot drawing feedback, this one will correctly wrap lines when tests are run concurrently (the
- * rate capping ensures that this does not become a hot-spot for thread contention).
- *
- * <p/>Where rate capping causes the conflation of multiple requested dots into a single dot, the dot that is actually
- * drawn will be the worst result within the conflation period, that is, error is worse than fail which is worse than pass.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Draw dots as each test result completes, at a capped rate.
- * </table>
- *
- * @author Rupert Smith
- */
-public class ConsoleTestListener implements TestListener, TKTestListener
-{
- /** Used to indicate a test pass. */
- private static final int PASS = 1;
-
- /** Used to indicate a test failure. */
- private static final int FAIL = 2;
-
- /** Used to indicate a test error. */
- private static final int ERROR = 3;
-
- /** Defines the maximum number of columns of dots to print. */
- private static final int MAX_COLUMNS = 80;
-
- /** Used to throttle the dot writing rate. */
- Throttle throttle;
-
- /** Tracks the worst test result so far, when the throttled print method must conflate results due to throttling. */
- private int conflatedResult = 0;
-
- /** Tracks the column count as dots are printed, so that newlines can be inserted at the right margin. */
- private int columnCount = 0;
-
- /** Used as a monitor on the print method criticial section, to ensure that line ends always happen in the right place. */
- private final Object printMonitor = new Object();
-
- /**
- * Creates a dot drawing feedback test listener, set by default to 80 columns at 80 dots per second capped rate.
- */
- public ConsoleTestListener()
- {
- throttle = new SleepThrottle();
- throttle.setRate(80f);
- }
-
- /**
- * Prints dots at a capped rate, conflating the requested type of dot to draw if this method is called at a rate
- * higher than the capped rate. The conflation works by always printing the worst result that occurs within the
- * conflation period, that is, error is worse than fail which is worse than a pass.
- *
- * @param result The type of dot to draw, {@link #PASS}, {@link #FAIL} or {@link #ERROR}.
- */
- private void throttledPrint(int result)
- {
- conflatedResult = (result > conflatedResult) ? result : conflatedResult;
-
- if (throttle.checkThrottle())
- {
- synchronized (printMonitor)
- {
- switch (conflatedResult)
- {
- default:
- case PASS:
- System.out.print('.');
- break;
-
- case FAIL:
- System.out.print('F');
- break;
-
- case ERROR:
- System.out.print('E');
- break;
- }
-
- columnCount = (columnCount >= MAX_COLUMNS) ? 0 : (columnCount + 1);
-
- if (columnCount == 0)
- {
- System.out.print('\n');
- }
-
- conflatedResult = 0;
- }
- }
- }
-
- /**
- * An error occurred.
- *
- * @param test The test in error. Ignored.
- * @param t The error that the test threw. Ignored.
- */
- public void addError(Test test, Throwable t)
- {
- throttledPrint(ERROR);
- }
-
- /**
- * A failure occurred.
- *
- * @param test The test that failed. Ignored.
- * @param t The assertion failure that the test threw. Ignored.
- */
- public void addFailure(Test test, AssertionFailedError t)
- {
- throttledPrint(FAIL);
- }
-
- /**
- * A test ended.
- *
- * @param test The test that ended. Ignored.
- */
- public void endTest(Test test)
- {
- throttledPrint(PASS);
- }
-
- /**
- * A test started.
- *
- * @param test The test that started. Ignored.
- */
- public void startTest(Test test)
- { }
-
- /**
- * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed.
- *
- * @param test The test to resest any results for.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void reset(Test test, Long threadId)
- { }
-
- /**
- * Should be called every time a test completes with the run time of that test.
- *
- * @param test The name of the test.
- * @param nanos The run time of the test in nanoseconds.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void timing(Test test, long nanos, Long threadId)
- { }
-
- /**
- * Should be called every time a test completed with the amount of memory used before and after the test was run.
- *
- * @param test The test which memory was measured for.
- * @param memStart The total JVM memory used before the test was run.
- * @param memEnd The total JVM memory used after the test was run.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void memoryUsed(Test test, long memStart, long memEnd, Long threadId)
- { }
-
- /**
- * Should be called every time a parameterized test completed with the int value of its test parameter.
- *
- * @param test The test which memory was measured for.
- * @param parameter The int parameter value.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void parameterValue(Test test, int parameter, Long threadId)
- { }
-
- /**
- * Should be called every time a test completes with the current number of test threads running.
- *
- * @param test The test for which the measurement is being generated.
- * @param threads The number of tests being run concurrently.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void concurrencyLevel(Test test, int threads, Long threadId)
- { }
-
- /**
- * Called when a test completes. Success, failure and errors. This method should be used when registering an
- * end test from a different thread than the one that started the test.
- *
- * @param test The test which completed.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void endTest(Test test, Long threadId)
- {
- throttledPrint(PASS);
- }
-
- /**
- * Called when a test completes to mark it as a test fail. This method should be used when registering a
- * failure from a different thread than the one that started the test.
- *
- * @param test The test which failed.
- * @param e The assertion that failed the test.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void addFailure(Test test, AssertionFailedError e, Long threadId)
- {
- throttledPrint(FAIL);
- }
-
- /**
- * Notifies listeners of the start of a complete run of tests.
- */
- public void startBatch()
- { }
-
- /**
- * Notifies listeners of the end of a complete run of tests.
- *
- * @param parameters The optional test parameters to log out with the batch results.
- */
- public void endBatch(Properties parameters)
- { }
-
- /**
- * Notifies listeners of the tests read/set properties.
- *
- * @param properties The tests read/set properties.
- */
- public void properties(Properties properties)
- { }
-}
+/* + * + * 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.junit.extensions.listeners; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestListener; + +import org.apache.qpid.junit.extensions.SleepThrottle; +import org.apache.qpid.junit.extensions.Throttle; + +import java.util.Properties; + +/** + * ConsoleTestListener provides feedback to the console, as test timings are taken, by drawing a '.', or an 'E', or an + * 'F', for each test that passes, is in error or fails. It does this for every test result registered with the framework, + * not just on the completion of each test method as the JUnit one does. It also uses a throttle to cap the rate of + * dot drawing, as exessively high rates can degrade test performance without providing much usefull feedback to the user. + * Unlike the JUnit dot drawing feedback, this one will correctly wrap lines when tests are run concurrently (the + * rate capping ensures that this does not become a hot-spot for thread contention). + * + * <p/>Where rate capping causes the conflation of multiple requested dots into a single dot, the dot that is actually + * drawn will be the worst result within the conflation period, that is, error is worse than fail which is worse than pass. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Draw dots as each test result completes, at a capped rate. + * </table> + * + * @author Rupert Smith + */ +public class ConsoleTestListener implements TestListener, TKTestListener +{ + /** Used to indicate a test pass. */ + private static final int PASS = 1; + + /** Used to indicate a test failure. */ + private static final int FAIL = 2; + + /** Used to indicate a test error. */ + private static final int ERROR = 3; + + /** Defines the maximum number of columns of dots to print. */ + private static final int MAX_COLUMNS = 80; + + /** Used to throttle the dot writing rate. */ + Throttle throttle; + + /** Tracks the worst test result so far, when the throttled print method must conflate results due to throttling. */ + private int conflatedResult = 0; + + /** Tracks the column count as dots are printed, so that newlines can be inserted at the right margin. */ + private int columnCount = 0; + + /** Used as a monitor on the print method criticial section, to ensure that line ends always happen in the right place. */ + private final Object printMonitor = new Object(); + + /** + * Creates a dot drawing feedback test listener, set by default to 80 columns at 80 dots per second capped rate. + */ + public ConsoleTestListener() + { + throttle = new SleepThrottle(); + throttle.setRate(80f); + } + + /** + * Prints dots at a capped rate, conflating the requested type of dot to draw if this method is called at a rate + * higher than the capped rate. The conflation works by always printing the worst result that occurs within the + * conflation period, that is, error is worse than fail which is worse than a pass. + * + * @param result The type of dot to draw, {@link #PASS}, {@link #FAIL} or {@link #ERROR}. + */ + private void throttledPrint(int result) + { + conflatedResult = (result > conflatedResult) ? result : conflatedResult; + + if (throttle.checkThrottle()) + { + synchronized (printMonitor) + { + switch (conflatedResult) + { + default: + case PASS: + System.out.print('.'); + break; + + case FAIL: + System.out.print('F'); + break; + + case ERROR: + System.out.print('E'); + break; + } + + columnCount = (columnCount >= MAX_COLUMNS) ? 0 : (columnCount + 1); + + if (columnCount == 0) + { + System.out.print('\n'); + } + + conflatedResult = 0; + } + } + } + + /** + * An error occurred. + * + * @param test The test in error. Ignored. + * @param t The error that the test threw. Ignored. + */ + public void addError(Test test, Throwable t) + { + throttledPrint(ERROR); + } + + /** + * A failure occurred. + * + * @param test The test that failed. Ignored. + * @param t The assertion failure that the test threw. Ignored. + */ + public void addFailure(Test test, AssertionFailedError t) + { + throttledPrint(FAIL); + } + + /** + * A test ended. + * + * @param test The test that ended. Ignored. + */ + public void endTest(Test test) + { + throttledPrint(PASS); + } + + /** + * A test started. + * + * @param test The test that started. Ignored. + */ + public void startTest(Test test) + { } + + /** + * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed. + * + * @param test The test to resest any results for. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void reset(Test test, Long threadId) + { } + + /** + * Should be called every time a test completes with the run time of that test. + * + * @param test The name of the test. + * @param nanos The run time of the test in nanoseconds. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void timing(Test test, long nanos, Long threadId) + { } + + /** + * Should be called every time a test completed with the amount of memory used before and after the test was run. + * + * @param test The test which memory was measured for. + * @param memStart The total JVM memory used before the test was run. + * @param memEnd The total JVM memory used after the test was run. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void memoryUsed(Test test, long memStart, long memEnd, Long threadId) + { } + + /** + * Should be called every time a parameterized test completed with the int value of its test parameter. + * + * @param test The test which memory was measured for. + * @param parameter The int parameter value. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void parameterValue(Test test, int parameter, Long threadId) + { } + + /** + * Should be called every time a test completes with the current number of test threads running. + * + * @param test The test for which the measurement is being generated. + * @param threads The number of tests being run concurrently. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void concurrencyLevel(Test test, int threads, Long threadId) + { } + + /** + * Called when a test completes. Success, failure and errors. This method should be used when registering an + * end test from a different thread than the one that started the test. + * + * @param test The test which completed. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void endTest(Test test, Long threadId) + { + throttledPrint(PASS); + } + + /** + * Called when a test completes to mark it as a test fail. This method should be used when registering a + * failure from a different thread than the one that started the test. + * + * @param test The test which failed. + * @param e The assertion that failed the test. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void addFailure(Test test, AssertionFailedError e, Long threadId) + { + throttledPrint(FAIL); + } + + /** + * Notifies listeners of the start of a complete run of tests. + */ + public void startBatch() + { } + + /** + * Notifies listeners of the end of a complete run of tests. + * + * @param parameters The optional test parameters to log out with the batch results. + */ + public void endBatch(Properties parameters) + { } + + /** + * Notifies listeners of the tests read/set properties. + * + * @param properties The tests read/set properties. + */ + public void properties(Properties properties) + { } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/TKTestListener.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/TKTestListener.java index 4f08e8bf2d..11fc6a7451 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/TKTestListener.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/TKTestListener.java @@ -1,132 +1,132 @@ -/*
- *
- * 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.junit.extensions.listeners;
-
-import junit.framework.AssertionFailedError;
-import junit.framework.Test;
-import junit.framework.TestListener;
-
-import java.util.Properties;
-
-/**
- * TKTestListener is a listener interface for listeners that want to be informed of the run times of tests, the memory
- * usage of tests, the 'size' parameters of parameterized tests and the begin and end events of complete test runs.
- * {@link org.apache.qpid.junit.extensions.TKTestResult} is an example of a test result class that listeners
- * interested in these events can be attached to.
- *
- * The {@link #timing(junit.framework.Test, long, Long)}, {@link #memoryUsed(junit.framework.Test, long, long, Long)},
- * {@link #parameterValue(junit.framework.Test, int, Long)} and {@link #endTest(junit.framework.Test, Long)} methods
- * all accept on optional thread id parameter.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities
- * <tr><td> Listen to test timings.
- * <tr><td> Listen to test memory usages.
- * <tr><td> Listen to parameterized test parameters.
- * </table>
- *
- * @author Rupert Smith
- */
-public interface TKTestListener extends TestListener
-{
- /**
- * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed.
- *
- * @param test The test to resest any results for.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void reset(Test test, Long threadId);
-
- /**
- * Should be called every time a test completes with the run time of that test.
- *
- * @param test The name of the test.
- * @param nanos The run time of the test in nanoseconds.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void timing(Test test, long nanos, Long threadId);
-
- /**
- * Should be called every time a test completed with the amount of memory used before and after the test was run.
- *
- * @param test The test which memory was measured for.
- * @param memStart The total JVM memory used before the test was run.
- * @param memEnd The total JVM memory used after the test was run.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void memoryUsed(Test test, long memStart, long memEnd, Long threadId);
-
- /**
- * Should be called every time a parameterized test completed with the int value of its test parameter.
- *
- * @param test The test which memory was measured for.
- * @param parameter The int parameter value.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void parameterValue(Test test, int parameter, Long threadId);
-
- /**
- * Should be called every time a test completes with the current number of test threads running.
- *
- * @param test The test for which the measurement is being generated.
- * @param threads The number of tests being run concurrently.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void concurrencyLevel(Test test, int threads, Long threadId);
-
- /**
- * Called when a test completes. Success, failure and errors. This method should be used when registering an
- * end test from a different thread than the one that started the test.
- *
- * @param test The test which completed.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void endTest(Test test, Long threadId);
-
- /**
- * Called when a test completes to mark it as a test fail. This method should be used when registering a
- * failure from a different thread than the one that started the test.
- *
- * @param test The test which failed.
- * @param e The assertion that failed the test.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void addFailure(Test test, AssertionFailedError e, Long threadId);
-
- /**
- * Notifies listeners of the start of a complete run of tests.
- */
- public void startBatch();
-
- /**
- * Notifies listeners of the end of a complete run of tests.
- *
- * @param parameters The optional test parameters to log out with the batch results.
- */
- public void endBatch(Properties parameters);
-
- /**
- * Notifies listeners of the tests read/set properties.
- *
- * @param properties The tests read/set properties.
- */
- public void properties(Properties properties);
-}
+/* + * + * 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.junit.extensions.listeners; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestListener; + +import java.util.Properties; + +/** + * TKTestListener is a listener interface for listeners that want to be informed of the run times of tests, the memory + * usage of tests, the 'size' parameters of parameterized tests and the begin and end events of complete test runs. + * {@link org.apache.qpid.junit.extensions.TKTestResult} is an example of a test result class that listeners + * interested in these events can be attached to. + * + * The {@link #timing(junit.framework.Test, long, Long)}, {@link #memoryUsed(junit.framework.Test, long, long, Long)}, + * {@link #parameterValue(junit.framework.Test, int, Long)} and {@link #endTest(junit.framework.Test, Long)} methods + * all accept on optional thread id parameter. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities + * <tr><td> Listen to test timings. + * <tr><td> Listen to test memory usages. + * <tr><td> Listen to parameterized test parameters. + * </table> + * + * @author Rupert Smith + */ +public interface TKTestListener extends TestListener +{ + /** + * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed. + * + * @param test The test to resest any results for. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void reset(Test test, Long threadId); + + /** + * Should be called every time a test completes with the run time of that test. + * + * @param test The name of the test. + * @param nanos The run time of the test in nanoseconds. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void timing(Test test, long nanos, Long threadId); + + /** + * Should be called every time a test completed with the amount of memory used before and after the test was run. + * + * @param test The test which memory was measured for. + * @param memStart The total JVM memory used before the test was run. + * @param memEnd The total JVM memory used after the test was run. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void memoryUsed(Test test, long memStart, long memEnd, Long threadId); + + /** + * Should be called every time a parameterized test completed with the int value of its test parameter. + * + * @param test The test which memory was measured for. + * @param parameter The int parameter value. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void parameterValue(Test test, int parameter, Long threadId); + + /** + * Should be called every time a test completes with the current number of test threads running. + * + * @param test The test for which the measurement is being generated. + * @param threads The number of tests being run concurrently. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void concurrencyLevel(Test test, int threads, Long threadId); + + /** + * Called when a test completes. Success, failure and errors. This method should be used when registering an + * end test from a different thread than the one that started the test. + * + * @param test The test which completed. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void endTest(Test test, Long threadId); + + /** + * Called when a test completes to mark it as a test fail. This method should be used when registering a + * failure from a different thread than the one that started the test. + * + * @param test The test which failed. + * @param e The assertion that failed the test. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void addFailure(Test test, AssertionFailedError e, Long threadId); + + /** + * Notifies listeners of the start of a complete run of tests. + */ + public void startBatch(); + + /** + * Notifies listeners of the end of a complete run of tests. + * + * @param parameters The optional test parameters to log out with the batch results. + */ + public void endBatch(Properties parameters); + + /** + * Notifies listeners of the tests read/set properties. + * + * @param properties The tests read/set properties. + */ + public void properties(Properties properties); +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/XMLTestListener.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/XMLTestListener.java index a88837e323..ded07ef5bb 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/XMLTestListener.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/XMLTestListener.java @@ -1,400 +1,400 @@ -/*
- *
- * 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.junit.extensions.listeners;
-
-import junit.framework.AssertionFailedError;
-import junit.framework.Test;
-import junit.framework.TestCase;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.junit.extensions.ShutdownHookable;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.Writer;
-import java.util.*;
-
-/**
- * Listens for test results for a named test and outputs these in the standard JUnit XML format to the specified
- * writer.
- *
- * <p/>The API for this listener accepts notifications about different aspects of a tests results through different
- * methods, so some assumption needs to be made as to which test result a notification refers to. For example
- * {@link #startTest} will be called, then possibly {@link #timing} will be called, even though the test instance is
- * passed in both cases, it is not enough to distinguish a particular run of the test, as the test case instance may
- * be being shared between multiple threads, or being run a repeated number of times, and can therfore be re-used
- * between calls. The listeners make the assumption that, for every test, a unique thread will call {@link #startTest}
- * and {@link #endTest} to delimit each test. All calls to set test parameters, timings, state and so on, will occur
- * between the start and end and will be given with the same thread id as the start and end, so the thread id provides
- * a unqiue value to identify a particular test run against.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Listen to test lifecycle notifications.
- * <tr><td> Listen to test errors and failures.
- * <tr><td> Listen to test timings.
- * <tr><td> Listen to test memory usages.
- * <tr><td> Listen to parameterized test parameters.
- * <tr><th> Responsibilities
- * </table>
- *
- * @todo Merge this class with CSV test listener, making the collection of results common to both, and only factoring
- * out the results printing code into sub-classes. Provide a simple XML results formatter with the same format as
- * the ant XML formatter, and a more structured one for outputing results with timings and summaries from
- * performance tests.
- *
- * @author Rupert Smith
- */
-public class XMLTestListener implements TKTestListener, ShutdownHookable
-{
- /** Used for debugging. */
- private static final Logger log = Logger.getLogger(XMLTestListener.class);
-
- /** The results file writer. */
- protected Writer writer;
-
- /** Holds the results for individual tests. */
- // protected Map<Result, Result> results = new LinkedHashMap<Result, Result>();
- // protected List<Result> results = new ArrayList<Result>();
-
- /**
- * Map for holding results on a per thread basis as they come in. A ThreadLocal is not used as sometimes an
- * explicit thread id must be used, where notifications come from different threads than the ones that called
- * the test method.
- */
- Map<Long, Result> threadLocalResults = Collections.synchronizedMap(new LinkedHashMap<Long, Result>());
-
- /**
- * Holds results for tests that have ended. Transferring these results here from the per-thread results map, means
- * that the thread id is freed for the thread to generate more results.
- */
- List<Result> results = new ArrayList<Result>();
-
- /** Holds the overall error count. */
- protected int errors = 0;
-
- /** Holds the overall failure count. */
- protected int failures = 0;
-
- /** Holds the overall tests run count. */
- protected int runs = 0;
-
- /** Holds the name of the class that tests are being run for. */
- String testClassName;
-
- /**
- * Creates a new XML results output listener that writes to the specified location.
- *
- * @param writer The location to write results to.
- * @param testClassName The name of the test class to include in the test results.
- */
- public XMLTestListener(Writer writer, String testClassName)
- {
- log.debug("public XMLTestListener(Writer writer, String testClassName = " + testClassName + "): called");
-
- this.writer = writer;
- this.testClassName = testClassName;
- }
-
- /**
- * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed.
- *
- * @param test The test to resest any results for.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void reset(Test test, Long threadId)
- {
- log.debug("public void reset(Test test = " + test + ", Long threadId = " + threadId + "): called");
-
- XMLTestListener.Result r =
- (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
-
- r.error = null;
- r.failure = null;
-
- }
-
- /**
- * Notification that a test started.
- *
- * @param test The test that started.
- */
- public void startTest(Test test)
- {
- log.debug("public void startTest(Test test = " + test + "): called");
-
- Result newResult = new Result(test.getClass().getName(), ((TestCase) test).getName());
-
- // Initialize the thread local test results.
- threadLocalResults.put(Thread.currentThread().getId(), newResult);
- runs++;
- }
-
- /**
- * Should be called every time a test completes with the run time of that test.
- *
- * @param test The name of the test.
- * @param nanos The run time of the test in nanoseconds.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void timing(Test test, long nanos, Long threadId)
- { }
-
- /**
- * Should be called every time a test completed with the amount of memory used before and after the test was run.
- *
- * @param test The test which memory was measured for.
- * @param memStart The total JVM memory used before the test was run.
- * @param memEnd The total JVM memory used after the test was run.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void memoryUsed(Test test, long memStart, long memEnd, Long threadId)
- { }
-
- /**
- * Should be called every time a parameterized test completed with the int value of its test parameter.
- *
- * @param test The test which memory was measured for.
- * @param parameter The int parameter value.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void parameterValue(Test test, int parameter, Long threadId)
- { }
-
- /**
- * Should be called every time a test completes with the current number of test threads running.
- *
- * @param test The test for which the measurement is being generated.
- * @param threads The number of tests being run concurrently.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void concurrencyLevel(Test test, int threads, Long threadId)
- { }
-
- /**
- * Notifies listeners of the tests read/set properties.
- *
- * @param properties The tests read/set properties.
- */
- public void properties(Properties properties)
- { }
-
- /**
- * Notification that a test ended.
- *
- * @param test The test that ended.
- */
- public void endTest(Test test)
- {
- log.debug("public void endTest(Test test = " + test + "): called");
-
- // Move complete test results into the completed tests list.
- Result r = threadLocalResults.get(Thread.currentThread().getId());
- results.add(r);
-
- // Clear all the test results for the thread.
- threadLocalResults.remove(Thread.currentThread().getId());
- }
-
- /**
- * Called when a test completes. Success, failure and errors. This method should be used when registering an
- * end test from a different thread than the one that started the test.
- *
- * @param test The test which completed.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void endTest(Test test, Long threadId)
- {
- log.debug("public void endTest(Test test = " + test + ", Long threadId = " + threadId + "): called");
-
- // Move complete test results into the completed tests list.
- Result r =
- (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
- results.add(r);
-
- // Clear all the test results for the thread.
- threadLocalResults.remove(Thread.currentThread().getId());
- }
-
- /**
- * An error occurred.
- *
- * @param test The test in which the error occurred.
- * @param t The throwable that resulted from the error.
- */
- public void addError(Test test, Throwable t)
- {
- log.debug("public void addError(Test test = " + test + ", Throwable t = " + t + "): called");
-
- Result r = threadLocalResults.get(Thread.currentThread().getId());
- r.error = t;
- errors++;
- }
-
- /**
- * A failure occurred.
- *
- * @param test The test in which the failure occurred.
- * @param t The JUnit assertions that led to the failure.
- */
- public void addFailure(Test test, AssertionFailedError t)
- {
- log.debug("public void addFailure(Test test = " + test + ", AssertionFailedError t = " + t + "): called");
-
- Result r = threadLocalResults.get(Thread.currentThread().getId());
- r.failure = t;
- failures++;
- }
-
- /**
- * Called when a test completes to mark it as a test fail. This method should be used when registering a
- * failure from a different thread than the one that started the test.
- *
- * @param test The test which failed.
- * @param e The assertion that failed the test.
- * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
- */
- public void addFailure(Test test, AssertionFailedError e, Long threadId)
- {
- log.debug("public void addFailure(Test test, AssertionFailedError e, Long threadId): called");
-
- Result r =
- (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
- r.failure = e;
- failures++;
- }
-
- /**
- * Notifies listeners of the start of a complete run of tests.
- */
- public void startBatch()
- {
- log.debug("public void startBatch(): called");
-
- // Reset all results counts.
- threadLocalResults = Collections.synchronizedMap(new HashMap<Long, Result>());
- errors = 0;
- failures = 0;
- runs = 0;
-
- // Write out the file header.
- try
- {
- writer.write("<?xml version=\"1.0\" ?>\n");
- }
- catch (IOException e)
- {
- throw new RuntimeException("Unable to write the test results.", e);
- }
- }
-
- /**
- * Notifies listeners of the end of a complete run of tests.
- *
- * @param parameters The optional test parameters to log out with the batch results.
- */
- public void endBatch(Properties parameters)
- {
- log.debug("public void endBatch(Properties parameters = " + parameters + "): called");
-
- // Write out the results.
- try
- {
- // writer.write("<?xml version=\"1.0\" ?>\n");
- writer.write("<testsuite errors=\"" + errors + "\" failures=\"" + failures + "\" tests=\"" + runs + "\" name=\""
- + testClassName + "\">\n");
-
- for (Result result : results)
- {
- writer.write(" <testcase classname=\"" + result.testClass + "\" name=\"" + result.testName + "\">\n");
-
- if (result.error != null)
- {
- writer.write(" <error type=\"" + result.error.getClass() + "\">");
- result.error.printStackTrace(new PrintWriter(writer));
- writer.write(" </error>");
- }
- else if (result.failure != null)
- {
- writer.write(" <failure type=\"" + result.failure.getClass() + "\">");
- result.failure.printStackTrace(new PrintWriter(writer));
- writer.write(" </failure>");
- }
-
- writer.write(" </testcase>\n");
- }
-
- writer.write("</testsuite>\n");
- writer.flush();
- }
- catch (IOException e)
- {
- throw new RuntimeException("Unable to write the test results.", e);
- }
- }
-
- /**
- * Supplies the shutdown hook.
- *
- * @return The shut down hook.
- */
- public Thread getShutdownHook()
- {
- return new Thread(new Runnable()
- {
- public void run()
- {
- log.debug("XMLTestListener::ShutdownHook: called");
- }
- });
- }
-
- /**
- * Used to capture the results of a particular test run.
- */
- protected static class Result
- {
- /** Holds the name of the test class. */
- public String testClass;
-
- /** Holds the name of the test method. */
- public String testName;
-
- /** Holds the exception that caused error in this test. */
- public Throwable error;
-
- /** Holds the assertion exception that caused failure in this test. */
- public AssertionFailedError failure;
-
- /**
- * Creates a placeholder for the results of a test.
- *
- * @param testClass The test class.
- * @param testName The name of the test that was run.
- */
- public Result(String testClass, String testName)
- {
- this.testClass = testClass;
- this.testName = testName;
- }
- }
-}
+/* + * + * 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.junit.extensions.listeners; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestCase; + +import org.apache.log4j.Logger; + +import org.apache.qpid.junit.extensions.ShutdownHookable; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; +import java.util.*; + +/** + * Listens for test results for a named test and outputs these in the standard JUnit XML format to the specified + * writer. + * + * <p/>The API for this listener accepts notifications about different aspects of a tests results through different + * methods, so some assumption needs to be made as to which test result a notification refers to. For example + * {@link #startTest} will be called, then possibly {@link #timing} will be called, even though the test instance is + * passed in both cases, it is not enough to distinguish a particular run of the test, as the test case instance may + * be being shared between multiple threads, or being run a repeated number of times, and can therfore be re-used + * between calls. The listeners make the assumption that, for every test, a unique thread will call {@link #startTest} + * and {@link #endTest} to delimit each test. All calls to set test parameters, timings, state and so on, will occur + * between the start and end and will be given with the same thread id as the start and end, so the thread id provides + * a unqiue value to identify a particular test run against. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Listen to test lifecycle notifications. + * <tr><td> Listen to test errors and failures. + * <tr><td> Listen to test timings. + * <tr><td> Listen to test memory usages. + * <tr><td> Listen to parameterized test parameters. + * <tr><th> Responsibilities + * </table> + * + * @todo Merge this class with CSV test listener, making the collection of results common to both, and only factoring + * out the results printing code into sub-classes. Provide a simple XML results formatter with the same format as + * the ant XML formatter, and a more structured one for outputing results with timings and summaries from + * performance tests. + * + * @author Rupert Smith + */ +public class XMLTestListener implements TKTestListener, ShutdownHookable +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(XMLTestListener.class); + + /** The results file writer. */ + protected Writer writer; + + /** Holds the results for individual tests. */ + // protected Map<Result, Result> results = new LinkedHashMap<Result, Result>(); + // protected List<Result> results = new ArrayList<Result>(); + + /** + * Map for holding results on a per thread basis as they come in. A ThreadLocal is not used as sometimes an + * explicit thread id must be used, where notifications come from different threads than the ones that called + * the test method. + */ + Map<Long, Result> threadLocalResults = Collections.synchronizedMap(new LinkedHashMap<Long, Result>()); + + /** + * Holds results for tests that have ended. Transferring these results here from the per-thread results map, means + * that the thread id is freed for the thread to generate more results. + */ + List<Result> results = new ArrayList<Result>(); + + /** Holds the overall error count. */ + protected int errors = 0; + + /** Holds the overall failure count. */ + protected int failures = 0; + + /** Holds the overall tests run count. */ + protected int runs = 0; + + /** Holds the name of the class that tests are being run for. */ + String testClassName; + + /** + * Creates a new XML results output listener that writes to the specified location. + * + * @param writer The location to write results to. + * @param testClassName The name of the test class to include in the test results. + */ + public XMLTestListener(Writer writer, String testClassName) + { + log.debug("public XMLTestListener(Writer writer, String testClassName = " + testClassName + "): called"); + + this.writer = writer; + this.testClassName = testClassName; + } + + /** + * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed. + * + * @param test The test to resest any results for. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void reset(Test test, Long threadId) + { + log.debug("public void reset(Test test = " + test + ", Long threadId = " + threadId + "): called"); + + XMLTestListener.Result r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + + r.error = null; + r.failure = null; + + } + + /** + * Notification that a test started. + * + * @param test The test that started. + */ + public void startTest(Test test) + { + log.debug("public void startTest(Test test = " + test + "): called"); + + Result newResult = new Result(test.getClass().getName(), ((TestCase) test).getName()); + + // Initialize the thread local test results. + threadLocalResults.put(Thread.currentThread().getId(), newResult); + runs++; + } + + /** + * Should be called every time a test completes with the run time of that test. + * + * @param test The name of the test. + * @param nanos The run time of the test in nanoseconds. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void timing(Test test, long nanos, Long threadId) + { } + + /** + * Should be called every time a test completed with the amount of memory used before and after the test was run. + * + * @param test The test which memory was measured for. + * @param memStart The total JVM memory used before the test was run. + * @param memEnd The total JVM memory used after the test was run. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void memoryUsed(Test test, long memStart, long memEnd, Long threadId) + { } + + /** + * Should be called every time a parameterized test completed with the int value of its test parameter. + * + * @param test The test which memory was measured for. + * @param parameter The int parameter value. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void parameterValue(Test test, int parameter, Long threadId) + { } + + /** + * Should be called every time a test completes with the current number of test threads running. + * + * @param test The test for which the measurement is being generated. + * @param threads The number of tests being run concurrently. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void concurrencyLevel(Test test, int threads, Long threadId) + { } + + /** + * Notifies listeners of the tests read/set properties. + * + * @param properties The tests read/set properties. + */ + public void properties(Properties properties) + { } + + /** + * Notification that a test ended. + * + * @param test The test that ended. + */ + public void endTest(Test test) + { + log.debug("public void endTest(Test test = " + test + "): called"); + + // Move complete test results into the completed tests list. + Result r = threadLocalResults.get(Thread.currentThread().getId()); + results.add(r); + + // Clear all the test results for the thread. + threadLocalResults.remove(Thread.currentThread().getId()); + } + + /** + * Called when a test completes. Success, failure and errors. This method should be used when registering an + * end test from a different thread than the one that started the test. + * + * @param test The test which completed. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void endTest(Test test, Long threadId) + { + log.debug("public void endTest(Test test = " + test + ", Long threadId = " + threadId + "): called"); + + // Move complete test results into the completed tests list. + Result r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + results.add(r); + + // Clear all the test results for the thread. + threadLocalResults.remove(Thread.currentThread().getId()); + } + + /** + * An error occurred. + * + * @param test The test in which the error occurred. + * @param t The throwable that resulted from the error. + */ + public void addError(Test test, Throwable t) + { + log.debug("public void addError(Test test = " + test + ", Throwable t = " + t + "): called"); + + Result r = threadLocalResults.get(Thread.currentThread().getId()); + r.error = t; + errors++; + } + + /** + * A failure occurred. + * + * @param test The test in which the failure occurred. + * @param t The JUnit assertions that led to the failure. + */ + public void addFailure(Test test, AssertionFailedError t) + { + log.debug("public void addFailure(Test test = " + test + ", AssertionFailedError t = " + t + "): called"); + + Result r = threadLocalResults.get(Thread.currentThread().getId()); + r.failure = t; + failures++; + } + + /** + * Called when a test completes to mark it as a test fail. This method should be used when registering a + * failure from a different thread than the one that started the test. + * + * @param test The test which failed. + * @param e The assertion that failed the test. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void addFailure(Test test, AssertionFailedError e, Long threadId) + { + log.debug("public void addFailure(Test test, AssertionFailedError e, Long threadId): called"); + + Result r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + r.failure = e; + failures++; + } + + /** + * Notifies listeners of the start of a complete run of tests. + */ + public void startBatch() + { + log.debug("public void startBatch(): called"); + + // Reset all results counts. + threadLocalResults = Collections.synchronizedMap(new HashMap<Long, Result>()); + errors = 0; + failures = 0; + runs = 0; + + // Write out the file header. + try + { + writer.write("<?xml version=\"1.0\" ?>\n"); + } + catch (IOException e) + { + throw new RuntimeException("Unable to write the test results.", e); + } + } + + /** + * Notifies listeners of the end of a complete run of tests. + * + * @param parameters The optional test parameters to log out with the batch results. + */ + public void endBatch(Properties parameters) + { + log.debug("public void endBatch(Properties parameters = " + parameters + "): called"); + + // Write out the results. + try + { + // writer.write("<?xml version=\"1.0\" ?>\n"); + writer.write("<testsuite errors=\"" + errors + "\" failures=\"" + failures + "\" tests=\"" + runs + "\" name=\"" + + testClassName + "\">\n"); + + for (Result result : results) + { + writer.write(" <testcase classname=\"" + result.testClass + "\" name=\"" + result.testName + "\">\n"); + + if (result.error != null) + { + writer.write(" <error type=\"" + result.error.getClass() + "\">"); + result.error.printStackTrace(new PrintWriter(writer)); + writer.write(" </error>"); + } + else if (result.failure != null) + { + writer.write(" <failure type=\"" + result.failure.getClass() + "\">"); + result.failure.printStackTrace(new PrintWriter(writer)); + writer.write(" </failure>"); + } + + writer.write(" </testcase>\n"); + } + + writer.write("</testsuite>\n"); + writer.flush(); + } + catch (IOException e) + { + throw new RuntimeException("Unable to write the test results.", e); + } + } + + /** + * Supplies the shutdown hook. + * + * @return The shut down hook. + */ + public Thread getShutdownHook() + { + return new Thread(new Runnable() + { + public void run() + { + log.debug("XMLTestListener::ShutdownHook: called"); + } + }); + } + + /** + * Used to capture the results of a particular test run. + */ + protected static class Result + { + /** Holds the name of the test class. */ + public String testClass; + + /** Holds the name of the test method. */ + public String testName; + + /** Holds the exception that caused error in this test. */ + public Throwable error; + + /** Holds the assertion exception that caused failure in this test. */ + public AssertionFailedError failure; + + /** + * Creates a placeholder for the results of a test. + * + * @param testClass The test class. + * @param testName The name of the test that was run. + */ + public Result(String testClass, String testName) + { + this.testClass = testClass; + this.testName = testName; + } + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/CommandLineParser.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/CommandLineParser.java index 61c58bf3ba..f158090e96 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/CommandLineParser.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/CommandLineParser.java @@ -1,787 +1,787 @@ -/*
- *
- * 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.junit.extensions.util;
-
-import java.text.CharacterIterator;
-import java.text.StringCharacterIterator;
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * CommandLineParser provides a utility for specifying the format of a command line and parsing command lines to ensure
- * that they fit their specified format. A command line is made up of flags and options, both may be refered to as
- * options. A flag is an option that does not take an argument (specifying it means it has the value 'true' and not
- * specifying it means it has the value 'false'). Options must take arguments but they can be set up with defaults so
- * that they take a default value when not set. Options may be mandatory in wich case it is an error not to specify
- * them on the command line. Flags are never mandatory because they are implicitly set to false when not specified.
- *
- * <p/>Some examples command line are:
- *
- * <ul>
- * <li>This one has two options that expect arguments:
- * <pre>
- * cruisecontrol -configfile cruisecontrol.xml -port 9000
- * </pre>
- * <li>This has one no-arg flag and two 'free' arguments:
- * <pre>
- * zip -r project.zip project/*
- * </pre>
- * <li>This one concatenates multiple flags into a single block with only one '-':
- * <pre>
- * jar -tvf mytar.tar
- * </pre>
- *
- * <p/>The parsing rules are:
- *
- * <ol>
- * <li>Flags may be combined after a single '-' because they never take arguments. Normally such flags are single letter
- * flags but this is only a convention and not enforced. Flags of more than one letter are usually specified on their own.
- * <li>Options expecting arguments must always be on their own.
- * <li>The argument to an option may be seperated from it by whitespace or appended directly onto the option.
- * <li>The argument to an option may never begin with a '-' character.
- * <li>All other arguments not beginning with a '-' character are free arguments that do not belong to any option.
- * <li>The second or later of a set of duplicate or repeated flags override earlier ones.
- * <li>Options are matched up to the shortest matching option. This is because of the possibility of having no space
- * between an option and its argument. This rules out the possibility of using two options where one is an opening
- * substring of the other. For example, the options "foo" and "foobar" cannot be used on the same command line because
- * it is not possible to distinguish the argument "-foobar" from being the "foobar" option or the "foo" option with
- * the "bar" argument.
- * </ol>
- *
- * <p/>By default, unknown options are simply ignored if specified on the command line. This behaviour may be changed
- * so that the parser reports all unknowns as errors by using the {@link #setErrorsOnUnknowns} method.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Accept a command line specification.
- * <tr><td> Parse a command line into properties, validating it against its specification.
- * <tr><td> Report all errors between a command line and its specification.
- * <tr><td> Provide a formatted usage string for a command line.
- * <tr><td> Provide a formatted options in force string for a command line.
- * <tr><td> Allow errors on unknowns behaviour to be turned on or off.
- * </table>
- *
- * @author Rupert Smith
- */
-public class CommandLineParser
-{
- /**
- * Holds a mapping from command line option names to detailed information about those options.
- * Use of a tree map ensures that the options are easy to print in alphabetical order as a usage string.
- * An alternative might be to use a LinkedHashMap to print them in the order they are specified.
- */
- private Map<String, CommandLineOption> optionMap = new TreeMap<String, CommandLineOption>();
-
- /** Holds a list of parsing errors. */
- private List<String> parsingErrors = new ArrayList<String>();
-
- /** Holds the regular head matcher to match command line options with. */
- private Matcher optionMatcher = null;
-
- /** Holds the parsed command line properties after parsing. */
- private Properties parsedProperties = null;
-
- /** Holds any trailing name=value pairs specified in the free arguments. */
- private Properties trailingProperties = null;
-
- /** Flag used to indicate that errors should be created for unknown options. False by default. */
- private boolean errorsOnUnknowns = false;
-
- /**
- * Creates a command line options parser from a command line specification. This is passed to this constructor
- * as an array of arrays of strings. Each array of strings specifies the command line for a single option. A static
- * array may therefore easily be used to configure the command line parser in a single method call with an easily
- * readable format.
- *
- * <p/>Each array of strings must be 2, 3, 4 or 5 elements long. If any of the last three elements are missing they
- * are assumed to be null. The elements specify the following parameters:
- * <ol>
- * <li>The name of the option without the leading '-'. For example, "file". To specify the format of the 'free'
- * arguments use the option names "1", "2", ... and so on.
- * <li>The option comment. A line of text describing the usage of the option. For example, "The file to be processed."
- * <li>The options argument. This is a very short description of the argument to the option, often a single word
- * or a reminder as to the arguments format. When this element is null the option is a flag and does not
- * accept any arguments. For example, "filename" or "(unix | windows)" or null. The actual text specified
- * is only used to print in the usage message to remind the user of the usage of the option.
- * <li>The mandatory flag. When set to "true" an option must always be specified. Any other value, including null,
- * means that the option is mandatory. Flags are always mandatory (see class javadoc for explanation of why) so
- * this is ignored for flags.
- * <li>A regular head describing the format that the argument must take. Ignored if null.
- * </ol>
- * <p/>An example call to this constructor is:
- *
- * <pre>
- * CommandLineParser commandLine = new CommandLineParser(
- * new String[][] {{"file", "The file to be processed. ", "filename", "true"},
- * {"dir", "Directory to store results in. Current dir used if not set.", "out dir"},
- * {"os", "Operating system EOL format to use.", "(windows | unix)", null, "windows\|unix"},
- * {"v", "Verbose mode. Prints information about the processing as it goes."},
- * {"1", "The processing command to run.", "command", "true", "add\|remove\|list"}});
- * </pre>
- *
- * @param config The configuration as an array of arrays of strings.
- */
- public CommandLineParser(String[][] config)
- {
- // Loop through all the command line option specifications creating details for each in the options map.
- for (String[] nextOptionSpec : config)
- {
- addOption(nextOptionSpec[0], nextOptionSpec[1], (nextOptionSpec.length > 2) ? nextOptionSpec[2] : null,
- (nextOptionSpec.length > 3) && ("true".equals(nextOptionSpec[3])),
- (nextOptionSpec.length > 4) ? nextOptionSpec[4] : null);
- }
- }
-
- /**
- * Extracts all name=value pairs from the command line, sets them all as system properties and also returns
- * a map of properties containing them.
- *
- * @param args The command line.
- * @param commandLine The command line parser.
- * @param properties The properties object to inject all parsed properties into (optional may be <tt>null</tt>).
- *
- * @return A set of properties containing all name=value pairs from the command line.
- */
- public static Properties processCommandLine(String[] args, CommandLineParser commandLine, Properties properties)
- {
- // Capture the command line arguments or display errors and correct usage and then exit.
- Properties options = null;
-
- try
- {
- options = commandLine.parseCommandLine(args);
-
- // Add all the command line options and trailing settings to properties if the optional properties object
- // to copy them into has been set.
- if (properties != null)
- {
- commandLine.addTrailingPairsToProperties(properties);
- commandLine.addOptionsToProperties(properties);
- }
- }
- catch (IllegalArgumentException e)
- {
- System.out.println(commandLine.getErrors());
- System.out.println(commandLine.getUsage());
- System.exit(1);
- }
-
- return options;
- }
-
- /**
- * Lists all the parsing errors from the most recent parsing in a string.
- *
- * @return All the parsing errors from the most recent parsing.
- */
- public String getErrors()
- {
- // Return the empty string if there are no errors.
- if (parsingErrors.isEmpty())
- {
- return "";
- }
-
- // Concatenate all the parsing errors together.
- String result = "";
-
- for (String s : parsingErrors)
- {
- result += s;
- }
-
- return result;
- }
-
- /**
- * Lists the properties set from the most recent parsing or an empty string if no parsing has been done yet.
- *
- * @return The properties set from the most recent parsing or an empty string if no parsing has been done yet.
- */
- public String getOptionsInForce()
- {
- // Check if there are no properties to report and return and empty string if so.
- if (parsedProperties == null)
- {
- return "";
- }
-
- // List all the properties.
- String result = "Options in force:\n";
-
- for (Map.Entry<Object, Object> property : parsedProperties.entrySet())
- {
- result += property.getKey() + " = " + property.getValue() + "\n";
- }
-
- return result;
- }
-
- /**
- * Generates a usage string consisting of the name of each option and each options argument description and
- * comment.
- *
- * @return A usage string for all the options.
- */
- public String getUsage()
- {
- String result = "Options:\n";
-
- int optionWidth = 0;
- int argumentWidth = 0;
-
- // Calculate the column widths required for aligned layout.
- for (CommandLineOption optionInfo : optionMap.values())
- {
- int oWidth = optionInfo.option.length();
- int aWidth = (optionInfo.argument != null) ? (optionInfo.argument.length()) : 0;
-
- optionWidth = (oWidth > optionWidth) ? oWidth : optionWidth;
- argumentWidth = (aWidth > argumentWidth) ? aWidth : argumentWidth;
- }
-
- // Print usage on each of the command line options.
- for (CommandLineOption optionInfo : optionMap.values())
- {
- String argString = ((optionInfo.argument != null) ? (optionInfo.argument) : "");
- String optionString = optionInfo.option;
-
- argString = rightPad(argString, " ", argumentWidth);
- optionString = rightPad(optionString, " ", optionWidth);
-
- result += "-" + optionString + " " + argString + " " + optionInfo.comment + "\n";
- }
-
- return result;
- }
-
- /**
- * Right pads a string with a given string to a given size. This method will repeat the padder string as many
- * times as is necessary until the exact specified size is reached. If the specified size is less than the size
- * of the original string then the original string is returned unchanged.
- *
- * <pre>
- * Example1 - original string "cat", padder string "white", size 8 gives "catwhite".
- * Example2 - original string "cat", padder string "white", size 15 gives "catwhitewhitewh".
- * Example3 - original string "cat", padder string "white", size 2 gives "cat".
- * </pre>
- *
- * @param stringToPad The original string.
- * @param padder The string to pad onto the original string.
- * @param size The required size of the new string.
- *
- * @return The newly padded string.
- */
- public static String rightPad(String stringToPad, String padder, int size)
- {
- if (padder.length() == 0)
- {
- return stringToPad;
- }
-
- StringBuffer strb = new StringBuffer(stringToPad);
- StringCharacterIterator sci = new StringCharacterIterator(padder);
-
- while (strb.length() < size)
- {
- for (char ch = sci.first(); ch != CharacterIterator.DONE; ch = sci.next())
- {
- if (strb.length() < size)
- {
- strb.append(String.valueOf(ch));
- }
- }
- }
-
- return strb.toString();
- }
-
- /**
- * Control the behaviour of the errors on unkowns reporting. When turned on this reports all unkowns options
- * as errors. When turned off, all unknowns are simply ignored.
- *
- * @param errors The setting of the errors on unkown flag. True to turn it on.
- */
- public void setErrorsOnUnknowns(boolean errors)
- {
- errorsOnUnknowns = errors;
- }
-
- /**
- * Parses a set of command line arguments into a set of properties, keyed by the argument flag. The free arguments
- * are keyed by integers as strings starting at "1" and then "2", ... and so on.
- *
- * <p/>See the class level comment for a description of the parsing rules.
- *
- * @param args The command line arguments.
- *
- * @return The arguments as a set of properties.
- *
- * @throws IllegalArgumentException If the command line cannot be parsed against its specification. If this exception
- * is thrown a call to {@link #getErrors} will provide a diagnostic of the command
- * line errors.
- */
- public Properties parseCommandLine(String[] args) throws IllegalArgumentException
- {
- Properties options = new Properties();
-
- // Used to keep count of the current 'free' argument.
- int free = 1;
-
- // Used to indicate that the most recently parsed option is expecting arguments.
- boolean expectingArgs = false;
-
- // The option that is expecting arguments from the next element of the command line.
- String optionExpectingArgs = null;
-
- // Used to indicate that the most recently parsed option is a duplicate and should be ignored.
- // boolean ignore = false;
-
- // Create the regular head matcher for the command line options.
- String regexp = "^(";
- int optionsAdded = 0;
-
- for (Iterator<String> i = optionMap.keySet().iterator(); i.hasNext();)
- {
- String nextOption = i.next();
-
- // Check that the option is not a free argument definition.
- boolean notFree = false;
-
- try
- {
- Integer.parseInt(nextOption);
- }
- catch (NumberFormatException e)
- {
- notFree = true;
- }
-
- // Add the option to the regular head matcher if it is not a free argument definition.
- if (notFree)
- {
- regexp += nextOption + (i.hasNext() ? "|" : "");
- optionsAdded++;
- }
- }
-
- // There has to be more that one option in the regular head or else the compiler complains that the close
- // cannot be nullable if the '?' token is used to make the matched option string optional.
- regexp += ")" + ((optionsAdded > 0) ? "?" : "") + "(.*)";
- Pattern pattern = Pattern.compile(regexp);
-
- // Loop through all the command line arguments.
- for (String arg1 : args)
- {
- // Check if the next command line argument begins with a '-' character and is therefore the start of
- // an option.
- if (arg1.startsWith("-"))
- {
- // Extract the value of the option without the leading '-'.
- String arg = arg1.substring(1);
-
- // Match up to the longest matching option.
- optionMatcher = pattern.matcher(arg);
- optionMatcher.matches();
-
- String matchedOption = optionMatcher.group(1);
-
- // Match any argument directly appended onto the longest matching option.
- String matchedArg = optionMatcher.group(2);
-
- // Check that a known option was matched.
- if ((matchedOption != null) && !"".equals(matchedOption))
- {
- // Get the command line option information for the matched option.
- CommandLineOption optionInfo = optionMap.get(matchedOption);
-
- // Check if this option is expecting arguments.
- if (optionInfo.expectsArgs)
- {
- // The option is expecting arguments so swallow the next command line argument as an
- // argument to this option.
- expectingArgs = true;
- optionExpectingArgs = matchedOption;
-
- // In the mean time set this options argument to the empty string in case no argument is ever
- // supplied.
- // options.put(matchedOption, "");
- }
-
- // Check if the option was matched on its own and is a flag in which case set that flag.
- if ("".equals(matchedArg) && !optionInfo.expectsArgs)
- {
- options.put(matchedOption, "true");
- }
- // The option was matched as a substring with its argument appended to it or is a flag that is
- // condensed together with other flags.
- else if (!"".equals(matchedArg))
- {
- // Check if the option is a flag and therefore is allowed to be condensed together
- // with other flags.
- if (!optionInfo.expectsArgs)
- {
- // Set the first matched flag.
- options.put(matchedOption, "true");
-
- // Repeat the longest matching process on the remainder but ensure that the remainder
- // consists only of flags as only flags may be condensed together in this fashion.
- do
- {
- // Match the remainder against the options.
- optionMatcher = pattern.matcher(matchedArg);
- optionMatcher.matches();
-
- matchedOption = optionMatcher.group(1);
- matchedArg = optionMatcher.group(2);
-
- // Check that an option was matched.
- if (matchedOption != null)
- {
- // Get the command line option information for the next matched option.
- optionInfo = optionMap.get(matchedOption);
-
- // Ensure that the next option is a flag or raise an error if not.
- if (optionInfo.expectsArgs)
- {
- parsingErrors.add("Option " + matchedOption + " cannot be combined with flags.\n");
- }
-
- options.put(matchedOption, "true");
- }
- // The remainder could not be matched against a flag it is either an unknown flag
- // or an illegal argument to a flag.
- else
- {
- parsingErrors.add("Illegal argument to a flag in the option " + arg + "\n");
-
- break;
- }
- }
- // Continue until the remainder of the argument has all been matched with flags.
- while (!"".equals(matchedArg));
- }
- // The option is expecting an argument, so store the unmatched portion against it
- // as its argument.
- else
- {
- // Check the arguments format is correct against any specified format.
- checkArgumentFormat(optionInfo, matchedArg);
-
- // Store the argument against its option (regardless of its format).
- options.put(matchedOption, matchedArg);
-
- // The argument to this flag has already been supplied to it. Do not swallow the
- // next command line argument as an argument to this flag.
- expectingArgs = false;
- }
- }
- }
- else // No matching option was found.
- {
- // Add this to the list of parsing errors if errors on unkowns is being used.
- if (errorsOnUnknowns)
- {
- parsingErrors.add("Option " + matchedOption + " is not a recognized option.\n");
- }
- }
- }
- // The command line argument did not being with a '-' so it is an argument to the previous flag or it
- // is a free argument.
- else
- {
- // Check if a previous flag is expecting to swallow this next argument as its argument.
- if (expectingArgs)
- {
- // Get the option info for the option waiting for arguments.
- CommandLineOption optionInfo = optionMap.get(optionExpectingArgs);
-
- // Check the arguments format is correct against any specified format.
- checkArgumentFormat(optionInfo, arg1);
-
- // Store the argument against its option (regardless of its format).
- options.put(optionExpectingArgs, arg1);
-
- // Clear the expecting args flag now that the argument has been swallowed.
- expectingArgs = false;
- optionExpectingArgs = null;
- }
- // This command line option is not an argument to any option. Add it to the set of 'free' options.
- else
- {
- // Get the option info for the free option, if there is any.
- CommandLineOption optionInfo = optionMap.get(Integer.toString(free));
-
- if (optionInfo != null)
- {
- // Check the arguments format is correct against any specified format.
- checkArgumentFormat(optionInfo, arg1);
- }
-
- // Add to the list of free options.
- options.put(Integer.toString(free), arg1);
-
- // Move on to the next free argument.
- free++;
- }
- }
- }
-
- // Scan through all the specified options to check that all mandatory options have been set and that all flags
- // that were not set are set to false in the set of properties.
- for (CommandLineOption optionInfo : optionMap.values())
- {
- // Check if this is a flag.
- if (!optionInfo.expectsArgs)
- {
- // Check if the flag is not set in the properties and set it to false if so.
- if (!options.containsKey(optionInfo.option))
- {
- options.put(optionInfo.option, "false");
- }
- }
- // Check if this is a mandatory option and was not set.
- else if (optionInfo.mandatory && !options.containsKey(optionInfo.option))
- {
- // Create an error for the missing option.
- parsingErrors.add("Option " + optionInfo.option + " is mandatory but not was not specified.\n");
- }
- }
-
- // Check if there were any errors.
- if (!parsingErrors.isEmpty())
- {
- // Throw an illegal argument exception to signify that there were parsing errors.
- throw new IllegalArgumentException();
- }
-
- // Convert any name/value pairs in the free arguments into properties in the parsed options.
- trailingProperties = takeFreeArgsAsProperties(options, 1);
-
- parsedProperties = options;
-
- return options;
- }
-
- /**
- * If a command line has been parsed, calling this method sets all of its free arguments that were name=value pairs
- * on the specified properties.
- *
- * @param properties The property set to add the name=value pairs to.
- */
- public void addTrailingPairsToProperties(Properties properties)
- {
- if (trailingProperties != null)
- {
- for (Object propKey : trailingProperties.keySet())
- {
- String name = (String) propKey;
- String value = trailingProperties.getProperty(name);
-
- properties.setProperty(name, value);
- }
- }
- }
-
- /**
- * If a command line has been parsed, calling this method sets all of its options that were set to the specified
- * properties.
- *
- * @param properties The property set to the options to.
- */
- public void addOptionsToProperties(Properties properties)
- {
- if (parsedProperties != null)
- {
- for (Object propKey : parsedProperties.keySet())
- {
- String name = (String) propKey;
- String value = parsedProperties.getProperty(name);
-
- // This filters out all trailing items.
- if (!name.matches("^[0-9]+$"))
- {
- properties.setProperty(name, value);
- }
- }
- }
- }
-
- /**
- * Resets this command line parser after it has been used to parse a command line. This method will only need
- * to be called to use this parser a second time which is not likely seeing as a command line is usually only
- * specified once. However, it is exposed as a public method for the rare case where this may be done.
- *
- * <p/>Cleans the internal state of this parser, removing all stored errors and information about the options in
- * force.
- */
- public void reset()
- {
- parsingErrors = new ArrayList<String>();
- parsedProperties = null;
- }
-
- /**
- * Adds the option to list of available command line options.
- *
- * @param option The option to add as an available command line option.
- * @param comment A comment for the option.
- * @param argument The text that appears after the option in the usage string.
- * @param mandatory When true, indicates that this option is mandatory.
- * @param formatRegexp The format that the argument must take, defined as a regular head.
- */
- protected void addOption(String option, String comment, String argument, boolean mandatory, String formatRegexp)
- {
- // Check if usage text has been set in which case this option is expecting arguments.
- boolean expectsArgs = (!((argument == null) || argument.equals("")));
-
- // Add the option to the map of command line options.
- CommandLineOption opt = new CommandLineOption(option, expectsArgs, comment, argument, mandatory, formatRegexp);
- optionMap.put(option, opt);
- }
-
- /**
- * Converts the free arguments into property declarations. After parsing the command line the free arguments
- * are numbered from 1, such that the parsed properties contain values for the keys "1", "2", ... This method
- * converts any free arguments declared using the 'name=value' syntax into properties with key 'name', value
- * 'value'.
- *
- * <p/>For example the comand line:
- * <pre>
- * ... debug=true
- * </pre>
- *
- * <p/>After parsing has properties:
- * <pre>[[1, debug=true]]</pre>
- *
- * <p/>After applying this method the properties are:
- * <pre>[[1, debug=true], [debug, true]]</pre>
- *
- * @param properties The parsed command line properties.
- * @param from The free argument index to convert to properties from.
- *
- * @return The parsed command line properties, with free argument name value pairs too.
- */
- private Properties takeFreeArgsAsProperties(Properties properties, int from)
- {
- Properties result = new Properties();
-
- for (int i = from; true; i++)
- {
- String nextFreeArg = properties.getProperty(Integer.toString(i));
-
- // Terminate the loop once all free arguments have been consumed.
- if (nextFreeArg == null)
- {
- break;
- }
-
- // Split it on the =, strip any whitespace and set it as a system property.
- String[] nameValuePair = nextFreeArg.split("=");
-
- if (nameValuePair.length == 2)
- {
- result.setProperty(nameValuePair[0], nameValuePair[1]);
- }
- }
-
- return result;
- }
-
- /**
- * Checks the format of an argument to an option against its specified regular head format if one has
- * been set. Any errors are added to the list of parsing errors.
- *
- * @param optionInfo The command line option information for the option which is havings its argument checked.
- * @param matchedArg The string argument to the option.
- */
- private void checkArgumentFormat(CommandLineOption optionInfo, String matchedArg)
- {
- // Check if this option enforces a format for its argument.
- if (optionInfo.argumentFormatRegexp != null)
- {
- Pattern pattern = Pattern.compile(optionInfo.argumentFormatRegexp);
- Matcher argumentMatcher = pattern.matcher(matchedArg);
-
- // Check if the argument does not meet its required format.
- if (!argumentMatcher.matches())
- {
- // Create an error for this badly formed argument.
- parsingErrors.add("The argument to option " + optionInfo.option + " does not meet its required format.\n");
- }
- }
- }
-
- /**
- * Holds information about a command line options. This includes what its name is, whether or not it is a flag,
- * whether or not it is mandatory, what its user comment is, what its argument reminder text is and what its
- * regular head format is.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Hold details of a command line option.
- * </table>
- *
- * @author Rupert Smith
- */
- protected class CommandLineOption
- {
- /** Holds the text for the flag to match this argument with. */
- public String option = null;
-
- /** Holds a string describing how to use this command line argument. */
- public String argument = null;
-
- /** Flag that determines whether or not this command line argument can take arguments. */
- public boolean expectsArgs = false;
-
- /** Holds a short comment describing what this command line argument is for. */
- public String comment = null;
-
- /** Flag that determines whether or not this is an mandatory command line argument. */
- public boolean mandatory = false;
-
- /** A regular head describing what format the argument to this option muist have. */
- public String argumentFormatRegexp = null;
-
- /**
- * Create a command line option object that holds specific information about a command line option.
- *
- * @param option The text that matches the option.
- * @param expectsArgs Whether or not the option expects arguments. It is a flag if this is false.
- * @param comment A comment explaining how to use this option.
- * @param argument A short reminder of the format of the argument to this option/
- * @param mandatory Set to true if this option is mandatory.
- * @param formatRegexp The regular head that the argument to this option must meet to be valid.
- */
- public CommandLineOption(String option, boolean expectsArgs, String comment, String argument, boolean mandatory,
- String formatRegexp)
- {
- this.option = option;
- this.expectsArgs = expectsArgs;
- this.comment = comment;
- this.argument = argument;
- this.mandatory = mandatory;
- this.argumentFormatRegexp = formatRegexp;
- }
- }
-}
+/* + * + * 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.junit.extensions.util; + +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * CommandLineParser provides a utility for specifying the format of a command line and parsing command lines to ensure + * that they fit their specified format. A command line is made up of flags and options, both may be refered to as + * options. A flag is an option that does not take an argument (specifying it means it has the value 'true' and not + * specifying it means it has the value 'false'). Options must take arguments but they can be set up with defaults so + * that they take a default value when not set. Options may be mandatory in wich case it is an error not to specify + * them on the command line. Flags are never mandatory because they are implicitly set to false when not specified. + * + * <p/>Some examples command line are: + * + * <ul> + * <li>This one has two options that expect arguments: + * <pre> + * cruisecontrol -configfile cruisecontrol.xml -port 9000 + * </pre> + * <li>This has one no-arg flag and two 'free' arguments: + * <pre> + * zip -r project.zip project/* + * </pre> + * <li>This one concatenates multiple flags into a single block with only one '-': + * <pre> + * jar -tvf mytar.tar + * </pre> + * + * <p/>The parsing rules are: + * + * <ol> + * <li>Flags may be combined after a single '-' because they never take arguments. Normally such flags are single letter + * flags but this is only a convention and not enforced. Flags of more than one letter are usually specified on their own. + * <li>Options expecting arguments must always be on their own. + * <li>The argument to an option may be seperated from it by whitespace or appended directly onto the option. + * <li>The argument to an option may never begin with a '-' character. + * <li>All other arguments not beginning with a '-' character are free arguments that do not belong to any option. + * <li>The second or later of a set of duplicate or repeated flags override earlier ones. + * <li>Options are matched up to the shortest matching option. This is because of the possibility of having no space + * between an option and its argument. This rules out the possibility of using two options where one is an opening + * substring of the other. For example, the options "foo" and "foobar" cannot be used on the same command line because + * it is not possible to distinguish the argument "-foobar" from being the "foobar" option or the "foo" option with + * the "bar" argument. + * </ol> + * + * <p/>By default, unknown options are simply ignored if specified on the command line. This behaviour may be changed + * so that the parser reports all unknowns as errors by using the {@link #setErrorsOnUnknowns} method. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Accept a command line specification. + * <tr><td> Parse a command line into properties, validating it against its specification. + * <tr><td> Report all errors between a command line and its specification. + * <tr><td> Provide a formatted usage string for a command line. + * <tr><td> Provide a formatted options in force string for a command line. + * <tr><td> Allow errors on unknowns behaviour to be turned on or off. + * </table> + * + * @author Rupert Smith + */ +public class CommandLineParser +{ + /** + * Holds a mapping from command line option names to detailed information about those options. + * Use of a tree map ensures that the options are easy to print in alphabetical order as a usage string. + * An alternative might be to use a LinkedHashMap to print them in the order they are specified. + */ + private Map<String, CommandLineOption> optionMap = new TreeMap<String, CommandLineOption>(); + + /** Holds a list of parsing errors. */ + private List<String> parsingErrors = new ArrayList<String>(); + + /** Holds the regular head matcher to match command line options with. */ + private Matcher optionMatcher = null; + + /** Holds the parsed command line properties after parsing. */ + private Properties parsedProperties = null; + + /** Holds any trailing name=value pairs specified in the free arguments. */ + private Properties trailingProperties = null; + + /** Flag used to indicate that errors should be created for unknown options. False by default. */ + private boolean errorsOnUnknowns = false; + + /** + * Creates a command line options parser from a command line specification. This is passed to this constructor + * as an array of arrays of strings. Each array of strings specifies the command line for a single option. A static + * array may therefore easily be used to configure the command line parser in a single method call with an easily + * readable format. + * + * <p/>Each array of strings must be 2, 3, 4 or 5 elements long. If any of the last three elements are missing they + * are assumed to be null. The elements specify the following parameters: + * <ol> + * <li>The name of the option without the leading '-'. For example, "file". To specify the format of the 'free' + * arguments use the option names "1", "2", ... and so on. + * <li>The option comment. A line of text describing the usage of the option. For example, "The file to be processed." + * <li>The options argument. This is a very short description of the argument to the option, often a single word + * or a reminder as to the arguments format. When this element is null the option is a flag and does not + * accept any arguments. For example, "filename" or "(unix | windows)" or null. The actual text specified + * is only used to print in the usage message to remind the user of the usage of the option. + * <li>The mandatory flag. When set to "true" an option must always be specified. Any other value, including null, + * means that the option is mandatory. Flags are always mandatory (see class javadoc for explanation of why) so + * this is ignored for flags. + * <li>A regular head describing the format that the argument must take. Ignored if null. + * </ol> + * <p/>An example call to this constructor is: + * + * <pre> + * CommandLineParser commandLine = new CommandLineParser( + * new String[][] {{"file", "The file to be processed. ", "filename", "true"}, + * {"dir", "Directory to store results in. Current dir used if not set.", "out dir"}, + * {"os", "Operating system EOL format to use.", "(windows | unix)", null, "windows\|unix"}, + * {"v", "Verbose mode. Prints information about the processing as it goes."}, + * {"1", "The processing command to run.", "command", "true", "add\|remove\|list"}}); + * </pre> + * + * @param config The configuration as an array of arrays of strings. + */ + public CommandLineParser(String[][] config) + { + // Loop through all the command line option specifications creating details for each in the options map. + for (String[] nextOptionSpec : config) + { + addOption(nextOptionSpec[0], nextOptionSpec[1], (nextOptionSpec.length > 2) ? nextOptionSpec[2] : null, + (nextOptionSpec.length > 3) && ("true".equals(nextOptionSpec[3])), + (nextOptionSpec.length > 4) ? nextOptionSpec[4] : null); + } + } + + /** + * Extracts all name=value pairs from the command line, sets them all as system properties and also returns + * a map of properties containing them. + * + * @param args The command line. + * @param commandLine The command line parser. + * @param properties The properties object to inject all parsed properties into (optional may be <tt>null</tt>). + * + * @return A set of properties containing all name=value pairs from the command line. + */ + public static Properties processCommandLine(String[] args, CommandLineParser commandLine, Properties properties) + { + // Capture the command line arguments or display errors and correct usage and then exit. + Properties options = null; + + try + { + options = commandLine.parseCommandLine(args); + + // Add all the command line options and trailing settings to properties if the optional properties object + // to copy them into has been set. + if (properties != null) + { + commandLine.addTrailingPairsToProperties(properties); + commandLine.addOptionsToProperties(properties); + } + } + catch (IllegalArgumentException e) + { + System.out.println(commandLine.getErrors()); + System.out.println(commandLine.getUsage()); + System.exit(1); + } + + return options; + } + + /** + * Lists all the parsing errors from the most recent parsing in a string. + * + * @return All the parsing errors from the most recent parsing. + */ + public String getErrors() + { + // Return the empty string if there are no errors. + if (parsingErrors.isEmpty()) + { + return ""; + } + + // Concatenate all the parsing errors together. + String result = ""; + + for (String s : parsingErrors) + { + result += s; + } + + return result; + } + + /** + * Lists the properties set from the most recent parsing or an empty string if no parsing has been done yet. + * + * @return The properties set from the most recent parsing or an empty string if no parsing has been done yet. + */ + public String getOptionsInForce() + { + // Check if there are no properties to report and return and empty string if so. + if (parsedProperties == null) + { + return ""; + } + + // List all the properties. + String result = "Options in force:\n"; + + for (Map.Entry<Object, Object> property : parsedProperties.entrySet()) + { + result += property.getKey() + " = " + property.getValue() + "\n"; + } + + return result; + } + + /** + * Generates a usage string consisting of the name of each option and each options argument description and + * comment. + * + * @return A usage string for all the options. + */ + public String getUsage() + { + String result = "Options:\n"; + + int optionWidth = 0; + int argumentWidth = 0; + + // Calculate the column widths required for aligned layout. + for (CommandLineOption optionInfo : optionMap.values()) + { + int oWidth = optionInfo.option.length(); + int aWidth = (optionInfo.argument != null) ? (optionInfo.argument.length()) : 0; + + optionWidth = (oWidth > optionWidth) ? oWidth : optionWidth; + argumentWidth = (aWidth > argumentWidth) ? aWidth : argumentWidth; + } + + // Print usage on each of the command line options. + for (CommandLineOption optionInfo : optionMap.values()) + { + String argString = ((optionInfo.argument != null) ? (optionInfo.argument) : ""); + String optionString = optionInfo.option; + + argString = rightPad(argString, " ", argumentWidth); + optionString = rightPad(optionString, " ", optionWidth); + + result += "-" + optionString + " " + argString + " " + optionInfo.comment + "\n"; + } + + return result; + } + + /** + * Right pads a string with a given string to a given size. This method will repeat the padder string as many + * times as is necessary until the exact specified size is reached. If the specified size is less than the size + * of the original string then the original string is returned unchanged. + * + * <pre> + * Example1 - original string "cat", padder string "white", size 8 gives "catwhite". + * Example2 - original string "cat", padder string "white", size 15 gives "catwhitewhitewh". + * Example3 - original string "cat", padder string "white", size 2 gives "cat". + * </pre> + * + * @param stringToPad The original string. + * @param padder The string to pad onto the original string. + * @param size The required size of the new string. + * + * @return The newly padded string. + */ + public static String rightPad(String stringToPad, String padder, int size) + { + if (padder.length() == 0) + { + return stringToPad; + } + + StringBuffer strb = new StringBuffer(stringToPad); + StringCharacterIterator sci = new StringCharacterIterator(padder); + + while (strb.length() < size) + { + for (char ch = sci.first(); ch != CharacterIterator.DONE; ch = sci.next()) + { + if (strb.length() < size) + { + strb.append(String.valueOf(ch)); + } + } + } + + return strb.toString(); + } + + /** + * Control the behaviour of the errors on unkowns reporting. When turned on this reports all unkowns options + * as errors. When turned off, all unknowns are simply ignored. + * + * @param errors The setting of the errors on unkown flag. True to turn it on. + */ + public void setErrorsOnUnknowns(boolean errors) + { + errorsOnUnknowns = errors; + } + + /** + * Parses a set of command line arguments into a set of properties, keyed by the argument flag. The free arguments + * are keyed by integers as strings starting at "1" and then "2", ... and so on. + * + * <p/>See the class level comment for a description of the parsing rules. + * + * @param args The command line arguments. + * + * @return The arguments as a set of properties. + * + * @throws IllegalArgumentException If the command line cannot be parsed against its specification. If this exception + * is thrown a call to {@link #getErrors} will provide a diagnostic of the command + * line errors. + */ + public Properties parseCommandLine(String[] args) throws IllegalArgumentException + { + Properties options = new Properties(); + + // Used to keep count of the current 'free' argument. + int free = 1; + + // Used to indicate that the most recently parsed option is expecting arguments. + boolean expectingArgs = false; + + // The option that is expecting arguments from the next element of the command line. + String optionExpectingArgs = null; + + // Used to indicate that the most recently parsed option is a duplicate and should be ignored. + // boolean ignore = false; + + // Create the regular head matcher for the command line options. + String regexp = "^("; + int optionsAdded = 0; + + for (Iterator<String> i = optionMap.keySet().iterator(); i.hasNext();) + { + String nextOption = i.next(); + + // Check that the option is not a free argument definition. + boolean notFree = false; + + try + { + Integer.parseInt(nextOption); + } + catch (NumberFormatException e) + { + notFree = true; + } + + // Add the option to the regular head matcher if it is not a free argument definition. + if (notFree) + { + regexp += nextOption + (i.hasNext() ? "|" : ""); + optionsAdded++; + } + } + + // There has to be more that one option in the regular head or else the compiler complains that the close + // cannot be nullable if the '?' token is used to make the matched option string optional. + regexp += ")" + ((optionsAdded > 0) ? "?" : "") + "(.*)"; + Pattern pattern = Pattern.compile(regexp); + + // Loop through all the command line arguments. + for (String arg1 : args) + { + // Check if the next command line argument begins with a '-' character and is therefore the start of + // an option. + if (arg1.startsWith("-")) + { + // Extract the value of the option without the leading '-'. + String arg = arg1.substring(1); + + // Match up to the longest matching option. + optionMatcher = pattern.matcher(arg); + optionMatcher.matches(); + + String matchedOption = optionMatcher.group(1); + + // Match any argument directly appended onto the longest matching option. + String matchedArg = optionMatcher.group(2); + + // Check that a known option was matched. + if ((matchedOption != null) && !"".equals(matchedOption)) + { + // Get the command line option information for the matched option. + CommandLineOption optionInfo = optionMap.get(matchedOption); + + // Check if this option is expecting arguments. + if (optionInfo.expectsArgs) + { + // The option is expecting arguments so swallow the next command line argument as an + // argument to this option. + expectingArgs = true; + optionExpectingArgs = matchedOption; + + // In the mean time set this options argument to the empty string in case no argument is ever + // supplied. + // options.put(matchedOption, ""); + } + + // Check if the option was matched on its own and is a flag in which case set that flag. + if ("".equals(matchedArg) && !optionInfo.expectsArgs) + { + options.put(matchedOption, "true"); + } + // The option was matched as a substring with its argument appended to it or is a flag that is + // condensed together with other flags. + else if (!"".equals(matchedArg)) + { + // Check if the option is a flag and therefore is allowed to be condensed together + // with other flags. + if (!optionInfo.expectsArgs) + { + // Set the first matched flag. + options.put(matchedOption, "true"); + + // Repeat the longest matching process on the remainder but ensure that the remainder + // consists only of flags as only flags may be condensed together in this fashion. + do + { + // Match the remainder against the options. + optionMatcher = pattern.matcher(matchedArg); + optionMatcher.matches(); + + matchedOption = optionMatcher.group(1); + matchedArg = optionMatcher.group(2); + + // Check that an option was matched. + if (matchedOption != null) + { + // Get the command line option information for the next matched option. + optionInfo = optionMap.get(matchedOption); + + // Ensure that the next option is a flag or raise an error if not. + if (optionInfo.expectsArgs) + { + parsingErrors.add("Option " + matchedOption + " cannot be combined with flags.\n"); + } + + options.put(matchedOption, "true"); + } + // The remainder could not be matched against a flag it is either an unknown flag + // or an illegal argument to a flag. + else + { + parsingErrors.add("Illegal argument to a flag in the option " + arg + "\n"); + + break; + } + } + // Continue until the remainder of the argument has all been matched with flags. + while (!"".equals(matchedArg)); + } + // The option is expecting an argument, so store the unmatched portion against it + // as its argument. + else + { + // Check the arguments format is correct against any specified format. + checkArgumentFormat(optionInfo, matchedArg); + + // Store the argument against its option (regardless of its format). + options.put(matchedOption, matchedArg); + + // The argument to this flag has already been supplied to it. Do not swallow the + // next command line argument as an argument to this flag. + expectingArgs = false; + } + } + } + else // No matching option was found. + { + // Add this to the list of parsing errors if errors on unkowns is being used. + if (errorsOnUnknowns) + { + parsingErrors.add("Option " + matchedOption + " is not a recognized option.\n"); + } + } + } + // The command line argument did not being with a '-' so it is an argument to the previous flag or it + // is a free argument. + else + { + // Check if a previous flag is expecting to swallow this next argument as its argument. + if (expectingArgs) + { + // Get the option info for the option waiting for arguments. + CommandLineOption optionInfo = optionMap.get(optionExpectingArgs); + + // Check the arguments format is correct against any specified format. + checkArgumentFormat(optionInfo, arg1); + + // Store the argument against its option (regardless of its format). + options.put(optionExpectingArgs, arg1); + + // Clear the expecting args flag now that the argument has been swallowed. + expectingArgs = false; + optionExpectingArgs = null; + } + // This command line option is not an argument to any option. Add it to the set of 'free' options. + else + { + // Get the option info for the free option, if there is any. + CommandLineOption optionInfo = optionMap.get(Integer.toString(free)); + + if (optionInfo != null) + { + // Check the arguments format is correct against any specified format. + checkArgumentFormat(optionInfo, arg1); + } + + // Add to the list of free options. + options.put(Integer.toString(free), arg1); + + // Move on to the next free argument. + free++; + } + } + } + + // Scan through all the specified options to check that all mandatory options have been set and that all flags + // that were not set are set to false in the set of properties. + for (CommandLineOption optionInfo : optionMap.values()) + { + // Check if this is a flag. + if (!optionInfo.expectsArgs) + { + // Check if the flag is not set in the properties and set it to false if so. + if (!options.containsKey(optionInfo.option)) + { + options.put(optionInfo.option, "false"); + } + } + // Check if this is a mandatory option and was not set. + else if (optionInfo.mandatory && !options.containsKey(optionInfo.option)) + { + // Create an error for the missing option. + parsingErrors.add("Option " + optionInfo.option + " is mandatory but not was not specified.\n"); + } + } + + // Check if there were any errors. + if (!parsingErrors.isEmpty()) + { + // Throw an illegal argument exception to signify that there were parsing errors. + throw new IllegalArgumentException(); + } + + // Convert any name/value pairs in the free arguments into properties in the parsed options. + trailingProperties = takeFreeArgsAsProperties(options, 1); + + parsedProperties = options; + + return options; + } + + /** + * If a command line has been parsed, calling this method sets all of its free arguments that were name=value pairs + * on the specified properties. + * + * @param properties The property set to add the name=value pairs to. + */ + public void addTrailingPairsToProperties(Properties properties) + { + if (trailingProperties != null) + { + for (Object propKey : trailingProperties.keySet()) + { + String name = (String) propKey; + String value = trailingProperties.getProperty(name); + + properties.setProperty(name, value); + } + } + } + + /** + * If a command line has been parsed, calling this method sets all of its options that were set to the specified + * properties. + * + * @param properties The property set to the options to. + */ + public void addOptionsToProperties(Properties properties) + { + if (parsedProperties != null) + { + for (Object propKey : parsedProperties.keySet()) + { + String name = (String) propKey; + String value = parsedProperties.getProperty(name); + + // This filters out all trailing items. + if (!name.matches("^[0-9]+$")) + { + properties.setProperty(name, value); + } + } + } + } + + /** + * Resets this command line parser after it has been used to parse a command line. This method will only need + * to be called to use this parser a second time which is not likely seeing as a command line is usually only + * specified once. However, it is exposed as a public method for the rare case where this may be done. + * + * <p/>Cleans the internal state of this parser, removing all stored errors and information about the options in + * force. + */ + public void reset() + { + parsingErrors = new ArrayList<String>(); + parsedProperties = null; + } + + /** + * Adds the option to list of available command line options. + * + * @param option The option to add as an available command line option. + * @param comment A comment for the option. + * @param argument The text that appears after the option in the usage string. + * @param mandatory When true, indicates that this option is mandatory. + * @param formatRegexp The format that the argument must take, defined as a regular head. + */ + protected void addOption(String option, String comment, String argument, boolean mandatory, String formatRegexp) + { + // Check if usage text has been set in which case this option is expecting arguments. + boolean expectsArgs = (!((argument == null) || argument.equals(""))); + + // Add the option to the map of command line options. + CommandLineOption opt = new CommandLineOption(option, expectsArgs, comment, argument, mandatory, formatRegexp); + optionMap.put(option, opt); + } + + /** + * Converts the free arguments into property declarations. After parsing the command line the free arguments + * are numbered from 1, such that the parsed properties contain values for the keys "1", "2", ... This method + * converts any free arguments declared using the 'name=value' syntax into properties with key 'name', value + * 'value'. + * + * <p/>For example the comand line: + * <pre> + * ... debug=true + * </pre> + * + * <p/>After parsing has properties: + * <pre>[[1, debug=true]]</pre> + * + * <p/>After applying this method the properties are: + * <pre>[[1, debug=true], [debug, true]]</pre> + * + * @param properties The parsed command line properties. + * @param from The free argument index to convert to properties from. + * + * @return The parsed command line properties, with free argument name value pairs too. + */ + private Properties takeFreeArgsAsProperties(Properties properties, int from) + { + Properties result = new Properties(); + + for (int i = from; true; i++) + { + String nextFreeArg = properties.getProperty(Integer.toString(i)); + + // Terminate the loop once all free arguments have been consumed. + if (nextFreeArg == null) + { + break; + } + + // Split it on the =, strip any whitespace and set it as a system property. + String[] nameValuePair = nextFreeArg.split("="); + + if (nameValuePair.length == 2) + { + result.setProperty(nameValuePair[0], nameValuePair[1]); + } + } + + return result; + } + + /** + * Checks the format of an argument to an option against its specified regular head format if one has + * been set. Any errors are added to the list of parsing errors. + * + * @param optionInfo The command line option information for the option which is havings its argument checked. + * @param matchedArg The string argument to the option. + */ + private void checkArgumentFormat(CommandLineOption optionInfo, String matchedArg) + { + // Check if this option enforces a format for its argument. + if (optionInfo.argumentFormatRegexp != null) + { + Pattern pattern = Pattern.compile(optionInfo.argumentFormatRegexp); + Matcher argumentMatcher = pattern.matcher(matchedArg); + + // Check if the argument does not meet its required format. + if (!argumentMatcher.matches()) + { + // Create an error for this badly formed argument. + parsingErrors.add("The argument to option " + optionInfo.option + " does not meet its required format.\n"); + } + } + } + + /** + * Holds information about a command line options. This includes what its name is, whether or not it is a flag, + * whether or not it is mandatory, what its user comment is, what its argument reminder text is and what its + * regular head format is. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Hold details of a command line option. + * </table> + * + * @author Rupert Smith + */ + protected class CommandLineOption + { + /** Holds the text for the flag to match this argument with. */ + public String option = null; + + /** Holds a string describing how to use this command line argument. */ + public String argument = null; + + /** Flag that determines whether or not this command line argument can take arguments. */ + public boolean expectsArgs = false; + + /** Holds a short comment describing what this command line argument is for. */ + public String comment = null; + + /** Flag that determines whether or not this is an mandatory command line argument. */ + public boolean mandatory = false; + + /** A regular head describing what format the argument to this option muist have. */ + public String argumentFormatRegexp = null; + + /** + * Create a command line option object that holds specific information about a command line option. + * + * @param option The text that matches the option. + * @param expectsArgs Whether or not the option expects arguments. It is a flag if this is false. + * @param comment A comment explaining how to use this option. + * @param argument A short reminder of the format of the argument to this option/ + * @param mandatory Set to true if this option is mandatory. + * @param formatRegexp The regular head that the argument to this option must meet to be valid. + */ + public CommandLineOption(String option, boolean expectsArgs, String comment, String argument, boolean mandatory, + String formatRegexp) + { + this.option = option; + this.expectsArgs = expectsArgs; + this.comment = comment; + this.argument = argument; + this.mandatory = mandatory; + this.argumentFormatRegexp = formatRegexp; + } + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ContextualProperties.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ContextualProperties.java index cabbf7869a..14de96d165 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ContextualProperties.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ContextualProperties.java @@ -1,494 +1,494 @@ -/*
- *
- * 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.junit.extensions.util;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Properties;
-
-/**
- * ContextualProperties is an extension of {@link java.util.Properties} that automatically selects properties based on an
- * environment parameter (defined by the system property {@link #ENV_SYS_PROPERTY}), the name of a class, plus a modifier
- * (which can be used to name a method of a class) and a property key. It also supports the definition of arrays of
- * property values using indexes. The properties are searched in the following order until a match is found:
- *
- * <ol>
- * <li>environment + class name with package name + modifier + key
- * <li>environment + class name with package name + key
- * <li>environment + key
- * <li>class name with package name + modifier + key
- * <li>class name with package name + key
- * <li>key
- * </ol>
- *
- * <p>To create arrays of property values add index numbers onto the end of the property keys. An array of string values
- * will be created with the elements of the array set to the value of the property at the matching index. Ideally the
- * index values will be contiguous, starting at 0. This does not need to be the case however. If an array definition
- * begins at index n, and ends at index m, Then an array big enough to hold m + 1 elements will be created and populated
- * with values from n to m. Values before n and any missing values between n and m will be null in the array.
- *
- * <p>To give an example, suppose you have two different environments 'DEVELOPMENT' and 'PRODUCTION' and they each need
- * the same properties but set to different values for each environment and some properties the same in both, you could
- * create a properties file like:
- *
- * <p><code>
- * # Project configuration properties file.<br/>
- * <br/>
- * # These properties are environment specific.<br/>
- * DEVELOPMENT.debug=true<br/>
- * PRODUCTION.debug=false<br/>
- * <br/>
- * # Always debug MyClass in all environments but not the myMethod method.<br/>
- * MyClass.debug=true<br/>
- * MyClass.myMethod.debug=false<br/>
- * <br/>
- * # Set up an array of my ten favourite animals. Leave elements 3 to 8 as null as I haven't decided on them yet.<br/>
- * animals.0=cat<br/>
- * animals.1=dog<br/>
- * animals.2=elephant<br/>
- * animals.9=lion<br/>
- * <br/>
- * # This is a default value that will be used when the environment is not known.<br/>
- * debug=false<br/>
- * </code>
- *
- * <p>The most specific definition of a property is searched for first moving out to the most general. This allows
- * general property defaults to be set and then overiden for specific uses by some classes and modifiers.
- *
- * <p>A ContextualProperties object can be loaded in the same way as a java.utils.Properties. A recommended way to do
- * this that does not assume that the properties file is a file (it could be in a jar) is to load the properties from the
- * url for the resource lookup up on the classpath:
- *
- * <p><code>
- * Properties configProperties = new ContextualProperties();<br/>
- * configProperties.load(this.getClass().getClassLoader().getResourceAsStream("config.properties"));<br/>
- * </code>
- *
- * <p>EnvironmentProperties will load the 'DEVELOPMENT.debug' property or 'PROUCTION.debug' property based on the setting
- * of the system environment property. If a matching property for the environment cannot be found then the simple property
- * name without the environment pre-pended onto it will be used instead. This 'use of default environments' behaviour is
- * turned on initially but it can be disabled by calling the {@link #useDefaultEnvironments} method.
- *
- * <p>When a property matching a key cannot be found then the property accessor methods will always return null. If a
- * default value for a property exists but the 'use of default environments' behavious prevents it being used then the
- * accessor methods will return null.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Automatically select properties dependant on environment, class name and modifier as well as property key.
- * <tr><td> Convert indexed properties into arrays.
- * </table>
- *
- * @author Rupert Smith
- */
-public class ContextualProperties extends ParsedProperties
-{
- /** The name of the system property that is used to define the environment. */
- public static final String ENV_SYS_PROPERTY = "environment";
-
- /**
- * <p>Holds the iteration count down order.
- *
- * <p>If e = 4, b = 2, m = 1 then the iteration order or i is 7,6,4 and then if using environment defaults 3,2,0
- * where the accessor key is:
- * (i & e != 0 ? environment : "") + (i & b != 0 ? base : "") + (1 + m != 0 ? modifier : "") + key
- *
- * <p>In other words the presence or otherwise of the three least significant bits when counting down from 7
- * specifies which of the environment, base and modifier are to be included in the key where the environment, base
- * and modifier stand for the bits in positions 2, 1 and 0. The numbers 5 and 1 are missed out of the count because
- * they stand for the case where the modifier is used without the base which is not done.
- */
- private static final int[] ORDER = new int[] { 7, 6, 4, 3, 2, 0 };
-
- /**
- * Defines the point in the iteration count order below which the 'use environment defaults' feature is being used.
- */
- private static final int ENVIRONMENT_DEFAULTS_CUTOFF = 4;
-
- /** Defines the bit representation for the environment in the key ordering. See {@link #ORDER}. */
- private static final int E = 4;
-
- /** Defines the bit representation for the base in the key ordering. See {@link #ORDER}. */
- private static final int B = 2;
-
- /** Defines the bit representation for the modifier in the key ordering. See {@link #ORDER}. */
- private static final int M = 1;
-
- /** Used to hold the value of the environment system property. */
- private String environment;
-
- /** Used to indicate that the 'use of defaults' behaviour should be used. */
- private boolean useDefaults = true;
-
- /** Used to hold all the array properties. This is a mapping from property names to ArrayLists of Strings. */
- protected Map arrayProperties = new HashMap();
-
- /**
- * Default constructor that builds a ContextualProperties that uses environment defaults.
- */
- public ContextualProperties()
- {
- super();
-
- // Keep the value of the system environment property.
- environment = System.getProperty(ENV_SYS_PROPERTY);
- }
-
- /**
- * Creates a ContextualProperties that uses environment defaults and is initialized with the specified properties.
- *
- * @param props The properties to initialize this with.
- */
- public ContextualProperties(Properties props)
- {
- super(props);
-
- // Keep the value of the system environment property.
- environment = System.getProperty(ENV_SYS_PROPERTY);
-
- // Extract any array properties as arrays.
- createArrayProperties();
- }
-
- /**
- * Parses an input stream as properties.
- *
- * @param inStream The input stream to read the properties from.
- *
- * @exception IOException If there is an IO error during reading from the input stream.
- */
- public void load(InputStream inStream) throws IOException
- {
- super.load(inStream);
-
- // Extract any array properties as arrays.
- createArrayProperties();
- }
-
- /**
- * Tells this environment aware properties object whether it should use default environment properties without a
- * pre-pended environment when a property for the current environment cannot be found.
- *
- * @param flag True to use defaults, false to not use defaults.
- */
- public void useDefaultEnvironments(boolean flag)
- {
- useDefaults = flag;
- }
-
- /**
- * Looks up a property value relative to the environment, callers class and method. The default environment will be
- * checked for a matching property if defaults are being used. In order to work out the callers class and method this
- * method throws an exception and then searches one level up its stack frames.
- *
- * @param key The property key.
- *
- * @return The value of this property searching from the most specific definition (environment, class, method, key)
- * to the most general (key only), unless use of default environments is turned off in which case the most general
- * proeprty searched is (environment, key).
- */
- public String getProperty(String key)
- {
- // Try to get the callers class name and method name by examing the stack.
- String className = null;
- String methodName = null;
-
- // Java 1.4 onwards only.
- /*try
- {
- throw new Exception();
- }
- catch (Exception e)
- {
- StackTraceElement[] stack = e.getStackTrace();
-
- // Check that the stack trace contains at least two elements, one for this method and one for the caller.
- if (stack.length >= 2)
- {
- className = stack[1].getClassName();
- methodName = stack[1].getMethodName();
- }
- }*/
-
- // Java 1.5 onwards only.
- StackTraceElement[] stack = Thread.currentThread().getStackTrace();
-
- // Check that the stack trace contains at least two elements, one for this method and one for the caller.
- if (stack.length >= 2)
- {
- className = stack[1].getClassName();
- methodName = stack[1].getMethodName();
- }
-
- // Java 1.3 and before? Not sure, some horrible thing that parses the text spat out by printStackTrace?
-
- return getProperty(className, methodName, key);
- }
-
- /**
- * Looks up a property value relative to the environment, base class and modifier. The default environment will be
- * checked for a matching property if defaults are being used.
- *
- * @param base An object of the class to retrieve properties relative to.
- * @param modifier The modifier (which may stand for a method of the class).
- * @param key The property key.
- *
- * @return The value of this property searching from the most specific definition (environment, class, modifier, key)
- * to the most general (key only), unless use of default environments is turned off in which case the most general
- * property searched is (environment, key).
- */
- public String getProperty(Object base, String modifier, String key)
- {
- return getProperty(base.getClass().getName(), modifier, key);
- }
-
- /**
- * Looks up a property value relative to the environment, base class and modifier. The default environment will be
- * checked for a matching property if defaults are being used.
- *
- * @param base The name of the class to retrieve properties relative to.
- * @param modifier The modifier (which may stand for a method of the class).
- * @param key The property key.
- *
- * @return The value of this property searching from the most specific definition (environment, class, modifier, key)
- * to the most general (key only), unless use of default environments is turned off in which case the most general
- * property searched is (environment, key).
- */
- public String getProperty(String base, String modifier, String key)
- {
- String result = null;
-
- // Loop over the key orderings, from the most specific to the most general, until a matching value is found.
- for (Iterator i = getKeyIterator(base, modifier, key); i.hasNext();)
- {
- String nextKey = (String) i.next();
-
- result = super.getProperty(nextKey);
-
- if (result != null)
- {
- break;
- }
- }
-
- return result;
- }
-
- /**
- * Looks up an array property value relative to the environment, callers class and method. The default environment
- * will be checked for a matching array property if defaults are being used. In order to work out the callers class
- * and method this method throws an exception and then searches one level up its stack frames.
- *
- * @param key The property key.
- *
- * @return The array value of this indexed property searching from the most specific definition (environment, class,
- * method, key) to the most general (key only), unless use of default environments is turned off in which
- * case the most general proeprty searched is (environment, key).
- */
- public String[] getProperties(String key)
- {
- // Try to get the callers class name and method name by throwing an exception an searching the stack frames.
- String className = null;
- String methodName = null;
-
- /* Java 1.4 onwards only.
- try {
- throw new Exception();
- } catch (Exception e) {
- StackTraceElement[] stack = e.getStackTrace();
- // Check that the stack trace contains at least two elements, one for this method and one for the caller.
- if (stack.length >= 2) {
- className = stack[1].getClassName();
- methodName = stack[1].getMethodName();
- }
- }*/
- return getProperties(className, methodName, key);
- }
-
- /**
- * Looks up an array property value relative to the environment, base class and modifier. The default environment will
- * be checked for a matching array property if defaults are being used.
- *
- * @param base An object of the class to retrieve properties relative to.
- * @param modifier The modifier (which may stand for a method of the class).
- * @param key The property key.
- *
- * @return The array value of this indexed property searching from the most specific definition (environment, class,
- * modifier, key) to the most general (key only), unless use of default environments is turned off in which
- * case the most general proeprty searched is (environment, key).
- */
- public String[] getProperties(Object base, String modifier, String key)
- {
- return getProperties(base.getClass().getName(), modifier, key);
- }
-
- /**
- * Looks up an array property value relative to the environment, base class and modifier. The default environment will
- * be checked for a matching array property if defaults are being used.
- *
- * @param base The name of the class to retrieve properties relative to.
- * @param modifier The modifier (which may stand for a method of the class).
- * @param key The property key.
- *
- * @return The array value of this indexed property searching from the most specific definition (environment, class,
- * modifier, key) to the most general (key only), unless use of default environments is turned off in which
- * case the most general property searched is (environment, key).
- */
- public String[] getProperties(String base, String modifier, String key)
- {
- String[] result = null;
-
- // Loop over the key orderings, from the most specific to the most general, until a matching value is found.
- for (Iterator i = getKeyIterator(base, modifier, key); i.hasNext();)
- {
- String nextKey = (String) i.next();
- ArrayList arrayList = (ArrayList) arrayProperties.get(nextKey);
-
- if (arrayList != null)
- {
- result = (String[]) arrayList.toArray(new String[] {});
-
- break;
- }
- }
-
- return result;
- }
-
- /**
- * For a given environment, base, modifier and key and setting of the use of default environments feature this
- * generates an iterator that walks over the order in which to try and access properties.
- *
- * <p>See the {@link #ORDER} constant for an explanation of how the key ordering is generated.
- *
- * @param base The name of the class to retrieve properties relative to.
- * @param modifier The modifier (which may stand for a method of the class).
- * @param key The property key.
- *
- * @return An Iterator over String keys defining the order in which properties should be accessed.
- */
- protected Iterator getKeyIterator(final String base, final String modifier, final String key)
- {
- return new Iterator()
- {
- // The key ordering count always begins at the start of the ORDER array.
- private int i = 0;
-
- public boolean hasNext()
- {
- return (useDefaults ? ((i < ORDER.length) && (ORDER[i] > ENVIRONMENT_DEFAULTS_CUTOFF))
- : (i < ORDER.length));
- }
-
- public Object next()
- {
- // Check that there is a next element and return null if not.
- if (!hasNext())
- {
- return null;
- }
-
- // Get the next ordering count.
- int o = ORDER[i];
-
- // Do bit matching on the count to choose which elements to include in the key.
- String result =
- (((o & E) != 0) ? (environment + ".") : "") + (((o & B) != 0) ? (base + ".") : "")
- + (((o & M) != 0) ? (modifier + ".") : "") + key;
-
- // Increment the iterator to get the next key on the next call.
- i++;
-
- return result;
- }
-
- public void remove()
- {
- // This method is not supported.
- throw new UnsupportedOperationException("remove() is not supported on this key order iterator as "
- + "the ordering cannot be changed");
- }
- };
- }
-
- /**
- * Scans all the properties in the parent Properties object and creates arrays for any array property definitions.
- *
- * <p>Array properties are defined with indexes. For example:
- *
- * <p><code>
- * property.1=one<br/>
- * property.2=two<br/>
- * property.3=three<br/>
- * </code>
- *
- * <p>Note that these properties will be stored as the 'empty string' or "" property array.
- *
- * <p><code>
- * .1=one<br/>
- * 2=two<br/>
- * </code>
- */
- protected void createArrayProperties()
- {
- // Scan through all defined properties.
- for (Object o : keySet())
- {
- String key = (String) o;
- String value = super.getProperty(key);
-
- // Split the property key into everything before the last '.' and after it.
- int lastDotIndex = key.lastIndexOf('.');
- String keyEnding = key.substring(lastDotIndex + 1, key.length());
- String keyStart = key.substring(0, (lastDotIndex == -1) ? 0 : lastDotIndex);
-
- // Check if the property key ends in an integer, in which case it is an array property.
- int index = 0;
-
- try
- {
- index = Integer.parseInt(keyEnding);
- }
- // The ending is not an integer so its not an array.
- catch (NumberFormatException e)
- {
- // Scan the next property.
- continue;
- }
-
- // Check if an array property already exists for this base name and create one if not.
- ArrayList propArray = (ArrayList) arrayProperties.get(keyStart);
-
- if (propArray == null)
- {
- propArray = new ArrayList();
- arrayProperties.put(keyStart, propArray);
- }
-
- // Add the new property value to the array property for the index.
- propArray.set(index, value);
- }
- }
-}
+/* + * + * 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.junit.extensions.util; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; + +/** + * ContextualProperties is an extension of {@link java.util.Properties} that automatically selects properties based on an + * environment parameter (defined by the system property {@link #ENV_SYS_PROPERTY}), the name of a class, plus a modifier + * (which can be used to name a method of a class) and a property key. It also supports the definition of arrays of + * property values using indexes. The properties are searched in the following order until a match is found: + * + * <ol> + * <li>environment + class name with package name + modifier + key + * <li>environment + class name with package name + key + * <li>environment + key + * <li>class name with package name + modifier + key + * <li>class name with package name + key + * <li>key + * </ol> + * + * <p>To create arrays of property values add index numbers onto the end of the property keys. An array of string values + * will be created with the elements of the array set to the value of the property at the matching index. Ideally the + * index values will be contiguous, starting at 0. This does not need to be the case however. If an array definition + * begins at index n, and ends at index m, Then an array big enough to hold m + 1 elements will be created and populated + * with values from n to m. Values before n and any missing values between n and m will be null in the array. + * + * <p>To give an example, suppose you have two different environments 'DEVELOPMENT' and 'PRODUCTION' and they each need + * the same properties but set to different values for each environment and some properties the same in both, you could + * create a properties file like: + * + * <p><code> + * # Project configuration properties file.<br/> + * <br/> + * # These properties are environment specific.<br/> + * DEVELOPMENT.debug=true<br/> + * PRODUCTION.debug=false<br/> + * <br/> + * # Always debug MyClass in all environments but not the myMethod method.<br/> + * MyClass.debug=true<br/> + * MyClass.myMethod.debug=false<br/> + * <br/> + * # Set up an array of my ten favourite animals. Leave elements 3 to 8 as null as I haven't decided on them yet.<br/> + * animals.0=cat<br/> + * animals.1=dog<br/> + * animals.2=elephant<br/> + * animals.9=lion<br/> + * <br/> + * # This is a default value that will be used when the environment is not known.<br/> + * debug=false<br/> + * </code> + * + * <p>The most specific definition of a property is searched for first moving out to the most general. This allows + * general property defaults to be set and then overiden for specific uses by some classes and modifiers. + * + * <p>A ContextualProperties object can be loaded in the same way as a java.utils.Properties. A recommended way to do + * this that does not assume that the properties file is a file (it could be in a jar) is to load the properties from the + * url for the resource lookup up on the classpath: + * + * <p><code> + * Properties configProperties = new ContextualProperties();<br/> + * configProperties.load(this.getClass().getClassLoader().getResourceAsStream("config.properties"));<br/> + * </code> + * + * <p>EnvironmentProperties will load the 'DEVELOPMENT.debug' property or 'PROUCTION.debug' property based on the setting + * of the system environment property. If a matching property for the environment cannot be found then the simple property + * name without the environment pre-pended onto it will be used instead. This 'use of default environments' behaviour is + * turned on initially but it can be disabled by calling the {@link #useDefaultEnvironments} method. + * + * <p>When a property matching a key cannot be found then the property accessor methods will always return null. If a + * default value for a property exists but the 'use of default environments' behavious prevents it being used then the + * accessor methods will return null. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Automatically select properties dependant on environment, class name and modifier as well as property key. + * <tr><td> Convert indexed properties into arrays. + * </table> + * + * @author Rupert Smith + */ +public class ContextualProperties extends ParsedProperties +{ + /** The name of the system property that is used to define the environment. */ + public static final String ENV_SYS_PROPERTY = "environment"; + + /** + * <p>Holds the iteration count down order. + * + * <p>If e = 4, b = 2, m = 1 then the iteration order or i is 7,6,4 and then if using environment defaults 3,2,0 + * where the accessor key is: + * (i & e != 0 ? environment : "") + (i & b != 0 ? base : "") + (1 + m != 0 ? modifier : "") + key + * + * <p>In other words the presence or otherwise of the three least significant bits when counting down from 7 + * specifies which of the environment, base and modifier are to be included in the key where the environment, base + * and modifier stand for the bits in positions 2, 1 and 0. The numbers 5 and 1 are missed out of the count because + * they stand for the case where the modifier is used without the base which is not done. + */ + private static final int[] ORDER = new int[] { 7, 6, 4, 3, 2, 0 }; + + /** + * Defines the point in the iteration count order below which the 'use environment defaults' feature is being used. + */ + private static final int ENVIRONMENT_DEFAULTS_CUTOFF = 4; + + /** Defines the bit representation for the environment in the key ordering. See {@link #ORDER}. */ + private static final int E = 4; + + /** Defines the bit representation for the base in the key ordering. See {@link #ORDER}. */ + private static final int B = 2; + + /** Defines the bit representation for the modifier in the key ordering. See {@link #ORDER}. */ + private static final int M = 1; + + /** Used to hold the value of the environment system property. */ + private String environment; + + /** Used to indicate that the 'use of defaults' behaviour should be used. */ + private boolean useDefaults = true; + + /** Used to hold all the array properties. This is a mapping from property names to ArrayLists of Strings. */ + protected Map arrayProperties = new HashMap(); + + /** + * Default constructor that builds a ContextualProperties that uses environment defaults. + */ + public ContextualProperties() + { + super(); + + // Keep the value of the system environment property. + environment = System.getProperty(ENV_SYS_PROPERTY); + } + + /** + * Creates a ContextualProperties that uses environment defaults and is initialized with the specified properties. + * + * @param props The properties to initialize this with. + */ + public ContextualProperties(Properties props) + { + super(props); + + // Keep the value of the system environment property. + environment = System.getProperty(ENV_SYS_PROPERTY); + + // Extract any array properties as arrays. + createArrayProperties(); + } + + /** + * Parses an input stream as properties. + * + * @param inStream The input stream to read the properties from. + * + * @exception IOException If there is an IO error during reading from the input stream. + */ + public void load(InputStream inStream) throws IOException + { + super.load(inStream); + + // Extract any array properties as arrays. + createArrayProperties(); + } + + /** + * Tells this environment aware properties object whether it should use default environment properties without a + * pre-pended environment when a property for the current environment cannot be found. + * + * @param flag True to use defaults, false to not use defaults. + */ + public void useDefaultEnvironments(boolean flag) + { + useDefaults = flag; + } + + /** + * Looks up a property value relative to the environment, callers class and method. The default environment will be + * checked for a matching property if defaults are being used. In order to work out the callers class and method this + * method throws an exception and then searches one level up its stack frames. + * + * @param key The property key. + * + * @return The value of this property searching from the most specific definition (environment, class, method, key) + * to the most general (key only), unless use of default environments is turned off in which case the most general + * proeprty searched is (environment, key). + */ + public String getProperty(String key) + { + // Try to get the callers class name and method name by examing the stack. + String className = null; + String methodName = null; + + // Java 1.4 onwards only. + /*try + { + throw new Exception(); + } + catch (Exception e) + { + StackTraceElement[] stack = e.getStackTrace(); + + // Check that the stack trace contains at least two elements, one for this method and one for the caller. + if (stack.length >= 2) + { + className = stack[1].getClassName(); + methodName = stack[1].getMethodName(); + } + }*/ + + // Java 1.5 onwards only. + StackTraceElement[] stack = Thread.currentThread().getStackTrace(); + + // Check that the stack trace contains at least two elements, one for this method and one for the caller. + if (stack.length >= 2) + { + className = stack[1].getClassName(); + methodName = stack[1].getMethodName(); + } + + // Java 1.3 and before? Not sure, some horrible thing that parses the text spat out by printStackTrace? + + return getProperty(className, methodName, key); + } + + /** + * Looks up a property value relative to the environment, base class and modifier. The default environment will be + * checked for a matching property if defaults are being used. + * + * @param base An object of the class to retrieve properties relative to. + * @param modifier The modifier (which may stand for a method of the class). + * @param key The property key. + * + * @return The value of this property searching from the most specific definition (environment, class, modifier, key) + * to the most general (key only), unless use of default environments is turned off in which case the most general + * property searched is (environment, key). + */ + public String getProperty(Object base, String modifier, String key) + { + return getProperty(base.getClass().getName(), modifier, key); + } + + /** + * Looks up a property value relative to the environment, base class and modifier. The default environment will be + * checked for a matching property if defaults are being used. + * + * @param base The name of the class to retrieve properties relative to. + * @param modifier The modifier (which may stand for a method of the class). + * @param key The property key. + * + * @return The value of this property searching from the most specific definition (environment, class, modifier, key) + * to the most general (key only), unless use of default environments is turned off in which case the most general + * property searched is (environment, key). + */ + public String getProperty(String base, String modifier, String key) + { + String result = null; + + // Loop over the key orderings, from the most specific to the most general, until a matching value is found. + for (Iterator i = getKeyIterator(base, modifier, key); i.hasNext();) + { + String nextKey = (String) i.next(); + + result = super.getProperty(nextKey); + + if (result != null) + { + break; + } + } + + return result; + } + + /** + * Looks up an array property value relative to the environment, callers class and method. The default environment + * will be checked for a matching array property if defaults are being used. In order to work out the callers class + * and method this method throws an exception and then searches one level up its stack frames. + * + * @param key The property key. + * + * @return The array value of this indexed property searching from the most specific definition (environment, class, + * method, key) to the most general (key only), unless use of default environments is turned off in which + * case the most general proeprty searched is (environment, key). + */ + public String[] getProperties(String key) + { + // Try to get the callers class name and method name by throwing an exception an searching the stack frames. + String className = null; + String methodName = null; + + /* Java 1.4 onwards only. + try { + throw new Exception(); + } catch (Exception e) { + StackTraceElement[] stack = e.getStackTrace(); + // Check that the stack trace contains at least two elements, one for this method and one for the caller. + if (stack.length >= 2) { + className = stack[1].getClassName(); + methodName = stack[1].getMethodName(); + } + }*/ + return getProperties(className, methodName, key); + } + + /** + * Looks up an array property value relative to the environment, base class and modifier. The default environment will + * be checked for a matching array property if defaults are being used. + * + * @param base An object of the class to retrieve properties relative to. + * @param modifier The modifier (which may stand for a method of the class). + * @param key The property key. + * + * @return The array value of this indexed property searching from the most specific definition (environment, class, + * modifier, key) to the most general (key only), unless use of default environments is turned off in which + * case the most general proeprty searched is (environment, key). + */ + public String[] getProperties(Object base, String modifier, String key) + { + return getProperties(base.getClass().getName(), modifier, key); + } + + /** + * Looks up an array property value relative to the environment, base class and modifier. The default environment will + * be checked for a matching array property if defaults are being used. + * + * @param base The name of the class to retrieve properties relative to. + * @param modifier The modifier (which may stand for a method of the class). + * @param key The property key. + * + * @return The array value of this indexed property searching from the most specific definition (environment, class, + * modifier, key) to the most general (key only), unless use of default environments is turned off in which + * case the most general property searched is (environment, key). + */ + public String[] getProperties(String base, String modifier, String key) + { + String[] result = null; + + // Loop over the key orderings, from the most specific to the most general, until a matching value is found. + for (Iterator i = getKeyIterator(base, modifier, key); i.hasNext();) + { + String nextKey = (String) i.next(); + ArrayList arrayList = (ArrayList) arrayProperties.get(nextKey); + + if (arrayList != null) + { + result = (String[]) arrayList.toArray(new String[] {}); + + break; + } + } + + return result; + } + + /** + * For a given environment, base, modifier and key and setting of the use of default environments feature this + * generates an iterator that walks over the order in which to try and access properties. + * + * <p>See the {@link #ORDER} constant for an explanation of how the key ordering is generated. + * + * @param base The name of the class to retrieve properties relative to. + * @param modifier The modifier (which may stand for a method of the class). + * @param key The property key. + * + * @return An Iterator over String keys defining the order in which properties should be accessed. + */ + protected Iterator getKeyIterator(final String base, final String modifier, final String key) + { + return new Iterator() + { + // The key ordering count always begins at the start of the ORDER array. + private int i = 0; + + public boolean hasNext() + { + return (useDefaults ? ((i < ORDER.length) && (ORDER[i] > ENVIRONMENT_DEFAULTS_CUTOFF)) + : (i < ORDER.length)); + } + + public Object next() + { + // Check that there is a next element and return null if not. + if (!hasNext()) + { + return null; + } + + // Get the next ordering count. + int o = ORDER[i]; + + // Do bit matching on the count to choose which elements to include in the key. + String result = + (((o & E) != 0) ? (environment + ".") : "") + (((o & B) != 0) ? (base + ".") : "") + + (((o & M) != 0) ? (modifier + ".") : "") + key; + + // Increment the iterator to get the next key on the next call. + i++; + + return result; + } + + public void remove() + { + // This method is not supported. + throw new UnsupportedOperationException("remove() is not supported on this key order iterator as " + + "the ordering cannot be changed"); + } + }; + } + + /** + * Scans all the properties in the parent Properties object and creates arrays for any array property definitions. + * + * <p>Array properties are defined with indexes. For example: + * + * <p><code> + * property.1=one<br/> + * property.2=two<br/> + * property.3=three<br/> + * </code> + * + * <p>Note that these properties will be stored as the 'empty string' or "" property array. + * + * <p><code> + * .1=one<br/> + * 2=two<br/> + * </code> + */ + protected void createArrayProperties() + { + // Scan through all defined properties. + for (Object o : keySet()) + { + String key = (String) o; + String value = super.getProperty(key); + + // Split the property key into everything before the last '.' and after it. + int lastDotIndex = key.lastIndexOf('.'); + String keyEnding = key.substring(lastDotIndex + 1, key.length()); + String keyStart = key.substring(0, (lastDotIndex == -1) ? 0 : lastDotIndex); + + // Check if the property key ends in an integer, in which case it is an array property. + int index = 0; + + try + { + index = Integer.parseInt(keyEnding); + } + // The ending is not an integer so its not an array. + catch (NumberFormatException e) + { + // Scan the next property. + continue; + } + + // Check if an array property already exists for this base name and create one if not. + ArrayList propArray = (ArrayList) arrayProperties.get(keyStart); + + if (propArray == null) + { + propArray = new ArrayList(); + arrayProperties.put(keyStart, propArray); + } + + // Add the new property value to the array property for the index. + propArray.set(index, value); + } + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/MathUtils.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/MathUtils.java index 7a45632643..7c803294f4 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/MathUtils.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/MathUtils.java @@ -1,428 +1,428 @@ -/*
- *
- * 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.junit.extensions.util;
-
-import java.util.ArrayList;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Mathematical support methods for the toolkit. Caculating averages, variances, min/max for test latencies and
- * generating linear/exponential sequences for test size/concurrency ramping up.
- *
- * <p/>The sequence specifications are of the form [lowest(, ...)(, highest)](,sample=s)(,exp), where round brackets
- * enclose optional values. Using this pattern form it is possible to specify a single value, a range of values divided
- * into s samples, a range of values divided into s samples but distributed exponentially, or a fixed set of samples.
- *
- * <p/>The duration arguments are of the form (dD)(hH)(mM)(sS), where round brackets enclose optional values. At least
- * one of the optional values must be present.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Generate a sequene of integers from a sequence specification.
- * <tr><td> Parse an encoded duration into milliseconds.
- * </table>
- *
- * @author Rupert Smith
- */
-public class MathUtils
-{
- /** Used for debugging. */
- // private static final Logger log = Logger.getLogger(MathUtils.class);
-
- /** The sequence defintion matching regular expression. */
- public static final String SEQUENCE_REGEXP = "^(\\[[0-9:]+\\])(:samples=[0-9]+)?(:exp)?$";
-
- /** The regular expression that matches sequence definitions. */
- private static final Pattern SEQUENCE_PATTERN = Pattern.compile(SEQUENCE_REGEXP);
-
- /** The duration definition matching regular expression. */
- public static final String DURATION_REGEXP = "^(\\d+D)?(\\d+H)?(\\d+M)?(\\d+S)?$";
-
- /** The regular expression that matches the duration expression. */
- public static final Pattern DURATION_PATTERN = Pattern.compile(DURATION_REGEXP);
-
- /** For matching name=value pairs. */
- public static final String NAME_VALUE_REGEXP = "^\\w+=\\w+$";
-
- /** For matching name=[value1: value2: ...] variations. */
- public static final String NAME_VALUE_VARIATION_REGEXP = "^\\w+=\\[[\\w:]+\\]$";
-
- /** For matching name=[n: ... :m](:sample=s)(:exp) sequences. */
- public static final String NAME_VALUE_SEQUENCE_REGEXP = "^\\w+=(\\[[0-9:]+\\])(:samples=[0-9]+)?(:exp)?$";
-
- /** The regular expression that matches name=value pairs and variations. */
- public static final Pattern NAME_VALUE_PATTERN =
- Pattern.compile("(" + NAME_VALUE_REGEXP + ")|(" + NAME_VALUE_VARIATION_REGEXP + ")|(" + NAME_VALUE_SEQUENCE_REGEXP
- + ")");
-
- /**
- * Runs a quick test of the sequence generation methods to confirm that they work as expected.
- *
- * @param args The command line parameters.
- */
- public static void main(String[] args)
- {
- // Use the command line parser to evaluate the command line.
- CommandLineParser commandLine =
- new CommandLineParser(
- new String[][]
- {
- { "s", "The sequence definition.", "[m:...:n](:sample=s)(:exp)", "true", MathUtils.SEQUENCE_REGEXP },
- { "d", "The duration definition.", "dDhHmMsS", "false", MathUtils.DURATION_REGEXP }
- });
-
- // Capture the command line arguments or display errors and correct usage and then exit.
- ParsedProperties options = null;
-
- try
- {
- options = new ParsedProperties(commandLine.parseCommandLine(args));
- }
- catch (IllegalArgumentException e)
- {
- System.out.println(commandLine.getErrors());
- System.out.println(commandLine.getUsage());
- System.exit(-1);
- }
-
- // Extract the command line options.
- String sequence = options.getProperty("s");
- String durationString = options.getProperty("d");
-
- System.out.println("Sequence is: " + printArray(parseSequence(sequence)));
-
- if (durationString != null)
- {
- System.out.println("Duration is: " + parseDuration(durationString));
- }
- }
-
- /**
- * Given a start and end and a number of steps this method generates a sequence of evenly spaced integer
- * values, starting at the start (inclusive) and finishing at the end (inclusive) with the specified number
- * of values in the sequence. The sequence returned may contain less than the specified number where the integer
- * range between start and end is too small to contain that many.
- *
- * <p/>As the results are integers, they will not be perfectly evenly spaced but a best-fit.
- *
- * @param start The sequence start.
- * @param end The sequence end.
- * @param steps The number of steps.
- *
- * @return The sequence.
- */
- public static int[] generateSequence(int start, int end, int steps)
- {
- // Check that there are at least two steps.
- if (steps < 2)
- {
- throw new IllegalArgumentException("There must be at least 2 steps.");
- }
-
- ArrayList<Integer> result = new ArrayList<Integer>();
-
- // Calculate the sequence using floating point, then round into the results.
- double fStart = start;
- double fEnd = end;
- double fCurrent = start;
-
- for (int i = 0; i < steps; i++)
- {
- fCurrent = (((fEnd - fStart) / (steps - 1)) * i) + fStart;
-
- roundAndAdd(result, fCurrent);
- }
-
- // Return the results after converting to a primitive array.
- return intListToPrimitiveArray(result);
- }
-
- /**
- * Given a start and end and a number of steps this method generates a sequence of expontentially spaced integer
- * values, starting at the start (inclusive) and finishing at the end (inclusive) with the specified number
- * of values in the sequence. An exponentially spaced sequence is one where the ratio between any two consecutive
- * numbers in the sequence remains constant. The sequence returned may contain less than the specified number where
- * the difference between two consecutive values is too small (this is more likely at the start of the sequence,
- * where the values are closer together).
- *
- * <p/>As the results are integers, they will not be perfectly exponentially spaced but a best-fit.
- *
- * @param start The sequence start.
- * @param end The sequence end.
- * @param steps The number of steps.
- *
- * @return The sequence.
- */
- public static int[] generateExpSequence(int start, int end, int steps)
- {
- // Check that there are at least two steps.
- if (steps < 2)
- {
- throw new IllegalArgumentException("There must be at least 2 steps.");
- }
-
- ArrayList<Integer> result = new ArrayList<Integer>();
-
- // Calculate the sequence using floating point, then round into the results.
- double fStart = start;
- double fEnd = end;
- // float fCurrent = start;
- double diff = fEnd - fStart;
- double factor = java.lang.Math.pow(diff, (1.0f / (steps - 1)));
-
- for (int i = 0; i < steps; i++)
- {
- // This is a cheat to get the end exactly on and lose the accumulated rounding error.
- if (i == (steps - 1))
- {
- result.add(end);
- }
- else
- {
- roundAndAdd(result, fStart - 1.0f + java.lang.Math.pow(factor, i));
- }
- }
-
- // Return the results after converting to a primitive array.
- return intListToPrimitiveArray(result);
- }
-
- /**
- * Parses a string defintion of a sequence into an int array containing the sequence. The definition will conform
- * to the regular expression: "^(\[[0-9,]+\])(,samples=[0-9]+)?(,exp)?$". This splits it into three parts,
- * an array of integers, the optional sample count and the optional exponential flag.
- *
- * @param sequenceDef The sequence definition.
- *
- * @return The sequence as a fully expanded int array.
- */
- public static int[] parseSequence(String sequenceDef)
- {
- // Match the sequence definition against the regular expression for sequences.
- Matcher matcher = SEQUENCE_PATTERN.matcher(sequenceDef);
-
- // Check that the argument is of the right format accepted by this method.
- if (!matcher.matches())
- {
- throw new IllegalArgumentException("The sequence definition is not in the correct format.");
- }
-
- // Get the total number of matching groups to see if either of the optional samples or exponential flag
- // goups were set.
- int numGroups = matcher.groupCount();
-
- // Split the array of integers on commas.
- String intArrayString = matcher.group(1);
-
- String[] intSplits = intArrayString.split("[:\\[\\]]");
-
- int[] sequence = new int[intSplits.length - 1];
-
- for (int i = 1; i < intSplits.length; i++)
- {
- sequence[i - 1] = Integer.parseInt(intSplits[i]);
- }
-
- // Check for the optional samples count.
- int samples = 0;
-
- if ((numGroups > 1) && (matcher.group(2) != null))
- {
- String samplesGroup = matcher.group(2);
-
- String samplesString = samplesGroup.substring(",samples=".length());
- samples = Integer.parseInt(samplesString);
- }
-
- // Check for the optional exponential flag.
- boolean expFlag = false;
-
- if ((numGroups > 2) && (matcher.group(3) != null))
- {
- expFlag = true;
- }
-
- // If there is a sample count and 2 or more sequence values defined, then generate the sequence from the first
- // and last sequence values.
- if ((samples != 0) && (sequence.length >= 2))
- {
- int start = sequence[0];
- int end = sequence[sequence.length - 1];
-
- if (!expFlag)
- {
- sequence = generateSequence(start, end, samples);
- }
- else
- {
- sequence = generateExpSequence(start, end, samples);
- }
- }
-
- return sequence;
- }
-
- /**
- * Parses a duration defined as a string, giving a duration in days, hours, minutes and seconds into a number
- * of milliseconds equal to that duration.
- *
- * @param duration The duration definition string.
- *
- * @return The duration in millliseconds.
- */
- public static long parseDuration(String duration)
- {
- // Match the duration against the regular expression.
- Matcher matcher = DURATION_PATTERN.matcher(duration);
-
- // Check that the argument is of the right format accepted by this method.
- if (!matcher.matches())
- {
- throw new IllegalArgumentException("The duration definition is not in the correct format.");
- }
-
- // This accumulates the duration.
- long result = 0;
-
- int numGroups = matcher.groupCount();
-
- // Extract the days.
- if (numGroups >= 1)
- {
- String daysString = matcher.group(1);
- result +=
- (daysString == null)
- ? 0 : (Long.parseLong(daysString.substring(0, daysString.length() - 1)) * 24 * 60 * 60 * 1000);
- }
-
- // Extract the hours.
- if (numGroups >= 2)
- {
- String hoursString = matcher.group(2);
- result +=
- (hoursString == null) ? 0
- : (Long.parseLong(hoursString.substring(0, hoursString.length() - 1)) * 60 * 60 * 1000);
- }
-
- // Extract the minutes.
- if (numGroups >= 3)
- {
- String minutesString = matcher.group(3);
- result +=
- (minutesString == null)
- ? 0 : (Long.parseLong(minutesString.substring(0, minutesString.length() - 1)) * 60 * 1000);
- }
-
- // Extract the seconds.
- if (numGroups >= 4)
- {
- String secondsString = matcher.group(4);
- result +=
- (secondsString == null) ? 0 : (Long.parseLong(secondsString.substring(0, secondsString.length() - 1)) * 1000);
- }
-
- return result;
- }
-
- /**
- * Pretty prints an array of ints as a string.
- *
- * @param array The array to pretty print.
- *
- * @return The pretty printed string.
- */
- public static String printArray(int[] array)
- {
- String result = "[";
- for (int i = 0; i < array.length; i++)
- {
- result += array[i];
- result += (i < (array.length - 1)) ? ", " : "";
- }
-
- result += "]";
-
- return result;
- }
-
- /**
- * Returns the maximum value in an array of integers.
- *
- * @param values The array to find the amx in.
- *
- * @return The max value.
- */
- public static int maxInArray(int[] values)
- {
- if ((values == null) || (values.length == 0))
- {
- throw new IllegalArgumentException("Cannot find the max of a null or empty array.");
- }
-
- int max = values[0];
-
- for (int value : values)
- {
- max = (max < value) ? value : max;
- }
-
- return max;
- }
-
- /**
- * The #toArray methods of collections cannot be used with primitive arrays. This loops over and array list
- * of Integers and outputs and array of int.
- *
- * @param result The array of Integers to convert.
- *
- * @return An array of int.
- */
- private static int[] intListToPrimitiveArray(ArrayList<Integer> result)
- {
- int[] resultArray = new int[result.size()];
- int index = 0;
- for (int r : result)
- {
- resultArray[index] = result.get(index);
- index++;
- }
-
- return resultArray;
- }
-
- /**
- * Rounds the specified floating point value to the nearest integer and adds it to the specified list of
- * integers, provided it is not already in the list.
- *
- * @param result The list of integers to add to.
- * @param value The new candidate to round and add to the list.
- */
- private static void roundAndAdd(ArrayList<Integer> result, double value)
- {
- int roundedValue = (int) Math.round(value);
-
- if (!result.contains(roundedValue))
- {
- result.add(roundedValue);
- }
- }
-}
+/* + * + * 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.junit.extensions.util; + +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Mathematical support methods for the toolkit. Caculating averages, variances, min/max for test latencies and + * generating linear/exponential sequences for test size/concurrency ramping up. + * + * <p/>The sequence specifications are of the form [lowest(, ...)(, highest)](,sample=s)(,exp), where round brackets + * enclose optional values. Using this pattern form it is possible to specify a single value, a range of values divided + * into s samples, a range of values divided into s samples but distributed exponentially, or a fixed set of samples. + * + * <p/>The duration arguments are of the form (dD)(hH)(mM)(sS), where round brackets enclose optional values. At least + * one of the optional values must be present. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Generate a sequene of integers from a sequence specification. + * <tr><td> Parse an encoded duration into milliseconds. + * </table> + * + * @author Rupert Smith + */ +public class MathUtils +{ + /** Used for debugging. */ + // private static final Logger log = Logger.getLogger(MathUtils.class); + + /** The sequence defintion matching regular expression. */ + public static final String SEQUENCE_REGEXP = "^(\\[[0-9:]+\\])(:samples=[0-9]+)?(:exp)?$"; + + /** The regular expression that matches sequence definitions. */ + private static final Pattern SEQUENCE_PATTERN = Pattern.compile(SEQUENCE_REGEXP); + + /** The duration definition matching regular expression. */ + public static final String DURATION_REGEXP = "^(\\d+D)?(\\d+H)?(\\d+M)?(\\d+S)?$"; + + /** The regular expression that matches the duration expression. */ + public static final Pattern DURATION_PATTERN = Pattern.compile(DURATION_REGEXP); + + /** For matching name=value pairs. */ + public static final String NAME_VALUE_REGEXP = "^\\w+=\\w+$"; + + /** For matching name=[value1: value2: ...] variations. */ + public static final String NAME_VALUE_VARIATION_REGEXP = "^\\w+=\\[[\\w:]+\\]$"; + + /** For matching name=[n: ... :m](:sample=s)(:exp) sequences. */ + public static final String NAME_VALUE_SEQUENCE_REGEXP = "^\\w+=(\\[[0-9:]+\\])(:samples=[0-9]+)?(:exp)?$"; + + /** The regular expression that matches name=value pairs and variations. */ + public static final Pattern NAME_VALUE_PATTERN = + Pattern.compile("(" + NAME_VALUE_REGEXP + ")|(" + NAME_VALUE_VARIATION_REGEXP + ")|(" + NAME_VALUE_SEQUENCE_REGEXP + + ")"); + + /** + * Runs a quick test of the sequence generation methods to confirm that they work as expected. + * + * @param args The command line parameters. + */ + public static void main(String[] args) + { + // Use the command line parser to evaluate the command line. + CommandLineParser commandLine = + new CommandLineParser( + new String[][] + { + { "s", "The sequence definition.", "[m:...:n](:sample=s)(:exp)", "true", MathUtils.SEQUENCE_REGEXP }, + { "d", "The duration definition.", "dDhHmMsS", "false", MathUtils.DURATION_REGEXP } + }); + + // Capture the command line arguments or display errors and correct usage and then exit. + ParsedProperties options = null; + + try + { + options = new ParsedProperties(commandLine.parseCommandLine(args)); + } + catch (IllegalArgumentException e) + { + System.out.println(commandLine.getErrors()); + System.out.println(commandLine.getUsage()); + System.exit(-1); + } + + // Extract the command line options. + String sequence = options.getProperty("s"); + String durationString = options.getProperty("d"); + + System.out.println("Sequence is: " + printArray(parseSequence(sequence))); + + if (durationString != null) + { + System.out.println("Duration is: " + parseDuration(durationString)); + } + } + + /** + * Given a start and end and a number of steps this method generates a sequence of evenly spaced integer + * values, starting at the start (inclusive) and finishing at the end (inclusive) with the specified number + * of values in the sequence. The sequence returned may contain less than the specified number where the integer + * range between start and end is too small to contain that many. + * + * <p/>As the results are integers, they will not be perfectly evenly spaced but a best-fit. + * + * @param start The sequence start. + * @param end The sequence end. + * @param steps The number of steps. + * + * @return The sequence. + */ + public static int[] generateSequence(int start, int end, int steps) + { + // Check that there are at least two steps. + if (steps < 2) + { + throw new IllegalArgumentException("There must be at least 2 steps."); + } + + ArrayList<Integer> result = new ArrayList<Integer>(); + + // Calculate the sequence using floating point, then round into the results. + double fStart = start; + double fEnd = end; + double fCurrent = start; + + for (int i = 0; i < steps; i++) + { + fCurrent = (((fEnd - fStart) / (steps - 1)) * i) + fStart; + + roundAndAdd(result, fCurrent); + } + + // Return the results after converting to a primitive array. + return intListToPrimitiveArray(result); + } + + /** + * Given a start and end and a number of steps this method generates a sequence of expontentially spaced integer + * values, starting at the start (inclusive) and finishing at the end (inclusive) with the specified number + * of values in the sequence. An exponentially spaced sequence is one where the ratio between any two consecutive + * numbers in the sequence remains constant. The sequence returned may contain less than the specified number where + * the difference between two consecutive values is too small (this is more likely at the start of the sequence, + * where the values are closer together). + * + * <p/>As the results are integers, they will not be perfectly exponentially spaced but a best-fit. + * + * @param start The sequence start. + * @param end The sequence end. + * @param steps The number of steps. + * + * @return The sequence. + */ + public static int[] generateExpSequence(int start, int end, int steps) + { + // Check that there are at least two steps. + if (steps < 2) + { + throw new IllegalArgumentException("There must be at least 2 steps."); + } + + ArrayList<Integer> result = new ArrayList<Integer>(); + + // Calculate the sequence using floating point, then round into the results. + double fStart = start; + double fEnd = end; + // float fCurrent = start; + double diff = fEnd - fStart; + double factor = java.lang.Math.pow(diff, (1.0f / (steps - 1))); + + for (int i = 0; i < steps; i++) + { + // This is a cheat to get the end exactly on and lose the accumulated rounding error. + if (i == (steps - 1)) + { + result.add(end); + } + else + { + roundAndAdd(result, fStart - 1.0f + java.lang.Math.pow(factor, i)); + } + } + + // Return the results after converting to a primitive array. + return intListToPrimitiveArray(result); + } + + /** + * Parses a string defintion of a sequence into an int array containing the sequence. The definition will conform + * to the regular expression: "^(\[[0-9,]+\])(,samples=[0-9]+)?(,exp)?$". This splits it into three parts, + * an array of integers, the optional sample count and the optional exponential flag. + * + * @param sequenceDef The sequence definition. + * + * @return The sequence as a fully expanded int array. + */ + public static int[] parseSequence(String sequenceDef) + { + // Match the sequence definition against the regular expression for sequences. + Matcher matcher = SEQUENCE_PATTERN.matcher(sequenceDef); + + // Check that the argument is of the right format accepted by this method. + if (!matcher.matches()) + { + throw new IllegalArgumentException("The sequence definition is not in the correct format."); + } + + // Get the total number of matching groups to see if either of the optional samples or exponential flag + // goups were set. + int numGroups = matcher.groupCount(); + + // Split the array of integers on commas. + String intArrayString = matcher.group(1); + + String[] intSplits = intArrayString.split("[:\\[\\]]"); + + int[] sequence = new int[intSplits.length - 1]; + + for (int i = 1; i < intSplits.length; i++) + { + sequence[i - 1] = Integer.parseInt(intSplits[i]); + } + + // Check for the optional samples count. + int samples = 0; + + if ((numGroups > 1) && (matcher.group(2) != null)) + { + String samplesGroup = matcher.group(2); + + String samplesString = samplesGroup.substring(",samples=".length()); + samples = Integer.parseInt(samplesString); + } + + // Check for the optional exponential flag. + boolean expFlag = false; + + if ((numGroups > 2) && (matcher.group(3) != null)) + { + expFlag = true; + } + + // If there is a sample count and 2 or more sequence values defined, then generate the sequence from the first + // and last sequence values. + if ((samples != 0) && (sequence.length >= 2)) + { + int start = sequence[0]; + int end = sequence[sequence.length - 1]; + + if (!expFlag) + { + sequence = generateSequence(start, end, samples); + } + else + { + sequence = generateExpSequence(start, end, samples); + } + } + + return sequence; + } + + /** + * Parses a duration defined as a string, giving a duration in days, hours, minutes and seconds into a number + * of milliseconds equal to that duration. + * + * @param duration The duration definition string. + * + * @return The duration in millliseconds. + */ + public static long parseDuration(String duration) + { + // Match the duration against the regular expression. + Matcher matcher = DURATION_PATTERN.matcher(duration); + + // Check that the argument is of the right format accepted by this method. + if (!matcher.matches()) + { + throw new IllegalArgumentException("The duration definition is not in the correct format."); + } + + // This accumulates the duration. + long result = 0; + + int numGroups = matcher.groupCount(); + + // Extract the days. + if (numGroups >= 1) + { + String daysString = matcher.group(1); + result += + (daysString == null) + ? 0 : (Long.parseLong(daysString.substring(0, daysString.length() - 1)) * 24 * 60 * 60 * 1000); + } + + // Extract the hours. + if (numGroups >= 2) + { + String hoursString = matcher.group(2); + result += + (hoursString == null) ? 0 + : (Long.parseLong(hoursString.substring(0, hoursString.length() - 1)) * 60 * 60 * 1000); + } + + // Extract the minutes. + if (numGroups >= 3) + { + String minutesString = matcher.group(3); + result += + (minutesString == null) + ? 0 : (Long.parseLong(minutesString.substring(0, minutesString.length() - 1)) * 60 * 1000); + } + + // Extract the seconds. + if (numGroups >= 4) + { + String secondsString = matcher.group(4); + result += + (secondsString == null) ? 0 : (Long.parseLong(secondsString.substring(0, secondsString.length() - 1)) * 1000); + } + + return result; + } + + /** + * Pretty prints an array of ints as a string. + * + * @param array The array to pretty print. + * + * @return The pretty printed string. + */ + public static String printArray(int[] array) + { + String result = "["; + for (int i = 0; i < array.length; i++) + { + result += array[i]; + result += (i < (array.length - 1)) ? ", " : ""; + } + + result += "]"; + + return result; + } + + /** + * Returns the maximum value in an array of integers. + * + * @param values The array to find the amx in. + * + * @return The max value. + */ + public static int maxInArray(int[] values) + { + if ((values == null) || (values.length == 0)) + { + throw new IllegalArgumentException("Cannot find the max of a null or empty array."); + } + + int max = values[0]; + + for (int value : values) + { + max = (max < value) ? value : max; + } + + return max; + } + + /** + * The #toArray methods of collections cannot be used with primitive arrays. This loops over and array list + * of Integers and outputs and array of int. + * + * @param result The array of Integers to convert. + * + * @return An array of int. + */ + private static int[] intListToPrimitiveArray(ArrayList<Integer> result) + { + int[] resultArray = new int[result.size()]; + int index = 0; + for (int r : result) + { + resultArray[index] = result.get(index); + index++; + } + + return resultArray; + } + + /** + * Rounds the specified floating point value to the nearest integer and adds it to the specified list of + * integers, provided it is not already in the list. + * + * @param result The list of integers to add to. + * @param value The new candidate to round and add to the list. + */ + private static void roundAndAdd(ArrayList<Integer> result, double value) + { + int roundedValue = (int) Math.round(value); + + if (!result.contains(roundedValue)) + { + result.add(roundedValue); + } + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ParsedProperties.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ParsedProperties.java index 59c8cfbd3a..1cc6757675 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ParsedProperties.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ParsedProperties.java @@ -1,390 +1,390 @@ -/*
- *
- * 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.junit.extensions.util;
-
-import java.util.Properties;
-
-/**
- * ParsedProperties extends the basic Properties class with methods to extract properties, not as strings but as strings
- * parsed into basic types.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * </table>
- *
- * @author Rupert Smith
- */
-public class ParsedProperties extends Properties
-{
- /**
- * Creates an empty ParsedProperties.
- */
- public ParsedProperties()
- {
- super();
- }
-
- /**
- * Creates a ParsedProperties initialized with the specified properties.
- *
- * @param props The properties to initialize this with.
- */
- public ParsedProperties(Properties props)
- {
- super(props);
- }
-
- /**
- * Helper method for setting system properties to defaults when they are not already set.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property after this method call.
- */
- public static boolean setSysPropertyIfNull(String propname, boolean value)
- {
- return Boolean.parseBoolean(setSysPropertyIfNull(propname, Boolean.toString(value)));
- }
-
- /**
- * Helper method for setting system properties to defaults when they are not already set.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property after this method call.
- */
- public static short setSysPropertyIfNull(String propname, short value)
- {
- return Short.parseShort(setSysPropertyIfNull(propname, Short.toString(value)));
- }
-
- /**
- * Helper method for setting system properties to defaults when they are not already set.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property after this method call.
- */
- public static int setSysPropertyIfNull(String propname, int value)
- {
- return Integer.parseInt(setSysPropertyIfNull(propname, Integer.toString(value)));
- }
-
- /**
- * Helper method for setting system properties to defaults when they are not already set.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property after this method call.
- */
- public static long setSysPropertyIfNull(String propname, long value)
- {
- return Long.parseLong(setSysPropertyIfNull(propname, Long.toString(value)));
- }
-
- /**
- * Helper method for setting system properties to defaults when they are not already set.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property after this method call.
- */
- public static float setSysPropertyIfNull(String propname, float value)
- {
- return Float.parseFloat(setSysPropertyIfNull(propname, Float.toString(value)));
- }
-
- /**
- * Helper method for setting system properties to defaults when they are not already set.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property after this method call.
- */
- public static double setSysPropertyIfNull(String propname, double value)
- {
- return Double.parseDouble(setSysPropertyIfNull(propname, Double.toString(value)));
- }
-
- /**
- * Helper method for setting system properties to defaults when they are not already set.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the system property after this method call.
- */
- public static String setSysPropertyIfNull(String propname, String value)
- {
- String property = System.getProperty(propname);
-
- if (property == null)
- {
- System.setProperty(propname, value);
-
- return value;
- }
- else
- {
- return property;
- }
- }
-
- /**
- * Helper method for setting properties to defaults when they are not already set.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property after this method call.
- */
- public boolean setPropertyIfNull(String propname, boolean value)
- {
- return Boolean.parseBoolean(setPropertyIfNull(propname, Boolean.toString(value)));
- }
-
- /**
- * Helper method for setting properties to defaults when they are not already set.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property after this method call.
- */
- public short setPropertyIfNull(String propname, short value)
- {
- return Short.parseShort(setPropertyIfNull(propname, Short.toString(value)));
- }
-
- /**
- * Helper method for setting properties to defaults when they are not already set.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property after this method call.
- */
- public int setPropertyIfNull(String propname, int value)
- {
- return Integer.parseInt(setPropertyIfNull(propname, Integer.toString(value)));
- }
-
- /**
- * Helper method for setting properties to defaults when they are not already set.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property after this method call.
- */
- public long setPropertyIfNull(String propname, long value)
- {
- return Long.parseLong(setPropertyIfNull(propname, Long.toString(value)));
- }
-
- /**
- * Helper method for setting properties to defaults when they are not already set.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property after this method call.
- */
- public float setPropertyIfNull(String propname, float value)
- {
- return Float.parseFloat(setPropertyIfNull(propname, Float.toString(value)));
- }
-
- /**
- * Helper method for setting properties to defaults when they are not already set.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property after this method call.
- */
- public double setPropertyIfNull(String propname, double value)
- {
- return Double.parseDouble(setPropertyIfNull(propname, Double.toString(value)));
- }
-
- /**
- * Helper method for setting properties to defaults when they are not already set.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property after this method call.
- */
- public String setPropertyIfNull(String propname, String value)
- {
- String property = super.getProperty(propname);
-
- if (property == null)
- {
- super.setProperty(propname, value);
-
- return value;
- }
- else
- {
- return property;
- }
- }
-
- /**
- * Helper method for setting properties.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property after this method call.
- */
- public boolean setProperty(String propname, boolean value)
- {
- setProperty(propname, Boolean.toString(value));
-
- return value;
- }
-
- /**
- * Helper method for setting properties.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property after this method call.
- */
- public short setProperty(String propname, short value)
- {
- setProperty(propname, Short.toString(value));
-
- return value;
- }
-
- /**
- * Helper method for setting properties.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property after this method call.
- */
- public int setProperty(String propname, int value)
- {
- setProperty(propname, Integer.toString(value));
-
- return value;
- }
-
- /**
- * Helper method for setting properties.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property after this method call.
- */
- public long setProperty(String propname, long value)
- {
- setProperty(propname, Long.toString(value));
-
- return value;
- }
-
- /**
- * Helper method for setting properties.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property after this method call.
- */
- public float setProperty(String propname, float value)
- {
- setProperty(propname, Float.toString(value));
-
- return value;
- }
-
- /**
- * Helper method for setting properties.
- *
- * @param propname The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property after this method call.
- */
- public double setProperty(String propname, double value)
- {
- setProperty(propname, Double.toString(value));
-
- return value;
- }
-
- /**
- * Parses a property as a boolean.
- *
- * @param propName The property.
- *
- * @return The property as a boolean, or false if it does not exist.
- */
- public boolean getPropertyAsBoolean(String propName)
- {
- String prop = getProperty(propName);
-
- return (prop != null) && Boolean.parseBoolean(prop);
- }
-
- /**
- * Parses a property as an integer.
- *
- * @param propName The property.
- *
- * @return The property as a integer, or null if it does not exist.
- */
- public Integer getPropertyAsInteger(String propName)
- {
- String prop = getProperty(propName);
-
- return (prop != null) ? new Integer(prop) : null;
- }
-
- /**
- * Parses a property as a long.
- *
- * @param propName The property.
- *
- * @return The property as a long, or null if it does not exist.
- */
- public Long getPropertyAsLong(String propName)
- {
- String prop = getProperty(propName);
-
- return (prop != null) ? new Long(prop) : null;
- }
-}
+/* + * + * 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.junit.extensions.util; + +import java.util.Properties; + +/** + * ParsedProperties extends the basic Properties class with methods to extract properties, not as strings but as strings + * parsed into basic types. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * </table> + * + * @author Rupert Smith + */ +public class ParsedProperties extends Properties +{ + /** + * Creates an empty ParsedProperties. + */ + public ParsedProperties() + { + super(); + } + + /** + * Creates a ParsedProperties initialized with the specified properties. + * + * @param props The properties to initialize this with. + */ + public ParsedProperties(Properties props) + { + super(props); + } + + /** + * Helper method for setting system properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public static boolean setSysPropertyIfNull(String propname, boolean value) + { + return Boolean.parseBoolean(setSysPropertyIfNull(propname, Boolean.toString(value))); + } + + /** + * Helper method for setting system properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public static short setSysPropertyIfNull(String propname, short value) + { + return Short.parseShort(setSysPropertyIfNull(propname, Short.toString(value))); + } + + /** + * Helper method for setting system properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public static int setSysPropertyIfNull(String propname, int value) + { + return Integer.parseInt(setSysPropertyIfNull(propname, Integer.toString(value))); + } + + /** + * Helper method for setting system properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public static long setSysPropertyIfNull(String propname, long value) + { + return Long.parseLong(setSysPropertyIfNull(propname, Long.toString(value))); + } + + /** + * Helper method for setting system properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public static float setSysPropertyIfNull(String propname, float value) + { + return Float.parseFloat(setSysPropertyIfNull(propname, Float.toString(value))); + } + + /** + * Helper method for setting system properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public static double setSysPropertyIfNull(String propname, double value) + { + return Double.parseDouble(setSysPropertyIfNull(propname, Double.toString(value))); + } + + /** + * Helper method for setting system properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the system property after this method call. + */ + public static String setSysPropertyIfNull(String propname, String value) + { + String property = System.getProperty(propname); + + if (property == null) + { + System.setProperty(propname, value); + + return value; + } + else + { + return property; + } + } + + /** + * Helper method for setting properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public boolean setPropertyIfNull(String propname, boolean value) + { + return Boolean.parseBoolean(setPropertyIfNull(propname, Boolean.toString(value))); + } + + /** + * Helper method for setting properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public short setPropertyIfNull(String propname, short value) + { + return Short.parseShort(setPropertyIfNull(propname, Short.toString(value))); + } + + /** + * Helper method for setting properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public int setPropertyIfNull(String propname, int value) + { + return Integer.parseInt(setPropertyIfNull(propname, Integer.toString(value))); + } + + /** + * Helper method for setting properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public long setPropertyIfNull(String propname, long value) + { + return Long.parseLong(setPropertyIfNull(propname, Long.toString(value))); + } + + /** + * Helper method for setting properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public float setPropertyIfNull(String propname, float value) + { + return Float.parseFloat(setPropertyIfNull(propname, Float.toString(value))); + } + + /** + * Helper method for setting properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public double setPropertyIfNull(String propname, double value) + { + return Double.parseDouble(setPropertyIfNull(propname, Double.toString(value))); + } + + /** + * Helper method for setting properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public String setPropertyIfNull(String propname, String value) + { + String property = super.getProperty(propname); + + if (property == null) + { + super.setProperty(propname, value); + + return value; + } + else + { + return property; + } + } + + /** + * Helper method for setting properties. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public boolean setProperty(String propname, boolean value) + { + setProperty(propname, Boolean.toString(value)); + + return value; + } + + /** + * Helper method for setting properties. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public short setProperty(String propname, short value) + { + setProperty(propname, Short.toString(value)); + + return value; + } + + /** + * Helper method for setting properties. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public int setProperty(String propname, int value) + { + setProperty(propname, Integer.toString(value)); + + return value; + } + + /** + * Helper method for setting properties. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public long setProperty(String propname, long value) + { + setProperty(propname, Long.toString(value)); + + return value; + } + + /** + * Helper method for setting properties. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public float setProperty(String propname, float value) + { + setProperty(propname, Float.toString(value)); + + return value; + } + + /** + * Helper method for setting properties. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public double setProperty(String propname, double value) + { + setProperty(propname, Double.toString(value)); + + return value; + } + + /** + * Parses a property as a boolean. + * + * @param propName The property. + * + * @return The property as a boolean, or false if it does not exist. + */ + public boolean getPropertyAsBoolean(String propName) + { + String prop = getProperty(propName); + + return (prop != null) && Boolean.parseBoolean(prop); + } + + /** + * Parses a property as an integer. + * + * @param propName The property. + * + * @return The property as a integer, or null if it does not exist. + */ + public Integer getPropertyAsInteger(String propName) + { + String prop = getProperty(propName); + + return (prop != null) ? new Integer(prop) : null; + } + + /** + * Parses a property as a long. + * + * @param propName The property. + * + * @return The property as a long, or null if it does not exist. + */ + public Long getPropertyAsLong(String propName) + { + String prop = getProperty(propName); + + return (prop != null) ? new Long(prop) : null; + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/SizeOf.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/SizeOf.java index 5f3ebb4545..ecc08770a9 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/SizeOf.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/SizeOf.java @@ -1,94 +1,94 @@ -/*
- *
- * 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.junit.extensions.util;
-
-/**
- * SizeOf provides a static method that does its best to return an accurate measure of the total amount of memory used by
- * the virtual machine. This is calculated as the total memory available to the VM minus the actual amount used by it.
- * Before this measurement is taken the garbage collector is run many times until the used memory calculation stabilizes.
- * Generally, this trick works quite well to provide an accurate reading, however, it cannot be relied upon to be totally
- * accurate. It is also quite slow.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Calculate total memory used.
- * </table>
- *
- * @author Rupert Smith
- */
-public class SizeOf
-{
- /** Holds a reference to the runtime object. */
- private static final Runtime RUNTIME = Runtime.getRuntime();
-
- /**
- * Makes 4 calls the {@link #runGCTillStable} method.
- */
- public static void runGCTillStableSeveralTimes()
- {
- // It helps to call Runtime.gc() using several method calls.
- for (int r = 0; r < 4; ++r)
- {
- runGCTillStable();
- }
- }
-
- /**
- * Runs the garbage collector until the used memory reading stabilizes. It may run the garbage collector up
- * to 500 times.
- */
- public static void runGCTillStable()
- {
- long usedMem1 = usedMemory(), usedMem2 = Long.MAX_VALUE;
-
- for (int i = 0; (usedMem1 < usedMem2) && (i < 500); ++i)
- {
- RUNTIME.runFinalization();
- RUNTIME.gc();
- Thread.currentThread().yield();
-
- usedMem2 = usedMem1;
- usedMem1 = usedMemory();
- }
- }
-
- /**
- * Runs the garbage collector until the used memory stabilizes and then measures it.
- *
- * @return The amount of memory used by the virtual machine.
- */
- public static long getUsedMemory()
- {
- runGCTillStableSeveralTimes();
-
- return usedMemory();
- }
-
- /**
- * Returns the amount of memory used by subtracting the free memory from the total available memory.
- *
- * @return The amount of memory used.
- */
- private static long usedMemory()
- {
- return RUNTIME.totalMemory() - RUNTIME.freeMemory();
- }
-}
+/* + * + * 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.junit.extensions.util; + +/** + * SizeOf provides a static method that does its best to return an accurate measure of the total amount of memory used by + * the virtual machine. This is calculated as the total memory available to the VM minus the actual amount used by it. + * Before this measurement is taken the garbage collector is run many times until the used memory calculation stabilizes. + * Generally, this trick works quite well to provide an accurate reading, however, it cannot be relied upon to be totally + * accurate. It is also quite slow. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Calculate total memory used. + * </table> + * + * @author Rupert Smith + */ +public class SizeOf +{ + /** Holds a reference to the runtime object. */ + private static final Runtime RUNTIME = Runtime.getRuntime(); + + /** + * Makes 4 calls the {@link #runGCTillStable} method. + */ + public static void runGCTillStableSeveralTimes() + { + // It helps to call Runtime.gc() using several method calls. + for (int r = 0; r < 4; ++r) + { + runGCTillStable(); + } + } + + /** + * Runs the garbage collector until the used memory reading stabilizes. It may run the garbage collector up + * to 500 times. + */ + public static void runGCTillStable() + { + long usedMem1 = usedMemory(), usedMem2 = Long.MAX_VALUE; + + for (int i = 0; (usedMem1 < usedMem2) && (i < 500); ++i) + { + RUNTIME.runFinalization(); + RUNTIME.gc(); + Thread.currentThread().yield(); + + usedMem2 = usedMem1; + usedMem1 = usedMemory(); + } + } + + /** + * Runs the garbage collector until the used memory stabilizes and then measures it. + * + * @return The amount of memory used by the virtual machine. + */ + public static long getUsedMemory() + { + runGCTillStableSeveralTimes(); + + return usedMemory(); + } + + /** + * Returns the amount of memory used by subtracting the free memory from the total available memory. + * + * @return The amount of memory used. + */ + private static long usedMemory() + { + return RUNTIME.totalMemory() - RUNTIME.freeMemory(); + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/StackQueue.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/StackQueue.java index 9078c0e247..acc1e2c218 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/StackQueue.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/StackQueue.java @@ -1,131 +1,131 @@ -/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.junit.extensions.util;
-
-import java.util.EmptyStackException;
-import java.util.NoSuchElementException;
-import java.util.Queue;
-import java.util.Stack;
-
-/**
- * The Stack class in java.util (most unhelpfully) does not implement the Queue interface. This is an adaption of that
- * class as a queue.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Turn a stack into a queue.
- * </table>
- *
- * @todo Need to override the add method, and iterator and consider other methods too. They work like LIFO but
- * really wany FIFO behaviour accross the whole data structure.
- *
- * @author Rupert Smith
- */
-public class StackQueue<E> extends Stack<E> implements Queue<E>
-{
- /**
- * Retrieves, but does not remove, the head of this queue.
- *
- * @return The element at the top of the stack.
- */
- public E element()
- {
- try
- {
- return super.peek();
- }
- catch (EmptyStackException e)
- {
- NoSuchElementException t = new NoSuchElementException();
- t.initCause(e);
- throw t;
- }
- }
-
- /**
- * Inserts the specified element into this queue, if possible.
- *
- * @param o The data element to push onto the stack.
- *
- * @return True if it was added to the stack, false otherwise (this implementation always returns true).
- */
- public boolean offer(E o)
- {
- push(o);
-
- return true;
- }
-
- /**
- * Retrieves, but does not remove, the head of this queue, returning null if this queue is empty.
- *
- * @return The top element from the stack, or null if the stack is empty.
- */
- public E peek()
- {
- try
- {
- return super.peek();
- }
- catch (EmptyStackException e)
- {
- return null;
- }
- }
-
- /**
- * Retrieves and removes the head of this queue, or null if this queue is empty.
- *
- * @return The top element from the stack, or null if the stack is empty.
- */
- public E poll()
- {
- try
- {
- return super.pop();
- }
- catch (EmptyStackException e)
- {
- return null;
- }
- }
-
- /**
- * Retrieves and removes the head of this queue.
- *
- * @return The top element from the stack, or null if the stack is empty.
- *
- * @throws NoSuchElementException If the stack is empty so no element can be removed from it.
- */
- public E remove()
- {
- try
- {
- return super.pop();
- }
- catch (EmptyStackException e)
- {
- NoSuchElementException t = new NoSuchElementException();
- t.initCause(e);
- throw t;
- }
- }
-}
+/* + * + * 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.junit.extensions.util; + +import java.util.EmptyStackException; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.Stack; + +/** + * The Stack class in java.util (most unhelpfully) does not implement the Queue interface. This is an adaption of that + * class as a queue. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Turn a stack into a queue. + * </table> + * + * @todo Need to override the add method, and iterator and consider other methods too. They work like LIFO but + * really wany FIFO behaviour accross the whole data structure. + * + * @author Rupert Smith + */ +public class StackQueue<E> extends Stack<E> implements Queue<E> +{ + /** + * Retrieves, but does not remove, the head of this queue. + * + * @return The element at the top of the stack. + */ + public E element() + { + try + { + return super.peek(); + } + catch (EmptyStackException e) + { + NoSuchElementException t = new NoSuchElementException(); + t.initCause(e); + throw t; + } + } + + /** + * Inserts the specified element into this queue, if possible. + * + * @param o The data element to push onto the stack. + * + * @return True if it was added to the stack, false otherwise (this implementation always returns true). + */ + public boolean offer(E o) + { + push(o); + + return true; + } + + /** + * Retrieves, but does not remove, the head of this queue, returning null if this queue is empty. + * + * @return The top element from the stack, or null if the stack is empty. + */ + public E peek() + { + try + { + return super.peek(); + } + catch (EmptyStackException e) + { + return null; + } + } + + /** + * Retrieves and removes the head of this queue, or null if this queue is empty. + * + * @return The top element from the stack, or null if the stack is empty. + */ + public E poll() + { + try + { + return super.pop(); + } + catch (EmptyStackException e) + { + return null; + } + } + + /** + * Retrieves and removes the head of this queue. + * + * @return The top element from the stack, or null if the stack is empty. + * + * @throws NoSuchElementException If the stack is empty so no element can be removed from it. + */ + public E remove() + { + try + { + return super.pop(); + } + catch (EmptyStackException e) + { + NoSuchElementException t = new NoSuchElementException(); + t.initCause(e); + throw t; + } + } +} diff --git a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/TestContextProperties.java b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/TestContextProperties.java index edb7b6d73a..d402077963 100644 --- a/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/TestContextProperties.java +++ b/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/TestContextProperties.java @@ -1,202 +1,202 @@ -/*
- *
- * 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.junit.extensions.util;
-
-import java.util.Properties;
-
-/**
- * TestContextProperties is an extension of {@link ParsedProperties} that keeps track of property key/value pairs
- * that are used by tests being run under the {@link org.apache.qpid.junit.extensions.TKTestRunner}. To keep the
- * test runner notified of configurable test parameters, tests should establish their required property values by
- * initiliazing fields or statics or in the constructor, through this class. The tk test runner automatically places
- * any additional properties specified on the command line into the this class, and these are held statically.
- *
- * <p/>Here is an example:
- *
- * <pre>
- * public class MyTestClass extends TestCase {
- * ParsedProperties testProps = TestContextProperties.getInstance();
- * private int testParam = testProps.setPropertyIfNull("testParam", 1);
- * ...
- * </pre>
- *
- * <p/>This has the effect of setting up the field testParam with the default value of 1, unless it is overridden
- * by values passed to the tk test runner. It also notifies the tk test runner of the name and value of the test
- * parameter actually used for the test, so that this can be logged in the test output file.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Log all name/value pairs read or written.
- * </table>
- *
- * @author Rupert Smith
- */
-public class TestContextProperties extends ParsedProperties
-{
- /** Used for debugging. */
- // Logger log = Logger.getLogger(TestContextProperties.class);
-
- /** Holds all properties set or read through this property extension class. */
- private Properties accessedProps = new Properties();
-
- /** The singleton instance of the test context properties. */
- private static TestContextProperties singleton = null;
-
- /**
- * Default constructor that builds a ContextualProperties that uses environment defaults.
- */
- private TestContextProperties()
- {
- super();
- }
-
- /**
- * Gets the singleton instance of the test context properties.
- *
- * @return The singleton instance of the test context properties.
- */
- public static synchronized ParsedProperties getInstance()
- {
- if (singleton == null)
- {
- singleton = new TestContextProperties();
- }
-
- return singleton;
- }
-
- /**
- * Gets the singleton instance of the test context properties, applying a specified set of default properties to
- * it, if they are not already set.
- *
- * @param defaults The defaults to apply for properties not already set.
- *
- * @return The singleton instance of the test context properties.
- */
- public static synchronized ParsedProperties getInstance(Properties defaults)
- {
- ParsedProperties props = getInstance();
-
- for (Object key : defaults.keySet())
- {
- String stringKey = (String) key;
- String value = defaults.getProperty(stringKey);
-
- props.setPropertyIfNull(stringKey, value);
- }
-
- return props;
- }
-
- /*
- * Creates a ContextualProperties that uses environment defaults and is initialized with the specified properties.
- *
- * @param props The properties to initialize this with.
- */
- /*public TestContextProperties(Properties props)
- {
- super();
- }*/
-
- /**
- * Gets all of the properties (with their most recent values) that have been set or read through this class.
- *
- * @return All of the properties accessed through this class.
- */
- public static Properties getAccessedProps()
- {
- return (singleton == null) ? new Properties() : singleton;
- // return accessedProps;
- }
-
- /**
- * Looks up a property value relative to the environment, callers class and method. The default environment will be
- * checked for a matching property if defaults are being used. The property key/value pair is remembered and made
- * available to {@link org.apache.qpid.junit.extensions.TKTestRunner}.
- *
- * @param key The property key.
- *
- * @return The value of this property searching from the most specific definition (environment, class, method, key)
- * to the most general (key only), unless use of default environments is turned off in which case the most general
- * proeprty searched is (environment, key).
- */
- public String getProperty(String key)
- {
- // log.debug("public String getProperty(String key = " + key + "): called");
-
- String value = super.getProperty(key);
-
- if (value != null)
- {
- accessedProps.setProperty(key, value);
- }
-
- // log.debug("value = " + value);
-
- return value;
- }
-
- /**
- * Calls the <tt>Hashtable</tt> method <code>put</code>. Provided for parallelism with the <tt>getProperty</tt>
- * method. Enforces use of strings for property keys and values. The value returned is the result of the
- * <tt>Hashtable</tt> call to <code>put</code>. The property key/value pair is remembered and made
- * available to {@link org.apache.qpid.junit.extensions.TKTestRunner}.
- *
- * @param key The key to be placed into this property list.
- * @param value The value corresponding to <tt>key</tt>.
- *
- * @return The previous value of the specified key in this property list, or <code>null</code> if it did not have one.
- */
- public synchronized Object setProperty(String key, String value)
- {
- // log.debug("public synchronized Object setProperty(String key = " + key + ", String value = " + value + "): called");
-
- Object result = super.setProperty(key, value);
- accessedProps.setProperty(key, value);
-
- return result;
- }
-
- /**
- * Helper method for setting properties to defaults when they are not already set. The property key/value pair is
- * remembered and made available to {@link org.apache.qpid.junit.extensions.TKTestRunner}.
- *
- * @param key The name of the system property to set.
- * @param value The value to set it to.
- *
- * @return The value of the property, which will be the value passed in if it was null, or the existing value otherwise.
- */
- public String setPropertyIfNull(String key, String value)
- {
- // log.debug("public String setPropertyIfNull(String key = " + key + ", String value = " + value + "): called");
-
- String result = super.setPropertyIfNull(key, value);
-
- if (value != null)
- {
- accessedProps.setProperty(key, result);
- }
-
- // log.debug("result = " + result);
-
- return result;
- }
-}
+/* + * + * 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.junit.extensions.util; + +import java.util.Properties; + +/** + * TestContextProperties is an extension of {@link ParsedProperties} that keeps track of property key/value pairs + * that are used by tests being run under the {@link org.apache.qpid.junit.extensions.TKTestRunner}. To keep the + * test runner notified of configurable test parameters, tests should establish their required property values by + * initiliazing fields or statics or in the constructor, through this class. The tk test runner automatically places + * any additional properties specified on the command line into the this class, and these are held statically. + * + * <p/>Here is an example: + * + * <pre> + * public class MyTestClass extends TestCase { + * ParsedProperties testProps = TestContextProperties.getInstance(); + * private int testParam = testProps.setPropertyIfNull("testParam", 1); + * ... + * </pre> + * + * <p/>This has the effect of setting up the field testParam with the default value of 1, unless it is overridden + * by values passed to the tk test runner. It also notifies the tk test runner of the name and value of the test + * parameter actually used for the test, so that this can be logged in the test output file. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Log all name/value pairs read or written. + * </table> + * + * @author Rupert Smith + */ +public class TestContextProperties extends ParsedProperties +{ + /** Used for debugging. */ + // Logger log = Logger.getLogger(TestContextProperties.class); + + /** Holds all properties set or read through this property extension class. */ + private Properties accessedProps = new Properties(); + + /** The singleton instance of the test context properties. */ + private static TestContextProperties singleton = null; + + /** + * Default constructor that builds a ContextualProperties that uses environment defaults. + */ + private TestContextProperties() + { + super(); + } + + /** + * Gets the singleton instance of the test context properties. + * + * @return The singleton instance of the test context properties. + */ + public static synchronized ParsedProperties getInstance() + { + if (singleton == null) + { + singleton = new TestContextProperties(); + } + + return singleton; + } + + /** + * Gets the singleton instance of the test context properties, applying a specified set of default properties to + * it, if they are not already set. + * + * @param defaults The defaults to apply for properties not already set. + * + * @return The singleton instance of the test context properties. + */ + public static synchronized ParsedProperties getInstance(Properties defaults) + { + ParsedProperties props = getInstance(); + + for (Object key : defaults.keySet()) + { + String stringKey = (String) key; + String value = defaults.getProperty(stringKey); + + props.setPropertyIfNull(stringKey, value); + } + + return props; + } + + /* + * Creates a ContextualProperties that uses environment defaults and is initialized with the specified properties. + * + * @param props The properties to initialize this with. + */ + /*public TestContextProperties(Properties props) + { + super(); + }*/ + + /** + * Gets all of the properties (with their most recent values) that have been set or read through this class. + * + * @return All of the properties accessed through this class. + */ + public static Properties getAccessedProps() + { + return (singleton == null) ? new Properties() : singleton; + // return accessedProps; + } + + /** + * Looks up a property value relative to the environment, callers class and method. The default environment will be + * checked for a matching property if defaults are being used. The property key/value pair is remembered and made + * available to {@link org.apache.qpid.junit.extensions.TKTestRunner}. + * + * @param key The property key. + * + * @return The value of this property searching from the most specific definition (environment, class, method, key) + * to the most general (key only), unless use of default environments is turned off in which case the most general + * proeprty searched is (environment, key). + */ + public String getProperty(String key) + { + // log.debug("public String getProperty(String key = " + key + "): called"); + + String value = super.getProperty(key); + + if (value != null) + { + accessedProps.setProperty(key, value); + } + + // log.debug("value = " + value); + + return value; + } + + /** + * Calls the <tt>Hashtable</tt> method <code>put</code>. Provided for parallelism with the <tt>getProperty</tt> + * method. Enforces use of strings for property keys and values. The value returned is the result of the + * <tt>Hashtable</tt> call to <code>put</code>. The property key/value pair is remembered and made + * available to {@link org.apache.qpid.junit.extensions.TKTestRunner}. + * + * @param key The key to be placed into this property list. + * @param value The value corresponding to <tt>key</tt>. + * + * @return The previous value of the specified key in this property list, or <code>null</code> if it did not have one. + */ + public synchronized Object setProperty(String key, String value) + { + // log.debug("public synchronized Object setProperty(String key = " + key + ", String value = " + value + "): called"); + + Object result = super.setProperty(key, value); + accessedProps.setProperty(key, value); + + return result; + } + + /** + * Helper method for setting properties to defaults when they are not already set. The property key/value pair is + * remembered and made available to {@link org.apache.qpid.junit.extensions.TKTestRunner}. + * + * @param key The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property, which will be the value passed in if it was null, or the existing value otherwise. + */ + public String setPropertyIfNull(String key, String value) + { + // log.debug("public String setPropertyIfNull(String key = " + key + ", String value = " + value + "): called"); + + String result = super.setPropertyIfNull(key, value); + + if (value != null) + { + accessedProps.setProperty(key, result); + } + + // log.debug("result = " + result); + + return result; + } +} diff --git a/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingClient.java b/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingClient.java index b9632eee4c..0afec83b19 100644 --- a/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingClient.java +++ b/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingClient.java @@ -1,107 +1,107 @@ -/*
- *
- * 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.ping;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.requestreply.PingPongProducer;
-
-import javax.jms.Destination;
-
-import java.util.List;
-import java.util.Properties;
-
-/**
- * PingClient is a {@link PingPongProducer} that does not need a {@link org.apache.qpid.requestreply.PingPongBouncer}
- * to send replies to its pings. It simply listens to its own ping destinations, rather than seperate reply queues.
- * It is an all in one ping client, that produces and consumes its own pings.
- *
- * <p/>The constructor increments a count of the number of ping clients created. It is assumed that where many
- * are created they will all be run in parallel and be active in sending and consuming pings at the same time.
- * If the unique destinations flag is not set and a pub/sub ping cycle is being run, this means that they will all hear
- * pings sent by each other. The expected number of pings received will therefore be multiplied up by the number of
- * active ping clients. The {@link #getConsumersPerDestination()} method is used to supply this multiplier under these
- * conditions.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Create a ping producer that listens to its own pings <td> {@link PingPongProducer}
- * <tr><td> Count the number of ping producers and produce multiplier for scaling up messages expected over topic pings.
- * </table>
- */
-public class PingClient extends PingPongProducer
-{
- /** Used for debugging. */
- private final Logger log = Logger.getLogger(PingClient.class);
-
- /** Used to count the number of ping clients created. */
- private static int _pingClientCount;
-
- /**
- * Creates a ping producer with the specified parameters, of which there are many. See the class level comments
- * for {@link PingPongProducer} for details. This constructor creates a connection to the broker and creates
- * producer and consumer sessions on it, to send and recieve its pings and replies on.
- *
- * @param overrides Properties containing any desired overrides to the defaults.
- *
- * @throws Exception Any exceptions are allowed to fall through.
- */
- public PingClient(Properties overrides) throws Exception
- {
- super(overrides);
-
- _pingClientCount++;
- }
-
- /**
- * Returns the ping destinations themselves as the reply destinations for this pinger to listen to. This has the
- * effect of making this pinger listen to its own pings.
- *
- * @return The ping destinations.
- */
- public List<Destination> getReplyDestinations()
- {
- return _pingDestinations;
- }
-
- /**
- * Supplies the multiplier for the number of ping clients that will hear each ping when doing pub/sub pinging.
- *
- * @return The scaling up of the number of expected pub/sub pings.
- */
- public int getConsumersPerDestination()
- {
- log.debug("public int getConsumersPerDestination(): called");
-
- if (_isUnique)
- {
- log.debug(_noOfConsumers + " consumer per destination.");
-
- return _noOfConsumers;
- }
- else
- {
- log.debug((_pingClientCount * _noOfConsumers) + " consumers per destination.");
-
- return _pingClientCount * _noOfConsumers;
- }
- }
-}
+/* + * + * 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.ping; + +import org.apache.log4j.Logger; + +import org.apache.qpid.requestreply.PingPongProducer; + +import javax.jms.Destination; + +import java.util.List; +import java.util.Properties; + +/** + * PingClient is a {@link PingPongProducer} that does not need a {@link org.apache.qpid.requestreply.PingPongBouncer} + * to send replies to its pings. It simply listens to its own ping destinations, rather than seperate reply queues. + * It is an all in one ping client, that produces and consumes its own pings. + * + * <p/>The constructor increments a count of the number of ping clients created. It is assumed that where many + * are created they will all be run in parallel and be active in sending and consuming pings at the same time. + * If the unique destinations flag is not set and a pub/sub ping cycle is being run, this means that they will all hear + * pings sent by each other. The expected number of pings received will therefore be multiplied up by the number of + * active ping clients. The {@link #getConsumersPerDestination()} method is used to supply this multiplier under these + * conditions. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Create a ping producer that listens to its own pings <td> {@link PingPongProducer} + * <tr><td> Count the number of ping producers and produce multiplier for scaling up messages expected over topic pings. + * </table> + */ +public class PingClient extends PingPongProducer +{ + /** Used for debugging. */ + private final Logger log = Logger.getLogger(PingClient.class); + + /** Used to count the number of ping clients created. */ + private static int _pingClientCount; + + /** + * Creates a ping producer with the specified parameters, of which there are many. See the class level comments + * for {@link PingPongProducer} for details. This constructor creates a connection to the broker and creates + * producer and consumer sessions on it, to send and recieve its pings and replies on. + * + * @param overrides Properties containing any desired overrides to the defaults. + * + * @throws Exception Any exceptions are allowed to fall through. + */ + public PingClient(Properties overrides) throws Exception + { + super(overrides); + + _pingClientCount++; + } + + /** + * Returns the ping destinations themselves as the reply destinations for this pinger to listen to. This has the + * effect of making this pinger listen to its own pings. + * + * @return The ping destinations. + */ + public List<Destination> getReplyDestinations() + { + return _pingDestinations; + } + + /** + * Supplies the multiplier for the number of ping clients that will hear each ping when doing pub/sub pinging. + * + * @return The scaling up of the number of expected pub/sub pings. + */ + public int getConsumersPerDestination() + { + log.debug("public int getConsumersPerDestination(): called"); + + if (_isUnique) + { + log.debug(_noOfConsumers + " consumer per destination."); + + return _noOfConsumers; + } + else + { + log.debug((_pingClientCount * _noOfConsumers) + " consumers per destination."); + + return _pingClientCount * _noOfConsumers; + } + } +} diff --git a/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java b/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java index 0c8c19243a..a15897c82b 100644 --- a/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java +++ b/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java @@ -1,452 +1,452 @@ -/*
- *
- * 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.ping;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.requestreply.PingPongProducer;
-import org.apache.qpid.util.CommandLineParser;
-
-import org.apache.qpid.junit.extensions.util.MathUtils;
-import org.apache.qpid.junit.extensions.util.ParsedProperties;
-
-import javax.jms.*;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.List;
-import java.util.Properties;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * PingDurableClient is a variation of the {@link PingPongProducer} ping tool. Instead of sending its pings and
- * receiving replies to them at the same time, this tool sends pings until it is signalled by some 'event' to stop
- * sending. It then waits for another signal before it re-opens a fresh connection and attempts to receive all of the
- * pings that it has succesfully sent. It is intended to be an interactive test that lets a user experiment with
- * failure conditions when using durable messaging.
- *
- * <p/>The events that can stop it from sending are input from the user on the console, failure of its connection to
- * the broker, completion of sending a specified number of messages, or expiry of a specified duration. In all cases
- * it will do its best to clean up and close the connection before opening a fresh connection to receive the pings
- * with.
- *
- * <p/>The event to re-connect and attempt to recieve the pings is input from the user on the console.
- *
- * <p/>This ping client inherits the configuration properties of its parent class ({@link PingPongProducer}) and
- * additionally accepts the following parameters:
- *
- * <p/><table><caption>Parameters</caption>
- * <tr><th> Parameter <th> Default <th> Comments
- * <tr><td> numMessages <td> 100 <td> The total number of messages to send.
- * <tr><td> numMessagesToAction <td> -1 <td> The number of messages to send before taking a custom 'action'.
- * <tr><td> duration <td> 30S <td> The length of time to ping for. (Format dDhHmMsS, for d days, h hours,
- * m minutes and s seconds).
- * </table>
- *
- * <p/>This ping client also overrides some of the defaults of its parent class, to provide a reasonable set up
- * when no parameters are specified.
- *
- * <p/><table><caption>Parameters</caption>
- * <tr><th> Parameter <th> Default <th> Comments
- * <tr><td> uniqueDests <td> false <td> Prevents destination names being timestamped.
- * <tr><td> transacted <td> true <td> Only makes sense to test with transactions.
- * <tr><td> persistent <td> true <td> Only makes sense to test persistent.
- * <tr><td> durableDests <td> true <td> Should use durable queues with persistent messages.
- * <tr><td> commitBatchSize <td> 10
- * <tr><td> rate <td> 20 <td> Total default test time is 5 seconds.
- * </table>
- *
- * <p/>When a number of messages or duration is specified, this ping client will ping until the first of those limits
- * is reached. Reaching the limit will be interpreted as the first signal to stop sending, and the ping client will
- * wait for the second signal before receiving its pings.
- *
- * <p/>This class provides a mechanism for extensions to add arbitrary actions, after a particular number of messages
- * have been sent. When the number of messages equal the value set in the 'numMessagesToAction' property is method,
- * the {@link #takeAction} method is called. By default this does nothing, but extensions of this class can provide
- * custom behaviour with alternative implementations of this method (for example taking a backup).
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Send and receive pings.
- * <tr><td> Accept user input to signal stop sending.
- * <tr><td> Accept user input to signal start receiving.
- * <tr><td> Provide feedback on pings sent versus pings received.
- * <tr><td> Provide extension point for arbitrary action on a particular message count.
- * </table>
- */
-public class PingDurableClient extends PingPongProducer implements ExceptionListener
-{
- private static final Logger log = Logger.getLogger(PingDurableClient.class);
-
- public static final String NUM_MESSAGES_PROPNAME = "numMessages";
- public static final String NUM_MESSAGES_DEFAULT = "100";
- public static final String DURATION_PROPNAME = "duration";
- public static final String DURATION_DEFAULT = "30S";
- public static final String NUM_MESSAGES_TO_ACTION_PROPNAME = "numMessagesToAction";
- public static final String NUM_MESSAGES_TO_ACTION_DEFAULT = "-1";
-
- /** The maximum length of time to wait whilst receiving pings before assuming that no more are coming. */
- private static final long TIME_OUT = 3000;
-
- static
- {
- defaults.setProperty(NUM_MESSAGES_PROPNAME, NUM_MESSAGES_DEFAULT);
- defaults.setProperty(DURATION_PROPNAME, DURATION_DEFAULT);
- defaults.setProperty(UNIQUE_DESTS_PROPNAME, "false");
- defaults.setProperty(TRANSACTED_PROPNAME, "true");
- defaults.setProperty(PERSISTENT_MODE_PROPNAME, "true");
- defaults.setProperty(TX_BATCH_SIZE_PROPNAME, "10");
- defaults.setProperty(RATE_PROPNAME, "20");
- defaults.setProperty(DURABLE_DESTS_PROPNAME, "true");
- defaults.setProperty(NUM_MESSAGES_TO_ACTION_PROPNAME, NUM_MESSAGES_TO_ACTION_DEFAULT);
- }
-
- /** Specifies the number of pings to send, if larger than 0. 0 means send until told to stop. */
- private int numMessages;
-
- /** Holds the number of messages to send before taking triggering the action. */
- private int numMessagesToAction;
-
- /** Sepcifies how long to ping for, if larger than 0. 0 means send until told to stop. */
- private long duration;
-
- /** Used to indciate that this application should terminate. Set by the shutdown hook. */
- private boolean terminate = false;
-
- /**
- * @throws Exception Any exceptions are allowed to fall through.
- */
- public PingDurableClient(Properties overrides) throws Exception
- {
- super(overrides);
- log.debug("public PingDurableClient(Properties overrides = " + overrides + "): called");
-
- // Extract the additional configuration parameters.
- ParsedProperties properties = new ParsedProperties(defaults);
- properties.putAll(overrides);
-
- numMessages = properties.getPropertyAsInteger(NUM_MESSAGES_PROPNAME);
- String durationSpec = properties.getProperty(DURATION_PROPNAME);
- numMessagesToAction = properties.getPropertyAsInteger(NUM_MESSAGES_TO_ACTION_PROPNAME);
-
- if (durationSpec != null)
- {
- duration = MathUtils.parseDuration(durationSpec) * 1000000;
- }
- }
-
- /**
- * Starts the ping/wait/receive process.
- *
- * @param args The command line arguments.
- */
- public static void main(String[] args)
- {
- try
- {
- // Create a ping producer overriding its defaults with all options passed on the command line.
- Properties options =
- CommandLineParser.processCommandLine(args, new CommandLineParser(new String[][] {}), System.getProperties());
- PingDurableClient pingProducer = new PingDurableClient(options);
-
- // Create a shutdown hook to terminate the ping-pong producer.
- Runtime.getRuntime().addShutdownHook(pingProducer.getShutdownHook());
-
- // Ensure that the ping pong producer is registered to listen for exceptions on the connection too.
- // pingProducer.getConnection().setExceptionListener(pingProducer);
-
- // Run the test procedure.
- int sent = pingProducer.send();
- pingProducer.closeConnection();
- pingProducer.waitForUser("Press return to begin receiving the pings.");
- pingProducer.receive(sent);
-
- System.exit(0);
- }
- catch (Exception e)
- {
- System.err.println(e.getMessage());
- log.error("Top level handler caught execption.", e);
- System.exit(1);
- }
- }
-
- /**
- * Performs the main test procedure implemented by this ping client. See the class level comment for details.
- */
- protected int send() throws Exception
- {
- log.debug("public void sendWaitReceive(): called");
-
- log.debug("duration = " + duration);
- log.debug("numMessages = " + numMessages);
-
- if (duration > 0)
- {
- System.out.println("Sending for up to " + (duration / 1000000000f) + " seconds.");
- }
-
- if (_rate > 0)
- {
- System.out.println("Sending at " + _rate + " messages per second.");
- }
-
- if (numMessages > 0)
- {
- System.out.println("Sending up to " + numMessages + " messages.");
- }
-
- // Establish the connection and the message producer.
- establishConnection(true, false);
- _connection.start();
-
- Message message = getTestMessage(getReplyDestinations().get(0), _messageSize, _persistent);
-
- // Send pings until a terminating condition is received.
- boolean endCondition = false;
- int messagesSent = 0;
- int messagesCommitted = 0;
- int messagesNotCommitted = 0;
- long start = System.nanoTime();
-
- // Clear console in.
- clearConsole();
-
- while (!endCondition)
- {
- boolean committed = false;
-
- try
- {
- committed = sendMessage(messagesSent, message) && _transacted;
-
- messagesSent++;
- messagesNotCommitted++;
-
- // Keep count of the number of messsages currently committed and pending commit.
- if (committed)
- {
- log.debug("Adding " + messagesNotCommitted + " messages to the committed count.");
- messagesCommitted += messagesNotCommitted;
- messagesNotCommitted = 0;
-
- System.out.println("Commited: " + messagesCommitted);
- }
- }
- catch (JMSException e)
- {
- log.debug("Got JMSException whilst sending.");
- _publish = false;
- }
-
- // Perform the arbitrary action if the number of messages sent has reached the right number.
- if (messagesSent == numMessagesToAction)
- {
- System.out.println("At action point, Messages sent = " + messagesSent + ", Messages Committed = "
- + messagesCommitted + ", Messages not Committed = " + messagesNotCommitted);
- takeAction();
- }
-
- // Determine if the end condition has been met, based on the number of messages, time passed, errors on
- // the connection or user input.
- long now = System.nanoTime();
-
- if ((duration != 0) && ((now - start) > duration))
- {
- System.out.println("Send halted because duration expired.");
- endCondition = true;
- }
- else if ((numMessages != 0) && (messagesSent >= numMessages))
- {
- System.out.println("Send halted because # messages completed.");
- endCondition = true;
- }
- else if (System.in.available() > 0)
- {
- System.out.println("Send halted by user input.");
- endCondition = true;
-
- clearConsole();
- }
- else if (!_publish)
- {
- System.out.println("Send halted by error on the connection.");
- endCondition = true;
- }
- }
-
- log.debug("messagesSent = " + messagesSent);
- log.debug("messagesCommitted = " + messagesCommitted);
- log.debug("messagesNotCommitted = " + messagesNotCommitted);
-
- System.out.println("Messages sent: " + messagesSent + ", Messages Committed = " + messagesCommitted
- + ", Messages not Committed = " + messagesNotCommitted);
-
- return messagesSent;
- }
-
- protected void closeConnection()
- {
- // Clean up the connection.
- try
- {
- close();
- }
- catch (JMSException e)
- {
- log.debug("There was an error whilst closing the connection: " + e, e);
- System.out.println("There was an error whilst closing the connection.");
-
- // Ignore as did best could manage to clean up.
- }
- }
-
- protected void receive(int messagesSent) throws Exception
- {
- // Re-establish the connection and the message consumer.
- _queueJVMSequenceID = new AtomicInteger();
- _queueSharedID = new AtomicInteger();
-
- establishConnection(false, true);
- _consumer[0].setMessageListener(null);
- _consumerConnection[0].start();
-
- // Try to receive all of the pings that were successfully sent.
- int messagesReceived = 0;
- boolean endCondition = false;
-
- while (!endCondition)
- {
- // Message received = _consumer.receiveNoWait();
- Message received = _consumer[0].receive(TIME_OUT);
- log.debug("received = " + received);
-
- if (received != null)
- {
- messagesReceived++;
- }
-
- // Determine if the end condition has been met, based on the number of messages and time passed since last
- // receiving a message.
- if (received == null)
- {
- System.out.println("Timed out.");
- endCondition = true;
- }
- else if (messagesReceived >= messagesSent)
- {
- System.out.println("Got all messages.");
- endCondition = true;
- }
- }
-
- // Ensure messages received are committed.
- if (_consTransacted)
- {
- try
- {
- _consumerSession[0].commit();
- System.out.println("Committed for all messages received.");
- }
- catch (JMSException e)
- {
- log.debug("Error during commit: " + e, e);
- System.out.println("Error during commit.");
- try
- {
- _consumerSession[0].rollback();
- System.out.println("Rolled back on all messages received.");
- }
- catch (JMSException e2)
- {
- log.debug("Error during rollback: " + e, e);
- System.out.println("Error on roll back of all messages received.");
- }
-
- }
- }
-
- log.debug("messagesReceived = " + messagesReceived);
-
- System.out.println("Messages received: " + messagesReceived);
-
- // Clean up the connection.
- close();
- }
-
- /**
- * Clears any pending input from the console.
- */
- private void clearConsole()
- {
- try
- {
- BufferedReader bis = new BufferedReader(new InputStreamReader(System.in));
-
- // System.in.skip(System.in.available());
- while (bis.ready())
- {
- bis.readLine();
- }
- }
- catch (IOException e)
- { }
- }
-
- /**
- * Returns the ping destinations themselves as the reply destinations for this pinger to listen to. This has the
- * effect of making this pinger listen to its own pings.
- *
- * @return The ping destinations.
- */
- public List<Destination> getReplyDestinations()
- {
- return _pingDestinations;
- }
-
- /**
- * Gets a shutdown hook that will cleanly shut this down when it is running the ping loop. This can be registered with
- * the runtime system as a shutdown hook. This shutdown hook sets an additional terminate flag, compared with the
- * shutdown hook in {@link PingPongProducer}, because the publish flag is used to indicate that sending or receiving
- * message should stop, not that the application should termiante.
- *
- * @return A shutdown hook for the ping loop.
- */
- public Thread getShutdownHook()
- {
- return new Thread(new Runnable()
- {
- public void run()
- {
- stop();
- terminate = true;
- }
- });
- }
-
- /**
- * Performs an aribtrary action once the 'numMesagesToAction' count is reached on sending messages. This default
- * implementation does nothing.
- */
- public void takeAction()
- { }
-}
+/* + * + * 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.ping; + +import org.apache.log4j.Logger; + +import org.apache.qpid.requestreply.PingPongProducer; +import org.apache.qpid.util.CommandLineParser; + +import org.apache.qpid.junit.extensions.util.MathUtils; +import org.apache.qpid.junit.extensions.util.ParsedProperties; + +import javax.jms.*; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * PingDurableClient is a variation of the {@link PingPongProducer} ping tool. Instead of sending its pings and + * receiving replies to them at the same time, this tool sends pings until it is signalled by some 'event' to stop + * sending. It then waits for another signal before it re-opens a fresh connection and attempts to receive all of the + * pings that it has succesfully sent. It is intended to be an interactive test that lets a user experiment with + * failure conditions when using durable messaging. + * + * <p/>The events that can stop it from sending are input from the user on the console, failure of its connection to + * the broker, completion of sending a specified number of messages, or expiry of a specified duration. In all cases + * it will do its best to clean up and close the connection before opening a fresh connection to receive the pings + * with. + * + * <p/>The event to re-connect and attempt to recieve the pings is input from the user on the console. + * + * <p/>This ping client inherits the configuration properties of its parent class ({@link PingPongProducer}) and + * additionally accepts the following parameters: + * + * <p/><table><caption>Parameters</caption> + * <tr><th> Parameter <th> Default <th> Comments + * <tr><td> numMessages <td> 100 <td> The total number of messages to send. + * <tr><td> numMessagesToAction <td> -1 <td> The number of messages to send before taking a custom 'action'. + * <tr><td> duration <td> 30S <td> The length of time to ping for. (Format dDhHmMsS, for d days, h hours, + * m minutes and s seconds). + * </table> + * + * <p/>This ping client also overrides some of the defaults of its parent class, to provide a reasonable set up + * when no parameters are specified. + * + * <p/><table><caption>Parameters</caption> + * <tr><th> Parameter <th> Default <th> Comments + * <tr><td> uniqueDests <td> false <td> Prevents destination names being timestamped. + * <tr><td> transacted <td> true <td> Only makes sense to test with transactions. + * <tr><td> persistent <td> true <td> Only makes sense to test persistent. + * <tr><td> durableDests <td> true <td> Should use durable queues with persistent messages. + * <tr><td> commitBatchSize <td> 10 + * <tr><td> rate <td> 20 <td> Total default test time is 5 seconds. + * </table> + * + * <p/>When a number of messages or duration is specified, this ping client will ping until the first of those limits + * is reached. Reaching the limit will be interpreted as the first signal to stop sending, and the ping client will + * wait for the second signal before receiving its pings. + * + * <p/>This class provides a mechanism for extensions to add arbitrary actions, after a particular number of messages + * have been sent. When the number of messages equal the value set in the 'numMessagesToAction' property is method, + * the {@link #takeAction} method is called. By default this does nothing, but extensions of this class can provide + * custom behaviour with alternative implementations of this method (for example taking a backup). + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Send and receive pings. + * <tr><td> Accept user input to signal stop sending. + * <tr><td> Accept user input to signal start receiving. + * <tr><td> Provide feedback on pings sent versus pings received. + * <tr><td> Provide extension point for arbitrary action on a particular message count. + * </table> + */ +public class PingDurableClient extends PingPongProducer implements ExceptionListener +{ + private static final Logger log = Logger.getLogger(PingDurableClient.class); + + public static final String NUM_MESSAGES_PROPNAME = "numMessages"; + public static final String NUM_MESSAGES_DEFAULT = "100"; + public static final String DURATION_PROPNAME = "duration"; + public static final String DURATION_DEFAULT = "30S"; + public static final String NUM_MESSAGES_TO_ACTION_PROPNAME = "numMessagesToAction"; + public static final String NUM_MESSAGES_TO_ACTION_DEFAULT = "-1"; + + /** The maximum length of time to wait whilst receiving pings before assuming that no more are coming. */ + private static final long TIME_OUT = 3000; + + static + { + defaults.setProperty(NUM_MESSAGES_PROPNAME, NUM_MESSAGES_DEFAULT); + defaults.setProperty(DURATION_PROPNAME, DURATION_DEFAULT); + defaults.setProperty(UNIQUE_DESTS_PROPNAME, "false"); + defaults.setProperty(TRANSACTED_PROPNAME, "true"); + defaults.setProperty(PERSISTENT_MODE_PROPNAME, "true"); + defaults.setProperty(TX_BATCH_SIZE_PROPNAME, "10"); + defaults.setProperty(RATE_PROPNAME, "20"); + defaults.setProperty(DURABLE_DESTS_PROPNAME, "true"); + defaults.setProperty(NUM_MESSAGES_TO_ACTION_PROPNAME, NUM_MESSAGES_TO_ACTION_DEFAULT); + } + + /** Specifies the number of pings to send, if larger than 0. 0 means send until told to stop. */ + private int numMessages; + + /** Holds the number of messages to send before taking triggering the action. */ + private int numMessagesToAction; + + /** Sepcifies how long to ping for, if larger than 0. 0 means send until told to stop. */ + private long duration; + + /** Used to indciate that this application should terminate. Set by the shutdown hook. */ + private boolean terminate = false; + + /** + * @throws Exception Any exceptions are allowed to fall through. + */ + public PingDurableClient(Properties overrides) throws Exception + { + super(overrides); + log.debug("public PingDurableClient(Properties overrides = " + overrides + "): called"); + + // Extract the additional configuration parameters. + ParsedProperties properties = new ParsedProperties(defaults); + properties.putAll(overrides); + + numMessages = properties.getPropertyAsInteger(NUM_MESSAGES_PROPNAME); + String durationSpec = properties.getProperty(DURATION_PROPNAME); + numMessagesToAction = properties.getPropertyAsInteger(NUM_MESSAGES_TO_ACTION_PROPNAME); + + if (durationSpec != null) + { + duration = MathUtils.parseDuration(durationSpec) * 1000000; + } + } + + /** + * Starts the ping/wait/receive process. + * + * @param args The command line arguments. + */ + public static void main(String[] args) + { + try + { + // Create a ping producer overriding its defaults with all options passed on the command line. + Properties options = + CommandLineParser.processCommandLine(args, new CommandLineParser(new String[][] {}), System.getProperties()); + PingDurableClient pingProducer = new PingDurableClient(options); + + // Create a shutdown hook to terminate the ping-pong producer. + Runtime.getRuntime().addShutdownHook(pingProducer.getShutdownHook()); + + // Ensure that the ping pong producer is registered to listen for exceptions on the connection too. + // pingProducer.getConnection().setExceptionListener(pingProducer); + + // Run the test procedure. + int sent = pingProducer.send(); + pingProducer.closeConnection(); + pingProducer.waitForUser("Press return to begin receiving the pings."); + pingProducer.receive(sent); + + System.exit(0); + } + catch (Exception e) + { + System.err.println(e.getMessage()); + log.error("Top level handler caught execption.", e); + System.exit(1); + } + } + + /** + * Performs the main test procedure implemented by this ping client. See the class level comment for details. + */ + protected int send() throws Exception + { + log.debug("public void sendWaitReceive(): called"); + + log.debug("duration = " + duration); + log.debug("numMessages = " + numMessages); + + if (duration > 0) + { + System.out.println("Sending for up to " + (duration / 1000000000f) + " seconds."); + } + + if (_rate > 0) + { + System.out.println("Sending at " + _rate + " messages per second."); + } + + if (numMessages > 0) + { + System.out.println("Sending up to " + numMessages + " messages."); + } + + // Establish the connection and the message producer. + establishConnection(true, false); + _connection.start(); + + Message message = getTestMessage(getReplyDestinations().get(0), _messageSize, _persistent); + + // Send pings until a terminating condition is received. + boolean endCondition = false; + int messagesSent = 0; + int messagesCommitted = 0; + int messagesNotCommitted = 0; + long start = System.nanoTime(); + + // Clear console in. + clearConsole(); + + while (!endCondition) + { + boolean committed = false; + + try + { + committed = sendMessage(messagesSent, message) && _transacted; + + messagesSent++; + messagesNotCommitted++; + + // Keep count of the number of messsages currently committed and pending commit. + if (committed) + { + log.debug("Adding " + messagesNotCommitted + " messages to the committed count."); + messagesCommitted += messagesNotCommitted; + messagesNotCommitted = 0; + + System.out.println("Commited: " + messagesCommitted); + } + } + catch (JMSException e) + { + log.debug("Got JMSException whilst sending."); + _publish = false; + } + + // Perform the arbitrary action if the number of messages sent has reached the right number. + if (messagesSent == numMessagesToAction) + { + System.out.println("At action point, Messages sent = " + messagesSent + ", Messages Committed = " + + messagesCommitted + ", Messages not Committed = " + messagesNotCommitted); + takeAction(); + } + + // Determine if the end condition has been met, based on the number of messages, time passed, errors on + // the connection or user input. + long now = System.nanoTime(); + + if ((duration != 0) && ((now - start) > duration)) + { + System.out.println("Send halted because duration expired."); + endCondition = true; + } + else if ((numMessages != 0) && (messagesSent >= numMessages)) + { + System.out.println("Send halted because # messages completed."); + endCondition = true; + } + else if (System.in.available() > 0) + { + System.out.println("Send halted by user input."); + endCondition = true; + + clearConsole(); + } + else if (!_publish) + { + System.out.println("Send halted by error on the connection."); + endCondition = true; + } + } + + log.debug("messagesSent = " + messagesSent); + log.debug("messagesCommitted = " + messagesCommitted); + log.debug("messagesNotCommitted = " + messagesNotCommitted); + + System.out.println("Messages sent: " + messagesSent + ", Messages Committed = " + messagesCommitted + + ", Messages not Committed = " + messagesNotCommitted); + + return messagesSent; + } + + protected void closeConnection() + { + // Clean up the connection. + try + { + close(); + } + catch (JMSException e) + { + log.debug("There was an error whilst closing the connection: " + e, e); + System.out.println("There was an error whilst closing the connection."); + + // Ignore as did best could manage to clean up. + } + } + + protected void receive(int messagesSent) throws Exception + { + // Re-establish the connection and the message consumer. + _queueJVMSequenceID = new AtomicInteger(); + _queueSharedID = new AtomicInteger(); + + establishConnection(false, true); + _consumer[0].setMessageListener(null); + _consumerConnection[0].start(); + + // Try to receive all of the pings that were successfully sent. + int messagesReceived = 0; + boolean endCondition = false; + + while (!endCondition) + { + // Message received = _consumer.receiveNoWait(); + Message received = _consumer[0].receive(TIME_OUT); + log.debug("received = " + received); + + if (received != null) + { + messagesReceived++; + } + + // Determine if the end condition has been met, based on the number of messages and time passed since last + // receiving a message. + if (received == null) + { + System.out.println("Timed out."); + endCondition = true; + } + else if (messagesReceived >= messagesSent) + { + System.out.println("Got all messages."); + endCondition = true; + } + } + + // Ensure messages received are committed. + if (_consTransacted) + { + try + { + _consumerSession[0].commit(); + System.out.println("Committed for all messages received."); + } + catch (JMSException e) + { + log.debug("Error during commit: " + e, e); + System.out.println("Error during commit."); + try + { + _consumerSession[0].rollback(); + System.out.println("Rolled back on all messages received."); + } + catch (JMSException e2) + { + log.debug("Error during rollback: " + e, e); + System.out.println("Error on roll back of all messages received."); + } + + } + } + + log.debug("messagesReceived = " + messagesReceived); + + System.out.println("Messages received: " + messagesReceived); + + // Clean up the connection. + close(); + } + + /** + * Clears any pending input from the console. + */ + private void clearConsole() + { + try + { + BufferedReader bis = new BufferedReader(new InputStreamReader(System.in)); + + // System.in.skip(System.in.available()); + while (bis.ready()) + { + bis.readLine(); + } + } + catch (IOException e) + { } + } + + /** + * Returns the ping destinations themselves as the reply destinations for this pinger to listen to. This has the + * effect of making this pinger listen to its own pings. + * + * @return The ping destinations. + */ + public List<Destination> getReplyDestinations() + { + return _pingDestinations; + } + + /** + * Gets a shutdown hook that will cleanly shut this down when it is running the ping loop. This can be registered with + * the runtime system as a shutdown hook. This shutdown hook sets an additional terminate flag, compared with the + * shutdown hook in {@link PingPongProducer}, because the publish flag is used to indicate that sending or receiving + * message should stop, not that the application should termiante. + * + * @return A shutdown hook for the ping loop. + */ + public Thread getShutdownHook() + { + return new Thread(new Runnable() + { + public void run() + { + stop(); + terminate = true; + } + }); + } + + /** + * Performs an aribtrary action once the 'numMesagesToAction' count is reached on sending messages. This default + * implementation does nothing. + */ + public void takeAction() + { } +} diff --git a/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java b/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java index 215dbcefa3..5ba4004c56 100644 --- a/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java +++ b/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java @@ -1,311 +1,311 @@ -/*
- *
- * 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.ping;
-
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.client.AMQSession;
-import org.apache.qpid.requestreply.PingPongProducer;
-
-import org.apache.qpid.junit.extensions.TimingController;
-import org.apache.qpid.junit.extensions.TimingControllerAware;
-import org.apache.qpid.junit.extensions.util.ParsedProperties;
-
-import javax.jms.JMSException;
-import javax.jms.Message;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicLong;
-
-/**
- * PingLatencyTestPerf is a performance test that outputs multiple timings from its test method, using the timing
- * controller interface supplied by the test runner from a seperate listener thread. It outputs round trip timings for
- * individual ping messages rather than for how long a complete batch of messages took to process. It also differs from
- * the {@link PingTestPerf} test that it extends because it can output timings as replies are received, rather than
- * waiting until all expected replies are received.
- *
- * <p/>This test does not output timings for every single ping message, as when running at high volume, writing the test
- * log for a vast number of messages would slow the testing down. Instead samples ping latency occasionally. The
- * frequency of ping sampling is set using the {@link #TEST_RESULTS_BATCH_SIZE_PROPNAME} property, to override the
- * default of every {@link #DEFAULT_TEST_RESULTS_BATCH_SIZE}.
- *
- * <p/>The size parameter logged for each individual ping is set to the size of the batch of messages that the
- * individual timed ping was taken from, rather than 1 for a single message. This is so that the total throughput
- * (messages / time) can be calculated in order to examine the relationship between throughput and latency.
- *
- * <p/><table id="crc"><caption>CRC Card</caption> <tr><td> Responsibilities <th> Collaborations <tr><td> Send many ping
- * messages and output timings for sampled individual pings. </table>
- */
-public class PingLatencyTestPerf extends PingTestPerf implements TimingControllerAware
-{
- private static Logger _logger = Logger.getLogger(PingLatencyTestPerf.class);
-
- /** Holds the name of the property to get the test results logging batch size. */
- public static final String TEST_RESULTS_BATCH_SIZE_PROPNAME = "batchSize";
-
- /** Holds the default test results logging batch size. */
- public static final int DEFAULT_TEST_RESULTS_BATCH_SIZE = 1000;
-
- /** Used to hold the timing controller passed from the test runner. */
- private TimingController _timingController;
-
- /** Used to generate unique correlation ids for each test run. */
- private AtomicLong corellationIdGenerator = new AtomicLong();
-
- /**
- * Holds test specifics by correlation id. This consists of the expected number of messages and the timing
- * controler.
- */
- private Map<String, PerCorrelationId> perCorrelationIds =
- Collections.synchronizedMap(new HashMap<String, PerCorrelationId>());
-
- /** Holds the batched results listener, that does logging on batch boundaries. */
- private BatchedResultsListener batchedResultsListener = null;
-
- /**
- * Creates a new asynchronous ping performance test with the specified name.
- *
- * @param name The test name.
- */
- public PingLatencyTestPerf(String name)
- {
- super(name);
-
- // Sets up the test parameters with defaults.
- ParsedProperties.setSysPropertyIfNull(TEST_RESULTS_BATCH_SIZE_PROPNAME,
- Integer.toString(DEFAULT_TEST_RESULTS_BATCH_SIZE));
- }
-
- /** Compile all the tests into a test suite. */
- public static Test suite()
- {
- // Build a new test suite
- TestSuite suite = new TestSuite("Ping Latency Tests");
-
- // Run performance tests in read committed mode.
- suite.addTest(new PingLatencyTestPerf("testPingLatency"));
-
- return suite;
- }
-
- /**
- * Accepts a timing controller from the test runner.
- *
- * @param timingController The timing controller to register mutliple timings with.
- */
- public void setTimingController(TimingController timingController)
- {
- _timingController = timingController;
- }
-
- /**
- * Gets the timing controller passed in by the test runner.
- *
- * @return The timing controller passed in by the test runner.
- */
- public TimingController getTimingController()
- {
- return _timingController;
- }
-
- /**
- * Sends the specified number of pings, asynchronously outputs timings on every batch boundary, and waits until all
- * replies have been received or a time out occurs before exiting this method.
- *
- * @param numPings The number of pings to send.
- */
- public void testPingLatency(int numPings) throws Exception
- {
- _logger.debug("public void testPingLatency(int numPings): called");
-
- // Ensure that at least one ping was requeusted.
- if (numPings == 0)
- {
- _logger.error("Number of pings requested was zero.");
- }
-
- // Get the per thread test setup to run the test through.
- PerThreadSetup perThreadSetup = threadSetup.get();
- PingClient pingClient = perThreadSetup._pingClient;
-
- // Advance the correlation id of messages to send, to make it unique for this run.
- String messageCorrelationId = Long.toString(corellationIdGenerator.incrementAndGet());
- _logger.debug("messageCorrelationId = " + messageCorrelationId);
-
- // Initialize the count and timing controller for the new correlation id.
- PerCorrelationId perCorrelationId = new PerCorrelationId();
- TimingController tc = getTimingController().getControllerForCurrentThread();
- perCorrelationId._tc = tc;
- perCorrelationId._expectedCount = numPings;
- perCorrelationIds.put(messageCorrelationId, perCorrelationId);
-
- // Attach the chained message listener to the ping producer to listen asynchronously for the replies to these
- // messages.
- pingClient.setChainedMessageListener(batchedResultsListener);
-
- // Generate a sample message of the specified size.
- Message msg =
- pingClient.getTestMessage(perThreadSetup._pingClient.getReplyDestinations().get(0),
- testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME),
- testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME));
-
- // Send the requested number of messages, and wait until they have all been received.
- long timeout = Long.parseLong(testParameters.getProperty(PingPongProducer.TIMEOUT_PROPNAME));
- int numReplies = pingClient.pingAndWaitForReply(msg, numPings, timeout, null);
-
- // Check that all the replies were received and log a fail if they were not.
- if (numReplies < numPings)
- {
- tc.completeTest(false, 0);
- }
-
- // Remove the chained message listener from the ping producer.
- pingClient.removeChainedMessageListener();
-
- // Remove the expected count and timing controller for the message correlation id, to ensure they are cleaned up.
- perCorrelationIds.remove(messageCorrelationId);
- }
-
- /** Performs test fixture creation on a per thread basis. This will only be called once for each test thread. */
- public void threadSetUp()
- {
- _logger.debug("public void threadSetUp(): called");
-
- try
- {
- // Call the set up method in the super class. This creates a PingClient pinger.
- super.threadSetUp();
-
- // Create the chained message listener, only if it has not already been created. This is set up with the
- // batch size property, to tell it what batch size to output results on. A synchronized block is used to
- // ensure that only one thread creates this.
- synchronized (this)
- {
- if (batchedResultsListener == null)
- {
- int batchSize = Integer.parseInt(testParameters.getProperty(TEST_RESULTS_BATCH_SIZE_PROPNAME));
- batchedResultsListener = new BatchedResultsListener(batchSize);
- }
- }
-
- // Get the set up that the super class created.
- PerThreadSetup perThreadSetup = threadSetup.get();
-
- // Register the chained message listener on the pinger to do its asynchronous test timings from.
- perThreadSetup._pingClient.setChainedMessageListener(batchedResultsListener);
- }
- catch (Exception e)
- {
- _logger.warn("There was an exception during per thread setup.", e);
- }
- }
-
- /**
- * BatchedResultsListener is a {@link org.apache.qpid.requestreply.PingPongProducer.ChainedMessageListener} that can
- * be attached to the pinger, in order to receive notifications about every message received and the number
- * remaining to be received. Whenever the number remaining crosses a batch size boundary this results listener
- * outputs a test timing for the actual number of messages received in the current batch.
- */
- private class BatchedResultsListener implements PingPongProducer.ChainedMessageListener
- {
- /** The test results logging batch size. */
- int _batchSize;
- private boolean _strictAMQP;
-
- /**
- * Creates a results listener on the specified batch size.
- *
- * @param batchSize The batch size to use.
- */
- public BatchedResultsListener(int batchSize)
- {
- _batchSize = batchSize;
- _strictAMQP =
- Boolean.parseBoolean(System.getProperties().getProperty(AMQSession.STRICT_AMQP,
- AMQSession.STRICT_AMQP_DEFAULT));
- }
-
- /**
- * This callback method is called from all of the pingers that this test creates. It uses the correlation id
- * from the message to identify the timing controller for the test thread that was responsible for sending those
- * messages.
- *
- * @param message The message.
- * @param remainingCount The count of messages remaining to be received with a particular correlation id.
- *
- * @throws javax.jms.JMSException Any underlying JMSException is allowed to fall through.
- */
- public void onMessage(Message message, int remainingCount, long latency) throws JMSException
- {
- _logger.debug("public void onMessage(Message message, int remainingCount = " + remainingCount + "): called");
-
- // Check if a batch boundary has been crossed.
- if ((remainingCount % _batchSize) == 0)
- {
- // Extract the correlation id from the message.
- String correlationId = message.getJMSCorrelationID();
-
- // Get the details for the correlation id and check that they are not null. They can become null
- // if a test times out.
- PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationId);
- if (perCorrelationId != null)
- {
- // Get the timing controller and expected count for this correlation id.
- TimingController tc = perCorrelationId._tc;
- int expected = perCorrelationId._expectedCount;
-
- // Calculate how many messages were actually received in the last batch. This will be the batch size
- // except where the number expected is not a multiple of the batch size and this is the first remaining
- // count to cross a batch size boundary, in which case it will be the number expected modulo the batch
- // size.
- int receivedInBatch = ((expected - remainingCount) < _batchSize) ? (expected % _batchSize) : _batchSize;
-
- // Register a test result for the correlation id.
- try
- {
- tc.completeTest(true, receivedInBatch, latency);
- }
- catch (InterruptedException e)
- {
- // Ignore this. It means the test runner wants to stop as soon as possible.
- _logger.warn("Got InterruptedException.", e);
- }
- }
- // Else ignore, test timed out. Should log a fail here?
- }
- }
- }
-
- /**
- * Holds state specific to each correlation id, needed to output test results. This consists of the count of the
- * total expected number of messages, and the timing controller for the thread sending those message ids.
- */
- private static class PerCorrelationId
- {
- public int _expectedCount;
- public TimingController _tc;
- }
-}
+/* + * + * 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.ping; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.requestreply.PingPongProducer; + +import org.apache.qpid.junit.extensions.TimingController; +import org.apache.qpid.junit.extensions.TimingControllerAware; +import org.apache.qpid.junit.extensions.util.ParsedProperties; + +import javax.jms.JMSException; +import javax.jms.Message; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +/** + * PingLatencyTestPerf is a performance test that outputs multiple timings from its test method, using the timing + * controller interface supplied by the test runner from a seperate listener thread. It outputs round trip timings for + * individual ping messages rather than for how long a complete batch of messages took to process. It also differs from + * the {@link PingTestPerf} test that it extends because it can output timings as replies are received, rather than + * waiting until all expected replies are received. + * + * <p/>This test does not output timings for every single ping message, as when running at high volume, writing the test + * log for a vast number of messages would slow the testing down. Instead samples ping latency occasionally. The + * frequency of ping sampling is set using the {@link #TEST_RESULTS_BATCH_SIZE_PROPNAME} property, to override the + * default of every {@link #DEFAULT_TEST_RESULTS_BATCH_SIZE}. + * + * <p/>The size parameter logged for each individual ping is set to the size of the batch of messages that the + * individual timed ping was taken from, rather than 1 for a single message. This is so that the total throughput + * (messages / time) can be calculated in order to examine the relationship between throughput and latency. + * + * <p/><table id="crc"><caption>CRC Card</caption> <tr><td> Responsibilities <th> Collaborations <tr><td> Send many ping + * messages and output timings for sampled individual pings. </table> + */ +public class PingLatencyTestPerf extends PingTestPerf implements TimingControllerAware +{ + private static Logger _logger = Logger.getLogger(PingLatencyTestPerf.class); + + /** Holds the name of the property to get the test results logging batch size. */ + public static final String TEST_RESULTS_BATCH_SIZE_PROPNAME = "batchSize"; + + /** Holds the default test results logging batch size. */ + public static final int DEFAULT_TEST_RESULTS_BATCH_SIZE = 1000; + + /** Used to hold the timing controller passed from the test runner. */ + private TimingController _timingController; + + /** Used to generate unique correlation ids for each test run. */ + private AtomicLong corellationIdGenerator = new AtomicLong(); + + /** + * Holds test specifics by correlation id. This consists of the expected number of messages and the timing + * controler. + */ + private Map<String, PerCorrelationId> perCorrelationIds = + Collections.synchronizedMap(new HashMap<String, PerCorrelationId>()); + + /** Holds the batched results listener, that does logging on batch boundaries. */ + private BatchedResultsListener batchedResultsListener = null; + + /** + * Creates a new asynchronous ping performance test with the specified name. + * + * @param name The test name. + */ + public PingLatencyTestPerf(String name) + { + super(name); + + // Sets up the test parameters with defaults. + ParsedProperties.setSysPropertyIfNull(TEST_RESULTS_BATCH_SIZE_PROPNAME, + Integer.toString(DEFAULT_TEST_RESULTS_BATCH_SIZE)); + } + + /** Compile all the tests into a test suite. */ + public static Test suite() + { + // Build a new test suite + TestSuite suite = new TestSuite("Ping Latency Tests"); + + // Run performance tests in read committed mode. + suite.addTest(new PingLatencyTestPerf("testPingLatency")); + + return suite; + } + + /** + * Accepts a timing controller from the test runner. + * + * @param timingController The timing controller to register mutliple timings with. + */ + public void setTimingController(TimingController timingController) + { + _timingController = timingController; + } + + /** + * Gets the timing controller passed in by the test runner. + * + * @return The timing controller passed in by the test runner. + */ + public TimingController getTimingController() + { + return _timingController; + } + + /** + * Sends the specified number of pings, asynchronously outputs timings on every batch boundary, and waits until all + * replies have been received or a time out occurs before exiting this method. + * + * @param numPings The number of pings to send. + */ + public void testPingLatency(int numPings) throws Exception + { + _logger.debug("public void testPingLatency(int numPings): called"); + + // Ensure that at least one ping was requeusted. + if (numPings == 0) + { + _logger.error("Number of pings requested was zero."); + } + + // Get the per thread test setup to run the test through. + PerThreadSetup perThreadSetup = threadSetup.get(); + PingClient pingClient = perThreadSetup._pingClient; + + // Advance the correlation id of messages to send, to make it unique for this run. + String messageCorrelationId = Long.toString(corellationIdGenerator.incrementAndGet()); + _logger.debug("messageCorrelationId = " + messageCorrelationId); + + // Initialize the count and timing controller for the new correlation id. + PerCorrelationId perCorrelationId = new PerCorrelationId(); + TimingController tc = getTimingController().getControllerForCurrentThread(); + perCorrelationId._tc = tc; + perCorrelationId._expectedCount = numPings; + perCorrelationIds.put(messageCorrelationId, perCorrelationId); + + // Attach the chained message listener to the ping producer to listen asynchronously for the replies to these + // messages. + pingClient.setChainedMessageListener(batchedResultsListener); + + // Generate a sample message of the specified size. + Message msg = + pingClient.getTestMessage(perThreadSetup._pingClient.getReplyDestinations().get(0), + testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME), + testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME)); + + // Send the requested number of messages, and wait until they have all been received. + long timeout = Long.parseLong(testParameters.getProperty(PingPongProducer.TIMEOUT_PROPNAME)); + int numReplies = pingClient.pingAndWaitForReply(msg, numPings, timeout, null); + + // Check that all the replies were received and log a fail if they were not. + if (numReplies < numPings) + { + tc.completeTest(false, 0); + } + + // Remove the chained message listener from the ping producer. + pingClient.removeChainedMessageListener(); + + // Remove the expected count and timing controller for the message correlation id, to ensure they are cleaned up. + perCorrelationIds.remove(messageCorrelationId); + } + + /** Performs test fixture creation on a per thread basis. This will only be called once for each test thread. */ + public void threadSetUp() + { + _logger.debug("public void threadSetUp(): called"); + + try + { + // Call the set up method in the super class. This creates a PingClient pinger. + super.threadSetUp(); + + // Create the chained message listener, only if it has not already been created. This is set up with the + // batch size property, to tell it what batch size to output results on. A synchronized block is used to + // ensure that only one thread creates this. + synchronized (this) + { + if (batchedResultsListener == null) + { + int batchSize = Integer.parseInt(testParameters.getProperty(TEST_RESULTS_BATCH_SIZE_PROPNAME)); + batchedResultsListener = new BatchedResultsListener(batchSize); + } + } + + // Get the set up that the super class created. + PerThreadSetup perThreadSetup = threadSetup.get(); + + // Register the chained message listener on the pinger to do its asynchronous test timings from. + perThreadSetup._pingClient.setChainedMessageListener(batchedResultsListener); + } + catch (Exception e) + { + _logger.warn("There was an exception during per thread setup.", e); + } + } + + /** + * BatchedResultsListener is a {@link org.apache.qpid.requestreply.PingPongProducer.ChainedMessageListener} that can + * be attached to the pinger, in order to receive notifications about every message received and the number + * remaining to be received. Whenever the number remaining crosses a batch size boundary this results listener + * outputs a test timing for the actual number of messages received in the current batch. + */ + private class BatchedResultsListener implements PingPongProducer.ChainedMessageListener + { + /** The test results logging batch size. */ + int _batchSize; + private boolean _strictAMQP; + + /** + * Creates a results listener on the specified batch size. + * + * @param batchSize The batch size to use. + */ + public BatchedResultsListener(int batchSize) + { + _batchSize = batchSize; + _strictAMQP = + Boolean.parseBoolean(System.getProperties().getProperty(AMQSession.STRICT_AMQP, + AMQSession.STRICT_AMQP_DEFAULT)); + } + + /** + * This callback method is called from all of the pingers that this test creates. It uses the correlation id + * from the message to identify the timing controller for the test thread that was responsible for sending those + * messages. + * + * @param message The message. + * @param remainingCount The count of messages remaining to be received with a particular correlation id. + * + * @throws javax.jms.JMSException Any underlying JMSException is allowed to fall through. + */ + public void onMessage(Message message, int remainingCount, long latency) throws JMSException + { + _logger.debug("public void onMessage(Message message, int remainingCount = " + remainingCount + "): called"); + + // Check if a batch boundary has been crossed. + if ((remainingCount % _batchSize) == 0) + { + // Extract the correlation id from the message. + String correlationId = message.getJMSCorrelationID(); + + // Get the details for the correlation id and check that they are not null. They can become null + // if a test times out. + PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationId); + if (perCorrelationId != null) + { + // Get the timing controller and expected count for this correlation id. + TimingController tc = perCorrelationId._tc; + int expected = perCorrelationId._expectedCount; + + // Calculate how many messages were actually received in the last batch. This will be the batch size + // except where the number expected is not a multiple of the batch size and this is the first remaining + // count to cross a batch size boundary, in which case it will be the number expected modulo the batch + // size. + int receivedInBatch = ((expected - remainingCount) < _batchSize) ? (expected % _batchSize) : _batchSize; + + // Register a test result for the correlation id. + try + { + tc.completeTest(true, receivedInBatch, latency); + } + catch (InterruptedException e) + { + // Ignore this. It means the test runner wants to stop as soon as possible. + _logger.warn("Got InterruptedException.", e); + } + } + // Else ignore, test timed out. Should log a fail here? + } + } + } + + /** + * Holds state specific to each correlation id, needed to output test results. This consists of the count of the + * total expected number of messages, and the timing controller for the thread sending those message ids. + */ + private static class PerCorrelationId + { + public int _expectedCount; + public TimingController _tc; + } +} diff --git a/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingSendOnlyClient.java b/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingSendOnlyClient.java index 2879f0c322..04e723069a 100644 --- a/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingSendOnlyClient.java +++ b/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingSendOnlyClient.java @@ -1,93 +1,93 @@ -/*
- *
- * 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.ping;
-
-import java.util.Properties;
-
-import javax.jms.Destination;
-import javax.jms.JMSException;
-import javax.jms.Message;
-import javax.jms.ObjectMessage;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.client.message.TestMessageFactory;
-import org.apache.qpid.util.CommandLineParser;
-
-/**
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * </table>
- */
-public class PingSendOnlyClient extends PingDurableClient
-{
- private static final Logger log = Logger.getLogger(PingSendOnlyClient.class);
-
- public PingSendOnlyClient(Properties overrides) throws Exception
- {
- super(overrides);
- }
-
- /**
- * Starts the ping/wait/receive process.
- *
- * @param args The command line arguments.
- */
- public static void main(String[] args)
- {
- try
- {
- // Create a ping producer overriding its defaults with all options passed on the command line.
- Properties options = CommandLineParser.processCommandLine(args, new CommandLineParser(new String[][] {}), System.getProperties());
- PingSendOnlyClient pingProducer = new PingSendOnlyClient(options);
-
- // Create a shutdown hook to terminate the ping-pong producer.
- Runtime.getRuntime().addShutdownHook(pingProducer.getShutdownHook());
-
- // Ensure that the ping pong producer is registered to listen for exceptions on the connection too.
- // pingProducer.getConnection().setExceptionListener(pingProducer);
-
- // Run the test procedure.
- int sent = pingProducer.send();
- pingProducer.waitForUser("Press return to close connection and quit.");
- pingProducer.closeConnection();
-
- System.exit(0);
- }
- catch (Exception e)
- {
- System.err.println(e.getMessage());
- log.error("Top level handler caught execption.", e);
- System.exit(1);
- }
- }
-
- public Message getTestMessage(Destination replyQueue, int messageSize, boolean persistent) throws JMSException
- {
- Message msg = TestMessageFactory.newTextMessage(_producerSession, messageSize);
-
- // Timestamp the message in nanoseconds.
- msg.setLongProperty(MESSAGE_TIMESTAMP_PROPNAME, System.nanoTime());
-
- return msg;
- }
-}
+/* + * + * 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.ping; + +import java.util.Properties; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.ObjectMessage; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.message.TestMessageFactory; +import org.apache.qpid.util.CommandLineParser; + +/** + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * </table> + */ +public class PingSendOnlyClient extends PingDurableClient +{ + private static final Logger log = Logger.getLogger(PingSendOnlyClient.class); + + public PingSendOnlyClient(Properties overrides) throws Exception + { + super(overrides); + } + + /** + * Starts the ping/wait/receive process. + * + * @param args The command line arguments. + */ + public static void main(String[] args) + { + try + { + // Create a ping producer overriding its defaults with all options passed on the command line. + Properties options = CommandLineParser.processCommandLine(args, new CommandLineParser(new String[][] {}), System.getProperties()); + PingSendOnlyClient pingProducer = new PingSendOnlyClient(options); + + // Create a shutdown hook to terminate the ping-pong producer. + Runtime.getRuntime().addShutdownHook(pingProducer.getShutdownHook()); + + // Ensure that the ping pong producer is registered to listen for exceptions on the connection too. + // pingProducer.getConnection().setExceptionListener(pingProducer); + + // Run the test procedure. + int sent = pingProducer.send(); + pingProducer.waitForUser("Press return to close connection and quit."); + pingProducer.closeConnection(); + + System.exit(0); + } + catch (Exception e) + { + System.err.println(e.getMessage()); + log.error("Top level handler caught execption.", e); + System.exit(1); + } + } + + public Message getTestMessage(Destination replyQueue, int messageSize, boolean persistent) throws JMSException + { + Message msg = TestMessageFactory.newTextMessage(_producerSession, messageSize); + + // Timestamp the message in nanoseconds. + msg.setLongProperty(MESSAGE_TIMESTAMP_PROPNAME, System.nanoTime()); + + return msg; + } +} diff --git a/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java b/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java index fb071a87fe..94b8ea662e 100644 --- a/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java +++ b/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java @@ -1,196 +1,196 @@ -/*
- *
- * 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.ping;
-
-import junit.framework.Assert;
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.requestreply.PingPongProducer;
-
-import org.apache.qpid.junit.extensions.AsymptoticTestCase;
-import org.apache.qpid.junit.extensions.TestThreadAware;
-import org.apache.qpid.junit.extensions.util.ParsedProperties;
-import org.apache.qpid.junit.extensions.util.TestContextProperties;
-
-import javax.jms.*;
-
-/**
- * PingTestPerf is a ping test, that has been written with the intention of being scaled up to run many times
- * simultaneously to simluate many clients/producers/connections.
- *
- * <p/>A single run of the test using the default JUnit test runner will result in the sending and timing of a single
- * full round trip ping. This test may be scaled up using a suitable JUnit test runner.
- *
- * <p/>The setup/teardown cycle establishes a connection to a broker and sets up a queue to send ping messages to and a
- * temporary queue for replies. This setup is only established once for all the test repeats/threads that may be run,
- * except if the connection is lost in which case an attempt to re-establish the setup is made.
- *
- * <p/>The test cycle is: Connects to a queue, creates a temporary queue, creates messages containing a property that
- * is the name of the temporary queue, fires off a message on the original queue and waits for a response on the
- * temporary queue.
- *
- * <p/>Configurable test properties: message size, transacted or not, persistent or not. Broker connection details.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * </table>
- */
-public class PingTestPerf extends AsymptoticTestCase implements TestThreadAware
-{
- private static Logger _logger = Logger.getLogger(PingTestPerf.class);
-
- /** Thread local to hold the per-thread test setup fields. */
- ThreadLocal<PerThreadSetup> threadSetup = new ThreadLocal<PerThreadSetup>();
-
- /** Holds a property reader to extract the test parameters from. */
- protected ParsedProperties testParameters =
- TestContextProperties.getInstance(PingPongProducer.defaults /*System.getProperties()*/);
-
- public PingTestPerf(String name)
- {
- super(name);
-
- _logger.debug("testParameters = " + testParameters);
- }
-
- /**
- * Compile all the tests into a test suite.
- * @return The test method testPingOk.
- */
- public static Test suite()
- {
- // Build a new test suite
- TestSuite suite = new TestSuite("Ping Performance Tests");
-
- // Run performance tests in read committed mode.
- suite.addTest(new PingTestPerf("testPingOk"));
-
- return suite;
- }
-
- public void testPingOk(int numPings) throws Exception
- {
- if (numPings == 0)
- {
- Assert.fail("Number of pings requested was zero.");
- }
-
- // Get the per thread test setup to run the test through.
- PerThreadSetup perThreadSetup = threadSetup.get();
-
- if (perThreadSetup == null)
- {
- Assert.fail("Could not get per thread test setup, it was null.");
- }
-
- // Generate a sample message. This message is already time stamped and has its reply-to destination set.
- Message msg =
- perThreadSetup._pingClient.getTestMessage(perThreadSetup._pingClient.getReplyDestinations().get(0),
- testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME),
- testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME));
-
- // start the test
- long timeout = Long.parseLong(testParameters.getProperty(PingPongProducer.TIMEOUT_PROPNAME));
- int numReplies = perThreadSetup._pingClient.pingAndWaitForReply(msg, numPings, timeout, null);
-
- // Fail the test if the timeout was exceeded.
- if (numReplies != perThreadSetup._pingClient.getExpectedNumPings(numPings))
- {
- Assert.fail("The ping timed out after " + timeout + " ms. Messages Sent = " + numPings + ", MessagesReceived = "
- + numReplies);
- }
- }
-
- /**
- * Performs test fixture creation on a per thread basis. This will only be called once for each test thread.
- */
- public void threadSetUp()
- {
- _logger.debug("public void threadSetUp(): called");
-
- try
- {
- PerThreadSetup perThreadSetup = new PerThreadSetup();
-
- // This is synchronized because there is a race condition, which causes one connection to sleep if
- // all threads try to create connection concurrently.
- synchronized (this)
- {
- // Establish a client to ping a Destination and listen the reply back from same Destination
- perThreadSetup._pingClient = new PingClient(testParameters);
- perThreadSetup._pingClient.establishConnection(true, true);
- }
- // Start the client connection
- perThreadSetup._pingClient.start();
-
- // Attach the per-thread set to the thread.
- threadSetup.set(perThreadSetup);
- }
- catch (Exception e)
- {
- _logger.warn("There was an exception during per thread setup.", e);
- }
- }
-
- /**
- * Performs test fixture clean
- */
- public void threadTearDown()
- {
- _logger.debug("public void threadTearDown(): called");
-
- try
- {
- // Get the per thread test fixture.
- PerThreadSetup perThreadSetup = threadSetup.get();
-
- // Close the pingers so that it cleans up its connection cleanly.
- synchronized (this)
- {
- if ((perThreadSetup != null) && (perThreadSetup._pingClient != null))
- {
- perThreadSetup._pingClient.close();
- }
- }
- }
- catch (JMSException e)
- {
- _logger.warn("There was an exception during per thread tear down.");
- }
- finally
- {
- // Ensure the per thread fixture is reclaimed.
- threadSetup.remove();
- }
- }
-
- protected static class PerThreadSetup
- {
- /**
- * Holds the test ping client.
- */
- protected PingClient _pingClient;
- protected String _correlationId;
- }
-}
+/* + * + * 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.ping; + +import junit.framework.Assert; +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.apache.log4j.Logger; + +import org.apache.qpid.requestreply.PingPongProducer; + +import org.apache.qpid.junit.extensions.AsymptoticTestCase; +import org.apache.qpid.junit.extensions.TestThreadAware; +import org.apache.qpid.junit.extensions.util.ParsedProperties; +import org.apache.qpid.junit.extensions.util.TestContextProperties; + +import javax.jms.*; + +/** + * PingTestPerf is a ping test, that has been written with the intention of being scaled up to run many times + * simultaneously to simluate many clients/producers/connections. + * + * <p/>A single run of the test using the default JUnit test runner will result in the sending and timing of a single + * full round trip ping. This test may be scaled up using a suitable JUnit test runner. + * + * <p/>The setup/teardown cycle establishes a connection to a broker and sets up a queue to send ping messages to and a + * temporary queue for replies. This setup is only established once for all the test repeats/threads that may be run, + * except if the connection is lost in which case an attempt to re-establish the setup is made. + * + * <p/>The test cycle is: Connects to a queue, creates a temporary queue, creates messages containing a property that + * is the name of the temporary queue, fires off a message on the original queue and waits for a response on the + * temporary queue. + * + * <p/>Configurable test properties: message size, transacted or not, persistent or not. Broker connection details. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * </table> + */ +public class PingTestPerf extends AsymptoticTestCase implements TestThreadAware +{ + private static Logger _logger = Logger.getLogger(PingTestPerf.class); + + /** Thread local to hold the per-thread test setup fields. */ + ThreadLocal<PerThreadSetup> threadSetup = new ThreadLocal<PerThreadSetup>(); + + /** Holds a property reader to extract the test parameters from. */ + protected ParsedProperties testParameters = + TestContextProperties.getInstance(PingPongProducer.defaults /*System.getProperties()*/); + + public PingTestPerf(String name) + { + super(name); + + _logger.debug("testParameters = " + testParameters); + } + + /** + * Compile all the tests into a test suite. + * @return The test method testPingOk. + */ + public static Test suite() + { + // Build a new test suite + TestSuite suite = new TestSuite("Ping Performance Tests"); + + // Run performance tests in read committed mode. + suite.addTest(new PingTestPerf("testPingOk")); + + return suite; + } + + public void testPingOk(int numPings) throws Exception + { + if (numPings == 0) + { + Assert.fail("Number of pings requested was zero."); + } + + // Get the per thread test setup to run the test through. + PerThreadSetup perThreadSetup = threadSetup.get(); + + if (perThreadSetup == null) + { + Assert.fail("Could not get per thread test setup, it was null."); + } + + // Generate a sample message. This message is already time stamped and has its reply-to destination set. + Message msg = + perThreadSetup._pingClient.getTestMessage(perThreadSetup._pingClient.getReplyDestinations().get(0), + testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME), + testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME)); + + // start the test + long timeout = Long.parseLong(testParameters.getProperty(PingPongProducer.TIMEOUT_PROPNAME)); + int numReplies = perThreadSetup._pingClient.pingAndWaitForReply(msg, numPings, timeout, null); + + // Fail the test if the timeout was exceeded. + if (numReplies != perThreadSetup._pingClient.getExpectedNumPings(numPings)) + { + Assert.fail("The ping timed out after " + timeout + " ms. Messages Sent = " + numPings + ", MessagesReceived = " + + numReplies); + } + } + + /** + * Performs test fixture creation on a per thread basis. This will only be called once for each test thread. + */ + public void threadSetUp() + { + _logger.debug("public void threadSetUp(): called"); + + try + { + PerThreadSetup perThreadSetup = new PerThreadSetup(); + + // This is synchronized because there is a race condition, which causes one connection to sleep if + // all threads try to create connection concurrently. + synchronized (this) + { + // Establish a client to ping a Destination and listen the reply back from same Destination + perThreadSetup._pingClient = new PingClient(testParameters); + perThreadSetup._pingClient.establishConnection(true, true); + } + // Start the client connection + perThreadSetup._pingClient.start(); + + // Attach the per-thread set to the thread. + threadSetup.set(perThreadSetup); + } + catch (Exception e) + { + _logger.warn("There was an exception during per thread setup.", e); + } + } + + /** + * Performs test fixture clean + */ + public void threadTearDown() + { + _logger.debug("public void threadTearDown(): called"); + + try + { + // Get the per thread test fixture. + PerThreadSetup perThreadSetup = threadSetup.get(); + + // Close the pingers so that it cleans up its connection cleanly. + synchronized (this) + { + if ((perThreadSetup != null) && (perThreadSetup._pingClient != null)) + { + perThreadSetup._pingClient.close(); + } + } + } + catch (JMSException e) + { + _logger.warn("There was an exception during per thread tear down."); + } + finally + { + // Ensure the per thread fixture is reclaimed. + threadSetup.remove(); + } + } + + protected static class PerThreadSetup + { + /** + * Holds the test ping client. + */ + protected PingClient _pingClient; + protected String _correlationId; + } +} diff --git a/qpid/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java b/qpid/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java index 0712557383..8e010ccf07 100644 --- a/qpid/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java +++ b/qpid/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java @@ -1,453 +1,453 @@ -/*
- *
- * 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.requestreply;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-import javax.jms.*;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.client.AMQConnection;
-import org.apache.qpid.client.AMQQueue;
-import org.apache.qpid.client.AMQTopic;
-import org.apache.qpid.jms.ConnectionListener;
-import org.apache.qpid.jms.Session;
-import org.apache.qpid.topic.Config;
-import org.apache.qpid.exchange.ExchangeDefaults;
-
-/**
- * PingPongBouncer is a message listener the bounces back messages to their reply to destination. This is used to return
- * ping messages generated by {@link org.apache.qpid.requestreply.PingPongProducer} but could be used for other purposes
- * too.
- *
- * <p/>The correlation id from the received message is extracted, and placed into the reply as the correlation id. Messages
- * are bounced back to their reply-to destination. The original sender of the message has the option to use either a unique
- * temporary queue or the correlation id to correlate the original message to the reply.
- *
- * <p/>There is a verbose mode flag which causes information about each ping to be output to the console
- * (info level logging, so usually console). This can be helpfull to check the bounce backs are happening but should
- * be disabled for real timing tests as writing to the console will slow things down.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Bounce back messages to their reply to destination.
- * <tr><td> Provide command line invocation to start the bounce back on a configurable broker url.
- * </table>
- *
- * @todo Replace the command line parsing with a neater tool.
- *
- * @todo Make verbose accept a number of messages, only prints to console every X messages.
- */
-public class PingPongBouncer implements MessageListener
-{
- private static final Logger _logger = Logger.getLogger(PingPongBouncer.class);
-
- /** The default prefetch size for the message consumer. */
- private static final int PREFETCH = 1;
-
- /** The default no local flag for the message consumer. */
- private static final boolean NO_LOCAL = true;
-
- private static final String DEFAULT_DESTINATION_NAME = "ping";
-
- /** The default exclusive flag for the message consumer. */
- private static final boolean EXCLUSIVE = false;
-
- /** A convenient formatter to use when time stamping output. */
- protected static final SimpleDateFormat timestampFormatter = new SimpleDateFormat("hh:mm:ss:SS");
-
- /** Used to indicate that the reply generator should log timing info to the console (logger info level). */
- private boolean _verbose = false;
-
- /** Determines whether this bounce back client bounces back messages persistently. */
- private boolean _persistent = false;
-
- private Destination _consumerDestination;
-
- /** Keeps track of the response destination of the previous message for the last reply to producer cache. */
- private Destination _lastResponseDest;
-
- /** The producer for sending replies with. */
- private MessageProducer _replyProducer;
-
- /** The consumer controlSession. */
- private Session _consumerSession;
-
- /** The producer controlSession. */
- private Session _producerSession;
-
- /** Holds the connection to the broker. */
- private AMQConnection _connection;
-
- /** Flag used to indicate if this is a point to point or pub/sub ping client. */
- private boolean _isPubSub = false;
-
- /**
- * This flag is used to indicate that the user should be prompted to kill a broker, in order to test
- * failover, immediately before committing a transaction.
- */
- protected boolean _failBeforeCommit = false;
-
- /**
- * This flag is used to indicate that the user should be prompted to a kill a broker, in order to test
- * failover, immediate after committing a transaction.
- */
- protected boolean _failAfterCommit = false;
-
- /**
- * Creates a PingPongBouncer on the specified producer and consumer sessions.
- *
- * @param brokerDetails The addresses of the brokers to connect to.
- * @param username The broker username.
- * @param password The broker password.
- * @param virtualpath The virtual host name within the broker.
- * @param destinationName The name of the queue to receive pings on
- * (or root of the queue name where many queues are generated).
- * @param persistent A flag to indicate that persistent message should be used.
- * @param transacted A flag to indicate that pings should be sent within transactions.
- * @param selector A message selector to filter received pings with.
- * @param verbose A flag to indicate that message timings should be sent to the console.
- *
- * @throws Exception All underlying exceptions allowed to fall through. This is only test code...
- */
- public PingPongBouncer(String brokerDetails, String username, String password, String virtualpath,
- String destinationName, boolean persistent, boolean transacted, String selector, boolean verbose,
- boolean pubsub) throws Exception
- {
- // Create a client id to uniquely identify this client.
- InetAddress address = InetAddress.getLocalHost();
- String clientId = address.getHostName() + System.currentTimeMillis();
- _verbose = verbose;
- _persistent = persistent;
- setPubSub(pubsub);
- // Connect to the broker.
- setConnection(new AMQConnection(brokerDetails, username, password, clientId, virtualpath));
- _logger.info("Connected with URL:" + getConnection().toURL());
-
- // Set up the failover notifier.
- getConnection().setConnectionListener(new FailoverNotifier());
-
- // Create a controlSession to listen for messages on and one to send replies on, transactional depending on the
- // command line option.
- _consumerSession = (Session) getConnection().createSession(transacted, Session.AUTO_ACKNOWLEDGE);
- _producerSession = (Session) getConnection().createSession(transacted, Session.AUTO_ACKNOWLEDGE);
-
- // Create the queue to listen for message on.
- createConsumerDestination(destinationName);
- MessageConsumer consumer =
- _consumerSession.createConsumer(_consumerDestination, PREFETCH, NO_LOCAL, EXCLUSIVE, selector);
-
- // Create a producer for the replies, without a default destination.
- _replyProducer = _producerSession.createProducer(null);
- _replyProducer.setDisableMessageTimestamp(true);
- _replyProducer.setDeliveryMode(_persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT);
-
- // Set this up to listen for messages on the queue.
- consumer.setMessageListener(this);
- }
-
- /**
- * Starts a stand alone ping-pong client running in verbose mode.
- *
- * @param args
- */
- public static void main(String[] args) throws Exception
- {
- System.out.println("Starting...");
-
- // Display help on the command line.
- if (args.length == 0)
- {
- _logger.info("Running test with default values...");
- //usage();
- //System.exit(0);
- }
-
- // Extract all command line parameters.
- Config config = new Config();
- config.setOptions(args);
- String brokerDetails = config.getHost() + ":" + config.getPort();
- String virtualpath = "test";
- String destinationName = config.getDestination();
- if (destinationName == null)
- {
- destinationName = DEFAULT_DESTINATION_NAME;
- }
-
- String selector = config.getSelector();
- boolean transacted = config.isTransacted();
- boolean persistent = config.usePersistentMessages();
- boolean pubsub = config.isPubSub();
- boolean verbose = true;
-
- //String selector = null;
-
- // Instantiate the ping pong client with the command line options and start it running.
- PingPongBouncer pingBouncer =
- new PingPongBouncer(brokerDetails, "guest", "guest", virtualpath, destinationName, persistent, transacted,
- selector, verbose, pubsub);
- pingBouncer.getConnection().start();
-
- System.out.println("Waiting...");
- }
-
- private static void usage()
- {
- System.err.println("Usage: PingPongBouncer \n" + "-host : broker host\n" + "-port : broker port\n"
- + "-destinationname : queue/topic name\n" + "-transacted : (true/false). Default is false\n"
- + "-persistent : (true/false). Default is false\n"
- + "-pubsub : (true/false). Default is false\n" + "-selector : selector string\n");
- }
-
- /**
- * This is a callback method that is notified of all messages for which this has been registered as a message
- * listener on a message consumer. It sends a reply (pong) to all messages it receieves on the reply to
- * destination of the message.
- *
- * @param message The message that triggered this callback.
- */
- public void onMessage(Message message)
- {
- try
- {
- String messageCorrelationId = message.getJMSCorrelationID();
- if (_verbose)
- {
- _logger.info(timestampFormatter.format(new Date()) + ": Got ping with correlation id, "
- + messageCorrelationId);
- }
-
- // Get the reply to destination from the message and check it is set.
- Destination responseDest = message.getJMSReplyTo();
-
- if (responseDest == null)
- {
- _logger.debug("Cannot send reply because reply-to destination is null.");
-
- return;
- }
-
- // Spew out some timing information if verbose mode is on.
- if (_verbose)
- {
- Long timestamp = message.getLongProperty("timestamp");
-
- if (timestamp != null)
- {
- long diff = System.currentTimeMillis() - timestamp;
- _logger.info("Time to bounce point: " + diff);
- }
- }
-
- // Correlate the reply to the original.
- message.setJMSCorrelationID(messageCorrelationId);
-
- // Send the receieved message as the pong reply.
- _replyProducer.send(responseDest, message);
-
- if (_verbose)
- {
- _logger.info(timestampFormatter.format(new Date()) + ": Sent reply with correlation id, "
- + messageCorrelationId);
- }
-
- // Commit the transaction if running in transactional mode.
- commitTx(_producerSession);
- }
- catch (JMSException e)
- {
- _logger.debug("There was a JMSException: " + e.getMessage(), e);
- }
- }
-
- /**
- * Gets the underlying connection that this ping client is running on.
- *
- * @return The underlying connection that this ping client is running on.
- */
- public AMQConnection getConnection()
- {
- return _connection;
- }
-
- /**
- * Sets the connection that this ping client is using.
- *
- * @param connection The ping connection.
- */
- public void setConnection(AMQConnection connection)
- {
- this._connection = connection;
- }
-
- /**
- * Sets or clears the pub/sub flag to indiciate whether this client is pinging a queue or a topic.
- *
- * @param pubsub <tt>true</tt> if this client is pinging a topic, <tt>false</tt> if it is pinging a queue.
- */
- public void setPubSub(boolean pubsub)
- {
- _isPubSub = pubsub;
- }
-
- /**
- * Checks whether this client is a p2p or pub/sub ping client.
- *
- * @return <tt>true</tt> if this client is pinging a topic, <tt>false</tt> if it is pinging a queue.
- */
- public boolean isPubSub()
- {
- return _isPubSub;
- }
-
- /**
- * Convenience method to commit the transaction on the specified controlSession. If the controlSession to commit on is not
- * a transactional controlSession, this method does nothing.
- *
- * <p/>If the {@link #_failBeforeCommit} flag is set, this will prompt the user to kill the broker before the
- * commit is applied. If the {@link #_failAfterCommit} flag is set, this will prompt the user to kill the broker
- * after the commit is applied.
- *
- * @throws javax.jms.JMSException If the commit fails and then the rollback fails.
- */
- protected void commitTx(Session session) throws JMSException
- {
- if (session.getTransacted())
- {
- try
- {
- if (_failBeforeCommit)
- {
- _logger.debug("Failing Before Commit");
- doFailover();
- }
-
- session.commit();
-
- if (_failAfterCommit)
- {
- _logger.debug("Failing After Commit");
- doFailover();
- }
-
- _logger.debug("Session Commited.");
- }
- catch (JMSException e)
- {
- _logger.trace("JMSException on commit:" + e.getMessage(), e);
-
- try
- {
- session.rollback();
- _logger.debug("Message rolled back.");
- }
- catch (JMSException jmse)
- {
- _logger.trace("JMSE on rollback:" + jmse.getMessage(), jmse);
-
- // Both commit and rollback failed. Throw the rollback exception.
- throw jmse;
- }
- }
- }
- }
-
- /**
- * Prompts the user to terminate the named broker, in order to test failover functionality. This method will block
- * until the user supplied some input on the terminal.
- *
- * @param broker The name of the broker to terminate.
- */
- protected void doFailover(String broker)
- {
- System.out.println("Kill Broker " + broker + " now.");
- try
- {
- System.in.read();
- }
- catch (IOException e)
- { }
-
- System.out.println("Continuing.");
- }
-
- /**
- * Prompts the user to terminate the broker, in order to test failover functionality. This method will block
- * until the user supplied some input on the terminal.
- */
- protected void doFailover()
- {
- System.out.println("Kill Broker now.");
- try
- {
- System.in.read();
- }
- catch (IOException e)
- { }
-
- System.out.println("Continuing.");
-
- }
-
- private void createConsumerDestination(String name)
- {
- if (isPubSub())
- {
- _consumerDestination = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, name);
- }
- else
- {
- _consumerDestination = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, name);
- }
- }
-
- /**
- * A connection listener that logs out any failover complete events. Could do more interesting things with this
- * at some point...
- */
- public static class FailoverNotifier implements ConnectionListener
- {
- public void bytesSent(long count)
- { }
-
- public void bytesReceived(long count)
- { }
-
- public boolean preFailover(boolean redirect)
- {
- return true;
- }
-
- public boolean preResubscribe()
- {
- return true;
- }
-
- public void failoverComplete()
- {
- _logger.info("App got failover complete callback.");
- }
- }
-}
+/* + * + * 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.requestreply; + +import java.io.IOException; +import java.net.InetAddress; +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.jms.*; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.jms.ConnectionListener; +import org.apache.qpid.jms.Session; +import org.apache.qpid.topic.Config; +import org.apache.qpid.exchange.ExchangeDefaults; + +/** + * PingPongBouncer is a message listener the bounces back messages to their reply to destination. This is used to return + * ping messages generated by {@link org.apache.qpid.requestreply.PingPongProducer} but could be used for other purposes + * too. + * + * <p/>The correlation id from the received message is extracted, and placed into the reply as the correlation id. Messages + * are bounced back to their reply-to destination. The original sender of the message has the option to use either a unique + * temporary queue or the correlation id to correlate the original message to the reply. + * + * <p/>There is a verbose mode flag which causes information about each ping to be output to the console + * (info level logging, so usually console). This can be helpfull to check the bounce backs are happening but should + * be disabled for real timing tests as writing to the console will slow things down. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Bounce back messages to their reply to destination. + * <tr><td> Provide command line invocation to start the bounce back on a configurable broker url. + * </table> + * + * @todo Replace the command line parsing with a neater tool. + * + * @todo Make verbose accept a number of messages, only prints to console every X messages. + */ +public class PingPongBouncer implements MessageListener +{ + private static final Logger _logger = Logger.getLogger(PingPongBouncer.class); + + /** The default prefetch size for the message consumer. */ + private static final int PREFETCH = 1; + + /** The default no local flag for the message consumer. */ + private static final boolean NO_LOCAL = true; + + private static final String DEFAULT_DESTINATION_NAME = "ping"; + + /** The default exclusive flag for the message consumer. */ + private static final boolean EXCLUSIVE = false; + + /** A convenient formatter to use when time stamping output. */ + protected static final SimpleDateFormat timestampFormatter = new SimpleDateFormat("hh:mm:ss:SS"); + + /** Used to indicate that the reply generator should log timing info to the console (logger info level). */ + private boolean _verbose = false; + + /** Determines whether this bounce back client bounces back messages persistently. */ + private boolean _persistent = false; + + private Destination _consumerDestination; + + /** Keeps track of the response destination of the previous message for the last reply to producer cache. */ + private Destination _lastResponseDest; + + /** The producer for sending replies with. */ + private MessageProducer _replyProducer; + + /** The consumer controlSession. */ + private Session _consumerSession; + + /** The producer controlSession. */ + private Session _producerSession; + + /** Holds the connection to the broker. */ + private AMQConnection _connection; + + /** Flag used to indicate if this is a point to point or pub/sub ping client. */ + private boolean _isPubSub = false; + + /** + * This flag is used to indicate that the user should be prompted to kill a broker, in order to test + * failover, immediately before committing a transaction. + */ + protected boolean _failBeforeCommit = false; + + /** + * This flag is used to indicate that the user should be prompted to a kill a broker, in order to test + * failover, immediate after committing a transaction. + */ + protected boolean _failAfterCommit = false; + + /** + * Creates a PingPongBouncer on the specified producer and consumer sessions. + * + * @param brokerDetails The addresses of the brokers to connect to. + * @param username The broker username. + * @param password The broker password. + * @param virtualpath The virtual host name within the broker. + * @param destinationName The name of the queue to receive pings on + * (or root of the queue name where many queues are generated). + * @param persistent A flag to indicate that persistent message should be used. + * @param transacted A flag to indicate that pings should be sent within transactions. + * @param selector A message selector to filter received pings with. + * @param verbose A flag to indicate that message timings should be sent to the console. + * + * @throws Exception All underlying exceptions allowed to fall through. This is only test code... + */ + public PingPongBouncer(String brokerDetails, String username, String password, String virtualpath, + String destinationName, boolean persistent, boolean transacted, String selector, boolean verbose, + boolean pubsub) throws Exception + { + // Create a client id to uniquely identify this client. + InetAddress address = InetAddress.getLocalHost(); + String clientId = address.getHostName() + System.currentTimeMillis(); + _verbose = verbose; + _persistent = persistent; + setPubSub(pubsub); + // Connect to the broker. + setConnection(new AMQConnection(brokerDetails, username, password, clientId, virtualpath)); + _logger.info("Connected with URL:" + getConnection().toURL()); + + // Set up the failover notifier. + getConnection().setConnectionListener(new FailoverNotifier()); + + // Create a controlSession to listen for messages on and one to send replies on, transactional depending on the + // command line option. + _consumerSession = (Session) getConnection().createSession(transacted, Session.AUTO_ACKNOWLEDGE); + _producerSession = (Session) getConnection().createSession(transacted, Session.AUTO_ACKNOWLEDGE); + + // Create the queue to listen for message on. + createConsumerDestination(destinationName); + MessageConsumer consumer = + _consumerSession.createConsumer(_consumerDestination, PREFETCH, NO_LOCAL, EXCLUSIVE, selector); + + // Create a producer for the replies, without a default destination. + _replyProducer = _producerSession.createProducer(null); + _replyProducer.setDisableMessageTimestamp(true); + _replyProducer.setDeliveryMode(_persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT); + + // Set this up to listen for messages on the queue. + consumer.setMessageListener(this); + } + + /** + * Starts a stand alone ping-pong client running in verbose mode. + * + * @param args + */ + public static void main(String[] args) throws Exception + { + System.out.println("Starting..."); + + // Display help on the command line. + if (args.length == 0) + { + _logger.info("Running test with default values..."); + //usage(); + //System.exit(0); + } + + // Extract all command line parameters. + Config config = new Config(); + config.setOptions(args); + String brokerDetails = config.getHost() + ":" + config.getPort(); + String virtualpath = "test"; + String destinationName = config.getDestination(); + if (destinationName == null) + { + destinationName = DEFAULT_DESTINATION_NAME; + } + + String selector = config.getSelector(); + boolean transacted = config.isTransacted(); + boolean persistent = config.usePersistentMessages(); + boolean pubsub = config.isPubSub(); + boolean verbose = true; + + //String selector = null; + + // Instantiate the ping pong client with the command line options and start it running. + PingPongBouncer pingBouncer = + new PingPongBouncer(brokerDetails, "guest", "guest", virtualpath, destinationName, persistent, transacted, + selector, verbose, pubsub); + pingBouncer.getConnection().start(); + + System.out.println("Waiting..."); + } + + private static void usage() + { + System.err.println("Usage: PingPongBouncer \n" + "-host : broker host\n" + "-port : broker port\n" + + "-destinationname : queue/topic name\n" + "-transacted : (true/false). Default is false\n" + + "-persistent : (true/false). Default is false\n" + + "-pubsub : (true/false). Default is false\n" + "-selector : selector string\n"); + } + + /** + * This is a callback method that is notified of all messages for which this has been registered as a message + * listener on a message consumer. It sends a reply (pong) to all messages it receieves on the reply to + * destination of the message. + * + * @param message The message that triggered this callback. + */ + public void onMessage(Message message) + { + try + { + String messageCorrelationId = message.getJMSCorrelationID(); + if (_verbose) + { + _logger.info(timestampFormatter.format(new Date()) + ": Got ping with correlation id, " + + messageCorrelationId); + } + + // Get the reply to destination from the message and check it is set. + Destination responseDest = message.getJMSReplyTo(); + + if (responseDest == null) + { + _logger.debug("Cannot send reply because reply-to destination is null."); + + return; + } + + // Spew out some timing information if verbose mode is on. + if (_verbose) + { + Long timestamp = message.getLongProperty("timestamp"); + + if (timestamp != null) + { + long diff = System.currentTimeMillis() - timestamp; + _logger.info("Time to bounce point: " + diff); + } + } + + // Correlate the reply to the original. + message.setJMSCorrelationID(messageCorrelationId); + + // Send the receieved message as the pong reply. + _replyProducer.send(responseDest, message); + + if (_verbose) + { + _logger.info(timestampFormatter.format(new Date()) + ": Sent reply with correlation id, " + + messageCorrelationId); + } + + // Commit the transaction if running in transactional mode. + commitTx(_producerSession); + } + catch (JMSException e) + { + _logger.debug("There was a JMSException: " + e.getMessage(), e); + } + } + + /** + * Gets the underlying connection that this ping client is running on. + * + * @return The underlying connection that this ping client is running on. + */ + public AMQConnection getConnection() + { + return _connection; + } + + /** + * Sets the connection that this ping client is using. + * + * @param connection The ping connection. + */ + public void setConnection(AMQConnection connection) + { + this._connection = connection; + } + + /** + * Sets or clears the pub/sub flag to indiciate whether this client is pinging a queue or a topic. + * + * @param pubsub <tt>true</tt> if this client is pinging a topic, <tt>false</tt> if it is pinging a queue. + */ + public void setPubSub(boolean pubsub) + { + _isPubSub = pubsub; + } + + /** + * Checks whether this client is a p2p or pub/sub ping client. + * + * @return <tt>true</tt> if this client is pinging a topic, <tt>false</tt> if it is pinging a queue. + */ + public boolean isPubSub() + { + return _isPubSub; + } + + /** + * Convenience method to commit the transaction on the specified controlSession. If the controlSession to commit on is not + * a transactional controlSession, this method does nothing. + * + * <p/>If the {@link #_failBeforeCommit} flag is set, this will prompt the user to kill the broker before the + * commit is applied. If the {@link #_failAfterCommit} flag is set, this will prompt the user to kill the broker + * after the commit is applied. + * + * @throws javax.jms.JMSException If the commit fails and then the rollback fails. + */ + protected void commitTx(Session session) throws JMSException + { + if (session.getTransacted()) + { + try + { + if (_failBeforeCommit) + { + _logger.debug("Failing Before Commit"); + doFailover(); + } + + session.commit(); + + if (_failAfterCommit) + { + _logger.debug("Failing After Commit"); + doFailover(); + } + + _logger.debug("Session Commited."); + } + catch (JMSException e) + { + _logger.trace("JMSException on commit:" + e.getMessage(), e); + + try + { + session.rollback(); + _logger.debug("Message rolled back."); + } + catch (JMSException jmse) + { + _logger.trace("JMSE on rollback:" + jmse.getMessage(), jmse); + + // Both commit and rollback failed. Throw the rollback exception. + throw jmse; + } + } + } + } + + /** + * Prompts the user to terminate the named broker, in order to test failover functionality. This method will block + * until the user supplied some input on the terminal. + * + * @param broker The name of the broker to terminate. + */ + protected void doFailover(String broker) + { + System.out.println("Kill Broker " + broker + " now."); + try + { + System.in.read(); + } + catch (IOException e) + { } + + System.out.println("Continuing."); + } + + /** + * Prompts the user to terminate the broker, in order to test failover functionality. This method will block + * until the user supplied some input on the terminal. + */ + protected void doFailover() + { + System.out.println("Kill Broker now."); + try + { + System.in.read(); + } + catch (IOException e) + { } + + System.out.println("Continuing."); + + } + + private void createConsumerDestination(String name) + { + if (isPubSub()) + { + _consumerDestination = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, name); + } + else + { + _consumerDestination = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, name); + } + } + + /** + * A connection listener that logs out any failover complete events. Could do more interesting things with this + * at some point... + */ + public static class FailoverNotifier implements ConnectionListener + { + public void bytesSent(long count) + { } + + public void bytesReceived(long count) + { } + + public boolean preFailover(boolean redirect) + { + return true; + } + + public boolean preResubscribe() + { + return true; + } + + public void failoverComplete() + { + _logger.info("App got failover complete callback."); + } + } +} diff --git a/qpid/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java b/qpid/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java index f328675488..4d8a736ec8 100644 --- a/qpid/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java +++ b/qpid/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java @@ -1,1720 +1,1720 @@ -/*
- *
- * 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.requestreply;
-
-import org.apache.log4j.Logger;
-import org.apache.log4j.NDC;
-
-import org.apache.qpid.test.framework.TestUtils;
-
-import org.apache.qpid.junit.extensions.BatchedThrottle;
-import org.apache.qpid.junit.extensions.Throttle;
-import org.apache.qpid.junit.extensions.util.CommandLineParser;
-import org.apache.qpid.junit.extensions.util.ParsedProperties;
-
-import javax.jms.*;
-import javax.naming.Context;
-import javax.naming.InitialContext;
-import javax.naming.NamingException;
-
-import java.io.*;
-import java.net.InetAddress;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.*;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-
-/**
- * PingPongProducer is a client that sends test messages, and waits for replies to these messages. The replies may
- * either be generated by another client (see {@link PingPongBouncer}, or an extension of it may be used that listens
- * to its own messages and does not send replies (see {@link org.apache.qpid.ping.PingClient}). The intention of ping
- * pong producer is that it is a swiss-army knife test client that makes almost every aspect of its behaviour
- * configurable.
- *
- * <p/>The pings are sent with a reply-to field set to a single temporary queue, which is the same for all pings. This
- * means that this class has to do some work to correlate pings with pongs; it expectes the original message correlation
- * id in the ping to be bounced back in the reply correlation id.
- *
- * <p/>This ping tool accepts a vast number of configuration options, all of which are passed in to the constructor. It
- * can ping topics or queues; ping multiple destinations; do persistent pings; send messages of any size; do pings within
- * transactions; control the number of pings to send in each transaction; limit its sending rate; and perform failover
- * testing. A complete list of accepted parameters, default values and comments on their usage is provided here:
- *
- * <p/><table><caption>Parameters</caption>
- * <tr><th> Parameter <th> Default <th> Comments
- * <tr><td> messageSize <td> 0 <td> Message size in bytes. Not including any headers.
- * <tr><td> destinationName <td> ping <td> The root name to use to generate destination names to ping.
- * <tr><td> persistent <td> false <td> Determines whether peristent delivery is used.
- * <tr><td> transacted <td> false <td> Determines whether messages are sent/received in transactions.
- * <tr><td> broker <td> tcp://localhost:5672 <td> Determines the broker to connect to.
- * <tr><td> virtualHost <td> test <td> Determines the virtual host to send all ping over.
- * <tr><td> rate <td> 0 <td> The maximum rate (in hertz) to send messages at. 0 means no limit.
- * <tr><td> verbose <td> false <td> The verbose flag for debugging. Prints to console on every message.
- * <tr><td> pubsub <td> false <td> Whether to ping topics or queues. Uses p2p by default.
- * <tr><td> failAfterCommit <td> false <td> Whether to prompt user to kill broker after a commit batch.
- * <tr><td> failBeforeCommit <td> false <td> Whether to prompt user to kill broker before a commit batch.
- * <tr><td> failAfterSend <td> false <td> Whether to prompt user to kill broker after a send.
- * <tr><td> failBeforeSend <td> false <td> Whether to prompt user to kill broker before a send.
- * <tr><td> failOnce <td> true <td> Whether to prompt for failover only once.
- * <tr><td> username <td> guest <td> The username to access the broker with.
- * <tr><td> password <td> guest <td> The password to access the broker with.
- * <tr><td> selector <td> null <td> Not used. Defines a message selector to filter pings with.
- * <tr><td> destinationCount <td> 1 <td> The number of destinations to send pings to.
- * <tr><td> numConsumers <td> 1 <td> The number of consumers on each destination.
- * <tr><td> timeout <td> 30000 <td> In milliseconds. The timeout to stop waiting for replies.
- * <tr><td> commitBatchSize <td> 1 <td> The number of messages per transaction in transactional mode.
- * <tr><td> uniqueDests <td> true <td> Whether each receivers only listens to one ping destination or all.
- * <tr><td> durableDests <td> false <td> Whether or not durable destinations are used.
- * <tr><td> ackMode <td> AUTO_ACK <td> The message acknowledgement mode. Possible values are:
- * 0 - SESSION_TRANSACTED
- * 1 - AUTO_ACKNOWLEDGE
- * 2 - CLIENT_ACKNOWLEDGE
- * 3 - DUPS_OK_ACKNOWLEDGE
- * 257 - NO_ACKNOWLEDGE
- * 258 - PRE_ACKNOWLEDGE
- * <tr><td> consTransacted <td> false <td> Whether or not consumers use transactions. Defaults to the same value
- * as the 'transacted' option if not seperately defined.
- * <tr><td> consAckMode <td> AUTO_ACK <td> The message acknowledgement mode for consumers. Defaults to the same
- * value as 'ackMode' if not seperately defined.
- * <tr><td> maxPending <td> 0 <td> The maximum size in bytes, of messages sent but not yet received.
- * Limits the volume of messages currently buffered on the client
- * or broker. Can help scale test clients by limiting amount of buffered
- * data to avoid out of memory errors.
- * </table>
- *
- * <p/>This implements the Runnable interface with a run method that implements an infinite ping loop. The ping loop
- * does all its work through helper methods, so that code wishing to run a ping-pong cycle is not forced to do so by
- * starting a new thread. The command line invocation does take advantage of this ping loop. A shutdown hook is also
- * registered to terminate the ping-pong loop cleanly.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Provide a ping and wait for all responses cycle.
- * <tr><td> Provide command line invocation to loop the ping cycle on a configurable broker url.
- * </table>
- *
- * @todo Use read/write lock in the onmessage, not for reading writing but to make use of a shared and exlcusive lock pair.
- * Obtain read lock on all messages, before decrementing the message count. At the end of the on message method add a
- * block that obtains the write lock for the very last message, releases any waiting producer. Means that the last
- * message waits until all other messages have been handled before releasing producers but allows messages to be
- * processed concurrently, unlike the current synchronized block.
- */
-public class PingPongProducer implements Runnable, ExceptionListener
-{
- /** Used for debugging. */
- private static final Logger log = Logger.getLogger(PingPongProducer.class);
-
- /** Holds the name of the property to determine whether of not client id is overridden at connection time. */
- public static final String OVERRIDE_CLIENT_ID_PROPNAME = "overrideClientId";
-
- /** Holds the default value of the override client id flag. */
- public static final String OVERRIDE_CLIENT_ID_DEAFULT = "false";
-
- /** Holds the name of the property to define the JNDI factory name with. */
- public static final String FACTORY_NAME_PROPNAME = "factoryName";
-
- /** Holds the default JNDI name of the connection factory. */
- public static final String FACTORY_NAME_DEAFULT = "local";
-
- /** Holds the name of the property to set the JNDI initial context properties with. */
- public static final String FILE_PROPERTIES_PROPNAME = "properties";
-
- /** Holds the default file name of the JNDI initial context properties. */
- public static final String FILE_PROPERTIES_DEAFULT = "perftests.properties";
-
- /** Holds the name of the property to get the test message size from. */
- public static final String MESSAGE_SIZE_PROPNAME = "messageSize";
-
- /** Used to set up a default message size. */
- public static final int MESSAGE_SIZE_DEAFULT = 0;
-
- /** Holds the name of the property to get the ping queue name from. */
- public static final String PING_QUEUE_NAME_PROPNAME = "destinationName";
-
- /** Holds the name of the default destination to send pings on. */
- public static final String PING_QUEUE_NAME_DEFAULT = "ping";
-
- /** Holds the name of the property to get the queue name postfix from. */
- public static final String QUEUE_NAME_POSTFIX_PROPNAME = "queueNamePostfix";
-
- /** Holds the default queue name postfix value. */
- public static final String QUEUE_NAME_POSTFIX_DEFAULT = "";
-
- /** Holds the name of the property to get the test delivery mode from. */
- public static final String PERSISTENT_MODE_PROPNAME = "persistent";
-
- /** Holds the message delivery mode to use for the test. */
- public static final boolean PERSISTENT_MODE_DEFAULT = false;
-
- /** Holds the name of the property to get the test transactional mode from. */
- public static final String TRANSACTED_PROPNAME = "transacted";
-
- /** Holds the transactional mode to use for the test. */
- public static final boolean TRANSACTED_DEFAULT = false;
-
- /** Holds the name of the property to get the test consumer transacted mode from. */
- public static final String CONSUMER_TRANSACTED_PROPNAME = "consTransacted";
-
- /** Holds the consumer transactional mode default setting. */
- public static final boolean CONSUMER_TRANSACTED_DEFAULT = false;
-
- /** Holds the name of the property to get the test broker url from. */
- public static final String BROKER_PROPNAME = "broker";
-
- /** Holds the default broker url for the test. */
- public static final String BROKER_DEFAULT = "tcp://localhost:5672";
-
- /** Holds the name of the property to get the test broker virtual path. */
- public static final String VIRTUAL_HOST_PROPNAME = "virtualHost";
-
- /** Holds the default virtual path for the test. */
- public static final String VIRTUAL_HOST_DEFAULT = "";
-
- /** Holds the name of the property to get the message rate from. */
- public static final String RATE_PROPNAME = "rate";
-
- /** Defines the default rate (in pings per second) to send pings at. 0 means as fast as possible, no restriction. */
- public static final int RATE_DEFAULT = 0;
-
- /** Holds the name of the property to get the verbose mode proeprty from. */
- public static final String VERBOSE_PROPNAME = "verbose";
-
- /** Holds the default verbose mode. */
- public static final boolean VERBOSE_DEFAULT = false;
-
- /** Holds the name of the property to get the p2p or pub/sub messaging mode from. */
- public static final String PUBSUB_PROPNAME = "pubsub";
-
- /** Holds the pub/sub mode default, true means ping a topic, false means ping a queue. */
- public static final boolean PUBSUB_DEFAULT = false;
-
- /** Holds the name of the property to get the fail after commit flag from. */
- public static final String FAIL_AFTER_COMMIT_PROPNAME = "failAfterCommit";
-
- /** Holds the default failover after commit test flag. */
- public static final boolean FAIL_AFTER_COMMIT_DEFAULT = false;
-
- /** Holds the name of the proeprty to get the fail before commit flag from. */
- public static final String FAIL_BEFORE_COMMIT_PROPNAME = "failBeforeCommit";
-
- /** Holds the default failover before commit test flag. */
- public static final boolean FAIL_BEFORE_COMMIT_DEFAULT = false;
-
- /** Holds the name of the proeprty to get the fail after send flag from. */
- public static final String FAIL_AFTER_SEND_PROPNAME = "failAfterSend";
-
- /** Holds the default failover after send test flag. */
- public static final boolean FAIL_AFTER_SEND_DEFAULT = false;
-
- /** Holds the name of the property to get the fail before send flag from. */
- public static final String FAIL_BEFORE_SEND_PROPNAME = "failBeforeSend";
-
- /** Holds the default failover before send test flag. */
- public static final boolean FAIL_BEFORE_SEND_DEFAULT = false;
-
- /** Holds the name of the property to get the fail once flag from. */
- public static final String FAIL_ONCE_PROPNAME = "failOnce";
-
- /** The default failover once flag, true means only do one failover, false means failover on every commit cycle. */
- public static final boolean FAIL_ONCE_DEFAULT = true;
-
- /** Holds the name of the property to get the broker access username from. */
- public static final String USERNAME_PROPNAME = "username";
-
- /** Holds the default broker log on username. */
- public static final String USERNAME_DEFAULT = "guest";
-
- /** Holds the name of the property to get the broker access password from. */
- public static final String PASSWORD_PROPNAME = "password";
-
- /** Holds the default broker log on password. */
- public static final String PASSWORD_DEFAULT = "guest";
-
- /** Holds the name of the proeprty to get the. */
- public static final String SELECTOR_PROPNAME = "selector";
-
- /** Holds the default message selector. */
- public static final String SELECTOR_DEFAULT = "";
-
- /** Holds the name of the property to get the destination count from. */
- public static final String DESTINATION_COUNT_PROPNAME = "destinationCount";
-
- /** Defines the default number of destinations to ping. */
- public static final int DESTINATION_COUNT_DEFAULT = 1;
-
- /** Holds the name of the property to get the number of consumers per destination from. */
- public static final String NUM_CONSUMERS_PROPNAME = "numConsumers";
-
- /** Defines the default number consumers per destination. */
- public static final int NUM_CONSUMERS_DEFAULT = 1;
-
- /** Holds the name of the property to get the waiting timeout for response messages. */
- public static final String TIMEOUT_PROPNAME = "timeout";
-
- /** Default time to wait before assuming that a ping has timed out. */
- public static final long TIMEOUT_DEFAULT = 30000;
-
- /** Holds the name of the property to get the commit batch size from. */
- public static final String TX_BATCH_SIZE_PROPNAME = "commitBatchSize";
-
- /** Defines the default number of pings to send in each transaction when running transactionally. */
- public static final int TX_BATCH_SIZE_DEFAULT = 1;
-
- /** Holds the name of the property to get the unique destinations flag from. */
- public static final String UNIQUE_DESTS_PROPNAME = "uniqueDests";
-
- /** Defines the default value for the unique destinations property. */
- public static final boolean UNIQUE_DESTS_DEFAULT = true;
-
- /** Holds the name of the property to get the durable destinations flag from. */
- public static final String DURABLE_DESTS_PROPNAME = "durableDests";
-
- /** Defines the default value of the durable destinations flag. */
- public static final boolean DURABLE_DESTS_DEFAULT = false;
-
- /** Holds the name of the proeprty to get the message acknowledgement mode from. */
- public static final String ACK_MODE_PROPNAME = "ackMode";
-
- /** Defines the default message acknowledgement mode. */
- public static final int ACK_MODE_DEFAULT = Session.AUTO_ACKNOWLEDGE;
-
- /** Holds the name of the property to get the consumers message acknowledgement mode from. */
- public static final String CONSUMER_ACK_MODE_PROPNAME = "consAckMode";
-
- /** Defines the default consumers message acknowledgement mode. */
- public static final int CONSUMER_ACK_MODE_DEFAULT = Session.AUTO_ACKNOWLEDGE;
-
- /** Holds the name of the property to get the maximum pending message size setting from. */
- public static final String MAX_PENDING_PROPNAME = "maxPending";
-
- /** Defines the default value for the maximum pending message size setting. 0 means no limit. */
- public static final int MAX_PENDING_DEFAULT = 0;
-
- /** Defines the default prefetch size to use when consuming messages. */
- public static final int PREFETCH_DEFAULT = 100;
-
- /** Defines the default value of the no local flag to use when consuming messages. */
- public static final boolean NO_LOCAL_DEFAULT = false;
-
- /** Defines the default value of the exclusive flag to use when consuming messages. */
- public static final boolean EXCLUSIVE_DEFAULT = false;
-
- /** Holds the name of the property to store nanosecond timestamps in ping messages with. */
- public static final String MESSAGE_TIMESTAMP_PROPNAME = "timestamp";
-
- /** Holds the default configuration properties. */
- public static ParsedProperties defaults = new ParsedProperties();
-
- static
- {
- defaults.setPropertyIfNull(OVERRIDE_CLIENT_ID_PROPNAME, OVERRIDE_CLIENT_ID_DEAFULT);
- defaults.setPropertyIfNull(FILE_PROPERTIES_PROPNAME, FILE_PROPERTIES_DEAFULT);
- defaults.setPropertyIfNull(FACTORY_NAME_PROPNAME, FACTORY_NAME_DEAFULT);
- defaults.setPropertyIfNull(BROKER_PROPNAME, BROKER_DEFAULT);
- defaults.setPropertyIfNull(USERNAME_PROPNAME, USERNAME_DEFAULT);
- defaults.setPropertyIfNull(PASSWORD_PROPNAME, PASSWORD_DEFAULT);
- defaults.setPropertyIfNull(VIRTUAL_HOST_PROPNAME, VIRTUAL_HOST_DEFAULT);
- defaults.setPropertyIfNull(PING_QUEUE_NAME_PROPNAME, PING_QUEUE_NAME_DEFAULT);
- defaults.setPropertyIfNull(QUEUE_NAME_POSTFIX_PROPNAME, QUEUE_NAME_POSTFIX_DEFAULT);
- defaults.setPropertyIfNull(SELECTOR_PROPNAME, SELECTOR_DEFAULT);
- defaults.setPropertyIfNull(TRANSACTED_PROPNAME, TRANSACTED_DEFAULT);
- defaults.setPropertyIfNull(CONSUMER_TRANSACTED_PROPNAME, CONSUMER_TRANSACTED_DEFAULT);
- defaults.setPropertyIfNull(PERSISTENT_MODE_PROPNAME, PERSISTENT_MODE_DEFAULT);
- defaults.setPropertyIfNull(ACK_MODE_PROPNAME, ACK_MODE_DEFAULT);
- defaults.setPropertyIfNull(CONSUMER_ACK_MODE_PROPNAME, CONSUMER_ACK_MODE_DEFAULT);
- defaults.setPropertyIfNull(MESSAGE_SIZE_PROPNAME, MESSAGE_SIZE_DEAFULT);
- defaults.setPropertyIfNull(VERBOSE_PROPNAME, VERBOSE_DEFAULT);
- defaults.setPropertyIfNull(PUBSUB_PROPNAME, PUBSUB_DEFAULT);
- defaults.setPropertyIfNull(UNIQUE_DESTS_PROPNAME, UNIQUE_DESTS_DEFAULT);
- defaults.setPropertyIfNull(DURABLE_DESTS_PROPNAME, DURABLE_DESTS_DEFAULT);
- defaults.setPropertyIfNull(FAIL_BEFORE_COMMIT_PROPNAME, FAIL_BEFORE_COMMIT_DEFAULT);
- defaults.setPropertyIfNull(FAIL_AFTER_COMMIT_PROPNAME, FAIL_AFTER_COMMIT_DEFAULT);
- defaults.setPropertyIfNull(FAIL_BEFORE_SEND_PROPNAME, FAIL_BEFORE_SEND_DEFAULT);
- defaults.setPropertyIfNull(FAIL_AFTER_SEND_PROPNAME, FAIL_AFTER_SEND_DEFAULT);
- defaults.setPropertyIfNull(FAIL_ONCE_PROPNAME, FAIL_ONCE_DEFAULT);
- defaults.setPropertyIfNull(TX_BATCH_SIZE_PROPNAME, TX_BATCH_SIZE_DEFAULT);
- defaults.setPropertyIfNull(DESTINATION_COUNT_PROPNAME, DESTINATION_COUNT_DEFAULT);
- defaults.setPropertyIfNull(NUM_CONSUMERS_PROPNAME, NUM_CONSUMERS_DEFAULT);
- defaults.setPropertyIfNull(RATE_PROPNAME, RATE_DEFAULT);
- defaults.setPropertyIfNull(TIMEOUT_PROPNAME, TIMEOUT_DEFAULT);
- defaults.setPropertyIfNull(MAX_PENDING_PROPNAME, MAX_PENDING_DEFAULT);
- }
-
- /** Allows setting of client ID on the connection, rather than through the connection URL. */
- protected boolean _overrideClientId;
-
- /** Holds the JNDI name of the JMS connection factory. */
- protected String _factoryName;
-
- /** Holds the name of the properties file to configure JNDI with. */
- protected String _fileProperties;
-
- /** Holds the broker url. */
- protected String _brokerDetails;
-
- /** Holds the username to access the broker with. */
- protected String _username;
-
- /** Holds the password to access the broker with. */
- protected String _password;
-
- /** Holds the virtual host on the broker to run the tests through. */
- protected String _virtualpath;
-
- /** Holds the root name from which to generate test destination names. */
- protected String _destinationName;
-
- /** Holds the default queue name postfix value. */
- protected String _queueNamePostfix;
-
- /** Holds the message selector to filter the pings with. */
- protected String _selector;
-
- /** Holds the producers transactional mode flag. */
- protected boolean _transacted;
-
- /** Holds the consumers transactional mode flag. */
- protected boolean _consTransacted;
-
- /** Determines whether this producer sends persistent messages. */
- protected boolean _persistent;
-
- /** Holds the acknowledgement mode used for the producers. */
- protected int _ackMode;
-
- /** Holds the acknowledgement mode setting for the consumers. */
- protected int _consAckMode;
-
- /** Determines what size of messages this producer sends. */
- protected int _messageSize;
-
- /** Used to indicate that the ping loop should print out whenever it pings. */
- protected boolean _verbose;
-
- /** Flag used to indicate if this is a point to point or pub/sub ping client. */
- protected boolean _isPubSub;
-
- /** Flag used to indicate if the destinations should be unique client. */
- protected boolean _isUnique;
-
- /** Flag used to indicate that durable destination should be used. */
- protected boolean _isDurable;
-
- /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover before a commit. */
- protected boolean _failBeforeCommit;
-
- /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover after a commit. */
- protected boolean _failAfterCommit;
-
- /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover before a send. */
- protected boolean _failBeforeSend;
-
- /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover after a send. */
- protected boolean _failAfterSend;
-
- /** Flag used to indicate that failover prompting should only be done on the first commit, not on every commit. */
- protected boolean _failOnce;
-
- /** Holds the number of sends that should be performed in every transaction when using transactions. */
- protected int _txBatchSize;
-
- /** Holds the number of destinations to ping. */
- protected int _noOfDestinations;
-
- /** Holds the number of consumers per destination. */
- protected int _noOfConsumers;
-
- /** Holds the maximum send rate in herz. */
- protected int _rate;
-
- /**
- * Holds the size of the maximum amount of pending data that the client should buffer, sending is suspended
- * if this limit is breached.
- */
- protected int _maxPendingSize;
-
- /** A source for providing sequential unique correlation ids. These will be unique within the same JVM. */
- private static AtomicLong _correlationIdGenerator = new AtomicLong(0L);
-
- /** A source for providing sequential unqiue ids for instances of this class to be identifed with. */
- private static AtomicInteger _instanceIdGenerator = new AtomicInteger(0);
-
- /** Holds this instances unique id. */
- private int instanceId;
-
- /**
- * Holds a map from message ids to latches on which threads wait for replies. This map is shared accross multiple
- * ping producers on the same JVM.
- */
- private static Map<String, PerCorrelationId> perCorrelationIds =
- Collections.synchronizedMap(new HashMap<String, PerCorrelationId>());
-
- /** A convenient formatter to use when time stamping output. */
- protected static final DateFormat timestampFormatter = new SimpleDateFormat("hh:mm:ss:SS");
-
- /** Holds the connection for the message producer. */
- protected Connection _connection;
-
- /** Holds the consumer connections. */
- protected Connection[] _consumerConnection;
-
- /** Holds the controlSession on which ping replies are received. */
- protected Session[] _consumerSession;
-
- /** Holds the producer controlSession, needed to create ping messages. */
- protected Session _producerSession;
-
- /** Holds the destination where the response messages will arrive. */
- protected Destination _replyDestination;
-
- /** Holds the set of destinations that this ping producer pings. */
- protected List<Destination> _pingDestinations;
-
- /** Used to restrict the sending rate to a specified limit. */
- protected Throttle _rateLimiter;
-
- /** Holds a message listener that this message listener chains all its messages to. */
- protected ChainedMessageListener _chainedMessageListener = null;
-
- /**
- * This id generator is used to generate ids to append to the queue name to ensure that queues can be unique when
- * creating multiple ping producers in the same JVM.
- */
- protected static AtomicInteger _queueJVMSequenceID = new AtomicInteger();
-
- /**
- * This id generator is used to generates ids that are only unique within this pinger. Creating multiple pingers
- * on the same JVM using this id generator will allow them to ping on the same queues.
- */
- protected AtomicInteger _queueSharedID = new AtomicInteger();
-
- /** Used to tell the ping loop when to terminate, it only runs while this is true. */
- protected boolean _publish = true;
-
- /** Holds the message producer to send the pings through. */
- protected MessageProducer _producer;
-
- /** Holds the message consumer to receive the ping replies through. */
- protected MessageConsumer[] _consumer;
-
- /** The prompt to display when asking the user to kill the broker for failover testing. */
- private static final String KILL_BROKER_PROMPT = "Kill broker now, then press Return.";
-
- /** Holds the name for this test client to be identified to the broker with. */
- private String _clientID;
-
- /** Keeps count of the total messages sent purely for debugging purposes. */
- private static AtomicInteger numSent = new AtomicInteger();
-
- /**
- * Holds a monitor which is used to synchronize sender and receivers threads, where the sender has elected
- * to wait until the number of unreceived message is reduced before continuing to send. This monitor is a
- * fair SynchronousQueue becuase that provides fair scheduling, to ensure that all producer threads get an
- * equal chance to produce messages.
- */
- static final SynchronousQueue _sendPauseMonitor = new SynchronousQueue(true);
-
- /** Keeps a count of the number of message currently sent but not received. */
- static AtomicInteger _unreceived = new AtomicInteger(0);
-
- /**
- * Creates a ping producer with the specified parameters, of which there are many. See the class level comments
- * for details. This constructor creates a connection to the broker and creates producer and consumer sessions on
- * it, to send and recieve its pings and replies on.
- *
- * @param overrides Properties containing any desired overrides to the defaults.
- *
- * @throws Exception Any exceptions are allowed to fall through.
- */
- public PingPongProducer(Properties overrides) throws Exception
- {
- // log.debug("public PingPongProducer(Properties overrides = " + overrides + "): called");
- instanceId = _instanceIdGenerator.getAndIncrement();
-
- // Create a set of parsed properties from the defaults overriden by the passed in values.
- ParsedProperties properties = new ParsedProperties(defaults);
- properties.putAll(overrides);
-
- // Extract the configuration properties to set the pinger up with.
- _overrideClientId = properties.getPropertyAsBoolean(OVERRIDE_CLIENT_ID_PROPNAME);
- _factoryName = properties.getProperty(FACTORY_NAME_PROPNAME);
- _fileProperties = properties.getProperty(FILE_PROPERTIES_PROPNAME);
- _brokerDetails = properties.getProperty(BROKER_PROPNAME);
- _username = properties.getProperty(USERNAME_PROPNAME);
- _password = properties.getProperty(PASSWORD_PROPNAME);
- _virtualpath = properties.getProperty(VIRTUAL_HOST_PROPNAME);
- _destinationName = properties.getProperty(PING_QUEUE_NAME_PROPNAME);
- _queueNamePostfix = properties.getProperty(QUEUE_NAME_POSTFIX_PROPNAME);
- _selector = properties.getProperty(SELECTOR_PROPNAME);
- _transacted = properties.getPropertyAsBoolean(TRANSACTED_PROPNAME);
- _consTransacted = properties.getPropertyAsBoolean(CONSUMER_TRANSACTED_PROPNAME);
- _persistent = properties.getPropertyAsBoolean(PERSISTENT_MODE_PROPNAME);
- _messageSize = properties.getPropertyAsInteger(MESSAGE_SIZE_PROPNAME);
- _verbose = properties.getPropertyAsBoolean(VERBOSE_PROPNAME);
- _failAfterCommit = properties.getPropertyAsBoolean(FAIL_AFTER_COMMIT_PROPNAME);
- _failBeforeCommit = properties.getPropertyAsBoolean(FAIL_BEFORE_COMMIT_PROPNAME);
- _failAfterSend = properties.getPropertyAsBoolean(FAIL_AFTER_SEND_PROPNAME);
- _failBeforeSend = properties.getPropertyAsBoolean(FAIL_BEFORE_SEND_PROPNAME);
- _failOnce = properties.getPropertyAsBoolean(FAIL_ONCE_PROPNAME);
- _txBatchSize = properties.getPropertyAsInteger(TX_BATCH_SIZE_PROPNAME);
- _noOfDestinations = properties.getPropertyAsInteger(DESTINATION_COUNT_PROPNAME);
- _noOfConsumers = properties.getPropertyAsInteger(NUM_CONSUMERS_PROPNAME);
- _rate = properties.getPropertyAsInteger(RATE_PROPNAME);
- _isPubSub = properties.getPropertyAsBoolean(PUBSUB_PROPNAME);
- _isUnique = properties.getPropertyAsBoolean(UNIQUE_DESTS_PROPNAME);
- _isDurable = properties.getPropertyAsBoolean(DURABLE_DESTS_PROPNAME);
- _ackMode = _transacted ? 0 : properties.getPropertyAsInteger(ACK_MODE_PROPNAME);
- _consAckMode = _consTransacted ? 0 : properties.getPropertyAsInteger(CONSUMER_ACK_MODE_PROPNAME);
- _maxPendingSize = properties.getPropertyAsInteger(MAX_PENDING_PROPNAME);
-
- // Check that one or more destinations were specified.
- if (_noOfDestinations < 1)
- {
- throw new IllegalArgumentException("There must be at least one destination.");
- }
-
- // Set up a throttle to control the send rate, if a rate > 0 is specified.
- if (_rate > 0)
- {
- _rateLimiter = new BatchedThrottle();
- _rateLimiter.setRate(_rate);
- }
-
- // Create the connection and message producers/consumers.
- // establishConnection(true, true);
- }
-
- /**
- * Establishes a connection to the broker and creates message consumers and producers based on the parameters
- * that this ping client was created with.
- *
- * @param producer Flag to indicate whether or not the producer should be set up.
- * @param consumer Flag to indicate whether or not the consumers should be set up.
- *
- * @throws Exception Any exceptions are allowed to fall through.
- */
- public void establishConnection(boolean producer, boolean consumer) throws Exception
- {
- // log.debug("public void establishConnection(): called");
-
- // Generate a unique identifying name for this client, based on it ip address and the current time.
- InetAddress address = InetAddress.getLocalHost();
- // _clientID = address.getHostName() + System.currentTimeMillis();
- _clientID = "perftest_" + instanceId;
-
- // Create a connection to the broker.
- createConnection(_clientID);
-
- // Create transactional or non-transactional sessions, based on the command line arguments.
- _producerSession = _connection.createSession(_transacted, _ackMode);
-
- _consumerSession = new Session[_noOfConsumers];
-
- for (int i = 0; i < _noOfConsumers; i++)
- {
- _consumerSession[i] = _consumerConnection[i].createSession(_consTransacted, _consAckMode);
- }
-
- // Create the destinations to send pings to and receive replies from.
- _replyDestination = _consumerSession[0].createTemporaryQueue();
- createPingDestinations(_noOfDestinations, _selector, _destinationName, _isUnique, _isDurable);
-
- // Create the message producer only if instructed to.
- if (producer)
- {
- createProducer();
- }
-
- // Create the message consumer only if instructed to.
- if (consumer)
- {
- createReplyConsumers(getReplyDestinations(), _selector);
- }
- }
-
- /**
- * Establishes a connection to the broker, based on the configuration parameters that this ping client was
- * created with.
- *
- * @param clientID The clients identifier.
- *
- * @throws JMSException Underlying exceptions allowed to fall through.
- * @throws NamingException Underlying exceptions allowed to fall through.
- * @throws IOException Underlying exceptions allowed to fall through.
- */
- protected void createConnection(String clientID) throws JMSException, NamingException, IOException
- {
- // _log.debug("protected void createConnection(String clientID = " + clientID + "): called");
-
- // _log.debug("Creating a connection for the message producer.");
- File propsFile = new File(_fileProperties);
- InputStream is = new FileInputStream(propsFile);
- Properties properties = new Properties();
- properties.load(is);
-
- Context context = new InitialContext(properties);
- ConnectionFactory factory = (ConnectionFactory) context.lookup(_factoryName);
- _connection = factory.createConnection(_username, _password);
-
- if (_overrideClientId)
- {
- _connection.setClientID(clientID);
- }
-
- // _log.debug("Creating " + _noOfConsumers + " connections for the consumers.");
-
- _consumerConnection = new Connection[_noOfConsumers];
-
- for (int i = 0; i < _noOfConsumers; i++)
- {
- _consumerConnection[i] = factory.createConnection(_username, _password);
- // _consumerConnection[i].setClientID(clientID);
- }
- }
-
- /**
- * Starts a ping-pong loop running from the command line. The bounce back client {@link PingPongBouncer} also needs
- * to be started to bounce the pings back again.
- *
- * @param args The command line arguments.
- */
- public static void main(String[] args)
- {
- try
- {
- Properties options =
- CommandLineParser.processCommandLine(args, new CommandLineParser(new String[][] {}), System.getProperties());
-
- // Create a ping producer overriding its defaults with all options passed on the command line.
- PingPongProducer pingProducer = new PingPongProducer(options);
- pingProducer.establishConnection(true, true);
-
- // Start the ping producers dispatch thread running.
- pingProducer._connection.start();
-
- // Create a shutdown hook to terminate the ping-pong producer.
- Runtime.getRuntime().addShutdownHook(pingProducer.getShutdownHook());
-
- // Ensure that the ping pong producer is registered to listen for exceptions on the connection too.
- pingProducer._connection.setExceptionListener(pingProducer);
-
- // Create the ping loop thread and run it until it is terminated by the shutdown hook or exception.
- Thread pingThread = new Thread(pingProducer);
- pingThread.run();
- pingThread.join();
- }
- catch (Exception e)
- {
- System.err.println(e.getMessage());
- log.error("Top level handler caught execption.", e);
- System.exit(1);
- }
- }
-
- /**
- * Convenience method for a short pause.
- *
- * @param sleepTime The time in milliseconds to pause for.
- */
- public static void pause(long sleepTime)
- {
- if (sleepTime > 0)
- {
- try
- {
- Thread.sleep(sleepTime);
- }
- catch (InterruptedException ie)
- { }
- }
- }
-
- /**
- * Gets all the reply destinations (to listen for replies on). In this case this will just be the single reply to
- * destination of this pinger.
- *
- * @return The single reply to destination of this pinger, wrapped in a list.
- */
- public List<Destination> getReplyDestinations()
- {
- // log.debug("public List<Destination> getReplyDestinations(): called");
-
- List<Destination> replyDestinations = new ArrayList<Destination>();
- replyDestinations.add(_replyDestination);
-
- // log.debug("replyDestinations = " + replyDestinations);
-
- return replyDestinations;
- }
-
- /**
- * Creates the producer to send the pings on. This is created without a default destination. Its persistent delivery
- * flag is set accoring the ping producer creation options.
- *
- * @throws JMSException Any JMSExceptions are allowed to fall through.
- */
- public void createProducer() throws JMSException
- {
- // log.debug("public void createProducer(): called");
-
- _producer = (MessageProducer) _producerSession.createProducer(null);
- _producer.setDeliveryMode(_persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT);
-
- // log.debug("Created producer for " + (_persistent ? "persistent" : "non-persistent") + " messages.");
- }
-
- /**
- * Creates consumers for the specified number of destinations. The destinations themselves are also created by this
- * method.
- *
- * @param noOfDestinations The number of destinations to create consumers for.
- * @param selector The message selector to filter the consumers with.
- * @param rootName The root of the name, or actual name if only one is being created.
- * @param unique <tt>true</tt> to make the destinations unique to this pinger, <tt>false</tt> to share the
- * numbering with all pingers on the same JVM.
- * @param durable If the destinations are durable topics.
- *
- * @throws JMSException Any JMSExceptions are allowed to fall through.
- */
- public void createPingDestinations(int noOfDestinations, String selector, String rootName, boolean unique,
- boolean durable) throws JMSException
- {
- /*log.debug("public void createPingDestinations(int noOfDestinations = " + noOfDestinations + ", String selector = "
- + selector + ", String rootName = " + rootName + ", boolean unique = " + unique + ", boolean durable = "
- + durable + "): called");*/
-
- _pingDestinations = new ArrayList<Destination>();
-
- // Create the desired number of ping destinations and consumers for them.
- // log.debug("Creating " + noOfDestinations + " destinations to ping.");
-
- for (int i = 0; i < noOfDestinations; i++)
- {
- Destination destination;
- String id;
-
- // Generate an id, unique within this pinger or to the whole JVM depending on the unique flag.
- if (unique)
- {
- // log.debug("Creating unique destinations.");
- id = "_" + _queueJVMSequenceID.incrementAndGet() + "_" + _connection.getClientID();
- }
- else
- {
- // log.debug("Creating shared destinations.");
- id = "_" + _queueSharedID.incrementAndGet();
- }
-
- // Check if this is a pub/sub pinger, in which case create topics.
- if (_isPubSub)
- {
- destination = _producerSession.createTopic(rootName + id);
- // log.debug("Created non-durable topic " + destination);
-
- if (durable)
- {
- _producerSession.createDurableSubscriber((Topic) destination, _connection.getClientID());
- }
- }
- // Otherwise this is a p2p pinger, in which case create queues.
- else
- {
- destination = _producerSession.createQueue(rootName + id + _queueNamePostfix);
- // log.debug("Created queue " + destination);
- }
-
- // Keep the destination.
- _pingDestinations.add(destination);
- }
- }
-
- /**
- * Creates consumers for the specified destinations and registers this pinger to listen to their messages.
- *
- * @param destinations The destinations to listen to.
- * @param selector A selector to filter the messages with.
- *
- * @throws javax.jms.JMSException Any JMSExceptions are allowed to fall through.
- */
- public void createReplyConsumers(Collection<Destination> destinations, String selector) throws JMSException
- {
- /*log.debug("public void createReplyConsumers(Collection<Destination> destinations = " + destinations
- + ", String selector = " + selector + "): called");*/
-
- log.debug("There are " + destinations.size() + " destinations.");
- log.debug("Creating " + _noOfConsumers + " consumers on each destination.");
- log.debug("Total number of consumers is: " + (destinations.size() * _noOfConsumers));
-
- for (Destination destination : destinations)
- {
- _consumer = new MessageConsumer[_noOfConsumers];
-
- for (int i = 0; i < _noOfConsumers; i++)
- {
- // Create a consumer for the destination and set this pinger to listen to its messages.
- _consumer[i] = _consumerSession[i].createConsumer(destination, selector, NO_LOCAL_DEFAULT);
-
- final int consumerNo = i;
-
- _consumer[i].setMessageListener(new MessageListener()
- {
- public void onMessage(Message message)
- {
- onMessageWithConsumerNo(message, consumerNo);
- }
- });
-
- log.debug("Set consumer " + i + " to listen to replies sent to destination: " + destination);
- }
- }
- }
-
- /**
- * Stores the received message in the replies map, then resets the boolean latch that a thread waiting for a
- * correlating reply may be waiting on. This is only done if the reply has a correlation id that is expected in the
- * replies map.
- *
- * @param message The received message.
- * @param consumerNo The consumer number within this test pinger instance.
- */
- public void onMessageWithConsumerNo(Message message, int consumerNo)
- {
- // log.debug("public void onMessageWithConsumerNo(Message message, int consumerNo = " + consumerNo + "): called");
- try
- {
- long now = System.nanoTime();
- long timestamp = getTimestamp(message);
- long pingTime = now - timestamp;
-
- // NDC.push("id" + instanceId + "/cons" + consumerNo);
-
- // Extract the messages correlation id.
- String correlationID = message.getJMSCorrelationID();
- // log.debug("correlationID = " + correlationID);
-
- // int num = message.getIntProperty("MSG_NUM");
- // log.info("Message " + num + " received.");
-
- boolean isRedelivered = message.getJMSRedelivered();
- // log.debug("isRedelivered = " + isRedelivered);
-
- if (!isRedelivered)
- {
- // Countdown on the traffic light if there is one for the matching correlation id.
- PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationID);
-
- if (perCorrelationId != null)
- {
- CountDownLatch trafficLight = perCorrelationId.trafficLight;
-
- // Restart the timeout timer on every message.
- perCorrelationId.timeOutStart = System.nanoTime();
-
- // log.debug("Reply was expected, decrementing the latch for the id, " + correlationID);
-
- // Release waiting senders if there are some and using maxPending limit.
- if ((_maxPendingSize > 0))
- {
- // Decrement the count of sent but not yet received messages.
- int unreceived = _unreceived.decrementAndGet();
- int unreceivedSize =
- (unreceived * ((_messageSize == 0) ? 1 : _messageSize))
- / (_isPubSub ? getConsumersPerDestination() : 1);
-
- // log.debug("unreceived = " + unreceived);
- // log.debug("unreceivedSize = " + unreceivedSize);
-
- // synchronized (_sendPauseMonitor)
- // {
- if (unreceivedSize < _maxPendingSize)
- {
- _sendPauseMonitor.poll();
- }
- // }
- }
-
- // Decrement the countdown latch. Before this point, it is possible that two threads might enter this
- // method simultanesouly with the same correlation id. Decrementing the latch in a synchronized block
- // ensures that each thread will get a unique value for the remaining messages.
- long trueCount;
- long remainingCount;
-
- synchronized (trafficLight)
- {
- trafficLight.countDown();
-
- trueCount = trafficLight.getCount();
- remainingCount = trueCount - 1;
-
- // NDC.push("/rem" + remainingCount);
-
- // log.debug("remainingCount = " + remainingCount);
- // log.debug("trueCount = " + trueCount);
-
- // Commit on transaction batch size boundaries. At this point in time the waiting producer
- // remains blocked, even on the last message.
- // Commit count is divided by noOfConsumers in p2p mode, so that each consumer only commits on
- // each batch boundary. For pub/sub each consumer gets every message so no division is done.
- // When running in client ack mode, an ack is done instead of a commit, on the commit batch
- // size boundaries.
- long commitCount = _isPubSub ? remainingCount : (remainingCount / _noOfConsumers);
- // log.debug("commitCount = " + commitCount);
-
- if ((commitCount % _txBatchSize) == 0)
- {
- if (_consAckMode == 2)
- {
- // log.debug("Doing client ack for consumer " + consumerNo + ".");
- message.acknowledge();
- }
- else
- {
- // log.debug("Trying commit for consumer " + consumerNo + ".");
- commitTx(_consumerSession[consumerNo]);
- // log.info("Tx committed on consumer " + consumerNo);
- }
- }
-
- // Forward the message and remaining count to any interested chained message listener.
- if (_chainedMessageListener != null)
- {
- _chainedMessageListener.onMessage(message, (int) remainingCount, pingTime);
- }
-
- // Check if this is the last message, in which case release any waiting producers. This is done
- // after the transaction has been committed and any listeners notified.
- if (trueCount == 1)
- {
- trafficLight.countDown();
- }
- }
- }
- else
- {
- log.warn("Got unexpected message with correlationId: " + correlationID);
- }
- }
- else
- {
- log.warn("Got redelivered message, ignoring.");
- }
- }
- catch (JMSException e)
- {
- log.warn("There was a JMSException: " + e.getMessage(), e);
- }
- finally
- {
- // log.debug("public void onMessageWithConsumerNo(Message message, int consumerNo): ending");
- // NDC.clear();
- }
- }
-
- /**
- * Sends the specified number of ping message and then waits for all correlating replies. If the wait times out
- * before a reply arrives, then a null reply is returned from this method. This method allows the caller to specify
- * the correlation id.
- *
- * @param message The message to send. If this is null, one is generated.
- * @param numPings The number of ping messages to send.
- * @param timeout The timeout in milliseconds.
- * @param messageCorrelationId The message correlation id. If this is null, one is generated.
- *
- * @return The number of replies received. This may be less than the number sent if the timeout terminated the wait
- * for all prematurely.
- *
- * @throws JMSException All underlying JMSExceptions are allowed to fall through.
- * @throws InterruptedException When interrupted by a timeout
- */
- public int pingAndWaitForReply(Message message, int numPings, long timeout, String messageCorrelationId)
- throws JMSException, InterruptedException
- {
- /*log.debug("public int pingAndWaitForReply(Message message, int numPings = " + numPings + ", long timeout = "
- + timeout + ", String messageCorrelationId = " + messageCorrelationId + "): called");*/
-
- // Generate a unique correlation id to put on the messages before sending them, if one was not specified.
- if (messageCorrelationId == null)
- {
- messageCorrelationId = Long.toString(_correlationIdGenerator.incrementAndGet());
- }
-
- try
- {
- // NDC.push("prod");
-
- // Create a count down latch to count the number of replies with. This is created before the messages are
- // sent so that the replies cannot be received before the count down is created.
- // One is added to this, so that the last reply becomes a special case. The special case is that the
- // chained message listener must be called before this sender can be unblocked, but that decrementing the
- // countdown needs to be done before the chained listener can be called.
- PerCorrelationId perCorrelationId = new PerCorrelationId();
-
- perCorrelationId.trafficLight = new CountDownLatch(getExpectedNumPings(numPings) + 1);
- perCorrelationIds.put(messageCorrelationId, perCorrelationId);
-
- // Set up the current time as the start time for pinging on the correlation id. This is used to determine
- // timeouts.
- perCorrelationId.timeOutStart = System.nanoTime();
-
- // Send the specifed number of messages.
- pingNoWaitForReply(message, numPings, messageCorrelationId);
-
- boolean timedOut;
- boolean allMessagesReceived;
- int numReplies;
-
- do
- {
- // Block the current thread until replies to all the messages are received, or it times out.
- perCorrelationId.trafficLight.await(timeout, TimeUnit.MILLISECONDS);
-
- // Work out how many replies were receieved.
- numReplies = getExpectedNumPings(numPings) - (int) perCorrelationId.trafficLight.getCount();
-
- allMessagesReceived = numReplies == getExpectedNumPings(numPings);
-
- // log.debug("numReplies = " + numReplies);
- // log.debug("allMessagesReceived = " + allMessagesReceived);
-
- // Recheck the timeout condition.
- long now = System.nanoTime();
- long lastMessageReceievedAt = perCorrelationId.timeOutStart;
- timedOut = (now - lastMessageReceievedAt) > (timeout * 1000000);
-
- // log.debug("now = " + now);
- // log.debug("lastMessageReceievedAt = " + lastMessageReceievedAt);
- }
- while (!timedOut && !allMessagesReceived);
-
- if ((numReplies < getExpectedNumPings(numPings)) && _verbose)
- {
- log.info("Timed out (" + timeout + " ms) before all replies received on id, " + messageCorrelationId);
- }
- else if (_verbose)
- {
- log.info("Got all replies on id, " + messageCorrelationId);
- }
-
- // commitTx(_consumerSession);
-
- // log.debug("public int pingAndWaitForReply(Message message, int numPings, long timeout): ending");
-
- return numReplies;
- }
- // Ensure that the message countdown latch is always removed from the reply map. The reply map is long lived,
- // so will be a memory leak if this is not done.
- finally
- {
- // NDC.pop();
- perCorrelationIds.remove(messageCorrelationId);
- }
- }
-
- /**
- * Sends the specified number of ping messages and does not wait for correlating replies.
- *
- * @param message The message to send.
- * @param numPings The number of pings to send.
- * @param messageCorrelationId A correlation id to place on all messages sent.
- *
- * @throws JMSException All underlying JMSExceptions are allowed to fall through.
- */
- public void pingNoWaitForReply(Message message, int numPings, String messageCorrelationId) throws JMSException
- {
- /*log.debug("public void pingNoWaitForReply(Message message, int numPings = " + numPings
- + ", String messageCorrelationId = " + messageCorrelationId + "): called");*/
-
- if (message == null)
- {
- message = getTestMessage(getReplyDestinations().get(0), _messageSize, _persistent);
- }
-
- message.setJMSCorrelationID(messageCorrelationId);
-
- // Set up a committed flag to detect uncommitted messages at the end of the send loop. This may occurr if the
- // transaction batch size is not a factor of the number of pings. In which case an extra commit at the end is
- // needed.
- boolean committed = false;
-
- // Send all of the ping messages.
- for (int i = 0; i < numPings; i++)
- {
- // Re-timestamp the message.
- // message.setLongProperty(MESSAGE_TIMESTAMP_PROPNAME, System.nanoTime());
-
- // Send the message, passing in the message count.
- committed = sendMessage(i, message);
-
- // Spew out per message timings on every message sonly in verbose mode.
- /*if (_verbose)
- {
- log.info(timestampFormatter.format(new Date()) + ": Pinged at with correlation id, " + messageCorrelationId);
- }*/
- }
-
- // Call commit if the send loop finished before reaching a batch size boundary so there may still be uncommitted messages.
- if (!committed)
- {
- commitTx(_producerSession);
- }
- }
-
- /**
- * Sends the sepcified message, applies rate limiting and possibly commits the current transaction. The count of
- * messages sent so far must be specified and is used to round robin the ping destinations (where there are more
- * than one), and to determine if the transaction batch size has been reached and the sent messages should be
- * committed.
- *
- * @param i The count of messages sent so far in a loop of multiple calls to this send method.
- * @param message The message to send.
- *
- * @return <tt>true</tt> if the messages were committed, <tt>false</tt> otherwise.
- *
- * @throws JMSException All underlyiung JMSExceptions are allowed to fall through.
- */
- protected boolean sendMessage(int i, Message message) throws JMSException
- {
- try
- {
- NDC.push("id" + instanceId + "/prod");
-
- // log.debug("protected boolean sendMessage(int i = " + i + ", Message message): called");
- // log.debug("_txBatchSize = " + _txBatchSize);
-
- // Round robin the destinations as the messages are sent.
- Destination destination = _pingDestinations.get(i % _pingDestinations.size());
-
- // Prompt the user to kill the broker when doing failover testing.
- _failBeforeSend = waitForUserToPromptOnFailure(_failBeforeSend);
-
- // Get the test setup for the correlation id.
- String correlationID = message.getJMSCorrelationID();
- PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationID);
-
- // If necessary, wait until the max pending message size comes within its limit.
- if (_maxPendingSize > 0)
- {
- synchronized (_sendPauseMonitor)
- {
- // Used to keep track of the number of times that send has to wait.
- int numWaits = 0;
-
- // The maximum number of waits before the test gives up and fails. This has been chosen to correspond with
- // the test timeout.
- int waitLimit = (int) (TIMEOUT_DEFAULT / 10000);
-
- while (true)
- {
- // Get the size estimate of sent but not yet received messages.
- int unreceived = _unreceived.get();
- int unreceivedSize =
- (unreceived * ((_messageSize == 0) ? 1 : _messageSize))
- / (_isPubSub ? getConsumersPerDestination() : 1);
-
- // log.debug("unreceived = " + unreceived);
- // log.debug("unreceivedSize = " + unreceivedSize);
- // log.debug("_maxPendingSize = " + _maxPendingSize);
-
- if (unreceivedSize > _maxPendingSize)
- {
- // log.debug("unreceived size estimate over limit = " + unreceivedSize);
-
- // Fail the test if the send has had to wait more than the maximum allowed number of times.
- if (numWaits > waitLimit)
- {
- String errorMessage =
- "Send has had to wait for the unreceivedSize (" + unreceivedSize
- + ") to come below the maxPendingSize (" + _maxPendingSize + ") more that " + waitLimit
- + " times.";
- log.warn(errorMessage);
- throw new RuntimeException(errorMessage);
- }
-
- // Wait on the send pause barrier for the limit to be re-established.
- try
- {
- long start = System.nanoTime();
- // _sendPauseMonitor.wait(10000);
- _sendPauseMonitor.offer(new Object(), 10000, TimeUnit.MILLISECONDS);
- long end = System.nanoTime();
-
- // Count the wait only if it was for > 99% of the requested wait time.
- if (((float) (end - start) / (float) (10000 * 1000000L)) > 0.99)
- {
- numWaits++;
- }
- }
- catch (InterruptedException e)
- {
- // Restore the interrupted status
- Thread.currentThread().interrupt();
- throw new RuntimeException(e);
- }
- }
- else
- {
- break;
- }
- }
- }
- }
-
- // Send the message either to its round robin destination, or its default destination.
- // int num = numSent.incrementAndGet();
- // message.setIntProperty("MSG_NUM", num);
- setTimestamp(message);
-
- if (destination == null)
- {
- _producer.send(message);
- }
- else
- {
- _producer.send(destination, message);
- }
-
- // Increase the unreceived size, this may actually happen after the message is received.
- // The unreceived size is incremented by the number of consumers that will get a copy of the message,
- // in pub/sub mode.
- if (_maxPendingSize > 0)
- {
- int newUnreceivedCount = _unreceived.addAndGet(_isPubSub ? getConsumersPerDestination() : 1);
- // log.debug("newUnreceivedCount = " + newUnreceivedCount);
- }
-
- // Apply message rate throttling if a rate limit has been set up.
- if (_rateLimiter != null)
- {
- _rateLimiter.throttle();
- }
-
- // Call commit every time the commit batch size is reached.
- boolean committed = false;
-
- // Commit on every transaction batch size boundary. Here i + 1 is the count of actual messages sent.
- if (((i + 1) % _txBatchSize) == 0)
- {
- // log.debug("Trying commit on producer session.");
- committed = commitTx(_producerSession);
- }
-
- return committed;
- }
- finally
- {
- NDC.clear();
- }
- }
-
- /**
- * If the specified fail flag is set, this method waits for the user to cause a failure and then indicate to the
- * test that the failure has occurred, before the method returns.
- *
- * @param failFlag The fail flag to test.
- *
- * @return The new value for the fail flag. If the {@link #_failOnce} flag is set, then each fail flag is only
- * used once, then reset.
- */
- private boolean waitForUserToPromptOnFailure(boolean failFlag)
- {
- if (failFlag)
- {
- if (_failOnce)
- {
- failFlag = false;
- }
-
- // log.debug("Failing Before Send");
- waitForUser(KILL_BROKER_PROMPT);
- }
-
- return failFlag;
- }
-
- /**
- * Implements a single iteration of the ping loop. This sends the number of pings specified by the transaction batch
- * size property, and waits for replies to all of them. Any errors cause the publish flag to be cleared, which will
- * terminate the pinger.
- */
- public void pingLoop()
- {
- try
- {
- // Generate a sample message and time stamp it.
- Message msg = getTestMessage(_replyDestination, _messageSize, _persistent);
- // setTimestamp(msg);
-
- // Send the message and wait for a reply.
- pingAndWaitForReply(msg, TX_BATCH_SIZE_DEFAULT, TIMEOUT_DEFAULT, null);
- }
- catch (JMSException e)
- {
- _publish = false;
- // log.debug("There was a JMSException: " + e.getMessage(), e);
- }
- catch (InterruptedException e)
- {
- _publish = false;
- // log.debug("There was an interruption: " + e.getMessage(), e);
- }
- }
-
- /**
- * Sets a chained message listener. The message listener on this pinger, chains all its messages to the one set
- * here.
- *
- * @param messageListener The chained message listener.
- */
- public void setChainedMessageListener(ChainedMessageListener messageListener)
- {
- _chainedMessageListener = messageListener;
- }
-
- /** Removes any chained message listeners from this pinger. */
- public void removeChainedMessageListener()
- {
- _chainedMessageListener = null;
- }
-
- /**
- * Generates a test message of the specified size, with the specified reply-to destination and persistence flag.
- *
- * @param replyQueue The reply-to destination for the message.
- * @param messageSize The desired size of the message in bytes.
- * @param persistent <tt>true</tt> if the message should use persistent delivery, <tt>false</tt> otherwise.
- *
- * @return A freshly generated test message.
- *
- * @throws javax.jms.JMSException All underlying JMSException are allowed to fall through.
- */
- public Message getTestMessage(Destination replyQueue, int messageSize, boolean persistent) throws JMSException
- {
- // return TestMessageFactory.newObjectMessage(_producerSession, replyQueue, messageSize, persistent);
- return TestUtils.createTestMessageOfSize(_producerSession, messageSize);
- }
-
- /**
- * Sets the current time in nanoseconds as the timestamp on the message.
- *
- * @param msg The message to timestamp.
- *
- * @throws JMSException Any JMSExceptions are allowed to fall through.
- */
- protected void setTimestamp(Message msg) throws JMSException
- {
- /*if (((AMQSession)_producerSession).isStrictAMQP())
- {
- ((AMQMessage)msg).setTimestampProperty(new AMQShortString(MESSAGE_TIMESTAMP_PROPNAME), System.nanoTime());
- }
- else
- {*/
- msg.setLongProperty(MESSAGE_TIMESTAMP_PROPNAME, System.nanoTime());
- // }
- }
-
- /**
- * Extracts the nanosecond timestamp from a message.
- *
- * @param msg The message to extract the time stamp from.
- *
- * @return The timestamp in nanos.
- *
- * @throws JMSException Any JMSExceptions are allowed to fall through.
- */
- protected long getTimestamp(Message msg) throws JMSException
- {
- /*if (((AMQSession)_producerSession).isStrictAMQP())
- {
- Long value = ((AMQMessage)msg).getTimestampProperty(new AMQShortString(MESSAGE_TIMESTAMP_PROPNAME));
-
- return (value == null) ? 0L : value;
- }
- else
- {*/
- return msg.getLongProperty(PingPongProducer.MESSAGE_TIMESTAMP_PROPNAME);
- // }
- }
-
- /**
- * Stops the ping loop by clearing the publish flag. The current loop will complete when it notices that this flag
- * has been cleared.
- */
- public void stop()
- {
- _publish = false;
- }
-
- /**
- * Starts the producer and consumer connections.
- *
- * @throws JMSException Any JMSExceptions are allowed to fall through.
- */
- public void start() throws JMSException
- {
- // log.debug("public void start(): called");
-
- _connection.start();
- // log.debug("Producer started.");
-
- for (int i = 0; i < _noOfConsumers; i++)
- {
- _consumerConnection[i].start();
- // log.debug("Consumer " + i + " started.");
- }
- }
-
- /** Implements a ping loop that repeatedly pings until the publish flag becomes false. */
- public void run()
- {
- // Keep running until the publish flag is cleared.
- while (_publish)
- {
- pingLoop();
- }
- }
-
- /**
- * Callback method, implementing ExceptionListener. This should be registered to listen for exceptions on the
- * connection, this clears the publish flag which in turn will halt the ping loop.
- *
- * @param e The exception that triggered this callback method.
- */
- public void onException(JMSException e)
- {
- // log.debug("public void onException(JMSException e = " + e + "): called", e);
- _publish = false;
- }
-
- /**
- * Gets a shutdown hook that will cleanly shut this down when it is running the ping loop. This can be registered
- * with the runtime system as a shutdown hook.
- *
- * @return A shutdown hook for the ping loop.
- */
- public Thread getShutdownHook()
- {
- return new Thread(new Runnable()
- {
- public void run()
- {
- stop();
- }
- });
- }
-
- /**
- * Closes all of the producer and consumer connections.
- *
- * @throws JMSException All JMSException are allowed to fall through.
- */
- public void close() throws JMSException
- {
- // log.debug("public void close(): called");
-
- try
- {
- if (_connection != null)
- {
- // log.debug("Before close producer connection.");
- _connection.close();
- // log.debug("Closed producer connection.");
- }
-
- for (int i = 0; i < _noOfConsumers; i++)
- {
- if (_consumerConnection[i] != null)
- {
- // log.debug("Before close consumer connection " + i + ".");
- _consumerConnection[i].close();
- // log.debug("Closed consumer connection " + i + ".");
- }
- }
- }
- finally
- {
- _connection = null;
- _producerSession = null;
- _consumerSession = null;
- _consumerConnection = null;
- _producer = null;
- _consumer = null;
- _pingDestinations = null;
- _replyDestination = null;
- }
- }
-
- /**
- * Convenience method to commit the transaction on the specified controlSession. If the controlSession to commit on is not a
- * transactional controlSession, this method does nothing (unless the failover after send flag is set).
- *
- * <p/>If the {@link #_failAfterSend} flag is set, this will prompt the user to kill the broker before the commit is
- * applied. This flag applies whether the pinger is transactional or not.
- *
- * <p/>If the {@link #_failBeforeCommit} flag is set, this will prompt the user to kill the broker before the commit
- * is applied. If the {@link #_failAfterCommit} flag is set, this will prompt the user to kill the broker after the
- * commit is applied. These flags will only apply if using a transactional pinger.
- *
- * @param session The controlSession to commit
- *
- * @return <tt>true</tt> if the controlSession was committed, <tt>false</tt> if it was not.
- *
- * @throws javax.jms.JMSException If the commit fails and then the rollback fails.
- *
- * @todo Consider moving the fail after send logic into the send method. It is confusing to have it in this commit
- * method, because commits only apply to transactional pingers, but fail after send applied to transactional and
- * non-transactional alike.
- */
- protected boolean commitTx(Session session) throws JMSException
- {
- // log.debug("protected void commitTx(Session session): called");
-
- boolean committed = false;
-
- _failAfterSend = waitForUserToPromptOnFailure(_failAfterSend);
-
- if (session.getTransacted())
- {
- // log.debug("Session is transacted.");
-
- try
- {
- _failBeforeCommit = waitForUserToPromptOnFailure(_failBeforeCommit);
-
- long start = System.nanoTime();
- session.commit();
- committed = true;
- // log.debug("Time taken to commit :" + ((System.nanoTime() - start) / 1000000f) + " ms");
-
- _failAfterCommit = waitForUserToPromptOnFailure(_failAfterCommit);
-
- // log.debug("Session Commited.");
- }
- catch (JMSException e)
- {
- // log.debug("JMSException on commit:" + e.getMessage(), e);
-
- try
- {
- session.rollback();
- // log.debug("Message rolled back.");
- }
- catch (JMSException jmse)
- {
- // log.debug("JMSE on rollback:" + jmse.getMessage(), jmse);
-
- // Both commit and rollback failed. Throw the rollback exception.
- throw jmse;
- }
- }
- }
-
- return committed;
- }
-
- /**
- * Outputs a prompt to the console and waits for the user to press return.
- *
- * @param prompt The prompt to display on the console.
- */
- public void waitForUser(String prompt)
- {
- System.out.println(prompt);
-
- try
- {
- System.in.read();
- }
- catch (IOException e)
- {
- // Ignored.
- }
-
- System.out.println("Continuing.");
- }
-
- /**
- * Gets the number of consumers that are listening to each destination in the test.
- *
- * @return int The number of consumers subscribing to each topic.
- */
- public int getConsumersPerDestination()
- {
- return _noOfConsumers;
- }
-
- /**
- * Calculates how many pings are expected to be received for the given number sent.
- *
- * @param numpings The number of pings that will be sent.
- *
- * @return The number that should be received, for the test to pass.
- */
- public int getExpectedNumPings(int numpings)
- {
- // log.debug("public int getExpectedNumPings(int numpings = " + numpings + "): called");
-
- // log.debug("Each ping will be received by " + (_isPubSub ? getConsumersPerDestination() : 1) + " consumers.");
-
- return numpings * (_isPubSub ? getConsumersPerDestination() : 1);
- }
-
- /**
- * Defines a chained message listener interface that can be attached to this pinger. Whenever this pinger's {@link
- * PingPongProducer#onMessageWithConsumerNo} method is called, the chained listener set through the {@link
- * PingPongProducer#setChainedMessageListener} method is passed the message, and the remaining expected count of
- * messages with that correlation id.
- *
- * <p/>Provided only one pinger is producing messages with that correlation id, the chained listener will always be
- * given unique message counts. It will always be called while the producer waiting for all messages to arrive is
- * still blocked.
- */
- public static interface ChainedMessageListener
- {
- /**
- * Notifies interested listeners about message arrival and important test stats, the number of messages
- * remaining in the test, and the messages send timestamp.
- *
- * @param message The newly arrived message.
- * @param remainingCount The number of messages left to complete the test.
- * @param latency The nanosecond latency of the message.
- *
- * @throws JMSException Any JMS exceptions is allowed to fall through.
- */
- public void onMessage(Message message, int remainingCount, long latency) throws JMSException;
- }
-
- /**
- * Holds information on each correlation id. The countdown latch, the current timeout timer... More stuff to be
- * added to this: read/write lock to make onMessage more concurrent as described in class header comment.
- */
- protected static class PerCorrelationId
- {
- /** Holds a countdown on number of expected messages. */
- CountDownLatch trafficLight;
-
- /** Holds the last timestamp that the timeout was reset to. */
- Long timeOutStart;
- }
-}
+/* + * + * 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.requestreply; + +import org.apache.log4j.Logger; +import org.apache.log4j.NDC; + +import org.apache.qpid.test.framework.TestUtils; + +import org.apache.qpid.junit.extensions.BatchedThrottle; +import org.apache.qpid.junit.extensions.Throttle; +import org.apache.qpid.junit.extensions.util.CommandLineParser; +import org.apache.qpid.junit.extensions.util.ParsedProperties; + +import javax.jms.*; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; + +import java.io.*; +import java.net.InetAddress; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +/** + * PingPongProducer is a client that sends test messages, and waits for replies to these messages. The replies may + * either be generated by another client (see {@link PingPongBouncer}, or an extension of it may be used that listens + * to its own messages and does not send replies (see {@link org.apache.qpid.ping.PingClient}). The intention of ping + * pong producer is that it is a swiss-army knife test client that makes almost every aspect of its behaviour + * configurable. + * + * <p/>The pings are sent with a reply-to field set to a single temporary queue, which is the same for all pings. This + * means that this class has to do some work to correlate pings with pongs; it expectes the original message correlation + * id in the ping to be bounced back in the reply correlation id. + * + * <p/>This ping tool accepts a vast number of configuration options, all of which are passed in to the constructor. It + * can ping topics or queues; ping multiple destinations; do persistent pings; send messages of any size; do pings within + * transactions; control the number of pings to send in each transaction; limit its sending rate; and perform failover + * testing. A complete list of accepted parameters, default values and comments on their usage is provided here: + * + * <p/><table><caption>Parameters</caption> + * <tr><th> Parameter <th> Default <th> Comments + * <tr><td> messageSize <td> 0 <td> Message size in bytes. Not including any headers. + * <tr><td> destinationName <td> ping <td> The root name to use to generate destination names to ping. + * <tr><td> persistent <td> false <td> Determines whether peristent delivery is used. + * <tr><td> transacted <td> false <td> Determines whether messages are sent/received in transactions. + * <tr><td> broker <td> tcp://localhost:5672 <td> Determines the broker to connect to. + * <tr><td> virtualHost <td> test <td> Determines the virtual host to send all ping over. + * <tr><td> rate <td> 0 <td> The maximum rate (in hertz) to send messages at. 0 means no limit. + * <tr><td> verbose <td> false <td> The verbose flag for debugging. Prints to console on every message. + * <tr><td> pubsub <td> false <td> Whether to ping topics or queues. Uses p2p by default. + * <tr><td> failAfterCommit <td> false <td> Whether to prompt user to kill broker after a commit batch. + * <tr><td> failBeforeCommit <td> false <td> Whether to prompt user to kill broker before a commit batch. + * <tr><td> failAfterSend <td> false <td> Whether to prompt user to kill broker after a send. + * <tr><td> failBeforeSend <td> false <td> Whether to prompt user to kill broker before a send. + * <tr><td> failOnce <td> true <td> Whether to prompt for failover only once. + * <tr><td> username <td> guest <td> The username to access the broker with. + * <tr><td> password <td> guest <td> The password to access the broker with. + * <tr><td> selector <td> null <td> Not used. Defines a message selector to filter pings with. + * <tr><td> destinationCount <td> 1 <td> The number of destinations to send pings to. + * <tr><td> numConsumers <td> 1 <td> The number of consumers on each destination. + * <tr><td> timeout <td> 30000 <td> In milliseconds. The timeout to stop waiting for replies. + * <tr><td> commitBatchSize <td> 1 <td> The number of messages per transaction in transactional mode. + * <tr><td> uniqueDests <td> true <td> Whether each receivers only listens to one ping destination or all. + * <tr><td> durableDests <td> false <td> Whether or not durable destinations are used. + * <tr><td> ackMode <td> AUTO_ACK <td> The message acknowledgement mode. Possible values are: + * 0 - SESSION_TRANSACTED + * 1 - AUTO_ACKNOWLEDGE + * 2 - CLIENT_ACKNOWLEDGE + * 3 - DUPS_OK_ACKNOWLEDGE + * 257 - NO_ACKNOWLEDGE + * 258 - PRE_ACKNOWLEDGE + * <tr><td> consTransacted <td> false <td> Whether or not consumers use transactions. Defaults to the same value + * as the 'transacted' option if not seperately defined. + * <tr><td> consAckMode <td> AUTO_ACK <td> The message acknowledgement mode for consumers. Defaults to the same + * value as 'ackMode' if not seperately defined. + * <tr><td> maxPending <td> 0 <td> The maximum size in bytes, of messages sent but not yet received. + * Limits the volume of messages currently buffered on the client + * or broker. Can help scale test clients by limiting amount of buffered + * data to avoid out of memory errors. + * </table> + * + * <p/>This implements the Runnable interface with a run method that implements an infinite ping loop. The ping loop + * does all its work through helper methods, so that code wishing to run a ping-pong cycle is not forced to do so by + * starting a new thread. The command line invocation does take advantage of this ping loop. A shutdown hook is also + * registered to terminate the ping-pong loop cleanly. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Provide a ping and wait for all responses cycle. + * <tr><td> Provide command line invocation to loop the ping cycle on a configurable broker url. + * </table> + * + * @todo Use read/write lock in the onmessage, not for reading writing but to make use of a shared and exlcusive lock pair. + * Obtain read lock on all messages, before decrementing the message count. At the end of the on message method add a + * block that obtains the write lock for the very last message, releases any waiting producer. Means that the last + * message waits until all other messages have been handled before releasing producers but allows messages to be + * processed concurrently, unlike the current synchronized block. + */ +public class PingPongProducer implements Runnable, ExceptionListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(PingPongProducer.class); + + /** Holds the name of the property to determine whether of not client id is overridden at connection time. */ + public static final String OVERRIDE_CLIENT_ID_PROPNAME = "overrideClientId"; + + /** Holds the default value of the override client id flag. */ + public static final String OVERRIDE_CLIENT_ID_DEAFULT = "false"; + + /** Holds the name of the property to define the JNDI factory name with. */ + public static final String FACTORY_NAME_PROPNAME = "factoryName"; + + /** Holds the default JNDI name of the connection factory. */ + public static final String FACTORY_NAME_DEAFULT = "local"; + + /** Holds the name of the property to set the JNDI initial context properties with. */ + public static final String FILE_PROPERTIES_PROPNAME = "properties"; + + /** Holds the default file name of the JNDI initial context properties. */ + public static final String FILE_PROPERTIES_DEAFULT = "perftests.properties"; + + /** Holds the name of the property to get the test message size from. */ + public static final String MESSAGE_SIZE_PROPNAME = "messageSize"; + + /** Used to set up a default message size. */ + public static final int MESSAGE_SIZE_DEAFULT = 0; + + /** Holds the name of the property to get the ping queue name from. */ + public static final String PING_QUEUE_NAME_PROPNAME = "destinationName"; + + /** Holds the name of the default destination to send pings on. */ + public static final String PING_QUEUE_NAME_DEFAULT = "ping"; + + /** Holds the name of the property to get the queue name postfix from. */ + public static final String QUEUE_NAME_POSTFIX_PROPNAME = "queueNamePostfix"; + + /** Holds the default queue name postfix value. */ + public static final String QUEUE_NAME_POSTFIX_DEFAULT = ""; + + /** Holds the name of the property to get the test delivery mode from. */ + public static final String PERSISTENT_MODE_PROPNAME = "persistent"; + + /** Holds the message delivery mode to use for the test. */ + public static final boolean PERSISTENT_MODE_DEFAULT = false; + + /** Holds the name of the property to get the test transactional mode from. */ + public static final String TRANSACTED_PROPNAME = "transacted"; + + /** Holds the transactional mode to use for the test. */ + public static final boolean TRANSACTED_DEFAULT = false; + + /** Holds the name of the property to get the test consumer transacted mode from. */ + public static final String CONSUMER_TRANSACTED_PROPNAME = "consTransacted"; + + /** Holds the consumer transactional mode default setting. */ + public static final boolean CONSUMER_TRANSACTED_DEFAULT = false; + + /** Holds the name of the property to get the test broker url from. */ + public static final String BROKER_PROPNAME = "broker"; + + /** Holds the default broker url for the test. */ + public static final String BROKER_DEFAULT = "tcp://localhost:5672"; + + /** Holds the name of the property to get the test broker virtual path. */ + public static final String VIRTUAL_HOST_PROPNAME = "virtualHost"; + + /** Holds the default virtual path for the test. */ + public static final String VIRTUAL_HOST_DEFAULT = ""; + + /** Holds the name of the property to get the message rate from. */ + public static final String RATE_PROPNAME = "rate"; + + /** Defines the default rate (in pings per second) to send pings at. 0 means as fast as possible, no restriction. */ + public static final int RATE_DEFAULT = 0; + + /** Holds the name of the property to get the verbose mode proeprty from. */ + public static final String VERBOSE_PROPNAME = "verbose"; + + /** Holds the default verbose mode. */ + public static final boolean VERBOSE_DEFAULT = false; + + /** Holds the name of the property to get the p2p or pub/sub messaging mode from. */ + public static final String PUBSUB_PROPNAME = "pubsub"; + + /** Holds the pub/sub mode default, true means ping a topic, false means ping a queue. */ + public static final boolean PUBSUB_DEFAULT = false; + + /** Holds the name of the property to get the fail after commit flag from. */ + public static final String FAIL_AFTER_COMMIT_PROPNAME = "failAfterCommit"; + + /** Holds the default failover after commit test flag. */ + public static final boolean FAIL_AFTER_COMMIT_DEFAULT = false; + + /** Holds the name of the proeprty to get the fail before commit flag from. */ + public static final String FAIL_BEFORE_COMMIT_PROPNAME = "failBeforeCommit"; + + /** Holds the default failover before commit test flag. */ + public static final boolean FAIL_BEFORE_COMMIT_DEFAULT = false; + + /** Holds the name of the proeprty to get the fail after send flag from. */ + public static final String FAIL_AFTER_SEND_PROPNAME = "failAfterSend"; + + /** Holds the default failover after send test flag. */ + public static final boolean FAIL_AFTER_SEND_DEFAULT = false; + + /** Holds the name of the property to get the fail before send flag from. */ + public static final String FAIL_BEFORE_SEND_PROPNAME = "failBeforeSend"; + + /** Holds the default failover before send test flag. */ + public static final boolean FAIL_BEFORE_SEND_DEFAULT = false; + + /** Holds the name of the property to get the fail once flag from. */ + public static final String FAIL_ONCE_PROPNAME = "failOnce"; + + /** The default failover once flag, true means only do one failover, false means failover on every commit cycle. */ + public static final boolean FAIL_ONCE_DEFAULT = true; + + /** Holds the name of the property to get the broker access username from. */ + public static final String USERNAME_PROPNAME = "username"; + + /** Holds the default broker log on username. */ + public static final String USERNAME_DEFAULT = "guest"; + + /** Holds the name of the property to get the broker access password from. */ + public static final String PASSWORD_PROPNAME = "password"; + + /** Holds the default broker log on password. */ + public static final String PASSWORD_DEFAULT = "guest"; + + /** Holds the name of the proeprty to get the. */ + public static final String SELECTOR_PROPNAME = "selector"; + + /** Holds the default message selector. */ + public static final String SELECTOR_DEFAULT = ""; + + /** Holds the name of the property to get the destination count from. */ + public static final String DESTINATION_COUNT_PROPNAME = "destinationCount"; + + /** Defines the default number of destinations to ping. */ + public static final int DESTINATION_COUNT_DEFAULT = 1; + + /** Holds the name of the property to get the number of consumers per destination from. */ + public static final String NUM_CONSUMERS_PROPNAME = "numConsumers"; + + /** Defines the default number consumers per destination. */ + public static final int NUM_CONSUMERS_DEFAULT = 1; + + /** Holds the name of the property to get the waiting timeout for response messages. */ + public static final String TIMEOUT_PROPNAME = "timeout"; + + /** Default time to wait before assuming that a ping has timed out. */ + public static final long TIMEOUT_DEFAULT = 30000; + + /** Holds the name of the property to get the commit batch size from. */ + public static final String TX_BATCH_SIZE_PROPNAME = "commitBatchSize"; + + /** Defines the default number of pings to send in each transaction when running transactionally. */ + public static final int TX_BATCH_SIZE_DEFAULT = 1; + + /** Holds the name of the property to get the unique destinations flag from. */ + public static final String UNIQUE_DESTS_PROPNAME = "uniqueDests"; + + /** Defines the default value for the unique destinations property. */ + public static final boolean UNIQUE_DESTS_DEFAULT = true; + + /** Holds the name of the property to get the durable destinations flag from. */ + public static final String DURABLE_DESTS_PROPNAME = "durableDests"; + + /** Defines the default value of the durable destinations flag. */ + public static final boolean DURABLE_DESTS_DEFAULT = false; + + /** Holds the name of the proeprty to get the message acknowledgement mode from. */ + public static final String ACK_MODE_PROPNAME = "ackMode"; + + /** Defines the default message acknowledgement mode. */ + public static final int ACK_MODE_DEFAULT = Session.AUTO_ACKNOWLEDGE; + + /** Holds the name of the property to get the consumers message acknowledgement mode from. */ + public static final String CONSUMER_ACK_MODE_PROPNAME = "consAckMode"; + + /** Defines the default consumers message acknowledgement mode. */ + public static final int CONSUMER_ACK_MODE_DEFAULT = Session.AUTO_ACKNOWLEDGE; + + /** Holds the name of the property to get the maximum pending message size setting from. */ + public static final String MAX_PENDING_PROPNAME = "maxPending"; + + /** Defines the default value for the maximum pending message size setting. 0 means no limit. */ + public static final int MAX_PENDING_DEFAULT = 0; + + /** Defines the default prefetch size to use when consuming messages. */ + public static final int PREFETCH_DEFAULT = 100; + + /** Defines the default value of the no local flag to use when consuming messages. */ + public static final boolean NO_LOCAL_DEFAULT = false; + + /** Defines the default value of the exclusive flag to use when consuming messages. */ + public static final boolean EXCLUSIVE_DEFAULT = false; + + /** Holds the name of the property to store nanosecond timestamps in ping messages with. */ + public static final String MESSAGE_TIMESTAMP_PROPNAME = "timestamp"; + + /** Holds the default configuration properties. */ + public static ParsedProperties defaults = new ParsedProperties(); + + static + { + defaults.setPropertyIfNull(OVERRIDE_CLIENT_ID_PROPNAME, OVERRIDE_CLIENT_ID_DEAFULT); + defaults.setPropertyIfNull(FILE_PROPERTIES_PROPNAME, FILE_PROPERTIES_DEAFULT); + defaults.setPropertyIfNull(FACTORY_NAME_PROPNAME, FACTORY_NAME_DEAFULT); + defaults.setPropertyIfNull(BROKER_PROPNAME, BROKER_DEFAULT); + defaults.setPropertyIfNull(USERNAME_PROPNAME, USERNAME_DEFAULT); + defaults.setPropertyIfNull(PASSWORD_PROPNAME, PASSWORD_DEFAULT); + defaults.setPropertyIfNull(VIRTUAL_HOST_PROPNAME, VIRTUAL_HOST_DEFAULT); + defaults.setPropertyIfNull(PING_QUEUE_NAME_PROPNAME, PING_QUEUE_NAME_DEFAULT); + defaults.setPropertyIfNull(QUEUE_NAME_POSTFIX_PROPNAME, QUEUE_NAME_POSTFIX_DEFAULT); + defaults.setPropertyIfNull(SELECTOR_PROPNAME, SELECTOR_DEFAULT); + defaults.setPropertyIfNull(TRANSACTED_PROPNAME, TRANSACTED_DEFAULT); + defaults.setPropertyIfNull(CONSUMER_TRANSACTED_PROPNAME, CONSUMER_TRANSACTED_DEFAULT); + defaults.setPropertyIfNull(PERSISTENT_MODE_PROPNAME, PERSISTENT_MODE_DEFAULT); + defaults.setPropertyIfNull(ACK_MODE_PROPNAME, ACK_MODE_DEFAULT); + defaults.setPropertyIfNull(CONSUMER_ACK_MODE_PROPNAME, CONSUMER_ACK_MODE_DEFAULT); + defaults.setPropertyIfNull(MESSAGE_SIZE_PROPNAME, MESSAGE_SIZE_DEAFULT); + defaults.setPropertyIfNull(VERBOSE_PROPNAME, VERBOSE_DEFAULT); + defaults.setPropertyIfNull(PUBSUB_PROPNAME, PUBSUB_DEFAULT); + defaults.setPropertyIfNull(UNIQUE_DESTS_PROPNAME, UNIQUE_DESTS_DEFAULT); + defaults.setPropertyIfNull(DURABLE_DESTS_PROPNAME, DURABLE_DESTS_DEFAULT); + defaults.setPropertyIfNull(FAIL_BEFORE_COMMIT_PROPNAME, FAIL_BEFORE_COMMIT_DEFAULT); + defaults.setPropertyIfNull(FAIL_AFTER_COMMIT_PROPNAME, FAIL_AFTER_COMMIT_DEFAULT); + defaults.setPropertyIfNull(FAIL_BEFORE_SEND_PROPNAME, FAIL_BEFORE_SEND_DEFAULT); + defaults.setPropertyIfNull(FAIL_AFTER_SEND_PROPNAME, FAIL_AFTER_SEND_DEFAULT); + defaults.setPropertyIfNull(FAIL_ONCE_PROPNAME, FAIL_ONCE_DEFAULT); + defaults.setPropertyIfNull(TX_BATCH_SIZE_PROPNAME, TX_BATCH_SIZE_DEFAULT); + defaults.setPropertyIfNull(DESTINATION_COUNT_PROPNAME, DESTINATION_COUNT_DEFAULT); + defaults.setPropertyIfNull(NUM_CONSUMERS_PROPNAME, NUM_CONSUMERS_DEFAULT); + defaults.setPropertyIfNull(RATE_PROPNAME, RATE_DEFAULT); + defaults.setPropertyIfNull(TIMEOUT_PROPNAME, TIMEOUT_DEFAULT); + defaults.setPropertyIfNull(MAX_PENDING_PROPNAME, MAX_PENDING_DEFAULT); + } + + /** Allows setting of client ID on the connection, rather than through the connection URL. */ + protected boolean _overrideClientId; + + /** Holds the JNDI name of the JMS connection factory. */ + protected String _factoryName; + + /** Holds the name of the properties file to configure JNDI with. */ + protected String _fileProperties; + + /** Holds the broker url. */ + protected String _brokerDetails; + + /** Holds the username to access the broker with. */ + protected String _username; + + /** Holds the password to access the broker with. */ + protected String _password; + + /** Holds the virtual host on the broker to run the tests through. */ + protected String _virtualpath; + + /** Holds the root name from which to generate test destination names. */ + protected String _destinationName; + + /** Holds the default queue name postfix value. */ + protected String _queueNamePostfix; + + /** Holds the message selector to filter the pings with. */ + protected String _selector; + + /** Holds the producers transactional mode flag. */ + protected boolean _transacted; + + /** Holds the consumers transactional mode flag. */ + protected boolean _consTransacted; + + /** Determines whether this producer sends persistent messages. */ + protected boolean _persistent; + + /** Holds the acknowledgement mode used for the producers. */ + protected int _ackMode; + + /** Holds the acknowledgement mode setting for the consumers. */ + protected int _consAckMode; + + /** Determines what size of messages this producer sends. */ + protected int _messageSize; + + /** Used to indicate that the ping loop should print out whenever it pings. */ + protected boolean _verbose; + + /** Flag used to indicate if this is a point to point or pub/sub ping client. */ + protected boolean _isPubSub; + + /** Flag used to indicate if the destinations should be unique client. */ + protected boolean _isUnique; + + /** Flag used to indicate that durable destination should be used. */ + protected boolean _isDurable; + + /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover before a commit. */ + protected boolean _failBeforeCommit; + + /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover after a commit. */ + protected boolean _failAfterCommit; + + /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover before a send. */ + protected boolean _failBeforeSend; + + /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover after a send. */ + protected boolean _failAfterSend; + + /** Flag used to indicate that failover prompting should only be done on the first commit, not on every commit. */ + protected boolean _failOnce; + + /** Holds the number of sends that should be performed in every transaction when using transactions. */ + protected int _txBatchSize; + + /** Holds the number of destinations to ping. */ + protected int _noOfDestinations; + + /** Holds the number of consumers per destination. */ + protected int _noOfConsumers; + + /** Holds the maximum send rate in herz. */ + protected int _rate; + + /** + * Holds the size of the maximum amount of pending data that the client should buffer, sending is suspended + * if this limit is breached. + */ + protected int _maxPendingSize; + + /** A source for providing sequential unique correlation ids. These will be unique within the same JVM. */ + private static AtomicLong _correlationIdGenerator = new AtomicLong(0L); + + /** A source for providing sequential unqiue ids for instances of this class to be identifed with. */ + private static AtomicInteger _instanceIdGenerator = new AtomicInteger(0); + + /** Holds this instances unique id. */ + private int instanceId; + + /** + * Holds a map from message ids to latches on which threads wait for replies. This map is shared accross multiple + * ping producers on the same JVM. + */ + private static Map<String, PerCorrelationId> perCorrelationIds = + Collections.synchronizedMap(new HashMap<String, PerCorrelationId>()); + + /** A convenient formatter to use when time stamping output. */ + protected static final DateFormat timestampFormatter = new SimpleDateFormat("hh:mm:ss:SS"); + + /** Holds the connection for the message producer. */ + protected Connection _connection; + + /** Holds the consumer connections. */ + protected Connection[] _consumerConnection; + + /** Holds the controlSession on which ping replies are received. */ + protected Session[] _consumerSession; + + /** Holds the producer controlSession, needed to create ping messages. */ + protected Session _producerSession; + + /** Holds the destination where the response messages will arrive. */ + protected Destination _replyDestination; + + /** Holds the set of destinations that this ping producer pings. */ + protected List<Destination> _pingDestinations; + + /** Used to restrict the sending rate to a specified limit. */ + protected Throttle _rateLimiter; + + /** Holds a message listener that this message listener chains all its messages to. */ + protected ChainedMessageListener _chainedMessageListener = null; + + /** + * This id generator is used to generate ids to append to the queue name to ensure that queues can be unique when + * creating multiple ping producers in the same JVM. + */ + protected static AtomicInteger _queueJVMSequenceID = new AtomicInteger(); + + /** + * This id generator is used to generates ids that are only unique within this pinger. Creating multiple pingers + * on the same JVM using this id generator will allow them to ping on the same queues. + */ + protected AtomicInteger _queueSharedID = new AtomicInteger(); + + /** Used to tell the ping loop when to terminate, it only runs while this is true. */ + protected boolean _publish = true; + + /** Holds the message producer to send the pings through. */ + protected MessageProducer _producer; + + /** Holds the message consumer to receive the ping replies through. */ + protected MessageConsumer[] _consumer; + + /** The prompt to display when asking the user to kill the broker for failover testing. */ + private static final String KILL_BROKER_PROMPT = "Kill broker now, then press Return."; + + /** Holds the name for this test client to be identified to the broker with. */ + private String _clientID; + + /** Keeps count of the total messages sent purely for debugging purposes. */ + private static AtomicInteger numSent = new AtomicInteger(); + + /** + * Holds a monitor which is used to synchronize sender and receivers threads, where the sender has elected + * to wait until the number of unreceived message is reduced before continuing to send. This monitor is a + * fair SynchronousQueue becuase that provides fair scheduling, to ensure that all producer threads get an + * equal chance to produce messages. + */ + static final SynchronousQueue _sendPauseMonitor = new SynchronousQueue(true); + + /** Keeps a count of the number of message currently sent but not received. */ + static AtomicInteger _unreceived = new AtomicInteger(0); + + /** + * Creates a ping producer with the specified parameters, of which there are many. See the class level comments + * for details. This constructor creates a connection to the broker and creates producer and consumer sessions on + * it, to send and recieve its pings and replies on. + * + * @param overrides Properties containing any desired overrides to the defaults. + * + * @throws Exception Any exceptions are allowed to fall through. + */ + public PingPongProducer(Properties overrides) throws Exception + { + // log.debug("public PingPongProducer(Properties overrides = " + overrides + "): called"); + instanceId = _instanceIdGenerator.getAndIncrement(); + + // Create a set of parsed properties from the defaults overriden by the passed in values. + ParsedProperties properties = new ParsedProperties(defaults); + properties.putAll(overrides); + + // Extract the configuration properties to set the pinger up with. + _overrideClientId = properties.getPropertyAsBoolean(OVERRIDE_CLIENT_ID_PROPNAME); + _factoryName = properties.getProperty(FACTORY_NAME_PROPNAME); + _fileProperties = properties.getProperty(FILE_PROPERTIES_PROPNAME); + _brokerDetails = properties.getProperty(BROKER_PROPNAME); + _username = properties.getProperty(USERNAME_PROPNAME); + _password = properties.getProperty(PASSWORD_PROPNAME); + _virtualpath = properties.getProperty(VIRTUAL_HOST_PROPNAME); + _destinationName = properties.getProperty(PING_QUEUE_NAME_PROPNAME); + _queueNamePostfix = properties.getProperty(QUEUE_NAME_POSTFIX_PROPNAME); + _selector = properties.getProperty(SELECTOR_PROPNAME); + _transacted = properties.getPropertyAsBoolean(TRANSACTED_PROPNAME); + _consTransacted = properties.getPropertyAsBoolean(CONSUMER_TRANSACTED_PROPNAME); + _persistent = properties.getPropertyAsBoolean(PERSISTENT_MODE_PROPNAME); + _messageSize = properties.getPropertyAsInteger(MESSAGE_SIZE_PROPNAME); + _verbose = properties.getPropertyAsBoolean(VERBOSE_PROPNAME); + _failAfterCommit = properties.getPropertyAsBoolean(FAIL_AFTER_COMMIT_PROPNAME); + _failBeforeCommit = properties.getPropertyAsBoolean(FAIL_BEFORE_COMMIT_PROPNAME); + _failAfterSend = properties.getPropertyAsBoolean(FAIL_AFTER_SEND_PROPNAME); + _failBeforeSend = properties.getPropertyAsBoolean(FAIL_BEFORE_SEND_PROPNAME); + _failOnce = properties.getPropertyAsBoolean(FAIL_ONCE_PROPNAME); + _txBatchSize = properties.getPropertyAsInteger(TX_BATCH_SIZE_PROPNAME); + _noOfDestinations = properties.getPropertyAsInteger(DESTINATION_COUNT_PROPNAME); + _noOfConsumers = properties.getPropertyAsInteger(NUM_CONSUMERS_PROPNAME); + _rate = properties.getPropertyAsInteger(RATE_PROPNAME); + _isPubSub = properties.getPropertyAsBoolean(PUBSUB_PROPNAME); + _isUnique = properties.getPropertyAsBoolean(UNIQUE_DESTS_PROPNAME); + _isDurable = properties.getPropertyAsBoolean(DURABLE_DESTS_PROPNAME); + _ackMode = _transacted ? 0 : properties.getPropertyAsInteger(ACK_MODE_PROPNAME); + _consAckMode = _consTransacted ? 0 : properties.getPropertyAsInteger(CONSUMER_ACK_MODE_PROPNAME); + _maxPendingSize = properties.getPropertyAsInteger(MAX_PENDING_PROPNAME); + + // Check that one or more destinations were specified. + if (_noOfDestinations < 1) + { + throw new IllegalArgumentException("There must be at least one destination."); + } + + // Set up a throttle to control the send rate, if a rate > 0 is specified. + if (_rate > 0) + { + _rateLimiter = new BatchedThrottle(); + _rateLimiter.setRate(_rate); + } + + // Create the connection and message producers/consumers. + // establishConnection(true, true); + } + + /** + * Establishes a connection to the broker and creates message consumers and producers based on the parameters + * that this ping client was created with. + * + * @param producer Flag to indicate whether or not the producer should be set up. + * @param consumer Flag to indicate whether or not the consumers should be set up. + * + * @throws Exception Any exceptions are allowed to fall through. + */ + public void establishConnection(boolean producer, boolean consumer) throws Exception + { + // log.debug("public void establishConnection(): called"); + + // Generate a unique identifying name for this client, based on it ip address and the current time. + InetAddress address = InetAddress.getLocalHost(); + // _clientID = address.getHostName() + System.currentTimeMillis(); + _clientID = "perftest_" + instanceId; + + // Create a connection to the broker. + createConnection(_clientID); + + // Create transactional or non-transactional sessions, based on the command line arguments. + _producerSession = _connection.createSession(_transacted, _ackMode); + + _consumerSession = new Session[_noOfConsumers]; + + for (int i = 0; i < _noOfConsumers; i++) + { + _consumerSession[i] = _consumerConnection[i].createSession(_consTransacted, _consAckMode); + } + + // Create the destinations to send pings to and receive replies from. + _replyDestination = _consumerSession[0].createTemporaryQueue(); + createPingDestinations(_noOfDestinations, _selector, _destinationName, _isUnique, _isDurable); + + // Create the message producer only if instructed to. + if (producer) + { + createProducer(); + } + + // Create the message consumer only if instructed to. + if (consumer) + { + createReplyConsumers(getReplyDestinations(), _selector); + } + } + + /** + * Establishes a connection to the broker, based on the configuration parameters that this ping client was + * created with. + * + * @param clientID The clients identifier. + * + * @throws JMSException Underlying exceptions allowed to fall through. + * @throws NamingException Underlying exceptions allowed to fall through. + * @throws IOException Underlying exceptions allowed to fall through. + */ + protected void createConnection(String clientID) throws JMSException, NamingException, IOException + { + // _log.debug("protected void createConnection(String clientID = " + clientID + "): called"); + + // _log.debug("Creating a connection for the message producer."); + File propsFile = new File(_fileProperties); + InputStream is = new FileInputStream(propsFile); + Properties properties = new Properties(); + properties.load(is); + + Context context = new InitialContext(properties); + ConnectionFactory factory = (ConnectionFactory) context.lookup(_factoryName); + _connection = factory.createConnection(_username, _password); + + if (_overrideClientId) + { + _connection.setClientID(clientID); + } + + // _log.debug("Creating " + _noOfConsumers + " connections for the consumers."); + + _consumerConnection = new Connection[_noOfConsumers]; + + for (int i = 0; i < _noOfConsumers; i++) + { + _consumerConnection[i] = factory.createConnection(_username, _password); + // _consumerConnection[i].setClientID(clientID); + } + } + + /** + * Starts a ping-pong loop running from the command line. The bounce back client {@link PingPongBouncer} also needs + * to be started to bounce the pings back again. + * + * @param args The command line arguments. + */ + public static void main(String[] args) + { + try + { + Properties options = + CommandLineParser.processCommandLine(args, new CommandLineParser(new String[][] {}), System.getProperties()); + + // Create a ping producer overriding its defaults with all options passed on the command line. + PingPongProducer pingProducer = new PingPongProducer(options); + pingProducer.establishConnection(true, true); + + // Start the ping producers dispatch thread running. + pingProducer._connection.start(); + + // Create a shutdown hook to terminate the ping-pong producer. + Runtime.getRuntime().addShutdownHook(pingProducer.getShutdownHook()); + + // Ensure that the ping pong producer is registered to listen for exceptions on the connection too. + pingProducer._connection.setExceptionListener(pingProducer); + + // Create the ping loop thread and run it until it is terminated by the shutdown hook or exception. + Thread pingThread = new Thread(pingProducer); + pingThread.run(); + pingThread.join(); + } + catch (Exception e) + { + System.err.println(e.getMessage()); + log.error("Top level handler caught execption.", e); + System.exit(1); + } + } + + /** + * Convenience method for a short pause. + * + * @param sleepTime The time in milliseconds to pause for. + */ + public static void pause(long sleepTime) + { + if (sleepTime > 0) + { + try + { + Thread.sleep(sleepTime); + } + catch (InterruptedException ie) + { } + } + } + + /** + * Gets all the reply destinations (to listen for replies on). In this case this will just be the single reply to + * destination of this pinger. + * + * @return The single reply to destination of this pinger, wrapped in a list. + */ + public List<Destination> getReplyDestinations() + { + // log.debug("public List<Destination> getReplyDestinations(): called"); + + List<Destination> replyDestinations = new ArrayList<Destination>(); + replyDestinations.add(_replyDestination); + + // log.debug("replyDestinations = " + replyDestinations); + + return replyDestinations; + } + + /** + * Creates the producer to send the pings on. This is created without a default destination. Its persistent delivery + * flag is set accoring the ping producer creation options. + * + * @throws JMSException Any JMSExceptions are allowed to fall through. + */ + public void createProducer() throws JMSException + { + // log.debug("public void createProducer(): called"); + + _producer = (MessageProducer) _producerSession.createProducer(null); + _producer.setDeliveryMode(_persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT); + + // log.debug("Created producer for " + (_persistent ? "persistent" : "non-persistent") + " messages."); + } + + /** + * Creates consumers for the specified number of destinations. The destinations themselves are also created by this + * method. + * + * @param noOfDestinations The number of destinations to create consumers for. + * @param selector The message selector to filter the consumers with. + * @param rootName The root of the name, or actual name if only one is being created. + * @param unique <tt>true</tt> to make the destinations unique to this pinger, <tt>false</tt> to share the + * numbering with all pingers on the same JVM. + * @param durable If the destinations are durable topics. + * + * @throws JMSException Any JMSExceptions are allowed to fall through. + */ + public void createPingDestinations(int noOfDestinations, String selector, String rootName, boolean unique, + boolean durable) throws JMSException + { + /*log.debug("public void createPingDestinations(int noOfDestinations = " + noOfDestinations + ", String selector = " + + selector + ", String rootName = " + rootName + ", boolean unique = " + unique + ", boolean durable = " + + durable + "): called");*/ + + _pingDestinations = new ArrayList<Destination>(); + + // Create the desired number of ping destinations and consumers for them. + // log.debug("Creating " + noOfDestinations + " destinations to ping."); + + for (int i = 0; i < noOfDestinations; i++) + { + Destination destination; + String id; + + // Generate an id, unique within this pinger or to the whole JVM depending on the unique flag. + if (unique) + { + // log.debug("Creating unique destinations."); + id = "_" + _queueJVMSequenceID.incrementAndGet() + "_" + _connection.getClientID(); + } + else + { + // log.debug("Creating shared destinations."); + id = "_" + _queueSharedID.incrementAndGet(); + } + + // Check if this is a pub/sub pinger, in which case create topics. + if (_isPubSub) + { + destination = _producerSession.createTopic(rootName + id); + // log.debug("Created non-durable topic " + destination); + + if (durable) + { + _producerSession.createDurableSubscriber((Topic) destination, _connection.getClientID()); + } + } + // Otherwise this is a p2p pinger, in which case create queues. + else + { + destination = _producerSession.createQueue(rootName + id + _queueNamePostfix); + // log.debug("Created queue " + destination); + } + + // Keep the destination. + _pingDestinations.add(destination); + } + } + + /** + * Creates consumers for the specified destinations and registers this pinger to listen to their messages. + * + * @param destinations The destinations to listen to. + * @param selector A selector to filter the messages with. + * + * @throws javax.jms.JMSException Any JMSExceptions are allowed to fall through. + */ + public void createReplyConsumers(Collection<Destination> destinations, String selector) throws JMSException + { + /*log.debug("public void createReplyConsumers(Collection<Destination> destinations = " + destinations + + ", String selector = " + selector + "): called");*/ + + log.debug("There are " + destinations.size() + " destinations."); + log.debug("Creating " + _noOfConsumers + " consumers on each destination."); + log.debug("Total number of consumers is: " + (destinations.size() * _noOfConsumers)); + + for (Destination destination : destinations) + { + _consumer = new MessageConsumer[_noOfConsumers]; + + for (int i = 0; i < _noOfConsumers; i++) + { + // Create a consumer for the destination and set this pinger to listen to its messages. + _consumer[i] = _consumerSession[i].createConsumer(destination, selector, NO_LOCAL_DEFAULT); + + final int consumerNo = i; + + _consumer[i].setMessageListener(new MessageListener() + { + public void onMessage(Message message) + { + onMessageWithConsumerNo(message, consumerNo); + } + }); + + log.debug("Set consumer " + i + " to listen to replies sent to destination: " + destination); + } + } + } + + /** + * Stores the received message in the replies map, then resets the boolean latch that a thread waiting for a + * correlating reply may be waiting on. This is only done if the reply has a correlation id that is expected in the + * replies map. + * + * @param message The received message. + * @param consumerNo The consumer number within this test pinger instance. + */ + public void onMessageWithConsumerNo(Message message, int consumerNo) + { + // log.debug("public void onMessageWithConsumerNo(Message message, int consumerNo = " + consumerNo + "): called"); + try + { + long now = System.nanoTime(); + long timestamp = getTimestamp(message); + long pingTime = now - timestamp; + + // NDC.push("id" + instanceId + "/cons" + consumerNo); + + // Extract the messages correlation id. + String correlationID = message.getJMSCorrelationID(); + // log.debug("correlationID = " + correlationID); + + // int num = message.getIntProperty("MSG_NUM"); + // log.info("Message " + num + " received."); + + boolean isRedelivered = message.getJMSRedelivered(); + // log.debug("isRedelivered = " + isRedelivered); + + if (!isRedelivered) + { + // Countdown on the traffic light if there is one for the matching correlation id. + PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationID); + + if (perCorrelationId != null) + { + CountDownLatch trafficLight = perCorrelationId.trafficLight; + + // Restart the timeout timer on every message. + perCorrelationId.timeOutStart = System.nanoTime(); + + // log.debug("Reply was expected, decrementing the latch for the id, " + correlationID); + + // Release waiting senders if there are some and using maxPending limit. + if ((_maxPendingSize > 0)) + { + // Decrement the count of sent but not yet received messages. + int unreceived = _unreceived.decrementAndGet(); + int unreceivedSize = + (unreceived * ((_messageSize == 0) ? 1 : _messageSize)) + / (_isPubSub ? getConsumersPerDestination() : 1); + + // log.debug("unreceived = " + unreceived); + // log.debug("unreceivedSize = " + unreceivedSize); + + // synchronized (_sendPauseMonitor) + // { + if (unreceivedSize < _maxPendingSize) + { + _sendPauseMonitor.poll(); + } + // } + } + + // Decrement the countdown latch. Before this point, it is possible that two threads might enter this + // method simultanesouly with the same correlation id. Decrementing the latch in a synchronized block + // ensures that each thread will get a unique value for the remaining messages. + long trueCount; + long remainingCount; + + synchronized (trafficLight) + { + trafficLight.countDown(); + + trueCount = trafficLight.getCount(); + remainingCount = trueCount - 1; + + // NDC.push("/rem" + remainingCount); + + // log.debug("remainingCount = " + remainingCount); + // log.debug("trueCount = " + trueCount); + + // Commit on transaction batch size boundaries. At this point in time the waiting producer + // remains blocked, even on the last message. + // Commit count is divided by noOfConsumers in p2p mode, so that each consumer only commits on + // each batch boundary. For pub/sub each consumer gets every message so no division is done. + // When running in client ack mode, an ack is done instead of a commit, on the commit batch + // size boundaries. + long commitCount = _isPubSub ? remainingCount : (remainingCount / _noOfConsumers); + // log.debug("commitCount = " + commitCount); + + if ((commitCount % _txBatchSize) == 0) + { + if (_consAckMode == 2) + { + // log.debug("Doing client ack for consumer " + consumerNo + "."); + message.acknowledge(); + } + else + { + // log.debug("Trying commit for consumer " + consumerNo + "."); + commitTx(_consumerSession[consumerNo]); + // log.info("Tx committed on consumer " + consumerNo); + } + } + + // Forward the message and remaining count to any interested chained message listener. + if (_chainedMessageListener != null) + { + _chainedMessageListener.onMessage(message, (int) remainingCount, pingTime); + } + + // Check if this is the last message, in which case release any waiting producers. This is done + // after the transaction has been committed and any listeners notified. + if (trueCount == 1) + { + trafficLight.countDown(); + } + } + } + else + { + log.warn("Got unexpected message with correlationId: " + correlationID); + } + } + else + { + log.warn("Got redelivered message, ignoring."); + } + } + catch (JMSException e) + { + log.warn("There was a JMSException: " + e.getMessage(), e); + } + finally + { + // log.debug("public void onMessageWithConsumerNo(Message message, int consumerNo): ending"); + // NDC.clear(); + } + } + + /** + * Sends the specified number of ping message and then waits for all correlating replies. If the wait times out + * before a reply arrives, then a null reply is returned from this method. This method allows the caller to specify + * the correlation id. + * + * @param message The message to send. If this is null, one is generated. + * @param numPings The number of ping messages to send. + * @param timeout The timeout in milliseconds. + * @param messageCorrelationId The message correlation id. If this is null, one is generated. + * + * @return The number of replies received. This may be less than the number sent if the timeout terminated the wait + * for all prematurely. + * + * @throws JMSException All underlying JMSExceptions are allowed to fall through. + * @throws InterruptedException When interrupted by a timeout + */ + public int pingAndWaitForReply(Message message, int numPings, long timeout, String messageCorrelationId) + throws JMSException, InterruptedException + { + /*log.debug("public int pingAndWaitForReply(Message message, int numPings = " + numPings + ", long timeout = " + + timeout + ", String messageCorrelationId = " + messageCorrelationId + "): called");*/ + + // Generate a unique correlation id to put on the messages before sending them, if one was not specified. + if (messageCorrelationId == null) + { + messageCorrelationId = Long.toString(_correlationIdGenerator.incrementAndGet()); + } + + try + { + // NDC.push("prod"); + + // Create a count down latch to count the number of replies with. This is created before the messages are + // sent so that the replies cannot be received before the count down is created. + // One is added to this, so that the last reply becomes a special case. The special case is that the + // chained message listener must be called before this sender can be unblocked, but that decrementing the + // countdown needs to be done before the chained listener can be called. + PerCorrelationId perCorrelationId = new PerCorrelationId(); + + perCorrelationId.trafficLight = new CountDownLatch(getExpectedNumPings(numPings) + 1); + perCorrelationIds.put(messageCorrelationId, perCorrelationId); + + // Set up the current time as the start time for pinging on the correlation id. This is used to determine + // timeouts. + perCorrelationId.timeOutStart = System.nanoTime(); + + // Send the specifed number of messages. + pingNoWaitForReply(message, numPings, messageCorrelationId); + + boolean timedOut; + boolean allMessagesReceived; + int numReplies; + + do + { + // Block the current thread until replies to all the messages are received, or it times out. + perCorrelationId.trafficLight.await(timeout, TimeUnit.MILLISECONDS); + + // Work out how many replies were receieved. + numReplies = getExpectedNumPings(numPings) - (int) perCorrelationId.trafficLight.getCount(); + + allMessagesReceived = numReplies == getExpectedNumPings(numPings); + + // log.debug("numReplies = " + numReplies); + // log.debug("allMessagesReceived = " + allMessagesReceived); + + // Recheck the timeout condition. + long now = System.nanoTime(); + long lastMessageReceievedAt = perCorrelationId.timeOutStart; + timedOut = (now - lastMessageReceievedAt) > (timeout * 1000000); + + // log.debug("now = " + now); + // log.debug("lastMessageReceievedAt = " + lastMessageReceievedAt); + } + while (!timedOut && !allMessagesReceived); + + if ((numReplies < getExpectedNumPings(numPings)) && _verbose) + { + log.info("Timed out (" + timeout + " ms) before all replies received on id, " + messageCorrelationId); + } + else if (_verbose) + { + log.info("Got all replies on id, " + messageCorrelationId); + } + + // commitTx(_consumerSession); + + // log.debug("public int pingAndWaitForReply(Message message, int numPings, long timeout): ending"); + + return numReplies; + } + // Ensure that the message countdown latch is always removed from the reply map. The reply map is long lived, + // so will be a memory leak if this is not done. + finally + { + // NDC.pop(); + perCorrelationIds.remove(messageCorrelationId); + } + } + + /** + * Sends the specified number of ping messages and does not wait for correlating replies. + * + * @param message The message to send. + * @param numPings The number of pings to send. + * @param messageCorrelationId A correlation id to place on all messages sent. + * + * @throws JMSException All underlying JMSExceptions are allowed to fall through. + */ + public void pingNoWaitForReply(Message message, int numPings, String messageCorrelationId) throws JMSException + { + /*log.debug("public void pingNoWaitForReply(Message message, int numPings = " + numPings + + ", String messageCorrelationId = " + messageCorrelationId + "): called");*/ + + if (message == null) + { + message = getTestMessage(getReplyDestinations().get(0), _messageSize, _persistent); + } + + message.setJMSCorrelationID(messageCorrelationId); + + // Set up a committed flag to detect uncommitted messages at the end of the send loop. This may occurr if the + // transaction batch size is not a factor of the number of pings. In which case an extra commit at the end is + // needed. + boolean committed = false; + + // Send all of the ping messages. + for (int i = 0; i < numPings; i++) + { + // Re-timestamp the message. + // message.setLongProperty(MESSAGE_TIMESTAMP_PROPNAME, System.nanoTime()); + + // Send the message, passing in the message count. + committed = sendMessage(i, message); + + // Spew out per message timings on every message sonly in verbose mode. + /*if (_verbose) + { + log.info(timestampFormatter.format(new Date()) + ": Pinged at with correlation id, " + messageCorrelationId); + }*/ + } + + // Call commit if the send loop finished before reaching a batch size boundary so there may still be uncommitted messages. + if (!committed) + { + commitTx(_producerSession); + } + } + + /** + * Sends the sepcified message, applies rate limiting and possibly commits the current transaction. The count of + * messages sent so far must be specified and is used to round robin the ping destinations (where there are more + * than one), and to determine if the transaction batch size has been reached and the sent messages should be + * committed. + * + * @param i The count of messages sent so far in a loop of multiple calls to this send method. + * @param message The message to send. + * + * @return <tt>true</tt> if the messages were committed, <tt>false</tt> otherwise. + * + * @throws JMSException All underlyiung JMSExceptions are allowed to fall through. + */ + protected boolean sendMessage(int i, Message message) throws JMSException + { + try + { + NDC.push("id" + instanceId + "/prod"); + + // log.debug("protected boolean sendMessage(int i = " + i + ", Message message): called"); + // log.debug("_txBatchSize = " + _txBatchSize); + + // Round robin the destinations as the messages are sent. + Destination destination = _pingDestinations.get(i % _pingDestinations.size()); + + // Prompt the user to kill the broker when doing failover testing. + _failBeforeSend = waitForUserToPromptOnFailure(_failBeforeSend); + + // Get the test setup for the correlation id. + String correlationID = message.getJMSCorrelationID(); + PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationID); + + // If necessary, wait until the max pending message size comes within its limit. + if (_maxPendingSize > 0) + { + synchronized (_sendPauseMonitor) + { + // Used to keep track of the number of times that send has to wait. + int numWaits = 0; + + // The maximum number of waits before the test gives up and fails. This has been chosen to correspond with + // the test timeout. + int waitLimit = (int) (TIMEOUT_DEFAULT / 10000); + + while (true) + { + // Get the size estimate of sent but not yet received messages. + int unreceived = _unreceived.get(); + int unreceivedSize = + (unreceived * ((_messageSize == 0) ? 1 : _messageSize)) + / (_isPubSub ? getConsumersPerDestination() : 1); + + // log.debug("unreceived = " + unreceived); + // log.debug("unreceivedSize = " + unreceivedSize); + // log.debug("_maxPendingSize = " + _maxPendingSize); + + if (unreceivedSize > _maxPendingSize) + { + // log.debug("unreceived size estimate over limit = " + unreceivedSize); + + // Fail the test if the send has had to wait more than the maximum allowed number of times. + if (numWaits > waitLimit) + { + String errorMessage = + "Send has had to wait for the unreceivedSize (" + unreceivedSize + + ") to come below the maxPendingSize (" + _maxPendingSize + ") more that " + waitLimit + + " times."; + log.warn(errorMessage); + throw new RuntimeException(errorMessage); + } + + // Wait on the send pause barrier for the limit to be re-established. + try + { + long start = System.nanoTime(); + // _sendPauseMonitor.wait(10000); + _sendPauseMonitor.offer(new Object(), 10000, TimeUnit.MILLISECONDS); + long end = System.nanoTime(); + + // Count the wait only if it was for > 99% of the requested wait time. + if (((float) (end - start) / (float) (10000 * 1000000L)) > 0.99) + { + numWaits++; + } + } + catch (InterruptedException e) + { + // Restore the interrupted status + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + } + else + { + break; + } + } + } + } + + // Send the message either to its round robin destination, or its default destination. + // int num = numSent.incrementAndGet(); + // message.setIntProperty("MSG_NUM", num); + setTimestamp(message); + + if (destination == null) + { + _producer.send(message); + } + else + { + _producer.send(destination, message); + } + + // Increase the unreceived size, this may actually happen after the message is received. + // The unreceived size is incremented by the number of consumers that will get a copy of the message, + // in pub/sub mode. + if (_maxPendingSize > 0) + { + int newUnreceivedCount = _unreceived.addAndGet(_isPubSub ? getConsumersPerDestination() : 1); + // log.debug("newUnreceivedCount = " + newUnreceivedCount); + } + + // Apply message rate throttling if a rate limit has been set up. + if (_rateLimiter != null) + { + _rateLimiter.throttle(); + } + + // Call commit every time the commit batch size is reached. + boolean committed = false; + + // Commit on every transaction batch size boundary. Here i + 1 is the count of actual messages sent. + if (((i + 1) % _txBatchSize) == 0) + { + // log.debug("Trying commit on producer session."); + committed = commitTx(_producerSession); + } + + return committed; + } + finally + { + NDC.clear(); + } + } + + /** + * If the specified fail flag is set, this method waits for the user to cause a failure and then indicate to the + * test that the failure has occurred, before the method returns. + * + * @param failFlag The fail flag to test. + * + * @return The new value for the fail flag. If the {@link #_failOnce} flag is set, then each fail flag is only + * used once, then reset. + */ + private boolean waitForUserToPromptOnFailure(boolean failFlag) + { + if (failFlag) + { + if (_failOnce) + { + failFlag = false; + } + + // log.debug("Failing Before Send"); + waitForUser(KILL_BROKER_PROMPT); + } + + return failFlag; + } + + /** + * Implements a single iteration of the ping loop. This sends the number of pings specified by the transaction batch + * size property, and waits for replies to all of them. Any errors cause the publish flag to be cleared, which will + * terminate the pinger. + */ + public void pingLoop() + { + try + { + // Generate a sample message and time stamp it. + Message msg = getTestMessage(_replyDestination, _messageSize, _persistent); + // setTimestamp(msg); + + // Send the message and wait for a reply. + pingAndWaitForReply(msg, TX_BATCH_SIZE_DEFAULT, TIMEOUT_DEFAULT, null); + } + catch (JMSException e) + { + _publish = false; + // log.debug("There was a JMSException: " + e.getMessage(), e); + } + catch (InterruptedException e) + { + _publish = false; + // log.debug("There was an interruption: " + e.getMessage(), e); + } + } + + /** + * Sets a chained message listener. The message listener on this pinger, chains all its messages to the one set + * here. + * + * @param messageListener The chained message listener. + */ + public void setChainedMessageListener(ChainedMessageListener messageListener) + { + _chainedMessageListener = messageListener; + } + + /** Removes any chained message listeners from this pinger. */ + public void removeChainedMessageListener() + { + _chainedMessageListener = null; + } + + /** + * Generates a test message of the specified size, with the specified reply-to destination and persistence flag. + * + * @param replyQueue The reply-to destination for the message. + * @param messageSize The desired size of the message in bytes. + * @param persistent <tt>true</tt> if the message should use persistent delivery, <tt>false</tt> otherwise. + * + * @return A freshly generated test message. + * + * @throws javax.jms.JMSException All underlying JMSException are allowed to fall through. + */ + public Message getTestMessage(Destination replyQueue, int messageSize, boolean persistent) throws JMSException + { + // return TestMessageFactory.newObjectMessage(_producerSession, replyQueue, messageSize, persistent); + return TestUtils.createTestMessageOfSize(_producerSession, messageSize); + } + + /** + * Sets the current time in nanoseconds as the timestamp on the message. + * + * @param msg The message to timestamp. + * + * @throws JMSException Any JMSExceptions are allowed to fall through. + */ + protected void setTimestamp(Message msg) throws JMSException + { + /*if (((AMQSession)_producerSession).isStrictAMQP()) + { + ((AMQMessage)msg).setTimestampProperty(new AMQShortString(MESSAGE_TIMESTAMP_PROPNAME), System.nanoTime()); + } + else + {*/ + msg.setLongProperty(MESSAGE_TIMESTAMP_PROPNAME, System.nanoTime()); + // } + } + + /** + * Extracts the nanosecond timestamp from a message. + * + * @param msg The message to extract the time stamp from. + * + * @return The timestamp in nanos. + * + * @throws JMSException Any JMSExceptions are allowed to fall through. + */ + protected long getTimestamp(Message msg) throws JMSException + { + /*if (((AMQSession)_producerSession).isStrictAMQP()) + { + Long value = ((AMQMessage)msg).getTimestampProperty(new AMQShortString(MESSAGE_TIMESTAMP_PROPNAME)); + + return (value == null) ? 0L : value; + } + else + {*/ + return msg.getLongProperty(PingPongProducer.MESSAGE_TIMESTAMP_PROPNAME); + // } + } + + /** + * Stops the ping loop by clearing the publish flag. The current loop will complete when it notices that this flag + * has been cleared. + */ + public void stop() + { + _publish = false; + } + + /** + * Starts the producer and consumer connections. + * + * @throws JMSException Any JMSExceptions are allowed to fall through. + */ + public void start() throws JMSException + { + // log.debug("public void start(): called"); + + _connection.start(); + // log.debug("Producer started."); + + for (int i = 0; i < _noOfConsumers; i++) + { + _consumerConnection[i].start(); + // log.debug("Consumer " + i + " started."); + } + } + + /** Implements a ping loop that repeatedly pings until the publish flag becomes false. */ + public void run() + { + // Keep running until the publish flag is cleared. + while (_publish) + { + pingLoop(); + } + } + + /** + * Callback method, implementing ExceptionListener. This should be registered to listen for exceptions on the + * connection, this clears the publish flag which in turn will halt the ping loop. + * + * @param e The exception that triggered this callback method. + */ + public void onException(JMSException e) + { + // log.debug("public void onException(JMSException e = " + e + "): called", e); + _publish = false; + } + + /** + * Gets a shutdown hook that will cleanly shut this down when it is running the ping loop. This can be registered + * with the runtime system as a shutdown hook. + * + * @return A shutdown hook for the ping loop. + */ + public Thread getShutdownHook() + { + return new Thread(new Runnable() + { + public void run() + { + stop(); + } + }); + } + + /** + * Closes all of the producer and consumer connections. + * + * @throws JMSException All JMSException are allowed to fall through. + */ + public void close() throws JMSException + { + // log.debug("public void close(): called"); + + try + { + if (_connection != null) + { + // log.debug("Before close producer connection."); + _connection.close(); + // log.debug("Closed producer connection."); + } + + for (int i = 0; i < _noOfConsumers; i++) + { + if (_consumerConnection[i] != null) + { + // log.debug("Before close consumer connection " + i + "."); + _consumerConnection[i].close(); + // log.debug("Closed consumer connection " + i + "."); + } + } + } + finally + { + _connection = null; + _producerSession = null; + _consumerSession = null; + _consumerConnection = null; + _producer = null; + _consumer = null; + _pingDestinations = null; + _replyDestination = null; + } + } + + /** + * Convenience method to commit the transaction on the specified controlSession. If the controlSession to commit on is not a + * transactional controlSession, this method does nothing (unless the failover after send flag is set). + * + * <p/>If the {@link #_failAfterSend} flag is set, this will prompt the user to kill the broker before the commit is + * applied. This flag applies whether the pinger is transactional or not. + * + * <p/>If the {@link #_failBeforeCommit} flag is set, this will prompt the user to kill the broker before the commit + * is applied. If the {@link #_failAfterCommit} flag is set, this will prompt the user to kill the broker after the + * commit is applied. These flags will only apply if using a transactional pinger. + * + * @param session The controlSession to commit + * + * @return <tt>true</tt> if the controlSession was committed, <tt>false</tt> if it was not. + * + * @throws javax.jms.JMSException If the commit fails and then the rollback fails. + * + * @todo Consider moving the fail after send logic into the send method. It is confusing to have it in this commit + * method, because commits only apply to transactional pingers, but fail after send applied to transactional and + * non-transactional alike. + */ + protected boolean commitTx(Session session) throws JMSException + { + // log.debug("protected void commitTx(Session session): called"); + + boolean committed = false; + + _failAfterSend = waitForUserToPromptOnFailure(_failAfterSend); + + if (session.getTransacted()) + { + // log.debug("Session is transacted."); + + try + { + _failBeforeCommit = waitForUserToPromptOnFailure(_failBeforeCommit); + + long start = System.nanoTime(); + session.commit(); + committed = true; + // log.debug("Time taken to commit :" + ((System.nanoTime() - start) / 1000000f) + " ms"); + + _failAfterCommit = waitForUserToPromptOnFailure(_failAfterCommit); + + // log.debug("Session Commited."); + } + catch (JMSException e) + { + // log.debug("JMSException on commit:" + e.getMessage(), e); + + try + { + session.rollback(); + // log.debug("Message rolled back."); + } + catch (JMSException jmse) + { + // log.debug("JMSE on rollback:" + jmse.getMessage(), jmse); + + // Both commit and rollback failed. Throw the rollback exception. + throw jmse; + } + } + } + + return committed; + } + + /** + * Outputs a prompt to the console and waits for the user to press return. + * + * @param prompt The prompt to display on the console. + */ + public void waitForUser(String prompt) + { + System.out.println(prompt); + + try + { + System.in.read(); + } + catch (IOException e) + { + // Ignored. + } + + System.out.println("Continuing."); + } + + /** + * Gets the number of consumers that are listening to each destination in the test. + * + * @return int The number of consumers subscribing to each topic. + */ + public int getConsumersPerDestination() + { + return _noOfConsumers; + } + + /** + * Calculates how many pings are expected to be received for the given number sent. + * + * @param numpings The number of pings that will be sent. + * + * @return The number that should be received, for the test to pass. + */ + public int getExpectedNumPings(int numpings) + { + // log.debug("public int getExpectedNumPings(int numpings = " + numpings + "): called"); + + // log.debug("Each ping will be received by " + (_isPubSub ? getConsumersPerDestination() : 1) + " consumers."); + + return numpings * (_isPubSub ? getConsumersPerDestination() : 1); + } + + /** + * Defines a chained message listener interface that can be attached to this pinger. Whenever this pinger's {@link + * PingPongProducer#onMessageWithConsumerNo} method is called, the chained listener set through the {@link + * PingPongProducer#setChainedMessageListener} method is passed the message, and the remaining expected count of + * messages with that correlation id. + * + * <p/>Provided only one pinger is producing messages with that correlation id, the chained listener will always be + * given unique message counts. It will always be called while the producer waiting for all messages to arrive is + * still blocked. + */ + public static interface ChainedMessageListener + { + /** + * Notifies interested listeners about message arrival and important test stats, the number of messages + * remaining in the test, and the messages send timestamp. + * + * @param message The newly arrived message. + * @param remainingCount The number of messages left to complete the test. + * @param latency The nanosecond latency of the message. + * + * @throws JMSException Any JMS exceptions is allowed to fall through. + */ + public void onMessage(Message message, int remainingCount, long latency) throws JMSException; + } + + /** + * Holds information on each correlation id. The countdown latch, the current timeout timer... More stuff to be + * added to this: read/write lock to make onMessage more concurrent as described in class header comment. + */ + protected static class PerCorrelationId + { + /** Holds a countdown on number of expected messages. */ + CountDownLatch trafficLight; + + /** Holds the last timestamp that the timeout was reset to. */ + Long timeOutStart; + } +} diff --git a/qpid/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java b/qpid/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java index 2610b32220..009254c612 100644 --- a/qpid/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java +++ b/qpid/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java @@ -1,251 +1,251 @@ -/*
- *
- * 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.requestreply;
-
-import junit.framework.Assert;
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.junit.extensions.AsymptoticTestCase;
-import org.apache.qpid.junit.extensions.util.ParsedProperties;
-import org.apache.qpid.junit.extensions.util.TestContextProperties;
-
-import javax.jms.*;
-
-/**
- * PingPongTestPerf is a full round trip ping test, that has been written with the intention of being scaled up to run
- * many times simultaneously to simluate many clients/producer/connections. A full round trip ping sends a message from
- * a producer to a conumer, then the consumer replies to the message on a temporary queue.
- *
- * <p/>A single run of the test using the default JUnit test runner will result in the sending and timing of the number
- * of pings specified by the test size and time how long it takes for all of these to complete. This test may be scaled
- * up using a suitable JUnit test runner. See {@link org.apache.qpid.junit.extensions.TKTestRunner} for more
- * information on how to do this.
- *
- * <p/>The setup/teardown cycle establishes a connection to a broker and sets up a queue to send ping messages to and a
- * temporary queue for replies. This setup is only established once for all the test repeats, but each test threads
- * gets its own connection/producer/consumer, this is only re-established if the connection is lost.
- *
- * <p/>The test cycle is: Connects to a queue, creates a temporary queue, creates messages containing a property that
- * is the name of the temporary queue, fires off many messages on the original queue and waits for them all to come
- * back on the temporary queue.
- *
- * <p/>Configurable test properties: message size, transacted or not, persistent or not. Broker connection details.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * </table>
- */
-public class PingPongTestPerf extends AsymptoticTestCase
-{
- private static Logger _logger = Logger.getLogger(PingPongTestPerf.class);
-
- /** Thread local to hold the per-thread test setup fields. */
- ThreadLocal<PerThreadSetup> threadSetup = new ThreadLocal<PerThreadSetup>();
-
- // Set up a property reader to extract the test parameters from. Once ContextualProperties is available in
- // the project dependencies, use it to get property overrides for configurable tests and to notify the test runner
- // of the test parameters to log with the results. It also providers some basic type parsing convenience methods.
- // private Properties testParameters = System.getProperties();
- private ParsedProperties testParameters =
- TestContextProperties.getInstance(PingPongProducer.defaults /*System.getProperties()*/);
-
- public PingPongTestPerf(String name)
- {
- super(name);
-
- _logger.debug(testParameters);
-
- // Sets up the test parameters with defaults.
- /*testParameters.setPropertyIfNull(PingPongProducer.TX_BATCH_SIZE_PROPNAME,
- Integer.toString(PingPongProducer.TX_BATCH_SIZE_DEFAULT));
- testParameters.setPropertyIfNull(PingPongProducer.MESSAGE_SIZE_PROPNAME,
- Integer.toString(PingPongProducer.MESSAGE_SIZE_DEAFULT));
- testParameters.setPropertyIfNull(PingPongProducer.PING_QUEUE_NAME_PROPNAME,
- PingPongProducer.PING_QUEUE_NAME_DEFAULT);
- testParameters.setPropertyIfNull(PingPongProducer.PERSISTENT_MODE_PROPNAME,
- Boolean.toString(PingPongProducer.PERSISTENT_MODE_DEFAULT));
- testParameters.setPropertyIfNull(PingPongProducer.TRANSACTED_PROPNAME,
- Boolean.toString(PingPongProducer.TRANSACTED_DEFAULT));
- testParameters.setPropertyIfNull(PingPongProducer.BROKER_PROPNAME, PingPongProducer.BROKER_DEFAULT);
- testParameters.setPropertyIfNull(PingPongProducer.USERNAME_PROPNAME, PingPongProducer.USERNAME_DEFAULT);
- testParameters.setPropertyIfNull(PingPongProducer.PASSWORD_PROPNAME, PingPongProducer.PASSWORD_DEFAULT);
- testParameters.setPropertyIfNull(PingPongProducer.VIRTUAL_HOST_PROPNAME, PingPongProducer.VIRTUAL_HOST_DEFAULT);
- testParameters.setPropertyIfNull(PingPongProducer.VERBOSE_PROPNAME,
- Boolean.toString(PingPongProducer.VERBOSE_DEFAULT));
- testParameters.setPropertyIfNull(PingPongProducer.RATE_PROPNAME, Integer.toString(PingPongProducer.RATE_DEFAULT));
- testParameters.setPropertyIfNull(PingPongProducer.PUBSUB_PROPNAME,
- Boolean.toString(PingPongProducer.PUBSUB_DEFAULT));
- testParameters.setPropertyIfNull(PingPongProducer.TX_BATCH_SIZE_PROPNAME,
- Integer.toString(PingPongProducer.TX_BATCH_SIZE_DEFAULT));
- testParameters.setPropertyIfNull(PingPongProducer.TIMEOUT_PROPNAME, Long.toString(PingPongProducer.TIMEOUT_DEFAULT));
- testParameters.setPropertyIfNull(PingPongProducer.DESTINATION_COUNT_PROPNAME,
- Integer.toString(PingPongProducer.DESTINATION_COUNT_DEFAULT));
- testParameters.setPropertyIfNull(PingPongProducer.FAIL_AFTER_COMMIT_PROPNAME,
- PingPongProducer.FAIL_AFTER_COMMIT_DEFAULT);
- testParameters.setPropertyIfNull(PingPongProducer.FAIL_BEFORE_COMMIT_PROPNAME,
- PingPongProducer.FAIL_BEFORE_COMMIT_DEFAULT);
- testParameters.setPropertyIfNull(PingPongProducer.FAIL_AFTER_SEND_PROPNAME,
- PingPongProducer.FAIL_AFTER_SEND_DEFAULT);
- testParameters.setPropertyIfNull(PingPongProducer.FAIL_BEFORE_SEND_PROPNAME,
- PingPongProducer.FAIL_BEFORE_SEND_DEFAULT);
- testParameters.setPropertyIfNull(PingPongProducer.FAIL_ONCE_PROPNAME, PingPongProducer.FAIL_ONCE_DEFAULT);
- testParameters.setPropertyIfNull(PingPongProducer.UNIQUE_DESTS_PROPNAME,
- Boolean.toString(PingPongProducer.UNIQUE_DESTS_DEFAULT));
- testParameters.setPropertyIfNull(PingPongProducer.ACK_MODE_PROPNAME,
- Integer.toString(PingPongProducer.ACK_MODE_DEFAULT));
- testParameters.setPropertyIfNull(PingPongProducer.PAUSE_AFTER_BATCH_PROPNAME,
- PingPongProducer.PAUSE_AFTER_BATCH_DEFAULT);*/
- }
-
- /**
- * Compile all the tests into a test suite.
- */
- public static Test suite()
- {
- // Build a new test suite
- TestSuite suite = new TestSuite("Ping-Pong Performance Tests");
-
- // Run performance tests in read committed mode.
- suite.addTest(new PingPongTestPerf("testPingPongOk"));
-
- return suite;
- }
-
- private static void setSystemPropertyIfNull(String propName, String propValue)
- {
- if (System.getProperty(propName) == null)
- {
- System.setProperty(propName, propValue);
- }
- }
-
- public void testPingPongOk(int numPings) throws Exception
- {
- // Get the per thread test setup to run the test through.
- PerThreadSetup perThreadSetup = threadSetup.get();
-
- // Generate a sample message. This message is already time stamped and has its reply-to destination set.
- Message msg =
- perThreadSetup._testPingProducer.getTestMessage(perThreadSetup._testPingProducer.getReplyDestinations().get(0),
- testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME),
- testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME));
-
- // Send the message and wait for a reply.
- int numReplies =
- perThreadSetup._testPingProducer.pingAndWaitForReply(msg, numPings, PingPongProducer.TIMEOUT_DEFAULT, null);
-
- // Fail the test if the timeout was exceeded.
- if (numReplies != numPings)
- {
- Assert.fail("The ping timed out, got " + numReplies + " out of " + numPings);
- }
- }
-
- /**
- * Performs test fixture creation on a per thread basis. This will only be called once for each test thread.
- */
- public void threadSetUp()
- {
- try
- {
- PerThreadSetup perThreadSetup = new PerThreadSetup();
-
- // Extract the test set up paramaeters.
- String brokerDetails = testParameters.getProperty(PingPongProducer.BROKER_PROPNAME);
- String username = testParameters.getProperty(PingPongProducer.USERNAME_PROPNAME);
- String password = testParameters.getProperty(PingPongProducer.PASSWORD_PROPNAME);
- String virtualPath = testParameters.getProperty(PingPongProducer.VIRTUAL_HOST_PROPNAME);
- String destinationName = testParameters.getProperty(PingPongProducer.PING_QUEUE_NAME_PROPNAME);
- boolean persistent = testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME);
- boolean transacted = testParameters.getPropertyAsBoolean(PingPongProducer.TRANSACTED_PROPNAME);
- String selector = testParameters.getProperty(PingPongProducer.SELECTOR_PROPNAME);
- boolean verbose = testParameters.getPropertyAsBoolean(PingPongProducer.VERBOSE_PROPNAME);
- boolean pubsub = testParameters.getPropertyAsBoolean(PingPongProducer.PUBSUB_PROPNAME);
-
- synchronized (this)
- {
- // Establish a bounce back client on the ping queue to bounce back the pings.
- perThreadSetup._testPingBouncer =
- new PingPongBouncer(brokerDetails, username, password, virtualPath, destinationName, persistent,
- transacted, selector, verbose, pubsub);
-
- // Start the connections for client and producer running.
- perThreadSetup._testPingBouncer.getConnection().start();
-
- // Establish a ping-pong client on the ping queue to send the pings and receive replies with.
- perThreadSetup._testPingProducer = new PingPongProducer(testParameters);
- perThreadSetup._testPingProducer.establishConnection(true, true);
- perThreadSetup._testPingProducer.start();
- }
-
- // Attach the per-thread set to the thread.
- threadSetup.set(perThreadSetup);
- }
- catch (Exception e)
- {
- _logger.warn("There was an exception during per thread setup.", e);
- }
- }
-
- /**
- * Performs test fixture clean
- */
- public void threadTearDown()
- {
- _logger.debug("public void threadTearDown(): called");
-
- try
- {
- // Get the per thread test fixture.
- PerThreadSetup perThreadSetup = threadSetup.get();
-
- // Close the pingers so that it cleans up its connection cleanly.
- synchronized (this)
- {
- perThreadSetup._testPingProducer.close();
- // perThreadSetup._testPingBouncer.close();
- }
-
- // Ensure the per thread fixture is reclaimed.
- threadSetup.remove();
- }
- catch (JMSException e)
- {
- _logger.warn("There was an exception during per thread tear down.");
- }
- }
-
- protected static class PerThreadSetup
- {
- /**
- * Holds the test ping-pong producer.
- */
- private PingPongProducer _testPingProducer;
-
- /**
- * Holds the test ping client.
- */
- private PingPongBouncer _testPingBouncer;
- }
-}
+/* + * + * 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.requestreply; + +import junit.framework.Assert; +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.apache.log4j.Logger; + +import org.apache.qpid.junit.extensions.AsymptoticTestCase; +import org.apache.qpid.junit.extensions.util.ParsedProperties; +import org.apache.qpid.junit.extensions.util.TestContextProperties; + +import javax.jms.*; + +/** + * PingPongTestPerf is a full round trip ping test, that has been written with the intention of being scaled up to run + * many times simultaneously to simluate many clients/producer/connections. A full round trip ping sends a message from + * a producer to a conumer, then the consumer replies to the message on a temporary queue. + * + * <p/>A single run of the test using the default JUnit test runner will result in the sending and timing of the number + * of pings specified by the test size and time how long it takes for all of these to complete. This test may be scaled + * up using a suitable JUnit test runner. See {@link org.apache.qpid.junit.extensions.TKTestRunner} for more + * information on how to do this. + * + * <p/>The setup/teardown cycle establishes a connection to a broker and sets up a queue to send ping messages to and a + * temporary queue for replies. This setup is only established once for all the test repeats, but each test threads + * gets its own connection/producer/consumer, this is only re-established if the connection is lost. + * + * <p/>The test cycle is: Connects to a queue, creates a temporary queue, creates messages containing a property that + * is the name of the temporary queue, fires off many messages on the original queue and waits for them all to come + * back on the temporary queue. + * + * <p/>Configurable test properties: message size, transacted or not, persistent or not. Broker connection details. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * </table> + */ +public class PingPongTestPerf extends AsymptoticTestCase +{ + private static Logger _logger = Logger.getLogger(PingPongTestPerf.class); + + /** Thread local to hold the per-thread test setup fields. */ + ThreadLocal<PerThreadSetup> threadSetup = new ThreadLocal<PerThreadSetup>(); + + // Set up a property reader to extract the test parameters from. Once ContextualProperties is available in + // the project dependencies, use it to get property overrides for configurable tests and to notify the test runner + // of the test parameters to log with the results. It also providers some basic type parsing convenience methods. + // private Properties testParameters = System.getProperties(); + private ParsedProperties testParameters = + TestContextProperties.getInstance(PingPongProducer.defaults /*System.getProperties()*/); + + public PingPongTestPerf(String name) + { + super(name); + + _logger.debug(testParameters); + + // Sets up the test parameters with defaults. + /*testParameters.setPropertyIfNull(PingPongProducer.TX_BATCH_SIZE_PROPNAME, + Integer.toString(PingPongProducer.TX_BATCH_SIZE_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.MESSAGE_SIZE_PROPNAME, + Integer.toString(PingPongProducer.MESSAGE_SIZE_DEAFULT)); + testParameters.setPropertyIfNull(PingPongProducer.PING_QUEUE_NAME_PROPNAME, + PingPongProducer.PING_QUEUE_NAME_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.PERSISTENT_MODE_PROPNAME, + Boolean.toString(PingPongProducer.PERSISTENT_MODE_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.TRANSACTED_PROPNAME, + Boolean.toString(PingPongProducer.TRANSACTED_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.BROKER_PROPNAME, PingPongProducer.BROKER_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.USERNAME_PROPNAME, PingPongProducer.USERNAME_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.PASSWORD_PROPNAME, PingPongProducer.PASSWORD_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.VIRTUAL_HOST_PROPNAME, PingPongProducer.VIRTUAL_HOST_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.VERBOSE_PROPNAME, + Boolean.toString(PingPongProducer.VERBOSE_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.RATE_PROPNAME, Integer.toString(PingPongProducer.RATE_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.PUBSUB_PROPNAME, + Boolean.toString(PingPongProducer.PUBSUB_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.TX_BATCH_SIZE_PROPNAME, + Integer.toString(PingPongProducer.TX_BATCH_SIZE_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.TIMEOUT_PROPNAME, Long.toString(PingPongProducer.TIMEOUT_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.DESTINATION_COUNT_PROPNAME, + Integer.toString(PingPongProducer.DESTINATION_COUNT_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.FAIL_AFTER_COMMIT_PROPNAME, + PingPongProducer.FAIL_AFTER_COMMIT_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.FAIL_BEFORE_COMMIT_PROPNAME, + PingPongProducer.FAIL_BEFORE_COMMIT_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.FAIL_AFTER_SEND_PROPNAME, + PingPongProducer.FAIL_AFTER_SEND_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.FAIL_BEFORE_SEND_PROPNAME, + PingPongProducer.FAIL_BEFORE_SEND_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.FAIL_ONCE_PROPNAME, PingPongProducer.FAIL_ONCE_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.UNIQUE_DESTS_PROPNAME, + Boolean.toString(PingPongProducer.UNIQUE_DESTS_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.ACK_MODE_PROPNAME, + Integer.toString(PingPongProducer.ACK_MODE_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.PAUSE_AFTER_BATCH_PROPNAME, + PingPongProducer.PAUSE_AFTER_BATCH_DEFAULT);*/ + } + + /** + * Compile all the tests into a test suite. + */ + public static Test suite() + { + // Build a new test suite + TestSuite suite = new TestSuite("Ping-Pong Performance Tests"); + + // Run performance tests in read committed mode. + suite.addTest(new PingPongTestPerf("testPingPongOk")); + + return suite; + } + + private static void setSystemPropertyIfNull(String propName, String propValue) + { + if (System.getProperty(propName) == null) + { + System.setProperty(propName, propValue); + } + } + + public void testPingPongOk(int numPings) throws Exception + { + // Get the per thread test setup to run the test through. + PerThreadSetup perThreadSetup = threadSetup.get(); + + // Generate a sample message. This message is already time stamped and has its reply-to destination set. + Message msg = + perThreadSetup._testPingProducer.getTestMessage(perThreadSetup._testPingProducer.getReplyDestinations().get(0), + testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME), + testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME)); + + // Send the message and wait for a reply. + int numReplies = + perThreadSetup._testPingProducer.pingAndWaitForReply(msg, numPings, PingPongProducer.TIMEOUT_DEFAULT, null); + + // Fail the test if the timeout was exceeded. + if (numReplies != numPings) + { + Assert.fail("The ping timed out, got " + numReplies + " out of " + numPings); + } + } + + /** + * Performs test fixture creation on a per thread basis. This will only be called once for each test thread. + */ + public void threadSetUp() + { + try + { + PerThreadSetup perThreadSetup = new PerThreadSetup(); + + // Extract the test set up paramaeters. + String brokerDetails = testParameters.getProperty(PingPongProducer.BROKER_PROPNAME); + String username = testParameters.getProperty(PingPongProducer.USERNAME_PROPNAME); + String password = testParameters.getProperty(PingPongProducer.PASSWORD_PROPNAME); + String virtualPath = testParameters.getProperty(PingPongProducer.VIRTUAL_HOST_PROPNAME); + String destinationName = testParameters.getProperty(PingPongProducer.PING_QUEUE_NAME_PROPNAME); + boolean persistent = testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME); + boolean transacted = testParameters.getPropertyAsBoolean(PingPongProducer.TRANSACTED_PROPNAME); + String selector = testParameters.getProperty(PingPongProducer.SELECTOR_PROPNAME); + boolean verbose = testParameters.getPropertyAsBoolean(PingPongProducer.VERBOSE_PROPNAME); + boolean pubsub = testParameters.getPropertyAsBoolean(PingPongProducer.PUBSUB_PROPNAME); + + synchronized (this) + { + // Establish a bounce back client on the ping queue to bounce back the pings. + perThreadSetup._testPingBouncer = + new PingPongBouncer(brokerDetails, username, password, virtualPath, destinationName, persistent, + transacted, selector, verbose, pubsub); + + // Start the connections for client and producer running. + perThreadSetup._testPingBouncer.getConnection().start(); + + // Establish a ping-pong client on the ping queue to send the pings and receive replies with. + perThreadSetup._testPingProducer = new PingPongProducer(testParameters); + perThreadSetup._testPingProducer.establishConnection(true, true); + perThreadSetup._testPingProducer.start(); + } + + // Attach the per-thread set to the thread. + threadSetup.set(perThreadSetup); + } + catch (Exception e) + { + _logger.warn("There was an exception during per thread setup.", e); + } + } + + /** + * Performs test fixture clean + */ + public void threadTearDown() + { + _logger.debug("public void threadTearDown(): called"); + + try + { + // Get the per thread test fixture. + PerThreadSetup perThreadSetup = threadSetup.get(); + + // Close the pingers so that it cleans up its connection cleanly. + synchronized (this) + { + perThreadSetup._testPingProducer.close(); + // perThreadSetup._testPingBouncer.close(); + } + + // Ensure the per thread fixture is reclaimed. + threadSetup.remove(); + } + catch (JMSException e) + { + _logger.warn("There was an exception during per thread tear down."); + } + } + + protected static class PerThreadSetup + { + /** + * Holds the test ping-pong producer. + */ + private PingPongProducer _testPingProducer; + + /** + * Holds the test ping client. + */ + private PingPongBouncer _testPingBouncer; + } +} diff --git a/qpid/java/perftests/src/main/java/org/apache/qpid/test/testcases/MessageThroughputPerf.java b/qpid/java/perftests/src/main/java/org/apache/qpid/test/testcases/MessageThroughputPerf.java index f699295b06..0fcb0a8538 100644 --- a/qpid/java/perftests/src/main/java/org/apache/qpid/test/testcases/MessageThroughputPerf.java +++ b/qpid/java/perftests/src/main/java/org/apache/qpid/test/testcases/MessageThroughputPerf.java @@ -1,199 +1,199 @@ -/*
- *
- * 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.testcases;
-
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
-import org.apache.log4j.Logger;
-import org.apache.log4j.NDC;
-
-import org.apache.qpid.test.framework.Assertion;
-import org.apache.qpid.test.framework.Circuit;
-import org.apache.qpid.test.framework.FrameworkBaseCase;
-import org.apache.qpid.test.framework.MessagingTestConfigProperties;
-import org.apache.qpid.test.framework.sequencers.CircuitFactory;
-
-import org.apache.qpid.junit.extensions.TestThreadAware;
-import org.apache.qpid.junit.extensions.TimingController;
-import org.apache.qpid.junit.extensions.TimingControllerAware;
-import org.apache.qpid.junit.extensions.util.ParsedProperties;
-import org.apache.qpid.junit.extensions.util.TestContextProperties;
-
-import java.util.LinkedList;
-
-/**
- * MessageThroughputPerf runs a test over a {@link Circuit} controlled by the test parameters. It logs timings of
- * the time required to receive samples consisting of batches of messages.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Measure message throughput accross a test circuit. <td> {@link Circuit}
- * </table>
- *
- * @todo Check that all of the messages were sent. Check that the receiving end got the same number of messages as
- * the publishing end.
- *
- * @todo Set this up to run with zero sized tests. Size zero means send forever. Continuous sending to be interrupted
- * by completion of the test duration, or shutdown hook when the user presses Ctrl-C.
- */
-public class MessageThroughputPerf extends FrameworkBaseCase implements TimingControllerAware, TestThreadAware
-{
- /** Used for debugging. */
- private static final Logger log = Logger.getLogger(MessageThroughputPerf.class);
-
- /** Holds the timing controller, used to log test timings from self-timed tests. */
- private TimingController timingController;
-
- /** Thread local to hold the per-thread test setup fields. */
- ThreadLocal<PerThreadSetup> threadSetup = new ThreadLocal<PerThreadSetup>();
-
- /**
- * Creates a new test case with the specified name.
- *
- * @param name The test case name.
- */
- public MessageThroughputPerf(String name)
- {
- super(name);
- }
-
- /**
- * Performs the a basic P2P test case.
- *
- * @param numMessages The number of messages to send in the test.
- */
- public void testThroughput(int numMessages)
- {
- log.debug("public void testThroughput(): called");
-
- PerThreadSetup setup = threadSetup.get();
- assertNoFailures(setup.testCircuit.test(numMessages, new LinkedList<Assertion>()));
- }
-
- /**
- * Should provide a translation from the junit method name of a test to its test case name as known to the test
- * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test
- * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case
- * name "TC2_BasicP2P".
- *
- * @param methodName The name of the JUnit test method.
- *
- * @return The name of the corresponding interop test case.
- */
- public String getTestCaseNameForTestMethod(String methodName)
- {
- log.debug("public String getTestCaseNameForTestMethod(String methodName = " + methodName + "): called");
-
- return "DEFAULT_CIRCUIT_TEST";
- }
-
- /**
- * Used by test runners that can supply a {@link org.apache.qpid.junit.extensions.TimingController} to set the
- * controller on an aware test.
- *
- * @param controller The timing controller.
- */
- public void setTimingController(TimingController controller)
- {
- timingController = controller;
- }
-
- /**
- * Overrides the parent setUp method so that the in-vm broker creation is not done on a per test basis.
- *
- * @throws Exception Any exceptions allowed to fall through and fail the test.
- */
- protected void setUp() throws Exception
- {
- NDC.push(getName());
-
- testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults);
- }
-
- /**
- * Overrides the parent setUp method so that the in-vm broker clean-up is not done on a per test basis.
- */
- protected void tearDown()
- {
- NDC.pop();
- }
-
- /**
- * Performs test fixture creation on a per thread basis. This will only be called once for each test thread.
- */
- public void threadSetUp()
- {
- // Run the test setup tasks. This may create an in-vm broker, if a decorator has injected a task for this.
- taskHandler.runSetupTasks();
-
- // Get the test parameters, any overrides on the command line will have been applied.
- ParsedProperties testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults);
-
- // Customize the test parameters.
- testProps.setProperty("TEST_NAME", "DEFAULT_CIRCUIT_TEST");
- testProps.setProperty(MessagingTestConfigProperties.SEND_DESTINATION_NAME_ROOT_PROPNAME, "testqueue");
-
- // Get the test circuit factory to create test circuits and run the standard test procedure through.
- CircuitFactory circuitFactory = getCircuitFactory();
-
- // Create the test circuit. This projects the circuit onto the available test nodes and connects it up.
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- // Store the test configuration for the thread.
- PerThreadSetup setup = new PerThreadSetup();
- setup.testCircuit = testCircuit;
- threadSetup.set(setup);
- }
-
- /**
- * Called when a test thread is destroyed.
- */
- public void threadTearDown()
- {
- // Run the test teardown tasks. This may destroy the in-vm broker, if a decorator has injected a task for this.
- taskHandler.runSetupTasks();
- }
-
- /**
- * Holds the per-thread test configurations.
- */
- protected static class PerThreadSetup
- {
- /** Holds the test circuit to run tests on. */
- Circuit testCircuit;
- }
-
- /**
- * Compiles all the tests in this class into a suite.
- *
- * @return The test suite.
- */
- public static Test suite()
- {
- // Build a new test suite
- TestSuite suite = new TestSuite("Qpid Throughput Performance Tests");
-
- suite.addTest(new MessageThroughputPerf("testThroughput"));
-
- return suite;
- }
-}
+/* + * + * 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.testcases; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.apache.log4j.Logger; +import org.apache.log4j.NDC; + +import org.apache.qpid.test.framework.Assertion; +import org.apache.qpid.test.framework.Circuit; +import org.apache.qpid.test.framework.FrameworkBaseCase; +import org.apache.qpid.test.framework.MessagingTestConfigProperties; +import org.apache.qpid.test.framework.sequencers.CircuitFactory; + +import org.apache.qpid.junit.extensions.TestThreadAware; +import org.apache.qpid.junit.extensions.TimingController; +import org.apache.qpid.junit.extensions.TimingControllerAware; +import org.apache.qpid.junit.extensions.util.ParsedProperties; +import org.apache.qpid.junit.extensions.util.TestContextProperties; + +import java.util.LinkedList; + +/** + * MessageThroughputPerf runs a test over a {@link Circuit} controlled by the test parameters. It logs timings of + * the time required to receive samples consisting of batches of messages. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Measure message throughput accross a test circuit. <td> {@link Circuit} + * </table> + * + * @todo Check that all of the messages were sent. Check that the receiving end got the same number of messages as + * the publishing end. + * + * @todo Set this up to run with zero sized tests. Size zero means send forever. Continuous sending to be interrupted + * by completion of the test duration, or shutdown hook when the user presses Ctrl-C. + */ +public class MessageThroughputPerf extends FrameworkBaseCase implements TimingControllerAware, TestThreadAware +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(MessageThroughputPerf.class); + + /** Holds the timing controller, used to log test timings from self-timed tests. */ + private TimingController timingController; + + /** Thread local to hold the per-thread test setup fields. */ + ThreadLocal<PerThreadSetup> threadSetup = new ThreadLocal<PerThreadSetup>(); + + /** + * Creates a new test case with the specified name. + * + * @param name The test case name. + */ + public MessageThroughputPerf(String name) + { + super(name); + } + + /** + * Performs the a basic P2P test case. + * + * @param numMessages The number of messages to send in the test. + */ + public void testThroughput(int numMessages) + { + log.debug("public void testThroughput(): called"); + + PerThreadSetup setup = threadSetup.get(); + assertNoFailures(setup.testCircuit.test(numMessages, new LinkedList<Assertion>())); + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as known to the test + * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test + * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case + * name "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + log.debug("public String getTestCaseNameForTestMethod(String methodName = " + methodName + "): called"); + + return "DEFAULT_CIRCUIT_TEST"; + } + + /** + * Used by test runners that can supply a {@link org.apache.qpid.junit.extensions.TimingController} to set the + * controller on an aware test. + * + * @param controller The timing controller. + */ + public void setTimingController(TimingController controller) + { + timingController = controller; + } + + /** + * Overrides the parent setUp method so that the in-vm broker creation is not done on a per test basis. + * + * @throws Exception Any exceptions allowed to fall through and fail the test. + */ + protected void setUp() throws Exception + { + NDC.push(getName()); + + testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + } + + /** + * Overrides the parent setUp method so that the in-vm broker clean-up is not done on a per test basis. + */ + protected void tearDown() + { + NDC.pop(); + } + + /** + * Performs test fixture creation on a per thread basis. This will only be called once for each test thread. + */ + public void threadSetUp() + { + // Run the test setup tasks. This may create an in-vm broker, if a decorator has injected a task for this. + taskHandler.runSetupTasks(); + + // Get the test parameters, any overrides on the command line will have been applied. + ParsedProperties testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + + // Customize the test parameters. + testProps.setProperty("TEST_NAME", "DEFAULT_CIRCUIT_TEST"); + testProps.setProperty(MessagingTestConfigProperties.SEND_DESTINATION_NAME_ROOT_PROPNAME, "testqueue"); + + // Get the test circuit factory to create test circuits and run the standard test procedure through. + CircuitFactory circuitFactory = getCircuitFactory(); + + // Create the test circuit. This projects the circuit onto the available test nodes and connects it up. + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Store the test configuration for the thread. + PerThreadSetup setup = new PerThreadSetup(); + setup.testCircuit = testCircuit; + threadSetup.set(setup); + } + + /** + * Called when a test thread is destroyed. + */ + public void threadTearDown() + { + // Run the test teardown tasks. This may destroy the in-vm broker, if a decorator has injected a task for this. + taskHandler.runSetupTasks(); + } + + /** + * Holds the per-thread test configurations. + */ + protected static class PerThreadSetup + { + /** Holds the test circuit to run tests on. */ + Circuit testCircuit; + } + + /** + * Compiles all the tests in this class into a suite. + * + * @return The test suite. + */ + public static Test suite() + { + // Build a new test suite + TestSuite suite = new TestSuite("Qpid Throughput Performance Tests"); + + suite.addTest(new MessageThroughputPerf("testThroughput")); + + return suite; + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/exchange/MessagingTestConfigProperties.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/exchange/MessagingTestConfigProperties.java index 3a3b7f92dd..2d89d319d7 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/exchange/MessagingTestConfigProperties.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/exchange/MessagingTestConfigProperties.java @@ -1,308 +1,308 @@ -/*
- *
- * 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.server.exchange;
-
-import org.apache.qpid.jms.Session;
-import org.apache.qpid.junit.extensions.util.ParsedProperties;
-
-/**
- * MessagingTestConfigProperties defines a set of property names and default values for specifying a messaging topology,
- * and test parameters for running a messaging test over that topology. A Properties object holding some of these
- * properties, superimposed onto the defaults, is used to establish test topologies and control test behaviour.
- *
- * <p/>A complete list of the parameters, default values and comments on their usage is provided here:
- *
- * <p/><table><caption>Parameters</caption>
- * <tr><th> Parameter <th> Default <th> Comments
- * <tr><td> messageSize <td> 0 <td> Message size in bytes. Not including any headers.
- * <tr><td> destinationName <td> ping <td> The root name to use to generate destination names to ping.
- * <tr><td> persistent <td> false <td> Determines whether peristent delivery is used.
- * <tr><td> transacted <td> false <td> Determines whether messages are sent/received in transactions.
- * <tr><td> broker <td> tcp://localhost:5672 <td> Determines the broker to connect to.
- * <tr><td> virtualHost <td> test <td> Determines the virtual host to send all ping over.
- * <tr><td> rate <td> 0 <td> The maximum rate (in hertz) to send messages at. 0 means no limit.
- * <tr><td> verbose <td> false <td> The verbose flag for debugging. Prints to console on every message.
- * <tr><td> pubsub <td> false <td> Whether to ping topics or queues. Uses p2p by default.
- * <tr><td> username <td> guest <td> The username to access the broker with.
- * <tr><td> password <td> guest <td> The password to access the broker with.
- * <tr><td> selector <td> null <td> Not used. Defines a message selector to filter pings with.
- * <tr><td> destinationCount <td> 1 <td> The number of receivers listening to the pings.
- * <tr><td> timeout <td> 30000 <td> In milliseconds. The timeout to stop waiting for replies.
- * <tr><td> commitBatchSize <td> 1 <td> The number of messages per transaction in transactional mode.
- * <tr><td> uniqueDests <td> true <td> Whether each receiver only listens to one ping destination or all.
- * <tr><td> durableDests <td> false <td> Whether or not durable destinations are used.
- * <tr><td> ackMode <td> AUTO_ACK <td> The message acknowledgement mode. Possible values are:
- * 0 - SESSION_TRANSACTED
- * 1 - AUTO_ACKNOWLEDGE
- * 2 - CLIENT_ACKNOWLEDGE
- * 3 - DUPS_OK_ACKNOWLEDGE
- * 257 - NO_ACKNOWLEDGE
- * 258 - PRE_ACKNOWLEDGE
- * <tr><td> maxPending <td> 0 <td> The maximum size in bytes, of messages sent but not yet received.
- * Limits the volume of messages currently buffered on the client
- * or broker. Can help scale test clients by limiting amount of buffered
- * data to avoid out of memory errors.
- * </table>
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Provide the names and defaults of all test parameters.
- * </table>
- */
-public class MessagingTestConfigProperties
-{
- // ====================== Connection Properties ==================================
-
- /** Holds the name of the default connection configuration. */
- public static final String CONNECTION_NAME = "broker";
-
- /** Holds the name of the property to get the initial context factory name from. */
- public static final String INITIAL_CONTEXT_FACTORY_PROPNAME = "java.naming.factory.initial";
-
- /** Defines the class to use as the initial context factory by default. */
- public static final String INITIAL_CONTEXT_FACTORY_DEFAULT = "org.apache.qpid.jndi.PropertiesFileInitialContextFactory";
-
- /** Holds the name of the default connection factory configuration property. */
- public static final String CONNECTION_PROPNAME = "connectionfactory.broker";
-
- /** Defeins the default connection configuration. */
- public static final String CONNECTION_DEFAULT = "amqp://guest:guest@clientid/?brokerlist='vm://:1'";
-
- /** Holds the name of the property to get the test broker url from. */
- public static final String BROKER_PROPNAME = "qpid.test.broker";
-
- /** Holds the default broker url for the test. */
- public static final String BROKER_DEFAULT = "vm://:1";
-
- /** Holds the name of the property to get the test broker virtual path. */
- public static final String VIRTUAL_HOST_PROPNAME = "virtualHost";
-
- /** Holds the default virtual path for the test. */
- public static final String VIRTUAL_HOST_DEFAULT = "";
-
- /** Holds the name of the property to get the broker access username from. */
- public static final String USERNAME_PROPNAME = "username";
-
- /** Holds the default broker log on username. */
- public static final String USERNAME_DEFAULT = "guest";
-
- /** Holds the name of the property to get the broker access password from. */
- public static final String PASSWORD_PROPNAME = "password";
-
- /** Holds the default broker log on password. */
- public static final String PASSWORD_DEFAULT = "guest";
-
- // ====================== Messaging Topology Properties ==========================
-
- /** Holds the name of the property to get the bind publisher procuder flag from. */
- public static final String PUBLISHER_PRODUCER_BIND_PROPNAME = "publisherProducerBind";
-
- /** Holds the default value of the publisher producer flag. */
- public static final boolean PUBLISHER_PRODUCER_BIND_DEFAULT = true;
-
- /** Holds the name of the property to get the bind publisher procuder flag from. */
- public static final String PUBLISHER_CONSUMER_BIND_PROPNAME = "publisherConsumerBind";
-
- /** Holds the default value of the publisher consumer flag. */
- public static final boolean PUBLISHER_CONSUMER_BIND_DEFAULT = false;
-
- /** Holds the name of the property to get the bind receiver procuder flag from. */
- public static final String RECEIVER_PRODUCER_BIND_PROPNAME = "receiverProducerBind";
-
- /** Holds the default value of the receiver producer flag. */
- public static final boolean RECEIVER_PRODUCER_BIND_DEFAULT = false;
-
- /** Holds the name of the property to get the bind receiver procuder flag from. */
- public static final String RECEIVER_CONSUMER_BIND_PROPNAME = "receiverConsumerBind";
-
- /** Holds the default value of the receiver consumer flag. */
- public static final boolean RECEIVER_CONSUMER_BIND_DEFAULT = true;
-
- /** Holds the name of the property to get the destination name root from. */
- public static final String SEND_DESTINATION_NAME_ROOT_PROPNAME = "sendDestinationRoot";
-
- /** Holds the root of the name of the default destination to send to. */
- public static final String SEND_DESTINATION_NAME_ROOT_DEFAULT = "sendTo";
-
- /** Holds the name of the property to get the destination name root from. */
- public static final String RECEIVE_DESTINATION_NAME_ROOT_PROPNAME = "receiveDestinationRoot";
-
- /** Holds the root of the name of the default destination to send to. */
- public static final String RECEIVE_DESTINATION_NAME_ROOT_DEFAULT = "receiveFrom";
-
- /** Holds the name of the proeprty to get the destination count from. */
- public static final String DESTINATION_COUNT_PROPNAME = "destinationCount";
-
- /** Defines the default number of destinations to ping. */
- public static final int DESTINATION_COUNT_DEFAULT = 1;
-
- /** Holds the name of the property to get the p2p or pub/sub messaging mode from. */
- public static final String PUBSUB_PROPNAME = "pubsub";
-
- /** Holds the pub/sub mode default, true means ping a topic, false means ping a queue. */
- public static final boolean PUBSUB_DEFAULT = false;
-
- // ====================== JMS Options and Flags =================================
-
- /** Holds the name of the property to get the test delivery mode from. */
- public static final String PERSISTENT_MODE_PROPNAME = "persistent";
-
- /** Holds the message delivery mode to use for the test. */
- public static final boolean PERSISTENT_MODE_DEFAULT = false;
-
- /** Holds the name of the property to get the test transactional mode from. */
- public static final String TRANSACTED_PROPNAME = "transacted";
-
- /** Holds the transactional mode to use for the test. */
- public static final boolean TRANSACTED_DEFAULT = false;
-
- /** Holds the name of the property to set the no local flag from. */
- public static final String NO_LOCAL_PROPNAME = "noLocal";
-
- /** Defines the default value of the no local flag to use when consuming messages. */
- public static final boolean NO_LOCAL_DEFAULT = false;
-
- /** Holds the name of the property to get the message acknowledgement mode from. */
- public static final String ACK_MODE_PROPNAME = "ackMode";
-
- /** Defines the default message acknowledgement mode. */
- public static final int ACK_MODE_DEFAULT = Session.AUTO_ACKNOWLEDGE;
-
- /** Holds the name of the property to get the durable subscriptions flag from, when doing pub/sub messaging. */
- public static final String DURABLE_SUBSCRIPTION_PROPNAME = "durableSubscription";
-
- /** Defines the default value of the durable subscriptions flag. */
- public static final boolean DURABLE_SUBSCRIPTION_DEFAULT = false;
-
- // ====================== Qpid Options and Flags ================================
-
- /** Holds the name of the property to set the exclusive flag from. */
- public static final String EXCLUSIVE_PROPNAME = "exclusive";
-
- /** Defines the default value of the exclusive flag to use when consuming messages. */
- public static final boolean EXCLUSIVE_DEFAULT = false;
-
- /** Holds the name of the property to set the immediate flag from. */
- public static final String IMMEDIATE_PROPNAME = "immediate";
-
- /** Defines the default value of the immediate flag to use when sending messages. */
- public static final boolean IMMEDIATE_DEFAULT = false;
-
- /** Holds the name of the property to set the mandatory flag from. */
- public static final String MANDATORY_PROPNAME = "mandatory";
-
- /** Defines the default value of the mandatory flag to use when sending messages. */
- public static final boolean MANDATORY_DEFAULT = false;
-
- /** Holds the name of the property to get the durable destinations flag from. */
- public static final String DURABLE_DESTS_PROPNAME = "durableDests";
-
- /** Default value for the durable destinations flag. */
- public static final boolean DURABLE_DESTS_DEFAULT = false;
-
- /** Holds the name of the proeprty to set the prefetch size from. */
- public static final String PREFECTH_PROPNAME = "prefetch";
-
- /** Defines the default prefetch size to use when consuming messages. */
- public static final int PREFETCH_DEFAULT = 100;
-
- // ====================== Common Test Parameters ================================
-
- /** Holds the name of the property to get the test message size from. */
- public static final String MESSAGE_SIZE_PROPNAME = "messageSize";
-
- /** Used to set up a default message size. */
- public static final int MESSAGE_SIZE_DEAFULT = 0;
-
- /** Holds the name of the property to get the message rate from. */
- public static final String RATE_PROPNAME = "rate";
-
- /** Defines the default rate (in pings per second) to send pings at. 0 means as fast as possible, no restriction. */
- public static final int RATE_DEFAULT = 0;
-
- /** Holds the name of the proeprty to get the. */
- public static final String SELECTOR_PROPNAME = "selector";
-
- /** Holds the default message selector. */
- public static final String SELECTOR_DEFAULT = "";
-
- /** Holds the name of the property to get the waiting timeout for response messages. */
- public static final String TIMEOUT_PROPNAME = "timeout";
-
- /** Default time to wait before assuming that a ping has timed out. */
- public static final long TIMEOUT_DEFAULT = 30000;
-
- /** Holds the name of the property to get the commit batch size from. */
- public static final String TX_BATCH_SIZE_PROPNAME = "commitBatchSize";
-
- /** Defines the default number of pings to send in each transaction when running transactionally. */
- public static final int TX_BATCH_SIZE_DEFAULT = 1;
-
- /** Holds the name of the property to set the maximum amount of pending message data for a producer to hold. */
- public static final String MAX_PENDING_PROPNAME = "maxPending";
-
- /** Defines the default maximum quantity of pending message data to allow producers to hold. */
- public static final int MAX_PENDING_DEFAULT = 0;
-
- /** Holds the name of the property to get the verbose mode proeprty from. */
- public static final String VERBOSE_PROPNAME = "verbose";
-
- /** Holds the default verbose mode. */
- public static final boolean VERBOSE_DEFAULT = false;
-
- /** Holds the default configuration properties. */
- public static ParsedProperties defaults = new ParsedProperties();
-
- static
- {
- defaults.setPropertyIfNull(INITIAL_CONTEXT_FACTORY_PROPNAME, INITIAL_CONTEXT_FACTORY_DEFAULT);
- defaults.setPropertyIfNull(CONNECTION_PROPNAME, CONNECTION_DEFAULT);
- defaults.setPropertyIfNull(MESSAGE_SIZE_PROPNAME, MESSAGE_SIZE_DEAFULT);
- defaults.setPropertyIfNull(PUBLISHER_PRODUCER_BIND_PROPNAME, PUBLISHER_PRODUCER_BIND_DEFAULT);
- defaults.setPropertyIfNull(PUBLISHER_CONSUMER_BIND_PROPNAME, PUBLISHER_CONSUMER_BIND_DEFAULT);
- defaults.setPropertyIfNull(RECEIVER_PRODUCER_BIND_PROPNAME, RECEIVER_PRODUCER_BIND_DEFAULT);
- defaults.setPropertyIfNull(RECEIVER_CONSUMER_BIND_PROPNAME, RECEIVER_CONSUMER_BIND_DEFAULT);
- defaults.setPropertyIfNull(SEND_DESTINATION_NAME_ROOT_PROPNAME, SEND_DESTINATION_NAME_ROOT_DEFAULT);
- defaults.setPropertyIfNull(RECEIVE_DESTINATION_NAME_ROOT_PROPNAME, RECEIVE_DESTINATION_NAME_ROOT_DEFAULT);
- defaults.setPropertyIfNull(PERSISTENT_MODE_PROPNAME, PERSISTENT_MODE_DEFAULT);
- defaults.setPropertyIfNull(TRANSACTED_PROPNAME, TRANSACTED_DEFAULT);
- defaults.setPropertyIfNull(BROKER_PROPNAME, BROKER_DEFAULT);
- defaults.setPropertyIfNull(VIRTUAL_HOST_PROPNAME, VIRTUAL_HOST_DEFAULT);
- defaults.setPropertyIfNull(RATE_PROPNAME, RATE_DEFAULT);
- defaults.setPropertyIfNull(VERBOSE_PROPNAME, VERBOSE_DEFAULT);
- defaults.setPropertyIfNull(PUBSUB_PROPNAME, PUBSUB_DEFAULT);
- defaults.setPropertyIfNull(USERNAME_PROPNAME, USERNAME_DEFAULT);
- defaults.setPropertyIfNull(PASSWORD_PROPNAME, PASSWORD_DEFAULT);
- defaults.setPropertyIfNull(SELECTOR_PROPNAME, SELECTOR_DEFAULT);
- defaults.setPropertyIfNull(DESTINATION_COUNT_PROPNAME, DESTINATION_COUNT_DEFAULT);
- defaults.setPropertyIfNull(TIMEOUT_PROPNAME, TIMEOUT_DEFAULT);
- defaults.setPropertyIfNull(TX_BATCH_SIZE_PROPNAME, TX_BATCH_SIZE_DEFAULT);
- defaults.setPropertyIfNull(DURABLE_DESTS_PROPNAME, DURABLE_DESTS_DEFAULT);
- defaults.setPropertyIfNull(ACK_MODE_PROPNAME, ACK_MODE_DEFAULT);
- defaults.setPropertyIfNull(DURABLE_SUBSCRIPTION_PROPNAME, DURABLE_SUBSCRIPTION_DEFAULT);
- defaults.setPropertyIfNull(MAX_PENDING_PROPNAME, MAX_PENDING_DEFAULT);
- defaults.setPropertyIfNull(PREFECTH_PROPNAME, PREFETCH_DEFAULT);
- defaults.setPropertyIfNull(NO_LOCAL_PROPNAME, NO_LOCAL_DEFAULT);
- defaults.setPropertyIfNull(EXCLUSIVE_PROPNAME, EXCLUSIVE_DEFAULT);
- defaults.setPropertyIfNull(IMMEDIATE_PROPNAME, IMMEDIATE_DEFAULT);
- defaults.setPropertyIfNull(MANDATORY_PROPNAME, MANDATORY_DEFAULT);
- }
-}
+/* + * + * 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.server.exchange; + +import org.apache.qpid.jms.Session; +import org.apache.qpid.junit.extensions.util.ParsedProperties; + +/** + * MessagingTestConfigProperties defines a set of property names and default values for specifying a messaging topology, + * and test parameters for running a messaging test over that topology. A Properties object holding some of these + * properties, superimposed onto the defaults, is used to establish test topologies and control test behaviour. + * + * <p/>A complete list of the parameters, default values and comments on their usage is provided here: + * + * <p/><table><caption>Parameters</caption> + * <tr><th> Parameter <th> Default <th> Comments + * <tr><td> messageSize <td> 0 <td> Message size in bytes. Not including any headers. + * <tr><td> destinationName <td> ping <td> The root name to use to generate destination names to ping. + * <tr><td> persistent <td> false <td> Determines whether peristent delivery is used. + * <tr><td> transacted <td> false <td> Determines whether messages are sent/received in transactions. + * <tr><td> broker <td> tcp://localhost:5672 <td> Determines the broker to connect to. + * <tr><td> virtualHost <td> test <td> Determines the virtual host to send all ping over. + * <tr><td> rate <td> 0 <td> The maximum rate (in hertz) to send messages at. 0 means no limit. + * <tr><td> verbose <td> false <td> The verbose flag for debugging. Prints to console on every message. + * <tr><td> pubsub <td> false <td> Whether to ping topics or queues. Uses p2p by default. + * <tr><td> username <td> guest <td> The username to access the broker with. + * <tr><td> password <td> guest <td> The password to access the broker with. + * <tr><td> selector <td> null <td> Not used. Defines a message selector to filter pings with. + * <tr><td> destinationCount <td> 1 <td> The number of receivers listening to the pings. + * <tr><td> timeout <td> 30000 <td> In milliseconds. The timeout to stop waiting for replies. + * <tr><td> commitBatchSize <td> 1 <td> The number of messages per transaction in transactional mode. + * <tr><td> uniqueDests <td> true <td> Whether each receiver only listens to one ping destination or all. + * <tr><td> durableDests <td> false <td> Whether or not durable destinations are used. + * <tr><td> ackMode <td> AUTO_ACK <td> The message acknowledgement mode. Possible values are: + * 0 - SESSION_TRANSACTED + * 1 - AUTO_ACKNOWLEDGE + * 2 - CLIENT_ACKNOWLEDGE + * 3 - DUPS_OK_ACKNOWLEDGE + * 257 - NO_ACKNOWLEDGE + * 258 - PRE_ACKNOWLEDGE + * <tr><td> maxPending <td> 0 <td> The maximum size in bytes, of messages sent but not yet received. + * Limits the volume of messages currently buffered on the client + * or broker. Can help scale test clients by limiting amount of buffered + * data to avoid out of memory errors. + * </table> + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Provide the names and defaults of all test parameters. + * </table> + */ +public class MessagingTestConfigProperties +{ + // ====================== Connection Properties ================================== + + /** Holds the name of the default connection configuration. */ + public static final String CONNECTION_NAME = "broker"; + + /** Holds the name of the property to get the initial context factory name from. */ + public static final String INITIAL_CONTEXT_FACTORY_PROPNAME = "java.naming.factory.initial"; + + /** Defines the class to use as the initial context factory by default. */ + public static final String INITIAL_CONTEXT_FACTORY_DEFAULT = "org.apache.qpid.jndi.PropertiesFileInitialContextFactory"; + + /** Holds the name of the default connection factory configuration property. */ + public static final String CONNECTION_PROPNAME = "connectionfactory.broker"; + + /** Defeins the default connection configuration. */ + public static final String CONNECTION_DEFAULT = "amqp://guest:guest@clientid/?brokerlist='vm://:1'"; + + /** Holds the name of the property to get the test broker url from. */ + public static final String BROKER_PROPNAME = "qpid.test.broker"; + + /** Holds the default broker url for the test. */ + public static final String BROKER_DEFAULT = "vm://:1"; + + /** Holds the name of the property to get the test broker virtual path. */ + public static final String VIRTUAL_HOST_PROPNAME = "virtualHost"; + + /** Holds the default virtual path for the test. */ + public static final String VIRTUAL_HOST_DEFAULT = ""; + + /** Holds the name of the property to get the broker access username from. */ + public static final String USERNAME_PROPNAME = "username"; + + /** Holds the default broker log on username. */ + public static final String USERNAME_DEFAULT = "guest"; + + /** Holds the name of the property to get the broker access password from. */ + public static final String PASSWORD_PROPNAME = "password"; + + /** Holds the default broker log on password. */ + public static final String PASSWORD_DEFAULT = "guest"; + + // ====================== Messaging Topology Properties ========================== + + /** Holds the name of the property to get the bind publisher procuder flag from. */ + public static final String PUBLISHER_PRODUCER_BIND_PROPNAME = "publisherProducerBind"; + + /** Holds the default value of the publisher producer flag. */ + public static final boolean PUBLISHER_PRODUCER_BIND_DEFAULT = true; + + /** Holds the name of the property to get the bind publisher procuder flag from. */ + public static final String PUBLISHER_CONSUMER_BIND_PROPNAME = "publisherConsumerBind"; + + /** Holds the default value of the publisher consumer flag. */ + public static final boolean PUBLISHER_CONSUMER_BIND_DEFAULT = false; + + /** Holds the name of the property to get the bind receiver procuder flag from. */ + public static final String RECEIVER_PRODUCER_BIND_PROPNAME = "receiverProducerBind"; + + /** Holds the default value of the receiver producer flag. */ + public static final boolean RECEIVER_PRODUCER_BIND_DEFAULT = false; + + /** Holds the name of the property to get the bind receiver procuder flag from. */ + public static final String RECEIVER_CONSUMER_BIND_PROPNAME = "receiverConsumerBind"; + + /** Holds the default value of the receiver consumer flag. */ + public static final boolean RECEIVER_CONSUMER_BIND_DEFAULT = true; + + /** Holds the name of the property to get the destination name root from. */ + public static final String SEND_DESTINATION_NAME_ROOT_PROPNAME = "sendDestinationRoot"; + + /** Holds the root of the name of the default destination to send to. */ + public static final String SEND_DESTINATION_NAME_ROOT_DEFAULT = "sendTo"; + + /** Holds the name of the property to get the destination name root from. */ + public static final String RECEIVE_DESTINATION_NAME_ROOT_PROPNAME = "receiveDestinationRoot"; + + /** Holds the root of the name of the default destination to send to. */ + public static final String RECEIVE_DESTINATION_NAME_ROOT_DEFAULT = "receiveFrom"; + + /** Holds the name of the proeprty to get the destination count from. */ + public static final String DESTINATION_COUNT_PROPNAME = "destinationCount"; + + /** Defines the default number of destinations to ping. */ + public static final int DESTINATION_COUNT_DEFAULT = 1; + + /** Holds the name of the property to get the p2p or pub/sub messaging mode from. */ + public static final String PUBSUB_PROPNAME = "pubsub"; + + /** Holds the pub/sub mode default, true means ping a topic, false means ping a queue. */ + public static final boolean PUBSUB_DEFAULT = false; + + // ====================== JMS Options and Flags ================================= + + /** Holds the name of the property to get the test delivery mode from. */ + public static final String PERSISTENT_MODE_PROPNAME = "persistent"; + + /** Holds the message delivery mode to use for the test. */ + public static final boolean PERSISTENT_MODE_DEFAULT = false; + + /** Holds the name of the property to get the test transactional mode from. */ + public static final String TRANSACTED_PROPNAME = "transacted"; + + /** Holds the transactional mode to use for the test. */ + public static final boolean TRANSACTED_DEFAULT = false; + + /** Holds the name of the property to set the no local flag from. */ + public static final String NO_LOCAL_PROPNAME = "noLocal"; + + /** Defines the default value of the no local flag to use when consuming messages. */ + public static final boolean NO_LOCAL_DEFAULT = false; + + /** Holds the name of the property to get the message acknowledgement mode from. */ + public static final String ACK_MODE_PROPNAME = "ackMode"; + + /** Defines the default message acknowledgement mode. */ + public static final int ACK_MODE_DEFAULT = Session.AUTO_ACKNOWLEDGE; + + /** Holds the name of the property to get the durable subscriptions flag from, when doing pub/sub messaging. */ + public static final String DURABLE_SUBSCRIPTION_PROPNAME = "durableSubscription"; + + /** Defines the default value of the durable subscriptions flag. */ + public static final boolean DURABLE_SUBSCRIPTION_DEFAULT = false; + + // ====================== Qpid Options and Flags ================================ + + /** Holds the name of the property to set the exclusive flag from. */ + public static final String EXCLUSIVE_PROPNAME = "exclusive"; + + /** Defines the default value of the exclusive flag to use when consuming messages. */ + public static final boolean EXCLUSIVE_DEFAULT = false; + + /** Holds the name of the property to set the immediate flag from. */ + public static final String IMMEDIATE_PROPNAME = "immediate"; + + /** Defines the default value of the immediate flag to use when sending messages. */ + public static final boolean IMMEDIATE_DEFAULT = false; + + /** Holds the name of the property to set the mandatory flag from. */ + public static final String MANDATORY_PROPNAME = "mandatory"; + + /** Defines the default value of the mandatory flag to use when sending messages. */ + public static final boolean MANDATORY_DEFAULT = false; + + /** Holds the name of the property to get the durable destinations flag from. */ + public static final String DURABLE_DESTS_PROPNAME = "durableDests"; + + /** Default value for the durable destinations flag. */ + public static final boolean DURABLE_DESTS_DEFAULT = false; + + /** Holds the name of the proeprty to set the prefetch size from. */ + public static final String PREFECTH_PROPNAME = "prefetch"; + + /** Defines the default prefetch size to use when consuming messages. */ + public static final int PREFETCH_DEFAULT = 100; + + // ====================== Common Test Parameters ================================ + + /** Holds the name of the property to get the test message size from. */ + public static final String MESSAGE_SIZE_PROPNAME = "messageSize"; + + /** Used to set up a default message size. */ + public static final int MESSAGE_SIZE_DEAFULT = 0; + + /** Holds the name of the property to get the message rate from. */ + public static final String RATE_PROPNAME = "rate"; + + /** Defines the default rate (in pings per second) to send pings at. 0 means as fast as possible, no restriction. */ + public static final int RATE_DEFAULT = 0; + + /** Holds the name of the proeprty to get the. */ + public static final String SELECTOR_PROPNAME = "selector"; + + /** Holds the default message selector. */ + public static final String SELECTOR_DEFAULT = ""; + + /** Holds the name of the property to get the waiting timeout for response messages. */ + public static final String TIMEOUT_PROPNAME = "timeout"; + + /** Default time to wait before assuming that a ping has timed out. */ + public static final long TIMEOUT_DEFAULT = 30000; + + /** Holds the name of the property to get the commit batch size from. */ + public static final String TX_BATCH_SIZE_PROPNAME = "commitBatchSize"; + + /** Defines the default number of pings to send in each transaction when running transactionally. */ + public static final int TX_BATCH_SIZE_DEFAULT = 1; + + /** Holds the name of the property to set the maximum amount of pending message data for a producer to hold. */ + public static final String MAX_PENDING_PROPNAME = "maxPending"; + + /** Defines the default maximum quantity of pending message data to allow producers to hold. */ + public static final int MAX_PENDING_DEFAULT = 0; + + /** Holds the name of the property to get the verbose mode proeprty from. */ + public static final String VERBOSE_PROPNAME = "verbose"; + + /** Holds the default verbose mode. */ + public static final boolean VERBOSE_DEFAULT = false; + + /** Holds the default configuration properties. */ + public static ParsedProperties defaults = new ParsedProperties(); + + static + { + defaults.setPropertyIfNull(INITIAL_CONTEXT_FACTORY_PROPNAME, INITIAL_CONTEXT_FACTORY_DEFAULT); + defaults.setPropertyIfNull(CONNECTION_PROPNAME, CONNECTION_DEFAULT); + defaults.setPropertyIfNull(MESSAGE_SIZE_PROPNAME, MESSAGE_SIZE_DEAFULT); + defaults.setPropertyIfNull(PUBLISHER_PRODUCER_BIND_PROPNAME, PUBLISHER_PRODUCER_BIND_DEFAULT); + defaults.setPropertyIfNull(PUBLISHER_CONSUMER_BIND_PROPNAME, PUBLISHER_CONSUMER_BIND_DEFAULT); + defaults.setPropertyIfNull(RECEIVER_PRODUCER_BIND_PROPNAME, RECEIVER_PRODUCER_BIND_DEFAULT); + defaults.setPropertyIfNull(RECEIVER_CONSUMER_BIND_PROPNAME, RECEIVER_CONSUMER_BIND_DEFAULT); + defaults.setPropertyIfNull(SEND_DESTINATION_NAME_ROOT_PROPNAME, SEND_DESTINATION_NAME_ROOT_DEFAULT); + defaults.setPropertyIfNull(RECEIVE_DESTINATION_NAME_ROOT_PROPNAME, RECEIVE_DESTINATION_NAME_ROOT_DEFAULT); + defaults.setPropertyIfNull(PERSISTENT_MODE_PROPNAME, PERSISTENT_MODE_DEFAULT); + defaults.setPropertyIfNull(TRANSACTED_PROPNAME, TRANSACTED_DEFAULT); + defaults.setPropertyIfNull(BROKER_PROPNAME, BROKER_DEFAULT); + defaults.setPropertyIfNull(VIRTUAL_HOST_PROPNAME, VIRTUAL_HOST_DEFAULT); + defaults.setPropertyIfNull(RATE_PROPNAME, RATE_DEFAULT); + defaults.setPropertyIfNull(VERBOSE_PROPNAME, VERBOSE_DEFAULT); + defaults.setPropertyIfNull(PUBSUB_PROPNAME, PUBSUB_DEFAULT); + defaults.setPropertyIfNull(USERNAME_PROPNAME, USERNAME_DEFAULT); + defaults.setPropertyIfNull(PASSWORD_PROPNAME, PASSWORD_DEFAULT); + defaults.setPropertyIfNull(SELECTOR_PROPNAME, SELECTOR_DEFAULT); + defaults.setPropertyIfNull(DESTINATION_COUNT_PROPNAME, DESTINATION_COUNT_DEFAULT); + defaults.setPropertyIfNull(TIMEOUT_PROPNAME, TIMEOUT_DEFAULT); + defaults.setPropertyIfNull(TX_BATCH_SIZE_PROPNAME, TX_BATCH_SIZE_DEFAULT); + defaults.setPropertyIfNull(DURABLE_DESTS_PROPNAME, DURABLE_DESTS_DEFAULT); + defaults.setPropertyIfNull(ACK_MODE_PROPNAME, ACK_MODE_DEFAULT); + defaults.setPropertyIfNull(DURABLE_SUBSCRIPTION_PROPNAME, DURABLE_SUBSCRIPTION_DEFAULT); + defaults.setPropertyIfNull(MAX_PENDING_PROPNAME, MAX_PENDING_DEFAULT); + defaults.setPropertyIfNull(PREFECTH_PROPNAME, PREFETCH_DEFAULT); + defaults.setPropertyIfNull(NO_LOCAL_PROPNAME, NO_LOCAL_DEFAULT); + defaults.setPropertyIfNull(EXCLUSIVE_PROPNAME, EXCLUSIVE_DEFAULT); + defaults.setPropertyIfNull(IMMEDIATE_PROPNAME, IMMEDIATE_DEFAULT); + defaults.setPropertyIfNull(MANDATORY_PROPNAME, MANDATORY_DEFAULT); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/exchange/ReturnUnroutableMandatoryMessageTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/exchange/ReturnUnroutableMandatoryMessageTest.java index a6a2bbb80f..bae3f844d7 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/exchange/ReturnUnroutableMandatoryMessageTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/exchange/ReturnUnroutableMandatoryMessageTest.java @@ -1,312 +1,312 @@ -/*
- *
- * 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.server.exchange;
-
-import junit.framework.TestCase;
-import org.apache.log4j.Logger;
-import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.server.util.NullApplicationRegistry;
-import org.apache.qpid.client.*;
-import org.apache.qpid.client.transport.TransportConnection;
-import org.apache.qpid.url.AMQBindingURL;
-import org.apache.qpid.url.BindingURL;
-import org.apache.qpid.exchange.ExchangeDefaults;
-import org.apache.qpid.framing.FieldTable;
-
-import javax.jms.*;
-import java.util.List;
-import java.util.Collections;
-import java.util.ArrayList;
-
-public class ReturnUnroutableMandatoryMessageTest extends TestCase implements ExceptionListener
-{
- private static final Logger _logger = Logger.getLogger(ReturnUnroutableMandatoryMessageTest.class);
-
- private final List<Message> _bouncedMessageList = Collections.synchronizedList(new ArrayList<Message>());
- private static final String VIRTUALHOST = "test";
- private static final String BROKER = "vm://:1";
-
- static
- {
- 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");
- }
-
- protected void setUp() throws Exception
- {
- super.setUp();
- TransportConnection.createVMBroker(1);
- ApplicationRegistry.initialise(new NullApplicationRegistry(), 1);
- }
-
- protected void tearDown() throws Exception
- {
- super.tearDown();
- TransportConnection.killAllVMBrokers();
- }
-
- /**
- * Tests that mandatory message which are not routable are returned to the producer
- *
- * @throws Exception
- */
- public void testReturnUnroutableMandatoryMessage_HEADERS() throws Exception
- {
- _bouncedMessageList.clear();
- Connection con = new AMQConnection(BROKER, "guest", "guest", "consumer1", VIRTUALHOST);
-
-
- AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
-
- AMQHeadersExchange queue = new AMQHeadersExchange(new AMQBindingURL(ExchangeDefaults.HEADERS_EXCHANGE_CLASS + "://" + ExchangeDefaults.HEADERS_EXCHANGE_NAME + "/test/queue1?" + BindingURL.OPTION_ROUTING_KEY + "='F0000=1'"));
- FieldTable ft = new FieldTable();
- ft.setString("F1000", "1");
- MessageConsumer consumer = consumerSession.createConsumer(queue, AMQSession.DEFAULT_PREFETCH_LOW_MARK, AMQSession.DEFAULT_PREFETCH_HIGH_MARK, false, false, (String) null, ft);
-
- //force synch to ensure the consumer has resulted in a bound queue
- //((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS);
- // This is the default now
-
- Connection con2 = new AMQConnection(BROKER, "guest", "guest", "producer1", VIRTUALHOST);
-
- con2.setExceptionListener(this);
- AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
-
- // Need to start the "producer" connection in order to receive bounced messages
- _logger.info("Starting producer connection");
- con2.start();
-
-
- MessageProducer nonMandatoryProducer = producerSession.createProducer(queue, false, false);
- MessageProducer mandatoryProducer = producerSession.createProducer(queue);
-
- // First test - should neither be bounced nor routed
- _logger.info("Sending non-routable non-mandatory message");
- TextMessage msg1 = producerSession.createTextMessage("msg1");
- nonMandatoryProducer.send(msg1);
-
- // Second test - should be bounced
- _logger.info("Sending non-routable mandatory message");
- TextMessage msg2 = producerSession.createTextMessage("msg2");
- mandatoryProducer.send(msg2);
-
- // Third test - should be routed
- _logger.info("Sending routable message");
- TextMessage msg3 = producerSession.createTextMessage("msg3");
- msg3.setStringProperty("F1000", "1");
- mandatoryProducer.send(msg3);
-
-
- _logger.info("Starting consumer connection");
- con.start();
- TextMessage tm = (TextMessage) consumer.receive(1000L);
-
- assertTrue("No message routed to receiver", tm != null);
- assertTrue("Wrong message routed to receiver: " + tm.getText(), "msg3".equals(tm.getText()));
-
- try
- {
- Thread.sleep(1000L);
- }
- catch (InterruptedException e)
- {
- ;
- }
-
- assertTrue("Wrong number of messages bounced (expect 1): " + _bouncedMessageList.size(), _bouncedMessageList.size() == 1);
- Message m = _bouncedMessageList.get(0);
- assertTrue("Wrong message bounced: " + m.toString(), m.toString().contains("msg2"));
-
-
- con.close();
- con2.close();
-
-
- }
-
- public void testReturnUnroutableMandatoryMessage_QUEUE() throws Exception
- {
- _bouncedMessageList.clear();
- Connection con = new AMQConnection(BROKER, "guest", "guest", "consumer1", VIRTUALHOST);
-
-
- AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
-
- AMQQueue valid_queue = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_CLASS, "testReturnUnroutableMandatoryMessage_QUEUE");
- AMQQueue invalid_queue = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_CLASS, "testReturnUnroutableMandatoryMessage_QUEUE_INVALID");
- MessageConsumer consumer = consumerSession.createConsumer(valid_queue);
-
- //force synch to ensure the consumer has resulted in a bound queue
- //((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS);
- // This is the default now
-
- Connection con2 = new AMQConnection(BROKER, "guest", "guest", "producer1", VIRTUALHOST);
-
- con2.setExceptionListener(this);
- AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
-
- // Need to start the "producer" connection in order to receive bounced messages
- _logger.info("Starting producer connection");
- con2.start();
-
-
- MessageProducer nonMandatoryProducer = producerSession.createProducer(valid_queue, false, false);
- MessageProducer mandatoryProducer = producerSession.createProducer(invalid_queue);
-
- // First test - should be routed
- _logger.info("Sending non-mandatory message");
- TextMessage msg1 = producerSession.createTextMessage("msg1");
- nonMandatoryProducer.send(msg1);
-
- // Second test - should be bounced
- _logger.info("Sending non-routable mandatory message");
- TextMessage msg2 = producerSession.createTextMessage("msg2");
- mandatoryProducer.send(msg2);
-
-
- _logger.info("Starting consumer connection");
- con.start();
- TextMessage tm = (TextMessage) consumer.receive(1000L);
-
- assertTrue("No message routed to receiver", tm != null);
- assertTrue("Wrong message routed to receiver: " + tm.getText(), "msg1".equals(tm.getText()));
-
- try
- {
- Thread.sleep(1000L);
- }
- catch (InterruptedException e)
- {
- ;
- }
-
- assertTrue("Wrong number of messages bounced (expect 1): " + _bouncedMessageList.size(), _bouncedMessageList.size() == 1);
- Message m = _bouncedMessageList.get(0);
- assertTrue("Wrong message bounced: " + m.toString(), m.toString().contains("msg2"));
-
-
- con.close();
- con2.close();
- }
-
-
- public void testReturnUnroutableMandatoryMessage_TOPIC() throws Exception
- {
- _bouncedMessageList.clear();
- Connection con = new AMQConnection(BROKER, "guest", "guest", "consumer1", VIRTUALHOST);
-
-
- AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
-
- AMQTopic valid_topic = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_CLASS, "test.Return.Unroutable.Mandatory.Message.TOPIC");
- AMQTopic invalid_topic = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_CLASS, "test.Return.Unroutable.Mandatory.Message.TOPIC.invalid");
- MessageConsumer consumer = consumerSession.createConsumer(valid_topic);
-
- //force synch to ensure the consumer has resulted in a bound queue
- //((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS);
- // This is the default now
-
- Connection con2 = new AMQConnection(BROKER, "guest", "guest", "producer1", VIRTUALHOST);
-
- con2.setExceptionListener(this);
- AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
-
- // Need to start the "producer" connection in order to receive bounced messages
- _logger.info("Starting producer connection");
- con2.start();
-
-
- MessageProducer nonMandatoryProducer = producerSession.createProducer(valid_topic, false, false);
- MessageProducer mandatoryProducer = producerSession.createProducer(invalid_topic);
-
- // First test - should be routed
- _logger.info("Sending non-mandatory message");
- TextMessage msg1 = producerSession.createTextMessage("msg1");
- nonMandatoryProducer.send(msg1);
-
- // Second test - should be bounced
- _logger.info("Sending non-routable mandatory message");
- TextMessage msg2 = producerSession.createTextMessage("msg2");
- mandatoryProducer.send(msg2);
-
-
- _logger.info("Starting consumer connection");
- con.start();
- TextMessage tm = (TextMessage) consumer.receive(1000L);
-
- assertTrue("No message routed to receiver", tm != null);
- assertTrue("Wrong message routed to receiver: " + tm.getText(), "msg1".equals(tm.getText()));
-
- try
- {
- Thread.sleep(1000L);
- }
- catch (InterruptedException e)
- {
- ;
- }
-
- assertEquals("Wrong number of messages bounced: ", 1, _bouncedMessageList.size());
- Message m = _bouncedMessageList.get(0);
- assertTrue("Wrong message bounced: " + m.toString(), m.toString().contains("msg2"));
-
-
- con.close();
- con2.close();
- }
-
-
- public static junit.framework.Test suite()
- {
- return new junit.framework.TestSuite(ReturnUnroutableMandatoryMessageTest.class);
- }
-
- public void onException(JMSException jmsException)
- {
-
- Exception linkedException = null;
- try
- {
- linkedException = jmsException.getLinkedException();
- } catch (Exception e)
- {
- e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
- }
- if (linkedException instanceof AMQNoRouteException)
- {
- AMQNoRouteException noRoute = (AMQNoRouteException) linkedException;
- Message bounced = (Message) noRoute.getUndeliveredMessage();
- _bouncedMessageList.add(bounced);
- _logger.info("Caught expected NoRouteException");
- }
- else
- {
- _logger.warn("Caught exception on producer: ", jmsException);
- }
- }
-}
+/* + * + * 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.server.exchange; + +import junit.framework.TestCase; +import org.apache.log4j.Logger; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.util.NullApplicationRegistry; +import org.apache.qpid.client.*; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.url.AMQBindingURL; +import org.apache.qpid.url.BindingURL; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.FieldTable; + +import javax.jms.*; +import java.util.List; +import java.util.Collections; +import java.util.ArrayList; + +public class ReturnUnroutableMandatoryMessageTest extends TestCase implements ExceptionListener +{ + private static final Logger _logger = Logger.getLogger(ReturnUnroutableMandatoryMessageTest.class); + + private final List<Message> _bouncedMessageList = Collections.synchronizedList(new ArrayList<Message>()); + private static final String VIRTUALHOST = "test"; + private static final String BROKER = "vm://:1"; + + static + { + 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"); + } + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + ApplicationRegistry.initialise(new NullApplicationRegistry(), 1); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killAllVMBrokers(); + } + + /** + * Tests that mandatory message which are not routable are returned to the producer + * + * @throws Exception + */ + public void testReturnUnroutableMandatoryMessage_HEADERS() throws Exception + { + _bouncedMessageList.clear(); + Connection con = new AMQConnection(BROKER, "guest", "guest", "consumer1", VIRTUALHOST); + + + AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + AMQHeadersExchange queue = new AMQHeadersExchange(new AMQBindingURL(ExchangeDefaults.HEADERS_EXCHANGE_CLASS + "://" + ExchangeDefaults.HEADERS_EXCHANGE_NAME + "/test/queue1?" + BindingURL.OPTION_ROUTING_KEY + "='F0000=1'")); + FieldTable ft = new FieldTable(); + ft.setString("F1000", "1"); + MessageConsumer consumer = consumerSession.createConsumer(queue, AMQSession.DEFAULT_PREFETCH_LOW_MARK, AMQSession.DEFAULT_PREFETCH_HIGH_MARK, false, false, (String) null, ft); + + //force synch to ensure the consumer has resulted in a bound queue + //((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS); + // This is the default now + + Connection con2 = new AMQConnection(BROKER, "guest", "guest", "producer1", VIRTUALHOST); + + con2.setExceptionListener(this); + AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + // Need to start the "producer" connection in order to receive bounced messages + _logger.info("Starting producer connection"); + con2.start(); + + + MessageProducer nonMandatoryProducer = producerSession.createProducer(queue, false, false); + MessageProducer mandatoryProducer = producerSession.createProducer(queue); + + // First test - should neither be bounced nor routed + _logger.info("Sending non-routable non-mandatory message"); + TextMessage msg1 = producerSession.createTextMessage("msg1"); + nonMandatoryProducer.send(msg1); + + // Second test - should be bounced + _logger.info("Sending non-routable mandatory message"); + TextMessage msg2 = producerSession.createTextMessage("msg2"); + mandatoryProducer.send(msg2); + + // Third test - should be routed + _logger.info("Sending routable message"); + TextMessage msg3 = producerSession.createTextMessage("msg3"); + msg3.setStringProperty("F1000", "1"); + mandatoryProducer.send(msg3); + + + _logger.info("Starting consumer connection"); + con.start(); + TextMessage tm = (TextMessage) consumer.receive(1000L); + + assertTrue("No message routed to receiver", tm != null); + assertTrue("Wrong message routed to receiver: " + tm.getText(), "msg3".equals(tm.getText())); + + try + { + Thread.sleep(1000L); + } + catch (InterruptedException e) + { + ; + } + + assertTrue("Wrong number of messages bounced (expect 1): " + _bouncedMessageList.size(), _bouncedMessageList.size() == 1); + Message m = _bouncedMessageList.get(0); + assertTrue("Wrong message bounced: " + m.toString(), m.toString().contains("msg2")); + + + con.close(); + con2.close(); + + + } + + public void testReturnUnroutableMandatoryMessage_QUEUE() throws Exception + { + _bouncedMessageList.clear(); + Connection con = new AMQConnection(BROKER, "guest", "guest", "consumer1", VIRTUALHOST); + + + AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + AMQQueue valid_queue = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_CLASS, "testReturnUnroutableMandatoryMessage_QUEUE"); + AMQQueue invalid_queue = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_CLASS, "testReturnUnroutableMandatoryMessage_QUEUE_INVALID"); + MessageConsumer consumer = consumerSession.createConsumer(valid_queue); + + //force synch to ensure the consumer has resulted in a bound queue + //((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS); + // This is the default now + + Connection con2 = new AMQConnection(BROKER, "guest", "guest", "producer1", VIRTUALHOST); + + con2.setExceptionListener(this); + AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + // Need to start the "producer" connection in order to receive bounced messages + _logger.info("Starting producer connection"); + con2.start(); + + + MessageProducer nonMandatoryProducer = producerSession.createProducer(valid_queue, false, false); + MessageProducer mandatoryProducer = producerSession.createProducer(invalid_queue); + + // First test - should be routed + _logger.info("Sending non-mandatory message"); + TextMessage msg1 = producerSession.createTextMessage("msg1"); + nonMandatoryProducer.send(msg1); + + // Second test - should be bounced + _logger.info("Sending non-routable mandatory message"); + TextMessage msg2 = producerSession.createTextMessage("msg2"); + mandatoryProducer.send(msg2); + + + _logger.info("Starting consumer connection"); + con.start(); + TextMessage tm = (TextMessage) consumer.receive(1000L); + + assertTrue("No message routed to receiver", tm != null); + assertTrue("Wrong message routed to receiver: " + tm.getText(), "msg1".equals(tm.getText())); + + try + { + Thread.sleep(1000L); + } + catch (InterruptedException e) + { + ; + } + + assertTrue("Wrong number of messages bounced (expect 1): " + _bouncedMessageList.size(), _bouncedMessageList.size() == 1); + Message m = _bouncedMessageList.get(0); + assertTrue("Wrong message bounced: " + m.toString(), m.toString().contains("msg2")); + + + con.close(); + con2.close(); + } + + + public void testReturnUnroutableMandatoryMessage_TOPIC() throws Exception + { + _bouncedMessageList.clear(); + Connection con = new AMQConnection(BROKER, "guest", "guest", "consumer1", VIRTUALHOST); + + + AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + AMQTopic valid_topic = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_CLASS, "test.Return.Unroutable.Mandatory.Message.TOPIC"); + AMQTopic invalid_topic = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_CLASS, "test.Return.Unroutable.Mandatory.Message.TOPIC.invalid"); + MessageConsumer consumer = consumerSession.createConsumer(valid_topic); + + //force synch to ensure the consumer has resulted in a bound queue + //((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS); + // This is the default now + + Connection con2 = new AMQConnection(BROKER, "guest", "guest", "producer1", VIRTUALHOST); + + con2.setExceptionListener(this); + AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + // Need to start the "producer" connection in order to receive bounced messages + _logger.info("Starting producer connection"); + con2.start(); + + + MessageProducer nonMandatoryProducer = producerSession.createProducer(valid_topic, false, false); + MessageProducer mandatoryProducer = producerSession.createProducer(invalid_topic); + + // First test - should be routed + _logger.info("Sending non-mandatory message"); + TextMessage msg1 = producerSession.createTextMessage("msg1"); + nonMandatoryProducer.send(msg1); + + // Second test - should be bounced + _logger.info("Sending non-routable mandatory message"); + TextMessage msg2 = producerSession.createTextMessage("msg2"); + mandatoryProducer.send(msg2); + + + _logger.info("Starting consumer connection"); + con.start(); + TextMessage tm = (TextMessage) consumer.receive(1000L); + + assertTrue("No message routed to receiver", tm != null); + assertTrue("Wrong message routed to receiver: " + tm.getText(), "msg1".equals(tm.getText())); + + try + { + Thread.sleep(1000L); + } + catch (InterruptedException e) + { + ; + } + + assertEquals("Wrong number of messages bounced: ", 1, _bouncedMessageList.size()); + Message m = _bouncedMessageList.get(0); + assertTrue("Wrong message bounced: " + m.toString(), m.toString().contains("msg2")); + + + con.close(); + con2.close(); + } + + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(ReturnUnroutableMandatoryMessageTest.class); + } + + public void onException(JMSException jmsException) + { + + Exception linkedException = null; + try + { + linkedException = jmsException.getLinkedException(); + } catch (Exception e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + if (linkedException instanceof AMQNoRouteException) + { + AMQNoRouteException noRoute = (AMQNoRouteException) linkedException; + Message bounced = (Message) noRoute.getUndeliveredMessage(); + _bouncedMessageList.add(bounced); + _logger.info("Caught expected NoRouteException"); + } + else + { + _logger.warn("Caught exception on producer: ", jmsException); + } + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java index 706d99ffe2..13465741bd 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java @@ -1,54 +1,54 @@ -/*
- *
- * 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.framework;
-
-import org.apache.qpid.junit.extensions.util.ParsedProperties;
-
-/**
- * An AMQPPublisher represents the status of the publishing side of a test circuit that exposes AMQP specific features.
- * Its provides additional assertions not available through the plain JMS {@link Publisher} interface.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities
- * <tr><td> Provide assertion that the publishers received a no consumers error code on every message.
- * <tr><td> Provide assertion that the publishers received a no route error code on every message.
- * </table>
- */
-public interface AMQPPublisher extends Publisher
-{
- /**
- * Provides an assertion that the publisher got a no consumers exception on every message.
- *
- * @param testProps The test configuration properties.
- *
- * @return An assertion that the publisher got a no consumers exception on every message.
- */
- Assertion noConsumersAssertion(ParsedProperties testProps);
-
- /**
- * Provides an assertion that the publisher got a no rout exception on every message.
- *
- * @param testProps The test configuration properties.
- *
- * @return An assertion that the publisher got a no rout exception on every message.
- */
- Assertion noRouteAssertion(ParsedProperties testProps);
-}
+/* + * + * 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.framework; + +import org.apache.qpid.junit.extensions.util.ParsedProperties; + +/** + * An AMQPPublisher represents the status of the publishing side of a test circuit that exposes AMQP specific features. + * Its provides additional assertions not available through the plain JMS {@link Publisher} interface. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities + * <tr><td> Provide assertion that the publishers received a no consumers error code on every message. + * <tr><td> Provide assertion that the publishers received a no route error code on every message. + * </table> + */ +public interface AMQPPublisher extends Publisher +{ + /** + * Provides an assertion that the publisher got a no consumers exception on every message. + * + * @param testProps The test configuration properties. + * + * @return An assertion that the publisher got a no consumers exception on every message. + */ + Assertion noConsumersAssertion(ParsedProperties testProps); + + /** + * Provides an assertion that the publisher got a no rout exception on every message. + * + * @param testProps The test configuration properties. + * + * @return An assertion that the publisher got a no rout exception on every message. + */ + Assertion noRouteAssertion(ParsedProperties testProps); +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/BrokerLifecycleAware.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/BrokerLifecycleAware.java index e8b7da2537..41614f92fc 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/BrokerLifecycleAware.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/BrokerLifecycleAware.java @@ -1,70 +1,70 @@ -/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.test.framework;
-
-/**
- * BrokerLifecycleAware is an awareness interface implemented by test cases that can run control the life-cycle of
- * the brokers on which they run. Its purpose is to expose additional instrumentation of brokers during testing, that
- * enables tests to use an automated failure mechanism to simulate broker failures, and to re-start failed brokers.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Indicate whether or not a test case is using an in-vm broker.
- * <tr><td> Track which in-vm broker is currently in use.
- * <tr><td> Accept setting of a failure mechanism. <td> {@link CauseFailure}.
- * </table>
- *
- * @todo Need to think about how to present the brokers through this interface. Thinking numbering the available
- * brokers from 1 will do. Then can kill 1 and assume failing onto 2. Restart 1 and kill 2 and fail back onto
- * 1 again?
- */
-public interface BrokerLifecycleAware
-{
- public void setInVmBrokers();
-
- /**
- * Indicates whether or not a test case is using in-vm brokers.
- *
- * @return <tt>true</tt> if the test is using in-vm brokers, <tt>false</tt> otherwise.
- */
- public boolean usingInVmBroker();
-
- /**
- * Sets the currently live in-vm broker.
- *
- * @param i The currently live in-vm broker.
- */
- public void setLiveBroker(int i);
-
- /**
- * Reports the currently live in-vm broker.
- *
- * @return The currently live in-vm broker.
- */
- public int getLiveBroker();
-
- /**
- * Accepts a failure mechanism.
- *
- * @param failureMechanism The failure mechanism.
- */
- public void setFailureMechanism(CauseFailure failureMechanism);
-}
+/* + * + * 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.framework; + +/** + * BrokerLifecycleAware is an awareness interface implemented by test cases that can run control the life-cycle of + * the brokers on which they run. Its purpose is to expose additional instrumentation of brokers during testing, that + * enables tests to use an automated failure mechanism to simulate broker failures, and to re-start failed brokers. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Indicate whether or not a test case is using an in-vm broker. + * <tr><td> Track which in-vm broker is currently in use. + * <tr><td> Accept setting of a failure mechanism. <td> {@link CauseFailure}. + * </table> + * + * @todo Need to think about how to present the brokers through this interface. Thinking numbering the available + * brokers from 1 will do. Then can kill 1 and assume failing onto 2. Restart 1 and kill 2 and fail back onto + * 1 again? + */ +public interface BrokerLifecycleAware +{ + public void setInVmBrokers(); + + /** + * Indicates whether or not a test case is using in-vm brokers. + * + * @return <tt>true</tt> if the test is using in-vm brokers, <tt>false</tt> otherwise. + */ + public boolean usingInVmBroker(); + + /** + * Sets the currently live in-vm broker. + * + * @param i The currently live in-vm broker. + */ + public void setLiveBroker(int i); + + /** + * Reports the currently live in-vm broker. + * + * @return The currently live in-vm broker. + */ + public int getLiveBroker(); + + /** + * Accepts a failure mechanism. + * + * @param failureMechanism The failure mechanism. + */ + public void setFailureMechanism(CauseFailure failureMechanism); +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailure.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailure.java index 8a5a9560a0..9bdd5a72c5 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailure.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailure.java @@ -1,42 +1,42 @@ -/*
- *
- * 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.framework;
-
-/**
- * CauseFailure provides a method to cause a failure in a messaging broker, usually used in conjunction with fail-over
- * or other failure mode testing. In some cases failures may be automated, for example by shutting down an in-vm broker,
- * or by sending a special control signal to a broker over a network connection. In other cases, it may be preferable
- * to ask a user interactively to cause a failure scenario, in which case an implementation may display a prompt or
- * dialog box asking for notification once the failure has been caused. The purpose of this interface is to abstract
- * the exact cause and nature of a failure out of failure test cases.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities
- * <tr><td> Cause messaging broker failure.
- * </table>
- */
-public interface CauseFailure
-{
- /**
- * Causes the active message broker to fail.
- */
- void causeFailure();
-}
+/* + * + * 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.framework; + +/** + * CauseFailure provides a method to cause a failure in a messaging broker, usually used in conjunction with fail-over + * or other failure mode testing. In some cases failures may be automated, for example by shutting down an in-vm broker, + * or by sending a special control signal to a broker over a network connection. In other cases, it may be preferable + * to ask a user interactively to cause a failure scenario, in which case an implementation may display a prompt or + * dialog box asking for notification once the failure has been caused. The purpose of this interface is to abstract + * the exact cause and nature of a failure out of failure test cases. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities + * <tr><td> Cause messaging broker failure. + * </table> + */ +public interface CauseFailure +{ + /** + * Causes the active message broker to fail. + */ + void causeFailure(); +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailureUserPrompt.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailureUserPrompt.java index 6b96ade674..889df4ad07 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailureUserPrompt.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailureUserPrompt.java @@ -1,65 +1,65 @@ -/*
- *
- * 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.framework;
-
-import org.apache.qpid.test.framework.CauseFailure;
-
-import java.io.IOException;
-
-/**
- * Causes a message broker failure by interactively prompting the user to cause it.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Cause messaging broker failure.
- * </table>
- */
-public class CauseFailureUserPrompt implements CauseFailure
-{
- /**
- * Causes the active message broker to fail.
- */
- public void causeFailure()
- {
- waitForUser("Cause a broker failure now, then press Return.");
- }
-
- /**
- * Outputs a prompt to the console and waits for the user to press return.
- *
- * @param prompt The prompt to display on the console.
- */
- private void waitForUser(String prompt)
- {
- System.out.println(prompt);
-
- try
- {
- System.in.read();
- }
- catch (IOException e)
- {
- // Ignored.
- }
-
- System.out.println("Continuing.");
- }
-}
+/* + * + * 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.framework; + +import org.apache.qpid.test.framework.CauseFailure; + +import java.io.IOException; + +/** + * Causes a message broker failure by interactively prompting the user to cause it. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Cause messaging broker failure. + * </table> + */ +public class CauseFailureUserPrompt implements CauseFailure +{ + /** + * Causes the active message broker to fail. + */ + public void causeFailure() + { + waitForUser("Cause a broker failure now, then press Return."); + } + + /** + * Outputs a prompt to the console and waits for the user to press return. + * + * @param prompt The prompt to display on the console. + */ + private void waitForUser(String prompt) + { + System.out.println(prompt); + + try + { + System.in.read(); + } + catch (IOException e) + { + // Ignored. + } + + System.out.println("Continuing."); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkTestContext.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkTestContext.java index e7268db8eb..9a4668e86f 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkTestContext.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkTestContext.java @@ -1,48 +1,48 @@ -/*
- *
- * 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.framework;
-
-/**
- * A FrameworkTestContext provides context information to test code about the current test case being run; its name, its
- * parameters.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Provide the name of the current test case.
- * <tr><td> Provide the test parameters.
- * </table>
- */
-public interface FrameworkTestContext
-{
- /**
- * Reports the current test case name.
- *
- * @return The current test case name.
- */
- TestCaseVector getTestCaseVector();
-
- /**
- * Reports the current test case parameters.
- *
- * @return The current test case parameters.
- */
- MessagingTestConfigProperties getTestParameters();
-}
+/* + * + * 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.framework; + +/** + * A FrameworkTestContext provides context information to test code about the current test case being run; its name, its + * parameters. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Provide the name of the current test case. + * <tr><td> Provide the test parameters. + * </table> + */ +public interface FrameworkTestContext +{ + /** + * Reports the current test case name. + * + * @return The current test case name. + */ + TestCaseVector getTestCaseVector(); + + /** + * Reports the current test case parameters. + * + * @return The current test case parameters. + */ + MessagingTestConfigProperties getTestParameters(); +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java index d1fcad9cc0..7fbef06265 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java @@ -1,168 +1,168 @@ -/*
- *
- * 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.framework;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.client.AMQSession;
-import org.apache.qpid.test.framework.localcircuit.LocalAMQPPublisherImpl;
-import org.apache.qpid.test.framework.localcircuit.LocalPublisherImpl;
-
-import org.apache.qpid.junit.extensions.util.ParsedProperties;
-
-import javax.jms.*;
-
-/**
- * LocalAMQPCircuitFactory is a test sequencer that creates test circuits with publishing and receiving ends rooted
- * on the same JVM, allowing AMQP/Qpid specific options to be applied to the circuit.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Provide a standard test procedure over a test circuit.
- * <tr><td> Construct test circuits appropriate to a tests context.
- * <tr><td> Construct test circuits the support AMQP specific options.
- * </table>
- */
-public class LocalAMQPCircuitFactory extends LocalCircuitFactory
-{
- /** Used for debugging. */
- private static final Logger log = Logger.getLogger(LocalAMQPCircuitFactory.class);
-
- /**
- * Builds a circuit end suitable for the publishing side of a test circuit, from standard test parameters.
- *
- * @param connection The connection to build the circuit end on.
- * @param testProps The test parameters to configure the circuit end construction.
- * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique.
- *
- * @return A circuit end suitable for the publishing side of a test circuit.
- *
- * @throws javax.jms.JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation.
- */
- public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId)
- throws JMSException
- {
- log.debug(
- "public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = "
- + uniqueId + "): called");
-
- // Cast the test properties into a typed interface for convenience.
- MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps);
-
- Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode());
-
- Destination destination =
- props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId)
- : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId);
-
- MessageProducer producer =
- props.getPublisherProducerBind()
- ? ((props.getImmediate() | props.getMandatory())
- ? ((AMQSession) session).createProducer(destination, props.getMandatory(), props.getImmediate())
- : session.createProducer(destination)) : null;
-
- MessageConsumer consumer =
- props.getPublisherConsumerBind()
- ? session.createConsumer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null;
-
- MessageMonitor messageMonitor = new MessageMonitor();
-
- if (consumer != null)
- {
- consumer.setMessageListener(messageMonitor);
- }
-
- ExceptionMonitor exceptionMonitor = new ExceptionMonitor();
- connection.setExceptionListener(exceptionMonitor);
-
- if (!props.getPublisherConsumerActive() && (consumer != null))
- {
- consumer.close();
- }
-
- return new CircuitEndBase(producer, consumer, session, messageMonitor, exceptionMonitor);
- }
-
- /**
- * Builds a circuit end suitable for the receiving side of a test circuit, from standard test parameters.
- *
- * @param connection The connection to build the circuit end on.
- * @param testProps The test parameters to configure the circuit end construction.
- * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique.
- *
- * @return A circuit end suitable for the receiving side of a test circuit.
- *
- * @throws JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation.
- */
- public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId)
- throws JMSException
- {
- log.debug(
- "public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = "
- + uniqueId + "): called");
-
- // Cast the test properties into a typed interface for convenience.
- MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps);
-
- Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode());
-
- MessageProducer producer =
- props.getReceiverProducerBind()
- ? session.createProducer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null;
-
- Destination destination =
- props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId)
- : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId);
-
- MessageConsumer consumer =
- props.getReceiverConsumerBind()
- ? ((props.getDurableSubscription() && props.getPubsub())
- ? session.createDurableSubscriber((Topic) destination, "testsub") : session.createConsumer(destination))
- : null;
-
- MessageMonitor messageMonitor = new MessageMonitor();
-
- if (consumer != null)
- {
- consumer.setMessageListener(messageMonitor);
- }
-
- if (!props.getReceiverConsumerActive() && (consumer != null))
- {
- consumer.close();
- }
-
- return new CircuitEndBase(producer, consumer, session, messageMonitor, null);
- }
-
- /**
- * Creates a local {@link Publisher} from a {@link CircuitEnd}. The publisher implementation provides AMQP
- * specific assertion methods, for testing beyond JMS.
- *
- * @param publisherEnd The publishing circuit end.
- *
- * @return A {@link Receiver}.
- */
- protected LocalPublisherImpl createPublisherFromCircuitEnd(CircuitEndBase publisherEnd)
- {
- return new LocalAMQPPublisherImpl(publisherEnd);
- }
-}
+/* + * + * 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.framework; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.test.framework.localcircuit.LocalAMQPPublisherImpl; +import org.apache.qpid.test.framework.localcircuit.LocalPublisherImpl; + +import org.apache.qpid.junit.extensions.util.ParsedProperties; + +import javax.jms.*; + +/** + * LocalAMQPCircuitFactory is a test sequencer that creates test circuits with publishing and receiving ends rooted + * on the same JVM, allowing AMQP/Qpid specific options to be applied to the circuit. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Provide a standard test procedure over a test circuit. + * <tr><td> Construct test circuits appropriate to a tests context. + * <tr><td> Construct test circuits the support AMQP specific options. + * </table> + */ +public class LocalAMQPCircuitFactory extends LocalCircuitFactory +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(LocalAMQPCircuitFactory.class); + + /** + * Builds a circuit end suitable for the publishing side of a test circuit, from standard test parameters. + * + * @param connection The connection to build the circuit end on. + * @param testProps The test parameters to configure the circuit end construction. + * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique. + * + * @return A circuit end suitable for the publishing side of a test circuit. + * + * @throws javax.jms.JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation. + */ + public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId) + throws JMSException + { + log.debug( + "public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = " + + uniqueId + "): called"); + + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); + + Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode()); + + Destination destination = + props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId) + : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId); + + MessageProducer producer = + props.getPublisherProducerBind() + ? ((props.getImmediate() | props.getMandatory()) + ? ((AMQSession) session).createProducer(destination, props.getMandatory(), props.getImmediate()) + : session.createProducer(destination)) : null; + + MessageConsumer consumer = + props.getPublisherConsumerBind() + ? session.createConsumer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null; + + MessageMonitor messageMonitor = new MessageMonitor(); + + if (consumer != null) + { + consumer.setMessageListener(messageMonitor); + } + + ExceptionMonitor exceptionMonitor = new ExceptionMonitor(); + connection.setExceptionListener(exceptionMonitor); + + if (!props.getPublisherConsumerActive() && (consumer != null)) + { + consumer.close(); + } + + return new CircuitEndBase(producer, consumer, session, messageMonitor, exceptionMonitor); + } + + /** + * Builds a circuit end suitable for the receiving side of a test circuit, from standard test parameters. + * + * @param connection The connection to build the circuit end on. + * @param testProps The test parameters to configure the circuit end construction. + * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique. + * + * @return A circuit end suitable for the receiving side of a test circuit. + * + * @throws JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation. + */ + public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId) + throws JMSException + { + log.debug( + "public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = " + + uniqueId + "): called"); + + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); + + Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode()); + + MessageProducer producer = + props.getReceiverProducerBind() + ? session.createProducer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null; + + Destination destination = + props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId) + : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId); + + MessageConsumer consumer = + props.getReceiverConsumerBind() + ? ((props.getDurableSubscription() && props.getPubsub()) + ? session.createDurableSubscriber((Topic) destination, "testsub") : session.createConsumer(destination)) + : null; + + MessageMonitor messageMonitor = new MessageMonitor(); + + if (consumer != null) + { + consumer.setMessageListener(messageMonitor); + } + + if (!props.getReceiverConsumerActive() && (consumer != null)) + { + consumer.close(); + } + + return new CircuitEndBase(producer, consumer, session, messageMonitor, null); + } + + /** + * Creates a local {@link Publisher} from a {@link CircuitEnd}. The publisher implementation provides AMQP + * specific assertion methods, for testing beyond JMS. + * + * @param publisherEnd The publishing circuit end. + * + * @return A {@link Receiver}. + */ + protected LocalPublisherImpl createPublisherFromCircuitEnd(CircuitEndBase publisherEnd) + { + return new LocalAMQPPublisherImpl(publisherEnd); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java index 38a924a4ee..68aad6ee47 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java @@ -1,316 +1,316 @@ -/*
- *
- * 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.framework;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.test.framework.localcircuit.LocalCircuitImpl;
-import org.apache.qpid.test.framework.localcircuit.LocalPublisherImpl;
-import org.apache.qpid.test.framework.localcircuit.LocalReceiverImpl;
-import org.apache.qpid.test.framework.sequencers.CircuitFactory;
-import org.apache.qpid.util.ConversationFactory;
-
-import org.apache.qpid.junit.extensions.util.ParsedProperties;
-
-import javax.jms.*;
-
-import java.util.List;
-import java.util.Properties;
-import java.util.concurrent.atomic.AtomicLong;
-
-/**
- * LocalCircuitFactory is a circuit factory that creates test circuits with publishing and receiving ends rooted
- * on the same JVM. The ends of the circuit are presented as {@link Publisher} and {@link Receiver} interfaces, which
- * in turn provide methods to apply assertions to the circuit. The creation of the circuit ends, and the presentation
- * of the ends as publisher/receiver interfaces, are designed to be overriden, so that circuits and assertions that
- * use messaging features not available in JMS can be written. This provides an extension point for writing tests
- * against proprietary features of JMS implementations.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Provide a standard test procedure over a test circuit.
- * <tr><td> Construct test circuits appropriate to a tests context.
- * </table>
- */
-public class LocalCircuitFactory implements CircuitFactory
-{
- /** Used for debugging. */
- private static final Logger log = Logger.getLogger(LocalCircuitFactory.class);
-
- /** Used to create unique destination names for each test. */
- protected static AtomicLong uniqueDestsId = new AtomicLong();
-
- /**
- * Holds a test coordinating conversation with the test clients. This should consist of assigning the test roles,
- * begining the test and gathering the test reports from the participants.
- *
- * @param testCircuit The test circuit.
- * @param assertions The list of assertions to apply to the test circuit.
- * @param testProperties The test case definition.
- */
- public void sequenceTest(Circuit testCircuit, List<Assertion> assertions, Properties testProperties)
- {
- FrameworkBaseCase.assertNoFailures(testCircuit.test(1, assertions));
- }
-
- /**
- * Creates a test circuit for the test, configered by the test parameters specified.
- *
- * @param testProperties The test parameters.
- *
- * @return A test circuit.
- */
- public Circuit createCircuit(ParsedProperties testProperties)
- {
- Circuit result;
-
- // Cast the test properties into a typed interface for convenience.
- MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProperties);
-
- // Create a standard publisher/receivers test client pair on a shared connection, individual sessions.
- try
- {
- // Get a unique offset to append to destination names to make them unique to the connection.
- long uniqueId = uniqueDestsId.incrementAndGet();
-
- // Set up the connection.
- Connection connection = TestUtils.createConnection(testProperties);
-
- // Add the connection exception listener to assert on exception conditions with.
- // ExceptionMonitor exceptionMonitor = new ExceptionMonitor();
- // connection.setExceptionListener(exceptionMonitor);
-
- // Set up the publisher.
- CircuitEndBase publisherEnd = createPublisherCircuitEnd(connection, props, uniqueId);
-
- // Set up the receiver.
- CircuitEndBase receiverEnd = createReceiverCircuitEnd(connection, props, uniqueId);
-
- // Start listening for incoming messages.
- connection.start();
-
- // Package everything up.
- LocalPublisherImpl publisher = createPublisherFromCircuitEnd(publisherEnd);
- LocalReceiverImpl receiver = createReceiverFromCircuitEnd(receiverEnd);
-
- result = new LocalCircuitImpl(testProperties, publisher, receiver, connection, publisher.getExceptionMonitor());
- }
- catch (JMSException e)
- {
- throw new RuntimeException("Could not create publisher/receivers pair due to a JMSException.", e);
- }
-
- return result;
- }
-
- /**
- * Creates a local {@link Receiver} from a {@link CircuitEnd}. Sub-classes may override this to provide more
- * specialized receivers if necessary.
- *
- * @param receiverEnd The receiving circuit end.
- *
- * @return A {@link Receiver}.
- */
- protected LocalReceiverImpl createReceiverFromCircuitEnd(CircuitEndBase receiverEnd)
- {
- return new LocalReceiverImpl(receiverEnd);
- }
-
- /**
- * Creates a local {@link Publisher} from a {@link CircuitEnd}. Sub-classes may override this to provide more
- * specialized receivers if necessary.
- *
- * @param publisherEnd The publishing circuit end.
- *
- * @return A {@link Receiver}.
- */
- protected LocalPublisherImpl createPublisherFromCircuitEnd(CircuitEndBase publisherEnd)
- {
- return new LocalPublisherImpl(publisherEnd);
- }
-
- /**
- * Builds a circuit end suitable for the publishing side of a test circuit, from standard test parameters.
- *
- * @param connection The connection to build the circuit end on.
- * @param testProps The test parameters to configure the circuit end construction.
- * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique.
- *
- * @return A circuit end suitable for the publishing side of a test circuit.
- *
- * @throws JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation.
- */
- public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId)
- throws JMSException
- {
- log.debug(
- "public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = "
- + uniqueId + "): called");
-
- // Cast the test properties into a typed interface for convenience.
- MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps);
-
- // Check that the test properties do not contain AMQP/Qpid specific settings, and fail if they do.
- if (props.getImmediate() || props.getMandatory())
- {
- throw new RuntimeException(
- "Cannot create a pure JMS circuit as the test properties require AMQP specific options.");
- }
-
- Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode());
-
- Destination destination =
- props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId)
- : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId);
-
- MessageProducer producer = props.getPublisherProducerBind() ? session.createProducer(destination) : null;
-
- MessageConsumer consumer =
- props.getPublisherConsumerBind()
- ? session.createConsumer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null;
-
- MessageMonitor messageMonitor = new MessageMonitor();
-
- if (consumer != null)
- {
- consumer.setMessageListener(messageMonitor);
- }
-
- ExceptionMonitor exceptionMonitor = new ExceptionMonitor();
- connection.setExceptionListener(exceptionMonitor);
-
- if (!props.getPublisherConsumerActive() && (consumer != null))
- {
- consumer.close();
- }
-
- return new CircuitEndBase(producer, consumer, session, messageMonitor, exceptionMonitor);
- }
-
- /**
- * Builds a circuit end suitable for the receiving side of a test circuit, from standard test parameters.
- *
- * @param connection The connection to build the circuit end on.
- * @param testProps The test parameters to configure the circuit end construction.
- * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique.
- *
- * @return A circuit end suitable for the receiving side of a test circuit.
- *
- * @throws JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation.
- */
- public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId)
- throws JMSException
- {
- log.debug(
- "public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = "
- + uniqueId + "): called");
-
- // Cast the test properties into a typed interface for convenience.
- MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps);
-
- // Check that the test properties do not contain AMQP/Qpid specific settings, and fail if they do.
- if (props.getImmediate() || props.getMandatory())
- {
- throw new RuntimeException(
- "Cannot create a pure JMS circuit as the test properties require AMQP specific options.");
- }
-
- Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode());
-
- MessageProducer producer =
- props.getReceiverProducerBind()
- ? session.createProducer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null;
-
- Destination destination =
- props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId)
- : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId);
-
- MessageConsumer consumer =
- props.getReceiverConsumerBind()
- ? ((props.getDurableSubscription() && props.getPubsub())
- ? session.createDurableSubscriber((Topic) destination, "testsub") : session.createConsumer(destination))
- : null;
-
- MessageMonitor messageMonitor = new MessageMonitor();
-
- if (consumer != null)
- {
- consumer.setMessageListener(messageMonitor);
- }
-
- if (!props.getReceiverConsumerActive() && (consumer != null))
- {
- consumer.close();
- }
-
- return new CircuitEndBase(producer, consumer, session, messageMonitor, null);
- }
-
- /**
- * Sets the sender test client to coordinate the test with.
- *
- * @param sender The contact details of the sending client in the test.
- */
- public void setSender(TestClientDetails sender)
- {
- throw new RuntimeException("Not implemented.");
- }
-
- /**
- * Sets the receiving test client to coordinate the test with.
- *
- * @param receiver The contact details of the sending client in the test.
- */
- public void setReceiver(TestClientDetails receiver)
- {
- throw new RuntimeException("Not implemented.");
- }
-
- /**
- * Supplies the sending test client.
- *
- * @return The sending test client.
- */
- public TestClientDetails getSender()
- {
- throw new RuntimeException("Not implemented.");
- }
-
- /**
- * Supplies the receiving test client.
- *
- * @return The receiving test client.
- */
- public List<TestClientDetails> getReceivers()
- {
- throw new RuntimeException("Not implemented.");
- }
-
- /**
- * Accepts the conversation factory over which to hold the test coordinating conversation.
- *
- * @param conversationFactory The conversation factory to coordinate the test over.
- */
- public void setConversationFactory(ConversationFactory conversationFactory)
- {
- throw new RuntimeException("Not implemented.");
- }
-}
+/* + * + * 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.framework; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.localcircuit.LocalCircuitImpl; +import org.apache.qpid.test.framework.localcircuit.LocalPublisherImpl; +import org.apache.qpid.test.framework.localcircuit.LocalReceiverImpl; +import org.apache.qpid.test.framework.sequencers.CircuitFactory; +import org.apache.qpid.util.ConversationFactory; + +import org.apache.qpid.junit.extensions.util.ParsedProperties; + +import javax.jms.*; + +import java.util.List; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicLong; + +/** + * LocalCircuitFactory is a circuit factory that creates test circuits with publishing and receiving ends rooted + * on the same JVM. The ends of the circuit are presented as {@link Publisher} and {@link Receiver} interfaces, which + * in turn provide methods to apply assertions to the circuit. The creation of the circuit ends, and the presentation + * of the ends as publisher/receiver interfaces, are designed to be overriden, so that circuits and assertions that + * use messaging features not available in JMS can be written. This provides an extension point for writing tests + * against proprietary features of JMS implementations. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Provide a standard test procedure over a test circuit. + * <tr><td> Construct test circuits appropriate to a tests context. + * </table> + */ +public class LocalCircuitFactory implements CircuitFactory +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(LocalCircuitFactory.class); + + /** Used to create unique destination names for each test. */ + protected static AtomicLong uniqueDestsId = new AtomicLong(); + + /** + * Holds a test coordinating conversation with the test clients. This should consist of assigning the test roles, + * begining the test and gathering the test reports from the participants. + * + * @param testCircuit The test circuit. + * @param assertions The list of assertions to apply to the test circuit. + * @param testProperties The test case definition. + */ + public void sequenceTest(Circuit testCircuit, List<Assertion> assertions, Properties testProperties) + { + FrameworkBaseCase.assertNoFailures(testCircuit.test(1, assertions)); + } + + /** + * Creates a test circuit for the test, configered by the test parameters specified. + * + * @param testProperties The test parameters. + * + * @return A test circuit. + */ + public Circuit createCircuit(ParsedProperties testProperties) + { + Circuit result; + + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProperties); + + // Create a standard publisher/receivers test client pair on a shared connection, individual sessions. + try + { + // Get a unique offset to append to destination names to make them unique to the connection. + long uniqueId = uniqueDestsId.incrementAndGet(); + + // Set up the connection. + Connection connection = TestUtils.createConnection(testProperties); + + // Add the connection exception listener to assert on exception conditions with. + // ExceptionMonitor exceptionMonitor = new ExceptionMonitor(); + // connection.setExceptionListener(exceptionMonitor); + + // Set up the publisher. + CircuitEndBase publisherEnd = createPublisherCircuitEnd(connection, props, uniqueId); + + // Set up the receiver. + CircuitEndBase receiverEnd = createReceiverCircuitEnd(connection, props, uniqueId); + + // Start listening for incoming messages. + connection.start(); + + // Package everything up. + LocalPublisherImpl publisher = createPublisherFromCircuitEnd(publisherEnd); + LocalReceiverImpl receiver = createReceiverFromCircuitEnd(receiverEnd); + + result = new LocalCircuitImpl(testProperties, publisher, receiver, connection, publisher.getExceptionMonitor()); + } + catch (JMSException e) + { + throw new RuntimeException("Could not create publisher/receivers pair due to a JMSException.", e); + } + + return result; + } + + /** + * Creates a local {@link Receiver} from a {@link CircuitEnd}. Sub-classes may override this to provide more + * specialized receivers if necessary. + * + * @param receiverEnd The receiving circuit end. + * + * @return A {@link Receiver}. + */ + protected LocalReceiverImpl createReceiverFromCircuitEnd(CircuitEndBase receiverEnd) + { + return new LocalReceiverImpl(receiverEnd); + } + + /** + * Creates a local {@link Publisher} from a {@link CircuitEnd}. Sub-classes may override this to provide more + * specialized receivers if necessary. + * + * @param publisherEnd The publishing circuit end. + * + * @return A {@link Receiver}. + */ + protected LocalPublisherImpl createPublisherFromCircuitEnd(CircuitEndBase publisherEnd) + { + return new LocalPublisherImpl(publisherEnd); + } + + /** + * Builds a circuit end suitable for the publishing side of a test circuit, from standard test parameters. + * + * @param connection The connection to build the circuit end on. + * @param testProps The test parameters to configure the circuit end construction. + * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique. + * + * @return A circuit end suitable for the publishing side of a test circuit. + * + * @throws JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation. + */ + public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId) + throws JMSException + { + log.debug( + "public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = " + + uniqueId + "): called"); + + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); + + // Check that the test properties do not contain AMQP/Qpid specific settings, and fail if they do. + if (props.getImmediate() || props.getMandatory()) + { + throw new RuntimeException( + "Cannot create a pure JMS circuit as the test properties require AMQP specific options."); + } + + Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode()); + + Destination destination = + props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId) + : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId); + + MessageProducer producer = props.getPublisherProducerBind() ? session.createProducer(destination) : null; + + MessageConsumer consumer = + props.getPublisherConsumerBind() + ? session.createConsumer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null; + + MessageMonitor messageMonitor = new MessageMonitor(); + + if (consumer != null) + { + consumer.setMessageListener(messageMonitor); + } + + ExceptionMonitor exceptionMonitor = new ExceptionMonitor(); + connection.setExceptionListener(exceptionMonitor); + + if (!props.getPublisherConsumerActive() && (consumer != null)) + { + consumer.close(); + } + + return new CircuitEndBase(producer, consumer, session, messageMonitor, exceptionMonitor); + } + + /** + * Builds a circuit end suitable for the receiving side of a test circuit, from standard test parameters. + * + * @param connection The connection to build the circuit end on. + * @param testProps The test parameters to configure the circuit end construction. + * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique. + * + * @return A circuit end suitable for the receiving side of a test circuit. + * + * @throws JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation. + */ + public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId) + throws JMSException + { + log.debug( + "public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = " + + uniqueId + "): called"); + + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); + + // Check that the test properties do not contain AMQP/Qpid specific settings, and fail if they do. + if (props.getImmediate() || props.getMandatory()) + { + throw new RuntimeException( + "Cannot create a pure JMS circuit as the test properties require AMQP specific options."); + } + + Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode()); + + MessageProducer producer = + props.getReceiverProducerBind() + ? session.createProducer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null; + + Destination destination = + props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId) + : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId); + + MessageConsumer consumer = + props.getReceiverConsumerBind() + ? ((props.getDurableSubscription() && props.getPubsub()) + ? session.createDurableSubscriber((Topic) destination, "testsub") : session.createConsumer(destination)) + : null; + + MessageMonitor messageMonitor = new MessageMonitor(); + + if (consumer != null) + { + consumer.setMessageListener(messageMonitor); + } + + if (!props.getReceiverConsumerActive() && (consumer != null)) + { + consumer.close(); + } + + return new CircuitEndBase(producer, consumer, session, messageMonitor, null); + } + + /** + * Sets the sender test client to coordinate the test with. + * + * @param sender The contact details of the sending client in the test. + */ + public void setSender(TestClientDetails sender) + { + throw new RuntimeException("Not implemented."); + } + + /** + * Sets the receiving test client to coordinate the test with. + * + * @param receiver The contact details of the sending client in the test. + */ + public void setReceiver(TestClientDetails receiver) + { + throw new RuntimeException("Not implemented."); + } + + /** + * Supplies the sending test client. + * + * @return The sending test client. + */ + public TestClientDetails getSender() + { + throw new RuntimeException("Not implemented."); + } + + /** + * Supplies the receiving test client. + * + * @return The receiving test client. + */ + public List<TestClientDetails> getReceivers() + { + throw new RuntimeException("Not implemented."); + } + + /** + * Accepts the conversation factory over which to hold the test coordinating conversation. + * + * @param conversationFactory The conversation factory to coordinate the test over. + */ + public void setConversationFactory(ConversationFactory conversationFactory) + { + throw new RuntimeException("Not implemented."); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/MessageIdentityVector.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/MessageIdentityVector.java index b672b9c3ce..397c4e9fbd 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/MessageIdentityVector.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/MessageIdentityVector.java @@ -1,167 +1,167 @@ -/*
- *
- * 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.framework;
-
-/**
- * MessageIdentityVector provides a message identification scheme, that matches individual messages with test cases.
- * Test messages are being sent by a number of test clients, sending messages over a set of routes, and being received
- * by another set of test clients. Each test is itself, being run within a test cycle, of which there could be many. It
- * is the job of the test coordinator to request and receive reports from the available test clients, on what has been
- * sent, what has been received, and what errors may have occurred, and to reconcile this information against the
- * assertions being applied by the test case. In order to be able to figure out which messages belong to which test,
- * there needs to be an identification scheme, that the coordinator can use to correlate messages in senders and
- * receiver reports. Every message sent in a test can be associated with this information.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Identify a test case, a handling client id, a circuit end within the client, and a test cycle number.
- * </table>
- */
-public class MessageIdentityVector
-{
- /** Holds the test case vector component of the message identity vector. */
- private TestCaseVector testCaseVector;
-
- /** The unique client id. */
- private String clientId;
-
- /** The unique circuit end number within the client id. */
- private int circuitEndId;
-
- /**
- * Creates a new identity vector for test messages.
- *
- * @param testCase The name of the test case generating the messages.
- * @param clientId The unique id of the client implementing a circuit end that is handling the messages.
- * @param circuitEndId The unique id number of the circuit end within the client.
- * @param testCycleNumber The cycle iteration number of the test case.
- */
- public MessageIdentityVector(String testCase, String clientId, int circuitEndId, int testCycleNumber)
- {
- this.testCaseVector = new TestCaseVector(testCase, testCycleNumber);
- this.clientId = clientId;
- this.circuitEndId = circuitEndId;
- }
-
- /**
- * Reports the test case vector component of the message identity vector.
- *
- * @return The test case vector component of the message identity vector.
- */
- public TestCaseVector getTestCaseVector()
- {
- return testCaseVector;
- }
-
- /**
- * Reports the name of the test case.
- *
- * @return The name of the test case.
- */
- public String getTestCase()
- {
- return testCaseVector.getTestCase();
- }
-
- /**
- * Reports the test iteration cycle number within the test case.
- *
- * @return The test iteration cycle number within the test case.
- */
- public int getTestCycleNumber()
- {
- return testCaseVector.getTestCycleNumber();
- }
-
- /**
- * Resports the client id.
- *
- * @return The client id.
- */
- public String getClientId()
- {
- return clientId;
- }
-
- /**
- * Reports the circuit end number within the test client.
- *
- * @return The circuit end number within the test client.
- */
- public int getCircuitEndId()
- {
- return circuitEndId;
- }
-
- /**
- * Compares this identity vector with another for equality. All fields must match.
- *
- * @param o The identity vector to compare with.
- *
- * @return <tt>true</tt> if the identity vector is identical to this one by all fields, <tt>false</tt> otherwise.
- */
- public boolean equals(Object o)
- {
- if (this == o)
- {
- return true;
- }
-
- if ((o == null) || (getClass() != o.getClass()))
- {
- return false;
- }
-
- MessageIdentityVector that = (MessageIdentityVector) o;
-
- if (circuitEndId != that.circuitEndId)
- {
- return false;
- }
-
- if ((clientId != null) ? (!clientId.equals(that.clientId)) : (that.clientId != null))
- {
- return false;
- }
-
- if ((testCaseVector != null) ? (!testCaseVector.equals(that.testCaseVector)) : (that.testCaseVector != null))
- {
- return false;
- }
-
- return true;
- }
-
- /**
- * Computes a hash code for this identity vector based on all fields.
- *
- * @return A hash code for this identity vector based on all fields.
- */
- public int hashCode()
- {
- int result;
- result = ((testCaseVector != null) ? testCaseVector.hashCode() : 0);
- result = (31 * result) + ((clientId != null) ? clientId.hashCode() : 0);
- result = (31 * result) + circuitEndId;
-
- return result;
- }
-}
+/* + * + * 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.framework; + +/** + * MessageIdentityVector provides a message identification scheme, that matches individual messages with test cases. + * Test messages are being sent by a number of test clients, sending messages over a set of routes, and being received + * by another set of test clients. Each test is itself, being run within a test cycle, of which there could be many. It + * is the job of the test coordinator to request and receive reports from the available test clients, on what has been + * sent, what has been received, and what errors may have occurred, and to reconcile this information against the + * assertions being applied by the test case. In order to be able to figure out which messages belong to which test, + * there needs to be an identification scheme, that the coordinator can use to correlate messages in senders and + * receiver reports. Every message sent in a test can be associated with this information. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Identify a test case, a handling client id, a circuit end within the client, and a test cycle number. + * </table> + */ +public class MessageIdentityVector +{ + /** Holds the test case vector component of the message identity vector. */ + private TestCaseVector testCaseVector; + + /** The unique client id. */ + private String clientId; + + /** The unique circuit end number within the client id. */ + private int circuitEndId; + + /** + * Creates a new identity vector for test messages. + * + * @param testCase The name of the test case generating the messages. + * @param clientId The unique id of the client implementing a circuit end that is handling the messages. + * @param circuitEndId The unique id number of the circuit end within the client. + * @param testCycleNumber The cycle iteration number of the test case. + */ + public MessageIdentityVector(String testCase, String clientId, int circuitEndId, int testCycleNumber) + { + this.testCaseVector = new TestCaseVector(testCase, testCycleNumber); + this.clientId = clientId; + this.circuitEndId = circuitEndId; + } + + /** + * Reports the test case vector component of the message identity vector. + * + * @return The test case vector component of the message identity vector. + */ + public TestCaseVector getTestCaseVector() + { + return testCaseVector; + } + + /** + * Reports the name of the test case. + * + * @return The name of the test case. + */ + public String getTestCase() + { + return testCaseVector.getTestCase(); + } + + /** + * Reports the test iteration cycle number within the test case. + * + * @return The test iteration cycle number within the test case. + */ + public int getTestCycleNumber() + { + return testCaseVector.getTestCycleNumber(); + } + + /** + * Resports the client id. + * + * @return The client id. + */ + public String getClientId() + { + return clientId; + } + + /** + * Reports the circuit end number within the test client. + * + * @return The circuit end number within the test client. + */ + public int getCircuitEndId() + { + return circuitEndId; + } + + /** + * Compares this identity vector with another for equality. All fields must match. + * + * @param o The identity vector to compare with. + * + * @return <tt>true</tt> if the identity vector is identical to this one by all fields, <tt>false</tt> otherwise. + */ + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + + if ((o == null) || (getClass() != o.getClass())) + { + return false; + } + + MessageIdentityVector that = (MessageIdentityVector) o; + + if (circuitEndId != that.circuitEndId) + { + return false; + } + + if ((clientId != null) ? (!clientId.equals(that.clientId)) : (that.clientId != null)) + { + return false; + } + + if ((testCaseVector != null) ? (!testCaseVector.equals(that.testCaseVector)) : (that.testCaseVector != null)) + { + return false; + } + + return true; + } + + /** + * Computes a hash code for this identity vector based on all fields. + * + * @return A hash code for this identity vector based on all fields. + */ + public int hashCode() + { + int result; + result = ((testCaseVector != null) ? testCaseVector.hashCode() : 0); + result = (31 * result) + ((clientId != null) ? clientId.hashCode() : 0); + result = (31 * result) + circuitEndId; + + return result; + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java index 63c7fd61c3..2a20be12d6 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java @@ -1,112 +1,112 @@ -/*
- *
- * 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.framework;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.junit.extensions.util.ParsedProperties;
-
-/**
- * NotApplicableAssertion is a messaging assertion that can be used when an assertion requested by a test-case is not
- * applicable to the testing scenario. For example an assertion may relate to AMQP functionality, but a test case may be
- * being run over a non-AMQP JMS implementation, in which case the request to create the assertion may return this
- * instead of the proper assertion. The test framework is configurable to quietly drop these assertions, log them
- * as warnings to the console, or raise them as test failures.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Quitely pass.
- * <tr><td> Log a warning.
- * <tr><td> Raise a test failure.
- * </table>
- */
-public class NotApplicableAssertion implements Assertion
-{
- /** Used for logging to the console. */
- private static final Logger console = Logger.getLogger("CONSOLE." + NotApplicableAssertion.class.getName());
-
- /** The possible behavioural modes of this assertion. */
- private enum Mode
- {
- /** Quietly ignore the assertion by passing. */
- Quiet,
-
- /** Ignore the assertion by passing but log a warning about it. */
- Warn,
-
- /** Fail the assertion. */
- Fail;
- }
-
- /** The behavioural mode of the assertion. */
- private Mode mode;
-
- /**
- * Creates an assertion that is driven by the value of the 'notApplicableAssertion' property of the test
- * configuration. Its value should match one of 'quiet', 'warn' or 'fail' and if it does not it is automatically
- * read as 'fail'.
- *
- * @param testProperties The test configuration properties.
- */
- public NotApplicableAssertion(ParsedProperties testProperties)
- {
- // Cast the test properties into a typed interface for convenience.
- MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProperties);
-
- String modeName = props.getNotApplicableAssertionMode();
-
- if ("quiet".equals(modeName))
- {
- mode = Mode.Quiet;
- }
- else if ("warn".equals(modeName))
- {
- mode = Mode.Warn;
- }
- else
- {
- mode = Mode.Fail;
- }
- }
-
- /**
- * Applies the assertion.
- *
- * @return <tt>true</tt> if the assertion passes, <tt>false</tt> if it fails.
- */
- public boolean apply()
- {
- switch (mode)
- {
- case Quiet:
- return true;
-
- case Warn:
- console.warn("Warning: Not applicable assertion being ignored.");
-
- return true;
-
- case Fail:
- default:
- return false;
- }
- }
-}
+/* + * + * 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.framework; + +import org.apache.log4j.Logger; + +import org.apache.qpid.junit.extensions.util.ParsedProperties; + +/** + * NotApplicableAssertion is a messaging assertion that can be used when an assertion requested by a test-case is not + * applicable to the testing scenario. For example an assertion may relate to AMQP functionality, but a test case may be + * being run over a non-AMQP JMS implementation, in which case the request to create the assertion may return this + * instead of the proper assertion. The test framework is configurable to quietly drop these assertions, log them + * as warnings to the console, or raise them as test failures. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Quitely pass. + * <tr><td> Log a warning. + * <tr><td> Raise a test failure. + * </table> + */ +public class NotApplicableAssertion implements Assertion +{ + /** Used for logging to the console. */ + private static final Logger console = Logger.getLogger("CONSOLE." + NotApplicableAssertion.class.getName()); + + /** The possible behavioural modes of this assertion. */ + private enum Mode + { + /** Quietly ignore the assertion by passing. */ + Quiet, + + /** Ignore the assertion by passing but log a warning about it. */ + Warn, + + /** Fail the assertion. */ + Fail; + } + + /** The behavioural mode of the assertion. */ + private Mode mode; + + /** + * Creates an assertion that is driven by the value of the 'notApplicableAssertion' property of the test + * configuration. Its value should match one of 'quiet', 'warn' or 'fail' and if it does not it is automatically + * read as 'fail'. + * + * @param testProperties The test configuration properties. + */ + public NotApplicableAssertion(ParsedProperties testProperties) + { + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProperties); + + String modeName = props.getNotApplicableAssertionMode(); + + if ("quiet".equals(modeName)) + { + mode = Mode.Quiet; + } + else if ("warn".equals(modeName)) + { + mode = Mode.Warn; + } + else + { + mode = Mode.Fail; + } + } + + /** + * Applies the assertion. + * + * @return <tt>true</tt> if the assertion passes, <tt>false</tt> if it fails. + */ + public boolean apply() + { + switch (mode) + { + case Quiet: + return true; + + case Warn: + console.warn("Warning: Not applicable assertion being ignored."); + + return true; + + case Fail: + default: + return false; + } + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/TestCaseVector.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/TestCaseVector.java index 0518a827ba..ad1e70f6f7 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/TestCaseVector.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/TestCaseVector.java @@ -1,88 +1,88 @@ -/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.test.framework;
-
-/**
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td>
- * </table>
- */
-public class TestCaseVector
-{
- /** The test case name. */
- private String testCase;
-
- /** The test cycle number within the test case. */
- private int testCycleNumber;
-
- public TestCaseVector(String testCase, int testCycleNumber)
- {
- this.testCase = testCase;
- this.testCycleNumber = testCycleNumber;
- }
-
- public String getTestCase()
- {
- return testCase;
- }
-
- public int getTestCycleNumber()
- {
- return testCycleNumber;
- }
-
- public boolean equals(Object o)
- {
- if (this == o)
- {
- return true;
- }
-
- if ((o == null) || (getClass() != o.getClass()))
- {
- return false;
- }
-
- TestCaseVector that = (TestCaseVector) o;
-
- if (testCycleNumber != that.testCycleNumber)
- {
- return false;
- }
-
- if ((testCase != null) ? (!testCase.equals(that.testCase)) : (that.testCase != null))
- {
- return false;
- }
-
- return true;
- }
-
- public int hashCode()
- {
- int result;
- result = ((testCase != null) ? testCase.hashCode() : 0);
- result = (31 * result) + testCycleNumber;
-
- return result;
- }
-}
+/* + * + * 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.framework; + +/** + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> + * </table> + */ +public class TestCaseVector +{ + /** The test case name. */ + private String testCase; + + /** The test cycle number within the test case. */ + private int testCycleNumber; + + public TestCaseVector(String testCase, int testCycleNumber) + { + this.testCase = testCase; + this.testCycleNumber = testCycleNumber; + } + + public String getTestCase() + { + return testCase; + } + + public int getTestCycleNumber() + { + return testCycleNumber; + } + + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + + if ((o == null) || (getClass() != o.getClass())) + { + return false; + } + + TestCaseVector that = (TestCaseVector) o; + + if (testCycleNumber != that.testCycleNumber) + { + return false; + } + + if ((testCase != null) ? (!testCase.equals(that.testCase)) : (that.testCase != null)) + { + return false; + } + + return true; + } + + public int hashCode() + { + int result; + result = ((testCase != null) ? testCase.hashCode() : 0); + result = (31 * result) + testCycleNumber; + + return result; + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java index 1c138fe575..9eba36e1e9 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java @@ -1,497 +1,497 @@ -/*
- *
- * 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.framework.distributedtesting;
-
-import org.apache.log4j.Logger;
-import org.apache.log4j.NDC;
-
-import org.apache.qpid.test.framework.MessagingTestConfigProperties;
-import org.apache.qpid.test.framework.TestUtils;
-import org.apache.qpid.test.framework.clocksynch.ClockSynchThread;
-import org.apache.qpid.test.framework.clocksynch.UDPClockSynchronizer;
-import org.apache.qpid.util.ReflectionUtils;
-import org.apache.qpid.util.ReflectionUtilsException;
-
-import org.apache.qpid.junit.extensions.SleepThrottle;
-import org.apache.qpid.junit.extensions.util.ParsedProperties;
-import org.apache.qpid.junit.extensions.util.TestContextProperties;
-
-import javax.jms.*;
-
-import java.util.*;
-
-/**
- * Implements a test client as described in the interop testing spec
- * (http://cwiki.apache.org/confluence/display/qpid/Interop+Testing+Specification). A test client is an agent that
- * reacts to control message sequences send by the test {@link Coordinator}.
- *
- * <p/><table><caption>Messages Handled by TestClient</caption>
- * <tr><th> Message <th> Action
- * <tr><td> Invite(compulsory) <td> Reply with Enlist.
- * <tr><td> Invite(test case) <td> Reply with Enlist if test case available.
- * <tr><td> AssignRole(test case) <td> Reply with Accept Role if matches an enlisted test. Keep test parameters.
- * <tr><td> Start <td> Send test messages defined by test parameters. Send report on messages sent.
- * <tr><td> Status Request <td> Send report on messages received.
- * <tr><td> Terminate <td> Terminate the test client.
- * <tr><td> ClockSynch <td> Synch clock against the supplied UDP address.
- * </table>
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Handle all incoming control messages. <td> {@link TestClientControlledTest}
- * <tr><td> Configure and look up test cases by name. <td> {@link TestClientControlledTest}
- * </table>
- */
-public class TestClient implements MessageListener
-{
- /** Used for debugging. */
- private static final Logger log = Logger.getLogger(TestClient.class);
-
- /** Used for reporting to the console. */
- private static final Logger console = Logger.getLogger("CONSOLE");
-
- /** Holds the default identifying name of the test client. */
- public static final String CLIENT_NAME = "java";
-
- /** Holds the URL of the broker to run the tests on. */
- public static String brokerUrl;
-
- /** Holds the virtual host to run the tests on. If <tt>null</tt>, then the default virtual host is used. */
- public static String virtualHost;
-
- /**
- * Holds the test context properties that provides the default test parameters, plus command line overrides.
- * This is initialized with the default test parameters, to which command line overrides may be applied.
- */
- public static ParsedProperties testContextProperties =
- TestContextProperties.getInstance(MessagingTestConfigProperties.defaults);
-
- /** Holds all the test cases loaded from the classpath. */
- Map<String, TestClientControlledTest> testCases = new HashMap<String, TestClientControlledTest>();
-
- /** Holds the test case currently being run by this client. */
- protected TestClientControlledTest currentTestCase;
-
- /** Holds the connection to the broker that the test is being coordinated on. */
- protected Connection connection;
-
- /** Holds the message producer to hold the test coordination over. */
- protected MessageProducer producer;
-
- /** Holds the JMS controlSession for the test coordination. */
- protected Session session;
-
- /** Holds the name of this client, with a default value. */
- protected String clientName = CLIENT_NAME;
-
- /** This flag indicates that the test client should attempt to join the currently running test case on start up. */
- protected boolean join;
-
- /** Holds the clock synchronizer for the test node. */
- ClockSynchThread clockSynchThread;
-
- /**
- * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified client
- * identifying name.
- *
- * @param pBrokerUrl The url of the broker to connect to.
- * @param pVirtualHost The virtual host to conect to.
- * @param clientName The client name to use.
- * @param join Flag to indicate that this client should attempt to join running tests.
- */
- public TestClient(String pBrokerUrl, String pVirtualHost, String clientName, boolean join)
- {
- log.debug("public TestClient(String pBrokerUrl = " + pBrokerUrl + ", String pVirtualHost = " + pVirtualHost
- + ", String clientName = " + clientName + ", boolean join = " + join + "): called");
-
- // Retain the connection parameters.
- brokerUrl = pBrokerUrl;
- virtualHost = pVirtualHost;
- this.clientName = clientName;
- this.join = join;
- }
-
- /**
- * The entry point for the interop test coordinator. This client accepts the following command line arguments:
- *
- * <p/><table>
- * <tr><td> -b <td> The broker URL. <td> Optional.
- * <tr><td> -h <td> The virtual host. <td> Optional.
- * <tr><td> -n <td> The test client name. <td> Optional.
- * <tr><td> name=value <td> Trailing argument define name/value pairs. Added to system properties. <td> Optional.
- * </table>
- *
- * @param args The command line arguments.
- */
- public static void main(String[] args)
- {
- log.debug("public static void main(String[] args = " + Arrays.toString(args) + "): called");
- console.info("Qpid Distributed Test Client.");
-
- // Override the default broker url to be localhost:5672.
- testContextProperties.setProperty(MessagingTestConfigProperties.BROKER_PROPNAME, "tcp://localhost:5672");
-
- // Use the command line parser to evaluate the command line with standard handling behaviour (print errors
- // and usage then exist if there are errors).
- // Any options and trailing name=value pairs are also injected into the test context properties object,
- // to override any defaults that may have been set up.
- ParsedProperties options =
- new ParsedProperties(org.apache.qpid.junit.extensions.util.CommandLineParser.processCommandLine(args,
- new org.apache.qpid.junit.extensions.util.CommandLineParser(
- new String[][]
- {
- { "b", "The broker URL.", "broker", "false" },
- { "h", "The virtual host to use.", "virtual host", "false" },
- { "o", "The name of the directory to output test timings to.", "dir", "false" },
- { "n", "The name of the test client.", "name", "false" },
- { "j", "Join this test client to running test.", "false" }
- }), testContextProperties));
-
- // Extract the command line options.
- String brokerUrl = options.getProperty("b");
- String virtualHost = options.getProperty("h");
- String clientName = options.getProperty("n");
- clientName = (clientName == null) ? CLIENT_NAME : clientName;
- boolean join = options.getPropertyAsBoolean("j");
-
- // To distinguish logging output set up an NDC on the client name.
- NDC.push(clientName);
-
- // Create a test client and start it running.
- TestClient client = new TestClient(brokerUrl, virtualHost, clientName, join);
-
- // Use a class path scanner to find all the interop test case implementations.
- // Hard code the test classes till the classpath scanner is fixed.
- Collection<Class<? extends TestClientControlledTest>> testCaseClasses =
- new ArrayList<Class<? extends TestClientControlledTest>>();
- // ClasspathScanner.getMatches(TestClientControlledTest.class, "^TestCase.*", true);
- testCaseClasses.addAll(loadTestCases("org.apache.qpid.interop.clienttestcases.TestCase1DummyRun",
- "org.apache.qpid.interop.clienttestcases.TestCase2BasicP2P",
- "org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub",
- "org.apache.qpid.interop.clienttestcases.TestCase4P2PMessageSize",
- "org.apache.qpid.interop.clienttestcases.TestCase5PubSubMessageSize",
- "org.apache.qpid.test.framework.distributedcircuit.TestClientCircuitEnd"));
-
- try
- {
- client.start(testCaseClasses);
- }
- catch (Exception e)
- {
- log.error("The test client was unable to start.", e);
- console.info(e.getMessage());
- System.exit(1);
- }
- }
-
- /**
- * Parses a list of class names, and loads them if they are available on the class path.
- *
- * @param classNames The names of the classes to load.
- *
- * @return A list of the loaded test case classes.
- */
- public static List<Class<? extends TestClientControlledTest>> loadTestCases(String... classNames)
- {
- List<Class<? extends TestClientControlledTest>> testCases =
- new LinkedList<Class<? extends TestClientControlledTest>>();
-
- for (String className : classNames)
- {
- try
- {
- Class<?> cls = ReflectionUtils.forName(className);
- testCases.add((Class<? extends TestClientControlledTest>) cls);
- }
- catch (ReflectionUtilsException e)
- {
- // Ignore, class could not be found, so test not available.
- console.warn("Requested class " + className + " cannot be found, ignoring it.");
- }
- catch (ClassCastException e)
- {
- // Ignore, class was not of correct type to be a test case.
- console.warn("Requested class " + className + " is not an instance of TestClientControlledTest.");
- }
- }
-
- return testCases;
- }
-
- /**
- * Starts the interop test client running. This causes it to start listening for incoming test invites.
- *
- * @param testCaseClasses The classes of the available test cases. The test case names from these are used to
- * matchin incoming test invites against.
- *
- * @throws JMSException Any underlying JMSExceptions are allowed to fall through.
- */
- protected void start(Collection<Class<? extends TestClientControlledTest>> testCaseClasses) throws JMSException
- {
- log.debug("protected void start(Collection<Class<? extends TestClientControlledTest>> testCaseClasses = "
- + testCaseClasses + "): called");
-
- // Create all the test case implementations and index them by the test names.
- for (Class<? extends TestClientControlledTest> nextClass : testCaseClasses)
- {
- try
- {
- TestClientControlledTest testCase = nextClass.newInstance();
- testCases.put(testCase.getName(), testCase);
- }
- catch (InstantiationException e)
- {
- log.warn("Could not instantiate test case class: " + nextClass.getName(), e);
- // Ignored.
- }
- catch (IllegalAccessException e)
- {
- log.warn("Could not instantiate test case class due to illegal access: " + nextClass.getName(), e);
- // Ignored.
- }
- }
-
- // Open a connection to communicate with the coordinator on.
- connection = TestUtils.createConnection(testContextProperties);
- session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
-
- // Set this up to listen for control messages.
- Topic privateControlTopic = session.createTopic("iop.control." + clientName);
- MessageConsumer consumer = session.createConsumer(privateControlTopic);
- consumer.setMessageListener(this);
-
- Topic controlTopic = session.createTopic("iop.control");
- MessageConsumer consumer2 = session.createConsumer(controlTopic);
- consumer2.setMessageListener(this);
-
- // Create a producer to send replies with.
- producer = session.createProducer(null);
-
- // If the join flag was set, then broadcast a join message to notify the coordinator that a new test client
- // is available to join the current test case, if it supports it. This message may be ignored, or it may result
- // in this test client receiving a test invite.
- if (join)
- {
- Message joinMessage = session.createMessage();
-
- joinMessage.setStringProperty("CONTROL_TYPE", "JOIN");
- joinMessage.setStringProperty("CLIENT_NAME", clientName);
- joinMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName);
- producer.send(controlTopic, joinMessage);
- }
-
- // Start listening for incoming control messages.
- connection.start();
- }
-
- /**
- * Handles all incoming control messages.
- *
- * @param message The incoming message.
- */
- public void onMessage(Message message)
- {
- NDC.push(clientName);
- log.debug("public void onMessage(Message message = " + message + "): called");
-
- try
- {
- String controlType = message.getStringProperty("CONTROL_TYPE");
- String testName = message.getStringProperty("TEST_NAME");
-
- log.debug("Received control of type '" + controlType + "' for the test '" + testName + "'");
-
- // Check if the message is a test invite.
- if ("INVITE".equals(controlType))
- {
- // Flag used to indicate that an enlist should be sent. Only enlist to compulsory invites or invites
- // for which test cases exist.
- boolean enlist = false;
-
- if (testName != null)
- {
- log.debug("Got an invite to test: " + testName);
-
- // Check if the requested test case is available.
- TestClientControlledTest testCase = testCases.get(testName);
-
- if (testCase != null)
- {
- log.debug("Found implementing class for test '" + testName + "', enlisting for it.");
-
- // Check if the test case will accept the invitation.
- enlist = testCase.acceptInvite(message);
-
- log.debug("The test case "
- + (enlist ? " accepted the invite, enlisting for it."
- : " did not accept the invite, not enlisting."));
-
- // Make the requested test case the current test case.
- currentTestCase = testCase;
- }
- else
- {
- log.debug("Received an invite to the test '" + testName + "' but this test is not known.");
- }
- }
- else
- {
- log.debug("Got a compulsory invite, enlisting for it.");
-
- enlist = true;
- }
-
- if (enlist)
- {
- // Reply with the client name in an Enlist message.
- Message enlistMessage = session.createMessage();
- enlistMessage.setStringProperty("CONTROL_TYPE", "ENLIST");
- enlistMessage.setStringProperty("CLIENT_NAME", clientName);
- enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName);
- enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID());
-
- log.debug("Sending enlist message '" + enlistMessage + "' to " + message.getJMSReplyTo());
-
- producer.send(message.getJMSReplyTo(), enlistMessage);
- }
- else
- {
- // Reply with the client name in an Decline message.
- Message enlistMessage = session.createMessage();
- enlistMessage.setStringProperty("CONTROL_TYPE", "DECLINE");
- enlistMessage.setStringProperty("CLIENT_NAME", clientName);
- enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName);
- enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID());
-
- log.debug("Sending decline message '" + enlistMessage + "' to " + message.getJMSReplyTo());
-
- producer.send(message.getJMSReplyTo(), enlistMessage);
- }
- }
- else if ("ASSIGN_ROLE".equals(controlType))
- {
- // Assign the role to the current test case.
- String roleName = message.getStringProperty("ROLE");
-
- log.debug("Got a role assignment to role: " + roleName);
-
- TestClientControlledTest.Roles role = Enum.valueOf(TestClientControlledTest.Roles.class, roleName);
-
- currentTestCase.assignRole(role, message);
-
- // Reply by accepting the role in an Accept Role message.
- Message acceptRoleMessage = session.createMessage();
- acceptRoleMessage.setStringProperty("CLIENT_NAME", clientName);
- acceptRoleMessage.setStringProperty("CONTROL_TYPE", "ACCEPT_ROLE");
- acceptRoleMessage.setJMSCorrelationID(message.getJMSCorrelationID());
-
- log.debug("Sending accept role message '" + acceptRoleMessage + "' to " + message.getJMSReplyTo());
-
- producer.send(message.getJMSReplyTo(), acceptRoleMessage);
- }
- else if ("START".equals(controlType) || "STATUS_REQUEST".equals(controlType))
- {
- if ("START".equals(controlType))
- {
- log.debug("Got a start notification.");
-
- // Extract the number of test messages to send from the start notification.
- int numMessages;
-
- try
- {
- numMessages = message.getIntProperty("MESSAGE_COUNT");
- }
- catch (NumberFormatException e)
- {
- // If the number of messages is not specified, use the default of one.
- numMessages = 1;
- }
-
- // Start the current test case.
- currentTestCase.start(numMessages);
- }
- else
- {
- log.debug("Got a status request.");
- }
-
- // Generate the report from the test case and reply with it as a Report message.
- Message reportMessage = currentTestCase.getReport(session);
- reportMessage.setStringProperty("CLIENT_NAME", clientName);
- reportMessage.setStringProperty("CONTROL_TYPE", "REPORT");
- reportMessage.setJMSCorrelationID(message.getJMSCorrelationID());
-
- log.debug("Sending report message '" + reportMessage + "' to " + message.getJMSReplyTo());
-
- producer.send(message.getJMSReplyTo(), reportMessage);
- }
- else if ("TERMINATE".equals(controlType))
- {
- console.info("Received termination instruction from coordinator.");
-
- // Is a cleaner shutdown needed?
- connection.close();
- System.exit(0);
- }
- else if ("CLOCK_SYNCH".equals(controlType))
- {
- log.debug("Received clock synch command.");
- String address = message.getStringProperty("ADDRESS");
-
- log.debug("address = " + address);
-
- // Re-create (if necessary) and start the clock synch thread to synch the clock every ten seconds.
- if (clockSynchThread != null)
- {
- clockSynchThread.terminate();
- }
-
- SleepThrottle throttle = new SleepThrottle();
- throttle.setRate(0.1f);
-
- clockSynchThread = new ClockSynchThread(new UDPClockSynchronizer(address), throttle);
- clockSynchThread.start();
- }
- else
- {
- // Log a warning about this but otherwise ignore it.
- log.warn("Got an unknown control message, controlType = " + controlType + ", message = " + message);
- }
- }
- catch (JMSException e)
- {
- // Log a warning about this, but otherwise ignore it.
- log.warn("Got JMSException whilst handling message: " + message, e);
- }
- // Log any runtimes that fall through this message handler. These are fatal errors for the test client.
- catch (RuntimeException e)
- {
- log.error("The test client message handler got an unhandled exception: ", e);
- console.info("The message handler got an unhandled exception, terminating the test client.");
- System.exit(1);
- }
- finally
- {
- NDC.pop();
- }
- }
-}
+/* + * + * 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.framework.distributedtesting; + +import org.apache.log4j.Logger; +import org.apache.log4j.NDC; + +import org.apache.qpid.test.framework.MessagingTestConfigProperties; +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.clocksynch.ClockSynchThread; +import org.apache.qpid.test.framework.clocksynch.UDPClockSynchronizer; +import org.apache.qpid.util.ReflectionUtils; +import org.apache.qpid.util.ReflectionUtilsException; + +import org.apache.qpid.junit.extensions.SleepThrottle; +import org.apache.qpid.junit.extensions.util.ParsedProperties; +import org.apache.qpid.junit.extensions.util.TestContextProperties; + +import javax.jms.*; + +import java.util.*; + +/** + * Implements a test client as described in the interop testing spec + * (http://cwiki.apache.org/confluence/display/qpid/Interop+Testing+Specification). A test client is an agent that + * reacts to control message sequences send by the test {@link Coordinator}. + * + * <p/><table><caption>Messages Handled by TestClient</caption> + * <tr><th> Message <th> Action + * <tr><td> Invite(compulsory) <td> Reply with Enlist. + * <tr><td> Invite(test case) <td> Reply with Enlist if test case available. + * <tr><td> AssignRole(test case) <td> Reply with Accept Role if matches an enlisted test. Keep test parameters. + * <tr><td> Start <td> Send test messages defined by test parameters. Send report on messages sent. + * <tr><td> Status Request <td> Send report on messages received. + * <tr><td> Terminate <td> Terminate the test client. + * <tr><td> ClockSynch <td> Synch clock against the supplied UDP address. + * </table> + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Handle all incoming control messages. <td> {@link TestClientControlledTest} + * <tr><td> Configure and look up test cases by name. <td> {@link TestClientControlledTest} + * </table> + */ +public class TestClient implements MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestClient.class); + + /** Used for reporting to the console. */ + private static final Logger console = Logger.getLogger("CONSOLE"); + + /** Holds the default identifying name of the test client. */ + public static final String CLIENT_NAME = "java"; + + /** Holds the URL of the broker to run the tests on. */ + public static String brokerUrl; + + /** Holds the virtual host to run the tests on. If <tt>null</tt>, then the default virtual host is used. */ + public static String virtualHost; + + /** + * Holds the test context properties that provides the default test parameters, plus command line overrides. + * This is initialized with the default test parameters, to which command line overrides may be applied. + */ + public static ParsedProperties testContextProperties = + TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + + /** Holds all the test cases loaded from the classpath. */ + Map<String, TestClientControlledTest> testCases = new HashMap<String, TestClientControlledTest>(); + + /** Holds the test case currently being run by this client. */ + protected TestClientControlledTest currentTestCase; + + /** Holds the connection to the broker that the test is being coordinated on. */ + protected Connection connection; + + /** Holds the message producer to hold the test coordination over. */ + protected MessageProducer producer; + + /** Holds the JMS controlSession for the test coordination. */ + protected Session session; + + /** Holds the name of this client, with a default value. */ + protected String clientName = CLIENT_NAME; + + /** This flag indicates that the test client should attempt to join the currently running test case on start up. */ + protected boolean join; + + /** Holds the clock synchronizer for the test node. */ + ClockSynchThread clockSynchThread; + + /** + * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified client + * identifying name. + * + * @param pBrokerUrl The url of the broker to connect to. + * @param pVirtualHost The virtual host to conect to. + * @param clientName The client name to use. + * @param join Flag to indicate that this client should attempt to join running tests. + */ + public TestClient(String pBrokerUrl, String pVirtualHost, String clientName, boolean join) + { + log.debug("public TestClient(String pBrokerUrl = " + pBrokerUrl + ", String pVirtualHost = " + pVirtualHost + + ", String clientName = " + clientName + ", boolean join = " + join + "): called"); + + // Retain the connection parameters. + brokerUrl = pBrokerUrl; + virtualHost = pVirtualHost; + this.clientName = clientName; + this.join = join; + } + + /** + * The entry point for the interop test coordinator. This client accepts the following command line arguments: + * + * <p/><table> + * <tr><td> -b <td> The broker URL. <td> Optional. + * <tr><td> -h <td> The virtual host. <td> Optional. + * <tr><td> -n <td> The test client name. <td> Optional. + * <tr><td> name=value <td> Trailing argument define name/value pairs. Added to system properties. <td> Optional. + * </table> + * + * @param args The command line arguments. + */ + public static void main(String[] args) + { + log.debug("public static void main(String[] args = " + Arrays.toString(args) + "): called"); + console.info("Qpid Distributed Test Client."); + + // Override the default broker url to be localhost:5672. + testContextProperties.setProperty(MessagingTestConfigProperties.BROKER_PROPNAME, "tcp://localhost:5672"); + + // Use the command line parser to evaluate the command line with standard handling behaviour (print errors + // and usage then exist if there are errors). + // Any options and trailing name=value pairs are also injected into the test context properties object, + // to override any defaults that may have been set up. + ParsedProperties options = + new ParsedProperties(org.apache.qpid.junit.extensions.util.CommandLineParser.processCommandLine(args, + new org.apache.qpid.junit.extensions.util.CommandLineParser( + new String[][] + { + { "b", "The broker URL.", "broker", "false" }, + { "h", "The virtual host to use.", "virtual host", "false" }, + { "o", "The name of the directory to output test timings to.", "dir", "false" }, + { "n", "The name of the test client.", "name", "false" }, + { "j", "Join this test client to running test.", "false" } + }), testContextProperties)); + + // Extract the command line options. + String brokerUrl = options.getProperty("b"); + String virtualHost = options.getProperty("h"); + String clientName = options.getProperty("n"); + clientName = (clientName == null) ? CLIENT_NAME : clientName; + boolean join = options.getPropertyAsBoolean("j"); + + // To distinguish logging output set up an NDC on the client name. + NDC.push(clientName); + + // Create a test client and start it running. + TestClient client = new TestClient(brokerUrl, virtualHost, clientName, join); + + // Use a class path scanner to find all the interop test case implementations. + // Hard code the test classes till the classpath scanner is fixed. + Collection<Class<? extends TestClientControlledTest>> testCaseClasses = + new ArrayList<Class<? extends TestClientControlledTest>>(); + // ClasspathScanner.getMatches(TestClientControlledTest.class, "^TestCase.*", true); + testCaseClasses.addAll(loadTestCases("org.apache.qpid.interop.clienttestcases.TestCase1DummyRun", + "org.apache.qpid.interop.clienttestcases.TestCase2BasicP2P", + "org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub", + "org.apache.qpid.interop.clienttestcases.TestCase4P2PMessageSize", + "org.apache.qpid.interop.clienttestcases.TestCase5PubSubMessageSize", + "org.apache.qpid.test.framework.distributedcircuit.TestClientCircuitEnd")); + + try + { + client.start(testCaseClasses); + } + catch (Exception e) + { + log.error("The test client was unable to start.", e); + console.info(e.getMessage()); + System.exit(1); + } + } + + /** + * Parses a list of class names, and loads them if they are available on the class path. + * + * @param classNames The names of the classes to load. + * + * @return A list of the loaded test case classes. + */ + public static List<Class<? extends TestClientControlledTest>> loadTestCases(String... classNames) + { + List<Class<? extends TestClientControlledTest>> testCases = + new LinkedList<Class<? extends TestClientControlledTest>>(); + + for (String className : classNames) + { + try + { + Class<?> cls = ReflectionUtils.forName(className); + testCases.add((Class<? extends TestClientControlledTest>) cls); + } + catch (ReflectionUtilsException e) + { + // Ignore, class could not be found, so test not available. + console.warn("Requested class " + className + " cannot be found, ignoring it."); + } + catch (ClassCastException e) + { + // Ignore, class was not of correct type to be a test case. + console.warn("Requested class " + className + " is not an instance of TestClientControlledTest."); + } + } + + return testCases; + } + + /** + * Starts the interop test client running. This causes it to start listening for incoming test invites. + * + * @param testCaseClasses The classes of the available test cases. The test case names from these are used to + * matchin incoming test invites against. + * + * @throws JMSException Any underlying JMSExceptions are allowed to fall through. + */ + protected void start(Collection<Class<? extends TestClientControlledTest>> testCaseClasses) throws JMSException + { + log.debug("protected void start(Collection<Class<? extends TestClientControlledTest>> testCaseClasses = " + + testCaseClasses + "): called"); + + // Create all the test case implementations and index them by the test names. + for (Class<? extends TestClientControlledTest> nextClass : testCaseClasses) + { + try + { + TestClientControlledTest testCase = nextClass.newInstance(); + testCases.put(testCase.getName(), testCase); + } + catch (InstantiationException e) + { + log.warn("Could not instantiate test case class: " + nextClass.getName(), e); + // Ignored. + } + catch (IllegalAccessException e) + { + log.warn("Could not instantiate test case class due to illegal access: " + nextClass.getName(), e); + // Ignored. + } + } + + // Open a connection to communicate with the coordinator on. + connection = TestUtils.createConnection(testContextProperties); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Set this up to listen for control messages. + Topic privateControlTopic = session.createTopic("iop.control." + clientName); + MessageConsumer consumer = session.createConsumer(privateControlTopic); + consumer.setMessageListener(this); + + Topic controlTopic = session.createTopic("iop.control"); + MessageConsumer consumer2 = session.createConsumer(controlTopic); + consumer2.setMessageListener(this); + + // Create a producer to send replies with. + producer = session.createProducer(null); + + // If the join flag was set, then broadcast a join message to notify the coordinator that a new test client + // is available to join the current test case, if it supports it. This message may be ignored, or it may result + // in this test client receiving a test invite. + if (join) + { + Message joinMessage = session.createMessage(); + + joinMessage.setStringProperty("CONTROL_TYPE", "JOIN"); + joinMessage.setStringProperty("CLIENT_NAME", clientName); + joinMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); + producer.send(controlTopic, joinMessage); + } + + // Start listening for incoming control messages. + connection.start(); + } + + /** + * Handles all incoming control messages. + * + * @param message The incoming message. + */ + public void onMessage(Message message) + { + NDC.push(clientName); + log.debug("public void onMessage(Message message = " + message + "): called"); + + try + { + String controlType = message.getStringProperty("CONTROL_TYPE"); + String testName = message.getStringProperty("TEST_NAME"); + + log.debug("Received control of type '" + controlType + "' for the test '" + testName + "'"); + + // Check if the message is a test invite. + if ("INVITE".equals(controlType)) + { + // Flag used to indicate that an enlist should be sent. Only enlist to compulsory invites or invites + // for which test cases exist. + boolean enlist = false; + + if (testName != null) + { + log.debug("Got an invite to test: " + testName); + + // Check if the requested test case is available. + TestClientControlledTest testCase = testCases.get(testName); + + if (testCase != null) + { + log.debug("Found implementing class for test '" + testName + "', enlisting for it."); + + // Check if the test case will accept the invitation. + enlist = testCase.acceptInvite(message); + + log.debug("The test case " + + (enlist ? " accepted the invite, enlisting for it." + : " did not accept the invite, not enlisting.")); + + // Make the requested test case the current test case. + currentTestCase = testCase; + } + else + { + log.debug("Received an invite to the test '" + testName + "' but this test is not known."); + } + } + else + { + log.debug("Got a compulsory invite, enlisting for it."); + + enlist = true; + } + + if (enlist) + { + // Reply with the client name in an Enlist message. + Message enlistMessage = session.createMessage(); + enlistMessage.setStringProperty("CONTROL_TYPE", "ENLIST"); + enlistMessage.setStringProperty("CLIENT_NAME", clientName); + enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); + enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + log.debug("Sending enlist message '" + enlistMessage + "' to " + message.getJMSReplyTo()); + + producer.send(message.getJMSReplyTo(), enlistMessage); + } + else + { + // Reply with the client name in an Decline message. + Message enlistMessage = session.createMessage(); + enlistMessage.setStringProperty("CONTROL_TYPE", "DECLINE"); + enlistMessage.setStringProperty("CLIENT_NAME", clientName); + enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); + enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + log.debug("Sending decline message '" + enlistMessage + "' to " + message.getJMSReplyTo()); + + producer.send(message.getJMSReplyTo(), enlistMessage); + } + } + else if ("ASSIGN_ROLE".equals(controlType)) + { + // Assign the role to the current test case. + String roleName = message.getStringProperty("ROLE"); + + log.debug("Got a role assignment to role: " + roleName); + + TestClientControlledTest.Roles role = Enum.valueOf(TestClientControlledTest.Roles.class, roleName); + + currentTestCase.assignRole(role, message); + + // Reply by accepting the role in an Accept Role message. + Message acceptRoleMessage = session.createMessage(); + acceptRoleMessage.setStringProperty("CLIENT_NAME", clientName); + acceptRoleMessage.setStringProperty("CONTROL_TYPE", "ACCEPT_ROLE"); + acceptRoleMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + log.debug("Sending accept role message '" + acceptRoleMessage + "' to " + message.getJMSReplyTo()); + + producer.send(message.getJMSReplyTo(), acceptRoleMessage); + } + else if ("START".equals(controlType) || "STATUS_REQUEST".equals(controlType)) + { + if ("START".equals(controlType)) + { + log.debug("Got a start notification."); + + // Extract the number of test messages to send from the start notification. + int numMessages; + + try + { + numMessages = message.getIntProperty("MESSAGE_COUNT"); + } + catch (NumberFormatException e) + { + // If the number of messages is not specified, use the default of one. + numMessages = 1; + } + + // Start the current test case. + currentTestCase.start(numMessages); + } + else + { + log.debug("Got a status request."); + } + + // Generate the report from the test case and reply with it as a Report message. + Message reportMessage = currentTestCase.getReport(session); + reportMessage.setStringProperty("CLIENT_NAME", clientName); + reportMessage.setStringProperty("CONTROL_TYPE", "REPORT"); + reportMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + log.debug("Sending report message '" + reportMessage + "' to " + message.getJMSReplyTo()); + + producer.send(message.getJMSReplyTo(), reportMessage); + } + else if ("TERMINATE".equals(controlType)) + { + console.info("Received termination instruction from coordinator."); + + // Is a cleaner shutdown needed? + connection.close(); + System.exit(0); + } + else if ("CLOCK_SYNCH".equals(controlType)) + { + log.debug("Received clock synch command."); + String address = message.getStringProperty("ADDRESS"); + + log.debug("address = " + address); + + // Re-create (if necessary) and start the clock synch thread to synch the clock every ten seconds. + if (clockSynchThread != null) + { + clockSynchThread.terminate(); + } + + SleepThrottle throttle = new SleepThrottle(); + throttle.setRate(0.1f); + + clockSynchThread = new ClockSynchThread(new UDPClockSynchronizer(address), throttle); + clockSynchThread.start(); + } + else + { + // Log a warning about this but otherwise ignore it. + log.warn("Got an unknown control message, controlType = " + controlType + ", message = " + message); + } + } + catch (JMSException e) + { + // Log a warning about this, but otherwise ignore it. + log.warn("Got JMSException whilst handling message: " + message, e); + } + // Log any runtimes that fall through this message handler. These are fatal errors for the test client. + catch (RuntimeException e) + { + log.error("The test client message handler got an unhandled exception: ", e); + console.info("The message handler got an unhandled exception, terminating the test client."); + System.exit(1); + } + finally + { + NDC.pop(); + } + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalAMQPPublisherImpl.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalAMQPPublisherImpl.java index 14ae108da8..4388c7fbd8 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalAMQPPublisherImpl.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalAMQPPublisherImpl.java @@ -1,133 +1,133 @@ -/*
- *
- * 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.framework.localcircuit;
-
-import org.apache.qpid.client.AMQNoConsumersException;
-import org.apache.qpid.client.AMQNoRouteException;
-import org.apache.qpid.test.framework.*;
-
-import org.apache.qpid.junit.extensions.util.ParsedProperties;
-
-import javax.jms.MessageConsumer;
-import javax.jms.MessageProducer;
-import javax.jms.Session;
-
-/**
- * LocalAMQPPublisherImpl is an extension of {@link LocalPublisherImpl} that adds AMQP specific features. Specifically
- * extra assertions for AMQP features not available through generic JMS.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td>
- * </table>
- */
-public class LocalAMQPPublisherImpl extends LocalPublisherImpl implements AMQPPublisher
-{
- /**
- * Creates a circuit end point on the specified producer, consumer and controlSession. Monitors are also configured
- * for messages and exceptions received by the circuit end.
- *
- * @param producer The message producer for the circuit end point.
- * @param consumer The message consumer for the circuit end point.
- * @param session The controlSession for the circuit end point.
- * @param messageMonitor The monitor to notify of all messages received by the circuit end.
- * @param exceptionMonitor The monitor to notify of all exceptions received by the circuit end.
- */
- public LocalAMQPPublisherImpl(MessageProducer producer, MessageConsumer consumer, Session session,
- MessageMonitor messageMonitor, ExceptionMonitor exceptionMonitor)
- {
- super(producer, consumer, session, messageMonitor, exceptionMonitor);
- }
-
- /**
- * Creates a circuit end point from the producer, consumer and controlSession in a circuit end base implementation.
- *
- * @param end The circuit end base implementation to take producers and consumers from.
- */
- public LocalAMQPPublisherImpl(CircuitEndBase end)
- {
- super(end);
- }
-
- /**
- * Provides an assertion that the publisher got a no consumers exception on every message.
- *
- * @param testProps The test configuration properties.
- *
- * @return An assertion that the publisher got a no consumers exception on every message.
- */
- public Assertion noConsumersAssertion(ParsedProperties testProps)
- {
- return new AssertionBase()
- {
- public boolean apply()
- {
- boolean passed = true;
- ExceptionMonitor connectionExceptionMonitor = circuit.getConnectionExceptionMonitor();
-
- if (!connectionExceptionMonitor.assertOneJMSExceptionWithLinkedCause(AMQNoConsumersException.class))
- {
- passed = false;
-
- addError("Was expecting linked exception type " + AMQNoConsumersException.class.getName()
- + " on the connection.\n");
- addError((connectionExceptionMonitor.size() > 0)
- ? ("Actually got the following exceptions on the connection, " + connectionExceptionMonitor)
- : "Got no exceptions on the connection.");
- }
-
- return passed;
- }
- };
- }
-
- /**
- * Provides an assertion that the publisher got a no rout exception on every message.
- *
- * @param testProps The test configuration properties.
- *
- * @return An assertion that the publisher got a no rout exception on every message.
- */
- public Assertion noRouteAssertion(ParsedProperties testProps)
- {
- return new AssertionBase()
- {
- public boolean apply()
- {
- boolean passed = true;
- ExceptionMonitor connectionExceptionMonitor = circuit.getConnectionExceptionMonitor();
-
- if (!connectionExceptionMonitor.assertOneJMSExceptionWithLinkedCause(AMQNoRouteException.class))
- {
- passed = false;
-
- addError("Was expecting linked exception type " + AMQNoRouteException.class.getName()
- + " on the connection.\n");
- addError((connectionExceptionMonitor.size() > 0)
- ? ("Actually got the following exceptions on the connection, " + connectionExceptionMonitor)
- : "Got no exceptions on the connection.");
- }
-
- return passed;
- }
- };
- }
-}
+/* + * + * 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.framework.localcircuit; + +import org.apache.qpid.client.AMQNoConsumersException; +import org.apache.qpid.client.AMQNoRouteException; +import org.apache.qpid.test.framework.*; + +import org.apache.qpid.junit.extensions.util.ParsedProperties; + +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; + +/** + * LocalAMQPPublisherImpl is an extension of {@link LocalPublisherImpl} that adds AMQP specific features. Specifically + * extra assertions for AMQP features not available through generic JMS. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> + * </table> + */ +public class LocalAMQPPublisherImpl extends LocalPublisherImpl implements AMQPPublisher +{ + /** + * Creates a circuit end point on the specified producer, consumer and controlSession. Monitors are also configured + * for messages and exceptions received by the circuit end. + * + * @param producer The message producer for the circuit end point. + * @param consumer The message consumer for the circuit end point. + * @param session The controlSession for the circuit end point. + * @param messageMonitor The monitor to notify of all messages received by the circuit end. + * @param exceptionMonitor The monitor to notify of all exceptions received by the circuit end. + */ + public LocalAMQPPublisherImpl(MessageProducer producer, MessageConsumer consumer, Session session, + MessageMonitor messageMonitor, ExceptionMonitor exceptionMonitor) + { + super(producer, consumer, session, messageMonitor, exceptionMonitor); + } + + /** + * Creates a circuit end point from the producer, consumer and controlSession in a circuit end base implementation. + * + * @param end The circuit end base implementation to take producers and consumers from. + */ + public LocalAMQPPublisherImpl(CircuitEndBase end) + { + super(end); + } + + /** + * Provides an assertion that the publisher got a no consumers exception on every message. + * + * @param testProps The test configuration properties. + * + * @return An assertion that the publisher got a no consumers exception on every message. + */ + public Assertion noConsumersAssertion(ParsedProperties testProps) + { + return new AssertionBase() + { + public boolean apply() + { + boolean passed = true; + ExceptionMonitor connectionExceptionMonitor = circuit.getConnectionExceptionMonitor(); + + if (!connectionExceptionMonitor.assertOneJMSExceptionWithLinkedCause(AMQNoConsumersException.class)) + { + passed = false; + + addError("Was expecting linked exception type " + AMQNoConsumersException.class.getName() + + " on the connection.\n"); + addError((connectionExceptionMonitor.size() > 0) + ? ("Actually got the following exceptions on the connection, " + connectionExceptionMonitor) + : "Got no exceptions on the connection."); + } + + return passed; + } + }; + } + + /** + * Provides an assertion that the publisher got a no rout exception on every message. + * + * @param testProps The test configuration properties. + * + * @return An assertion that the publisher got a no rout exception on every message. + */ + public Assertion noRouteAssertion(ParsedProperties testProps) + { + return new AssertionBase() + { + public boolean apply() + { + boolean passed = true; + ExceptionMonitor connectionExceptionMonitor = circuit.getConnectionExceptionMonitor(); + + if (!connectionExceptionMonitor.assertOneJMSExceptionWithLinkedCause(AMQNoRouteException.class)) + { + passed = false; + + addError("Was expecting linked exception type " + AMQNoRouteException.class.getName() + + " on the connection.\n"); + addError((connectionExceptionMonitor.size() > 0) + ? ("Actually got the following exceptions on the connection, " + connectionExceptionMonitor) + : "Got no exceptions on the connection."); + } + + return passed; + } + }; + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java index 4545e3c164..c11f75e742 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java @@ -1,96 +1,96 @@ -/*
- *
- * 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.framework.qpid;
-
-import junit.framework.Test;
-import junit.framework.TestResult;
-
-import org.apache.qpid.test.framework.FrameworkBaseCase;
-import org.apache.qpid.test.framework.LocalAMQPCircuitFactory;
-
-import org.apache.qpid.junit.extensions.WrappedSuiteTestDecorator;
-
-/**
- * AMQPFeatureDecorator applies decorations to {@link FrameworkBaseCase} tests, so that they may use Qpid/AMQP specific
- * features, not available through JMS. For example, the immediate and mandatory flags. This decorator replaces the
- * standard test circuit factory on the base class with one that allows these features to be used.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Substitute the circuit factory with an AMQP/Qpid specific one.
- * </table>
- *
- * @todo This wrapper substitutes in a LocalAMQPCircuitFactory, which is fine for local tests. For distributed tests
- * the Fanout or Interop factories are substituted in by their decorators instead. These actually use
- * distributed circuit static create methods to build the circuits, which should actually be changed to a factory,
- * so that static methods do not need to be used. The distributed circuit creater delegates the circuit
- * construction to remote test nodes. This decorator should not be used with distributed tests, or should be made
- * aware of them, in which case it might ensure that an AMQP feature (implied already by other properties) flag
- * is passed out to the remote test nodes, and provide a mechansim for them to decorate their circuit creation
- * with AMQP features too. Add factory substituion/decoration mechansim for test clients, here or in a seperate
- * class.
- */
-public class AMQPFeatureDecorator extends WrappedSuiteTestDecorator
-{
- /** The test suite to run. */
- private Test test;
-
- /**
- * Creates a wrapped test test decorator from another one.
- *
- * @param test The test test.
- */
- public AMQPFeatureDecorator(WrappedSuiteTestDecorator test)
- {
- super(test);
- this.test = test;
- }
-
- /**
- * Runs the tests with a LocalAMQPCircuitFactory. Only tests that extend FrameworkBaseCase are decorated.
- *
- * @param testResult The the results object to monitor the test results with.
- */
- public void run(TestResult testResult)
- {
- for (Test test : getAllUnderlyingTests())
- {
- if (test instanceof FrameworkBaseCase)
- {
- FrameworkBaseCase frameworkTest = (FrameworkBaseCase) test;
- frameworkTest.setCircuitFactory(new LocalAMQPCircuitFactory());
- }
- }
-
- // Run the test.
- test.run(testResult);
- }
-
- /**
- * Prints the name of the test for debugging purposes.
- *
- * @return The name of the test.
- */
- public String toString()
- {
- return "AMQPFeatureDecorator: [test = \"" + test + "\"]";
- }
-}
+/* + * + * 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.framework.qpid; + +import junit.framework.Test; +import junit.framework.TestResult; + +import org.apache.qpid.test.framework.FrameworkBaseCase; +import org.apache.qpid.test.framework.LocalAMQPCircuitFactory; + +import org.apache.qpid.junit.extensions.WrappedSuiteTestDecorator; + +/** + * AMQPFeatureDecorator applies decorations to {@link FrameworkBaseCase} tests, so that they may use Qpid/AMQP specific + * features, not available through JMS. For example, the immediate and mandatory flags. This decorator replaces the + * standard test circuit factory on the base class with one that allows these features to be used. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Substitute the circuit factory with an AMQP/Qpid specific one. + * </table> + * + * @todo This wrapper substitutes in a LocalAMQPCircuitFactory, which is fine for local tests. For distributed tests + * the Fanout or Interop factories are substituted in by their decorators instead. These actually use + * distributed circuit static create methods to build the circuits, which should actually be changed to a factory, + * so that static methods do not need to be used. The distributed circuit creater delegates the circuit + * construction to remote test nodes. This decorator should not be used with distributed tests, or should be made + * aware of them, in which case it might ensure that an AMQP feature (implied already by other properties) flag + * is passed out to the remote test nodes, and provide a mechansim for them to decorate their circuit creation + * with AMQP features too. Add factory substituion/decoration mechansim for test clients, here or in a seperate + * class. + */ +public class AMQPFeatureDecorator extends WrappedSuiteTestDecorator +{ + /** The test suite to run. */ + private Test test; + + /** + * Creates a wrapped test test decorator from another one. + * + * @param test The test test. + */ + public AMQPFeatureDecorator(WrappedSuiteTestDecorator test) + { + super(test); + this.test = test; + } + + /** + * Runs the tests with a LocalAMQPCircuitFactory. Only tests that extend FrameworkBaseCase are decorated. + * + * @param testResult The the results object to monitor the test results with. + */ + public void run(TestResult testResult) + { + for (Test test : getAllUnderlyingTests()) + { + if (test instanceof FrameworkBaseCase) + { + FrameworkBaseCase frameworkTest = (FrameworkBaseCase) test; + frameworkTest.setCircuitFactory(new LocalAMQPCircuitFactory()); + } + } + + // Run the test. + test.run(testResult); + } + + /** + * Prints the name of the test for debugging purposes. + * + * @return The name of the test. + */ + public String toString() + { + return "AMQPFeatureDecorator: [test = \"" + test + "\"]"; + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureDecorator.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureDecorator.java index 3a048ac042..2708253d86 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureDecorator.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureDecorator.java @@ -1,95 +1,95 @@ -/*
- *
- * 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.framework.qpid;
-
-import junit.framework.Test;
-import junit.framework.TestResult;
-
-import org.apache.qpid.test.framework.BrokerLifecycleAware;
-import org.apache.qpid.test.framework.CauseFailureUserPrompt;
-
-import org.apache.qpid.junit.extensions.WrappedSuiteTestDecorator;
-
-/**
- * CauseFailureDecorator applies decorations to {@link BrokerLifecycleAware} tests, so that they may use different failure
- * mechanisms. It is capable of detecting when a test case uses in-vm brokers, and setting up an automatic failure
- * for those tests, so that the current live broker can be shut-down by test cases. For external brokers, automatic
- * failure could be implemented, for example by having a kill script. At the moment this sets up the failure to prompt
- * a user interactively to cause a failure, using {@link CauseFailureUserPrompt}.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Setup automatic failures for in-vm brokers. <td> {@link CauseFailureInVM}
- * <tr><td> Setup user generated failures for external brokers. <td> {@link CauseFailureUserPrompt}.
- * <tr><td>
- * </table>
- *
- * @todo Slight problem in that CauseFailureInVM is Qpid specific, whereas CauseFailureUserPrompt is not. Would like the
- * failure decorator to be non-qpid specific so that it can test failure of any JMS implementation too. Either pass
- * in class name of failure mechanism, set it up in the in-vm decorator instead of here but with prompt user as the
- * default for when the in-vm decorator is not used?
- */
-public class CauseFailureDecorator extends WrappedSuiteTestDecorator
-{
- /** The test suite to run. */
- private Test test;
-
- /**
- * Creates a wrapped test test decorator from another one.
- *
- * @param test The test test.
- */
- public CauseFailureDecorator(WrappedSuiteTestDecorator test)
- {
- super(test);
- this.test = test;
- }
-
- /**
- * Runs the tests with a LocalAMQPCircuitFactory. Only tests that extend FrameworkBaseCase are decorated.
- *
- * @param testResult The the results object to monitor the test results with.
- */
- public void run(TestResult testResult)
- {
- for (Test test : getAllUnderlyingTests())
- {
- if (test instanceof BrokerLifecycleAware)
- {
- BrokerLifecycleAware failureTest = (BrokerLifecycleAware) test;
- failureTest.setFailureMechanism(new CauseFailureUserPrompt());
- }
- }
-
- // Run the test.
- test.run(testResult);
- }
-
- /**
- * Prints the name of the test for debugging purposes.
- *
- * @return The name of the test.
- */
- public String toString()
- {
- return "CauseFailureDecorator: [test = \"" + test + "\"]";
- }
-}
+/* + * + * 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.framework.qpid; + +import junit.framework.Test; +import junit.framework.TestResult; + +import org.apache.qpid.test.framework.BrokerLifecycleAware; +import org.apache.qpid.test.framework.CauseFailureUserPrompt; + +import org.apache.qpid.junit.extensions.WrappedSuiteTestDecorator; + +/** + * CauseFailureDecorator applies decorations to {@link BrokerLifecycleAware} tests, so that they may use different failure + * mechanisms. It is capable of detecting when a test case uses in-vm brokers, and setting up an automatic failure + * for those tests, so that the current live broker can be shut-down by test cases. For external brokers, automatic + * failure could be implemented, for example by having a kill script. At the moment this sets up the failure to prompt + * a user interactively to cause a failure, using {@link CauseFailureUserPrompt}. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Setup automatic failures for in-vm brokers. <td> {@link CauseFailureInVM} + * <tr><td> Setup user generated failures for external brokers. <td> {@link CauseFailureUserPrompt}. + * <tr><td> + * </table> + * + * @todo Slight problem in that CauseFailureInVM is Qpid specific, whereas CauseFailureUserPrompt is not. Would like the + * failure decorator to be non-qpid specific so that it can test failure of any JMS implementation too. Either pass + * in class name of failure mechanism, set it up in the in-vm decorator instead of here but with prompt user as the + * default for when the in-vm decorator is not used? + */ +public class CauseFailureDecorator extends WrappedSuiteTestDecorator +{ + /** The test suite to run. */ + private Test test; + + /** + * Creates a wrapped test test decorator from another one. + * + * @param test The test test. + */ + public CauseFailureDecorator(WrappedSuiteTestDecorator test) + { + super(test); + this.test = test; + } + + /** + * Runs the tests with a LocalAMQPCircuitFactory. Only tests that extend FrameworkBaseCase are decorated. + * + * @param testResult The the results object to monitor the test results with. + */ + public void run(TestResult testResult) + { + for (Test test : getAllUnderlyingTests()) + { + if (test instanceof BrokerLifecycleAware) + { + BrokerLifecycleAware failureTest = (BrokerLifecycleAware) test; + failureTest.setFailureMechanism(new CauseFailureUserPrompt()); + } + } + + // Run the test. + test.run(testResult); + } + + /** + * Prints the name of the test for debugging purposes. + * + * @return The name of the test. + */ + public String toString() + { + return "CauseFailureDecorator: [test = \"" + test + "\"]"; + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureInVM.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureInVM.java index b63ac43601..3e03ad0872 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureInVM.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureInVM.java @@ -1,70 +1,70 @@ -/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.test.framework.qpid;
-
-import org.apache.qpid.client.transport.TransportConnection;
-import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.test.framework.CauseFailure;
-import org.apache.qpid.test.framework.BrokerLifecycleAware;
-
-/**
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Cause messaging broker failure on the active in-vm broker.
- * <td> {@link TransportConnection}, {@link ApplicationRegistry}
- * </table>
- */
-public class CauseFailureInVM implements CauseFailure
-{
- /** Holds the in-vm broker instrumented test case to create failures for. */
- private BrokerLifecycleAware inVMTest;
-
- /**
- * Creates an automated failure mechanism for testing against in-vm brokers. The test to create the mechanism
- * for is specified, and as this failure is for in-vm brokers, the test must be {@link org.apache.qpid.test.framework.BrokerLifecycleAware}. The test
- * must also report that it is currently being run against an in-vm broker, and it is a runtime error if it is not,
- * as the creator of this failure mechanism should already have checked that it is.
- *
- * @param inVMTest The test case to create an automated failure mechanism for.
- */
- public CauseFailureInVM(BrokerLifecycleAware inVMTest)
- {
- // Check that the test is really using in-vm brokers.
- if (!inVMTest.usingInVmBroker())
- {
- throw new RuntimeException(
- "Cannot create in-vm broker failure mechanism for a test that is not using in-vm brokers.");
- }
-
- this.inVMTest = inVMTest;
- }
-
- /**
- * Causes the active message broker to fail.
- */
- public void causeFailure()
- {
- int liveBroker = inVMTest.getLiveBroker();
-
- TransportConnection.killVMBroker(liveBroker);
- ApplicationRegistry.remove(liveBroker);
- }
-}
+/* + * + * 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.framework.qpid; + +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.test.framework.CauseFailure; +import org.apache.qpid.test.framework.BrokerLifecycleAware; + +/** + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Cause messaging broker failure on the active in-vm broker. + * <td> {@link TransportConnection}, {@link ApplicationRegistry} + * </table> + */ +public class CauseFailureInVM implements CauseFailure +{ + /** Holds the in-vm broker instrumented test case to create failures for. */ + private BrokerLifecycleAware inVMTest; + + /** + * Creates an automated failure mechanism for testing against in-vm brokers. The test to create the mechanism + * for is specified, and as this failure is for in-vm brokers, the test must be {@link org.apache.qpid.test.framework.BrokerLifecycleAware}. The test + * must also report that it is currently being run against an in-vm broker, and it is a runtime error if it is not, + * as the creator of this failure mechanism should already have checked that it is. + * + * @param inVMTest The test case to create an automated failure mechanism for. + */ + public CauseFailureInVM(BrokerLifecycleAware inVMTest) + { + // Check that the test is really using in-vm brokers. + if (!inVMTest.usingInVmBroker()) + { + throw new RuntimeException( + "Cannot create in-vm broker failure mechanism for a test that is not using in-vm brokers."); + } + + this.inVMTest = inVMTest; + } + + /** + * Causes the active message broker to fail. + */ + public void causeFailure() + { + int liveBroker = inVMTest.getLiveBroker(); + + TransportConnection.killVMBroker(liveBroker); + ApplicationRegistry.remove(liveBroker); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java index bcf052ea06..e0fddb10b7 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java @@ -1,135 +1,135 @@ -/*
- *
- * 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.framework.qpid;
-
-import junit.framework.Test;
-import junit.framework.TestResult;
-
-import org.apache.qpid.client.transport.TransportConnection;
-import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException;
-import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.test.framework.BrokerLifecycleAware;
-import org.apache.qpid.test.framework.FrameworkBaseCase;
-
-import org.apache.qpid.junit.extensions.SetupTaskAware;
-import org.apache.qpid.junit.extensions.WrappedSuiteTestDecorator;
-
-/**
- * InVMBrokerDecorator is a test decorator, that is activated when running tests against an in-vm broker only. Its
- * purpose is to automatically create, and close and delete an in-vm broker, during the set-up and tear-down of
- * each test case. This decorator may only be used in conjunction with tests that extend {@link FrameworkBaseCase}.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Create/Destroy an in-vm broker on every test run.
- * </table>
- *
- * @todo May need to add a more fine grained injection point for the in-vm broker management, as this acts at the
- * suite level, rather than the individual test level.
- *
- * @todo Management of in-vm brokers for failure testing. Failure test setups may need to set their connection url to
- * use multiple broker (vm://:1;vm://:2), with fail-over between them. There is round-robin fail-over, but also
- * retry? A test case using an in-vm broker needs to record which one it is using, so that it can be
- * killed/restarted.
- */
-public class InVMBrokerDecorator extends WrappedSuiteTestDecorator
-{
- /** The test suite to run. */
- private Test test;
-
- /**
- * Creates a wrapped test suite decorator from another one.
- *
- * @param test The test suite.
- */
- public InVMBrokerDecorator(WrappedSuiteTestDecorator test)
- {
- super(test);
- this.test = test;
- }
-
- /**
- * Runs the tests with in-vm broker creation and clean-up added to the tests task stack.
- *
- * @param testResult The the results object to monitor the test results with.
- */
- public void run(TestResult testResult)
- {
- for (Test test : getAllUnderlyingTests())
- {
- // Check that the test to have an in-vm broker setup/teardown task added to it, is actually a framework
- // test that can handle setup tasks.
- if ((test instanceof SetupTaskAware))
- {
- SetupTaskAware frameworkTest = (SetupTaskAware) test;
-
- frameworkTest.chainSetupTask(new Runnable()
- {
- public void run()
- {
- // Ensure that the in-vm broker is created.
- try
- {
- TransportConnection.createVMBroker(1);
- }
- catch (AMQVMBrokerCreationException e)
- {
- throw new RuntimeException("In-VM broker creation failed: " + e.getMessage(), e);
- }
- }
- });
-
- frameworkTest.chainTearDownTask(new Runnable()
- {
- public void run()
- {
- // Ensure that the in-vm broker is cleaned up so that the next test starts afresh.
- TransportConnection.killVMBroker(1);
- ApplicationRegistry.remove(1);
- }
- });
-
- // Check if the test is aware whether or not it can control the broker life cycle, and if so provide
- // additional instrumentation for it to control the in-vm broker through.
- if (test instanceof BrokerLifecycleAware)
- {
- BrokerLifecycleAware inVMTest = (BrokerLifecycleAware) test;
- inVMTest.setInVmBrokers();
- inVMTest.setLiveBroker(1);
- inVMTest.setFailureMechanism(new CauseFailureInVM(inVMTest));
- }
- }
- }
-
- // Run the test.
- test.run(testResult);
- }
-
- /**
- * Prints the name of the test for debugging purposes.
- *
- * @return The name of the test.
- */
- public String toString()
- {
- return "InVMBrokerDecorator: [test = " + test + "]";
- }
-}
+/* + * + * 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.framework.qpid; + +import junit.framework.Test; +import junit.framework.TestResult; + +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.test.framework.BrokerLifecycleAware; +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import org.apache.qpid.junit.extensions.SetupTaskAware; +import org.apache.qpid.junit.extensions.WrappedSuiteTestDecorator; + +/** + * InVMBrokerDecorator is a test decorator, that is activated when running tests against an in-vm broker only. Its + * purpose is to automatically create, and close and delete an in-vm broker, during the set-up and tear-down of + * each test case. This decorator may only be used in conjunction with tests that extend {@link FrameworkBaseCase}. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Create/Destroy an in-vm broker on every test run. + * </table> + * + * @todo May need to add a more fine grained injection point for the in-vm broker management, as this acts at the + * suite level, rather than the individual test level. + * + * @todo Management of in-vm brokers for failure testing. Failure test setups may need to set their connection url to + * use multiple broker (vm://:1;vm://:2), with fail-over between them. There is round-robin fail-over, but also + * retry? A test case using an in-vm broker needs to record which one it is using, so that it can be + * killed/restarted. + */ +public class InVMBrokerDecorator extends WrappedSuiteTestDecorator +{ + /** The test suite to run. */ + private Test test; + + /** + * Creates a wrapped test suite decorator from another one. + * + * @param test The test suite. + */ + public InVMBrokerDecorator(WrappedSuiteTestDecorator test) + { + super(test); + this.test = test; + } + + /** + * Runs the tests with in-vm broker creation and clean-up added to the tests task stack. + * + * @param testResult The the results object to monitor the test results with. + */ + public void run(TestResult testResult) + { + for (Test test : getAllUnderlyingTests()) + { + // Check that the test to have an in-vm broker setup/teardown task added to it, is actually a framework + // test that can handle setup tasks. + if ((test instanceof SetupTaskAware)) + { + SetupTaskAware frameworkTest = (SetupTaskAware) test; + + frameworkTest.chainSetupTask(new Runnable() + { + public void run() + { + // Ensure that the in-vm broker is created. + try + { + TransportConnection.createVMBroker(1); + } + catch (AMQVMBrokerCreationException e) + { + throw new RuntimeException("In-VM broker creation failed: " + e.getMessage(), e); + } + } + }); + + frameworkTest.chainTearDownTask(new Runnable() + { + public void run() + { + // Ensure that the in-vm broker is cleaned up so that the next test starts afresh. + TransportConnection.killVMBroker(1); + ApplicationRegistry.remove(1); + } + }); + + // Check if the test is aware whether or not it can control the broker life cycle, and if so provide + // additional instrumentation for it to control the in-vm broker through. + if (test instanceof BrokerLifecycleAware) + { + BrokerLifecycleAware inVMTest = (BrokerLifecycleAware) test; + inVMTest.setInVmBrokers(); + inVMTest.setLiveBroker(1); + inVMTest.setFailureMechanism(new CauseFailureInVM(inVMTest)); + } + } + } + + // Run the test. + test.run(testResult); + } + + /** + * Prints the name of the test for debugging purposes. + * + * @return The name of the test. + */ + public String toString() + { + return "InVMBrokerDecorator: [test = " + test + "]"; + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/FailoverTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/FailoverTest.java index e7d874ffa9..5f41a07949 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/FailoverTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/FailoverTest.java @@ -1,119 +1,119 @@ -/*
- *
- * 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.testcases;
-
-import org.apache.qpid.test.framework.*;
-import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*;
-import org.apache.qpid.test.framework.localcircuit.LocalCircuitImpl;
-import org.apache.qpid.test.framework.sequencers.CircuitFactory;
-
-import javax.jms.JMSException;
-import javax.jms.MessageProducer;
-import javax.jms.Session;
-
-/**
- * FailoverTest provides testing of fail-over over a local-circuit implementation. The circuit being tested may be
- * against an in-vm broker or against an external broker, with the failure mechanism abstracted out of the test case.
- * Automatic failures can be simulated against an in-vm broker. Currently the test must interact with the user to
- * simulate failures on an external broker.
- *
- * Things to test:
- * In tx, failure duing tx causes tx to error on subsequent sends/receives or commits/rollbacks.
- * Outside of tx, reconnection allows msg flow to continue but there may be loss.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td>
- * </table>
- *
- * @todo This test is designed to be run over a local circuit only. For in-vm using automatic failures, for external
- * brokers by prompting the user (or maybe using a script). Enforce the local-circuit only nature of the tests as
- * well as thinking about how other local-circuit tests might be implemented. For example, could add a method
- * to the framework base case for local only tests to call, that allows them access to the local-circuit
- * implementation and so on.
- *
- * @todo More. Need to really expand the set of fail-over tests.
- */
-public class FailoverTest extends FrameworkBaseCase
-{
- /* Used for debugging purposes. */
- // private static final Logger log = Logger.getLogger(FailoverTest.class);
-
- /**
- * Creates a new test case with the specified name.
- *
- * @param name The test case name.
- */
- public FailoverTest(String name)
- {
- super(name);
- }
-
- /**
- * Checks that all messages sent within a transaction are receieved despite a fail-over occuring outside of
- * the transaction.
- *
- * @throws JMSException Allowed to fall through and fail test.
- */
- public void testTxP2PFailover() throws JMSException
- {
- // Set up the test properties to match the test cases requirements.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true);
- testProps.setProperty(ACK_MODE_PROPNAME, Session.AUTO_ACKNOWLEDGE);
- testProps.setProperty(PUBSUB_PROPNAME, false);
-
- // MessagingTestConfigProperties props = this.getTestParameters();
-
- // Create the test circuit from the test configuration parameters.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- // Create an assertion that all messages are received.
- Assertion allMessagesReceived = testCircuit.getReceiver().allMessagesReceivedAssertion(testProps);
-
- // This test case assumes it is using a local circuit.
- LocalCircuitImpl localCircuit = (LocalCircuitImpl) testCircuit;
-
- Session producerSession = localCircuit.getLocalPublisherCircuitEnd().getSession();
- MessageProducer producer = localCircuit.getLocalPublisherCircuitEnd().getProducer();
- // MessageConsumer consumer = localCircuit.getLocalReceiverCircuitEnd().getConsumer();
-
- // Send some test messages.
- for (int i = 0; i < 100; i++)
- {
- producer.send(TestUtils.createTestMessageOfSize(producerSession, 10));
- producerSession.commit();
-
- // Cause a failover.
- if (i == 50)
- {
- failureMechanism.causeFailure();
- }
-
- // Wait for the reconnection to complete.
- }
-
- // Check that trying to send within the original transaction fails.
-
- // Check that all messages sent were received.
- assertTrue("All messages sent were not received back again.", allMessagesReceived.apply());
- }
-}
+/* + * + * 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.testcases; + +import org.apache.qpid.test.framework.*; +import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*; +import org.apache.qpid.test.framework.localcircuit.LocalCircuitImpl; +import org.apache.qpid.test.framework.sequencers.CircuitFactory; + +import javax.jms.JMSException; +import javax.jms.MessageProducer; +import javax.jms.Session; + +/** + * FailoverTest provides testing of fail-over over a local-circuit implementation. The circuit being tested may be + * against an in-vm broker or against an external broker, with the failure mechanism abstracted out of the test case. + * Automatic failures can be simulated against an in-vm broker. Currently the test must interact with the user to + * simulate failures on an external broker. + * + * Things to test: + * In tx, failure duing tx causes tx to error on subsequent sends/receives or commits/rollbacks. + * Outside of tx, reconnection allows msg flow to continue but there may be loss. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> + * </table> + * + * @todo This test is designed to be run over a local circuit only. For in-vm using automatic failures, for external + * brokers by prompting the user (or maybe using a script). Enforce the local-circuit only nature of the tests as + * well as thinking about how other local-circuit tests might be implemented. For example, could add a method + * to the framework base case for local only tests to call, that allows them access to the local-circuit + * implementation and so on. + * + * @todo More. Need to really expand the set of fail-over tests. + */ +public class FailoverTest extends FrameworkBaseCase +{ + /* Used for debugging purposes. */ + // private static final Logger log = Logger.getLogger(FailoverTest.class); + + /** + * Creates a new test case with the specified name. + * + * @param name The test case name. + */ + public FailoverTest(String name) + { + super(name); + } + + /** + * Checks that all messages sent within a transaction are receieved despite a fail-over occuring outside of + * the transaction. + * + * @throws JMSException Allowed to fall through and fail test. + */ + public void testTxP2PFailover() throws JMSException + { + // Set up the test properties to match the test cases requirements. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(ACK_MODE_PROPNAME, Session.AUTO_ACKNOWLEDGE); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // MessagingTestConfigProperties props = this.getTestParameters(); + + // Create the test circuit from the test configuration parameters. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Create an assertion that all messages are received. + Assertion allMessagesReceived = testCircuit.getReceiver().allMessagesReceivedAssertion(testProps); + + // This test case assumes it is using a local circuit. + LocalCircuitImpl localCircuit = (LocalCircuitImpl) testCircuit; + + Session producerSession = localCircuit.getLocalPublisherCircuitEnd().getSession(); + MessageProducer producer = localCircuit.getLocalPublisherCircuitEnd().getProducer(); + // MessageConsumer consumer = localCircuit.getLocalReceiverCircuitEnd().getConsumer(); + + // Send some test messages. + for (int i = 0; i < 100; i++) + { + producer.send(TestUtils.createTestMessageOfSize(producerSession, 10)); + producerSession.commit(); + + // Cause a failover. + if (i == 50) + { + failureMechanism.causeFailure(); + } + + // Wait for the reconnection to complete. + } + + // Check that trying to send within the original transaction fails. + + // Check that all messages sent were received. + assertTrue("All messages sent were not received back again.", allMessagesReceived.apply()); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java index 845c3ed9c8..767871e25a 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java @@ -1,303 +1,303 @@ -/*
- *
- * 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.testcases;
-
-import org.apache.qpid.test.framework.AMQPPublisher;
-import org.apache.qpid.test.framework.Circuit;
-import org.apache.qpid.test.framework.FrameworkBaseCase;
-import org.apache.qpid.test.framework.MessagingTestConfigProperties;
-import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*;
-import org.apache.qpid.test.framework.sequencers.CircuitFactory;
-
-import org.apache.qpid.junit.extensions.util.TestContextProperties;
-
-/**
- * ImmediateMessageTest tests for the desired behaviour of immediate messages. Immediate messages are a non-JMS
- * feature. A message may be marked with an immediate delivery flag, which means that a consumer must be connected
- * to receive the message, through a valid route, when it is sent, or when its transaction is committed in the case
- * of transactional messaging. If this is not the case, the broker should return the message with a NO_CONSUMERS code.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Check that an immediate message is sent succesfully not using transactions when a consumer is connected.
- * <tr><td> Check that an immediate message is committed succesfully in a transaction when a consumer is connected.
- * <tr><td> Check that an immediate message results in no consumers code, not using transactions, when a consumer is
- * disconnected.
- * <tr><td> Check that an immediate message results in no consumers code, in a transaction, when a consumer is
- * disconnected.
- * <tr><td> Check that an immediate message results in no route code, not using transactions, when no outgoing route is
- * connected.
- * <tr><td> Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is
- * connected.
- * <tr><td> Check that an immediate message is sent succesfully not using transactions when a consumer is connected.
- * <tr><td> Check that an immediate message is committed succesfully in a transaction when a consumer is connected.
- * <tr><td> Check that an immediate message results in no consumers code, not using transactions, when a consumer is
- * disconnected.
- * <tr><td> Check that an immediate message results in no consumers code, in a transaction, when a consumer is
- * disconnected.
- * <tr><td> Check that an immediate message results in no route code, not using transactions, when no outgoing route is
- * connected.
- * <tr><td> Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is
- * connected.
- * </table>
- *
- * @todo All of these test cases will be generated by a test generator that thoroughly tests all combinations of test
- * circuits.
- */
-public class ImmediateMessageTest extends FrameworkBaseCase
-{
- /**
- * Creates a new test case with the specified name.
- *
- * @param name The test case name.
- */
- public ImmediateMessageTest(String name)
- {
- super(name);
- }
-
- /** Check that an immediate message is sent succesfully not using transactions when a consumer is connected. */
- public void test_QPID_517_ImmediateOkNoTxP2P()
- {
- // Ensure transactional sessions are off.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false);
- testProps.setProperty(PUBSUB_PROPNAME, false);
-
- // Run the default test sequence over the test circuit checking for no errors.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps))));
- }
-
- /** Check that an immediate message is committed succesfully in a transaction when a consumer is connected. */
- public void test_QPID_517_ImmediateOkTxP2P()
- {
- // Ensure transactional sessions are off.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true);
- testProps.setProperty(PUBSUB_PROPNAME, false);
-
- // Send one message with no errors.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps))));
- }
-
- /** Check that an immediate message results in no consumers code, not using transactions, when a consumer is disconnected. */
- public void test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxP2P()
- {
- // Ensure transactional sessions are off.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false);
- testProps.setProperty(PUBSUB_PROPNAME, false);
-
- // Disconnect the consumer.
- testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false);
-
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- // Send one message and get a linked no consumers exception.
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps))));
- }
-
- /** Check that an immediate message results in no consumers code, in a transaction, when a consumer is disconnected. */
- public void test_QPID_517_ImmediateFailsConsumerDisconnectedTxP2P()
- {
- // Ensure transactional sessions are on.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true);
- testProps.setProperty(PUBSUB_PROPNAME, false);
-
- // Disconnect the consumer.
- testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false);
-
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- // Send one message and get a linked no consumers exception.
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps))));
- }
-
- /** Check that an immediate message results in no route code, not using transactions, when no outgoing route is connected. */
- public void test_QPID_517_ImmediateFailsNoRouteNoTxP2P()
- {
- // Ensure transactional sessions are off.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false);
- testProps.setProperty(PUBSUB_PROPNAME, false);
-
- // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to
- // collect its messages).
- testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false);
-
- // Send one message and get a linked no route exception.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps))));
- }
-
- /** Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is connected. */
- public void test_QPID_517_ImmediateFailsNoRouteTxP2P()
- {
- // Ensure transactional sessions are on.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true);
- testProps.setProperty(PUBSUB_PROPNAME, false);
-
- // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to
- // collect its messages).
- testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false);
-
- // Send one message and get a linked no route exception.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps))));
- }
-
- /** Check that an immediate message is sent succesfully not using transactions when a consumer is connected. */
- public void test_QPID_517_ImmediateOkNoTxPubSub()
- {
- // Ensure transactional sessions are off.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false);
- testProps.setProperty(PUBSUB_PROPNAME, true);
-
- // Send one message with no errors.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps))));
- }
-
- /** Check that an immediate message is committed succesfully in a transaction when a consumer is connected. */
- public void test_QPID_517_ImmediateOkTxPubSub()
- {
- // Ensure transactional sessions are off.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true);
- testProps.setProperty(PUBSUB_PROPNAME, true);
-
- // Send one message with no errors.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps))));
- }
-
- /** Check that an immediate message results in no consumers code, not using transactions, when a consumer is disconnected. */
- public void test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxPubSub()
- {
- // Ensure transactional sessions are off.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false);
- testProps.setProperty(PUBSUB_PROPNAME, true);
-
- // Use durable subscriptions, so that the route remains open with no subscribers.
- testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true);
-
- // Disconnect the consumer.
- testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false);
-
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- // Send one message and get a linked no consumers exception.
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps))));
- }
-
- /** Check that an immediate message results in no consumers code, in a transaction, when a consumer is disconnected. */
- public void test_QPID_517_ImmediateFailsConsumerDisconnectedTxPubSub()
- {
- // Ensure transactional sessions are on.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true);
- testProps.setProperty(PUBSUB_PROPNAME, true);
-
- // Use durable subscriptions, so that the route remains open with no subscribers.
- testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true);
-
- // Disconnect the consumer.
- testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false);
-
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- // Send one message and get a linked no consumers exception.
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps))));
- }
-
- /** Check that an immediate message results in no route code, not using transactions, when no outgoing route is connected. */
- public void test_QPID_517_ImmediateFailsNoRouteNoTxPubSub()
- {
- // Ensure transactional sessions are off.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false);
- testProps.setProperty(PUBSUB_PROPNAME, true);
-
- // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to
- // collect its messages).
- testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false);
-
- // Send one message and get a linked no route exception.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps))));
- }
-
- /** Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is connected. */
- public void test_QPID_517_ImmediateFailsNoRouteTxPubSub()
- {
- // Ensure transactional sessions are on.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true);
- testProps.setProperty(PUBSUB_PROPNAME, true);
-
- // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to
- // collect its messages).
- testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false);
-
- // Send one message and get a linked no route exception.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps))));
- }
-
- protected void setUp() throws Exception
- {
- super.setUp();
-
- testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults);
-
- /** All these tests should have the immediate flag on. */
- testProps.setProperty(IMMEDIATE_PROPNAME, true);
- testProps.setProperty(MANDATORY_PROPNAME, false);
-
- /** Bind the receivers consumer by default. */
- testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, true);
- testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, true);
- }
-}
+/* + * + * 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.testcases; + +import org.apache.qpid.test.framework.AMQPPublisher; +import org.apache.qpid.test.framework.Circuit; +import org.apache.qpid.test.framework.FrameworkBaseCase; +import org.apache.qpid.test.framework.MessagingTestConfigProperties; +import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*; +import org.apache.qpid.test.framework.sequencers.CircuitFactory; + +import org.apache.qpid.junit.extensions.util.TestContextProperties; + +/** + * ImmediateMessageTest tests for the desired behaviour of immediate messages. Immediate messages are a non-JMS + * feature. A message may be marked with an immediate delivery flag, which means that a consumer must be connected + * to receive the message, through a valid route, when it is sent, or when its transaction is committed in the case + * of transactional messaging. If this is not the case, the broker should return the message with a NO_CONSUMERS code. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Check that an immediate message is sent succesfully not using transactions when a consumer is connected. + * <tr><td> Check that an immediate message is committed succesfully in a transaction when a consumer is connected. + * <tr><td> Check that an immediate message results in no consumers code, not using transactions, when a consumer is + * disconnected. + * <tr><td> Check that an immediate message results in no consumers code, in a transaction, when a consumer is + * disconnected. + * <tr><td> Check that an immediate message results in no route code, not using transactions, when no outgoing route is + * connected. + * <tr><td> Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is + * connected. + * <tr><td> Check that an immediate message is sent succesfully not using transactions when a consumer is connected. + * <tr><td> Check that an immediate message is committed succesfully in a transaction when a consumer is connected. + * <tr><td> Check that an immediate message results in no consumers code, not using transactions, when a consumer is + * disconnected. + * <tr><td> Check that an immediate message results in no consumers code, in a transaction, when a consumer is + * disconnected. + * <tr><td> Check that an immediate message results in no route code, not using transactions, when no outgoing route is + * connected. + * <tr><td> Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is + * connected. + * </table> + * + * @todo All of these test cases will be generated by a test generator that thoroughly tests all combinations of test + * circuits. + */ +public class ImmediateMessageTest extends FrameworkBaseCase +{ + /** + * Creates a new test case with the specified name. + * + * @param name The test case name. + */ + public ImmediateMessageTest(String name) + { + super(name); + } + + /** Check that an immediate message is sent succesfully not using transactions when a consumer is connected. */ + public void test_QPID_517_ImmediateOkNoTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps)))); + } + + /** Check that an immediate message is committed succesfully in a transaction when a consumer is connected. */ + public void test_QPID_517_ImmediateOkTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Send one message with no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps)))); + } + + /** Check that an immediate message results in no consumers code, not using transactions, when a consumer is disconnected. */ + public void test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message and get a linked no consumers exception. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps)))); + } + + /** Check that an immediate message results in no consumers code, in a transaction, when a consumer is disconnected. */ + public void test_QPID_517_ImmediateFailsConsumerDisconnectedTxP2P() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message and get a linked no consumers exception. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps)))); + } + + /** Check that an immediate message results in no route code, not using transactions, when no outgoing route is connected. */ + public void test_QPID_517_ImmediateFailsNoRouteNoTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + /** Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is connected. */ + public void test_QPID_517_ImmediateFailsNoRouteTxP2P() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + /** Check that an immediate message is sent succesfully not using transactions when a consumer is connected. */ + public void test_QPID_517_ImmediateOkNoTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Send one message with no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** Check that an immediate message is committed succesfully in a transaction when a consumer is connected. */ + public void test_QPID_517_ImmediateOkTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Send one message with no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** Check that an immediate message results in no consumers code, not using transactions, when a consumer is disconnected. */ + public void test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Use durable subscriptions, so that the route remains open with no subscribers. + testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message and get a linked no consumers exception. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps)))); + } + + /** Check that an immediate message results in no consumers code, in a transaction, when a consumer is disconnected. */ + public void test_QPID_517_ImmediateFailsConsumerDisconnectedTxPubSub() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Use durable subscriptions, so that the route remains open with no subscribers. + testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message and get a linked no consumers exception. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps)))); + } + + /** Check that an immediate message results in no route code, not using transactions, when no outgoing route is connected. */ + public void test_QPID_517_ImmediateFailsNoRouteNoTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + /** Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is connected. */ + public void test_QPID_517_ImmediateFailsNoRouteTxPubSub() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + protected void setUp() throws Exception + { + super.setUp(); + + testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + + /** All these tests should have the immediate flag on. */ + testProps.setProperty(IMMEDIATE_PROPNAME, true); + testProps.setProperty(MANDATORY_PROPNAME, false); + + /** Bind the receivers consumer by default. */ + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, true); + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, true); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java index 066b4e24ba..d46a866b93 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java @@ -1,321 +1,321 @@ -/*
- *
- * 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.testcases;
-
-import org.apache.qpid.test.framework.AMQPPublisher;
-import org.apache.qpid.test.framework.Circuit;
-import org.apache.qpid.test.framework.FrameworkBaseCase;
-import org.apache.qpid.test.framework.MessagingTestConfigProperties;
-import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*;
-import org.apache.qpid.test.framework.sequencers.CircuitFactory;
-
-import org.apache.qpid.junit.extensions.util.ParsedProperties;
-import org.apache.qpid.junit.extensions.util.TestContextProperties;
-
-/**
- * MandatoryMessageTest tests for the desired behaviour of mandatory messages. Mandatory messages are a non-JMS
- * feature. A message may be marked with a mandatory delivery flag, which means that a valid route for the message
- * must exist, when it is sent, or when its transaction is committed in the case of transactional messaging. If this
- * is not the case, the broker should return the message with a NO_CONSUMERS code.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Check that an mandatory message is sent succesfully not using transactions when a consumer is connected.
- * <tr><td> Check that an mandatory message is committed succesfully in a transaction when a consumer is connected.
- * <tr><td> Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected
- * but the route exists.
- * <tr><td> Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but
- * the route exists.
- * <tr><td> Check that an mandatory message results in no route code, not using transactions, when no consumer is
- * connected.
- * <tr><td> Check that an mandatory message results in no route code, upon transaction commit, when a consumer is
- * connected.
- * <tr><td> Check that an mandatory message is sent succesfully not using transactions when a consumer is connected.
- * <tr><td> Check that an mandatory message is committed succesfully in a transaction when a consumer is connected.
- * <tr><td> Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected
- * but the route exists.
- * <tr><td> Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but
- * the route exists.
- * <tr><td> Check that an mandatory message results in no route code, not using transactions, when no consumer is
- * connected.
- * <tr><td> Check that an mandatory message results in no route code, upon transaction commit, when a consumer is
- * connected.
- * </table>
- *
- * @todo All of these test cases will be generated by a test generator that thoroughly tests all combinations of test
- * circuits.
- */
-public class MandatoryMessageTest extends FrameworkBaseCase
-{
- /** Used to read the tests configurable properties through. */
- ParsedProperties testProps;
-
- /**
- * Creates a new test case with the specified name.
- *
- * @param name The test case name.
- */
- public MandatoryMessageTest(String name)
- {
- super(name);
- }
-
- /** Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. */
- public void test_QPID_508_MandatoryOkNoTxP2P()
- {
- // Ensure transactional sessions are off.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false);
- testProps.setProperty(PUBSUB_PROPNAME, false);
-
- // Run the default test sequence over the test circuit checking for no errors.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps))));
- }
-
- /** Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. */
- public void test_QPID_508_MandatoryOkTxP2P()
- {
- // Ensure transactional sessions are off.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true);
- testProps.setProperty(PUBSUB_PROPNAME, false);
-
- // Run the default test sequence over the test circuit checking for no errors.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps))));
- }
-
- /**
- * Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected but
- * the route exists.
- */
- public void test_QPID_517_MandatoryOkConsumerDisconnectedNoTxP2P()
- {
- // Ensure transactional sessions are off.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false);
- testProps.setProperty(PUBSUB_PROPNAME, false);
-
- // Disconnect the consumer.
- testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false);
-
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- // Send one message with no errors.
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps))));
- }
-
- /**
- * Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but
- * the route exists.
- */
- public void test_QPID_517_MandatoryOkConsumerDisconnectedTxP2P()
- {
- // Ensure transactional sessions are on.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true);
- testProps.setProperty(PUBSUB_PROPNAME, false);
-
- // Disconnect the consumer.
- testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false);
-
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- // Send one message with no errors.
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps))));
- }
-
- /** Check that an mandatory message results in no route code, not using transactions, when no consumer is connected. */
- public void test_QPID_508_MandatoryFailsNoRouteNoTxP2P()
- {
- // Ensure transactional sessions are off.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false);
- testProps.setProperty(PUBSUB_PROPNAME, false);
-
- // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to
- // collect its messages).
- testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false);
-
- // Send one message and get a linked no route exception.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps))));
- }
-
- /** Check that an mandatory message results in no route code, upon transaction commit, when a consumer is connected. */
- public void test_QPID_508_MandatoryFailsNoRouteTxP2P()
- {
- // Ensure transactional sessions are on.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true);
- testProps.setProperty(PUBSUB_PROPNAME, false);
-
- // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to
- // collect its messages).
- testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false);
-
- // Send one message and get a linked no route exception.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps))));
- }
-
- /** Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. */
- public void test_QPID_508_MandatoryOkNoTxPubSub()
- {
- // Ensure transactional sessions are off.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false);
- testProps.setProperty(PUBSUB_PROPNAME, true);
-
- // Run the default test sequence over the test circuit checking for no errors.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps))));
- }
-
- /** Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. */
- public void test_QPID_508_MandatoryOkTxPubSub()
- {
- // Ensure transactional sessions are on.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true);
- testProps.setProperty(PUBSUB_PROPNAME, true);
-
- // Run the default test sequence over the test circuit checking for no errors.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps))));
- }
-
- /**
- * Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected but
- * the route exists.
- */
- public void test_QPID_517_MandatoryOkConsumerDisconnectedNoTxPubSub()
- {
- // Ensure transactional sessions are off.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false);
- testProps.setProperty(PUBSUB_PROPNAME, true);
-
- // Use durable subscriptions, so that the route remains open with no subscribers.
- testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true);
-
- // Disconnect the consumer.
- testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false);
-
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- // Send one message with no errors.
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps))));
- }
-
- /**
- * Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but
- * the route exists.
- */
- public void test_QPID_517_MandatoryOkConsumerDisconnectedTxPubSub()
- {
- // Ensure transactional sessions are on.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true);
- testProps.setProperty(PUBSUB_PROPNAME, true);
-
- // Use durable subscriptions, so that the route remains open with no subscribers.
- testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true);
-
- // Disconnect the consumer.
- testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false);
-
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- // Send one message with no errors.
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps))));
- }
-
- /** Check that an mandatory message results in no route code, not using transactions, when no consumer is connected. */
- public void test_QPID_508_MandatoryFailsNoRouteNoTxPubSub()
- {
- // Ensure transactional sessions are off.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false);
- testProps.setProperty(PUBSUB_PROPNAME, true);
-
- // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to
- // collect its messages).
- testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false);
-
- // Send one message and get a linked no route exception.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps))));
- }
-
- /** Check that an mandatory message results in no route code, upon transaction commit, when a consumer is connected. */
- public void test_QPID_508_MandatoryFailsNoRouteTxPubSub()
- {
- // Ensure transactional sessions are on.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true);
- testProps.setProperty(PUBSUB_PROPNAME, true);
-
- // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to
- // collect its messages).
- testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false);
-
- // Send one message and get a linked no route exception.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1,
- assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps))));
- }
-
- protected void setUp() throws Exception
- {
- super.setUp();
-
- testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults);
-
- /** All these tests should have the mandatory flag on. */
- testProps.setProperty(IMMEDIATE_PROPNAME, false);
- testProps.setProperty(MANDATORY_PROPNAME, true);
-
- /** Bind the receivers consumer by default. */
- testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, true);
- testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, true);
- }
-}
+/* + * + * 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.testcases; + +import org.apache.qpid.test.framework.AMQPPublisher; +import org.apache.qpid.test.framework.Circuit; +import org.apache.qpid.test.framework.FrameworkBaseCase; +import org.apache.qpid.test.framework.MessagingTestConfigProperties; +import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*; +import org.apache.qpid.test.framework.sequencers.CircuitFactory; + +import org.apache.qpid.junit.extensions.util.ParsedProperties; +import org.apache.qpid.junit.extensions.util.TestContextProperties; + +/** + * MandatoryMessageTest tests for the desired behaviour of mandatory messages. Mandatory messages are a non-JMS + * feature. A message may be marked with a mandatory delivery flag, which means that a valid route for the message + * must exist, when it is sent, or when its transaction is committed in the case of transactional messaging. If this + * is not the case, the broker should return the message with a NO_CONSUMERS code. + * + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. + * <tr><td> Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. + * <tr><td> Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected + * but the route exists. + * <tr><td> Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but + * the route exists. + * <tr><td> Check that an mandatory message results in no route code, not using transactions, when no consumer is + * connected. + * <tr><td> Check that an mandatory message results in no route code, upon transaction commit, when a consumer is + * connected. + * <tr><td> Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. + * <tr><td> Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. + * <tr><td> Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected + * but the route exists. + * <tr><td> Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but + * the route exists. + * <tr><td> Check that an mandatory message results in no route code, not using transactions, when no consumer is + * connected. + * <tr><td> Check that an mandatory message results in no route code, upon transaction commit, when a consumer is + * connected. + * </table> + * + * @todo All of these test cases will be generated by a test generator that thoroughly tests all combinations of test + * circuits. + */ +public class MandatoryMessageTest extends FrameworkBaseCase +{ + /** Used to read the tests configurable properties through. */ + ParsedProperties testProps; + + /** + * Creates a new test case with the specified name. + * + * @param name The test case name. + */ + public MandatoryMessageTest(String name) + { + super(name); + } + + /** Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. */ + public void test_QPID_508_MandatoryOkNoTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. */ + public void test_QPID_508_MandatoryOkTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** + * Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected but + * the route exists. + */ + public void test_QPID_517_MandatoryOkConsumerDisconnectedNoTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message with no errors. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** + * Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but + * the route exists. + */ + public void test_QPID_517_MandatoryOkConsumerDisconnectedTxP2P() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message with no errors. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** Check that an mandatory message results in no route code, not using transactions, when no consumer is connected. */ + public void test_QPID_508_MandatoryFailsNoRouteNoTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + /** Check that an mandatory message results in no route code, upon transaction commit, when a consumer is connected. */ + public void test_QPID_508_MandatoryFailsNoRouteTxP2P() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + /** Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. */ + public void test_QPID_508_MandatoryOkNoTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. */ + public void test_QPID_508_MandatoryOkTxPubSub() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** + * Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected but + * the route exists. + */ + public void test_QPID_517_MandatoryOkConsumerDisconnectedNoTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Use durable subscriptions, so that the route remains open with no subscribers. + testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message with no errors. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** + * Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but + * the route exists. + */ + public void test_QPID_517_MandatoryOkConsumerDisconnectedTxPubSub() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Use durable subscriptions, so that the route remains open with no subscribers. + testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message with no errors. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** Check that an mandatory message results in no route code, not using transactions, when no consumer is connected. */ + public void test_QPID_508_MandatoryFailsNoRouteNoTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + /** Check that an mandatory message results in no route code, upon transaction commit, when a consumer is connected. */ + public void test_QPID_508_MandatoryFailsNoRouteTxPubSub() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + protected void setUp() throws Exception + { + super.setUp(); + + testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + + /** All these tests should have the mandatory flag on. */ + testProps.setProperty(IMMEDIATE_PROPNAME, false); + testProps.setProperty(MANDATORY_PROPNAME, true); + + /** Bind the receivers consumer by default. */ + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, true); + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, true); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java index f39d22bc67..c1a484b2aa 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java @@ -1,132 +1,132 @@ -/*
- *
- * 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.testcases;
-
-import org.apache.qpid.test.framework.Circuit;
-import org.apache.qpid.test.framework.FrameworkBaseCase;
-import org.apache.qpid.test.framework.MessagingTestConfigProperties;
-import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*;
-import org.apache.qpid.test.framework.sequencers.CircuitFactory;
-
-import org.apache.qpid.junit.extensions.util.ParsedProperties;
-import org.apache.qpid.junit.extensions.util.TestContextProperties;
-
-/**
- * RollbackTest tests the rollback ability of transactional messaging.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Check messages sent but rolled back are never received.
- * <tr><td> Check messages received but rolled back are redelivered on subsequent receives.
- * <tr><td> Attempting to rollback outside of a transaction results in an IllegalStateException.
- * </table>
- */
-public class RollbackTest extends FrameworkBaseCase
-{
- /** Used to read the tests configurable properties through. */
- ParsedProperties testProps;
-
- /**
- * Creates a new test case with the specified name.
- *
- * @param name The test case name.
- */
- public RollbackTest(String name)
- {
- super(name);
- }
-
- /** Check messages sent but rolled back are never received. */
- public void testRolledbackMessageNotDelivered()
- {
- // Ensure transactional sessions are on.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true);
- testProps.setProperty(ROLLBACK_PUBLISHER_PROPNAME, true);
-
- // Run the default test sequence over the test circuit checking for no errors.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1,
- assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps),
- testCircuit.getReceiver().noMessagesReceivedAssertion(testProps))));
- }
-
- /** Check messages received but rolled back are redelivered on subsequent receives. */
- public void testRolledbackMessagesSubsequentlyReceived()
- {
- // Ensure transactional sessions are on.
- testProps.setProperty(TRANSACTED_RECEIVER_PROPNAME, true);
- testProps.setProperty(ROLLBACK_RECEIVER_PROPNAME, true);
-
- // Run the default test sequence over the test circuit checking for no errors.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1,
- assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps),
- testCircuit.getReceiver().allMessagesReceivedAssertion(testProps))));
- }
-
- /** Attempting to rollback outside of a transaction results in an IllegalStateException. */
- public void testRollbackUnavailableOutsideTransactionPublisher()
- {
- // Ensure transactional sessions are on.
- testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false);
- testProps.setProperty(ROLLBACK_PUBLISHER_PROPNAME, true);
-
- // Run the default test sequence over the test circuit checking for no errors.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().channelClosedAssertion(testProps))));
- }
-
- /** Attempting to rollback outside of a transaction results in an IllegalStateException. */
- public void testRollbackUnavailableOutsideTransactionReceiver()
- {
- // Ensure transactional sessions are on.
- testProps.setProperty(TRANSACTED_RECEIVER_PROPNAME, false);
- testProps.setProperty(ROLLBACK_RECEIVER_PROPNAME, true);
-
- // Run the default test sequence over the test circuit checking for no errors.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getReceiver().channelClosedAssertion(testProps))));
- }
-
- /**
- * Sets up all tests to have an active outward route and consumer by default.
- *
- * @throws Exception Any exceptions are allowed to fall through and fail the test.
- */
- protected void setUp() throws Exception
- {
- super.setUp();
-
- testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults);
-
- /** Bind the receivers consumer by default. */
- testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, true);
- testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, true);
- }
-}
+/* + * + * 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.testcases; + +import org.apache.qpid.test.framework.Circuit; +import org.apache.qpid.test.framework.FrameworkBaseCase; +import org.apache.qpid.test.framework.MessagingTestConfigProperties; +import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*; +import org.apache.qpid.test.framework.sequencers.CircuitFactory; + +import org.apache.qpid.junit.extensions.util.ParsedProperties; +import org.apache.qpid.junit.extensions.util.TestContextProperties; + +/** + * RollbackTest tests the rollback ability of transactional messaging. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Check messages sent but rolled back are never received. + * <tr><td> Check messages received but rolled back are redelivered on subsequent receives. + * <tr><td> Attempting to rollback outside of a transaction results in an IllegalStateException. + * </table> + */ +public class RollbackTest extends FrameworkBaseCase +{ + /** Used to read the tests configurable properties through. */ + ParsedProperties testProps; + + /** + * Creates a new test case with the specified name. + * + * @param name The test case name. + */ + public RollbackTest(String name) + { + super(name); + } + + /** Check messages sent but rolled back are never received. */ + public void testRolledbackMessageNotDelivered() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(ROLLBACK_PUBLISHER_PROPNAME, true); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps), + testCircuit.getReceiver().noMessagesReceivedAssertion(testProps)))); + } + + /** Check messages received but rolled back are redelivered on subsequent receives. */ + public void testRolledbackMessagesSubsequentlyReceived() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_RECEIVER_PROPNAME, true); + testProps.setProperty(ROLLBACK_RECEIVER_PROPNAME, true); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps), + testCircuit.getReceiver().allMessagesReceivedAssertion(testProps)))); + } + + /** Attempting to rollback outside of a transaction results in an IllegalStateException. */ + public void testRollbackUnavailableOutsideTransactionPublisher() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(ROLLBACK_PUBLISHER_PROPNAME, true); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().channelClosedAssertion(testProps)))); + } + + /** Attempting to rollback outside of a transaction results in an IllegalStateException. */ + public void testRollbackUnavailableOutsideTransactionReceiver() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_RECEIVER_PROPNAME, false); + testProps.setProperty(ROLLBACK_RECEIVER_PROPNAME, true); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getReceiver().channelClosedAssertion(testProps)))); + } + + /** + * Sets up all tests to have an active outward route and consumer by default. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + protected void setUp() throws Exception + { + super.setUp(); + + testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + + /** Bind the receivers consumer by default. */ + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, true); + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, true); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/TTLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/TTLTest.java index f752ccba00..9004cb72df 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/TTLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/TTLTest.java @@ -1,151 +1,151 @@ -/*
- *
- * 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.testcases;
-
-import org.apache.qpid.test.framework.Circuit;
-import org.apache.qpid.test.framework.FrameworkBaseCase;
-import static org.apache.qpid.test.framework.MessagingTestConfigProperties.ACK_MODE_PROPNAME;
-import static org.apache.qpid.test.framework.MessagingTestConfigProperties.PUBSUB_PROPNAME;
-import org.apache.qpid.test.framework.TestUtils;
-import org.apache.qpid.test.framework.localcircuit.LocalCircuitImpl;
-import org.apache.qpid.test.framework.sequencers.CircuitFactory;
-
-import javax.jms.*;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Random;
-
-/**
- * TTLTest checks that time-to-live is applied to messages. The test sends messages with a variety of TTL stamps on them
- * then after a pause attempts to receive those messages. Only messages with a large enough TTL to have survived the pause
- * should be receiveable. This test case also applies an additional assertion against the broker, that the message store
- * is empty at the end of the test.
- *
- * <p/>This test is designed to run over local circuits only, as it must control a timed pause between sending and receiving
- * messages to that TTL can be applied to purge some of the messages.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td>
- * </table>
- *
- * @todo Use an interface or other method to mark this test as local only.
- *
- * @todo Implement the message store assertion for in-vm broker. Could also be done for external broker, for example
- * by using diagnostic exchange.
- *
- * @todo Implement and add a queue depth assertion too. This might already be in another test to copy from.
- *
- * @todo Create variations on test theme, for different ack mode and tx and message sizes etc.
- *
- * @todo Add an allowable margin of error to the test, as ttl may not be precise.
- */
-public class TTLTest extends FrameworkBaseCase
-{
- /**
- * Creates a new test case with the specified name.
- *
- * @param name The test case name.
- */
- public TTLTest(String name)
- {
- super(name);
- }
-
- /**
- * Checks that all messages sent with a TTL shorter than a pause between sending them and attempting to receive them
- * will fail to arrive. Once all messages have been purged by TTL or received, check that they no longer exist on
- * the broker.
- *
- * @throws javax.jms.JMSException Allowed to fall through and fail test.
- */
- public void testTTLP2P() throws JMSException
- {
- String errorMessages = "";
- Random r = new Random();
-
- // Used to accumulate correctly received messages in.
- List<Message> receivedMessages = new LinkedList<Message>();
-
- // Set up the test properties to match the test case requirements.
- testProps.setProperty(ACK_MODE_PROPNAME, Session.AUTO_ACKNOWLEDGE);
- testProps.setProperty(PUBSUB_PROPNAME, false);
-
- // Create the test circuit from the test configuration parameters.
- CircuitFactory circuitFactory = getCircuitFactory();
- Circuit testCircuit = circuitFactory.createCircuit(testProps);
-
- // This test case assumes it is using a local circuit.
- LocalCircuitImpl localCircuit = (LocalCircuitImpl) testCircuit;
-
- Session producerSession = localCircuit.getLocalPublisherCircuitEnd().getSession();
- MessageProducer producer = localCircuit.getLocalPublisherCircuitEnd().getProducer();
- MessageConsumer consumer = localCircuit.getLocalReceiverCircuitEnd().getConsumer();
-
- // Send some tests messages, with random TTLs, some shorter and some longer than the pause time.
- for (int i = 0; i < 100; i++)
- {
- Message testMessage = TestUtils.createTestMessageOfSize(producerSession, 10);
-
- // Set the TTL on the message and record its value in the message headers.
- long ttl = 500 + r.nextInt(1500);
- producer.setTimeToLive(ttl);
- testMessage.setLongProperty("testTTL", ttl);
-
- producer.send(testMessage);
- // producerSession.commit();
- }
-
- // Inject a pause to allow some messages to be purged by TTL.
- TestUtils.pause(1000);
-
- // Attempt to receive back all of the messages, confirming by the message time stamps and TTLs that only
- // those received should have avoided being purged by the TTL.
- boolean timedOut = false;
-
- while (!timedOut)
- {
- Message testMessage = consumer.receive(1000);
-
- long ttl = testMessage.getLongProperty("testTTL");
- long timeStamp = testMessage.getJMSTimestamp();
- long now = System.currentTimeMillis();
-
- if ((timeStamp + ttl) < now)
- {
- errorMessages +=
- "Received message [sent: " + timeStamp + ", ttl: " + ttl + ", received: " + now
- + "] which should have been purged by its TTL.\n";
- }
- /*else
- {
- receivedMessages.add(testMessage);
- }*/
- }
-
- // Check that the queue and message store on the broker are empty.
- // assertTrue("Message store is not empty.", messageStoreEmpty.apply());
- // assertTrue("Queue is not empty.", queueEmpty.apply());
-
- assertTrue(errorMessages, "".equals(errorMessages));
- }
-}
+/* + * + * 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.testcases; + +import org.apache.qpid.test.framework.Circuit; +import org.apache.qpid.test.framework.FrameworkBaseCase; +import static org.apache.qpid.test.framework.MessagingTestConfigProperties.ACK_MODE_PROPNAME; +import static org.apache.qpid.test.framework.MessagingTestConfigProperties.PUBSUB_PROPNAME; +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.localcircuit.LocalCircuitImpl; +import org.apache.qpid.test.framework.sequencers.CircuitFactory; + +import javax.jms.*; + +import java.util.LinkedList; +import java.util.List; +import java.util.Random; + +/** + * TTLTest checks that time-to-live is applied to messages. The test sends messages with a variety of TTL stamps on them + * then after a pause attempts to receive those messages. Only messages with a large enough TTL to have survived the pause + * should be receiveable. This test case also applies an additional assertion against the broker, that the message store + * is empty at the end of the test. + * + * <p/>This test is designed to run over local circuits only, as it must control a timed pause between sending and receiving + * messages to that TTL can be applied to purge some of the messages. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> + * </table> + * + * @todo Use an interface or other method to mark this test as local only. + * + * @todo Implement the message store assertion for in-vm broker. Could also be done for external broker, for example + * by using diagnostic exchange. + * + * @todo Implement and add a queue depth assertion too. This might already be in another test to copy from. + * + * @todo Create variations on test theme, for different ack mode and tx and message sizes etc. + * + * @todo Add an allowable margin of error to the test, as ttl may not be precise. + */ +public class TTLTest extends FrameworkBaseCase +{ + /** + * Creates a new test case with the specified name. + * + * @param name The test case name. + */ + public TTLTest(String name) + { + super(name); + } + + /** + * Checks that all messages sent with a TTL shorter than a pause between sending them and attempting to receive them + * will fail to arrive. Once all messages have been purged by TTL or received, check that they no longer exist on + * the broker. + * + * @throws javax.jms.JMSException Allowed to fall through and fail test. + */ + public void testTTLP2P() throws JMSException + { + String errorMessages = ""; + Random r = new Random(); + + // Used to accumulate correctly received messages in. + List<Message> receivedMessages = new LinkedList<Message>(); + + // Set up the test properties to match the test case requirements. + testProps.setProperty(ACK_MODE_PROPNAME, Session.AUTO_ACKNOWLEDGE); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Create the test circuit from the test configuration parameters. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // This test case assumes it is using a local circuit. + LocalCircuitImpl localCircuit = (LocalCircuitImpl) testCircuit; + + Session producerSession = localCircuit.getLocalPublisherCircuitEnd().getSession(); + MessageProducer producer = localCircuit.getLocalPublisherCircuitEnd().getProducer(); + MessageConsumer consumer = localCircuit.getLocalReceiverCircuitEnd().getConsumer(); + + // Send some tests messages, with random TTLs, some shorter and some longer than the pause time. + for (int i = 0; i < 100; i++) + { + Message testMessage = TestUtils.createTestMessageOfSize(producerSession, 10); + + // Set the TTL on the message and record its value in the message headers. + long ttl = 500 + r.nextInt(1500); + producer.setTimeToLive(ttl); + testMessage.setLongProperty("testTTL", ttl); + + producer.send(testMessage); + // producerSession.commit(); + } + + // Inject a pause to allow some messages to be purged by TTL. + TestUtils.pause(1000); + + // Attempt to receive back all of the messages, confirming by the message time stamps and TTLs that only + // those received should have avoided being purged by the TTL. + boolean timedOut = false; + + while (!timedOut) + { + Message testMessage = consumer.receive(1000); + + long ttl = testMessage.getLongProperty("testTTL"); + long timeStamp = testMessage.getJMSTimestamp(); + long now = System.currentTimeMillis(); + + if ((timeStamp + ttl) < now) + { + errorMessages += + "Received message [sent: " + timeStamp + ", ttl: " + ttl + ", received: " + now + + "] which should have been purged by its TTL.\n"; + } + /*else + { + receivedMessages.add(testMessage); + }*/ + } + + // Check that the queue and message store on the broker are empty. + // assertTrue("Message store is not empty.", messageStoreEmpty.apply()); + // assertTrue("Queue is not empty.", queueEmpty.apply()); + + assertTrue(errorMessages, "".equals(errorMessages)); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/testutil/QpidClientConnectionHelper.java b/qpid/java/systests/src/main/java/org/apache/qpid/testutil/QpidClientConnectionHelper.java index 398e3e7800..c43b65a805 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/testutil/QpidClientConnectionHelper.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/testutil/QpidClientConnectionHelper.java @@ -1,296 +1,296 @@ -/*
- *
- * 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.testutil;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.client.AMQConnection;
-import org.apache.qpid.client.AMQConnectionFactory;
-import org.apache.qpid.client.AMQConnectionURL;
-import org.apache.qpid.client.JMSAMQException;
-import org.apache.qpid.url.URLSyntaxException;
-
-import javax.jms.Connection;
-import javax.jms.DeliveryMode;
-import javax.jms.ExceptionListener;
-import javax.jms.JMSException;
-import javax.jms.Message;
-import javax.jms.MessageConsumer;
-import javax.jms.MessageProducer;
-import javax.jms.Queue;
-import javax.jms.Session;
-import javax.jms.TextMessage;
-
-/**
- * @todo This was originally cut and paste from the client module leading to a duplicate class, then altered very
- * slightly. To avoid the duplicate class the name was altered slightly to have 'Helper' on the end in order
- * to distinguish it from the original. Delete this class and use the original instead, just upgrade it to
- * provide the new features needed.
- */
-public class QpidClientConnectionHelper implements ExceptionListener
-{
-
- private static final Logger _logger = Logger.getLogger(QpidClientConnectionHelper.class);
-
- private boolean transacted = true;
- private int ackMode = Session.CLIENT_ACKNOWLEDGE;
- private Connection connection;
-
- private String virtualHost;
- private String brokerlist;
- private int prefetch;
- protected Session session;
- protected boolean connected;
-
- public QpidClientConnectionHelper(String broker)
- {
- super();
- setVirtualHost("/test");
- setBrokerList(broker);
- setPrefetch(5000);
- }
-
- public void connect() throws JMSException
- {
- if (!connected)
- {
- /*
- * amqp://[user:pass@][clientid]/virtualhost?
- * brokerlist='[transport://]host[:port][?option='value'[&option='value']];'
- * [&failover='method[?option='value'[&option='value']]']
- * [&option='value']"
- */
- String brokerUrl = "amqp://guest:guest@" + virtualHost + "?brokerlist='" + brokerlist + "'";
- try
- {
- AMQConnectionFactory factory = new AMQConnectionFactory(new AMQConnectionURL(brokerUrl));
- _logger.info("connecting to Qpid :" + brokerUrl);
- connection = factory.createConnection();
-
- // register exception listener
- connection.setExceptionListener(this);
-
- session = ((AMQConnection) connection).createSession(transacted, ackMode, prefetch);
-
- _logger.info("starting connection");
- connection.start();
-
- connected = true;
- }
- catch (URLSyntaxException e)
- {
- throw new JMSAMQException("URL syntax error in [" + brokerUrl + "]: " + e.getMessage(), e);
- }
- }
- }
-
- public void disconnect() throws JMSException
- {
- if (connected)
- {
- session.commit();
- session.close();
- connection.close();
- connected = false;
- _logger.info("disconnected");
- }
- }
-
- public void disconnectWithoutCommit() throws JMSException
- {
- if (connected)
- {
- session.close();
- connection.close();
- connected = false;
- _logger.info("disconnected without commit");
- }
- }
-
- public String getBrokerList()
- {
- return brokerlist;
- }
-
- public void setBrokerList(String brokerlist)
- {
- this.brokerlist = brokerlist;
- }
-
- public String getVirtualHost()
- {
- return virtualHost;
- }
-
- public void setVirtualHost(String virtualHost)
- {
- this.virtualHost = virtualHost;
- }
-
- public void setPrefetch(int prefetch)
- {
- this.prefetch = prefetch;
- }
-
- /** override as necessary */
- public void onException(JMSException exception)
- {
- _logger.info("ExceptionListener event: error " + exception.getErrorCode() + ", message: " + exception.getMessage());
- }
-
- public boolean isConnected()
- {
- return connected;
- }
-
- public Session getSession()
- {
- return session;
- }
-
- /**
- * Put a String as a text messages, repeat n times. A null payload will result in a null message.
- *
- * @param queueName The queue name to put to
- * @param payload the content of the payload
- * @param copies the number of messages to put
- *
- * @throws javax.jms.JMSException any exception that occurs
- */
- public void put(String queueName, String payload, int copies, int deliveryMode) throws JMSException
- {
- if (!connected)
- {
- connect();
- }
-
- _logger.info("putting to queue " + queueName);
- Queue queue = session.createQueue(queueName);
-
- final MessageProducer sender = session.createProducer(queue);
-
- sender.setDeliveryMode(deliveryMode);
-
- for (int i = 0; i < copies; i++)
- {
- Message m = session.createTextMessage(payload + i);
- m.setIntProperty("index", i + 1);
- sender.send(m);
- }
-
- session.commit();
- sender.close();
- _logger.info("put " + copies + " copies");
- }
-
- /**
- * GET the top message on a queue. Consumes the message. Accepts timeout value.
- *
- * @param queueName The quename to get from
- * @param readTimeout The timeout to use
- *
- * @return the content of the text message if any
- *
- * @throws javax.jms.JMSException any exception that occured
- */
- public Message getNextMessage(String queueName, long readTimeout) throws JMSException
- {
- if (!connected)
- {
- connect();
- }
-
- Queue queue = session.createQueue(queueName);
-
- final MessageConsumer consumer = session.createConsumer(queue);
-
- Message message = consumer.receive(readTimeout);
- session.commit();
- consumer.close();
-
- Message result;
-
- // all messages we consume should be TextMessages
- if (message instanceof TextMessage)
- {
- result = ((TextMessage) message);
- }
- else if (null == message)
- {
- result = null;
- }
- else
- {
- _logger.info("warning: received non-text message");
- result = message;
- }
-
- return result;
- }
-
- /**
- * GET the top message on a queue. Consumes the message.
- *
- * @param queueName The Queuename to get from
- *
- * @return The string content of the text message, if any received
- *
- * @throws javax.jms.JMSException any exception that occurs
- */
- public Message getNextMessage(String queueName) throws JMSException
- {
- return getNextMessage(queueName, 0);
- }
-
- /**
- * Completely clears a queue. For readTimeout behaviour see Javadocs for javax.jms.MessageConsumer.
- *
- * @param queueName The Queue name to consume from
- * @param readTimeout The timeout for each consume
- *
- * @throws javax.jms.JMSException Any exception that occurs during the consume
- * @throws InterruptedException If the consume thread was interrupted during a consume.
- */
- public void consume(String queueName, int readTimeout) throws JMSException, InterruptedException
- {
- if (!connected)
- {
- connect();
- }
-
- _logger.info("consuming queue " + queueName);
- Queue queue = session.createQueue(queueName);
-
- final MessageConsumer consumer = session.createConsumer(queue);
- int messagesReceived = 0;
-
- _logger.info("consuming...");
- while ((consumer.receive(readTimeout)) != null)
- {
- messagesReceived++;
- }
-
- session.commit();
- consumer.close();
- _logger.info("consumed: " + messagesReceived);
- }
-}
+/* + * + * 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.testutil; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQConnectionFactory; +import org.apache.qpid.client.AMQConnectionURL; +import org.apache.qpid.client.JMSAMQException; +import org.apache.qpid.url.URLSyntaxException; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; + +/** + * @todo This was originally cut and paste from the client module leading to a duplicate class, then altered very + * slightly. To avoid the duplicate class the name was altered slightly to have 'Helper' on the end in order + * to distinguish it from the original. Delete this class and use the original instead, just upgrade it to + * provide the new features needed. + */ +public class QpidClientConnectionHelper implements ExceptionListener +{ + + private static final Logger _logger = Logger.getLogger(QpidClientConnectionHelper.class); + + private boolean transacted = true; + private int ackMode = Session.CLIENT_ACKNOWLEDGE; + private Connection connection; + + private String virtualHost; + private String brokerlist; + private int prefetch; + protected Session session; + protected boolean connected; + + public QpidClientConnectionHelper(String broker) + { + super(); + setVirtualHost("/test"); + setBrokerList(broker); + setPrefetch(5000); + } + + public void connect() throws JMSException + { + if (!connected) + { + /* + * amqp://[user:pass@][clientid]/virtualhost? + * brokerlist='[transport://]host[:port][?option='value'[&option='value']];' + * [&failover='method[?option='value'[&option='value']]'] + * [&option='value']" + */ + String brokerUrl = "amqp://guest:guest@" + virtualHost + "?brokerlist='" + brokerlist + "'"; + try + { + AMQConnectionFactory factory = new AMQConnectionFactory(new AMQConnectionURL(brokerUrl)); + _logger.info("connecting to Qpid :" + brokerUrl); + connection = factory.createConnection(); + + // register exception listener + connection.setExceptionListener(this); + + session = ((AMQConnection) connection).createSession(transacted, ackMode, prefetch); + + _logger.info("starting connection"); + connection.start(); + + connected = true; + } + catch (URLSyntaxException e) + { + throw new JMSAMQException("URL syntax error in [" + brokerUrl + "]: " + e.getMessage(), e); + } + } + } + + public void disconnect() throws JMSException + { + if (connected) + { + session.commit(); + session.close(); + connection.close(); + connected = false; + _logger.info("disconnected"); + } + } + + public void disconnectWithoutCommit() throws JMSException + { + if (connected) + { + session.close(); + connection.close(); + connected = false; + _logger.info("disconnected without commit"); + } + } + + public String getBrokerList() + { + return brokerlist; + } + + public void setBrokerList(String brokerlist) + { + this.brokerlist = brokerlist; + } + + public String getVirtualHost() + { + return virtualHost; + } + + public void setVirtualHost(String virtualHost) + { + this.virtualHost = virtualHost; + } + + public void setPrefetch(int prefetch) + { + this.prefetch = prefetch; + } + + /** override as necessary */ + public void onException(JMSException exception) + { + _logger.info("ExceptionListener event: error " + exception.getErrorCode() + ", message: " + exception.getMessage()); + } + + public boolean isConnected() + { + return connected; + } + + public Session getSession() + { + return session; + } + + /** + * Put a String as a text messages, repeat n times. A null payload will result in a null message. + * + * @param queueName The queue name to put to + * @param payload the content of the payload + * @param copies the number of messages to put + * + * @throws javax.jms.JMSException any exception that occurs + */ + public void put(String queueName, String payload, int copies, int deliveryMode) throws JMSException + { + if (!connected) + { + connect(); + } + + _logger.info("putting to queue " + queueName); + Queue queue = session.createQueue(queueName); + + final MessageProducer sender = session.createProducer(queue); + + sender.setDeliveryMode(deliveryMode); + + for (int i = 0; i < copies; i++) + { + Message m = session.createTextMessage(payload + i); + m.setIntProperty("index", i + 1); + sender.send(m); + } + + session.commit(); + sender.close(); + _logger.info("put " + copies + " copies"); + } + + /** + * GET the top message on a queue. Consumes the message. Accepts timeout value. + * + * @param queueName The quename to get from + * @param readTimeout The timeout to use + * + * @return the content of the text message if any + * + * @throws javax.jms.JMSException any exception that occured + */ + public Message getNextMessage(String queueName, long readTimeout) throws JMSException + { + if (!connected) + { + connect(); + } + + Queue queue = session.createQueue(queueName); + + final MessageConsumer consumer = session.createConsumer(queue); + + Message message = consumer.receive(readTimeout); + session.commit(); + consumer.close(); + + Message result; + + // all messages we consume should be TextMessages + if (message instanceof TextMessage) + { + result = ((TextMessage) message); + } + else if (null == message) + { + result = null; + } + else + { + _logger.info("warning: received non-text message"); + result = message; + } + + return result; + } + + /** + * GET the top message on a queue. Consumes the message. + * + * @param queueName The Queuename to get from + * + * @return The string content of the text message, if any received + * + * @throws javax.jms.JMSException any exception that occurs + */ + public Message getNextMessage(String queueName) throws JMSException + { + return getNextMessage(queueName, 0); + } + + /** + * Completely clears a queue. For readTimeout behaviour see Javadocs for javax.jms.MessageConsumer. + * + * @param queueName The Queue name to consume from + * @param readTimeout The timeout for each consume + * + * @throws javax.jms.JMSException Any exception that occurs during the consume + * @throws InterruptedException If the consume thread was interrupted during a consume. + */ + public void consume(String queueName, int readTimeout) throws JMSException, InterruptedException + { + if (!connected) + { + connect(); + } + + _logger.info("consuming queue " + queueName); + Queue queue = session.createQueue(queueName); + + final MessageConsumer consumer = session.createConsumer(queue); + int messagesReceived = 0; + + _logger.info("consuming..."); + while ((consumer.receive(readTimeout)) != null) + { + messagesReceived++; + } + + session.commit(); + consumer.close(); + _logger.info("consumed: " + messagesReceived); + } +} |
