From 0314cbe225dce796e09ae9abbd450323808fe493 Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Tue, 18 Aug 2009 16:16:10 +0000 Subject: QPID-2024: Add ProtocolEngine and NetworkDriver interfaces and a NetworkDriver implementation that uses MINA. git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-network-refactor@805477 13f79535-47bb-0310-9956-ffa450edef68 --- .../qpid/client/transport/QpidThreadExecutor.java | 22 - .../qpid/client/transport/TransportConnection.java | 1 + .../org/apache/qpid/protocol/ProtocolEngine.java | 62 +++ .../qpid/protocol/ProtocolEngineFactory.java | 29 ++ .../org/apache/qpid/thread/QpidThreadExecutor.java | 43 ++ .../org/apache/qpid/transport/NetworkDriver.java | 61 +++ .../qpid/transport/NetworkDriverConfiguration.java | 44 ++ .../org/apache/qpid/transport/OpenException.java | 32 ++ .../transport/network/mina/MINANetworkDriver.java | 379 +++++++++++++++++ .../network/mina/MINANetworkDriverTest.java | 473 +++++++++++++++++++++ 10 files changed, 1124 insertions(+), 22 deletions(-) delete mode 100644 qpid/java/client/src/main/java/org/apache/qpid/client/transport/QpidThreadExecutor.java create mode 100644 qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngine.java create mode 100644 qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngineFactory.java create mode 100644 qpid/java/common/src/main/java/org/apache/qpid/thread/QpidThreadExecutor.java create mode 100644 qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriver.java create mode 100644 qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriverConfiguration.java create mode 100644 qpid/java/common/src/main/java/org/apache/qpid/transport/OpenException.java create mode 100644 qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java create mode 100644 qpid/java/common/src/test/java/org/apache/qpid/transport/network/mina/MINANetworkDriverTest.java (limited to 'qpid') diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/QpidThreadExecutor.java b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/QpidThreadExecutor.java deleted file mode 100644 index 3de410fb69..0000000000 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/QpidThreadExecutor.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.apache.qpid.client.transport; - -import org.apache.qpid.thread.Threading; - -import edu.emory.mathcs.backport.java.util.concurrent.Executor; - -public class QpidThreadExecutor implements Executor -{ - @Override - public void execute(Runnable command) - { - try - { - Threading.getThreadFactory().createThread(command).start(); - } - catch(Exception e) - { - throw new RuntimeException("Error creating a thread using Qpid thread factory",e); - } - } - -} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java index 0bacda04ff..32cc8c4cb5 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java @@ -31,6 +31,7 @@ import org.apache.mina.transport.vmpipe.VmPipeAddress; import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException; import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.pool.ReadWriteThreadModel; +import org.apache.qpid.thread.QpidThreadExecutor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngine.java b/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngine.java new file mode 100644 index 0000000000..8ab845454a --- /dev/null +++ b/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngine.java @@ -0,0 +1,62 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 java.net.SocketAddress; + +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.transport.NetworkDriver; +import org.apache.qpid.transport.Receiver; + +/** + * A ProtocolEngine is a Receiver for java.nio.ByteBuffers. It takes the data passed to it in the received + * decodes it and then process the result. + */ +public interface ProtocolEngine extends Receiver +{ + // Sets the network driver providing data for this ProtocolEngine + void setNetworkDriver (NetworkDriver driver); + + // Returns the remote address of the NetworkDriver + SocketAddress getRemoteAddress(); + + // Returns number of bytes written + long getWrittenBytes(); + + // Returns number of bytes read + long getReadBytes(); + + // Called by the NetworkDriver when the socket has been closed for reading + void closed(); + + // Called when the NetworkEngine has not written data for the specified period of time (will trigger a + // heartbeat) + void writerIdle(); + + // Called when the NetworkEngine has not read data for the specified period of time (will close the connection) + void readerIdle(); + + /** + * Accepts an AMQFrame for writing to the network. The ProtocolEngine encodes the frame into bytes and + * passes the data onto the NetworkDriver for sending + */ + void writeFrame(AMQDataBlock frame); +} \ No newline at end of file diff --git a/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngineFactory.java b/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngineFactory.java new file mode 100644 index 0000000000..d8c0f2c916 --- /dev/null +++ b/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngineFactory.java @@ -0,0 +1,29 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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; + +public interface ProtocolEngineFactory +{ + + // Returns a new instance of a ProtocolEngine + ProtocolEngine newProtocolEngine(); + +} \ No newline at end of file diff --git a/qpid/java/common/src/main/java/org/apache/qpid/thread/QpidThreadExecutor.java b/qpid/java/common/src/main/java/org/apache/qpid/thread/QpidThreadExecutor.java new file mode 100644 index 0000000000..376658bb99 --- /dev/null +++ b/qpid/java/common/src/main/java/org/apache/qpid/thread/QpidThreadExecutor.java @@ -0,0 +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.thread; + +import org.apache.qpid.thread.Threading; + +import edu.emory.mathcs.backport.java.util.concurrent.Executor; + +public class QpidThreadExecutor implements Executor +{ + @Override + public void execute(Runnable command) + { + try + { + Threading.getThreadFactory().createThread(command).start(); + } + catch(Exception e) + { + throw new RuntimeException("Error creating a thread using Qpid thread factory",e); + } + } + +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriver.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriver.java new file mode 100644 index 0000000000..d45cee8004 --- /dev/null +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriver.java @@ -0,0 +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.transport; + +import java.net.BindException; +import java.net.InetAddress; +import java.net.SocketAddress; + +import javax.net.ssl.SSLEngine; + +import org.apache.qpid.protocol.ProtocolEngine; +import org.apache.qpid.protocol.ProtocolEngineFactory; +import org.apache.qpid.ssl.SSLContextFactory; + +public interface NetworkDriver extends Sender +{ + // Creates a NetworkDriver which attempts to connect to destination on port and attaches the ProtocolEngine to + // it using the SSLEngine if provided + void open(int port, InetAddress destination, ProtocolEngine engine, + NetworkDriverConfiguration config, SSLEngine sslEngine) + throws OpenException; + + // listens for incoming connections on the specified ports and address and creates a new NetworkDriver which + // processes incoming connections with ProtocolEngines created from factory using the SSLEngine if provided + void bind (int port, InetAddress[] addresses, ProtocolEngineFactory protocolFactory, + NetworkDriverConfiguration config, SSLContextFactory sslFactory) throws BindException; + + // Returns the remote address of underlying socket + SocketAddress getRemoteAddress(); + + /** + * The length of time after which the ProtocolEngines readIdle() method should be called if no data has been + * read in seconds + */ + void setMaxReadIdle(int idleTime); + + /** + * The length of time after which the ProtocolEngines writeIdle() method should be called if no data has been + * written in seconds + */ + void setMaxWriteIdle(int idleTime); + +} \ No newline at end of file diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriverConfiguration.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriverConfiguration.java new file mode 100644 index 0000000000..18cae6bf85 --- /dev/null +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriverConfiguration.java @@ -0,0 +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.transport; + +/** + * This interface provides a means for NetworkDrivers to configure TCP options such as incoming and outgoing + * buffer sizes and set particular options on the socket. NetworkDrivers should honour the values returned + * from here if the underlying implementation supports them. + */ +public interface NetworkDriverConfiguration +{ + // Taken from Socket + boolean getKeepAlive(); + boolean getOOBInline(); + boolean getReuseAddress(); + Integer getSoLinger(); // null means off + int getSoTimeout(); + boolean getTcpNoDelay(); + int getTrafficClass(); + + // The amount of memory in bytes to allocate to the incoming buffer + int getReceiveBufferSize(); + + // The amount of memory in bytes to allocate to the outgoing buffer + int getSendBufferSize(); +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/OpenException.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/OpenException.java new file mode 100644 index 0000000000..8628b8c7aa --- /dev/null +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/OpenException.java @@ -0,0 +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.transport; + +public class OpenException extends Exception +{ + + public OpenException(String string, Throwable lastException) + { + super(string, lastException); + } + +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java new file mode 100644 index 0000000000..96fb7b1ef8 --- /dev/null +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java @@ -0,0 +1,379 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.transport.network.mina; + +import java.io.IOException; +import java.net.BindException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; + +import javax.net.ssl.SSLEngine; + +import org.apache.mina.common.ConnectFuture; +import org.apache.mina.common.IdleStatus; +import org.apache.mina.common.IoAcceptor; +import org.apache.mina.common.IoFilterChain; +import org.apache.mina.common.IoHandlerAdapter; +import org.apache.mina.common.IoSession; +import org.apache.mina.common.IoSessionConfig; +import org.apache.mina.common.SimpleByteBufferAllocator; +import org.apache.mina.common.WriteFuture; +import org.apache.mina.filter.ReadThrottleFilterBuilder; +import org.apache.mina.filter.SSLFilter; +import org.apache.mina.filter.WriteBufferLimitFilterBuilder; +import org.apache.mina.filter.executor.ExecutorFilter; +import org.apache.mina.transport.socket.nio.MultiThreadSocketConnector; +import org.apache.mina.transport.socket.nio.SocketAcceptorConfig; +import org.apache.mina.transport.socket.nio.SocketConnector; +import org.apache.mina.transport.socket.nio.SocketConnectorConfig; +import org.apache.mina.transport.socket.nio.SocketSessionConfig; +import org.apache.mina.util.NewThreadExecutor; +import org.apache.mina.util.SessionUtil; +import org.apache.qpid.pool.ReadWriteThreadModel; +import org.apache.qpid.protocol.ProtocolEngine; +import org.apache.qpid.protocol.ProtocolEngineFactory; +import org.apache.qpid.ssl.SSLContextFactory; +import org.apache.qpid.thread.QpidThreadExecutor; +import org.apache.qpid.transport.NetworkDriver; +import org.apache.qpid.transport.NetworkDriverConfiguration; +import org.apache.qpid.transport.OpenException; + +public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver +{ + + private static final int DEFAULT_BUFFER_SIZE = 32 * 1024; + + ProtocolEngine _protocolEngine; + private boolean _useNIO = false; + private int _processors = 4; + private boolean _executorPool = false; + private SSLContextFactory _sslFactory = null; + private SocketConnector _socketConnector; + private IoAcceptor _acceptor; + private IoSession _ioSession; + private ProtocolEngineFactory _factory; + private boolean _protectIO; + private NetworkDriverConfiguration _config; + private Throwable _lastException; + private boolean _acceptingConnections = false; + + public MINANetworkDriver(boolean useNIO, int processors, boolean executorPool, boolean protectIO) + { + _useNIO = useNIO; + _processors = processors; + _executorPool = executorPool; + _protectIO = protectIO; + } + + public MINANetworkDriver(boolean useNIO, int processors, boolean executorPool, boolean protectIO, + ProtocolEngine protocolEngine, IoSession session) + { + _useNIO = useNIO; + _processors = processors; + _executorPool = executorPool; + _protectIO = protectIO; + _protocolEngine = protocolEngine; + _ioSession = session; + } + + public MINANetworkDriver() + { + + } + + public void bind(int port, InetAddress[] addresses, ProtocolEngineFactory factory, + NetworkDriverConfiguration config, SSLContextFactory sslFactory) throws BindException + { + + _factory = factory; + _config = config; + + if (_useNIO) + { + _acceptor = new org.apache.mina.transport.socket.nio.MultiThreadSocketAcceptor(_processors, + new NewThreadExecutor()); + } + else + { + _acceptor = new org.apache.mina.transport.socket.nio.SocketAcceptor(_processors, new NewThreadExecutor()); + } + + SocketAcceptorConfig sconfig = (SocketAcceptorConfig) _acceptor.getDefaultConfig(); + SocketSessionConfig sc = (SocketSessionConfig) sconfig.getSessionConfig(); + + if (config != null) + { + sc.setReceiveBufferSize(config.getReceiveBufferSize()); + sc.setSendBufferSize(config.getSendBufferSize()); + sc.setTcpNoDelay(config.getTcpNoDelay()); + } + + // if we do not use the executor pool threading model we get the default + // leader follower + // implementation provided by MINA + if (_executorPool) + { + sconfig.setThreadModel(ReadWriteThreadModel.getInstance()); + } + + if (sslFactory != null) + { + _sslFactory = sslFactory; + } + + if (addresses != null && addresses.length > 0) + { + for (InetAddress addr : addresses) + { + try + { + _acceptor.bind(new InetSocketAddress(addr, port), this, sconfig); + } + catch (IOException e) + { + throw new BindException(String.format("Could not bind to {0}:{2}", addr, port)); + } + } + } + else + { + try + { + _acceptor.bind(new InetSocketAddress(port), this, sconfig); + } + catch (IOException e) + { + throw new BindException(String.format("Could not bind to *:{1}", port)); + } + } + _acceptingConnections = true; + } + + public SocketAddress getRemoteAddress() + { + return _ioSession.getRemoteAddress(); + } + + public void open(int port, InetAddress destination, ProtocolEngine engine, NetworkDriverConfiguration config, + SSLEngine sslEngine) throws OpenException + { + if (_useNIO) + { + _socketConnector = new MultiThreadSocketConnector(1, new QpidThreadExecutor()); + } + else + { + _socketConnector = new SocketConnector(1, new QpidThreadExecutor()); // non-blocking + // connector + } + + org.apache.mina.common.ByteBuffer.setUseDirectBuffers(Boolean.getBoolean("amqj.enableDirectBuffers")); + // the MINA default is currently to use the pooled allocator although this may change in future + // once more testing of the performance of the simple allocator has been done + if (!Boolean.getBoolean("amqj.enablePooledAllocator")) + { + org.apache.mina.common.ByteBuffer.setAllocator(new SimpleByteBufferAllocator()); + } + + + SocketConnectorConfig cfg = (SocketConnectorConfig) _socketConnector.getDefaultConfig(); + + // if we do not use our own thread model we get the MINA default which is to use + // its own leader-follower model + boolean readWriteThreading = Boolean.getBoolean("amqj.shared_read_write_pool"); + if (readWriteThreading) + { + cfg.setThreadModel(ReadWriteThreadModel.getInstance()); + } + + SocketSessionConfig scfg = (SocketSessionConfig) cfg.getSessionConfig(); + scfg.setTcpNoDelay((config != null) ? config.getTcpNoDelay() : true); + scfg.setSendBufferSize((config != null) ? config.getSendBufferSize() : DEFAULT_BUFFER_SIZE); + scfg.setReceiveBufferSize((config != null) ? config.getReceiveBufferSize() : DEFAULT_BUFFER_SIZE); + + // Don't have the connector's worker thread wait around for other + // connections (we only use + // one SocketConnector per connection at the moment anyway). This allows + // short-running + // clients (like unit tests) to complete quickly. + _socketConnector.setWorkerTimeout(0); + ConnectFuture future = _socketConnector.connect(new InetSocketAddress(destination, port), this, cfg); + future.join(); + if (!future.isConnected()) + { + throw new OpenException("Could not open connection", _lastException); + } + _ioSession = future.getSession(); + ReadWriteThreadModel.getInstance().getAsynchronousReadFilter().createNewJobForSession(_ioSession); + ReadWriteThreadModel.getInstance().getAsynchronousWriteFilter().createNewJobForSession(_ioSession); + _ioSession.setAttachment(engine); + engine.setNetworkDriver(this); + _protocolEngine = engine; + } + + public void setMaxReadIdle(int idleTime) + { + _ioSession.setIdleTime(IdleStatus.READER_IDLE, idleTime); + } + + public void setMaxWriteIdle(int idleTime) + { + _ioSession.setIdleTime(IdleStatus.WRITER_IDLE, idleTime); + } + + public void close() + { + if (_acceptor != null) + { + _acceptor.unbindAll(); + } + if (_ioSession != null) + { + _ioSession.close(); + } + } + + public void flush() + { + // MINA doesn't support flush + } + + public void send(ByteBuffer msg) + { + WriteFuture future = _ioSession.write(org.apache.mina.common.ByteBuffer.wrap(msg)); + future.join(); + } + + public void setIdleTimeout(long l) + { + // MINA doesn't support setting SO_TIMEOUT + } + + public void exceptionCaught(IoSession protocolSession, Throwable throwable) throws Exception + { + if (_protocolEngine != null) + { + _protocolEngine.exception(throwable); + } + _lastException = throwable; + } + + /** + * Invoked when a message is received on a particular protocol session. Note + * that a protocol session is directly tied to a particular physical + * connection. + * + * @param protocolSession + * the protocol session that received the message + * @param message + * the message itself (i.e. a decoded frame) + * + * @throws Exception + * if the message cannot be processed + */ + public void messageReceived(IoSession protocolSession, Object message) throws Exception + { + if (message instanceof org.apache.mina.common.ByteBuffer) + { + ((ProtocolEngine) protocolSession.getAttachment()).received(((org.apache.mina.common.ByteBuffer) message).buf()); + } + else + { + throw new IllegalStateException("Handed unhandled message. message.class = " + message.getClass() + " message = " + message); + } + } + + public void sessionClosed(IoSession protocolSession) throws Exception + { + ((ProtocolEngine) protocolSession.getAttachment()).closed(); + } + + public void sessionCreated(IoSession protocolSession) throws Exception + { + if (_acceptingConnections) + { + // Configure the session with SSL if necessary + SessionUtil.initialize(protocolSession); + if (_executorPool) + { + if (_sslFactory != null) + { + protocolSession.getFilterChain().addAfter("AsynchronousReadFilter", "sslFilter", + new SSLFilter(_sslFactory.buildServerContext())); + } + } + else + { + if (_sslFactory != null) + { + protocolSession.getFilterChain().addBefore("protocolFilter", "sslFilter", + new SSLFilter(_sslFactory.buildServerContext())); + } + } + + // Do we want to have read/write buffer limits? + if (_protectIO) + { + //Add IO Protection Filters + IoFilterChain chain = protocolSession.getFilterChain(); + + protocolSession.getFilterChain().addLast("tempExecutorFilterForFilterBuilder", new ExecutorFilter()); + + ReadThrottleFilterBuilder readfilter = new ReadThrottleFilterBuilder(); + readfilter.setMaximumConnectionBufferSize(_config.getReceiveBufferSize()); + readfilter.attach(chain); + + WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder(); + writefilter.setMaximumConnectionBufferSize(_config.getSendBufferSize()); + writefilter.attach(chain); + + protocolSession.getFilterChain().remove("tempExecutorFilterForFilterBuilder"); + } + + // Set up the protocol engine + ProtocolEngine protocolEngine = _factory.newProtocolEngine(); + MINANetworkDriver newDriver = new MINANetworkDriver(_useNIO, _processors, _executorPool, _protectIO, protocolEngine, protocolSession); + protocolEngine.setNetworkDriver(newDriver); + protocolSession.setAttachment(protocolEngine); + } + } + + public void sessionIdle(IoSession session, IdleStatus status) throws Exception + { + if (IdleStatus.WRITER_IDLE.equals(status)) + { + ((ProtocolEngine) session.getAttachment()).writerIdle(); + } + else if (IdleStatus.READER_IDLE.equals(status)) + { + ((ProtocolEngine) session.getAttachment()).readerIdle(); + } + } + + private ProtocolEngine getProtocolEngine() + { + return _protocolEngine; + } + +} diff --git a/qpid/java/common/src/test/java/org/apache/qpid/transport/network/mina/MINANetworkDriverTest.java b/qpid/java/common/src/test/java/org/apache/qpid/transport/network/mina/MINANetworkDriverTest.java new file mode 100644 index 0000000000..7901f6a99d --- /dev/null +++ b/qpid/java/common/src/test/java/org/apache/qpid/transport/network/mina/MINANetworkDriverTest.java @@ -0,0 +1,473 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.transport.network.mina; + +import java.net.BindException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import junit.framework.TestCase; + +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.protocol.ProtocolEngine; +import org.apache.qpid.protocol.ProtocolEngineFactory; +import org.apache.qpid.transport.NetworkDriver; +import org.apache.qpid.transport.OpenException; + +public class MINANetworkDriverTest extends TestCase +{ + + private static final String TEST_DATA = "YHALOTHAR"; + private static final int TEST_PORT = 2323; + private NetworkDriver _server; + private NetworkDriver _client; + private CountingProtocolEngine _countingEngine; // Keeps a count of how many bytes it's read + private Exception _thrownEx; + + @Override + public void setUp() + { + _server = new MINANetworkDriver(); + _client = new MINANetworkDriver(); + _thrownEx = null; + _countingEngine = new CountingProtocolEngine(); + } + + @Override + public void tearDown() + { + if (_server != null) + { + _server.close(); + } + + if (_client != null) + { + _client.close(); + } + } + + /** + * Tests that a socket can't be opened if a driver hasn't been bound + * to the port and can be opened if a driver has been bound. + * @throws BindException + * @throws UnknownHostException + * @throws OpenException + */ + public void testBindOpen() throws BindException, UnknownHostException, OpenException + { + try + { + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + } + catch (OpenException e) + { + _thrownEx = e; + } + + assertNotNull("Open should have failed since no engine bound", _thrownEx); + + _server.bind(TEST_PORT, null, new EchoProtocolEngineSingletonFactory(), null, null); + + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + } + + /** + * Tests that a socket can't be opened after a bound NetworkDriver has been closed + * @throws BindException + * @throws UnknownHostException + * @throws OpenException + */ + public void testBindOpenCloseOpen() throws BindException, UnknownHostException, OpenException + { + _server.bind(TEST_PORT, null, new EchoProtocolEngineSingletonFactory(), null, null); + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + _client.close(); + _server.close(); + + try + { + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + } + catch (OpenException e) + { + _thrownEx = e; + } + assertNotNull("Open should have failed", _thrownEx); + } + + /** + * Checks that the right exception is thrown when binding a NetworkDriver to an already + * existing socket. + */ + public void testBindPortInUse() + { + try + { + _server.bind(TEST_PORT, null, new EchoProtocolEngineSingletonFactory(), null, null); + } + catch (BindException e) + { + fail("First bind should not fail"); + } + + try + { + _client.bind(TEST_PORT, null, new EchoProtocolEngineSingletonFactory(), null, null); + } + catch (BindException e) + { + _thrownEx = e; + } + assertNotNull("Second bind should throw BindException", _thrownEx); + } + + /** + * tests that bytes sent on a network driver are received at the other end + * + * @throws UnknownHostException + * @throws OpenException + * @throws InterruptedException + * @throws BindException + */ + public void testSend() throws UnknownHostException, OpenException, InterruptedException, BindException + { + // Open a connection from a counting engine to an echo engine + _server.bind(TEST_PORT, null, new EchoProtocolEngineSingletonFactory(), null, null); + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + + // Tell the counting engine how much data we're sending + _countingEngine.setNewLatch(TEST_DATA.getBytes().length); + + // Send the data and wait for up to 2 seconds to get it back + _client.send(ByteBuffer.wrap(TEST_DATA.getBytes())); + _countingEngine.getLatch().await(2, TimeUnit.SECONDS); + + // Check what we got + assertEquals("Wrong amount of data recieved", TEST_DATA.getBytes().length, _countingEngine.getReadBytes()); + } + + /** + * Opens a connection with a low read idle and check that it gets triggered + * @throws BindException + * @throws OpenException + * @throws UnknownHostException + * + */ + public void testSetReadIdle() throws BindException, UnknownHostException, OpenException + { + // Open a connection from a counting engine to an echo engine + _server.bind(TEST_PORT, null, new EchoProtocolEngineSingletonFactory(), null, null); + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + assertFalse("Reader should not have been idle", _countingEngine.getReaderHasBeenIdle()); + _client.setMaxReadIdle(1); + try + { + Thread.sleep(1000); + } + catch (InterruptedException e) + { + // Eat it + } + assertTrue("Reader should have been idle", _countingEngine.getReaderHasBeenIdle()); + } + + /** + * Opens a connection with a low write idle and check that it gets triggered + * @throws BindException + * @throws OpenException + * @throws UnknownHostException + * + */ + public void testSetWriteIdle() throws BindException, UnknownHostException, OpenException + { + // Open a connection from a counting engine to an echo engine + _server.bind(TEST_PORT, null, new EchoProtocolEngineSingletonFactory(), null, null); + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + assertFalse("Reader should not have been idle", _countingEngine.getWriterHasBeenIdle()); + _client.setMaxWriteIdle(1); + try + { + Thread.sleep(1000); + } + catch (InterruptedException e) + { + // Eat it + } + assertTrue("Reader should have been idle", _countingEngine.getWriterHasBeenIdle()); + } + + + /** + * Creates and then closes a connection from client to server and checks that the server + * has its closed() method called. Then creates a new client and closes the server to check + * that the client has its closed() method called. + * @throws BindException + * @throws UnknownHostException + * @throws OpenException + */ + public void testClosed() throws BindException, UnknownHostException, OpenException + { + // Open a connection from a counting engine to an echo engine + EchoProtocolEngineSingletonFactory factory = new EchoProtocolEngineSingletonFactory(); + _server.bind(TEST_PORT, null, factory, null, null); + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + EchoProtocolEngine serverEngine = null; + while (serverEngine == null) + { + serverEngine = factory.getEngine(); + if (serverEngine == null) + { + try + { + Thread.sleep(10); + } + catch (InterruptedException e) + { + } + } + } + assertFalse("Server should not have been closed", serverEngine.getClosed()); + serverEngine.setNewLatch(1); + _client.close(); + try + { + serverEngine.getLatch().await(2, TimeUnit.SECONDS); + } + catch (InterruptedException e) + { + } + assertTrue("Server should have been closed", serverEngine.getClosed()); + + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + _countingEngine.setClosed(false); + assertFalse("Client should not have been closed", _countingEngine.getClosed()); + _countingEngine.setNewLatch(1); + _server.close(); + try + { + _countingEngine.getLatch().await(2, TimeUnit.SECONDS); + } + catch (InterruptedException e) + { + } + assertTrue("Client should have been closed", _countingEngine.getClosed()); + } + + /** + * Create a connection and instruct the client to throw an exception when it gets some data + * and that the latch gets counted down. + * @throws BindException + * @throws UnknownHostException + * @throws OpenException + * @throws InterruptedException + */ + public void testExceptionCaught() throws BindException, UnknownHostException, OpenException, InterruptedException + { + _server.bind(TEST_PORT, null, new EchoProtocolEngineSingletonFactory(), null, null); + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + + + assertEquals("Exception should not have been thrown", 1, + _countingEngine.getExceptionLatch().getCount()); + _countingEngine.setErrorOnNextRead(true); + _countingEngine.setNewLatch(TEST_DATA.getBytes().length); + _client.send(ByteBuffer.wrap(TEST_DATA.getBytes())); + _countingEngine.getExceptionLatch().await(2, TimeUnit.SECONDS); + assertEquals("Exception should not been thrown", 0, + _countingEngine.getExceptionLatch().getCount()); + } + + /** + * Opens a connection and checks that the remote address is the one that was asked for + * @throws BindException + * @throws UnknownHostException + * @throws OpenException + */ + public void testGetRemoteAddress() throws BindException, UnknownHostException, OpenException + { + _server.bind(TEST_PORT, null, new EchoProtocolEngineSingletonFactory(), null, null); + _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); + assertEquals(new InetSocketAddress(InetAddress.getLocalHost(), TEST_PORT), + _client.getRemoteAddress()); + } + + private class EchoProtocolEngineSingletonFactory implements ProtocolEngineFactory + { + EchoProtocolEngine _engine = null; + + public ProtocolEngine newProtocolEngine() + { + if (_engine == null) + { + _engine = new EchoProtocolEngine(); + } + return getEngine(); + } + + public EchoProtocolEngine getEngine() + { + return _engine; + } + } + + public class CountingProtocolEngine implements ProtocolEngine + { + + protected NetworkDriver _driver; + public ArrayList _receivedBytes = new ArrayList(); + private int _readBytes; + private CountDownLatch _latch = new CountDownLatch(0); + private boolean _readerHasBeenIdle; + private boolean _writerHasBeenIdle; + private boolean _closed = false; + private boolean _nextReadErrors = false; + private CountDownLatch _exceptionLatch = new CountDownLatch(1); + + public void closed() + { + setClosed(true); + _latch.countDown(); + } + + public void setErrorOnNextRead(boolean b) + { + _nextReadErrors = b; + } + + public void setNewLatch(int length) + { + _latch = new CountDownLatch(length); + } + + public long getReadBytes() + { + return _readBytes; + } + + public SocketAddress getRemoteAddress() + { + if (_driver != null) + { + return _driver.getRemoteAddress(); + } + else + { + return null; + } + } + + public long getWrittenBytes() + { + return 0; + } + + public void readerIdle() + { + _readerHasBeenIdle = true; + } + + public void setNetworkDriver(NetworkDriver driver) + { + _driver = driver; + } + + public void writeFrame(AMQDataBlock frame) + { + + } + + public void writerIdle() + { + _writerHasBeenIdle = true; + } + + public void exception(Throwable t) + { + _exceptionLatch.countDown(); + } + + public CountDownLatch getExceptionLatch() + { + return _exceptionLatch; + } + + public void received(ByteBuffer msg) + { + // increment read bytes and count down the latch for that many + int bytes = msg.remaining(); + _readBytes += bytes; + for (int i = 0; i < bytes; i++) + { + _latch.countDown(); + } + + // Throw an error if we've been asked too, but we can still count + if (_nextReadErrors) + { + throw new RuntimeException("Was asked to error"); + } + } + + public CountDownLatch getLatch() + { + return _latch; + } + + public boolean getWriterHasBeenIdle() + { + return _writerHasBeenIdle; + } + + public boolean getReaderHasBeenIdle() + { + return _readerHasBeenIdle; + } + + public void setClosed(boolean _closed) + { + this._closed = _closed; + } + + public boolean getClosed() + { + return _closed; + } + } + + private class EchoProtocolEngine extends CountingProtocolEngine + { + + public void received(ByteBuffer msg) + { + super.received(msg); + msg.rewind(); + _driver.send(msg); + } + } +} \ No newline at end of file -- cgit v1.2.1 From f0051104b5b99601507c578bd0a7b819a76aef55 Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Wed, 19 Aug 2009 14:03:25 +0000 Subject: QPID-2024: Change send to stash the future and have flush join on that so that it only returns when all data has been written. Add getLocalAddress. git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-network-refactor@805809 13f79535-47bb-0310-9956-ffa450edef68 --- .../java/org/apache/qpid/transport/NetworkDriver.java | 5 ++++- .../qpid/transport/network/mina/MINANetworkDriver.java | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) (limited to 'qpid') diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriver.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriver.java index d45cee8004..34b0ef65be 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriver.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriver.java @@ -43,9 +43,12 @@ public interface NetworkDriver extends Sender void bind (int port, InetAddress[] addresses, ProtocolEngineFactory protocolFactory, NetworkDriverConfiguration config, SSLContextFactory sslFactory) throws BindException; - // Returns the remote address of underlying socket + // Returns the remote address of the underlying socket SocketAddress getRemoteAddress(); + // Returns the local address of the underlying socket + SocketAddress getLocalAddress(); + /** * The length of time after which the ProtocolEngines readIdle() method should be called if no data has been * read in seconds diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java index 96fb7b1ef8..7330a042df 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java @@ -78,6 +78,8 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver private Throwable _lastException; private boolean _acceptingConnections = false; + private WriteFuture _lastWriteFuture; + public MINANetworkDriver(boolean useNIO, int processors, boolean executorPool, boolean protectIO) { _useNIO = useNIO; @@ -174,6 +176,11 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver { return _ioSession.getRemoteAddress(); } + + public SocketAddress getLocalAddress() + { + return _ioSession.getLocalAddress(); + } public void open(int port, InetAddress destination, ProtocolEngine engine, NetworkDriverConfiguration config, SSLEngine sslEngine) throws OpenException @@ -256,13 +263,15 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver public void flush() { - // MINA doesn't support flush + if (_lastWriteFuture != null) + { + _lastWriteFuture.join(); + } } public void send(ByteBuffer msg) { - WriteFuture future = _ioSession.write(org.apache.mina.common.ByteBuffer.wrap(msg)); - future.join(); + _lastWriteFuture = _ioSession.write(org.apache.mina.common.ByteBuffer.wrap(msg)); } public void setIdleTimeout(long l) -- cgit v1.2.1 From a7be8fc7337b5cc093f593cc1becb9fe7b4dc0fb Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Tue, 1 Sep 2009 16:27:52 +0000 Subject: QPID-2025: Add a AMQProtocolEngine from the de-MINAfied AMQMinaProtocolSession. Remove various now-unused classes and update references. Add tests for AMQDecoder. Net -1500 lines, +25% performance on transient messaging. Nice. git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-network-refactor@810110 13f79535-47bb-0310-9956-ffa450edef68 --- .../src/main/java/org/apache/qpid/server/Main.java | 199 ++-- .../server/configuration/ServerConfiguration.java | 56 +- .../qpid/server/connection/ConnectionRegistry.java | 1 - .../server/connection/IConnectionRegistry.java | 1 - .../server/protocol/AMQMinaProtocolSession.java | 923 ----------------- .../qpid/server/protocol/AMQProtocolEngine.java | 1054 ++++++++++++++++++++ .../server/protocol/AMQProtocolEngineFactory.java | 29 + .../qpid/server/protocol/AMQProtocolSession.java | 15 + .../server/protocol/AMQProtocolSessionMBean.java | 42 +- .../qpid/server/registry/ApplicationRegistry.java | 2 +- .../access/plugins/network/FirewallPlugin.java | 23 +- .../apache/qpid/server/transport/QpidAcceptor.java | 12 +- .../configuration/ServerConfigurationTest.java | 56 +- .../protocol/AMQProtocolSessionMBeanTest.java | 2 +- .../protocol/InternalTestProtocolSession.java | 23 +- .../qpid/server/protocol/MaxChannelsTest.java | 2 +- .../apache/qpid/server/protocol/TestIoSession.java | 328 ------ .../qpid/server/protocol/TestNetworkDriver.java | 126 +++ .../qpid/server/queue/AMQQueueAlertTest.java | 49 +- .../access/plugins/network/FirewallPluginTest.java | 39 +- .../qpid/client/protocol/AMQProtocolHandler.java | 2 +- .../qpid/client/transport/TransportConnection.java | 12 +- .../org/apache/qpid/codec/AMQCodecFactory.java | 7 +- .../java/org/apache/qpid/codec/AMQDecoder.java | 79 +- .../apache/qpid/framing/AMQDataBlockDecoder.java | 39 +- .../apache/qpid/framing/AMQDataBlockEncoder.java | 2 +- .../apache/qpid/framing/ProtocolInitiation.java | 20 +- .../src/main/java/org/apache/qpid/pool/Event.java | 58 +- .../src/main/java/org/apache/qpid/pool/Job.java | 34 +- .../java/org/apache/qpid/pool/PoolingFilter.java | 27 +- .../org/apache/qpid/pool/ReadWriteRunnable.java | 1 - .../qpid/protocol/ProtocolEngineFactory.java | 4 +- .../qpid/transport/NetworkDriverConfiguration.java | 16 +- .../transport/network/mina/MINANetworkDriver.java | 20 +- .../java/org/apache/qpid/codec/AMQDecoderTest.java | 130 +++ .../codec/MockAMQVersionAwareProtocolSession.java | 95 ++ .../network/mina/MINANetworkDriverTest.java | 5 +- 37 files changed, 1890 insertions(+), 1643 deletions(-) delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngineFactory.java delete mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/TestIoSession.java create mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/TestNetworkDriver.java create mode 100644 qpid/java/common/src/test/java/org/apache/qpid/codec/AMQDecoderTest.java create mode 100644 qpid/java/common/src/test/java/org/apache/qpid/codec/MockAMQVersionAwareProtocolSession.java (limited to 'qpid') diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java index f8deb95628..c45e794145 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java @@ -20,6 +20,13 @@ */ package org.apache.qpid.server; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.Properties; + import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; @@ -30,16 +37,9 @@ import org.apache.commons.cli.PosixParser; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.xml.QpidLog4JConfigurator; -import org.apache.mina.common.ByteBuffer; -import org.apache.mina.common.FixedSizeByteBufferAllocator; -import org.apache.mina.common.IoAcceptor; -import org.apache.mina.transport.socket.nio.SocketAcceptorConfig; -import org.apache.mina.transport.socket.nio.SocketSessionConfig; -import org.apache.mina.util.NewThreadExecutor; import org.apache.qpid.AMQException; import org.apache.qpid.common.QpidProperties; import org.apache.qpid.framing.ProtocolVersion; -import org.apache.qpid.pool.ReadWriteThreadModel; import org.apache.qpid.server.configuration.ServerConfiguration; import org.apache.qpid.server.configuration.management.ConfigurationManagementMBean; import org.apache.qpid.server.information.management.ServerInformationMBean; @@ -48,19 +48,13 @@ import org.apache.qpid.server.logging.actors.BrokerActor; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.management.LoggingManagementMBean; import org.apache.qpid.server.logging.messages.BrokerMessages; -import org.apache.qpid.server.protocol.AMQPFastProtocolHandler; -import org.apache.qpid.server.protocol.AMQPProtocolProvider; +import org.apache.qpid.server.protocol.AMQProtocolEngineFactory; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; import org.apache.qpid.server.transport.QpidAcceptor; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.BindException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.util.Properties; +import org.apache.qpid.ssl.SSLContextFactory; +import org.apache.qpid.transport.NetworkDriver; +import org.apache.qpid.transport.network.mina.MINANetworkDriver; /** * Main entry point for AMQPD. @@ -300,20 +294,6 @@ public class Main _brokerLogger.info("Starting Qpid Broker " + QpidProperties.getReleaseVersion() + " build: " + QpidProperties.getBuildVersion()); - ByteBuffer.setUseDirectBuffers(serverConfig.getEnableDirectBuffers()); - - // the MINA default is currently to use the pooled allocator although this may change in future - // once more testing of the performance of the simple allocator has been done - if (!serverConfig.getEnablePooledAllocator()) - { - ByteBuffer.setAllocator(new FixedSizeByteBufferAllocator()); - } - - if (serverConfig.getUseBiasedWrites()) - { - System.setProperty("org.apache.qpid.use_write_biased_pool", "true"); - } - int port = serverConfig.getPort(); String portStr = commandLine.getOptionValue("p"); @@ -329,7 +309,54 @@ public class Main } } - bind(port, serverConfig); + String bindAddr = commandLine.getOptionValue("b"); + if (bindAddr == null) + { + bindAddr = serverConfig.getBind(); + } + InetAddress bindAddress = null; + if (bindAddr.equals("wildcard")) + { + bindAddress = new InetSocketAddress(port).getAddress(); + } + else + { + bindAddress = InetAddress.getByAddress(parseIP(bindAddr)); + } + + String keystorePath = serverConfig.getKeystorePath(); + String keystorePassword = serverConfig.getKeystorePassword(); + String certType = serverConfig.getCertType(); + SSLContextFactory sslFactory = null; + boolean isSsl = false; + + if (!serverConfig.getSSLOnly()) + { + NetworkDriver driver = new MINANetworkDriver(); + driver.bind(port, new InetAddress[]{bindAddress}, new AMQProtocolEngineFactory(), + serverConfig.getNetworkConfiguration(), null); + ApplicationRegistry.getInstance().addAcceptor(new InetSocketAddress(bindAddress, port), + new QpidAcceptor(driver,"TCP")); + CurrentActor.get().message(BrokerMessages.BRK_1002("TCP", port)); + } + + if (serverConfig.getEnableSSL()) + { + sslFactory = new SSLContextFactory(keystorePath, keystorePassword, certType); + NetworkDriver driver = new MINANetworkDriver(); + driver.bind(serverConfig.getSSLPort(), new InetAddress[]{bindAddress}, + new AMQProtocolEngineFactory(), serverConfig.getNetworkConfiguration(), sslFactory); + ApplicationRegistry.getInstance().addAcceptor(new InetSocketAddress(bindAddress, port), + new QpidAcceptor(driver,"TCP")); + CurrentActor.get().message(BrokerMessages.BRK_1002("TCP/SSL", serverConfig.getSSLPort())); + } + + //fixme qpid.AMQP should be using qpidproperties to get value + _brokerLogger.info("Qpid Broker Ready :" + QpidProperties.getReleaseVersion() + + " build: " + QpidProperties.getBuildVersion()); + + CurrentActor.get().message(BrokerMessages.BRK_1004()); + } finally { @@ -358,114 +385,6 @@ public class Main } } - protected void bind(int port, ServerConfiguration config) throws BindException - { - String bindAddr = commandLine.getOptionValue("b"); - if (bindAddr == null) - { - bindAddr = config.getBind(); - } - - try - { - IoAcceptor acceptor; - - if (ApplicationRegistry.getInstance().getConfiguration().getQpidNIO()) - { - _logger.warn("Using Qpid Multithreaded IO Processing"); - acceptor = new org.apache.mina.transport.socket.nio.MultiThreadSocketAcceptor(config.getProcessors(), new NewThreadExecutor()); - } - else - { - _logger.warn("Using Mina IO Processing"); - acceptor = new org.apache.mina.transport.socket.nio.SocketAcceptor(config.getProcessors(), new NewThreadExecutor()); - } - - SocketAcceptorConfig sconfig = (SocketAcceptorConfig) acceptor.getDefaultConfig(); - SocketSessionConfig sc = (SocketSessionConfig) sconfig.getSessionConfig(); - - sc.setReceiveBufferSize(config.getReceiveBufferSize()); - sc.setSendBufferSize(config.getWriteBufferSize()); - sc.setTcpNoDelay(config.getTcpNoDelay()); - - // if we do not use the executor pool threading model we get the default leader follower - // implementation provided by MINA - if (config.getEnableExecutorPool()) - { - sconfig.setThreadModel(ReadWriteThreadModel.getInstance()); - } - - if (!config.getEnableSSL() || !config.getSSLOnly()) - { - AMQPFastProtocolHandler handler = new AMQPProtocolProvider().getHandler(); - InetSocketAddress bindAddress; - if (bindAddr.equals("wildcard")) - { - bindAddress = new InetSocketAddress(port); - } - else - { - bindAddress = new InetSocketAddress(InetAddress.getByAddress(parseIP(bindAddr)), port); - } - - bind(new QpidAcceptor(acceptor,"TCP"), bindAddress, handler, sconfig); - - //fixme qpid.AMQP should be using qpidproperties to get value - _brokerLogger.info("Qpid.AMQP listening on non-SSL address " + bindAddress); - } - - if (config.getEnableSSL()) - { - AMQPFastProtocolHandler handler = new AMQPProtocolProvider().getHandler(); - try - { - - bind(new QpidAcceptor(acceptor, "TCP/SSL"), new InetSocketAddress(config.getSSLPort()), handler, sconfig); - - //fixme qpid.AMQP should be using qpidproperties to get value - _brokerLogger.info("Qpid.AMQP listening on SSL port " + config.getSSLPort()); - - } - catch (IOException e) - { - _brokerLogger.error("Unable to listen on SSL port: " + e, e); - } - } - - //fixme qpid.AMQP should be using qpidproperties to get value - _brokerLogger.info("Qpid Broker Ready :" + QpidProperties.getReleaseVersion() - + " build: " + QpidProperties.getBuildVersion()); - - CurrentActor.get().message(BrokerMessages.BRK_1004()); - - } - catch (Exception e) - { - _logger.error("Unable to bind service to registry: " + e, e); - //fixme this need tidying up - throw new BindException(e.getMessage()); - } - } - - /** - * Ensure that any bound Acceptors are recorded in the registry so they can be closed later. - * - * @param acceptor - * @param bindAddress - * @param handler - * @param sconfig - * - * @throws IOException from the acceptor.bind command - */ - private void bind(QpidAcceptor acceptor, InetSocketAddress bindAddress, AMQPFastProtocolHandler handler, SocketAcceptorConfig sconfig) throws IOException - { - acceptor.getIoAcceptor().bind(bindAddress, handler, sconfig); - - CurrentActor.get().message(BrokerMessages.BRK_1002(acceptor.toString(), bindAddress.getPort())); - - ApplicationRegistry.getInstance().addAcceptor(bindAddress, acceptor); - } - public static void main(String[] args) { //if the -Dlog4j.configuration property has not been set, enable the init override diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java index 7ea7738189..b3c8975c7c 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java @@ -38,6 +38,7 @@ import org.apache.qpid.server.configuration.management.ConfigurationManagementMB import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; +import org.apache.qpid.transport.NetworkDriverConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -564,7 +565,7 @@ public class ServerConfiguration implements SignalHandler public boolean getSSLOnly() { - return getConfig().getBoolean("connector.ssl.sslOnly", true); + return getConfig().getBoolean("connector.ssl.sslOnly", false); } public int getSSLPort() @@ -613,4 +614,57 @@ public class ServerConfiguration implements SignalHandler getConfig().getLong("housekeeping.expiredMessageCheckPeriod", DEFAULT_HOUSEKEEPING_PERIOD)); } + + public NetworkDriverConfiguration getNetworkConfiguration() + { + return new NetworkDriverConfiguration() + { + + public Integer getTrafficClass() + { + return null; + } + + public Boolean getTcpNoDelay() + { + // Can't call parent getTcpNoDelay since it just calls this one + return getConfig().getBoolean("connector.tcpNoDelay", true); + } + + public Integer getSoTimeout() + { + return null; + } + + public Integer getSoLinger() + { + return null; + } + + public Integer getSendBufferSize() + { + return getBufferWriteLimit(); + } + + public Boolean getReuseAddress() + { + return null; + } + + public Integer getReceiveBufferSize() + { + return getBufferReadLimit(); + } + + public Boolean getOOBInline() + { + return null; + } + + public Boolean getKeepAlive() + { + return null; + } + }; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java index d287595e2d..71e07172ed 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java @@ -21,7 +21,6 @@ package org.apache.qpid.server.connection; import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.protocol.AMQMinaProtocolSession; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.AMQException; import org.apache.qpid.AMQConnectionException; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java index d64fde1c20..002269bbaa 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java @@ -20,7 +20,6 @@ */ package org.apache.qpid.server.connection; -import org.apache.qpid.server.protocol.AMQMinaProtocolSession; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.AMQException; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java deleted file mode 100644 index 7bc4365152..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java +++ /dev/null @@ -1,923 +0,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.server.protocol; - -import org.apache.log4j.Logger; -import org.apache.mina.common.CloseFuture; -import org.apache.mina.common.IdleStatus; -import org.apache.mina.common.IoServiceConfig; -import org.apache.mina.common.IoSession; -import org.apache.mina.transport.vmpipe.VmPipeAddress; -import org.apache.qpid.AMQChannelException; -import org.apache.qpid.AMQConnectionException; -import org.apache.qpid.AMQException; -import org.apache.qpid.codec.AMQCodecFactory; -import org.apache.qpid.codec.AMQDecoder; -import org.apache.qpid.common.ClientProperties; -import org.apache.qpid.framing.AMQBody; -import org.apache.qpid.framing.AMQDataBlock; -import org.apache.qpid.framing.AMQFrame; -import org.apache.qpid.framing.AMQMethodBody; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.ChannelCloseOkBody; -import org.apache.qpid.framing.ContentBody; -import org.apache.qpid.framing.ContentHeaderBody; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.HeartbeatBody; -import org.apache.qpid.framing.MethodDispatcher; -import org.apache.qpid.framing.MethodRegistry; -import org.apache.qpid.framing.ProtocolInitiation; -import org.apache.qpid.framing.ProtocolVersion; -import org.apache.qpid.pool.ReadWriteThreadModel; -import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.protocol.AMQMethodEvent; -import org.apache.qpid.protocol.AMQMethodListener; -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.handler.ServerMethodDispatcherImpl; -import org.apache.qpid.server.logging.actors.AMQPConnectionActor; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.subjects.ConnectionLogSubject; -import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.server.logging.LogActor; -import org.apache.qpid.server.logging.messages.ConnectionMessages; -import org.apache.qpid.server.management.Managable; -import org.apache.qpid.server.management.ManagedObject; -import org.apache.qpid.server.output.ProtocolOutputConverter; -import org.apache.qpid.server.output.ProtocolOutputConverterRegistry; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.state.AMQState; -import org.apache.qpid.server.state.AMQStateManager; -import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.virtualhost.VirtualHostRegistry; -import org.apache.qpid.transport.Sender; - -import javax.management.JMException; -import javax.security.sasl.SaslServer; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.security.Principal; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.atomic.AtomicLong; - -public class AMQMinaProtocolSession implements AMQProtocolSession, Managable -{ - private static final Logger _logger = Logger.getLogger(AMQProtocolSession.class); - - private static final String CLIENT_PROPERTIES_INSTANCE = ClientProperties.instance.toString(); - - private static final AtomicLong idGenerator = new AtomicLong(0); - - // to save boxing the channelId and looking up in a map... cache in an array the low numbered - // channels. This value must be of the form 2^x - 1. - private static final int CHANNEL_CACHE_SIZE = 0xff; - - private final IoSession _minaProtocolSession; - - private AMQShortString _contextKey; - - private AMQShortString _clientVersion = null; - - private VirtualHost _virtualHost; - - private final Map _channelMap = new HashMap(); - - private final AMQChannel[] _cachedChannels = new AMQChannel[CHANNEL_CACHE_SIZE + 1]; - - private final CopyOnWriteArraySet _frameListeners = new CopyOnWriteArraySet(); - - private final AMQStateManager _stateManager; - - private AMQCodecFactory _codecFactory; - - private AMQProtocolSessionMBean _managedObject; - - private SaslServer _saslServer; - - private Object _lastReceived; - - private Object _lastSent; - - protected boolean _closed; - // maximum number of channels this session should have - private long _maxNoOfChannels = 1000; - - /* AMQP Version for this session */ - private ProtocolVersion _protocolVersion = ProtocolVersion.getLatestSupportedVersion(); - - private FieldTable _clientProperties; - private final List _taskList = new CopyOnWriteArrayList(); - - private List _closingChannelsList = new CopyOnWriteArrayList(); - private ProtocolOutputConverter _protocolOutputConverter; - private Principal _authorizedID; - private MethodDispatcher _dispatcher; - private ProtocolSessionIdentifier _sessionIdentifier; - - private static final long LAST_WRITE_FUTURE_JOIN_TIMEOUT = 60000L; - private org.apache.mina.common.WriteFuture _lastWriteFuture; - - // Create a simple ID that increments for ever new Session - private final long _sessionID = idGenerator.getAndIncrement(); - - private AMQPConnectionActor _actor; - private LogSubject _logSubject; - - public ManagedObject getManagedObject() - { - return _managedObject; - } - - public AMQMinaProtocolSession(IoSession session, VirtualHostRegistry virtualHostRegistry, AMQCodecFactory codecFactory) - throws AMQException - { - _stateManager = new AMQStateManager(virtualHostRegistry, this); - _minaProtocolSession = session; - session.setAttachment(this); - - _codecFactory = codecFactory; - - _actor = new AMQPConnectionActor(this, virtualHostRegistry.getApplicationRegistry().getRootMessageLogger()); - - _actor.message(ConnectionMessages.CON_1001(null, null, false, false)); - - try - { - IoServiceConfig config = session.getServiceConfig(); - ReadWriteThreadModel threadModel = (ReadWriteThreadModel) config.getThreadModel(); - threadModel.getAsynchronousReadFilter().createNewJobForSession(session); - threadModel.getAsynchronousWriteFilter().createNewJobForSession(session); - } - catch (RuntimeException e) - { - e.printStackTrace(); - throw e; - - } - } - - private AMQProtocolSessionMBean createMBean() throws AMQException - { - try - { - return new AMQProtocolSessionMBean(this); - } - catch (JMException ex) - { - _logger.error("AMQProtocolSession MBean creation has failed ", ex); - throw new AMQException("AMQProtocolSession MBean creation has failed ", ex); - } - } - - public IoSession getIOSession() - { - return _minaProtocolSession; - } - - public static AMQProtocolSession getAMQProtocolSession(IoSession minaProtocolSession) - { - return (AMQProtocolSession) minaProtocolSession.getAttachment(); - } - - public long getSessionID() - { - return _sessionID; - } - - public LogActor getLogActor() - { - return _actor; - } - - public void dataBlockReceived(AMQDataBlock message) throws Exception - { - _lastReceived = message; - if (message instanceof ProtocolInitiation) - { - protocolInitiationReceived((ProtocolInitiation) message); - - } - else if (message instanceof AMQFrame) - { - AMQFrame frame = (AMQFrame) message; - frameReceived(frame); - - } - else - { - throw new UnknnownMessageTypeException(message); - } - } - - private void frameReceived(AMQFrame frame) throws AMQException - { - int channelId = frame.getChannel(); - AMQBody body = frame.getBodyFrame(); - - //Look up the Channel's Actor and set that as the current actor - // If that is not available then we can use the ConnectionActor - // that is associated with this AMQMPSession. - LogActor channelActor = null; - if (_channelMap.get(channelId) != null) - { - channelActor = _channelMap.get(channelId).getLogActor(); - } - CurrentActor.set(channelActor == null ? _actor : channelActor); - - try - { - if (_logger.isDebugEnabled()) - { - _logger.debug("Frame Received: " + frame); - } - - // Check that this channel is not closing - if (channelAwaitingClosure(channelId)) - { - if ((frame.getBodyFrame() instanceof ChannelCloseOkBody)) - { - if (_logger.isInfoEnabled()) - { - _logger.info("Channel[" + channelId + "] awaiting closure - processing close-ok"); - } - } - else - { - if (_logger.isInfoEnabled()) - { - _logger.info("Channel[" + channelId + "] awaiting closure. Should close socket as client did not close-ok :" + frame); - } - - closeProtocolSession(); - return; - } - } - - try - { - body.handle(channelId, this); - } - catch (AMQException e) - { - closeChannel(channelId); - throw e; - } - } - finally - { - CurrentActor.remove(); - } - } - - private void protocolInitiationReceived(ProtocolInitiation pi) - { - // this ensures the codec never checks for a PI message again - ((AMQDecoder) _codecFactory.getDecoder()).setExpectProtocolInitiation(false); - try - { - // Log incomming protocol negotiation request - _actor.message(ConnectionMessages.CON_1001(null, pi._protocolMajor + "-" + pi._protocolMinor, false, true)); - - ProtocolVersion pv = pi.checkVersion(); // Fails if not correct - - // This sets the protocol version (and hence framing classes) for this session. - setProtocolVersion(pv); - - String mechanisms = ApplicationRegistry.getInstance().getAuthenticationManager().getMechanisms(); - - String locales = "en_US"; - - AMQMethodBody responseBody = getMethodRegistry().createConnectionStartBody((short) getProtocolMajorVersion(), - (short) getProtocolMinorVersion(), - null, - mechanisms.getBytes(), - locales.getBytes()); - _minaProtocolSession.write(responseBody.generateFrame(0)); - - } - catch (AMQException e) - { - _logger.info("Received unsupported protocol initiation for protocol version: " + getProtocolVersion()); - - _minaProtocolSession.write(new ProtocolInitiation(ProtocolVersion.getLatestSupportedVersion())); - - // TODO: Close connection (but how to wait until message is sent?) - // ritchiem 2006-12-04 will this not do? - // WriteFuture future = _minaProtocolSession.write(new ProtocolInitiation(pv[i][PROTOCOLgetProtocolMajorVersion()], pv[i][PROTOCOLgetProtocolMinorVersion()])); - // future.join(); - // close connection - - } - } - - public void methodFrameReceived(int channelId, AMQMethodBody methodBody) - { - - final AMQMethodEvent evt = new AMQMethodEvent(channelId, methodBody); - - try - { - try - { - - boolean wasAnyoneInterested = _stateManager.methodReceived(evt); - - if (!_frameListeners.isEmpty()) - { - for (AMQMethodListener listener : _frameListeners) - { - wasAnyoneInterested = listener.methodReceived(evt) || wasAnyoneInterested; - } - } - - if (!wasAnyoneInterested) - { - throw new AMQNoMethodHandlerException(evt); - } - } - catch (AMQChannelException e) - { - if (getChannel(channelId) != null) - { - if (_logger.isInfoEnabled()) - { - _logger.info("Closing channel due to: " + e.getMessage()); - } - - writeFrame(e.getCloseFrame(channelId)); - closeChannel(channelId); - } - else - { - if (_logger.isDebugEnabled()) - { - _logger.debug("ChannelException occured on non-existent channel:" + e.getMessage()); - } - - if (_logger.isInfoEnabled()) - { - _logger.info("Closing connection due to: " + e.getMessage()); - } - - AMQConnectionException ce = - evt.getMethod().getConnectionException(AMQConstant.CHANNEL_ERROR, - AMQConstant.CHANNEL_ERROR.getName().toString()); - - closeConnection(channelId, ce, false); - } - } - catch (AMQConnectionException e) - { - closeConnection(channelId, e, false); - } - } - catch (Exception e) - { - - for (AMQMethodListener listener : _frameListeners) - { - listener.error(e); - } - - _logger.error("Unexpected exception while processing frame. Closing connection.", e); - - closeProtocolSession(); - } - } - - public void contentHeaderReceived(int channelId, ContentHeaderBody body) throws AMQException - { - - AMQChannel channel = getAndAssertChannel(channelId); - - channel.publishContentHeader(body); - - } - - public void contentBodyReceived(int channelId, ContentBody body) throws AMQException - { - AMQChannel channel = getAndAssertChannel(channelId); - - channel.publishContentBody(body); - } - - public void heartbeatBodyReceived(int channelId, HeartbeatBody body) - { - // NO - OP - } - - /** - * Convenience method that writes a frame to the protocol session. Equivalent to calling - * getProtocolSession().write(). - * - * @param frame the frame to write - */ - public void writeFrame(AMQDataBlock frame) - { - _lastSent = frame; - - _lastWriteFuture = _minaProtocolSession.write(frame); - } - - public AMQShortString getContextKey() - { - return _contextKey; - } - - public void setContextKey(AMQShortString contextKey) - { - _contextKey = contextKey; - } - - public List getChannels() - { - return new ArrayList(_channelMap.values()); - } - - public AMQChannel getAndAssertChannel(int channelId) throws AMQException - { - AMQChannel channel = getChannel(channelId); - if (channel == null) - { - throw new AMQException(AMQConstant.NOT_FOUND, "Channel not found with id:" + channelId); - } - - return channel; - } - - public AMQChannel getChannel(int channelId) throws AMQException - { - final AMQChannel channel = - ((channelId & CHANNEL_CACHE_SIZE) == channelId) ? _cachedChannels[channelId] : _channelMap.get(channelId); - if ((channel == null) || channel.isClosing()) - { - return null; - } - else - { - return channel; - } - } - - public boolean channelAwaitingClosure(int channelId) - { - return !_closingChannelsList.isEmpty() && _closingChannelsList.contains(channelId); - } - - public void addChannel(AMQChannel channel) throws AMQException - { - if (_closed) - { - throw new AMQException("Session is closed"); - } - - final int channelId = channel.getChannelId(); - - if (_closingChannelsList.contains(channelId)) - { - throw new AMQException("Session is marked awaiting channel close"); - } - - if (_channelMap.size() == _maxNoOfChannels) - { - String errorMessage = - toString() + ": maximum number of channels has been reached (" + _maxNoOfChannels - + "); can't create channel"; - _logger.error(errorMessage); - throw new AMQException(AMQConstant.NOT_ALLOWED, errorMessage); - } - else - { - _channelMap.put(channel.getChannelId(), channel); - } - - if (((channelId & CHANNEL_CACHE_SIZE) == channelId)) - { - _cachedChannels[channelId] = channel; - } - - checkForNotification(); - } - - private void checkForNotification() - { - int channelsCount = _channelMap.size(); - if (channelsCount >= _maxNoOfChannels) - { - _managedObject.notifyClients("Channel count (" + channelsCount + ") has reached the threshold value"); - } - } - - public Long getMaximumNumberOfChannels() - { - return _maxNoOfChannels; - } - - public void setMaximumNumberOfChannels(Long value) - { - _maxNoOfChannels = value; - } - - public void commitTransactions(AMQChannel channel) throws AMQException - { - if ((channel != null) && channel.isTransactional()) - { - channel.commit(); - } - } - - public void rollbackTransactions(AMQChannel channel) throws AMQException - { - if ((channel != null) && channel.isTransactional()) - { - channel.rollback(); - } - } - - /** - * Close a specific channel. This will remove any resources used by the channel, including:
  • any queue - * subscriptions (this may in turn remove queues if they are auto delete
- * - * @param channelId id of the channel to close - * - * @throws AMQException if an error occurs closing the channel - * @throws IllegalArgumentException if the channel id is not valid - */ - public void closeChannel(int channelId) throws AMQException - { - final AMQChannel channel = getChannel(channelId); - if (channel == null) - { - throw new IllegalArgumentException("Unknown channel id"); - } - else - { - try - { - channel.close(); - markChannelAwaitingCloseOk(channelId); - } - finally - { - removeChannel(channelId); - } - } - } - - public void closeChannelOk(int channelId) - { - // todo QPID-847 - This is called from two lcoations ChannelCloseHandler and ChannelCloseOkHandler. - // When it is the CC_OK_Handler then it makes sence to remove the channel else we will leak memory. - // We do it from the Close Handler as we are sending the OK back to the client. - // While this is AMQP spec compliant. The Java client in the event of an IllegalArgumentException - // will send a close-ok.. Where we should call removeChannel. - // However, due to the poor exception handling on the client. The client-user will be notified of the - // InvalidArgument and if they then decide to close the session/connection then the there will be time - // for that to occur i.e. a new close method be sent before the exeption handling can mark the session closed. - //removeChannel(channelId); - _closingChannelsList.remove(new Integer(channelId)); - } - - private void markChannelAwaitingCloseOk(int channelId) - { - _closingChannelsList.add(channelId); - } - - /** - * In our current implementation this is used by the clustering code. - * - * @param channelId The channel to remove - */ - public void removeChannel(int channelId) - { - _channelMap.remove(channelId); - if ((channelId & CHANNEL_CACHE_SIZE) == channelId) - { - _cachedChannels[channelId] = null; - } - } - - /** - * Initialise heartbeats on the session. - * - * @param delay delay in seconds (not ms) - */ - public void initHeartbeats(int delay) - { - if (delay > 0) - { - _minaProtocolSession.setIdleTime(IdleStatus.WRITER_IDLE, delay); - _minaProtocolSession.setIdleTime(IdleStatus.READER_IDLE, (int) (ApplicationRegistry.getInstance().getConfiguration().getHeartBeatTimeout() * delay)); - } - } - - /** - * Closes all channels that were opened by this protocol session. This frees up all resources used by the channel. - * - * @throws AMQException if an error occurs while closing any channel - */ - private void closeAllChannels() throws AMQException - { - for (AMQChannel channel : _channelMap.values()) - { - channel.close(); - } - - _channelMap.clear(); - for (int i = 0; i <= CHANNEL_CACHE_SIZE; i++) - { - _cachedChannels[i] = null; - } - } - - /** This must be called when the session is _closed in order to free up any resources managed by the session. */ - public void closeSession() throws AMQException - { - if (!_closed) - { - if (_virtualHost != null) - { - _virtualHost.getConnectionRegistry().deregisterConnection(this); - } - - closeAllChannels(); - if (_managedObject != null) - { - _managedObject.unregister(); - } - - for (Task task : _taskList) - { - task.doTask(this); - } - - _closed = true; - - CurrentActor.get().message(_logSubject, ConnectionMessages.CON_1002()); - } - } - - public void closeConnection(int channelId, AMQConnectionException e, boolean closeProtocolSession) throws AMQException - { - if (_logger.isInfoEnabled()) - { - _logger.info("Closing connection due to: " + e.getMessage()); - } - - markChannelAwaitingCloseOk(channelId); - closeSession(); - _stateManager.changeState(AMQState.CONNECTION_CLOSING); - writeFrame(e.getCloseFrame(channelId)); - - if (closeProtocolSession) - { - closeProtocolSession(); - } - } - - public void closeProtocolSession() - { - closeProtocolSession(true); - } - - public void closeProtocolSession(boolean waitLast) - { - if (waitLast && (_lastWriteFuture != null)) - { - _logger.debug("Waiting for last write to join."); - _lastWriteFuture.join(LAST_WRITE_FUTURE_JOIN_TIMEOUT); - } - - _logger.debug("REALLY Closing protocol session:" + _minaProtocolSession); - final CloseFuture future = _minaProtocolSession.close(); - future.join(LAST_WRITE_FUTURE_JOIN_TIMEOUT); - - try - { - _stateManager.changeState(AMQState.CONNECTION_CLOSED); - } - catch (AMQException e) - { - _logger.info(e.getMessage()); - } - } - - public String toString() - { - return _minaProtocolSession.getRemoteAddress() + "(" + (getAuthorizedID() == null ? "?" : getAuthorizedID().getName() + ")"); - } - - public String dump() - { - return this + " last_sent=" + _lastSent + " last_received=" + _lastReceived; - } - - /** @return an object that can be used to identity */ - public Object getKey() - { - return _minaProtocolSession.getRemoteAddress(); - } - - /** - * Get the fully qualified domain name of the local address to which this session is bound. Since some servers may - * be bound to multiple addresses this could vary depending on the acceptor this session was created from. - * - * @return a String FQDN - */ - public String getLocalFQDN() - { - SocketAddress address = _minaProtocolSession.getLocalAddress(); - // we use the vmpipe address in some tests hence the need for this rather ugly test. The host - // information is used by SASL primary. - if (address instanceof InetSocketAddress) - { - return ((InetSocketAddress) address).getHostName(); - } - else if (address instanceof VmPipeAddress) - { - return "vmpipe:" + ((VmPipeAddress) address).getPort(); - } - else - { - throw new IllegalArgumentException("Unsupported socket address class: " + address); - } - } - - public SaslServer getSaslServer() - { - return _saslServer; - } - - public void setSaslServer(SaslServer saslServer) - { - _saslServer = saslServer; - } - - public FieldTable getClientProperties() - { - return _clientProperties; - } - - public void setClientProperties(FieldTable clientProperties) - { - _clientProperties = clientProperties; - if (_clientProperties != null) - { - if (_clientProperties.getString(CLIENT_PROPERTIES_INSTANCE) != null) - { - String clientID = _clientProperties.getString(CLIENT_PROPERTIES_INSTANCE); - setContextKey(new AMQShortString(clientID)); - - // Log the Opening of the connection for this client - _actor.message(ConnectionMessages.CON_1001(clientID, _protocolVersion.toString(), true, true)); - } - - if (_clientProperties.getString(ClientProperties.version.toString()) != null) - { - _clientVersion = new AMQShortString(_clientProperties.getString(ClientProperties.version.toString())); - } - } - _sessionIdentifier = new ProtocolSessionIdentifier(this); - } - - private void setProtocolVersion(ProtocolVersion pv) - { - _protocolVersion = pv; - - _protocolOutputConverter = ProtocolOutputConverterRegistry.getConverter(this); - _dispatcher = ServerMethodDispatcherImpl.createMethodDispatcher(_stateManager, _protocolVersion); - } - - public byte getProtocolMajorVersion() - { - return _protocolVersion.getMajorVersion(); - } - - public ProtocolVersion getProtocolVersion() - { - return _protocolVersion; - } - - public byte getProtocolMinorVersion() - { - return _protocolVersion.getMinorVersion(); - } - - public boolean isProtocolVersion(byte major, byte minor) - { - return (getProtocolMajorVersion() == major) && (getProtocolMinorVersion() == minor); - } - - public MethodRegistry getRegistry() - { - return getMethodRegistry(); - } - - public Object getClientIdentifier() - { - return (_minaProtocolSession != null) ? _minaProtocolSession.getRemoteAddress() : null; - } - - public VirtualHost getVirtualHost() - { - return _virtualHost; - } - - public void setVirtualHost(VirtualHost virtualHost) throws AMQException - { - _virtualHost = virtualHost; - - _actor.virtualHostSelected(this); - _logSubject = new ConnectionLogSubject(this); - - _virtualHost.getConnectionRegistry().registerConnection(this); - - _managedObject = createMBean(); - _managedObject.register(); - } - - public void addSessionCloseTask(Task task) - { - _taskList.add(task); - } - - public void removeSessionCloseTask(Task task) - { - _taskList.remove(task); - } - - public ProtocolOutputConverter getProtocolOutputConverter() - { - return _protocolOutputConverter; - } - - public void setAuthorizedID(Principal authorizedID) - { - _authorizedID = authorizedID; - - // Let the actor know that this connection is now Authorized - _actor.connectionAuthorized(this); - } - - public Principal getAuthorizedID() - { - return _authorizedID; - } - - public SocketAddress getRemoteAddress() - { - return _minaProtocolSession.getRemoteAddress(); - } - - public MethodRegistry getMethodRegistry() - { - return MethodRegistry.getMethodRegistry(getProtocolVersion()); - } - - public MethodDispatcher getMethodDispatcher() - { - return _dispatcher; - } - - public ProtocolSessionIdentifier getSessionIdentifier() - { - return _sessionIdentifier; - } - - public String getClientVersion() - { - return (_clientVersion == null) ? null : _clientVersion.toString(); - } - - public void setSender(Sender sender) - { - // No-op, interface munging between this and AMQProtocolSession - } - - public void init() - { - // No-op, interface munging between this and AMQProtocolSession - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java new file mode 100644 index 0000000000..49bdffb584 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java @@ -0,0 +1,1054 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.security.Principal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.atomic.AtomicLong; + +import javax.management.JMException; +import javax.security.sasl.SaslServer; + +import org.apache.log4j.Logger; +import org.apache.mina.transport.vmpipe.VmPipeAddress; +import org.apache.qpid.AMQChannelException; +import org.apache.qpid.AMQConnectionException; +import org.apache.qpid.AMQException; +import org.apache.qpid.codec.AMQCodecFactory; +import org.apache.qpid.codec.AMQDecoder; +import org.apache.qpid.common.ClientProperties; +import org.apache.qpid.framing.AMQBody; +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.AMQProtocolHeaderException; +import org.apache.qpid.framing.AMQProtocolVersionException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ChannelCloseOkBody; +import org.apache.qpid.framing.ConnectionCloseBody; +import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.HeartbeatBody; +import org.apache.qpid.framing.MethodDispatcher; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.framing.ProtocolInitiation; +import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.pool.Event; +import org.apache.qpid.pool.Job; +import org.apache.qpid.pool.PoolingFilter; +import org.apache.qpid.pool.ReadWriteThreadModel; +import org.apache.qpid.pool.ReferenceCountingExecutorService; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.protocol.AMQMethodListener; +import org.apache.qpid.protocol.ProtocolEngine; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.handler.ServerMethodDispatcherImpl; +import org.apache.qpid.server.logging.LogActor; +import org.apache.qpid.server.logging.LogSubject; +import org.apache.qpid.server.logging.actors.AMQPConnectionActor; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.messages.ConnectionMessages; +import org.apache.qpid.server.logging.subjects.ConnectionLogSubject; +import org.apache.qpid.server.management.Managable; +import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.output.ProtocolOutputConverter; +import org.apache.qpid.server.output.ProtocolOutputConverterRegistry; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.state.AMQState; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; +import org.apache.qpid.transport.NetworkDriver; +import org.apache.qpid.transport.Sender; + +public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocolSession +{ + private static final Logger _logger = Logger.getLogger(AMQProtocolSession.class); + + private static final String CLIENT_PROPERTIES_INSTANCE = ClientProperties.instance.toString(); + + private static final AtomicLong idGenerator = new AtomicLong(0); + + // to save boxing the channelId and looking up in a map... cache in an array the low numbered + // channels. This value must be of the form 2^x - 1. + private static final int CHANNEL_CACHE_SIZE = 0xff; + + private AMQShortString _contextKey; + + private AMQShortString _clientVersion = null; + + private VirtualHost _virtualHost; + + private final Map _channelMap = new HashMap(); + + private final AMQChannel[] _cachedChannels = new AMQChannel[CHANNEL_CACHE_SIZE + 1]; + + private final CopyOnWriteArraySet _frameListeners = new CopyOnWriteArraySet(); + + private final AMQStateManager _stateManager; + + private AMQCodecFactory _codecFactory; + + private AMQProtocolSessionMBean _managedObject; + + private SaslServer _saslServer; + + private Object _lastReceived; + + private Object _lastSent; + + protected boolean _closed; + // maximum number of channels this session should have + private long _maxNoOfChannels = 1000; + + /* AMQP Version for this session */ + private ProtocolVersion _protocolVersion = ProtocolVersion.getLatestSupportedVersion(); + + private FieldTable _clientProperties; + private final List _taskList = new CopyOnWriteArrayList(); + + private List _closingChannelsList = new CopyOnWriteArrayList(); + private ProtocolOutputConverter _protocolOutputConverter; + private Principal _authorizedID; + private MethodDispatcher _dispatcher; + private ProtocolSessionIdentifier _sessionIdentifier; + + // Create a simple ID that increments for ever new Session + private final long _sessionID = idGenerator.getAndIncrement(); + + private AMQPConnectionActor _actor; + private LogSubject _logSubject; + + private NetworkDriver _networkDriver; + + private long _lastIoTime; + + private long _writtenBytes; + private long _readBytes; + + private Job _readJob; + private Job _writeJob; + + private ReferenceCountingExecutorService _poolReference = ReferenceCountingExecutorService.getInstance(); + + public ManagedObject getManagedObject() + { + return _managedObject; + } + + public AMQProtocolEngine(VirtualHostRegistry virtualHostRegistry, NetworkDriver driver) + { + _stateManager = new AMQStateManager(virtualHostRegistry, this); + _networkDriver = driver; + + _codecFactory = new AMQCodecFactory(true, this); + + ReadWriteThreadModel threadModel = ReadWriteThreadModel.getInstance(); + _readJob = new Job(threadModel.getAsynchronousReadFilter(), PoolingFilter.MAX_JOB_EVENTS, true); + _writeJob = new Job(threadModel.getAsynchronousWriteFilter(), PoolingFilter.MAX_JOB_EVENTS, false); + + _actor = new AMQPConnectionActor(this, virtualHostRegistry.getApplicationRegistry().getRootMessageLogger()); + _actor.message(ConnectionMessages.CON_1001(null, null, false, false)); + } + + private AMQProtocolSessionMBean createMBean() throws AMQException + { + try + { + return new AMQProtocolSessionMBean(this); + } + catch (JMException ex) + { + _logger.error("AMQProtocolSession MBean creation has failed ", ex); + throw new AMQException("AMQProtocolSession MBean creation has failed ", ex); + } + } + + public long getSessionID() + { + return _sessionID; + } + + public LogActor getLogActor() + { + return _actor; + } + + @Override + public void received(final ByteBuffer msg) + { + _lastIoTime = System.currentTimeMillis(); + try + { + final ArrayList dataBlocks = _codecFactory.getDecoder().decodeBuffer(msg); + fireAsynchEvent(_readJob, new Event(new Runnable() + { + @Override + public void run() + { + // Decode buffer + + for (AMQDataBlock dataBlock : dataBlocks) + { + try + { + dataBlockReceived(dataBlock); + } + catch (Exception e) + { + e.printStackTrace(); + closeProtocolSession(); + } + } + } + })); + } + catch (Exception e) + { + e.printStackTrace(); + closeProtocolSession(); + } + } + + public void dataBlockReceived(AMQDataBlock message) throws Exception + { + _lastReceived = message; + if (message instanceof ProtocolInitiation) + { + protocolInitiationReceived((ProtocolInitiation) message); + + } + else if (message instanceof AMQFrame) + { + AMQFrame frame = (AMQFrame) message; + frameReceived(frame); + + } + else + { + throw new UnknnownMessageTypeException(message); + } + } + + private void frameReceived(AMQFrame frame) throws AMQException + { + int channelId = frame.getChannel(); + AMQBody body = frame.getBodyFrame(); + + //Look up the Channel's Actor and set that as the current actor + // If that is not available then we can use the ConnectionActor + // that is associated with this AMQMPSession. + LogActor channelActor = null; + if (_channelMap.get(channelId) != null) + { + channelActor = _channelMap.get(channelId).getLogActor(); + } + CurrentActor.set(channelActor == null ? _actor : channelActor); + + try + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Frame Received: " + frame); + } + + // Check that this channel is not closing + if (channelAwaitingClosure(channelId)) + { + if ((frame.getBodyFrame() instanceof ChannelCloseOkBody)) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Channel[" + channelId + "] awaiting closure - processing close-ok"); + } + } + else + { + if (_logger.isInfoEnabled()) + { + _logger.info("Channel[" + channelId + "] awaiting closure. Should close socket as client did not close-ok :" + frame); + } + + closeProtocolSession(); + return; + } + } + + try + { + body.handle(channelId, this); + } + catch (AMQException e) + { + closeChannel(channelId); + throw e; + } + } + finally + { + CurrentActor.remove(); + } + } + + private void protocolInitiationReceived(ProtocolInitiation pi) + { + // this ensures the codec never checks for a PI message again + ((AMQDecoder) _codecFactory.getDecoder()).setExpectProtocolInitiation(false); + try + { + // Log incomming protocol negotiation request + _actor.message(ConnectionMessages.CON_1001(null, pi._protocolMajor + "-" + pi._protocolMinor, false, true)); + + ProtocolVersion pv = pi.checkVersion(); // Fails if not correct + + // This sets the protocol version (and hence framing classes) for this session. + setProtocolVersion(pv); + + String mechanisms = ApplicationRegistry.getInstance().getAuthenticationManager().getMechanisms(); + + String locales = "en_US"; + + AMQMethodBody responseBody = getMethodRegistry().createConnectionStartBody((short) getProtocolMajorVersion(), + (short) getProtocolMinorVersion(), + null, + mechanisms.getBytes(), + locales.getBytes()); + _networkDriver.send(responseBody.generateFrame(0).toNioByteBuffer()); + + } + catch (AMQException e) + { + _logger.info("Received unsupported protocol initiation for protocol version: " + getProtocolVersion()); + + _networkDriver.send(new ProtocolInitiation(ProtocolVersion.getLatestSupportedVersion()).toNioByteBuffer()); + } + } + + public void methodFrameReceived(int channelId, AMQMethodBody methodBody) + { + + final AMQMethodEvent evt = new AMQMethodEvent(channelId, methodBody); + + try + { + try + { + + boolean wasAnyoneInterested = _stateManager.methodReceived(evt); + + if (!_frameListeners.isEmpty()) + { + for (AMQMethodListener listener : _frameListeners) + { + wasAnyoneInterested = listener.methodReceived(evt) || wasAnyoneInterested; + } + } + + if (!wasAnyoneInterested) + { + throw new AMQNoMethodHandlerException(evt); + } + } + catch (AMQChannelException e) + { + if (getChannel(channelId) != null) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Closing channel due to: " + e.getMessage()); + } + + writeFrame(e.getCloseFrame(channelId)); + closeChannel(channelId); + } + else + { + if (_logger.isDebugEnabled()) + { + _logger.debug("ChannelException occured on non-existent channel:" + e.getMessage()); + } + + if (_logger.isInfoEnabled()) + { + _logger.info("Closing connection due to: " + e.getMessage()); + } + + AMQConnectionException ce = + evt.getMethod().getConnectionException(AMQConstant.CHANNEL_ERROR, + AMQConstant.CHANNEL_ERROR.getName().toString()); + + closeConnection(channelId, ce, false); + } + } + catch (AMQConnectionException e) + { + closeConnection(channelId, e, false); + } + } + catch (Exception e) + { + + for (AMQMethodListener listener : _frameListeners) + { + listener.error(e); + } + + _logger.error("Unexpected exception while processing frame. Closing connection.", e); + + closeProtocolSession(); + } + } + + public void contentHeaderReceived(int channelId, ContentHeaderBody body) throws AMQException + { + + AMQChannel channel = getAndAssertChannel(channelId); + + channel.publishContentHeader(body); + + } + + public void contentBodyReceived(int channelId, ContentBody body) throws AMQException + { + AMQChannel channel = getAndAssertChannel(channelId); + + channel.publishContentBody(body); + } + + public void heartbeatBodyReceived(int channelId, HeartbeatBody body) + { + // NO - OP + } + + /** + * Convenience method that writes a frame to the protocol session. Equivalent to calling + * getProtocolSession().write(). + * + * @param frame the frame to write + */ + public void writeFrame(AMQDataBlock frame) + { + _lastSent = frame; + final ByteBuffer buf = frame.toNioByteBuffer(); + _lastIoTime = System.currentTimeMillis(); + _writtenBytes += buf.remaining(); + fireAsynchEvent(_writeJob, new Event(new Runnable() + { + @Override + public void run() + { + _networkDriver.send(buf); + } + })); + } + + public AMQShortString getContextKey() + { + return _contextKey; + } + + public void setContextKey(AMQShortString contextKey) + { + _contextKey = contextKey; + } + + public List getChannels() + { + return new ArrayList(_channelMap.values()); + } + + public AMQChannel getAndAssertChannel(int channelId) throws AMQException + { + AMQChannel channel = getChannel(channelId); + if (channel == null) + { + throw new AMQException(AMQConstant.NOT_FOUND, "Channel not found with id:" + channelId); + } + + return channel; + } + + public AMQChannel getChannel(int channelId) throws AMQException + { + final AMQChannel channel = + ((channelId & CHANNEL_CACHE_SIZE) == channelId) ? _cachedChannels[channelId] : _channelMap.get(channelId); + if ((channel == null) || channel.isClosing()) + { + return null; + } + else + { + return channel; + } + } + + public boolean channelAwaitingClosure(int channelId) + { + return !_closingChannelsList.isEmpty() && _closingChannelsList.contains(channelId); + } + + public void addChannel(AMQChannel channel) throws AMQException + { + if (_closed) + { + throw new AMQException("Session is closed"); + } + + final int channelId = channel.getChannelId(); + + if (_closingChannelsList.contains(channelId)) + { + throw new AMQException("Session is marked awaiting channel close"); + } + + if (_channelMap.size() == _maxNoOfChannels) + { + String errorMessage = + toString() + ": maximum number of channels has been reached (" + _maxNoOfChannels + + "); can't create channel"; + _logger.error(errorMessage); + throw new AMQException(AMQConstant.NOT_ALLOWED, errorMessage); + } + else + { + _channelMap.put(channel.getChannelId(), channel); + } + + if (((channelId & CHANNEL_CACHE_SIZE) == channelId)) + { + _cachedChannels[channelId] = channel; + } + + checkForNotification(); + } + + private void checkForNotification() + { + int channelsCount = _channelMap.size(); + if (channelsCount >= _maxNoOfChannels) + { + _managedObject.notifyClients("Channel count (" + channelsCount + ") has reached the threshold value"); + } + } + + public Long getMaximumNumberOfChannels() + { + return _maxNoOfChannels; + } + + public void setMaximumNumberOfChannels(Long value) + { + _maxNoOfChannels = value; + } + + public void commitTransactions(AMQChannel channel) throws AMQException + { + if ((channel != null) && channel.isTransactional()) + { + channel.commit(); + } + } + + public void rollbackTransactions(AMQChannel channel) throws AMQException + { + if ((channel != null) && channel.isTransactional()) + { + channel.rollback(); + } + } + + /** + * Close a specific channel. This will remove any resources used by the channel, including:
  • any queue + * subscriptions (this may in turn remove queues if they are auto delete
+ * + * @param channelId id of the channel to close + * + * @throws AMQException if an error occurs closing the channel + * @throws IllegalArgumentException if the channel id is not valid + */ + public void closeChannel(int channelId) throws AMQException + { + final AMQChannel channel = getChannel(channelId); + if (channel == null) + { + throw new IllegalArgumentException("Unknown channel id"); + } + else + { + try + { + channel.close(); + markChannelAwaitingCloseOk(channelId); + } + finally + { + removeChannel(channelId); + } + } + } + + public void closeChannelOk(int channelId) + { + // todo QPID-847 - This is called from two lcoations ChannelCloseHandler and ChannelCloseOkHandler. + // When it is the CC_OK_Handler then it makes sence to remove the channel else we will leak memory. + // We do it from the Close Handler as we are sending the OK back to the client. + // While this is AMQP spec compliant. The Java client in the event of an IllegalArgumentException + // will send a close-ok.. Where we should call removeChannel. + // However, due to the poor exception handling on the client. The client-user will be notified of the + // InvalidArgument and if they then decide to close the session/connection then the there will be time + // for that to occur i.e. a new close method be sent before the exeption handling can mark the session closed. + //removeChannel(channelId); + _closingChannelsList.remove(new Integer(channelId)); + } + + private void markChannelAwaitingCloseOk(int channelId) + { + _closingChannelsList.add(channelId); + } + + /** + * In our current implementation this is used by the clustering code. + * + * @param channelId The channel to remove + */ + public void removeChannel(int channelId) + { + _channelMap.remove(channelId); + if ((channelId & CHANNEL_CACHE_SIZE) == channelId) + { + _cachedChannels[channelId] = null; + } + } + + /** + * Initialise heartbeats on the session. + * + * @param delay delay in seconds (not ms) + */ + public void initHeartbeats(int delay) + { + if (delay > 0) + { + _networkDriver.setMaxWriteIdle(delay); + _networkDriver.setMaxReadIdle((int) (ApplicationRegistry.getInstance().getConfiguration().getHeartBeatTimeout() * delay)); + } + } + + /** + * Closes all channels that were opened by this protocol session. This frees up all resources used by the channel. + * + * @throws AMQException if an error occurs while closing any channel + */ + private void closeAllChannels() throws AMQException + { + for (AMQChannel channel : _channelMap.values()) + { + channel.close(); + } + + _channelMap.clear(); + for (int i = 0; i <= CHANNEL_CACHE_SIZE; i++) + { + _cachedChannels[i] = null; + } + } + + /** This must be called when the session is _closed in order to free up any resources managed by the session. */ + public void closeSession() throws AMQException + { + if (!_closed) + { + if (_virtualHost != null) + { + _virtualHost.getConnectionRegistry().deregisterConnection(this); + } + + closeAllChannels(); + if (_managedObject != null) + { + _managedObject.unregister(); + } + + for (Task task : _taskList) + { + task.doTask(this); + } + + _closed = true; + + CurrentActor.get().message(_logSubject, ConnectionMessages.CON_1002()); + } + } + + public void closeConnection(int channelId, AMQConnectionException e, boolean closeProtocolSession) throws AMQException + { + if (_logger.isInfoEnabled()) + { + _logger.info("Closing connection due to: " + e.getMessage()); + } + + markChannelAwaitingCloseOk(channelId); + closeSession(); + _stateManager.changeState(AMQState.CONNECTION_CLOSING); + writeFrame(e.getCloseFrame(channelId)); + + if (closeProtocolSession) + { + closeProtocolSession(); + } + } + + public void closeProtocolSession() + { + _networkDriver.close(); + try + { + _stateManager.changeState(AMQState.CONNECTION_CLOSED); + } + catch (AMQException e) + { + _logger.info(e.getMessage()); + } + } + + public String toString() + { + return getRemoteAddress() + "(" + (getAuthorizedID() == null ? "?" : getAuthorizedID().getName() + ")"); + } + + public String dump() + { + return this + " last_sent=" + _lastSent + " last_received=" + _lastReceived; + } + + /** @return an object that can be used to identity */ + public Object getKey() + { + return getRemoteAddress(); + } + + /** + * Get the fully qualified domain name of the local address to which this session is bound. Since some servers may + * be bound to multiple addresses this could vary depending on the acceptor this session was created from. + * + * @return a String FQDN + */ + public String getLocalFQDN() + { + SocketAddress address = _networkDriver.getLocalAddress(); + // we use the vmpipe address in some tests hence the need for this rather ugly test. The host + // information is used by SASL primary. + if (address instanceof InetSocketAddress) + { + return ((InetSocketAddress) address).getHostName(); + } + else if (address instanceof VmPipeAddress) + { + return "vmpipe:" + ((VmPipeAddress) address).getPort(); + } + else + { + throw new IllegalArgumentException("Unsupported socket address class: " + address); + } + } + + public SaslServer getSaslServer() + { + return _saslServer; + } + + public void setSaslServer(SaslServer saslServer) + { + _saslServer = saslServer; + } + + public FieldTable getClientProperties() + { + return _clientProperties; + } + + public void setClientProperties(FieldTable clientProperties) + { + _clientProperties = clientProperties; + if (_clientProperties != null) + { + if (_clientProperties.getString(CLIENT_PROPERTIES_INSTANCE) != null) + { + String clientID = _clientProperties.getString(CLIENT_PROPERTIES_INSTANCE); + setContextKey(new AMQShortString(clientID)); + + // Log the Opening of the connection for this client + _actor.message(ConnectionMessages.CON_1001(clientID, _protocolVersion.toString(), true, true)); + } + + if (_clientProperties.getString(ClientProperties.version.toString()) != null) + { + _clientVersion = new AMQShortString(_clientProperties.getString(ClientProperties.version.toString())); + } + } + _sessionIdentifier = new ProtocolSessionIdentifier(this); + } + + private void setProtocolVersion(ProtocolVersion pv) + { + _protocolVersion = pv; + + _protocolOutputConverter = ProtocolOutputConverterRegistry.getConverter(this); + _dispatcher = ServerMethodDispatcherImpl.createMethodDispatcher(_stateManager, _protocolVersion); + } + + public byte getProtocolMajorVersion() + { + return _protocolVersion.getMajorVersion(); + } + + public ProtocolVersion getProtocolVersion() + { + return _protocolVersion; + } + + public byte getProtocolMinorVersion() + { + return _protocolVersion.getMinorVersion(); + } + + public boolean isProtocolVersion(byte major, byte minor) + { + return (getProtocolMajorVersion() == major) && (getProtocolMinorVersion() == minor); + } + + public MethodRegistry getRegistry() + { + return getMethodRegistry(); + } + + public Object getClientIdentifier() + { + return (_networkDriver != null) ? _networkDriver.getRemoteAddress() : null; + } + + public VirtualHost getVirtualHost() + { + return _virtualHost; + } + + public void setVirtualHost(VirtualHost virtualHost) throws AMQException + { + _virtualHost = virtualHost; + + _actor.virtualHostSelected(this); + _logSubject = new ConnectionLogSubject(this); + + _virtualHost.getConnectionRegistry().registerConnection(this); + + _managedObject = createMBean(); + _managedObject.register(); + } + + public void addSessionCloseTask(Task task) + { + _taskList.add(task); + } + + public void removeSessionCloseTask(Task task) + { + _taskList.remove(task); + } + + public ProtocolOutputConverter getProtocolOutputConverter() + { + return _protocolOutputConverter; + } + + public void setAuthorizedID(Principal authorizedID) + { + _authorizedID = authorizedID; + + // Let the actor know that this connection is now Authorized + _actor.connectionAuthorized(this); + } + + public Principal getAuthorizedID() + { + return _authorizedID; + } + + public SocketAddress getRemoteAddress() + { + return _networkDriver.getRemoteAddress(); + } + + public MethodRegistry getMethodRegistry() + { + return MethodRegistry.getMethodRegistry(getProtocolVersion()); + } + + public MethodDispatcher getMethodDispatcher() + { + return _dispatcher; + } + + @Override + public void closed() + { + try + { + closeSession(); + } + catch (AMQException e) + { + _logger.error("Could not close protocol engine", e); + } + } + + @Override + public void readerIdle() + { + // Nothing + } + + @Override + public void setNetworkDriver(NetworkDriver driver) + { + _networkDriver = driver; + } + + @Override + public void writerIdle() + { + _networkDriver.send(HeartbeatBody.FRAME.toNioByteBuffer()); + } + + @Override + public void exception(Throwable throwable) + { + if (throwable instanceof AMQProtocolHeaderException) + { + + writeFrame(new ProtocolInitiation(ProtocolVersion.getLatestSupportedVersion())); + _networkDriver.close(); + + _logger.error("Error in protocol initiation " + this + ":" + getRemoteAddress() + " :" + throwable.getMessage(), throwable); + } + else if (throwable instanceof IOException) + { + _logger.error("IOException caught in" + this + ", session closed implictly: " + throwable); + } + else + { + _logger.error("Exception caught in" + this + ", closing session explictly: " + throwable, throwable); + + + MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(getProtocolVersion()); + ConnectionCloseBody closeBody = methodRegistry.createConnectionCloseBody(200,new AMQShortString(throwable.getMessage()),0,0); + + writeFrame(closeBody.generateFrame(0)); + + _networkDriver.close(); + } + } + + @Override + public void init() + { + // Do nothing + } + + @Override + public void setSender(Sender sender) + { + // Do nothing + } + + @Override + public long getReadBytes() + { + return _readBytes; + } + + public long getWrittenBytes() + { + return _writtenBytes; + } + + public long getLastIoTime() + { + return _lastIoTime; + } + + public ProtocolSessionIdentifier getSessionIdentifier() + { + return _sessionIdentifier; + } + + public String getClientVersion() + { + return (_clientVersion == null) ? null : _clientVersion.toString(); + } + + /** + * Adds an {@link Event} to a {@link Job}, triggering the execution of the job if it is not already running. + * + * @param job The job. + * @param event The event to hand off asynchronously. + */ + void fireAsynchEvent(Job job, Event event) + { + + job.add(event); + + final ExecutorService pool = _poolReference .getPool(); + + if(pool == null) + { + return; + } + + // rather than perform additional checks on pool to check that it hasn't shutdown. + // catch the RejectedExecutionException that will result from executing on a shutdown pool + if (job.activate()) + { + try + { + pool.execute(job); + } + catch(RejectedExecutionException e) + { + _logger.warn("Thread pool shutdown while tasks still outstanding"); + } + } + + } + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngineFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngineFactory.java new file mode 100644 index 0000000000..ff0c007a60 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngineFactory.java @@ -0,0 +1,29 @@ +package org.apache.qpid.server.protocol; + +import org.apache.qpid.protocol.ProtocolEngine; +import org.apache.qpid.protocol.ProtocolEngineFactory; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; +import org.apache.qpid.transport.NetworkDriver; + +public class AMQProtocolEngineFactory implements ProtocolEngineFactory +{ + private VirtualHostRegistry _vhosts; + + public AMQProtocolEngineFactory() + { + this(1); + } + + public AMQProtocolEngineFactory(Integer port) + { + _vhosts = ApplicationRegistry.getInstance(port).getVirtualHostRegistry(); + } + + + public ProtocolEngine newProtocolEngine(NetworkDriver networkDriver) + { + return new AMQProtocolEngine(_vhosts, networkDriver); + } + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java index fff406bb3d..b0bef04354 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java @@ -34,6 +34,7 @@ import org.apache.qpid.server.output.ProtocolOutputConverter; import org.apache.qpid.server.virtualhost.VirtualHost; import java.security.Principal; +import java.util.List; public interface AMQProtocolSession extends AMQVersionAwareProtocolSession @@ -210,5 +211,19 @@ public interface AMQProtocolSession extends AMQVersionAwareProtocolSession public MethodDispatcher getMethodDispatcher(); public ProtocolSessionIdentifier getSessionIdentifier(); + + String getClientVersion(); + + long getLastIoTime(); + + long getWrittenBytes(); + + Long getMaximumNumberOfChannels(); + + void setMaximumNumberOfChannels(Long value); + + void commitTransactions(AMQChannel channel) throws AMQException; + + List getChannels(); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java index 81dbeeded2..8497c95e26 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java @@ -79,7 +79,7 @@ import org.apache.qpid.server.management.ManagedObject; @MBeanDescription("Management Bean for an AMQ Broker Connection") public class AMQProtocolSessionMBean extends AMQManagedObject implements ManagedConnection { - private AMQMinaProtocolSession _session = null; + private AMQProtocolSession _protocolSession = null; private String _name = null; // openmbean data types for representing the channel attributes @@ -92,10 +92,10 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed new AMQShortString("Broker Management Console has closed the connection."); @MBeanConstructor("Creates an MBean exposing an AMQ Broker Connection") - public AMQProtocolSessionMBean(AMQMinaProtocolSession session) throws NotCompliantMBeanException, OpenDataException + public AMQProtocolSessionMBean(AMQProtocolSession amqProtocolSession) throws NotCompliantMBeanException, OpenDataException { super(ManagedConnection.class, ManagedConnection.TYPE, ManagedConnection.VERSION); - _session = session; + _protocolSession = amqProtocolSession; String remote = getRemoteAddress(); remote = "anonymous".equals(remote) ? (remote + hashCode()) : remote; _name = jmxEncode(new StringBuffer(remote), 0).toString(); @@ -128,52 +128,52 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed public String getClientId() { - return (_session.getContextKey() == null) ? null : _session.getContextKey().toString(); + return (_protocolSession.getContextKey() == null) ? null : _protocolSession.getContextKey().toString(); } public String getAuthorizedId() { - return (_session.getAuthorizedID() != null ) ? _session.getAuthorizedID().getName() : null; + return (_protocolSession.getAuthorizedID() != null ) ? _protocolSession.getAuthorizedID().getName() : null; } public String getVersion() { - return (_session.getClientVersion() == null) ? null : _session.getClientVersion().toString(); + return (_protocolSession.getClientVersion() == null) ? null : _protocolSession.getClientVersion().toString(); } public Date getLastIoTime() { - return new Date(_session.getIOSession().getLastIoTime()); + return new Date(_protocolSession.getLastIoTime()); } public String getRemoteAddress() { - return _session.getIOSession().getRemoteAddress().toString(); + return _protocolSession.getRemoteAddress().toString(); } public ManagedObject getParentObject() { - return _session.getVirtualHost().getManagedObject(); + return _protocolSession.getVirtualHost().getManagedObject(); } public Long getWrittenBytes() { - return _session.getIOSession().getWrittenBytes(); + return _protocolSession.getWrittenBytes(); } public Long getReadBytes() { - return _session.getIOSession().getReadBytes(); + return _protocolSession.getWrittenBytes(); } public Long getMaximumNumberOfChannels() { - return _session.getMaximumNumberOfChannels(); + return _protocolSession.getMaximumNumberOfChannels(); } public void setMaximumNumberOfChannels(Long value) { - _session.setMaximumNumberOfChannels(value); + _protocolSession.setMaximumNumberOfChannels(value); } public String getObjectInstanceName() @@ -192,13 +192,13 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger())); try { - AMQChannel channel = _session.getChannel(channelId); + AMQChannel channel = _protocolSession.getChannel(channelId); if (channel == null) { throw new JMException("The channel (channel Id = " + channelId + ") does not exist"); } - _session.commitTransactions(channel); + _protocolSession.commitTransactions(channel); } catch (AMQException ex) { @@ -221,13 +221,13 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger())); try { - AMQChannel channel = _session.getChannel(channelId); + AMQChannel channel = _protocolSession.getChannel(channelId); if (channel == null) { throw new JMException("The channel (channel Id = " + channelId + ") does not exist"); } - _session.rollbackTransactions(channel); + _protocolSession.commitTransactions(channel); } catch (AMQException ex) { @@ -248,7 +248,7 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed public TabularData channels() throws OpenDataException { TabularDataSupport channelsList = new TabularDataSupport(_channelsType); - List list = _session.getChannels(); + List list = _protocolSession.getChannels(); for (AMQChannel channel : list) { @@ -274,7 +274,7 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed public void closeConnection() throws JMException { - MethodRegistry methodRegistry = _session.getMethodRegistry(); + MethodRegistry methodRegistry = _protocolSession.getMethodRegistry(); ConnectionCloseBody responseBody = methodRegistry.createConnectionCloseBody(AMQConstant.REPLY_SUCCESS.getCode(), // replyCode @@ -301,12 +301,12 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed try { - _session.writeFrame(responseBody.generateFrame(0)); + _protocolSession.writeFrame(responseBody.generateFrame(0)); try { - _session.closeSession(); + _protocolSession.closeSession(); } catch (AMQException ex) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java index b6137e83de..9575bfa1ec 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java @@ -258,7 +258,7 @@ public abstract class ApplicationRegistry implements IApplicationRegistry for (InetSocketAddress bindAddress : _acceptors.keySet()) { QpidAcceptor acceptor = _acceptors.get(bindAddress); - acceptor.getIoAcceptor().unbind(bindAddress); + acceptor.getNetworkDriver().close(); CurrentActor.get().message(BrokerMessages.BRK_1003(acceptor.toString(), bindAddress.getPort())); } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/network/FirewallPlugin.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/network/FirewallPlugin.java index 810be8ae22..3a81932123 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/network/FirewallPlugin.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/network/FirewallPlugin.java @@ -23,7 +23,6 @@ package org.apache.qpid.server.security.access.plugins.network; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; -import java.util.Iterator; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; @@ -32,7 +31,6 @@ import org.apache.commons.configuration.CompositeConfiguration; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.XMLConfiguration; -import org.apache.qpid.server.protocol.AMQMinaProtocolSession; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.security.access.ACLPlugin; import org.apache.qpid.server.security.access.ACLPluginFactory; @@ -180,13 +178,13 @@ public class FirewallPlugin extends AbstractACLPlugin @Override public AuthzResult authoriseConnect(AMQProtocolSession session, VirtualHost virtualHost) { - if (!(session instanceof AMQMinaProtocolSession)) + SocketAddress sockAddr = session.getRemoteAddress(); + if (!(sockAddr instanceof InetSocketAddress)) { - return AuthzResult.ABSTAIN; // We only deal with tcp sessions, which - // mean MINA right now + return AuthzResult.ABSTAIN; // We only deal with tcp sessions } - InetAddress addr = getInetAdressFromMinaSession((AMQMinaProtocolSession) session); + InetAddress addr = ((InetSocketAddress) sockAddr).getAddress(); if (addr == null) { @@ -213,19 +211,6 @@ public class FirewallPlugin extends AbstractACLPlugin } - private InetAddress getInetAdressFromMinaSession(AMQMinaProtocolSession session) - { - SocketAddress remote = session.getIOSession().getRemoteAddress(); - if (remote instanceof InetSocketAddress) - { - return ((InetSocketAddress) remote).getAddress(); - } - else - { - return null; - } - } - @Override public void setConfiguration(Configuration config) throws ConfigurationException { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/QpidAcceptor.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/QpidAcceptor.java index 61cc7cdeb6..3ca22b60c8 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/QpidAcceptor.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/QpidAcceptor.java @@ -20,21 +20,21 @@ */ package org.apache.qpid.server.transport; -import org.apache.mina.common.IoAcceptor; +import org.apache.qpid.transport.NetworkDriver; public class QpidAcceptor { - IoAcceptor _acceptor; + NetworkDriver _driver; String _protocol; - public QpidAcceptor(IoAcceptor acceptor, String protocol) + public QpidAcceptor(NetworkDriver driver, String protocol) { - _acceptor = acceptor; + _driver = driver; _protocol = protocol; } - public IoAcceptor getIoAcceptor() + public NetworkDriver getNetworkDriver() { - return _acceptor; + return _driver; } public String toString() diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java index 8cb0837b39..1162687741 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java @@ -20,27 +20,27 @@ */ package org.apache.qpid.server.configuration; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; + import junit.framework.TestCase; + import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.XMLConfiguration; -import org.apache.qpid.codec.AMQCodecFactory; -import org.apache.qpid.server.protocol.AMQMinaProtocolSession; +import org.apache.qpid.server.protocol.AMQProtocolEngine; import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.protocol.TestIoSession; +import org.apache.qpid.server.protocol.TestNetworkDriver; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; - public class ServerConfigurationTest extends TestCase { @@ -589,12 +589,12 @@ public class ServerConfigurationTest extends TestCase { // Check default ServerConfiguration serverConfig = new ServerConfiguration(_config); - assertEquals(true, serverConfig.getSSLOnly()); + assertEquals(false, serverConfig.getSSLOnly()); // Check value we set - _config.setProperty("connector.ssl.sslOnly", false); + _config.setProperty("connector.ssl.sslOnly", true); serverConfig = new ServerConfiguration(_config); - assertEquals(false, serverConfig.getSSLOnly()); + assertEquals(true, serverConfig.getSSLOnly()); } public void testGetSSLPort() throws ConfigurationException @@ -791,16 +791,15 @@ public class ServerConfigurationTest extends TestCase // Test config VirtualHostRegistry virtualHostRegistry = reg.getVirtualHostRegistry(); VirtualHost virtualHost = virtualHostRegistry.getVirtualHost("test"); - AMQCodecFactory codecFactory = new AMQCodecFactory(true); - - TestIoSession iosession = new TestIoSession(); - iosession.setAddress("127.0.0.1"); - AMQProtocolSession session = new AMQMinaProtocolSession(iosession, virtualHostRegistry, codecFactory); + TestNetworkDriver testDriver = new TestNetworkDriver(); + testDriver.setAddress("127.0.0.1"); + + AMQProtocolEngine session = new AMQProtocolEngine(virtualHostRegistry, testDriver); assertFalse(reg.getAccessManager().authoriseConnect(session, virtualHost)); - iosession.setAddress("127.1.2.3"); - session = new AMQMinaProtocolSession(iosession, virtualHostRegistry, codecFactory); + testDriver.setAddress("127.1.2.3"); + session = new AMQProtocolEngine(virtualHostRegistry, testDriver); assertTrue(reg.getAccessManager().authoriseConnect(session, virtualHost)); } @@ -866,12 +865,12 @@ public class ServerConfigurationTest extends TestCase // Test config VirtualHostRegistry virtualHostRegistry = reg.getVirtualHostRegistry(); VirtualHost virtualHost = virtualHostRegistry.getVirtualHost("test"); - AMQCodecFactory codecFactory = new AMQCodecFactory(true); - TestIoSession iosession = new TestIoSession(); - iosession.setAddress("127.0.0.1"); + TestNetworkDriver testDriver = new TestNetworkDriver(); + testDriver.setAddress("127.0.0.1"); - AMQProtocolSession session = new AMQMinaProtocolSession(iosession, virtualHostRegistry, codecFactory); + AMQProtocolEngine session = new AMQProtocolEngine(virtualHostRegistry, testDriver); + session.setNetworkDriver(testDriver); assertFalse(reg.getAccessManager().authoriseConnect(session, virtualHost)); } @@ -935,12 +934,11 @@ public class ServerConfigurationTest extends TestCase ApplicationRegistry.initialise(reg, 1); // Test config - TestIoSession iosession = new TestIoSession(); - iosession.setAddress("127.0.0.1"); + TestNetworkDriver testDriver = new TestNetworkDriver(); + testDriver.setAddress("127.0.0.1"); VirtualHostRegistry virtualHostRegistry = reg.getVirtualHostRegistry(); VirtualHost virtualHost = virtualHostRegistry.getVirtualHost("test"); - AMQCodecFactory codecFactory = new AMQCodecFactory(true); - AMQProtocolSession session = new AMQMinaProtocolSession(iosession, virtualHostRegistry, codecFactory); + AMQProtocolSession session = new AMQProtocolEngine(virtualHostRegistry, testDriver); assertFalse(reg.getAccessManager().authoriseConnect(session, virtualHost)); RandomAccessFile fileBRandom = new RandomAccessFile(fileB, "rw"); diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java index e199255f50..bc36c61382 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java @@ -44,7 +44,7 @@ public class AMQProtocolSessionMBeanTest extends TestCase private static final Logger log = Logger.getLogger(AMQProtocolSessionMBeanTest.class); private MessageStore _messageStore = new SkeletonMessageStore(); - private AMQMinaProtocolSession _protocolSession; + private AMQProtocolEngine _protocolSession; private AMQChannel _channel; private AMQProtocolSessionMBean _mbean; diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java index 37dfead2e5..c4362f2c60 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java @@ -20,23 +20,22 @@ */ package org.apache.qpid.server.protocol; -import org.apache.qpid.AMQException; -import org.apache.qpid.codec.AMQCodecFactory; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.output.ProtocolOutputConverter; -import org.apache.qpid.server.queue.AMQMessage; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.virtualhost.VirtualHost; - +import java.security.Principal; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; -import java.security.Principal; -public class InternalTestProtocolSession extends AMQMinaProtocolSession implements ProtocolOutputConverter +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.output.ProtocolOutputConverter; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class InternalTestProtocolSession extends AMQProtocolEngine implements ProtocolOutputConverter { // ChannelID(LIST) -> LinkedList final Map>> _channelDelivers; @@ -44,9 +43,7 @@ public class InternalTestProtocolSession extends AMQMinaProtocolSession implemen public InternalTestProtocolSession(VirtualHost virtualHost) throws AMQException { - super(new TestIoSession(), - ApplicationRegistry.getInstance().getVirtualHostRegistry(), - new AMQCodecFactory(true)); + super(ApplicationRegistry.getInstance().getVirtualHostRegistry(), new TestNetworkDriver()); _channelDelivers = new HashMap>>(); diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/MaxChannelsTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/MaxChannelsTest.java index 8597fc5863..e37492bcb0 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/MaxChannelsTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/MaxChannelsTest.java @@ -37,7 +37,7 @@ import java.security.Principal; /** Test class to test MBean operations for AMQMinaProtocolSession. */ public class MaxChannelsTest extends TestCase { - private AMQMinaProtocolSession _session; + private AMQProtocolEngine _session; public void testChannels() throws Exception { diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/TestIoSession.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/TestIoSession.java deleted file mode 100644 index 211f491867..0000000000 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/TestIoSession.java +++ /dev/null @@ -1,328 +0,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.server.protocol; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import org.apache.mina.common.CloseFuture; -import org.apache.mina.common.IdleStatus; -import org.apache.mina.common.IoFilterChain; -import org.apache.mina.common.IoHandler; -import org.apache.mina.common.IoService; -import org.apache.mina.common.IoServiceConfig; -import org.apache.mina.common.IoSession; -import org.apache.mina.common.IoSessionConfig; -import org.apache.mina.common.ThreadModel; -import org.apache.mina.common.TrafficMask; -import org.apache.mina.common.TransportType; -import org.apache.mina.common.WriteFuture; -import org.apache.mina.transport.socket.nio.SocketAcceptorConfig; -import org.apache.qpid.pool.ReadWriteThreadModel; - -/** - * Test implementation of IoSession, which is required for some tests. Methods not being used are not implemented, - * so if this class is being used and some methods are to be used, then please update those. - */ -public class TestIoSession implements IoSession -{ - private final ConcurrentMap attributes = new ConcurrentHashMap(); - private String _address = "127.0.0.1"; - private int _port = 1; - - public TestIoSession() - { - } - - public IoService getService() - { - return null; - } - - public IoServiceConfig getServiceConfig() - { - return new TestIoConfig(); - } - - public IoHandler getHandler() - { - return null; - } - - public IoSessionConfig getConfig() - { - return null; - } - - public IoFilterChain getFilterChain() - { - return null; - } - - public WriteFuture write(Object message) - { - return null; - } - - public CloseFuture close() - { - return null; - } - - public Object getAttachment() - { - return getAttribute(""); - } - - public Object setAttachment(Object attachment) - { - return setAttribute("",attachment); - } - - public Object getAttribute(String key) - { - return attributes.get(key); - } - - public Object setAttribute(String key, Object value) - { - return attributes.put(key,value); - } - - public Object setAttribute(String key) - { - return attributes.put(key, Boolean.TRUE); - } - - public Object removeAttribute(String key) - { - return attributes.remove(key); - } - - public boolean containsAttribute(String key) - { - return attributes.containsKey(key); - } - - public Set getAttributeKeys() - { - return attributes.keySet(); - } - - public TransportType getTransportType() - { - return null; - } - - public boolean isConnected() - { - return false; - } - - public boolean isClosing() - { - return false; - } - - public CloseFuture getCloseFuture() - { - return null; - } - - public SocketAddress getRemoteAddress() - { - return new InetSocketAddress(getAddress(), getPort()); - } - - public SocketAddress getLocalAddress() - { - return null; - } - - public SocketAddress getServiceAddress() - { - return null; - } - - public int getIdleTime(IdleStatus status) - { - return 0; - } - - public long getIdleTimeInMillis(IdleStatus status) - { - return 0; - } - - public void setIdleTime(IdleStatus status, int idleTime) - { - - } - - public int getWriteTimeout() - { - return 0; - } - - public long getWriteTimeoutInMillis() - { - return 0; - } - - public void setWriteTimeout(int writeTimeout) - { - - } - - public TrafficMask getTrafficMask() - { - return null; - } - - public void setTrafficMask(TrafficMask trafficMask) - { - - } - - public void suspendRead() - { - - } - - public void suspendWrite() - { - - } - - public void resumeRead() - { - - } - - public void resumeWrite() - { - - } - - public long getReadBytes() - { - return 0; - } - - public long getWrittenBytes() - { - return 0; - } - - public long getReadMessages() - { - return 0; - } - - public long getWrittenMessages() - { - return 0; - } - - public long getWrittenWriteRequests() - { - return 0; - } - - public int getScheduledWriteRequests() - { - return 0; - } - - public int getScheduledWriteBytes() - { - return 0; - } - - public long getCreationTime() - { - return 0; - } - - public long getLastIoTime() - { - return 0; - } - - public long getLastReadTime() - { - return 0; - } - - public long getLastWriteTime() - { - return 0; - } - - public boolean isIdle(IdleStatus status) - { - return false; - } - - public int getIdleCount(IdleStatus status) - { - return 0; - } - - public long getLastIdleTime(IdleStatus status) - { - return 0; - } - - public void setAddress(String string) - { - this._address = string; - } - - public String getAddress() - { - return _address; - } - - public void setPort(int _port) - { - this._port = _port; - } - - public int getPort() - { - return _port; - } - - /** - * Test implementation of IoServiceConfig - */ - private class TestIoConfig extends SocketAcceptorConfig - { - public ThreadModel getThreadModel() - { - return ReadWriteThreadModel.getInstance(); - } - } -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/TestNetworkDriver.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/TestNetworkDriver.java new file mode 100644 index 0000000000..098843d315 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/TestNetworkDriver.java @@ -0,0 +1,126 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.protocol; + +import java.net.BindException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import javax.net.ssl.SSLEngine; + +import org.apache.qpid.protocol.ProtocolEngine; +import org.apache.qpid.protocol.ProtocolEngineFactory; +import org.apache.qpid.ssl.SSLContextFactory; +import org.apache.qpid.transport.NetworkDriver; +import org.apache.qpid.transport.NetworkDriverConfiguration; +import org.apache.qpid.transport.OpenException; + +/** + * Test implementation of IoSession, which is required for some tests. Methods not being used are not implemented, + * so if this class is being used and some methods are to be used, then please update those. + */ +public class TestNetworkDriver implements NetworkDriver +{ + private final ConcurrentMap attributes = new ConcurrentHashMap(); + private String _address = "127.0.0.1"; + private int _port = 1; + + public TestNetworkDriver() + { + } + + public void setAddress(String string) + { + this._address = string; + } + + public String getAddress() + { + return _address; + } + + public void setPort(int _port) + { + this._port = _port; + } + + public int getPort() + { + return _port; + } + + public void bind(int port, InetAddress[] addresses, ProtocolEngineFactory protocolFactory, + NetworkDriverConfiguration config, SSLContextFactory sslFactory) throws BindException + { + + } + + public SocketAddress getLocalAddress() + { + return new InetSocketAddress(_address, _port); + } + + public SocketAddress getRemoteAddress() + { + return new InetSocketAddress(_address, _port); + } + + public void open(int port, InetAddress destination, ProtocolEngine engine, NetworkDriverConfiguration config, + SSLEngine sslEngine) throws OpenException + { + + } + + public void setMaxReadIdle(int idleTime) + { + + } + + public void setMaxWriteIdle(int idleTime) + { + + } + + public void close() + { + + } + + public void flush() + { + + } + + public void send(ByteBuffer msg) + { + + } + + public void setIdleTimeout(long l) + { + + } + +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java index 8c6260ca9e..19470e6226 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java @@ -20,37 +20,34 @@ */ package org.apache.qpid.server.queue; +import java.util.ArrayList; +import java.util.LinkedList; + +import javax.management.Notification; + import junit.framework.TestCase; + +import org.apache.mina.common.ByteBuffer; import org.apache.qpid.AMQException; -import org.apache.qpid.server.store.MessageStore; -import org.apache.qpid.server.store.MemoryMessageStore; -import org.apache.qpid.server.store.StoreContext; -import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.registry.IApplicationRegistry; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.txn.TransactionalContext; -import org.apache.qpid.server.txn.NonTransactionalContext; -import org.apache.qpid.server.RequiredDeliveryException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.RequiredDeliveryException; import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.protocol.AMQProtocolEngine; +import org.apache.qpid.server.protocol.InternalTestProtocolSession; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.store.MemoryMessageStore; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.StoreContext; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.subscription.SubscriptionFactoryImpl; -import org.apache.qpid.server.protocol.AMQMinaProtocolSession; -import org.apache.qpid.server.protocol.InternalTestProtocolSession; -import org.apache.qpid.framing.ContentHeaderBody; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.abstraction.MessagePublishInfo; -import org.apache.qpid.framing.abstraction.ContentChunk; -import org.apache.commons.configuration.CompositeConfiguration; -import org.apache.mina.common.ByteBuffer; - -import javax.management.Notification; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.Collections; -import java.util.Set; -import java.security.Principal; +import org.apache.qpid.server.txn.NonTransactionalContext; +import org.apache.qpid.server.txn.TransactionalContext; +import org.apache.qpid.server.virtualhost.VirtualHost; /** This class tests all the alerts an AMQQueue can throw based on threshold values of different parameters */ public class AMQQueueAlertTest extends TestCase @@ -62,7 +59,7 @@ public class AMQQueueAlertTest extends TestCase private AMQQueue _queue; private AMQQueueMBean _queueMBean; private VirtualHost _virtualHost; - private AMQMinaProtocolSession _protocolSession; + private AMQProtocolEngine _protocolSession; private MessageStore _messageStore = new MemoryMessageStore(); private StoreContext _storeContext = new StoreContext(); private TransactionalContext _transactionalContext = new NonTransactionalContext(_messageStore, _storeContext, diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/plugins/network/FirewallPluginTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/plugins/network/FirewallPluginTest.java index a497365b06..bda816f5ab 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/plugins/network/FirewallPluginTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/plugins/network/FirewallPluginTest.java @@ -30,17 +30,14 @@ import java.net.InetSocketAddress; import junit.framework.TestCase; import org.apache.commons.configuration.ConfigurationException; -import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.commons.configuration.XMLConfiguration; -import org.apache.qpid.codec.AMQCodecFactory; -import org.apache.qpid.server.configuration.VirtualHostConfiguration; -import org.apache.qpid.server.protocol.AMQMinaProtocolSession; -import org.apache.qpid.server.protocol.TestIoSession; +import org.apache.qpid.server.protocol.AMQProtocolEngine; +import org.apache.qpid.server.protocol.TestNetworkDriver; +import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.security.access.ACLPlugin.AuthzResult; import org.apache.qpid.server.store.TestableMemoryMessageStore; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; -import org.apache.qpid.server.registry.ApplicationRegistry; public class FirewallPluginTest extends TestCase { @@ -84,22 +81,22 @@ public class FirewallPluginTest extends TestCase private TestableMemoryMessageStore _store; private VirtualHost _virtualHost; - private AMQMinaProtocolSession _session; + private AMQProtocolEngine _session; + private TestNetworkDriver _testDriver; @Override public void setUp() throws Exception { super.setUp(); _store = new TestableMemoryMessageStore(); - TestIoSession iosession = new TestIoSession(); - iosession.setAddress("127.0.0.1"); + _testDriver = new TestNetworkDriver(); + _testDriver.setAddress("127.0.0.1"); // Retreive VirtualHost from the Registry VirtualHostRegistry virtualHostRegistry = ApplicationRegistry.getInstance().getVirtualHostRegistry(); _virtualHost = virtualHostRegistry.getVirtualHost("test"); - AMQCodecFactory codecFactory = new AMQCodecFactory(true); - _session = new AMQMinaProtocolSession(iosession, virtualHostRegistry, codecFactory); + _session = new AMQProtocolEngine(virtualHostRegistry, _testDriver); } public void tearDown() throws Exception @@ -170,7 +167,7 @@ public class FirewallPluginTest extends TestCase assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); // Set session IP so that we're connected from the right address - ((TestIoSession) _session.getIOSession()).setAddress("192.168.23.23"); + _testDriver.setAddress("192.168.23.23"); assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); } @@ -185,7 +182,7 @@ public class FirewallPluginTest extends TestCase assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); // Set session IP so that we're connected from the right address - ((TestIoSession) _session.getIOSession()).setAddress("192.168.23.23"); + _testDriver.setAddress("192.168.23.23"); assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); } @@ -198,7 +195,7 @@ public class FirewallPluginTest extends TestCase FirewallPlugin plugin = initialisePlugin("deny", new RuleInfo[]{rule}); // Set session IP so that we're connected from the right address - ((TestIoSession) _session.getIOSession()).setAddress("127.0.0.1"); + _testDriver.setAddress("127.0.0.1"); assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); } @@ -211,7 +208,7 @@ public class FirewallPluginTest extends TestCase FirewallPlugin plugin = initialisePlugin("deny", new RuleInfo[]{rule}); // Set session IP so that we're connected from the right address - ((TestIoSession) _session.getIOSession()).setAddress("127.0.0.1"); + _testDriver.setAddress("127.0.0.1"); assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); } @@ -234,7 +231,7 @@ public class FirewallPluginTest extends TestCase assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); // Set session IP so that we're connected from the right address - ((TestIoSession) _session.getIOSession()).setAddress("192.168.23.23"); + _testDriver.setAddress("192.168.23.23"); assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); } @@ -257,7 +254,7 @@ public class FirewallPluginTest extends TestCase assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); // Set session IP so that we're connected from the right address - ((TestIoSession) _session.getIOSession()).setAddress("192.168.23.23"); + _testDriver.setAddress("192.168.23.23"); assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); } @@ -271,7 +268,7 @@ public class FirewallPluginTest extends TestCase assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); // Set session IP so that we're connected from the right address - ((TestIoSession) _session.getIOSession()).setAddress("192.168.23.23"); + _testDriver.setAddress("192.168.23.23"); assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); } @@ -285,7 +282,7 @@ public class FirewallPluginTest extends TestCase assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); // Set session IP so that we're connected from the right address - ((TestIoSession) _session.getIOSession()).setAddress("192.168.23.23"); + _testDriver.setAddress("192.168.23.23"); assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); } @@ -295,11 +292,11 @@ public class FirewallPluginTest extends TestCase firstRule.setAccess("allow"); firstRule.setHostname("foo, bar, "+new InetSocketAddress("127.0.0.1", 5672).getHostName()); FirewallPlugin plugin = initialisePlugin("deny", new RuleInfo[]{firstRule}); - ((TestIoSession) _session.getIOSession()).setAddress("10.0.0.1"); + _testDriver.setAddress("10.0.0.1"); assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); // Set session IP so that we're connected from the right address - ((TestIoSession) _session.getIOSession()).setAddress("127.0.0.1"); + _testDriver.setAddress("127.0.0.1"); assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java index 2389c9e2da..e3a1a82dc4 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java @@ -191,7 +191,7 @@ public class AMQProtocolHandler extends IoHandlerAdapter _logger.debug("Protocol session created for session " + System.identityHashCode(session)); _failoverHandler = new FailoverHandler(this, session); - final ProtocolCodecFilter pcf = new ProtocolCodecFilter(new AMQCodecFactory(false)); + final ProtocolCodecFilter pcf = new ProtocolCodecFilter(new AMQCodecFactory(false, _protocolSession)); if (Boolean.getBoolean("amqj.shared_read_write_pool")) { diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java index 32cc8c4cb5..4ff24c3607 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java @@ -31,7 +31,10 @@ import org.apache.mina.transport.vmpipe.VmPipeAddress; import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException; import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.pool.ReadWriteThreadModel; +import org.apache.qpid.protocol.ProtocolEngine; +import org.apache.qpid.protocol.ProtocolEngineFactory; import org.apache.qpid.thread.QpidThreadExecutor; +import org.apache.qpid.transport.network.mina.MINANetworkDriver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,7 +65,7 @@ public class TransportConnection private static Logger _logger = LoggerFactory.getLogger(TransportConnection.class); - private static final String DEFAULT_QPID_SERVER = "org.apache.qpid.server.protocol.AMQPFastProtocolHandler"; + private static final String DEFAULT_QPID_SERVER = "org.apache.qpid.server.protocol.AMQProtocolEngineFactory"; private static Map _openSocketRegister = new ConcurrentHashMap(); @@ -190,8 +193,6 @@ public class TransportConnection _acceptor = new VmPipeAcceptor(); IoServiceConfig config = _acceptor.getDefaultConfig(); - - config.setThreadModel(ReadWriteThreadModel.getInstance()); } synchronized (_inVmPipeAddress) { @@ -276,7 +277,10 @@ public class TransportConnection { Class[] cnstr = {Integer.class}; Object[] params = {port}; - provider = (IoHandlerAdapter) Class.forName(protocolProviderClass).getConstructor(cnstr).newInstance(params); + + provider = new MINANetworkDriver(); + ProtocolEngineFactory engineFactory = (ProtocolEngineFactory) Class.forName(protocolProviderClass).getConstructor(cnstr).newInstance(params); + ((MINANetworkDriver) provider).setProtocolEngineFactory(engineFactory, true); // Give the broker a second to create _logger.info("Created VMBroker Instance:" + port); } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java b/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java index fa890d0ebb..591dbd085b 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java @@ -23,6 +23,7 @@ package org.apache.qpid.codec; import org.apache.mina.filter.codec.ProtocolCodecFactory; import org.apache.mina.filter.codec.ProtocolDecoder; import org.apache.mina.filter.codec.ProtocolEncoder; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; /** * AMQCodecFactory is a Mina codec factory. It supplies the encoders and decoders need to read and write the bytes to @@ -50,9 +51,9 @@ public class AMQCodecFactory implements ProtocolCodecFactory * @param expectProtocolInitiation true if the first frame received is going to be a protocol initiation * frame, false if it is going to be a standard AMQ data block. */ - public AMQCodecFactory(boolean expectProtocolInitiation) + public AMQCodecFactory(boolean expectProtocolInitiation, AMQVersionAwareProtocolSession session) { - _frameDecoder = new AMQDecoder(expectProtocolInitiation); + _frameDecoder = new AMQDecoder(expectProtocolInitiation, session); } /** @@ -70,7 +71,7 @@ public class AMQCodecFactory implements ProtocolCodecFactory * * @return The AMQP decoder. */ - public ProtocolDecoder getDecoder() + public AMQDecoder getDecoder() { return _frameDecoder; } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java b/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java index 7eef73f337..281c0761d9 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java @@ -20,14 +20,21 @@ */ package org.apache.qpid.codec; +import java.util.ArrayList; + import org.apache.mina.common.ByteBuffer; import org.apache.mina.common.IoSession; import org.apache.mina.common.SimpleByteBufferAllocator; import org.apache.mina.filter.codec.CumulativeProtocolDecoder; import org.apache.mina.filter.codec.ProtocolDecoderOutput; +import org.apache.qpid.framing.AMQDataBlock; import org.apache.qpid.framing.AMQDataBlockDecoder; +import org.apache.qpid.framing.AMQFrameDecodingException; +import org.apache.qpid.framing.AMQMethodBodyFactory; +import org.apache.qpid.framing.AMQProtocolVersionException; import org.apache.qpid.framing.ProtocolInitiation; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; /** * AMQDecoder delegates the decoding of AMQP either to a data block decoder, or in the case of new connections, to a @@ -62,14 +69,19 @@ public class AMQDecoder extends CumulativeProtocolDecoder private boolean _expectProtocolInitiation; private boolean firstDecode = true; + private AMQMethodBodyFactory _bodyFactory; + + private ByteBuffer _remainingBuf; + /** * Creates a new AMQP decoder. * * @param expectProtocolInitiation true if this decoder needs to handle protocol initiation. */ - public AMQDecoder(boolean expectProtocolInitiation) + public AMQDecoder(boolean expectProtocolInitiation, AMQVersionAwareProtocolSession session) { _expectProtocolInitiation = expectProtocolInitiation; + _bodyFactory = new AMQMethodBodyFactory(session); } /** @@ -120,7 +132,7 @@ public class AMQDecoder extends CumulativeProtocolDecoder protected boolean doDecodeDataBlock(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception { int pos = in.position(); - boolean enoughData = _dataBlockDecoder.decodable(session, in); + boolean enoughData = _dataBlockDecoder.decodable(in.buf()); in.position(pos); if (!enoughData) { @@ -149,7 +161,7 @@ public class AMQDecoder extends CumulativeProtocolDecoder */ private boolean doDecodePI(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception { - boolean enoughData = _piDecoder.decodable(session, in); + boolean enoughData = _piDecoder.decodable(in.buf()); if (!enoughData) { // returning false means it will leave the contents in the buffer and @@ -158,7 +170,8 @@ public class AMQDecoder extends CumulativeProtocolDecoder } else { - _piDecoder.decode(session, in, out); + ProtocolInitiation pi = new ProtocolInitiation(in.buf()); + out.write(pi); return true; } @@ -177,7 +190,7 @@ public class AMQDecoder extends CumulativeProtocolDecoder } - /** + /** * Cumulates content of in into internal buffer and forwards * decoding request to {@link #doDecode(IoSession, ByteBuffer, ProtocolDecoderOutput)}. * doDecode() is invoked repeatedly until it returns false @@ -268,4 +281,60 @@ public class AMQDecoder extends CumulativeProtocolDecoder session.setAttribute( BUFFER, remainingBuf ); } + public ArrayList decodeBuffer(java.nio.ByteBuffer buf) throws AMQFrameDecodingException, AMQProtocolVersionException + { + + // get prior remaining data from accumulator + ArrayList dataBlocks = new ArrayList(); + ByteBuffer msg; + // if we have a session buffer, append data to that otherwise + // use the buffer read from the network directly + if( _remainingBuf != null ) + { + _remainingBuf.put(buf); + _remainingBuf.flip(); + msg = _remainingBuf; + } + else + { + msg = ByteBuffer.wrap(buf); + } + + if (_expectProtocolInitiation + || (firstDecode + && (msg.remaining() > 0) + && (msg.get(msg.position()) == (byte)'A'))) + { + if (_piDecoder.decodable(msg.buf())) + { + dataBlocks.add(new ProtocolInitiation(msg.buf())); + } + } + else + { + boolean enoughData = true; + while (enoughData) + { + int pos = msg.position(); + + enoughData = _dataBlockDecoder.decodable(msg); + msg.position(pos); + if (enoughData) + { + dataBlocks.add(_dataBlockDecoder.createAndPopulateFrame(_bodyFactory, msg)); + } + else + { + _remainingBuf = SIMPLE_BYTE_BUFFER_ALLOCATOR.allocate(msg.remaining(), false); + _remainingBuf.setAutoExpand(true); + _remainingBuf.put(msg); + } + } + } + if(firstDecode && dataBlocks.size() > 0) + { + firstDecode = false; + } + return dataBlocks; + } } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java index 82ffc60802..228867b2b0 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java @@ -47,7 +47,7 @@ public class AMQDataBlockDecoder public AMQDataBlockDecoder() { } - public boolean decodable(IoSession session, ByteBuffer in) throws AMQFrameDecodingException + public boolean decodable(java.nio.ByteBuffer in) throws AMQFrameDecodingException { final int remainingAfterAttributes = in.remaining() - (1 + 2 + 4 + 1); // type, channel, body length and end byte @@ -56,14 +56,15 @@ public class AMQDataBlockDecoder return false; } - in.skip(1 + 2); - final long bodySize = in.getUnsignedInt(); + in.position(in.position() + 1 + 2); + // Get an unsigned int, lifted from MINA ByteBuffer getUnsignedInt() + final long bodySize = in.getInt() & 0xffffffffL; return (remainingAfterAttributes >= bodySize); } - protected Object createAndPopulateFrame(IoSession session, ByteBuffer in) + public AMQFrame createAndPopulateFrame(AMQMethodBodyFactory methodBodyFactory, ByteBuffer in) throws AMQFrameDecodingException, AMQProtocolVersionException { final byte type = in.get(); @@ -71,15 +72,7 @@ public class AMQDataBlockDecoder BodyFactory bodyFactory; if (type == AMQMethodBody.TYPE) { - bodyFactory = (BodyFactory) session.getAttribute(SESSION_METHOD_BODY_FACTORY); - if (bodyFactory == null) - { - AMQVersionAwareProtocolSession protocolSession = (AMQVersionAwareProtocolSession) session.getAttachment(); - bodyFactory = new AMQMethodBodyFactory(protocolSession); - session.setAttribute(SESSION_METHOD_BODY_FACTORY, bodyFactory); - - } - + bodyFactory = methodBodyFactory; } else { @@ -115,6 +108,24 @@ public class AMQDataBlockDecoder public void decode(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception { - out.write(createAndPopulateFrame(session, in)); + AMQMethodBodyFactory bodyFactory = (AMQMethodBodyFactory) session.getAttribute(SESSION_METHOD_BODY_FACTORY); + if (bodyFactory == null) + { + AMQVersionAwareProtocolSession protocolSession = (AMQVersionAwareProtocolSession) session.getAttachment(); + bodyFactory = new AMQMethodBodyFactory(protocolSession); + session.setAttribute(SESSION_METHOD_BODY_FACTORY, bodyFactory); + } + + out.write(createAndPopulateFrame(bodyFactory, in)); + } + + public boolean decodable(ByteBuffer msg) throws AMQFrameDecodingException + { + return decodable(msg.buf()); + } + + public AMQDataBlock createAndPopulateFrame(AMQMethodBodyFactory factory, java.nio.ByteBuffer msg) throws AMQProtocolVersionException, AMQFrameDecodingException + { + return createAndPopulateFrame(factory, ByteBuffer.wrap(msg)); } } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java index 05fd2bb480..374644b4f2 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java @@ -50,7 +50,7 @@ public final class AMQDataBlockEncoder implements MessageEncoder { _logger.debug("Encoded frame byte-buffer is '" + EncodingUtils.convertToHexString(buffer) + "'"); } - + out.write(buffer); } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java index 3ac17e9204..cf8a866e47 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java @@ -20,12 +20,10 @@ */ package org.apache.qpid.framing; -import org.apache.mina.common.ByteBuffer; -import org.apache.mina.common.IoSession; -import org.apache.mina.filter.codec.ProtocolDecoderOutput; import org.apache.qpid.AMQException; import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; public class ProtocolInitiation extends AMQDataBlock implements EncodableAMQDataBlock { @@ -53,13 +51,12 @@ public class ProtocolInitiation extends AMQDataBlock implements EncodableAMQData _protocolMajor = protocolMajor; _protocolMinor = protocolMinor; } - + public ProtocolInitiation(ProtocolVersion pv) { this(AMQP_HEADER, CURRENT_PROTOCOL_CLASS, TCP_PROTOCOL_INSTANCE, pv.getMajorVersion(), pv.getMinorVersion()); } - public ProtocolInitiation(ByteBuffer in) { _protocolHeader = new byte[4]; @@ -71,6 +68,11 @@ public class ProtocolInitiation extends AMQDataBlock implements EncodableAMQData _protocolMinor = in.get(); } + public void writePayload(org.apache.mina.common.ByteBuffer buffer) + { + writePayload(buffer.buf()); + } + public long getSize() { return 4 + 1 + 1 + 1 + 1; @@ -127,16 +129,11 @@ public class ProtocolInitiation extends AMQDataBlock implements EncodableAMQData * @return true if we have enough data to decode the PI frame fully, false if more * data is required */ - public boolean decodable(IoSession session, ByteBuffer in) + public boolean decodable(ByteBuffer in) { return (in.remaining() >= 8); } - public void decode(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) - { - ProtocolInitiation pi = new ProtocolInitiation(in); - out.write(pi); - } } public ProtocolVersion checkVersion() throws AMQException @@ -192,4 +189,5 @@ public class ProtocolInitiation extends AMQDataBlock implements EncodableAMQData buffer.append(Integer.toHexString(_protocolMinor)); return buffer.toString(); } + } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/pool/Event.java b/qpid/java/common/src/main/java/org/apache/qpid/pool/Event.java index 5996cbf89c..49bce9f2f9 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/pool/Event.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/pool/Event.java @@ -45,21 +45,31 @@ import org.apache.mina.common.IoSession; * a continuation. Job is also a continuation, as is the job completion handler. Or, as Event is totally abstract, * it is really an interface, so could just drop it and use the continuation interface instead. */ -public abstract class Event +public class Event { + private Runnable _runner; + + public Event() + { + + } + /** * Creates a continuation. */ - public Event() - { } + public Event(Runnable runner) + { + _runner = runner; + } /** - * Processes the continuation in the context of a Mina session. - * - * @param session The Mina session. + * Processes the continuation */ - public abstract void process(IoSession session); - + public void process() + { + _runner.run(); + } + /** * A continuation ({@link Event}) that takes a Mina messageReceived event, and passes it to a NextFilter. * @@ -68,22 +78,22 @@ public abstract class Event * Pass a Mina messageReceived event to a NextFilter. {@link IoFilter.NextFilter}, {@link IoSession} * */ - public static final class ReceivedEvent extends Event + public static final class MinaReceivedEvent extends Event { private final Object _data; - private final IoFilter.NextFilter _nextFilter; + private final IoSession _session; - public ReceivedEvent(final IoFilter.NextFilter nextFilter, final Object data) + public MinaReceivedEvent(final IoFilter.NextFilter nextFilter, final Object data, final IoSession session) { - super(); _nextFilter = nextFilter; _data = data; + _session = session; } - public void process(IoSession session) + public void process() { - _nextFilter.messageReceived(session, _data); + _nextFilter.messageReceived(_session, _data); } public IoFilter.NextFilter getNextFilter() @@ -101,21 +111,22 @@ public abstract class Event * {@link IoFilter.NextFilter}, {@link IoFilter.WriteRequest}, {@link IoSession} * */ - public static final class WriteEvent extends Event + public static final class MinaWriteEvent extends Event { private final IoFilter.WriteRequest _data; private final IoFilter.NextFilter _nextFilter; + private IoSession _session; - public WriteEvent(final IoFilter.NextFilter nextFilter, final IoFilter.WriteRequest data) + public MinaWriteEvent(final IoFilter.NextFilter nextFilter, final IoFilter.WriteRequest data, final IoSession session) { - super(); _nextFilter = nextFilter; _data = data; + _session = session; } - public void process(IoSession session) + public void process() { - _nextFilter.filterWrite(session, _data); + _nextFilter.filterWrite(_session, _data); } public IoFilter.NextFilter getNextFilter() @@ -135,16 +146,17 @@ public abstract class Event public static final class CloseEvent extends Event { private final IoFilter.NextFilter _nextFilter; + private final IoSession _session; - public CloseEvent(final IoFilter.NextFilter nextFilter) + public CloseEvent(final IoFilter.NextFilter nextFilter, final IoSession session) { - super(); _nextFilter = nextFilter; + _session = session; } - public void process(IoSession session) + public void process() { - _nextFilter.sessionClosed(session); + _nextFilter.sessionClosed(_session); } public IoFilter.NextFilter getNextFilter() diff --git a/qpid/java/common/src/main/java/org/apache/qpid/pool/Job.java b/qpid/java/common/src/main/java/org/apache/qpid/pool/Job.java index 00da005515..4e4192dbe3 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/pool/Job.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/pool/Job.java @@ -55,9 +55,6 @@ public class Job implements ReadWriteRunnable /** The maximum number of events to process per run of the job. More events than this may be queued in the job. */ private final int _maxEvents; - /** The Mina session. */ - private final IoSession _session; - /** Holds the queue of events that make up the job. */ private final java.util.Queue _eventQueue = new ConcurrentLinkedQueue(); @@ -79,7 +76,13 @@ public class Job implements ReadWriteRunnable */ Job(IoSession session, JobCompletionHandler completionHandler, int maxEvents, final boolean readJob) { - _session = session; + _completionHandler = completionHandler; + _maxEvents = maxEvents; + _readJob = readJob; + } + + public Job(JobCompletionHandler completionHandler, int maxEvents, boolean readJob) + { _completionHandler = completionHandler; _maxEvents = maxEvents; _readJob = readJob; @@ -90,7 +93,7 @@ public class Job implements ReadWriteRunnable * * @param evt The continuation to enqueue. */ - void add(Event evt) + public void add(Event evt) { _eventQueue.add(evt); } @@ -111,7 +114,7 @@ public class Job implements ReadWriteRunnable } else { - e.process(_session); + e.process(); } } return false; @@ -153,30 +156,19 @@ public class Job implements ReadWriteRunnable if(processAll()) { deactivate(); - _completionHandler.completed(_session, this); + _completionHandler.completed(this); } else { - _completionHandler.notCompleted(_session, this); + _completionHandler.notCompleted(this); } } - public boolean isReadJob() - { - return _readJob; - } - public boolean isRead() { return _readJob; } - public boolean isWrite() - { - return !_readJob; - } - - /** * Another interface for a continuation. * @@ -185,8 +177,8 @@ public class Job implements ReadWriteRunnable */ static interface JobCompletionHandler { - public void completed(IoSession session, Job job); + public void completed(Job job); - public void notCompleted(final IoSession session, final Job job); + public void notCompleted(final Job job); } } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java b/qpid/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java index a080cc7e04..4863611c42 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java @@ -20,19 +20,17 @@ */ package org.apache.qpid.pool; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.RejectedExecutionException; + import org.apache.mina.common.IdleStatus; import org.apache.mina.common.IoFilterAdapter; import org.apache.mina.common.IoSession; import org.apache.qpid.pool.Event.CloseEvent; - +import org.apache.qpid.pool.Event.MinaReceivedEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.ExecutorService; - /** * PoolingFilter, is a no-op pass through filter that hands all events down the Mina filter chain by default. As it * adds no behaviour by default to the filter chain, it is abstract. @@ -74,7 +72,7 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo private final String _name; /** Defines the maximum number of events that will be batched into a single job. */ - static final int MAX_JOB_EVENTS = Integer.getInteger("amqj.server.read_write_pool.max_events", 10); + public static final int MAX_JOB_EVENTS = Integer.getInteger("amqj.server.read_write_pool.max_events", 10); private final int _maxEvents; @@ -188,7 +186,7 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo Job job = new Job(session, this, MAX_JOB_EVENTS,_readFilter); session.setAttribute(_name, job); } - + /** * Retrieves this filters Job, by this filters name, from the Mina session. * @@ -208,7 +206,7 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo * @param session The Mina session to work in. * @param job The job that completed. */ - public void completed(IoSession session, Job job) + public void completed(Job job) { @@ -239,7 +237,7 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo } } - public void notCompleted(IoSession session, Job job) + public void notCompleted(Job job) { final ExecutorService pool = _poolReference.getPool(); @@ -430,7 +428,7 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo public void messageReceived(NextFilter nextFilter, final IoSession session, Object message) { Job job = getJobForSession(session); - fireAsynchEvent(job, new Event.ReceivedEvent(nextFilter, message)); + fireAsynchEvent(job, new MinaReceivedEvent(nextFilter, message, session)); } /** @@ -442,7 +440,7 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo public void sessionClosed(final NextFilter nextFilter, final IoSession session) { Job job = getJobForSession(session); - fireAsynchEvent(job, new CloseEvent(nextFilter)); + fireAsynchEvent(job, new CloseEvent(nextFilter, session)); } } @@ -473,7 +471,7 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo public void filterWrite(final NextFilter nextFilter, final IoSession session, final WriteRequest writeRequest) { Job job = getJobForSession(session); - fireAsynchEvent(job, new Event.WriteEvent(nextFilter, writeRequest)); + fireAsynchEvent(job, new Event.MinaWriteEvent(nextFilter, writeRequest, session)); } /** @@ -485,7 +483,8 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo public void sessionClosed(final NextFilter nextFilter, final IoSession session) { Job job = getJobForSession(session); - fireAsynchEvent(job, new CloseEvent(nextFilter)); + fireAsynchEvent(job, new CloseEvent(nextFilter, session)); } } + } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteRunnable.java b/qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteRunnable.java index ad04a923e1..140c93ca8d 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteRunnable.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteRunnable.java @@ -23,5 +23,4 @@ package org.apache.qpid.pool; public interface ReadWriteRunnable extends Runnable { boolean isRead(); - boolean isWrite(); } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngineFactory.java b/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngineFactory.java index d8c0f2c916..9df84eef90 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngineFactory.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngineFactory.java @@ -20,10 +20,12 @@ */ package org.apache.qpid.protocol; +import org.apache.qpid.transport.NetworkDriver; + public interface ProtocolEngineFactory { // Returns a new instance of a ProtocolEngine - ProtocolEngine newProtocolEngine(); + ProtocolEngine newProtocolEngine(NetworkDriver networkDriver); } \ No newline at end of file diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriverConfiguration.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriverConfiguration.java index 18cae6bf85..c38afe5dd5 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriverConfiguration.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriverConfiguration.java @@ -28,17 +28,17 @@ package org.apache.qpid.transport; public interface NetworkDriverConfiguration { // Taken from Socket - boolean getKeepAlive(); - boolean getOOBInline(); - boolean getReuseAddress(); + Boolean getKeepAlive(); + Boolean getOOBInline(); + Boolean getReuseAddress(); Integer getSoLinger(); // null means off - int getSoTimeout(); - boolean getTcpNoDelay(); - int getTrafficClass(); + Integer getSoTimeout(); + Boolean getTcpNoDelay(); + Integer getTrafficClass(); // The amount of memory in bytes to allocate to the incoming buffer - int getReceiveBufferSize(); + Integer getReceiveBufferSize(); // The amount of memory in bytes to allocate to the outgoing buffer - int getSendBufferSize(); + Integer getSendBufferSize(); } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java index 7330a042df..477e2cd5af 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java @@ -181,6 +181,7 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver { return _ioSession.getLocalAddress(); } + public void open(int port, InetAddress destination, ProtocolEngine engine, NetworkDriverConfiguration config, SSLEngine sslEngine) throws OpenException @@ -251,6 +252,10 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver public void close() { + if (_lastWriteFuture != null) + { + _lastWriteFuture.join(); + } if (_acceptor != null) { _acceptor.unbindAll(); @@ -359,9 +364,14 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver protocolSession.getFilterChain().remove("tempExecutorFilterForFilterBuilder"); } - + + if (_ioSession == null) + { + _ioSession = protocolSession; + } + // Set up the protocol engine - ProtocolEngine protocolEngine = _factory.newProtocolEngine(); + ProtocolEngine protocolEngine = _factory.newProtocolEngine(this); MINANetworkDriver newDriver = new MINANetworkDriver(_useNIO, _processors, _executorPool, _protectIO, protocolEngine, protocolSession); protocolEngine.setNetworkDriver(newDriver); protocolSession.setAttachment(protocolEngine); @@ -385,4 +395,10 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver return _protocolEngine; } + public void setProtocolEngineFactory(ProtocolEngineFactory engineFactory, boolean acceptingConnections) + { + _factory = engineFactory; + _acceptingConnections = acceptingConnections; + } + } diff --git a/qpid/java/common/src/test/java/org/apache/qpid/codec/AMQDecoderTest.java b/qpid/java/common/src/test/java/org/apache/qpid/codec/AMQDecoderTest.java new file mode 100644 index 0000000000..46c812e265 --- /dev/null +++ b/qpid/java/common/src/test/java/org/apache/qpid/codec/AMQDecoderTest.java @@ -0,0 +1,130 @@ +package org.apache.qpid.codec; + +import java.nio.ByteBuffer; +import java.util.ArrayList; + +import junit.framework.TestCase; + +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQFrameDecodingException; +import org.apache.qpid.framing.AMQProtocolVersionException; +import org.apache.qpid.framing.HeartbeatBody; + +public class AMQDecoderTest extends TestCase +{ + + private AMQCodecFactory _factory; + private AMQDecoder _decoder; + + + public void setUp() + { + _factory = new AMQCodecFactory(false, null); + _decoder = _factory.getDecoder(); + } + + + public void testSingleFrameDecode() throws AMQProtocolVersionException, AMQFrameDecodingException + { + ByteBuffer msg = HeartbeatBody.FRAME.toNioByteBuffer(); + ArrayList frames = _decoder.decodeBuffer(msg); + if (frames.get(0) instanceof AMQFrame) + { + assertEquals(HeartbeatBody.FRAME.getBodyFrame().getFrameType(), ((AMQFrame) frames.get(0)).getBodyFrame().getFrameType()); + } + else + { + fail("decode was not a frame"); + } + } + + public void testPartialFrameDecode() throws AMQProtocolVersionException, AMQFrameDecodingException + { + ByteBuffer msg = HeartbeatBody.FRAME.toNioByteBuffer(); + ByteBuffer msgA = msg.slice(); + int msgbPos = msg.remaining() / 2; + int msgaLimit = msg.remaining() - msgbPos; + msgA.limit(msgaLimit); + msg.position(msgbPos); + ByteBuffer msgB = msg.slice(); + ArrayList frames = _decoder.decodeBuffer(msgA); + assertEquals(0, frames.size()); + frames = _decoder.decodeBuffer(msgB); + assertEquals(1, frames.size()); + if (frames.get(0) instanceof AMQFrame) + { + assertEquals(HeartbeatBody.FRAME.getBodyFrame().getFrameType(), ((AMQFrame) frames.get(0)).getBodyFrame().getFrameType()); + } + else + { + fail("decode was not a frame"); + } + } + + public void testMultipleFrameDecode() throws AMQProtocolVersionException, AMQFrameDecodingException + { + ByteBuffer msgA = HeartbeatBody.FRAME.toNioByteBuffer(); + ByteBuffer msgB = HeartbeatBody.FRAME.toNioByteBuffer(); + ByteBuffer msg = ByteBuffer.allocate(msgA.remaining() + msgB.remaining()); + msg.put(msgA); + msg.put(msgB); + msg.flip(); + ArrayList frames = _decoder.decodeBuffer(msg); + assertEquals(2, frames.size()); + for (AMQDataBlock frame : frames) + { + if (frame instanceof AMQFrame) + { + assertEquals(HeartbeatBody.FRAME.getBodyFrame().getFrameType(), ((AMQFrame) frame).getBodyFrame().getFrameType()); + } + else + { + fail("decode was not a frame"); + } + } + } + + public void testMultiplePartialFrameDecode() throws AMQProtocolVersionException, AMQFrameDecodingException + { + ByteBuffer msgA = HeartbeatBody.FRAME.toNioByteBuffer(); + ByteBuffer msgB = HeartbeatBody.FRAME.toNioByteBuffer(); + ByteBuffer msgC = HeartbeatBody.FRAME.toNioByteBuffer(); + + ByteBuffer sliceA = ByteBuffer.allocate(msgA.remaining() + msgB.remaining() / 2); + sliceA.put(msgA); + int limit = msgB.limit(); + int pos = msgB.remaining() / 2; + msgB.limit(pos); + sliceA.put(msgB); + sliceA.flip(); + msgB.limit(limit); + msgB.position(pos); + + ByteBuffer sliceB = ByteBuffer.allocate(msgB.remaining() + pos); + sliceB.put(msgB); + msgC.limit(pos); + sliceB.put(msgC); + sliceB.flip(); + msgC.limit(limit); + + ArrayList frames = _decoder.decodeBuffer(sliceA); + assertEquals(1, frames.size()); + frames = _decoder.decodeBuffer(sliceB); + assertEquals(1, frames.size()); + frames = _decoder.decodeBuffer(msgC); + assertEquals(1, frames.size()); + for (AMQDataBlock frame : frames) + { + if (frame instanceof AMQFrame) + { + assertEquals(HeartbeatBody.FRAME.getBodyFrame().getFrameType(), ((AMQFrame) frame).getBodyFrame().getFrameType()); + } + else + { + fail("decode was not a frame"); + } + } + } + +} diff --git a/qpid/java/common/src/test/java/org/apache/qpid/codec/MockAMQVersionAwareProtocolSession.java b/qpid/java/common/src/test/java/org/apache/qpid/codec/MockAMQVersionAwareProtocolSession.java new file mode 100644 index 0000000000..bd7fb68d93 --- /dev/null +++ b/qpid/java/common/src/test/java/org/apache/qpid/codec/MockAMQVersionAwareProtocolSession.java @@ -0,0 +1,95 @@ +package org.apache.qpid.codec; + +import java.nio.ByteBuffer; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.HeartbeatBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; +import org.apache.qpid.transport.Sender; + +public class MockAMQVersionAwareProtocolSession implements AMQVersionAwareProtocolSession +{ + + @Override + public void contentBodyReceived(int channelId, ContentBody body) throws AMQException + { + // TODO Auto-generated method stub + + } + + @Override + public void contentHeaderReceived(int channelId, ContentHeaderBody body) throws AMQException + { + // TODO Auto-generated method stub + + } + + @Override + public MethodRegistry getMethodRegistry() + { + return MethodRegistry.getMethodRegistry(ProtocolVersion.v0_9); + } + + @Override + public void heartbeatBodyReceived(int channelId, HeartbeatBody body) throws AMQException + { + // TODO Auto-generated method stub + + } + + @Override + public void init() + { + // TODO Auto-generated method stub + + } + + @Override + public void methodFrameReceived(int channelId, AMQMethodBody body) throws AMQException + { + // TODO Auto-generated method stub + + } + + @Override + public void setSender(Sender sender) + { + // TODO Auto-generated method stub + + } + + @Override + public void writeFrame(AMQDataBlock frame) + { + // TODO Auto-generated method stub + + } + + @Override + public byte getProtocolMajorVersion() + { + // TODO Auto-generated method stub + return 0; + } + + @Override + public byte getProtocolMinorVersion() + { + // TODO Auto-generated method stub + return 0; + } + + @Override + public ProtocolVersion getProtocolVersion() + { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/qpid/java/common/src/test/java/org/apache/qpid/transport/network/mina/MINANetworkDriverTest.java b/qpid/java/common/src/test/java/org/apache/qpid/transport/network/mina/MINANetworkDriverTest.java index 7901f6a99d..6024875cf5 100644 --- a/qpid/java/common/src/test/java/org/apache/qpid/transport/network/mina/MINANetworkDriverTest.java +++ b/qpid/java/common/src/test/java/org/apache/qpid/transport/network/mina/MINANetworkDriverTest.java @@ -299,7 +299,7 @@ public class MINANetworkDriverTest extends TestCase _countingEngine.setNewLatch(TEST_DATA.getBytes().length); _client.send(ByteBuffer.wrap(TEST_DATA.getBytes())); _countingEngine.getExceptionLatch().await(2, TimeUnit.SECONDS); - assertEquals("Exception should not been thrown", 0, + assertEquals("Exception should have been thrown", 0, _countingEngine.getExceptionLatch().getCount()); } @@ -321,11 +321,12 @@ public class MINANetworkDriverTest extends TestCase { EchoProtocolEngine _engine = null; - public ProtocolEngine newProtocolEngine() + public ProtocolEngine newProtocolEngine(NetworkDriver driver) { if (_engine == null) { _engine = new EchoProtocolEngine(); + _engine.setNetworkDriver(driver); } return getEngine(); } -- cgit v1.2.1 From 6e7380bc8c31f5bc1709fe77a14b829575f59e08 Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Thu, 3 Sep 2009 14:09:09 +0000 Subject: QPID-2025: remove dead classes git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-network-refactor@810953 13f79535-47bb-0310-9956-ffa450edef68 --- .../server/protocol/AMQPFastProtocolHandler.java | 290 --------------------- .../qpid/server/protocol/AMQPProtocolProvider.java | 52 ---- 2 files changed, 342 deletions(-) delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPProtocolProvider.java (limited to 'qpid') diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java deleted file mode 100644 index 16b85e67b3..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java +++ /dev/null @@ -1,290 +0,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.server.protocol; - -import java.io.IOException; -import java.net.InetSocketAddress; - -import org.apache.log4j.Logger; -import org.apache.mina.common.ByteBuffer; -import org.apache.mina.common.IdleStatus; -import org.apache.mina.common.IoFilterChain; -import org.apache.mina.common.IoHandlerAdapter; -import org.apache.mina.common.IoSession; -import org.apache.mina.filter.ReadThrottleFilterBuilder; -import org.apache.mina.filter.SSLFilter; -import org.apache.mina.filter.WriteBufferLimitFilterBuilder; -import org.apache.mina.filter.codec.QpidProtocolCodecFilter; -import org.apache.mina.filter.executor.ExecutorFilter; -import org.apache.mina.util.SessionUtil; -import org.apache.qpid.AMQException; -import org.apache.qpid.codec.AMQCodecFactory; -import org.apache.qpid.framing.AMQDataBlock; -import org.apache.qpid.framing.AMQProtocolHeaderException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.ConnectionCloseBody; -import org.apache.qpid.framing.HeartbeatBody; -import org.apache.qpid.framing.MethodRegistry; -import org.apache.qpid.framing.ProtocolInitiation; -import org.apache.qpid.framing.ProtocolVersion; -import org.apache.qpid.server.configuration.ServerConfiguration; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.registry.IApplicationRegistry; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.ssl.SSLContextFactory; - -/** - * The protocol handler handles "protocol events" for all connections. The state - * associated with an individual connection is accessed through the protocol session. - * - * We delegate all frame (message) processing to the AMQProtocolSession which wraps - * the state for the connection. - */ -public class AMQPFastProtocolHandler extends IoHandlerAdapter -{ - private static final Logger _logger = Logger.getLogger(AMQPFastProtocolHandler.class); - - private final IApplicationRegistry _applicationRegistry; - - private final int BUFFER_READ_LIMIT_SIZE; - private final int BUFFER_WRITE_LIMIT_SIZE; - - public AMQPFastProtocolHandler(Integer applicationRegistryInstance) - { - this(ApplicationRegistry.getInstance(applicationRegistryInstance)); - } - - public AMQPFastProtocolHandler(IApplicationRegistry applicationRegistry) - { - _applicationRegistry = applicationRegistry; - - // Read the configuration from the application registry - BUFFER_READ_LIMIT_SIZE = _applicationRegistry.getConfiguration().getBufferReadLimit(); - BUFFER_WRITE_LIMIT_SIZE = _applicationRegistry.getConfiguration().getBufferWriteLimit(); - - _logger.debug("AMQPFastProtocolHandler created"); - } - - protected AMQPFastProtocolHandler(AMQPFastProtocolHandler handler) - { - this(handler._applicationRegistry); - } - - public void sessionCreated(IoSession protocolSession) throws Exception - { - SessionUtil.initialize(protocolSession); - final AMQCodecFactory codecFactory = new AMQCodecFactory(true); - - createSession(protocolSession, _applicationRegistry, codecFactory); - _logger.info("Protocol session created for:" + protocolSession.getRemoteAddress()); - - final QpidProtocolCodecFilter pcf = new QpidProtocolCodecFilter(codecFactory); - final ServerConfiguration config = _applicationRegistry.getConfiguration(); - - String keystorePath = config.getKeystorePath(); - String keystorePassword = config.getKeystorePassword(); - String certType = config.getCertType(); - SSLContextFactory sslContextFactory = null; - boolean isSsl = false; - if (config.getEnableSSL() && isSSLClient(config, protocolSession)) - { - sslContextFactory = new SSLContextFactory(keystorePath, keystorePassword, certType); - isSsl = true; - } - if (config.getEnableExecutorPool()) - { - if (isSsl) - { - protocolSession.getFilterChain().addAfter("AsynchronousReadFilter", "sslFilter", - new SSLFilter(sslContextFactory.buildServerContext())); - } - protocolSession.getFilterChain().addBefore("AsynchronousWriteFilter", "protocolFilter", pcf); - } - else - { - protocolSession.getFilterChain().addLast("protocolFilter", pcf); - if (isSsl) - { - protocolSession.getFilterChain().addBefore("protocolFilter", "sslFilter", - new SSLFilter(sslContextFactory.buildServerContext())); - } - } - - if (ApplicationRegistry.getInstance().getConfiguration().getProtectIOEnabled()) - { - try - { -// //Add IO Protection Filters - IoFilterChain chain = protocolSession.getFilterChain(); - - - protocolSession.getFilterChain().addLast("tempExecutorFilterForFilterBuilder", new ExecutorFilter()); - - ReadThrottleFilterBuilder readfilter = new ReadThrottleFilterBuilder(); - readfilter.setMaximumConnectionBufferSize(BUFFER_READ_LIMIT_SIZE); - readfilter.attach(chain); - - WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder(); - writefilter.setMaximumConnectionBufferSize(BUFFER_WRITE_LIMIT_SIZE); - writefilter.attach(chain); - - protocolSession.getFilterChain().remove("tempExecutorFilterForFilterBuilder"); - _logger.info("Using IO Read/Write Filter Protection"); - } - catch (Exception e) - { - _logger.error("Unable to attach IO Read/Write Filter Protection :" + e.getMessage()); - } - } - } - - /** Separated into its own, protected, method to allow easier reuse */ - protected void createSession(IoSession session, IApplicationRegistry applicationRegistry, AMQCodecFactory codec) throws AMQException - { - new AMQMinaProtocolSession(session, applicationRegistry.getVirtualHostRegistry(), codec); - } - - public void sessionOpened(IoSession protocolSession) throws Exception - { - _logger.info("Session opened for:" + protocolSession.getRemoteAddress()); - } - - public void sessionClosed(IoSession protocolSession) throws Exception - { - _logger.info("Protocol Session closed for:" + protocolSession.getRemoteAddress()); - final AMQProtocolSession amqProtocolSession = AMQMinaProtocolSession.getAMQProtocolSession(protocolSession); - //fixme -- this can be null - if (amqProtocolSession != null) - { - try - { - CurrentActor.set(amqProtocolSession.getLogActor()); - amqProtocolSession.closeSession(); - } - catch (AMQException e) - { - _logger.error("Caught AMQException whilst closingSession:" + e); - } - finally - { - CurrentActor.remove(); - } - } - } - - public void sessionIdle(IoSession session, IdleStatus status) throws Exception - { - _logger.debug("Protocol Session [" + this + "] idle: " + status + " :for:" + session.getRemoteAddress()); - if (IdleStatus.WRITER_IDLE.equals(status)) - { - //write heartbeat frame: - session.write(HeartbeatBody.FRAME); - } - else if (IdleStatus.READER_IDLE.equals(status)) - { - //failover: - throw new IOException("Timed out while waiting for heartbeat from peer."); - } - - } - - public void exceptionCaught(IoSession protocolSession, Throwable throwable) throws Exception - { - AMQProtocolSession session = AMQMinaProtocolSession.getAMQProtocolSession(protocolSession); - if (throwable instanceof AMQProtocolHeaderException) - { - - protocolSession.write(new ProtocolInitiation(ProtocolVersion.getLatestSupportedVersion())); - - protocolSession.close(); - - _logger.error("Error in protocol initiation " + session + ":" + protocolSession.getRemoteAddress() + " :" + throwable.getMessage(), throwable); - } - else if (throwable instanceof IOException) - { - _logger.error("IOException caught in" + session + ", session closed implictly: " + throwable); - } - else - { - _logger.error("Exception caught in" + session + ", closing session explictly: " + throwable, throwable); - - - MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(session.getProtocolVersion()); - ConnectionCloseBody closeBody = methodRegistry.createConnectionCloseBody(200,new AMQShortString(throwable.getMessage()),0,0); - - protocolSession.write(closeBody.generateFrame(0)); - - protocolSession.close(); - } - } - - /** - * Invoked when a message is received on a particular protocol session. Note that a - * protocol session is directly tied to a particular physical connection. - * - * @param protocolSession the protocol session that received the message - * @param message the message itself (i.e. a decoded frame) - * - * @throws Exception if the message cannot be processed - */ - public void messageReceived(IoSession protocolSession, Object message) throws Exception - { - final AMQProtocolSession amqProtocolSession = AMQMinaProtocolSession.getAMQProtocolSession(protocolSession); - - if (message instanceof AMQDataBlock) - { - amqProtocolSession.dataBlockReceived((AMQDataBlock) message); - - } - else if (message instanceof ByteBuffer) - { - throw new IllegalStateException("Handed undecoded ByteBuffer buf = " + message); - } - else - { - throw new IllegalStateException("Handed unhandled message. message.class = " + message.getClass() + " message = " + message); - } - } - - /** - * Called after a message has been sent out on a particular protocol session - * - * @param protocolSession the protocol session (i.e. connection) on which this - * message was sent - * @param object the message (frame) that was encoded and sent - * - * @throws Exception if we want to indicate an error - */ - public void messageSent(IoSession protocolSession, Object object) throws Exception - { - if (_logger.isDebugEnabled()) - { - _logger.debug("Message sent: " + object); - } - } - - protected boolean isSSLClient(ServerConfiguration connectionConfig, - IoSession protocolSession) - { - InetSocketAddress addr = (InetSocketAddress) protocolSession.getLocalAddress(); - return addr.getPort() == connectionConfig.getSSLPort(); - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPProtocolProvider.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPProtocolProvider.java deleted file mode 100644 index 07c153bfe8..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPProtocolProvider.java +++ /dev/null @@ -1,52 +0,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.server.protocol; - -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.registry.IApplicationRegistry; - -/** - * The protocol provide's role is to encapsulate the initialisation of the protocol handler. - * - * The protocol handler (see AMQPFastProtocolHandler class) handles protocol events - * such as connection closing or a frame being received. It can either do this directly - * or pass off to the protocol session in the cases where state information is required to - * deal with the event. - * - */ -public class AMQPProtocolProvider -{ - /** - * Handler for protocol events - */ - private AMQPFastProtocolHandler _handler; - - public AMQPProtocolProvider() - { - IApplicationRegistry registry = ApplicationRegistry.getInstance(); - _handler = new AMQPFastProtocolHandler(registry); - } - - public AMQPFastProtocolHandler getHandler() - { - return _handler; - } -} -- cgit v1.2.1 From 7b28732091473d93ce7546c70fa1d2dbd685161a Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Fri, 4 Sep 2009 09:40:32 +0000 Subject: QPID-2025: Log errors instead of printStackTracing() git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-network-refactor@811326 13f79535-47bb-0310-9956-ffa450edef68 --- .../java/org/apache/qpid/server/protocol/AMQProtocolEngine.java | 4 ++-- .../org/apache/qpid/transport/network/mina/MINANetworkDriver.java | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'qpid') diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java index 49bdffb584..19d98161c6 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java @@ -227,7 +227,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol } catch (Exception e) { - e.printStackTrace(); + _logger.error("Unexpected exception when processing datablock", e); closeProtocolSession(); } } @@ -236,7 +236,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol } catch (Exception e) { - e.printStackTrace(); + _logger.error("Unexpected exception when processing datablock", e); closeProtocolSession(); } } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java index 477e2cd5af..8df3644929 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java @@ -30,13 +30,13 @@ import java.nio.ByteBuffer; import javax.net.ssl.SSLEngine; +import org.apache.log4j.Logger; import org.apache.mina.common.ConnectFuture; import org.apache.mina.common.IdleStatus; import org.apache.mina.common.IoAcceptor; import org.apache.mina.common.IoFilterChain; import org.apache.mina.common.IoHandlerAdapter; import org.apache.mina.common.IoSession; -import org.apache.mina.common.IoSessionConfig; import org.apache.mina.common.SimpleByteBufferAllocator; import org.apache.mina.common.WriteFuture; import org.apache.mina.filter.ReadThrottleFilterBuilder; @@ -80,6 +80,8 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver private WriteFuture _lastWriteFuture; + private static final Logger _logger = Logger.getLogger(MINANetworkDriver.class); + public MINANetworkDriver(boolean useNIO, int processors, boolean executorPool, boolean protectIO) { _useNIO = useNIO; @@ -289,6 +291,10 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver if (_protocolEngine != null) { _protocolEngine.exception(throwable); + } + else + { + _logger.error("Exception thrown and no ProtocolEngine to handle it", throwable); } _lastException = throwable; } -- cgit v1.2.1 From c1ebe66bfab328c5192a35c21ea290b5c45f40f5 Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Wed, 9 Sep 2009 13:05:43 +0000 Subject: Merge from trunk git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-network-refactor@812936 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/buildtools/buildCreator/buildCreator.py | 2 +- qpid/cpp/bindings/qmf/Makefile.am | 10 +- qpid/cpp/bindings/qmf/python/Makefile.am | 9 +- qpid/cpp/bindings/qmf/python/python.i | 120 ++- qpid/cpp/bindings/qmf/python/qmf.py | 854 +++++++++++++++ qpid/cpp/bindings/qmf/qmfengine.i | 30 +- qpid/cpp/bindings/qmf/ruby/Makefile.am | 4 +- qpid/cpp/bindings/qmf/ruby/qmf.rb | 1131 +++++++++++++------- qpid/cpp/bindings/qmf/ruby/ruby.i | 42 +- qpid/cpp/bindings/qmf/tests/Makefile.am | 8 +- qpid/cpp/bindings/qmf/tests/agent_ruby.rb | 111 +- qpid/cpp/bindings/qmf/tests/python_agent.py | 192 ++++ qpid/cpp/bindings/qmf/tests/python_console.py | 99 +- qpid/cpp/bindings/qmf/tests/ruby_console.rb | 61 ++ qpid/cpp/bindings/qmf/tests/run_interop_tests | 64 +- qpid/cpp/configure.ac | 2 + qpid/cpp/docs/api/doxygen_mainpage.h | 2 + qpid/cpp/docs/api/user.doxygen.in | 4 +- qpid/cpp/examples/CMakeLists.txt | 1 + qpid/cpp/examples/Makefile.am | 2 +- qpid/cpp/examples/messaging/CMakeLists.txt | 32 + qpid/cpp/examples/messaging/Makefile.am | 54 + qpid/cpp/examples/messaging/client.cpp | 76 ++ qpid/cpp/examples/messaging/map_receiver.cpp | 54 + qpid/cpp/examples/messaging/map_sender.cpp | 66 ++ qpid/cpp/examples/messaging/queue_listener.cpp | 82 ++ qpid/cpp/examples/messaging/queue_receiver.cpp | 65 ++ qpid/cpp/examples/messaging/queue_sender.cpp | 65 ++ qpid/cpp/examples/messaging/server.cpp | 76 ++ qpid/cpp/examples/messaging/topic_listener.cpp | 82 ++ qpid/cpp/examples/messaging/topic_receiver.cpp | 66 ++ qpid/cpp/examples/messaging/topic_sender.cpp | 78 ++ qpid/cpp/include/qmf/Agent.h | 287 +++++ qpid/cpp/include/qmf/AgentObject.h | 95 ++ qpid/cpp/include/qmf/Connection.h | 125 +++ qpid/cpp/include/qmf/ConnectionSettings.h | 143 +++ qpid/cpp/include/qmf/QmfImportExport.h | 33 + qpid/cpp/include/qpid/client/Connection.h | 14 + qpid/cpp/include/qpid/client/amqp0_10/Codecs.h | 61 ++ qpid/cpp/include/qpid/framing/FieldTable.h | 9 +- qpid/cpp/include/qpid/framing/FieldValue.h | 120 ++- qpid/cpp/include/qpid/framing/List.h | 77 ++ qpid/cpp/include/qpid/framing/Uuid.h | 27 +- qpid/cpp/include/qpid/management/Manageable.h | 2 +- qpid/cpp/include/qpid/messaging/Address.h | 58 + qpid/cpp/include/qpid/messaging/Codec.h | 44 + qpid/cpp/include/qpid/messaging/Connection.h | 67 ++ qpid/cpp/include/qpid/messaging/Filter.h | 48 + qpid/cpp/include/qpid/messaging/Message.h | 88 ++ qpid/cpp/include/qpid/messaging/MessageContent.h | 90 ++ qpid/cpp/include/qpid/messaging/MessageListener.h | 49 + qpid/cpp/include/qpid/messaging/Receiver.h | 115 ++ qpid/cpp/include/qpid/messaging/Sender.h | 57 + qpid/cpp/include/qpid/messaging/Session.h | 99 ++ qpid/cpp/include/qpid/messaging/Variant.h | 167 +++ qpid/cpp/include/qpid/sys/uuid.h | 28 - qpid/cpp/include/qpid/sys/windows/Condition.h | 1 - qpid/cpp/include/qpid/sys/windows/Mutex.h | 1 - qpid/cpp/include/qpid/sys/windows/uuid.h | 38 - qpid/cpp/src/CMakeLists.txt | 68 +- qpid/cpp/src/Makefile.am | 56 +- qpid/cpp/src/qmf.mk | 29 +- qpid/cpp/src/qmf/Agent.cpp | 958 ----------------- qpid/cpp/src/qmf/Agent.h | 206 ---- qpid/cpp/src/qmf/AgentEngine.cpp | 854 +++++++++++++++ qpid/cpp/src/qmf/AgentEngine.h | 207 ++++ qpid/cpp/src/qmf/ConnectionSettingsImpl.cpp | 323 ++++++ qpid/cpp/src/qmf/ConnectionSettingsImpl.h | 60 ++ qpid/cpp/src/qmf/Console.h | 82 -- qpid/cpp/src/qmf/ConsoleEngine.cpp | 886 +++++++++++++++ qpid/cpp/src/qmf/ConsoleEngine.h | 200 ++++ qpid/cpp/src/qmf/Object.h | 2 +- qpid/cpp/src/qmf/ObjectId.h | 1 - qpid/cpp/src/qmf/ObjectIdImpl.cpp | 5 - qpid/cpp/src/qmf/ObjectImpl.cpp | 6 +- qpid/cpp/src/qmf/Protocol.cpp | 52 + qpid/cpp/src/qmf/Protocol.h | 67 ++ qpid/cpp/src/qmf/ResilientConnection.cpp | 76 +- qpid/cpp/src/qmf/ResilientConnection.h | 17 +- qpid/cpp/src/qmf/Schema.h | 23 +- qpid/cpp/src/qmf/SchemaImpl.cpp | 460 +++----- qpid/cpp/src/qmf/SchemaImpl.h | 40 +- qpid/cpp/src/qmf/SequenceManager.cpp | 50 + qpid/cpp/src/qmf/SequenceManager.h | 52 + qpid/cpp/src/qmf/Value.h | 2 +- qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp | 4 +- qpid/cpp/src/qpid/broker/Broker.cpp | 2 +- qpid/cpp/src/qpid/broker/Connection.cpp | 6 +- qpid/cpp/src/qpid/broker/Daemon.cpp | 2 +- qpid/cpp/src/qpid/broker/Exchange.cpp | 19 +- qpid/cpp/src/qpid/broker/Exchange.h | 2 +- qpid/cpp/src/qpid/broker/Link.cpp | 5 +- qpid/cpp/src/qpid/broker/SessionAdapter.cpp | 6 +- qpid/cpp/src/qpid/client/Demux.h | 21 +- qpid/cpp/src/qpid/client/Results.cpp | 1 - qpid/cpp/src/qpid/client/SessionImpl.cpp | 14 +- qpid/cpp/src/qpid/client/SessionImpl.h | 12 +- .../src/qpid/client/amqp0_10/AddressResolution.cpp | 461 ++++++++ .../src/qpid/client/amqp0_10/AddressResolution.h | 68 ++ qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp | 299 ++++++ qpid/cpp/src/qpid/client/amqp0_10/CodecsInternal.h | 41 + .../src/qpid/client/amqp0_10/CompletionTracker.cpp | 48 + .../src/qpid/client/amqp0_10/CompletionTracker.h | 50 + .../src/qpid/client/amqp0_10/ConnectionImpl.cpp | 181 ++++ qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h | 69 ++ .../src/qpid/client/amqp0_10/IncomingMessages.cpp | 244 +++++ .../src/qpid/client/amqp0_10/IncomingMessages.h | 91 ++ qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h | 52 + qpid/cpp/src/qpid/client/amqp0_10/MessageSource.h | 47 + .../src/qpid/client/amqp0_10/OutgoingMessage.cpp | 64 ++ .../cpp/src/qpid/client/amqp0_10/OutgoingMessage.h | 46 + qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp | 190 ++++ qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h | 165 +++ qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp | 98 ++ qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h | 118 ++ qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp | 375 +++++++ qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h | 202 ++++ qpid/cpp/src/qpid/cluster/UpdateClient.cpp | 2 +- qpid/cpp/src/qpid/framing/FieldTable.cpp | 20 +- qpid/cpp/src/qpid/framing/FieldValue.cpp | 35 + qpid/cpp/src/qpid/framing/List.cpp | 83 ++ qpid/cpp/src/qpid/framing/Uuid.cpp | 31 + qpid/cpp/src/qpid/management/Manageable.cpp | 2 +- qpid/cpp/src/qpid/management/ManagementAgent.cpp | 4 +- qpid/cpp/src/qpid/messaging/Address.cpp | 49 + qpid/cpp/src/qpid/messaging/Connection.cpp | 90 ++ qpid/cpp/src/qpid/messaging/ConnectionImpl.h | 45 + qpid/cpp/src/qpid/messaging/Filter.cpp | 39 + qpid/cpp/src/qpid/messaging/Message.cpp | 70 ++ qpid/cpp/src/qpid/messaging/MessageImpl.cpp | 205 ++++ qpid/cpp/src/qpid/messaging/MessageImpl.h | 134 +++ qpid/cpp/src/qpid/messaging/Receiver.cpp | 51 + qpid/cpp/src/qpid/messaging/ReceiverImpl.h | 52 + qpid/cpp/src/qpid/messaging/Sender.cpp | 44 + qpid/cpp/src/qpid/messaging/SenderImpl.h | 44 + qpid/cpp/src/qpid/messaging/Session.cpp | 117 ++ qpid/cpp/src/qpid/messaging/SessionImpl.h | 65 ++ qpid/cpp/src/qpid/messaging/Variant.cpp | 603 +++++++++++ qpid/cpp/src/qpid/sys/Timer.cpp | 5 +- qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp | 26 +- qpid/cpp/src/qpid/sys/uuid.h | 28 + qpid/cpp/src/qpid/sys/windows/uuid.h | 38 + qpid/cpp/src/qpidd.cpp | 2 +- qpid/cpp/src/tests/CMakeLists.txt | 2 + qpid/cpp/src/tests/FieldTable.cpp | 32 +- qpid/cpp/src/tests/Makefile.am | 9 +- qpid/cpp/src/tests/MessagingSessionTests.cpp | 360 +++++++ qpid/cpp/src/tests/Variant.cpp | 157 +++ qpid/cpp/src/tests/cli_tests.py | 22 + .../cpp/src/tests/cluster_python_tests_failing.txt | 1 + qpid/cpp/src/tests/federation.py | 79 +- qpid/cpp/src/tests/qpid_stream.cpp | 163 +++ qpid/cpp/src/tests/txtest.cpp | 9 +- qpid/java/broker/bin/qpid-server | 9 +- qpid/java/broker/etc/log4j.xml | 2 +- .../qpid/server/exchange/HeadersExchange.java | 14 +- .../server/logging/StartupRootMessageLogger.java | 7 + .../management/JMXManagedObjectRegistry.java | 2 + .../security/access/plugins/BasicACLPlugin.java | 6 - .../access/plugins/network/FirewallFactory.java | 45 - .../qpid/server/subscription/SubscriptionImpl.java | 3 +- .../server/security/access/ExchangeDenier.java | 13 - .../qpid/client/AMQAuthenticationException.java | 5 - .../java/org/apache/qpid/client/AMQSession.java | 51 +- .../org/apache/qpid/client/AMQSession_0_8.java | 24 +- .../client/handler/ChannelCloseMethodHandler.java | 91 +- .../handler/ConnectionOpenOkMethodHandler.java | 1 - .../handler/ConnectionTuneMethodHandler.java | 1 - .../protocol/AMQIoTransportProtocolSession.java | 2 +- .../qpid/client/protocol/AMQProtocolHandler.java | 54 +- .../qpid/client/protocol/AMQProtocolSession.java | 4 +- .../apache/qpid/client/state/AMQStateManager.java | 19 +- .../client/util/FlowControllingBlockingQueue.java | 18 +- .../org/apache/qpid/client/MockAMQConnection.java | 2 +- .../client/protocol/AMQProtocolHandlerTest.java | 11 +- .../transport/network/mina/MINANetworkDriver.java | 6 +- .../doc/broker-priority-queue-subscription.dia | Bin 0 -> 2991 bytes qpid/java/doc/broker-queue-subscription.dia | Bin 0 -> 2129 bytes .../qpid/management/ui/jmx/ClientListener.java | 7 +- .../qpid/management/ui/jmx/JMXServerRegistry.java | 41 +- .../management/ui/views/AttributesTabControl.java | 2 +- .../qpid/management/ui/views/NavigationView.java | 4 +- .../connection/ConnectionOperationsTabControl.java | 60 ++ .../exchange/ExchangeOperationsTabControl.java | 4 + .../HeadersExchangeOperationsTabControl.java | 144 ++- .../views/logging/ConfigurationFileTabControl.java | 4 +- .../ui/views/queue/QueueOperationsTabControl.java | 22 +- .../management/ui/views/vhost/VHostTabControl.java | 4 + .../src/main/resources/win32-win32-x86/qpidmc.ini | 22 +- .../management/jmx/ManagementActorLoggingTest.java | 28 +- .../server/logging/SubscriptionLoggingTest.java | 65 +- .../client/DynamicQueueExchangeCreateTest.java | 88 ++ .../close/JavaServerCloseRaceConditionTest.java | 119 ++ .../test/unit/topic/DurableSubscriptionTest.java | 59 +- .../test/unit/transacted/CommitRollbackTest.java | 2 +- .../apache/qpid/test/utils/FailoverBaseCase.java | 4 +- .../main/java/org/apache/qpid/util/LogMonitor.java | 20 +- .../java/org/apache/qpid/util/LogMonitorTest.java | 73 +- qpid/java/test-profiles/010Excludes | 7 + qpid/python/Makefile | 98 ++ qpid/python/commands/qpid-config | 8 + qpid/python/preppy | 67 ++ qpid/python/qpid/compat.py | 7 + qpid/python/qpid/concurrency.py | 65 ++ qpid/python/qpid/datatypes.py | 11 +- qpid/python/qpid/driver.py | 444 ++++++++ qpid/python/qpid/messaging.py | 587 +++++----- qpid/python/qpid/session.py | 14 +- qpid/python/qpid/tests/messaging.py | 124 ++- qpid/python/qpid/util.py | 6 + qpid/python/qpid_config.py | 8 +- qpid/python/tests/datatypes.py | 13 + qpid/python/tests_0-10/alternate_exchange.py | 56 +- qpid/python/tests_0-10/management.py | 22 + qpid/python/tests_0-9/queue.py | 261 ++++- qpid/specs/management-schema.xml | 12 +- qpid/wcf/QpidWcf.sln | 114 ++ qpid/wcf/ReadMe.txt | 162 +++ .../Channel/WCFToWCFDirect/Client/Client.cs | 68 ++ .../Channel/WCFToWCFDirect/Client/Client.csproj | 90 ++ .../Client/Properties/AssemblyInfo.cs | 55 + .../Service/Properties/AssemblyInfo.cs | 55 + .../Channel/WCFToWCFDirect/Service/Service.cs | 83 ++ .../Channel/WCFToWCFDirect/Service/Service.csproj | 85 ++ .../Channel/WCFToWCFDirect/WCFToWCFDirect.sln | 46 + .../Another_Topic_Consumer.cs | 67 ++ .../Another_Topic_Consumer.csproj | 90 ++ .../Properties/AssemblyInfo.cs | 55 + .../Topic_Consumer/Properties/AssemblyInfo.cs | 55 + .../Topic_Consumer/Topic_Consumer.cs | 85 ++ .../Topic_Consumer/Topic_Consumer.csproj | 84 ++ .../Topic_Producer/Properties/AssemblyInfo.cs | 55 + .../Topic_Producer/Topic_Producer.cs | 68 ++ .../Topic_Producer/Topic_Producer.csproj | 90 ++ .../Channel/WCFToWCFPubSub/WCFToWCFPubSub.sln | 52 + qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpBoolean.cs | 57 + qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpInt.cs | 57 + .../src/Apache/Qpid/AmqpTypes/AmqpProperties.cs | 292 +++++ qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpString.cs | 91 ++ qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpType.cs | 33 + .../wcf/src/Apache/Qpid/AmqpTypes/AmqpTypes.csproj | 153 +++ qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpUbyte.cs | 57 + .../src/Apache/Qpid/AmqpTypes/CreateNetModule.bat | 19 + .../Qpid/AmqpTypes/Properties/AssemblyInfo.cs | 55 + qpid/wcf/src/Apache/Qpid/AmqpTypes/PropertyName.cs | 35 + .../src/Apache/Qpid/Channel/AmqpBinaryBinding.cs | 60 ++ .../Channel/AmqpBinaryBindingCollectionElement.cs | 29 + .../AmqpBinaryBindingConfigurationElement.cs | 79 ++ qpid/wcf/src/Apache/Qpid/Channel/AmqpBinding.cs | 115 ++ .../Qpid/Channel/AmqpBindingCollectionElement.cs | 29 + .../Channel/AmqpBindingConfigurationElement.cs | 258 +++++ .../src/Apache/Qpid/Channel/AmqpChannelFactory.cs | 98 ++ .../src/Apache/Qpid/Channel/AmqpChannelHelpers.cs | 142 +++ .../src/Apache/Qpid/Channel/AmqpChannelListener.cs | 174 +++ .../Qpid/Channel/AmqpTransportBindingElement.cs | 145 +++ .../Apache/Qpid/Channel/AmqpTransportChannel.cs | 592 ++++++++++ qpid/wcf/src/Apache/Qpid/Channel/Channel.csproj | 102 ++ .../src/Apache/Qpid/Channel/ConnectionManager.cs | 266 +++++ .../Apache/Qpid/Channel/Properties/AssemblyInfo.cs | 52 + qpid/wcf/src/Apache/Qpid/Channel/RawMessage.cs | 374 +++++++ .../src/Apache/Qpid/Channel/RawMessageEncoder.cs | 113 ++ .../Qpid/Channel/RawMessageEncoderFactory.cs | 45 + .../Channel/RawMessageEncodingBindingElement.cs | 102 ++ qpid/wcf/src/Apache/Qpid/Channel/RawXmlReader.cs | 353 ++++++ qpid/wcf/src/Apache/Qpid/Channel/RawXmlWriter.cs | 221 ++++ .../wcf/src/Apache/Qpid/Interop/AmqpConnection.cpp | 165 +++ qpid/wcf/src/Apache/Qpid/Interop/AmqpConnection.h | 71 ++ qpid/wcf/src/Apache/Qpid/Interop/AmqpMessage.cpp | 76 ++ qpid/wcf/src/Apache/Qpid/Interop/AmqpMessage.h | 61 ++ qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.cpp | 287 +++++ qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.h | 80 ++ qpid/wcf/src/Apache/Qpid/Interop/AssemblyInfo.cpp | 57 + .../src/Apache/Qpid/Interop/CompletionWaiter.cpp | 145 +++ .../wcf/src/Apache/Qpid/Interop/CompletionWaiter.h | 99 ++ qpid/wcf/src/Apache/Qpid/Interop/InputLink.cpp | 685 ++++++++++++ qpid/wcf/src/Apache/Qpid/Interop/InputLink.h | 85 ++ qpid/wcf/src/Apache/Qpid/Interop/Interop.vcproj | 302 ++++++ .../src/Apache/Qpid/Interop/MessageBodyStream.cpp | 337 ++++++ .../src/Apache/Qpid/Interop/MessageBodyStream.h | 131 +++ qpid/wcf/src/Apache/Qpid/Interop/MessageWaiter.cpp | 251 +++++ qpid/wcf/src/Apache/Qpid/Interop/MessageWaiter.h | 127 +++ qpid/wcf/src/Apache/Qpid/Interop/OutputLink.cpp | 251 +++++ qpid/wcf/src/Apache/Qpid/Interop/OutputLink.h | 64 ++ qpid/wcf/src/Apache/Qpid/Interop/QpidException.h | 37 + qpid/wcf/src/Apache/Qpid/Interop/QpidMarshal.h | 53 + .../Qpid/Test/Channel/Functional/AsyncTest.cs | 190 ++++ .../Channel/Functional/CustomAmqpBindingTest.cs | 77 ++ .../Test/Channel/Functional/FunctionalTests.csproj | 110 ++ .../Channel/Functional/IGenericObjectService.cs | 30 + .../Test/Channel/Functional/IInteropService.cs | 31 + .../Channel/Functional/IQueuedDatagramService1.cs | 34 + .../Channel/Functional/IQueuedDatagramService2.cs | 34 + .../Channel/Functional/IQueuedDatagramService3.cs | 33 + .../Test/Channel/Functional/MessageBodyTest.cs | 135 +++ .../Qpid/Test/Channel/Functional/MessageClient.cs | 101 ++ .../Test/Channel/Functional/MessageProperties.txt | 22 + .../Channel/Functional/MessagePropertiesTest.cs | 131 +++ .../Qpid/Test/Channel/Functional/MessageService.cs | 158 +++ .../Functional/MultipleEndpointsSameQueueTest.cs | 83 ++ .../Channel/Functional/Properties/AssemblyInfo.cs | 55 + .../Qpid/Test/Channel/Functional/RunTests.bat | 34 + .../Apache/Qpid/Test/Channel/Functional/Util.cs | 74 ++ qpid/wcf/tools/QCreate/QCreate.cpp | 65 ++ qpid/wcf/tools/QCreate/QCreate.sln | 40 + qpid/wcf/tools/QCreate/QCreate.vcproj | 232 ++++ qpid/wcf/tools/QCreate/ReadMe.txt | 52 + qpid/wcf/tools/QCreate/stdafx.cpp | 27 + qpid/wcf/tools/QCreate/stdafx.h | 34 + qpid/wcf/tools/QCreate/targetver.h | 32 + 309 files changed, 26750 insertions(+), 3047 deletions(-) create mode 100644 qpid/cpp/bindings/qmf/python/qmf.py create mode 100644 qpid/cpp/bindings/qmf/tests/python_agent.py create mode 100755 qpid/cpp/bindings/qmf/tests/ruby_console.rb create mode 100644 qpid/cpp/examples/messaging/CMakeLists.txt create mode 100644 qpid/cpp/examples/messaging/Makefile.am create mode 100644 qpid/cpp/examples/messaging/client.cpp create mode 100644 qpid/cpp/examples/messaging/map_receiver.cpp create mode 100644 qpid/cpp/examples/messaging/map_sender.cpp create mode 100644 qpid/cpp/examples/messaging/queue_listener.cpp create mode 100644 qpid/cpp/examples/messaging/queue_receiver.cpp create mode 100644 qpid/cpp/examples/messaging/queue_sender.cpp create mode 100644 qpid/cpp/examples/messaging/server.cpp create mode 100644 qpid/cpp/examples/messaging/topic_listener.cpp create mode 100644 qpid/cpp/examples/messaging/topic_receiver.cpp create mode 100644 qpid/cpp/examples/messaging/topic_sender.cpp create mode 100644 qpid/cpp/include/qmf/Agent.h create mode 100644 qpid/cpp/include/qmf/AgentObject.h create mode 100644 qpid/cpp/include/qmf/Connection.h create mode 100644 qpid/cpp/include/qmf/ConnectionSettings.h create mode 100644 qpid/cpp/include/qmf/QmfImportExport.h create mode 100644 qpid/cpp/include/qpid/client/amqp0_10/Codecs.h create mode 100644 qpid/cpp/include/qpid/framing/List.h create mode 100644 qpid/cpp/include/qpid/messaging/Address.h create mode 100644 qpid/cpp/include/qpid/messaging/Codec.h create mode 100644 qpid/cpp/include/qpid/messaging/Connection.h create mode 100644 qpid/cpp/include/qpid/messaging/Filter.h create mode 100644 qpid/cpp/include/qpid/messaging/Message.h create mode 100644 qpid/cpp/include/qpid/messaging/MessageContent.h create mode 100644 qpid/cpp/include/qpid/messaging/MessageListener.h create mode 100644 qpid/cpp/include/qpid/messaging/Receiver.h create mode 100644 qpid/cpp/include/qpid/messaging/Sender.h create mode 100644 qpid/cpp/include/qpid/messaging/Session.h create mode 100644 qpid/cpp/include/qpid/messaging/Variant.h delete mode 100644 qpid/cpp/include/qpid/sys/uuid.h delete mode 100644 qpid/cpp/include/qpid/sys/windows/uuid.h delete mode 100644 qpid/cpp/src/qmf/Agent.cpp delete mode 100644 qpid/cpp/src/qmf/Agent.h create mode 100644 qpid/cpp/src/qmf/AgentEngine.cpp create mode 100644 qpid/cpp/src/qmf/AgentEngine.h create mode 100644 qpid/cpp/src/qmf/ConnectionSettingsImpl.cpp create mode 100644 qpid/cpp/src/qmf/ConnectionSettingsImpl.h delete mode 100644 qpid/cpp/src/qmf/Console.h create mode 100644 qpid/cpp/src/qmf/ConsoleEngine.cpp create mode 100644 qpid/cpp/src/qmf/ConsoleEngine.h create mode 100644 qpid/cpp/src/qmf/Protocol.cpp create mode 100644 qpid/cpp/src/qmf/Protocol.h create mode 100644 qpid/cpp/src/qmf/SequenceManager.cpp create mode 100644 qpid/cpp/src/qmf/SequenceManager.h create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.h create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/CodecsInternal.h create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.cpp create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.h create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/MessageSource.h create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.h create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h create mode 100644 qpid/cpp/src/qpid/framing/List.cpp create mode 100644 qpid/cpp/src/qpid/messaging/Address.cpp create mode 100644 qpid/cpp/src/qpid/messaging/Connection.cpp create mode 100644 qpid/cpp/src/qpid/messaging/ConnectionImpl.h create mode 100644 qpid/cpp/src/qpid/messaging/Filter.cpp create mode 100644 qpid/cpp/src/qpid/messaging/Message.cpp create mode 100644 qpid/cpp/src/qpid/messaging/MessageImpl.cpp create mode 100644 qpid/cpp/src/qpid/messaging/MessageImpl.h create mode 100644 qpid/cpp/src/qpid/messaging/Receiver.cpp create mode 100644 qpid/cpp/src/qpid/messaging/ReceiverImpl.h create mode 100644 qpid/cpp/src/qpid/messaging/Sender.cpp create mode 100644 qpid/cpp/src/qpid/messaging/SenderImpl.h create mode 100644 qpid/cpp/src/qpid/messaging/Session.cpp create mode 100644 qpid/cpp/src/qpid/messaging/SessionImpl.h create mode 100644 qpid/cpp/src/qpid/messaging/Variant.cpp create mode 100644 qpid/cpp/src/qpid/sys/uuid.h create mode 100644 qpid/cpp/src/qpid/sys/windows/uuid.h create mode 100644 qpid/cpp/src/tests/MessagingSessionTests.cpp create mode 100644 qpid/cpp/src/tests/Variant.cpp create mode 100644 qpid/cpp/src/tests/qpid_stream.cpp delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/network/FirewallFactory.java create mode 100644 qpid/java/doc/broker-priority-queue-subscription.dia create mode 100644 qpid/java/doc/broker-queue-subscription.dia create mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/DynamicQueueExchangeCreateTest.java create mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/test/unit/close/JavaServerCloseRaceConditionTest.java create mode 100644 qpid/python/Makefile create mode 100755 qpid/python/preppy create mode 100644 qpid/python/qpid/concurrency.py create mode 100644 qpid/python/qpid/driver.py create mode 100644 qpid/wcf/QpidWcf.sln create mode 100644 qpid/wcf/ReadMe.txt create mode 100644 qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Client.cs create mode 100644 qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Client.csproj create mode 100644 qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Properties/AssemblyInfo.cs create mode 100644 qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Properties/AssemblyInfo.cs create mode 100644 qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Service.cs create mode 100644 qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Service.csproj create mode 100644 qpid/wcf/samples/Channel/WCFToWCFDirect/WCFToWCFDirect.sln create mode 100644 qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Another_Topic_Consumer.cs create mode 100644 qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Another_Topic_Consumer.csproj create mode 100644 qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Properties/AssemblyInfo.cs create mode 100644 qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Properties/AssemblyInfo.cs create mode 100644 qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Topic_Consumer.cs create mode 100644 qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Topic_Consumer.csproj create mode 100644 qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Properties/AssemblyInfo.cs create mode 100644 qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Topic_Producer.cs create mode 100644 qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Topic_Producer.csproj create mode 100644 qpid/wcf/samples/Channel/WCFToWCFPubSub/WCFToWCFPubSub.sln create mode 100644 qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpBoolean.cs create mode 100644 qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpInt.cs create mode 100644 qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpProperties.cs create mode 100644 qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpString.cs create mode 100644 qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpType.cs create mode 100644 qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpTypes.csproj create mode 100644 qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpUbyte.cs create mode 100755 qpid/wcf/src/Apache/Qpid/AmqpTypes/CreateNetModule.bat create mode 100644 qpid/wcf/src/Apache/Qpid/AmqpTypes/Properties/AssemblyInfo.cs create mode 100644 qpid/wcf/src/Apache/Qpid/AmqpTypes/PropertyName.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBinding.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBindingCollectionElement.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBindingConfigurationElement.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/AmqpBinding.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/AmqpBindingCollectionElement.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/AmqpBindingConfigurationElement.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelFactory.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelHelpers.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelListener.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportBindingElement.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportChannel.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/Channel.csproj create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/ConnectionManager.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/Properties/AssemblyInfo.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/RawMessage.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncoder.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncoderFactory.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncodingBindingElement.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/RawXmlReader.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Channel/RawXmlWriter.cs create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/AmqpConnection.cpp create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/AmqpConnection.h create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/AmqpMessage.cpp create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/AmqpMessage.h create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.cpp create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.h create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/AssemblyInfo.cpp create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/CompletionWaiter.cpp create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/CompletionWaiter.h create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/InputLink.cpp create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/InputLink.h create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/Interop.vcproj create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/MessageBodyStream.cpp create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/MessageBodyStream.h create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/MessageWaiter.cpp create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/MessageWaiter.h create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/OutputLink.cpp create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/OutputLink.h create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/QpidException.h create mode 100644 qpid/wcf/src/Apache/Qpid/Interop/QpidMarshal.h create mode 100644 qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/AsyncTest.cs create mode 100644 qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/CustomAmqpBindingTest.cs create mode 100644 qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/FunctionalTests.csproj create mode 100644 qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IGenericObjectService.cs create mode 100644 qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IInteropService.cs create mode 100644 qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService1.cs create mode 100644 qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService2.cs create mode 100644 qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService3.cs create mode 100644 qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageBodyTest.cs create mode 100644 qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageClient.cs create mode 100644 qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageProperties.txt create mode 100644 qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessagePropertiesTest.cs create mode 100644 qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageService.cs create mode 100644 qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MultipleEndpointsSameQueueTest.cs create mode 100644 qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/Properties/AssemblyInfo.cs create mode 100755 qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/RunTests.bat create mode 100644 qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/Util.cs create mode 100644 qpid/wcf/tools/QCreate/QCreate.cpp create mode 100644 qpid/wcf/tools/QCreate/QCreate.sln create mode 100644 qpid/wcf/tools/QCreate/QCreate.vcproj create mode 100644 qpid/wcf/tools/QCreate/ReadMe.txt create mode 100644 qpid/wcf/tools/QCreate/stdafx.cpp create mode 100644 qpid/wcf/tools/QCreate/stdafx.h create mode 100644 qpid/wcf/tools/QCreate/targetver.h (limited to 'qpid') diff --git a/qpid/buildtools/buildCreator/buildCreator.py b/qpid/buildtools/buildCreator/buildCreator.py index 0eae0b5422..6b9fc3c68e 100755 --- a/qpid/buildtools/buildCreator/buildCreator.py +++ b/qpid/buildtools/buildCreator/buildCreator.py @@ -1112,7 +1112,7 @@ def downloadSource(source, destination): command = SVN_BIN+" co "+url+" "+targetdir if (source.getElementsByTagName(REVISION).length > 0): revision = getValue(source.getElementsByTagName(REVISION)[0]) - command = SVN_BIN+" co "+url+"@"+revision+" "+targetdir + command = SVN_BIN+" co -r"+revision+" "+url+" "+targetdir else: if (type == HTTP): command = WGET_BIN+" --no-directories -P "+targetdir+" "+url diff --git a/qpid/cpp/bindings/qmf/Makefile.am b/qpid/cpp/bindings/qmf/Makefile.am index 68312a4208..eebb4b94de 100644 --- a/qpid/cpp/bindings/qmf/Makefile.am +++ b/qpid/cpp/bindings/qmf/Makefile.am @@ -20,6 +20,14 @@ if HAVE_SWIG EXTRA_DIST = qmfengine.i -SUBDIRS = ruby tests +SUBDIRS = tests + +if HAVE_RUBY_DEVEL +SUBDIRS += ruby +endif + +if HAVE_PYTHON_DEVEL +SUBDIRS += python +endif endif diff --git a/qpid/cpp/bindings/qmf/python/Makefile.am b/qpid/cpp/bindings/qmf/python/Makefile.am index 7b3f4d3be7..f51d26bfad 100644 --- a/qpid/cpp/bindings/qmf/python/Makefile.am +++ b/qpid/cpp/bindings/qmf/python/Makefile.am @@ -35,11 +35,12 @@ pylibdir = $(PYTHON_LIB) lib_LTLIBRARIES = _qmfengine.la -_qmfengine_la_LDFLAGS = -avoid-version -module -shrext "$(PYTHON_SO)" -_qmfengine_la_LIBADD = $(PYTHON_LIBS) -L$(top_builddir)/src/.libs -lqpidclient $(top_builddir)/src/libqmfcommon.la +#_qmfengine_la_LDFLAGS = -avoid-version -module -shrext "$(PYTHON_SO)" +#_qmfengine_la_LDFLAGS = -avoid-version -module -shrext ".so" +_qmfengine_la_LDFLAGS = -avoid-version -module -shared +_qmfengine_la_LIBADD = $(PYTHON_LIBS) -L$(top_builddir)/src/.libs -lqpidclient $(top_builddir)/src/libqmfagent.la _qmfengine_la_CXXFLAGS = $(INCLUDES) -I$(srcdir)/qmf -I$(PYTHON_INC) -_qmfengine_la_SOURCES = \ - qmfengine.cpp +nodist__qmfengine_la_SOURCES = qmfengine.cpp CLEANFILES = $(generated_file_list) diff --git a/qpid/cpp/bindings/qmf/python/python.i b/qpid/cpp/bindings/qmf/python/python.i index ea14efd4dd..5e25d155f9 100644 --- a/qpid/cpp/bindings/qmf/python/python.i +++ b/qpid/cpp/bindings/qmf/python/python.i @@ -19,17 +19,125 @@ %module qmfengine -// These are probably wrong.. just to get it to compile for now. -%typemap (in) void * -{ - $1 = (void *) $input; + +/* unsigned32 Convert from Python --> C */ +%typemap(in) uint32_t { + if (PyInt_Check($input)) { + $1 = (uint32_t) PyInt_AsUnsignedLongMask($input); + } else if (PyLong_Check($input)) { + $1 = (uint32_t) PyLong_AsUnsignedLong($input); + } else { + SWIG_exception_fail(SWIG_ValueError, "unknown integer type"); + } +} + +/* unsinged32 Convert from C --> Python */ +%typemap(out) uint32_t { + $result = PyInt_FromLong((long)$1); +} + + +/* unsigned16 Convert from Python --> C */ +%typemap(in) uint16_t { + if (PyInt_Check($input)) { + $1 = (uint16_t) PyInt_AsUnsignedLongMask($input); + } else if (PyLong_Check($input)) { + $1 = (uint16_t) PyLong_AsUnsignedLong($input); + } else { + SWIG_exception_fail(SWIG_ValueError, "unknown integer type"); + } +} + +/* unsigned16 Convert from C --> Python */ +%typemap(out) uint16_t { + $result = PyInt_FromLong((long)$1); +} + + +/* signed32 Convert from Python --> C */ +%typemap(in) int32_t { + if (PyInt_Check($input)) { + $1 = (int32_t) PyInt_AsLong($input); + } else if (PyLong_Check($input)) { + $1 = (int32_t) PyLong_AsLong($input); + } else { + SWIG_exception_fail(SWIG_ValueError, "unknown integer type"); + } +} + +/* signed32 Convert from C --> Python */ +%typemap(out) int32_t { + $result = PyInt_FromLong((long)$1); +} + + +/* unsigned64 Convert from Python --> C */ +%typemap(in) uint64_t { +%#ifdef HAVE_LONG_LONG + if (PyLong_Check($input)) { + $1 = (uint64_t)PyLong_AsUnsignedLongLong($input); + } else if (PyInt_Check($input)) { + $1 = (uint64_t)PyInt_AsUnsignedLongLongMask($input); + } else +%#endif + { + SWIG_exception_fail(SWIG_ValueError, "unsupported integer size - uint64_t input too large"); + } +} + +/* unsigned64 Convert from C --> Python */ +%typemap(out) uint64_t { +%#ifdef HAVE_LONG_LONG + $result = PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)$1); +%#else + SWIG_exception_fail(SWIG_ValueError, "unsupported integer size - uint64_t output too large"); +%#endif +} + +/* signed64 Convert from Python --> C */ +%typemap(in) int64_t { +%#ifdef HAVE_LONG_LONG + if (PyLong_Check($input)) { + $1 = (int64_t)PyLong_AsLongLong($input); + } else if (PyInt_Check($input)) { + $1 = (int64_t)PyInt_AsLong($input); + } else +%#endif + { + SWIG_exception_fail(SWIG_ValueError, "unsupported integer size - int64_t input too large"); + } +} + +/* signed64 Convert from C --> Python */ +%typemap(out) int64_t { +%#ifdef HAVE_LONG_LONG + $result = PyLong_FromLongLong((PY_LONG_LONG)$1); +%#else + SWIG_exception_fail(SWIG_ValueError, "unsupported integer size - int64_t output too large"); +%#endif } -%typemap (out) void * -{ + +/* Convert from Python --> C */ +%typemap(in) void * { + $1 = (void *)$input; +} + +/* Convert from C --> Python */ +%typemap(out) void * { $result = (PyObject *) $1; + Py_INCREF($result); +} + +%typemap (typecheck, precedence=SWIG_TYPECHECK_UINT64) uint64_t { + $1 = PyLong_Check($input) ? 1 : 0; } +%typemap (typecheck, precedence=SWIG_TYPECHECK_UINT32) uint32_t { + $1 = PyInt_Check($input) ? 1 : 0; +} + + %include "../qmfengine.i" diff --git a/qpid/cpp/bindings/qmf/python/qmf.py b/qpid/cpp/bindings/qmf/python/qmf.py new file mode 100644 index 0000000000..265f204852 --- /dev/null +++ b/qpid/cpp/bindings/qmf/python/qmf.py @@ -0,0 +1,854 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT 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 sys +import socket +import os +from threading import Thread +from threading import RLock +import qmfengine +from qmfengine import (ACCESS_READ_CREATE, ACCESS_READ_ONLY, ACCESS_READ_WRITE) +from qmfengine import (CLASS_EVENT, CLASS_OBJECT) +from qmfengine import (DIR_IN, DIR_IN_OUT, DIR_OUT) +from qmfengine import (TYPE_ABSTIME, TYPE_ARRAY, TYPE_BOOL, TYPE_DELTATIME, + TYPE_DOUBLE, TYPE_FLOAT, TYPE_INT16, TYPE_INT32, TYPE_INT64, + TYPE_INT8, TYPE_LIST, TYPE_LSTR, TYPE_MAP, TYPE_OBJECT, + TYPE_REF, TYPE_SSTR, TYPE_UINT16, TYPE_UINT32, TYPE_UINT64, + TYPE_UINT8, TYPE_UUID) + + + ##============================================================================== + ## CONNECTION + ##============================================================================== + +class ConnectionSettings: + #attr_reader :impl + def __init__(self, url=None): + if url: + self.impl = qmfengine.ConnectionSettings(url) + else: + self.impl = qmfengine.ConnectionSettings() + + + def set_attr(self, key, val): + if type(val) == str: + _v = qmfengine.Value(TYPE_LSTR) + _v.setString(val) + elif type(val) == bool: + _v = qmfengine.Value(TYPE_BOOL) + _v.setBool(val) + elif type(val) == int: + _v = qmfengine.Value(TYPE_UINT32) + _v.setUint(val) + else: + raise ArgumentError("Value for attribute '%s' has unsupported type: %s" % ( key, type(val))) + + self.impl.setAttr(key, _v) + + + +class ConnectionHandler: + def conn_event_connected(self): None + def conn_event_disconnected(self, error): None + def sess_event_session_closed(self, context, error): None + def sess_event_recv(self, context, message): None + + + +class Connection(Thread): + def __init__(self, settings): + Thread.__init__(self) + self._lock = RLock() + self.impl = qmfengine.ResilientConnection(settings.impl) + self._sockEngine, self._sock = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM) + self.impl.setNotifyFd(self._sockEngine.fileno()) + self._new_conn_handlers = [] + self._conn_handlers = [] + self.start() + + + def add_conn_handler(self, handler): + self._lock.acquire() + try: + self._new_conn_handlers.append(handler) + finally: + self._lock.release() + self._sockEngine.send("x") + + + def run(self): + eventImpl = qmfengine.ResilientConnectionEvent() + connected = False + new_handlers = [] + bt_count = 0 + + while True: + # print "Waiting for socket data" + self._sock.recv(1) + + self._lock.acquire() + try: + new_handlers = self._new_conn_handlers + self._new_conn_handlers = [] + finally: + self._lock.release() + + for nh in new_handlers: + self._conn_handlers.append(nh) + if connected: + nh.conn_event_connected() + + new_handlers = [] + + valid = self.impl.getEvent(eventImpl) + while valid: + try: + if eventImpl.kind == qmfengine.ResilientConnectionEvent.CONNECTED: + connected = True + for h in self._conn_handlers: + h.conn_event_connected() + + elif eventImpl.kind == qmfengine.ResilientConnectionEvent.DISCONNECTED: + connected = False + for h in self._conn_handlers: + h.conn_event_disconnected(eventImpl.errorText) + + elif eventImpl.kind == qmfengine.ResilientConnectionEvent.SESSION_CLOSED: + eventImpl.sessionContext.handler.sess_event_session_closed(eventImpl.sessionContext, eventImpl.errorText) + + elif eventImpl.kind == qmfengine.ResilientConnectionEvent.RECV: + eventImpl.sessionContext.handler.sess_event_recv(eventImpl.sessionContext, eventImpl.message) + + except: + import traceback + print "Event Exception:", sys.exc_info() + if bt_count < 2: + traceback.print_exc() + traceback.print_stack() + bt_count += 1 + + self.impl.popEvent() + valid = self.impl.getEvent(eventImpl) + + + +class Session: + def __init__(self, conn, label, handler): + self._conn = conn + self._label = label + self.handler = handler + self.handle = qmfengine.SessionHandle() + result = self._conn.impl.createSession(label, self, self.handle) + + + def __del__(self): + self._conn.impl.destroySession(self.handle) + + + + ##============================================================================== + ## OBJECTS + ##============================================================================== + +class QmfObject: + # attr_reader :impl, :object_class + def __init__(self, cls): + self.object_class = cls + self.impl = qmfengine.Object(self.object_class.impl) + + + def __del__(self): + self.impl.destroy() + + + def object_id(self): + return ObjectId(self.impl.getObjectId()) + + + def set_object_id(self, oid): + self.impl.setObjectId(oid.impl) + + + def get_attr(self, name): + val = self._value(name) + vType = val.getType() + if vType == TYPE_UINT8: return val.asUint() + elif vType == TYPE_UINT16: return val.asUint() + elif vType == TYPE_UINT32: return val.asUint() + elif vType == TYPE_UINT64: return val.asUint64() + elif vType == TYPE_SSTR: return val.asString() + elif vType == TYPE_LSTR: return val.asString() + elif vType == TYPE_ABSTIME: return val.asInt64() + elif vType == TYPE_DELTATIME: return val.asUint64() + elif vType == TYPE_REF: return val.asObjectId() + elif vType == TYPE_BOOL: return val.asBool() + elif vType == TYPE_FLOAT: return val.asFloat() + elif vType == TYPE_DOUBLE: return val.asDouble() + elif vType == TYPE_UUID: return val.asUuid() + elif vType == TYPE_INT8: return val.asInt() + elif vType == TYPE_INT16: return val.asInt() + elif vType == TYPE_INT32: return val.asInt() + elif vType == TYPE_INT64: return val.asInt64() + else: + # when TYPE_MAP + # when TYPE_OBJECT + # when TYPE_LIST + # when TYPE_ARRAY + print "Unsupported type for get_attr?", val.getType() + return None + + + def set_attr(self, name, v): + val = self._value(name) + vType = val.getType() + if vType == TYPE_UINT8: return val.setUint(v) + elif vType == TYPE_UINT16: return val.setUint(v) + elif vType == TYPE_UINT32: return val.setUint(v) + elif vType == TYPE_UINT64: return val.setUint64(v) + elif vType == TYPE_SSTR: + if v: return val.setString(v) + else: return val.setString('') + elif vType == TYPE_LSTR: + if v: return val.setString(v) + else: return val.setString('') + elif vType == TYPE_ABSTIME: return val.setInt64(v) + elif vType == TYPE_DELTATIME: return val.setUint64(v) + elif vType == TYPE_REF: return val.setObjectId(v.impl) + elif vType == TYPE_BOOL: return val.setBool(v) + elif vType == TYPE_FLOAT: return val.setFloat(v) + elif vType == TYPE_DOUBLE: return val.setDouble(v) + elif vType == TYPE_UUID: return val.setUuid(v) + elif vType == TYPE_INT8: return val.setInt(v) + elif vType == TYPE_INT16: return val.setInt(v) + elif vType == TYPE_INT32: return val.setInt(v) + elif vType == TYPE_INT64: return val.setInt64(v) + else: + # when TYPE_MAP + # when TYPE_OBJECT + # when TYPE_LIST + # when TYPE_ARRAY + print "Unsupported type for get_attr?", val.getType() + return None + + + def __getitem__(self, name): + return self.get_attr(name) + + + def __setitem__(self, name, value): + self.set_attr(name, value) + + + def inc_attr(self, name, by=1): + self.set_attr(name, self.get_attr(name) + by) + + + def dec_attr(self, name, by=1): + self.set_attr(name, self.get_attr(name) - by) + + + def _value(self, name): + val = self.impl.getValue(name) + if not val: + raise ArgumentError("Attribute '%s' not defined for class %s" % (name, self.object_class.impl.getName())) + return val + + + +class ConsoleObject(QmfObject): + # attr_reader :current_time, :create_time, :delete_time + def __init__(self, cls): + QmfObject.__init__(self, cls) + + + def update(self): pass + def mergeUpdate(self, newObject): pass + def is_deleted(self): + return self.delete_time > 0 + def index(self): pass + def method_missing(self, name, *args): pass + + + +class ObjectId: + def __init__(self, impl=None): + if impl: + self.impl = impl + else: + self.impl = qmfengine.ObjectId() + + + def object_num_high(self): + return self.impl.getObjectNumHi() + + + def object_num_low(self): + return self.impl.getObjectNumLo() + + + def __eq__(self, other): + if self.__class__ != other.__class__: return False + return (self.impl.getObjectNumHi() == other.impl.getObjectNumHi() and + self.impl.getObjectNumLo() == other.impl.getObjectNumLo()) + + + def __ne__(self, other): + return not self.__eq__(other) + + + +class Arguments: + def __init__(self, map): + self.map = map + self._by_hash = {} + key_count = self.map.keyCount() + a = 0 + while a < key_count: + self._by_hash[self.map.key(a)] = self.by_key(self.map.key(a)) + a += 1 + + + def __getitem__(self, key): + return self._by_hash[key] + + + def __setitem__(self, key, value): + self._by_hash[key] = value + self.set(key, value) + + + def __iter__(self): + return _by_hash.__iter__ + + + def by_key(self, key): + val = self.map.byKey(key) + vType = val.getType() + if vType == TYPE_UINT8: return val.asUint() + elif vType == TYPE_UINT16: return val.asUint() + elif vType == TYPE_UINT32: return val.asUint() + elif vType == TYPE_UINT64: return val.asUint64() + elif vType == TYPE_SSTR: return val.asString() + elif vType == TYPE_LSTR: return val.asString() + elif vType == TYPE_ABSTIME: return val.asInt64() + elif vType == TYPE_DELTATIME: return val.asUint64() + elif vType == TYPE_REF: return val.asObjectId() + elif vType == TYPE_BOOL: return val.asBool() + elif vType == TYPE_FLOAT: return val.asFloat() + elif vType == TYPE_DOUBLE: return val.asDouble() + elif vType == TYPE_UUID: return val.asUuid() + elif vType == TYPE_INT8: return val.asInt() + elif vType == TYPE_INT16: return val.asInt() + elif vType == TYPE_INT32: return val.asInt() + elif vType == TYPE_INT64: return val.asInt64() + else: + # when TYPE_MAP + # when TYPE_OBJECT + # when TYPE_LIST + # when TYPE_ARRAY + print "Unsupported Type for Get?", val.getType() + return None + + + def set(self, key, value): + val = self.map.byKey(key) + vType = val.getType() + if vType == TYPE_UINT8: return val.setUint(value) + elif vType == TYPE_UINT16: return val.setUint(value) + elif vType == TYPE_UINT32: return val.setUint(value) + elif vType == TYPE_UINT64: return val.setUint64(value) + elif vType == TYPE_SSTR: + if value: + return val.setString(value) + else: + return val.setString('') + elif vType == TYPE_LSTR: + if value: + return val.setString(value) + else: + return val.setString('') + elif vType == TYPE_ABSTIME: return val.setInt64(value) + elif vType == TYPE_DELTATIME: return val.setUint64(value) + elif vType == TYPE_REF: return val.setObjectId(value.impl) + elif vType == TYPE_BOOL: return val.setBool(value) + elif vType == TYPE_FLOAT: return val.setFloat(value) + elif vType == TYPE_DOUBLE: return val.setDouble(value) + elif vType == TYPE_UUID: return val.setUuid(value) + elif vType == TYPE_INT8: return val.setInt(value) + elif vType == TYPE_INT16: return val.setInt(value) + elif vType == TYPE_INT32: return val.setInt(value) + elif vType == TYPE_INT64: return val.setInt64(value) + else: + # when TYPE_MAP + # when TYPE_OBJECT + # when TYPE_LIST + # when TYPE_ARRAY + print "Unsupported Type for Set?", val.getType() + return None + + + +class Query: + def __init__(self, i=None): + if i: + self.impl = i + else: + self.impl = qmfengine.Query() + + + def package_name(self): return self.impl.getPackage() + def class_name(self): return self.impl.getClass() + def object_id(self): + _objid = self.impl.getObjectId() + if _objid: + return ObjectId(_objid) + else: + return None + OPER_AND = qmfengine.Query.OPER_AND + OPER_OR = qmfengine.Query.OPER_OR + + + + ##============================================================================== + ## SCHEMA + ##============================================================================== + + + +class SchemaArgument: + #attr_reader :impl + def __init__(self, name, typecode, kwargs={}): + self.impl = qmfengine.SchemaArgument(name, typecode) + if kwargs.has_key("dir"): self.impl.setDirection(kwargs["dir"]) + if kwargs.has_key("unit"): self.impl.setUnit(kwargs["unit"]) + if kwargs.has_key("desc"): self.impl.setDesc(kwargs["desc"]) + + + +class SchemaMethod: + # attr_reader :impl + def __init__(self, name, kwargs={}): + self.impl = qmfengine.SchemaMethod(name) + if kwargs.has_key("desc"): self.impl.setDesc(kwargs["desc"]) + self._arguments = [] + + + def add_argument(self, arg): + self._arguments.append(arg) + self.impl.addArgument(arg.impl) + + +class SchemaProperty: + #attr_reader :impl + def __init__(self, name, typecode, kwargs={}): + self.impl = qmfengine.SchemaProperty(name, typecode) + if kwargs.has_key("access"): self.impl.setAccess(kwargs["access"]) + if kwargs.has_key("index"): self.impl.setIndex(kwargs["index"]) + if kwargs.has_key("optional"): self.impl.setOptional(kwargs["optional"]) + if kwargs.has_key("unit"): self.impl.setUnit(kwargs["unit"]) + if kwargs.has_key("desc"): self.impl.setDesc(kwargs["desc"]) + + + def name(self): + return self.impl.getName() + + + +class SchemaStatistic: + # attr_reader :impl + def __init__(self, name, typecode, kwargs={}): + self.impl = qmfengine.SchemaStatistic(name, typecode) + if kwargs.has_key("unit"): self.impl.setUnit(kwargs["unit"]) + if kwargs.has_key("desc"): self.impl.setDesc(kwargs["desc"]) + + + +class SchemaClassKey: + #attr_reader :impl + def __init__(self, i): + self.impl = i + + + def get_package(self): + self.impl.getPackageName() + + + def get_class(self): + self.impl.getClassName() + + + +class SchemaObjectClass: + # attr_reader :impl + def __init__(self, package, name, kwargs={}): + self.impl = qmfengine.SchemaObjectClass(package, name) + self._properties = [] + self._statistics = [] + self._methods = [] + + + def add_property(self, prop): + self._properties.append(prop) + self.impl.addProperty(prop.impl) + + + def add_statistic(self, stat): + self._statistics.append(stat) + self.impl.addStatistic(stat.impl) + + + def add_method(self, meth): + self._methods.append(meth) + self.impl.addMethod(meth.impl) + + + def name(self): + return self.impl.getName() + + + def properties(self): + return self._properties + + +class SchemaEventClass: + # attr_reader :impl + def __init__(self, package, name, kwargs={}): + self.impl = qmfengine.SchemaEventClass(package, name) + if kwargs.has_key("desc"): self.impl.setDesc(kwargs["desc"]) + self._arguments = [] + + + def add_argument(self, arg): + self._arguments.append(arg) + self.impl.addArgument(arg.impl) + + + + ##============================================================================== + ## CONSOLE + ##============================================================================== + + + +class ConsoleHandler: + def agent_added(self, agent): pass + def agent_deleted(self, agent): pass + def new_package(self, package): pass + def new_class(self, class_key): pass + def object_update(self, obj, hasProps, hasStats): pass + def event_received(self, event): pass + def agent_heartbeat(self, agent, timestamp): pass + def method_response(self, resp): pass + def broker_info(self, broker): pass + + + +class Console: + # attr_reader :impl + def initialize(handler=None, kwargs={}): + self._handler = handler + self.impl = qmfengine.ConsoleEngine() + self._event = qmfengine.ConsoleEvent() + self._broker_list = [] + + + def add_connection(self, conn): + broker = Broker(self, conn) + self._broker_list.append(broker) + return broker + + + def del_connection(self, broker): pass + + + def get_packages(self): pass + + + def get_classes(self, package): pass + + + def get_schema(self, class_key): pass + + + def bind_package(self, package): pass + + + def bind_class(self, kwargs = {}): pass + + + def get_agents(self, broker=None): pass + + + def get_objects(self, query, kwargs = {}): pass + + + def start_sync(self, query): pass + + + def touch_sync(self, sync): pass + + + def end_sync(self, sync): pass + + + def do_console_events(self): + count = 0 + valid = self.impl.getEvent(self._event) + while valid: + count += 1 + print "Console Event:", self._event.kind + if self._event.kind == qmfengine.ConsoleEvent.AGENT_ADDED: + pass + elif self._event.kind == qmfengine.ConsoleEvent.AGENT_DELETED: + pass + elif self._event.kind == qmfengine.ConsoleEvent.NEW_PACKAGE: + pass + elif self._event.kind == qmfengine.ConsoleEvent.NEW_CLASS: + pass + elif self._event.kind == qmfengine.ConsoleEvent.OBJECT_UPDATE: + pass + elif self._event.kind == qmfengine.ConsoleEvent.EVENT_RECEIVED: + pass + elif self._event.kind == qmfengine.ConsoleEvent.AGENT_HEARTBEAT: + pass + elif self._event.kind == qmfengine.ConsoleEvent.METHOD_RESPONSE: + pass + + self.impl.popEvent() + valid = self.impl.getEvent(self._event) + return count + + + +class Broker(ConnectionHandler): + # attr_reader :impl + def __init__(self, console, conn): + self._console = console + self._conn = conn + self._session = None + self._event = qmfengine.BrokerEvent() + self._xmtMessage = qmfengine.Message() + self.impl = qmfengine.BrokerProxy(self._console.impl) + self._console.impl.addConnection(self.impl, self) + self._conn.add_conn_handler(self) + + + def do_broker_events(self): + count = 0 + valid = self.impl.getEvent(self._event) + while valid: + count += 1 + print "Broker Event: ", self._event.kind + if self._event.kind == qmfengine.BrokerEvent.BROKER_INFO: + pass + elif self._event.kind == qmfengine.BrokerEvent.DECLARE_QUEUE: + self._conn.impl.declareQueue(self._session.handle, self._event.name) + elif self._event.kind == qmfengine.BrokerEvent.DELETE_QUEUE: + self._conn.impl.deleteQueue(self._session.handle, self._event.name) + elif self._event.kind == qmfengine.BrokerEvent.BIND: + self._conn.impl.bind(self._session.handle, self._event.exchange, self._event.name, self._event.bindingKey) + elif self._event.kind == qmfengine.BrokerEvent.UNBIND: + self._conn.impl.unbind(self._session.handle, self._event.exchange, self._event.name, self._event.bindingKey) + elif self._event.kind == qmfengine.BrokerEvent.SETUP_COMPLETE: + self.impl.startProtocol() + + self.impl.popEvent() + valid = self.impl.getEvent(self._event) + + return count + + + def do_broker_messages(self): + count = 0 + valid = self.impl.getXmtMessage(self._xmtMessage) + while valid: + count += 1 + self._conn.impl.sendMessage(self._session.handle, self._xmtMessage) + self.impl.popXmt() + valid = self.impl.getXmtMessage(self._xmtMessage) + + return count + + + def do_events(self): + while True: + ccnt = self._console.do_console_events() + bcnt = do_broker_events() + mcnt = do_broker_messages() + if ccnt == 0 and bcnt == 0 and mcnt == 0: + break; + + + def conn_event_connected(self): + print "Console Connection Established..." + self._session = Session(self._conn, "qmfc-%s.%d" % (socket.gethostname(), os.getpid()), self) + self.impl.sessionOpened(self._session.handle) + self.do_events() + + + def conn_event_disconnected(self, error): + print "Console Connection Lost" + pass + + + def sess_event_session_closed(self, context, error): + print "Console Session Lost" + self.impl.sessionClosed() + + + def sess_event_recv(self, context, message): + self.impl.handleRcvMessage(message) + self.do_events() + + + + ##============================================================================== + ## AGENT + ##============================================================================== + + + +class AgentHandler: + def get_query(self, context, query, userId): None + def method_call(self, context, name, object_id, args, userId): None + + + +class Agent(ConnectionHandler): + def __init__(self, handler, label=""): + if label == "": + self._agentLabel = "rb-%s.%d" % (socket.gethostname(), os.getpid()) + else: + self._agentLabel = label + self._conn = None + self._handler = handler + self.impl = qmfengine.AgentEngine(self._agentLabel) + self._event = qmfengine.AgentEvent() + self._xmtMessage = qmfengine.Message() + + + def set_connection(self, conn): + self._conn = conn + self._conn.add_conn_handler(self) + + + def register_class(self, cls): + self.impl.registerClass(cls.impl) + + + def alloc_object_id(self, low = 0, high = 0): + return ObjectId(self.impl.allocObjectId(low, high)) + + + def query_response(self, context, obj): + self.impl.queryResponse(context, obj.impl) + + + def query_complete(self, context): + self.impl.queryComplete(context) + + + def method_response(self, context, status, text, arguments): + self.impl.methodResponse(context, status, text, arguments.map) + + + def do_agent_events(self): + count = 0 + valid = self.impl.getEvent(self._event) + while valid: + count += 1 + if self._event.kind == qmfengine.AgentEvent.GET_QUERY: + self._handler.get_query(self._event.sequence, + Query(self._event.query), + self._event.authUserId) + + elif self._event.kind == qmfengine.AgentEvent.START_SYNC: + pass + elif self._event.kind == qmfengine.AgentEvent.END_SYNC: + pass + elif self._event.kind == qmfengine.AgentEvent.METHOD_CALL: + args = Arguments(self._event.arguments) + self._handler.method_call(self._event.sequence, self._event.name, + ObjectId(self._event.objectId), + args, self._event.authUserId) + + elif self._event.kind == qmfengine.AgentEvent.DECLARE_QUEUE: + self._conn.impl.declareQueue(self._session.handle, self._event.name) + + elif self._event.kind == qmfengine.AgentEvent.DELETE_QUEUE: + self._conn.impl.deleteQueue(self._session.handle, self._event.name) + + elif self._event.kind == qmfengine.AgentEvent.BIND: + self._conn.impl.bind(self._session.handle, self._event.exchange, + self._event.name, self._event.bindingKey) + + elif self._event.kind == qmfengine.AgentEvent.UNBIND: + self._conn.impl.unbind(self._session.handle, self._event.exchange, + self._event.name, self._event.bindingKey) + + elif self._event.kind == qmfengine.AgentEvent.SETUP_COMPLETE: + self.impl.startProtocol() + + self.impl.popEvent() + valid = self.impl.getEvent(self._event) + return count + + + def do_agent_messages(self): + count = 0 + valid = self.impl.getXmtMessage(self._xmtMessage) + while valid: + count += 1 + self._conn.impl.sendMessage(self._session.handle, self._xmtMessage) + self.impl.popXmt() + valid = self.impl.getXmtMessage(self._xmtMessage) + return count + + + def do_events(self): + while True: + ecnt = self.do_agent_events() + mcnt = self.do_agent_messages() + if ecnt == 0 and mcnt == 0: + break + + + def conn_event_connected(self): + print "Agent Connection Established..." + self._session = Session(self._conn, + "qmfa-%s.%d" % (socket.gethostname(), os.getpid()), + self) + self.impl.newSession() + self.do_events() + + + def conn_event_disconnected(self, error): + print "Agent Connection Lost" + pass + + + def sess_event_session_closed(self, context, error): + print "Agent Session Lost" + pass + + + def sess_event_recv(self, context, message): + self.impl.handleRcvMessage(message) + self.do_events() + + diff --git a/qpid/cpp/bindings/qmf/qmfengine.i b/qpid/cpp/bindings/qmf/qmfengine.i index 3c67d92031..d3500c9b8f 100644 --- a/qpid/cpp/bindings/qmf/qmfengine.i +++ b/qpid/cpp/bindings/qmf/qmfengine.i @@ -19,24 +19,24 @@ %{ -#include "Agent.h" -#include +#include "qmf/AgentEngine.h" +#include "qmf/ConsoleEngine.h" +#include "qmf/ResilientConnection.h" %} - -%include -%include -%include -%include -%include -%include -%include -%include -%include - -%include -%include +%include +%include +%include +%include +%include +%include +%include +%include +%include +%include +%include +%include %inline { diff --git a/qpid/cpp/bindings/qmf/ruby/Makefile.am b/qpid/cpp/bindings/qmf/ruby/Makefile.am index 532fdb6875..0537dd1cd8 100644 --- a/qpid/cpp/bindings/qmf/ruby/Makefile.am +++ b/qpid/cpp/bindings/qmf/ruby/Makefile.am @@ -19,7 +19,7 @@ if HAVE_RUBY_DEVEL -INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src/qmf -I$(top_srcdir)/src -I$(top_builddir)/src +INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src -I$(top_builddir)/src EXTRA_DIST = ruby.i BUILT_SOURCES = qmfengine.cpp @@ -36,7 +36,7 @@ rubylibarchdir = $(RUBY_LIB_ARCH) rubylibarch_LTLIBRARIES = qmfengine.la qmfengine_la_LDFLAGS = -avoid-version -module -shrext ".$(RUBY_DLEXT)" -qmfengine_la_LIBADD = $(RUBY_LIBS) -L$(top_builddir)/src/.libs -lqpidclient $(top_builddir)/src/libqmfcommon.la +qmfengine_la_LIBADD = $(RUBY_LIBS) -L$(top_builddir)/src/.libs -lqpidclient $(top_builddir)/src/libqmfagent.la qmfengine_la_CXXFLAGS = $(INCLUDES) -I$(RUBY_INC) -I$(RUBY_INC_ARCH) nodist_qmfengine_la_SOURCES = qmfengine.cpp diff --git a/qpid/cpp/bindings/qmf/ruby/qmf.rb b/qpid/cpp/bindings/qmf/ruby/qmf.rb index 7ee447c675..21fbf6c157 100644 --- a/qpid/cpp/bindings/qmf/ruby/qmf.rb +++ b/qpid/cpp/bindings/qmf/ruby/qmf.rb @@ -20,517 +20,846 @@ require 'qmfengine' require 'thread' require 'socket' +require 'monitor' module Qmf - # Pull all the TYPE_* constants into Qmf namespace. Maybe there's an easier way? - Qmfengine.constants.each do |c| - if c.index('TYPE_') == 0 or c.index('ACCESS_') == 0 or c.index('DIR_') == 0 - const_set(c, Qmfengine.const_get(c)) - end + # Pull all the TYPE_* constants into Qmf namespace. Maybe there's an easier way? + Qmfengine.constants.each do |c| + if c.index('TYPE_') == 0 or c.index('ACCESS_') == 0 or c.index('DIR_') == 0 or c.index('CLASS_') == 0 + const_set(c, Qmfengine.const_get(c)) end + end + + ##============================================================================== + ## CONNECTION + ##============================================================================== - class ConnectionSettings < Qmfengine::ConnectionSettings + class ConnectionSettings + attr_reader :impl + + def initialize(url = nil) + if url + @impl = Qmfengine::ConnectionSettings.new(url) + else + @impl = Qmfengine::ConnectionSettings.new() + end end - class ConnectionEvent - def conn_event_connected(); end - def conn_event_disconnected(error); end - def conn_event_session_closed(context, error); end - def conn_event_recv(context, message); end + def set_attr(key, val) + if val.class == String + v = Qmfengine::Value.new(TYPE_LSTR) + v.setString(val) + elsif val.class == TrueClass or val.class == FalseClass + v = Qmfengine::Value.new(TYPE_BOOL) + v.setBool(val) + elsif val.class == Fixnum + v = Qmfengine::Value.new(TYPE_UINT32) + v.setUint(val) + else + raise ArgumentError, "Value for attribute '#{key}' has unsupported type: #{val.class}" + end + + @impl.setAttr(key, v) end + end - class Query - attr_reader :impl - def initialize(i) - @impl = i - end + class ConnectionHandler + def conn_event_connected(); end + def conn_event_disconnected(error); end + def sess_event_session_closed(context, error); end + def sess_event_recv(context, message); end + end - def package_name - @impl.getPackage - end + class Connection + include MonitorMixin - def class_name - @impl.getClass - end + attr_reader :impl - def object_id - objid = @impl.getObjectId - if objid.class == NilClass - return nil - end - return ObjectId.new(objid) + def initialize(settings) + super() + @impl = Qmfengine::ResilientConnection.new(settings.impl) + @sockEngine, @sock = Socket::socketpair(Socket::PF_UNIX, Socket::SOCK_STREAM, 0) + @impl.setNotifyFd(@sockEngine.fileno) + @new_conn_handlers = [] + @conn_handlers = [] + + @thread = Thread.new do + run end end - class AgentHandler - def get_query(context, query, userId); end - def method_call(context, name, object_id, args, userId); end + def add_conn_handler(handler) + synchronize do + @new_conn_handlers << handler + end + @sockEngine.write("x") end - class Connection - attr_reader :impl + def run() + eventImpl = Qmfengine::ResilientConnectionEvent.new + connected = nil + new_handlers = nil + bt_count = 0 - def initialize(settings, event_handler = nil, delay_min = 1, delay_max = 128, delay_factor = 2) - @impl = Qmfengine::ResilientConnection.new(settings, delay_min, delay_max, delay_factor) - @sockEngine, @sock = Socket::socketpair(Socket::PF_UNIX, Socket::SOCK_STREAM, 0) - @impl.setNotifyFd(@sockEngine.fileno) - @new_conn_handlers = Array.new - @conn_handlers = Array.new - @sess_handlers = Array.new + while :true + @sock.read(1) - @thread = Thread.new do - run + synchronize do + new_handlers = @new_conn_handlers + @new_conn_handlers = [] end - end - - def add_conn_handler(handler) - @new_conn_handlers.push(handler) - @sockEngine.write("x") - end - def add_sess_handler(handler) - @sess_handlers.push(handler) - end - - def run() - event = Qmfengine::ResilientConnectionEvent.new - connected = nil - while :true - @sock.read(1) + new_handlers.each do |nh| + @conn_handlers << nh + nh.conn_event_connected() if connected + end + new_handlers = nil - @new_conn_handlers.each do |nh| - @conn_handlers.push(nh) - nh.conn_event_connected() if connected - end - @new_conn_handlers = Array.new - - valid = @impl.getEvent(event) - while valid - begin - case event.kind - when Qmfengine::ResilientConnectionEvent::CONNECTED - connected = :true - @conn_handlers.each { |h| h.conn_event_connected() } - when Qmfengine::ResilientConnectionEvent::DISCONNECTED - connected = nil - @conn_handlers.each { |h| h.conn_event_disconnected(event.errorText) } - when Qmfengine::ResilientConnectionEvent::SESSION_CLOSED - event.sessionContext.handler.sess_event_session_closed(event.sessionContext, event.errorText) - when Qmfengine::ResilientConnectionEvent::RECV - event.sessionContext.handler.sess_event_recv(event.sessionContext, event.message) - end - rescue Exception => ex - puts "Event Exception: #{ex}" + valid = @impl.getEvent(eventImpl) + while valid + begin + case eventImpl.kind + when Qmfengine::ResilientConnectionEvent::CONNECTED + connected = :true + @conn_handlers.each { |h| h.conn_event_connected() } + when Qmfengine::ResilientConnectionEvent::DISCONNECTED + connected = nil + @conn_handlers.each { |h| h.conn_event_disconnected(eventImpl.errorText) } + when Qmfengine::ResilientConnectionEvent::SESSION_CLOSED + eventImpl.sessionContext.handler.sess_event_session_closed(eventImpl.sessionContext, eventImpl.errorText) + when Qmfengine::ResilientConnectionEvent::RECV + eventImpl.sessionContext.handler.sess_event_recv(eventImpl.sessionContext, eventImpl.message) + end + rescue Exception => ex + puts "Event Exception: #{ex}" + if bt_count < 2 puts ex.backtrace + bt_count += 1 end - @impl.popEvent - valid = @impl.getEvent(event) end + @impl.popEvent + valid = @impl.getEvent(eventImpl) end end end + end - class Session - attr_reader :handle, :handler + class Session + attr_reader :handle, :handler - def initialize(conn, label, handler) - @conn = conn - @label = label - @handler = handler - @handle = Qmfengine::SessionHandle.new - @conn.add_sess_handler(@handler) - result = @conn.impl.createSession(label, self, @handle) - end + def initialize(conn, label, handler) + @conn = conn + @label = label + @handler = handler + @handle = Qmfengine::SessionHandle.new + result = @conn.impl.createSession(label, self, @handle) end - class ObjectId - attr_reader :impl - def initialize(impl=nil) - if impl - @impl = impl - else - @impl = Qmfengine::ObjectId.new - end - end - def object_num_high - return @impl.getObjectNumHi - end - def object_num_low - return @impl.getObjectNumLo - end + def destroy() + @conn.impl.destroySession(@handle) end + end - class Arguments - attr_reader :map - def initialize(map) - @map = map - @by_hash = {} - key_count = @map.keyCount - a = 0 - while a < key_count - @by_hash[@map.key(a)] = by_key(@map.key(a)) - a += 1 - end - end + ##============================================================================== + ## OBJECTS + ##============================================================================== - def [] (key) - return @by_hash[key] - end + class QmfObject + attr_reader :impl, :object_class + def initialize(cls) + @object_class = cls + @impl = Qmfengine::Object.new(@object_class.impl) + end - def []= (key, value) - @by_hash[key] = value - set(key, value) - end + def destroy + @impl.destroy + end - def each - @by_hash.each { |k, v| yield(k, v) } - end + def object_id + return ObjectId.new(@impl.getObjectId) + end - def by_key(key) - val = @map.byKey(key) - case val.getType - when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.asUint - when TYPE_UINT64 then val.asUint64 - when TYPE_SSTR, TYPE_LSTR then val.asString - when TYPE_ABSTIME then val.asInt64 - when TYPE_DELTATIME then val.asUint64 - when TYPE_REF then val.asObjectId - when TYPE_BOOL then val.asBool - when TYPE_FLOAT then val.asFloat - when TYPE_DOUBLE then val.asDouble - when TYPE_UUID then val.asUuid - when TYPE_INT8, TYPE_INT16, TYPE_INT32 then val.asInt - when TYPE_INT64 then val.asInt64 - when TYPE_MAP - when TYPE_OBJECT - when TYPE_LIST - when TYPE_ARRAY - end - end + def set_object_id(oid) + @impl.setObjectId(oid.impl) + end - def set(key, value) - val = @map.byKey(key) - case val.getType - when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.setUint(value) - when TYPE_UINT64 then val.setUint64(value) - when TYPE_SSTR, TYPE_LSTR then value ? val.setString(value) : val.setString('') - when TYPE_ABSTIME then val.setInt64(value) - when TYPE_DELTATIME then val.setUint64(value) - when TYPE_REF then val.setObjectId(value.impl) - when TYPE_BOOL then value ? val.setBool(value) : val.setBool(0) - when TYPE_FLOAT then val.setFloat(value) - when TYPE_DOUBLE then val.setDouble(value) - when TYPE_UUID then val.setUuid(value) - when TYPE_INT8, TYPE_INT16, TYPE_INT32 then val.setInt(value) - when TYPE_INT64 then val.setInt64(value) - when TYPE_MAP - when TYPE_OBJECT - when TYPE_LIST - when TYPE_ARRAY - end + def get_attr(name) + val = value(name) + case val.getType + when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.asUint + when TYPE_UINT64 then val.asUint64 + when TYPE_SSTR, TYPE_LSTR then val.asString + when TYPE_ABSTIME then val.asInt64 + when TYPE_DELTATIME then val.asUint64 + when TYPE_REF then val.asObjectId + when TYPE_BOOL then val.asBool + when TYPE_FLOAT then val.asFloat + when TYPE_DOUBLE then val.asDouble + when TYPE_UUID then val.asUuid + when TYPE_INT8, TYPE_INT16, TYPE_INT32 then val.asInt + when TYPE_INT64 then val.asInt64 + when TYPE_MAP + when TYPE_OBJECT + when TYPE_LIST + when TYPE_ARRAY end end - class Agent - def initialize(handler, label="") - if label == "" - @agentLabel = "rb-%s.%d" % [Socket.gethostname, Process::pid] - else - @agentLabel = label - end - @conn = nil - @handler = handler - @impl = Qmfengine::Agent.new(@agentLabel) - @event = Qmfengine::AgentEvent.new - @xmtMessage = Qmfengine::Message.new + def set_attr(name, v) + val = value(name) + case val.getType + when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.setUint(v) + when TYPE_UINT64 then val.setUint64(v) + when TYPE_SSTR, TYPE_LSTR then v ? val.setString(v) : val.setString('') + when TYPE_ABSTIME then val.setInt64(v) + when TYPE_DELTATIME then val.setUint64(v) + when TYPE_REF then val.setObjectId(v.impl) + when TYPE_BOOL then v ? val.setBool(v) : val.setBool(0) + when TYPE_FLOAT then val.setFloat(v) + when TYPE_DOUBLE then val.setDouble(v) + when TYPE_UUID then val.setUuid(v) + when TYPE_INT8, TYPE_INT16, TYPE_INT32 then val.setInt(v) + when TYPE_INT64 then val.setInt64(v) + when TYPE_MAP + when TYPE_OBJECT + when TYPE_LIST + when TYPE_ARRAY end + end - def set_connection(conn) - @conn = conn - @conn.add_conn_handler(self) - end + def [](name) + get_attr(name) + end - def register_class(cls) - @impl.registerClass(cls.impl) - end + def []=(name, value) + set_attr(name, value) + end - def alloc_object_id(low = 0, high = 0) - ObjectId.new(@impl.allocObjectId(low, high)) - end + def inc_attr(name, by=1) + set_attr(name, get_attr(name) + by) + end - def query_response(context, object) - @impl.queryResponse(context, object.impl) - end + def dec_attr(name, by=1) + set_attr(name, get_attr(name) - by) + end - def query_complete(context) - @impl.queryComplete(context) + private + def value(name) + val = @impl.getValue(name.to_s) + if val.nil? + raise ArgumentError, "Attribute '#{name}' not defined for class #{@object_class.impl.getName}" end + return val + end + end - def method_response(context, status, text, arguments) - @impl.methodResponse(context, status, text, arguments.map) - end + class ConsoleObject < QmfObject + attr_reader :current_time, :create_time, :delete_time - def do_agent_events() - count = 0 - valid = @impl.getEvent(@event) - while valid - count += 1 - case @event.kind - when Qmfengine::AgentEvent::GET_QUERY - @handler.get_query(@event.sequence, Query.new(@event.query), @event.authUserId) - when Qmfengine::AgentEvent::START_SYNC - when Qmfengine::AgentEvent::END_SYNC - when Qmfengine::AgentEvent::METHOD_CALL - args = Arguments.new(@event.arguments) - @handler.method_call(@event.sequence, @event.name, ObjectId.new(@event.objectId), - args, @event.authUserId) - when Qmfengine::AgentEvent::DECLARE_QUEUE - @conn.impl.declareQueue(@session.handle, @event.name) - when Qmfengine::AgentEvent::DELETE_QUEUE - @conn.impl.deleteQueue(@session.handle, @event.name) - when Qmfengine::AgentEvent::BIND - @conn.impl.bind(@session.handle, @event.exchange, @event.name, @event.bindingKey) - when Qmfengine::AgentEvent::UNBIND - @conn.impl.unbind(@session.handle, @event.exchange, @event.name, @event.bindingKey) - when Qmfengine::AgentEvent::SETUP_COMPLETE - @impl.startProtocol() - end - @impl.popEvent - valid = @impl.getEvent(@event) - end - return count - end + def initialize(cls) + super(cls) + end - def do_agent_messages() - count = 0 - valid = @impl.getXmtMessage(@xmtMessage) - while valid - count += 1 - @conn.impl.sendMessage(@session.handle, @xmtMessage) - @impl.popXmt - valid = @impl.getXmtMessage(@xmtMessage) - end - return count - end + def update() + end - def do_events() - begin - ecnt = do_agent_events - mcnt = do_agent_messages - end until ecnt == 0 and mcnt == 0 - end + def mergeUpdate(newObject) + end - def conn_event_connected() - puts "Agent Connection Established..." - @session = Session.new(@conn, "qmfa-%s.%d" % [Socket.gethostname, Process::pid], self) - @impl.newSession - do_events - end + def deleted?() + @delete_time > 0 + end - def conn_event_disconnected(error) - puts "Agent Connection Lost" - end + def index() + end - def sess_event_session_closed(context, error) - puts "Agent Session Lost" - end + def method_missing(name, *args) + end + end - def sess_event_recv(context, message) - @impl.handleRcvMessage(message) - do_events + class ObjectId + attr_reader :impl + def initialize(impl=nil) + if impl + @impl = impl + else + @impl = Qmfengine::ObjectId.new end end - class SchemaArgument - attr_reader :impl - def initialize(name, typecode, kwargs={}) - @impl = Qmfengine::SchemaArgument.new(name, typecode) - @impl.setDirection(kwargs[:dir]) if kwargs.include?(:dir) - @impl.setUnit(kwargs[:unit]) if kwargs.include?(:unit) - @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) - end + def object_num_high + return @impl.getObjectNumHi end - class SchemaMethod - attr_reader :impl - def initialize(name, kwargs={}) - @impl = Qmfengine::SchemaMethod.new(name) - @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) - @arguments = [] - end + def object_num_low + return @impl.getObjectNumLo + end - def add_argument(arg) - @arguments << arg - @impl.addArgument(arg.impl) + def ==(other) + return (@impl.getObjectNumHi == other.impl.getObjectNumHi) && + (@impl.getObjectNumLo == other.impl.getObjectNumLo) + end + end + + class Arguments + attr_reader :map + def initialize(map) + @map = map + @by_hash = {} + key_count = @map.keyCount + a = 0 + while a < key_count + @by_hash[@map.key(a)] = by_key(@map.key(a)) + a += 1 end end - class SchemaProperty - attr_reader :impl - def initialize(name, typecode, kwargs={}) - @impl = Qmfengine::SchemaProperty.new(name, typecode) - @impl.setAccess(kwargs[:access]) if kwargs.include?(:access) - @impl.setIndex(kwargs[:index]) if kwargs.include?(:index) - @impl.setOptional(kwargs[:optional]) if kwargs.include?(:optional) - @impl.setUnit(kwargs[:unit]) if kwargs.include?(:unit) - @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) + def [] (key) + return @by_hash[key] + end + + def []= (key, value) + @by_hash[key] = value + set(key, value) + end + + def each + @by_hash.each { |k, v| yield(k, v) } + end + + def by_key(key) + val = @map.byKey(key) + case val.getType + when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.asUint + when TYPE_UINT64 then val.asUint64 + when TYPE_SSTR, TYPE_LSTR then val.asString + when TYPE_ABSTIME then val.asInt64 + when TYPE_DELTATIME then val.asUint64 + when TYPE_REF then val.asObjectId + when TYPE_BOOL then val.asBool + when TYPE_FLOAT then val.asFloat + when TYPE_DOUBLE then val.asDouble + when TYPE_UUID then val.asUuid + when TYPE_INT8, TYPE_INT16, TYPE_INT32 then val.asInt + when TYPE_INT64 then val.asInt64 + when TYPE_MAP + when TYPE_OBJECT + when TYPE_LIST + when TYPE_ARRAY end + end - def name - @impl.getName + def set(key, value) + val = @map.byKey(key) + case val.getType + when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.setUint(value) + when TYPE_UINT64 then val.setUint64(value) + when TYPE_SSTR, TYPE_LSTR then value ? val.setString(value) : val.setString('') + when TYPE_ABSTIME then val.setInt64(value) + when TYPE_DELTATIME then val.setUint64(value) + when TYPE_REF then val.setObjectId(value.impl) + when TYPE_BOOL then value ? val.setBool(value) : val.setBool(0) + when TYPE_FLOAT then val.setFloat(value) + when TYPE_DOUBLE then val.setDouble(value) + when TYPE_UUID then val.setUuid(value) + when TYPE_INT8, TYPE_INT16, TYPE_INT32 then val.setInt(value) + when TYPE_INT64 then val.setInt64(value) + when TYPE_MAP + when TYPE_OBJECT + when TYPE_LIST + when TYPE_ARRAY end end + end - class SchemaStatistic - attr_reader :impl - def initialize(name, typecode, kwargs={}) - @impl = Qmfengine::SchemaStatistic.new(name, typecode) - @impl.setUnit(kwargs[:unit]) if kwargs.include?(:unit) - @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) + class Query + attr_reader :impl + def initialize(i) + @impl = i + end + + def package_name + @impl.getPackage + end + + def class_name + @impl.getClass + end + + def object_id + objid = @impl.getObjectId + if objid.class == NilClass + return nil end + return ObjectId.new(objid) + end + end + + ##============================================================================== + ## SCHEMA + ##============================================================================== + + class SchemaArgument + attr_reader :impl + def initialize(name, typecode, kwargs={}) + @impl = Qmfengine::SchemaArgument.new(name, typecode) + @impl.setDirection(kwargs[:dir]) if kwargs.include?(:dir) + @impl.setUnit(kwargs[:unit]) if kwargs.include?(:unit) + @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) + end + end + + class SchemaMethod + attr_reader :impl + def initialize(name, kwargs={}) + @impl = Qmfengine::SchemaMethod.new(name) + @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) + @arguments = [] + end + + def add_argument(arg) + @arguments << arg + @impl.addArgument(arg.impl) + end + end + + class SchemaProperty + attr_reader :impl + def initialize(name, typecode, kwargs={}) + @impl = Qmfengine::SchemaProperty.new(name, typecode) + @impl.setAccess(kwargs[:access]) if kwargs.include?(:access) + @impl.setIndex(kwargs[:index]) if kwargs.include?(:index) + @impl.setOptional(kwargs[:optional]) if kwargs.include?(:optional) + @impl.setUnit(kwargs[:unit]) if kwargs.include?(:unit) + @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) + end + + def name + @impl.getName + end + end + + class SchemaStatistic + attr_reader :impl + def initialize(name, typecode, kwargs={}) + @impl = Qmfengine::SchemaStatistic.new(name, typecode) + @impl.setUnit(kwargs[:unit]) if kwargs.include?(:unit) + @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) end + end - class SchemaObjectClass - attr_reader :impl - def initialize(package, name, kwargs={}) + class SchemaClassKey + attr_reader :impl + def initialize(i) + @impl = i + end + + def get_package() + @impl.getPackageName() + end + + def get_class() + @impl.getClassName() + end + end + + class SchemaObjectClass + attr_reader :impl + def initialize(package='', name='', kwargs={}) + @properties = [] + @statistics = [] + @methods = [] + if kwargs.include?(:impl) + @impl = kwargs[:impl] + else @impl = Qmfengine::SchemaObjectClass.new(package, name) - @properties = [] - @statistics = [] - @methods = [] end + end - def add_property(prop) - @properties << prop - @impl.addProperty(prop.impl) - end + def add_property(prop) + @properties << prop + @impl.addProperty(prop.impl) + end - def add_statistic(stat) - @statistics << stat - @impl.addStatistic(stat.impl) - end + def add_statistic(stat) + @statistics << stat + @impl.addStatistic(stat.impl) + end - def add_method(meth) - @methods << meth - @impl.addMethod(meth.impl) - end + def add_method(meth) + @methods << meth + @impl.addMethod(meth.impl) + end - def name - @impl.getName - end + def name + @impl.getClassKey.getClassName + end - def properties - unless @properties - @properties = [] - @impl.getPropertyCount.times do |i| - @properties << @impl.getProperty(i) - end + def properties + unless @properties + @properties = [] + @impl.getPropertyCount.times do |i| + @properties << @impl.getProperty(i) end - return @properties end + return @properties end - - class SchemaEventClass - attr_reader :impl - def initialize(package, name, kwargs={}) + end + + class SchemaEventClass + attr_reader :impl + def initialize(package='', name='', kwargs={}) + @arguments = [] + if kwargs.include?(:impl) + @impl = kwargs[:impl] + else @impl = Qmfengine::SchemaEventClass.new(package, name) @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) - @arguments = [] end + end - def add_argument(arg) - @arguments << arg - @impl.addArgument(arg.impl) - end + def add_argument(arg) + @arguments << arg + @impl.addArgument(arg.impl) end - class QmfObject - attr_reader :impl, :object_class - def initialize(cls) - @object_class = cls - @impl = Qmfengine::Object.new(@object_class.impl) - end + def name + @impl.getClassKey.getClassName + end + end + + ##============================================================================== + ## CONSOLE + ##============================================================================== + + class ConsoleHandler + def agent_added(agent); end + def agent_deleted(agent); end + def new_package(package); end + def new_class(class_key); end + def object_update(object, hasProps, hasStats); end + def event_received(event); end + def agent_heartbeat(agent, timestamp); end + def method_response(resp); end + def broker_info(broker); end + end + + class Console + attr_reader :impl + + def initialize(handler = nil, kwargs={}) + @handler = handler + @impl = Qmfengine::ConsoleEngine.new + @event = Qmfengine::ConsoleEvent.new + @broker_list = [] + end - def destroy - @impl.destroy - end + def add_connection(conn) + broker = Broker.new(self, conn) + @broker_list << broker + return broker + end - def object_id - return ObjectId.new(@impl.getObjectId) - end + def del_connection(broker) + end - def set_object_id(oid) - @impl.setObjectId(oid.impl) + def get_packages() + plist = [] + count = @impl.packageCount + for i in 0...count + plist << @impl.getPackageName(i) end + return plist + end - def get_attr(name) - val = value(name) - case val.getType - when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.asUint - when TYPE_UINT64 then val.asUint64 - when TYPE_SSTR, TYPE_LSTR then val.asString - when TYPE_ABSTIME then val.asInt64 - when TYPE_DELTATIME then val.asUint64 - when TYPE_REF then val.asObjectId - when TYPE_BOOL then val.asBool - when TYPE_FLOAT then val.asFloat - when TYPE_DOUBLE then val.asDouble - when TYPE_UUID then val.asUuid - when TYPE_INT8, TYPE_INT16, TYPE_INT32 then val.asInt - when TYPE_INT64 then val.asInt64 - when TYPE_MAP - when TYPE_OBJECT - when TYPE_LIST - when TYPE_ARRAY + def get_classes(package, kind=CLASS_OBJECT) + clist = [] + count = @impl.classCount(package) + for i in 0...count + key = @impl.getClass(package, i) + class_kind = @impl.getClassKind(key) + if class_kind == kind + if kind == CLASS_OBJECT + clist << SchemaObjectClass.new('', '', :impl => @impl.getObjectClass(key)) + elsif kind == CLASS_EVENT + clist << SchemaEventClass.new('', '', :impl => @impl.getEventClass(key)) + end end end - def set_attr(name, v) - val = value(name) - case val.getType - when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.setUint(v) - when TYPE_UINT64 then val.setUint64(v) - when TYPE_SSTR, TYPE_LSTR then v ? val.setString(v) : val.setString('') - when TYPE_ABSTIME then val.setInt64(v) - when TYPE_DELTATIME then val.setUint64(v) - when TYPE_REF then val.setObjectId(v.impl) - when TYPE_BOOL then v ? val.setBool(v) : val.setBool(0) - when TYPE_FLOAT then val.setFloat(v) - when TYPE_DOUBLE then val.setDouble(v) - when TYPE_UUID then val.setUuid(v) - when TYPE_INT8, TYPE_INT16, TYPE_INT32 then val.setInt(v) - when TYPE_INT64 then val.setInt64(v) - when TYPE_MAP - when TYPE_OBJECT - when TYPE_LIST - when TYPE_ARRAY + return clist + end + + def get_schema(class_key) + end + + def bind_package(package) + end + + def bind_class(kwargs = {}) + end + + def get_agents(broker = nil) + end + + def get_objects(query, kwargs = {}) + end + + def start_sync(query) + end + + def touch_sync(sync) + end + + def end_sync(sync) + end + + def do_console_events() + count = 0 + valid = @impl.getEvent(@event) + while valid + count += 1 + puts "Console Event: #{@event.kind}" + case @event.kind + when Qmfengine::ConsoleEvent::AGENT_ADDED + when Qmfengine::ConsoleEvent::AGENT_DELETED + when Qmfengine::ConsoleEvent::NEW_PACKAGE + when Qmfengine::ConsoleEvent::NEW_CLASS + when Qmfengine::ConsoleEvent::OBJECT_UPDATE + when Qmfengine::ConsoleEvent::EVENT_RECEIVED + when Qmfengine::ConsoleEvent::AGENT_HEARTBEAT + when Qmfengine::ConsoleEvent::METHOD_RESPONSE end + @impl.popEvent + valid = @impl.getEvent(@event) end + return count + end + end + + class Broker < ConnectionHandler + include MonitorMixin + attr_reader :impl + + def initialize(console, conn) + super() + @console = console + @conn = conn + @session = nil + @cv = new_cond + @stable = nil + @event = Qmfengine::BrokerEvent.new + @xmtMessage = Qmfengine::Message.new + @impl = Qmfengine::BrokerProxy.new(@console.impl) + @console.impl.addConnection(@impl, self) + @conn.add_conn_handler(self) + end - def [](name) - get_attr(name) + def waitForStable(timeout = nil) + synchronize do + return if @stable + if timeout + unless @cv.wait(timeout) { @stable } + raise "Timed out waiting for broker connection to become stable" + end + else + while not @stable + @cv.wait + end + end end + end - def []=(name, value) - set_attr(name, value) + def do_broker_events() + count = 0 + valid = @impl.getEvent(@event) + while valid + count += 1 + puts "Broker Event: #{@event.kind}" + case @event.kind + when Qmfengine::BrokerEvent::BROKER_INFO + when Qmfengine::BrokerEvent::DECLARE_QUEUE + @conn.impl.declareQueue(@session.handle, @event.name) + when Qmfengine::BrokerEvent::DELETE_QUEUE + @conn.impl.deleteQueue(@session.handle, @event.name) + when Qmfengine::BrokerEvent::BIND + @conn.impl.bind(@session.handle, @event.exchange, @event.name, @event.bindingKey) + when Qmfengine::BrokerEvent::UNBIND + @conn.impl.unbind(@session.handle, @event.exchange, @event.name, @event.bindingKey) + when Qmfengine::BrokerEvent::SETUP_COMPLETE + @impl.startProtocol + when Qmfengine::BrokerEvent::STABLE + synchronize do + @stable = :true + @cv.signal + end + end + @impl.popEvent + valid = @impl.getEvent(@event) end + return count + end - def inc_attr(name, by=1) - set_attr(name, get_attr(name) + by) + def do_broker_messages() + count = 0 + valid = @impl.getXmtMessage(@xmtMessage) + while valid + count += 1 + @conn.impl.sendMessage(@session.handle, @xmtMessage) + @impl.popXmt + valid = @impl.getXmtMessage(@xmtMessage) end + return count + end + + def do_events() + begin + ccnt = @console.do_console_events + bcnt = do_broker_events + mcnt = do_broker_messages + end until ccnt == 0 and bcnt == 0 and mcnt == 0 + end - def dec_attr(name, by=1) - set_attr(name, get_attr(name) - by) + def conn_event_connected() + puts "Console Connection Established..." + @session = Session.new(@conn, "qmfc-%s.%d" % [Socket.gethostname, Process::pid], self) + @impl.sessionOpened(@session.handle) + do_events + end + + def conn_event_disconnected(error) + puts "Console Connection Lost" + end + + def sess_event_session_closed(context, error) + puts "Console Session Lost" + @impl.sessionClosed() + end + + def sess_event_recv(context, message) + @impl.handleRcvMessage(message) + do_events + end + end + + ##============================================================================== + ## AGENT + ##============================================================================== + + class AgentHandler + def get_query(context, query, userId); end + def method_call(context, name, object_id, args, userId); end + end + + class Agent < ConnectionHandler + def initialize(handler, label="") + if label == "" + @agentLabel = "rb-%s.%d" % [Socket.gethostname, Process::pid] + else + @agentLabel = label + end + @conn = nil + @handler = handler + @impl = Qmfengine::AgentEngine.new(@agentLabel) + @event = Qmfengine::AgentEvent.new + @xmtMessage = Qmfengine::Message.new + end + + def set_connection(conn) + @conn = conn + @conn.add_conn_handler(self) + end + + def register_class(cls) + @impl.registerClass(cls.impl) + end + + def alloc_object_id(low = 0, high = 0) + ObjectId.new(@impl.allocObjectId(low, high)) + end + + def query_response(context, object) + @impl.queryResponse(context, object.impl) + end + + def query_complete(context) + @impl.queryComplete(context) + end + + def method_response(context, status, text, arguments) + @impl.methodResponse(context, status, text, arguments.map) + end + + def do_agent_events() + count = 0 + valid = @impl.getEvent(@event) + while valid + count += 1 + case @event.kind + when Qmfengine::AgentEvent::GET_QUERY + @handler.get_query(@event.sequence, Query.new(@event.query), @event.authUserId) + when Qmfengine::AgentEvent::START_SYNC + when Qmfengine::AgentEvent::END_SYNC + when Qmfengine::AgentEvent::METHOD_CALL + args = Arguments.new(@event.arguments) + @handler.method_call(@event.sequence, @event.name, ObjectId.new(@event.objectId), + args, @event.authUserId) + when Qmfengine::AgentEvent::DECLARE_QUEUE + @conn.impl.declareQueue(@session.handle, @event.name) + when Qmfengine::AgentEvent::DELETE_QUEUE + @conn.impl.deleteQueue(@session.handle, @event.name) + when Qmfengine::AgentEvent::BIND + @conn.impl.bind(@session.handle, @event.exchange, @event.name, @event.bindingKey) + when Qmfengine::AgentEvent::UNBIND + @conn.impl.unbind(@session.handle, @event.exchange, @event.name, @event.bindingKey) + when Qmfengine::AgentEvent::SETUP_COMPLETE + @impl.startProtocol() + end + @impl.popEvent + valid = @impl.getEvent(@event) end + return count + end - private - def value(name) - val = @impl.getValue(name.to_s) - if val.nil? - raise ArgumentError, "Attribute '#{name}' not defined for class #{@object_class.impl.getName}" - end - return val + def do_agent_messages() + count = 0 + valid = @impl.getXmtMessage(@xmtMessage) + while valid + count += 1 + @conn.impl.sendMessage(@session.handle, @xmtMessage) + @impl.popXmt + valid = @impl.getXmtMessage(@xmtMessage) end + return count + end + + def do_events() + begin + ecnt = do_agent_events + mcnt = do_agent_messages + end until ecnt == 0 and mcnt == 0 + end + + def conn_event_connected() + puts "Agent Connection Established..." + @session = Session.new(@conn, "qmfa-%s.%d" % [Socket.gethostname, Process::pid], self) + @impl.newSession + do_events + end + + def conn_event_disconnected(error) + puts "Agent Connection Lost" + end + + def sess_event_session_closed(context, error) + puts "Agent Session Lost" + end + + def sess_event_recv(context, message) + @impl.handleRcvMessage(message) + do_events end + end end diff --git a/qpid/cpp/bindings/qmf/ruby/ruby.i b/qpid/cpp/bindings/qmf/ruby/ruby.i index a8a2a87a97..b7fed403bd 100644 --- a/qpid/cpp/bindings/qmf/ruby/ruby.i +++ b/qpid/cpp/bindings/qmf/ruby/ruby.i @@ -38,17 +38,33 @@ %typemap (out) uint16_t { - $result = UINT2NUM((unsigned short) $1); + $result = UINT2NUM((uint16_t) $1); } %typemap (in) uint32_t { - $1 = NUM2UINT ($input); + if (TYPE($input) == T_BIGNUM) + $1 = NUM2UINT($input); + else + $1 = FIX2UINT($input); } %typemap (out) uint32_t { - $result = UINT2NUM((unsigned int) $1); + $result = UINT2NUM((uint32_t) $1); +} + +%typemap (in) int32_t +{ + if (TYPE($input) == T_BIGNUM) + $1 = NUM2INT($input); + else + $1 = FIX2INT($input); +} + +%typemap (out) int32_t +{ + $result = INT2NUM((int32_t) $1); } %typemap (typecheck, precedence=SWIG_TYPECHECK_INTEGER) uint32_t { @@ -57,12 +73,28 @@ %typemap (in) uint64_t { - $1 = NUM2ULONG ($input); + if (TYPE($input) == T_BIGNUM) + $1 = NUM2ULL($input); + else + $1 = (uint64_t) FIX2LONG($input); } %typemap (out) uint64_t { - $result = ULONG2NUM((unsigned long) $1); + $result = ULL2NUM((uint64_t) $1); +} + +%typemap (in) int64_t +{ + if (TYPE($input) == T_BIGNUM) + $1 = NUM2LL($input); + else + $1 = (int64_t) FIX2LONG($input); +} + +%typemap (out) int64_t +{ + $result = LL2NUM((int64_t) $1); } %typemap (typecheck, precedence=SWIG_TYPECHECK_INTEGER) uint64_t { diff --git a/qpid/cpp/bindings/qmf/tests/Makefile.am b/qpid/cpp/bindings/qmf/tests/Makefile.am index 1ff8a64bed..182771e16b 100644 --- a/qpid/cpp/bindings/qmf/tests/Makefile.am +++ b/qpid/cpp/bindings/qmf/tests/Makefile.am @@ -18,4 +18,10 @@ # TESTS = run_interop_tests -EXTRA_DIST = run_interop_tests + +EXTRA_DIST = \ + agent_ruby.rb \ + python_agent.py \ + python_console.py \ + ruby_console.rb \ + run_interop_tests diff --git a/qpid/cpp/bindings/qmf/tests/agent_ruby.rb b/qpid/cpp/bindings/qmf/tests/agent_ruby.rb index d395810cb6..75de2b5fa1 100755 --- a/qpid/cpp/bindings/qmf/tests/agent_ruby.rb +++ b/qpid/cpp/bindings/qmf/tests/agent_ruby.rb @@ -29,13 +29,34 @@ class Model @parent_class = Qmf::SchemaObjectClass.new("org.apache.qpid.qmf", "parent") @parent_class.add_property(Qmf::SchemaProperty.new("name", Qmf::TYPE_SSTR, :index => true)) @parent_class.add_property(Qmf::SchemaProperty.new("state", Qmf::TYPE_SSTR)) + + @parent_class.add_property(Qmf::SchemaProperty.new("uint64val", Qmf::TYPE_UINT64)) @parent_class.add_property(Qmf::SchemaProperty.new("uint32val", Qmf::TYPE_UINT32)) + @parent_class.add_property(Qmf::SchemaProperty.new("uint16val", Qmf::TYPE_UINT16)) + @parent_class.add_property(Qmf::SchemaProperty.new("uint8val", Qmf::TYPE_UINT8)) + + @parent_class.add_property(Qmf::SchemaProperty.new("int64val", Qmf::TYPE_INT64)) + @parent_class.add_property(Qmf::SchemaProperty.new("int32val", Qmf::TYPE_INT32)) + @parent_class.add_property(Qmf::SchemaProperty.new("int16val", Qmf::TYPE_INT16)) + @parent_class.add_property(Qmf::SchemaProperty.new("int8val", Qmf::TYPE_INT8)) + @parent_class.add_statistic(Qmf::SchemaStatistic.new("queryCount", Qmf::TYPE_UINT32, :unit => "query", :desc => "Query count")) + method = Qmf::SchemaMethod.new("echo", :desc => "Check responsiveness of the agent object") + method.add_argument(Qmf::SchemaArgument.new("sequence", Qmf::TYPE_UINT32, :dir => Qmf::DIR_IN_OUT)) + @parent_class.add_method(method) + + method = Qmf::SchemaMethod.new("set_numerics", :desc => "Set the numeric values in the object") + method.add_argument(Qmf::SchemaArgument.new("test", Qmf::TYPE_SSTR, :dir => Qmf::DIR_IN)) + @parent_class.add_method(method) + method = Qmf::SchemaMethod.new("create_child", :desc => "Create a new child object") method.add_argument(Qmf::SchemaArgument.new("child_name", Qmf::TYPE_LSTR, :dir => Qmf::DIR_IN)) method.add_argument(Qmf::SchemaArgument.new("child_ref", Qmf::TYPE_REF, :dir => Qmf::DIR_OUT)) + @parent_class.add_method(method) + method = Qmf::SchemaMethod.new("probe_userid", :desc => "Return the user-id for this method call") + method.add_argument(Qmf::SchemaArgument.new("userid", Qmf::TYPE_SSTR, :dir => Qmf::DIR_OUT)) @parent_class.add_method(method) @child_class = Qmf::SchemaObjectClass.new("org.apache.qpid.qmf", "child") @@ -55,24 +76,83 @@ class App < Qmf::AgentHandler #@parent.inc_attr("queryCount") if query.class_name == 'parent' @agent.query_response(context, @parent) + elsif query.object_id == @parent_oid + @agent.query_response(context, @parent) end @agent.query_complete(context) end def method_call(context, name, object_id, args, userId) # puts "Method: user=#{userId} context=#{context} method=#{name} object_num=#{object_id.object_num_low if object_id} args=#{args}" - oid = @agent.alloc_object_id(2) - args['child_ref'] = oid - @child = Qmf::QmfObject.new(@model.child_class) - @child.set_attr("name", args.by_key("child_name")) - @child.set_object_id(oid) - @agent.method_response(context, 0, "OK", args) + + if name == "echo" + @agent.method_response(context, 0, "OK", args) + + elsif name == "set_numerics" + retCode = 0 + retText = "OK" + + if args['test'] == "big" + @parent.set_attr("uint64val", 0x9494949449494949) + @parent.set_attr("uint32val", 0xa5a55a5a) + @parent.set_attr("uint16val", 0xb66b) + @parent.set_attr("uint8val", 0xc7) + + @parent.set_attr("int64val", 1000000000000000000) + @parent.set_attr("int32val", 1000000000) + @parent.set_attr("int16val", 10000) + @parent.set_attr("int8val", 100) + + elsif args['test'] == "small" + @parent.set_attr("uint64val", 4) + @parent.set_attr("uint32val", 5) + @parent.set_attr("uint16val", 6) + @parent.set_attr("uint8val", 7) + + @parent.set_attr("int64val", 8) + @parent.set_attr("int32val", 9) + @parent.set_attr("int16val", 10) + @parent.set_attr("int8val", 11) + + elsif args['test'] == "negative" + @parent.set_attr("uint64val", 0) + @parent.set_attr("uint32val", 0) + @parent.set_attr("uint16val", 0) + @parent.set_attr("uint8val", 0) + + @parent.set_attr("int64val", -10000000000) + @parent.set_attr("int32val", -100000) + @parent.set_attr("int16val", -1000) + @parent.set_attr("int8val", -100) + + else + retCode = 1 + retText = "Invalid argument value for test" + end + + @agent.method_response(context, retCode, retText, args) + + elsif name == "create_child" + oid = @agent.alloc_object_id(2) + args['child_ref'] = oid + @child = Qmf::QmfObject.new(@model.child_class) + @child.set_attr("name", args.by_key("child_name")) + @child.set_object_id(oid) + @agent.method_response(context, 0, "OK", args) + + elsif name == "probe_userid" + args['userid'] = userId + @agent.method_response(context, 0, "OK", args) + + else + @agent.method_response(context, 1, "Unimplemented Method: #{name}", args) + end end def main @settings = Qmf::ConnectionSettings.new - @settings.host = ARGV[0] if ARGV.size > 0 - @settings.port = ARGV[1].to_i if ARGV.size > 1 + @settings.set_attr("host", ARGV[0]) if ARGV.size > 0 + @settings.set_attr("port", ARGV[1].to_i) if ARGV.size > 1 @connection = Qmf::Connection.new(@settings) @agent = Qmf::Agent.new(self) @@ -84,10 +164,19 @@ class App < Qmf::AgentHandler @parent = Qmf::QmfObject.new(@model.parent_class) @parent.set_attr("name", "Parent One") @parent.set_attr("state", "OPERATIONAL") - @parent.set_attr("uint32val", 0xa5a5a5a5) - oid = @agent.alloc_object_id(1) - @parent.set_object_id(oid) + @parent.set_attr("uint64val", 0) + @parent.set_attr("uint32val", 0) + @parent.set_attr("uint16val", 0) + @parent.set_attr("uint8val", 0) + + @parent.set_attr("int64val", 0) + @parent.set_attr("int32val", 0) + @parent.set_attr("int16val", 0) + @parent.set_attr("int8val", 0) + + @parent_oid = @agent.alloc_object_id(1) + @parent.set_object_id(@parent_oid) sleep end diff --git a/qpid/cpp/bindings/qmf/tests/python_agent.py b/qpid/cpp/bindings/qmf/tests/python_agent.py new file mode 100644 index 0000000000..f6cb51cbf5 --- /dev/null +++ b/qpid/cpp/bindings/qmf/tests/python_agent.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT 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 qmf +import sys +import time + + +class Model: + # attr_reader :parent_class, :child_class + def __init__(self): + self.parent_class = qmf.SchemaObjectClass("org.apache.qpid.qmf", "parent") + self.parent_class.add_property(qmf.SchemaProperty("name", qmf.TYPE_SSTR, {"index":True})) + self.parent_class.add_property(qmf.SchemaProperty("state", qmf.TYPE_SSTR)) + + self.parent_class.add_property(qmf.SchemaProperty("uint64val", qmf.TYPE_UINT64)) + self.parent_class.add_property(qmf.SchemaProperty("uint32val", qmf.TYPE_UINT32)) + self.parent_class.add_property(qmf.SchemaProperty("uint16val", qmf.TYPE_UINT16)) + self.parent_class.add_property(qmf.SchemaProperty("uint8val", qmf.TYPE_UINT8)) + + self.parent_class.add_property(qmf.SchemaProperty("int64val", qmf.TYPE_INT64)) + self.parent_class.add_property(qmf.SchemaProperty("int32val", qmf.TYPE_INT32)) + self.parent_class.add_property(qmf.SchemaProperty("int16val", qmf.TYPE_INT16)) + self.parent_class.add_property(qmf.SchemaProperty("int8val", qmf.TYPE_INT8)) + + self.parent_class.add_statistic(qmf.SchemaStatistic("queryCount", qmf.TYPE_UINT32, {"unit":"query", "desc":"Query count"})) + + _method = qmf.SchemaMethod("echo", {"desc":"Check responsiveness of the agent object"}) + _method.add_argument(qmf.SchemaArgument("sequence", qmf.TYPE_UINT32, {"dir":qmf.DIR_IN_OUT})) + self.parent_class.add_method(_method) + + _method = qmf.SchemaMethod("set_numerics", {"desc":"Set the numeric values in the object"}) + _method.add_argument(qmf.SchemaArgument("test", qmf.TYPE_SSTR, {"dir":qmf.DIR_IN})) + self.parent_class.add_method(_method) + + _method = qmf.SchemaMethod("create_child", {"desc":"Create a new child object"}) + _method.add_argument(qmf.SchemaArgument("child_name", qmf.TYPE_LSTR, {"dir":qmf.DIR_IN})) + _method.add_argument(qmf.SchemaArgument("child_ref", qmf.TYPE_REF, {"dir":qmf.DIR_OUT})) + self.parent_class.add_method(_method) + + _method = qmf.SchemaMethod("probe_userid", {"desc":"Return the user-id for this method call"}) + _method.add_argument(qmf.SchemaArgument("userid", qmf.TYPE_SSTR, {"dir":qmf.DIR_OUT})) + self.parent_class.add_method(_method) + + self.child_class = qmf.SchemaObjectClass("org.apache.qpid.qmf", "child") + self.child_class.add_property(qmf.SchemaProperty("name", qmf.TYPE_SSTR, {"index":True})) + + + def register(self, agent): + agent.register_class(self.parent_class) + agent.register_class(self.child_class) + + + +class App(qmf.AgentHandler): + def get_query(self, context, query, userId): + # puts "Query: user=#{userId} context=#{context} class=#{query.class_name} object_num=#{query.object_id.object_num_low if query.object_id}" + self._parent.inc_attr("queryCount") + if query.class_name() == 'parent': + self._agent.query_response(context, self._parent) + elif query.object_id() == self._parent_oid: + self._agent.query_response(context, self._parent) + self._agent.query_complete(context) + + + def method_call(self, context, name, object_id, args, userId): + # puts "Method: user=#{userId} context=#{context} method=#{name} object_num=#{object_id.object_num_low if object_id} args=#{args}" + # oid = self._agent.alloc_object_id(2) + # args['child_ref'] = oid + # self._child = qmf.QmfObject(self._model.child_class) + # self._child.set_attr("name", args.by_key("child_name")) + # self._child.set_object_id(oid) + # self._agent.method_response(context, 0, "OK", args) + if name == "echo": + self._agent.method_response(context, 0, "OK", args) + + elif name == "set_numerics": + _retCode = 0 + _retText = "OK" + + if args['test'] == "big": + self._parent.set_attr("uint64val", 0x9494949449494949) + self._parent.set_attr("uint32val", 0xa5a55a5a) + self._parent.set_attr("uint16val", 0xb66b) + self._parent.set_attr("uint8val", 0xc7) + + self._parent.set_attr("int64val", 1000000000000000000) + self._parent.set_attr("int32val", 1000000000) + self._parent.set_attr("int16val", 10000) + self._parent.set_attr("int8val", 100) + + elif args['test'] == "small": + self._parent.set_attr("uint64val", 4) + self._parent.set_attr("uint32val", 5) + self._parent.set_attr("uint16val", 6) + self._parent.set_attr("uint8val", 7) + + self._parent.set_attr("int64val", 8) + self._parent.set_attr("int32val", 9) + self._parent.set_attr("int16val", 10) + self._parent.set_attr("int8val", 11) + + elif args['test'] == "negative": + self._parent.set_attr("uint64val", 0) + self._parent.set_attr("uint32val", 0) + self._parent.set_attr("uint16val", 0) + self._parent.set_attr("uint8val", 0) + + self._parent.set_attr("int64val", -10000000000) + self._parent.set_attr("int32val", -100000) + self._parent.set_attr("int16val", -1000) + self._parent.set_attr("int8val", -100) + + else: + _retCode = 1 + _retText = "Invalid argument value for test" + + self._agent.method_response(context, _retCode, _retText, args) + + elif name == "create_child": + _oid = self._agent.alloc_object_id(2) + args['child_ref'] = _oid + self._child = qmf.QmfObject(self._model.child_class) + self._child.set_attr("name", args["child_name"]) + self._child.set_object_id(_oid) + self._agent.method_response(context, 0, "OK", args) + + elif name == "probe_userid": + args['userid'] = userId + self._agent.method_response(context, 0, "OK", args) + + else: + self._agent.method_response(context, 1, "Unimplemented Method: %s" % name, args) + + + def main(self): + self._settings = qmf.ConnectionSettings() + if len(sys.argv) > 1: + self._settings.set_attr("host", sys.argv[1]) + if len(sys.argv) > 2: + self._settings.set_attr("port", int(sys.argv[2])) + self._connection = qmf.Connection(self._settings) + self._agent = qmf.Agent(self) + + self._model = Model() + self._model.register(self._agent) + + self._agent.set_connection(self._connection) + + self._parent = qmf.QmfObject(self._model.parent_class) + self._parent.set_attr("name", "Parent One") + self._parent.set_attr("state", "OPERATIONAL") + + self._parent.set_attr("uint64val", 0) + self._parent.set_attr("uint32val", 0) + self._parent.set_attr("uint16val", 0) + self._parent.set_attr("uint8val", 0) + + self._parent.set_attr("int64val", 0) + self._parent.set_attr("int32val", 0) + self._parent.set_attr("int16val", 0) + self._parent.set_attr("int8val", 0) + + self._parent_oid = self._agent.alloc_object_id(1) + self._parent.set_object_id(self._parent_oid) + + while True: # there may be a better way, but + time.sleep(1000) # I'm a python noob... + + + +app = App() +app.main() + diff --git a/qpid/cpp/bindings/qmf/tests/python_console.py b/qpid/cpp/bindings/qmf/tests/python_console.py index 7a8a2cb9fe..bcd3063fe3 100755 --- a/qpid/cpp/bindings/qmf/tests/python_console.py +++ b/qpid/cpp/bindings/qmf/tests/python_console.py @@ -36,16 +36,109 @@ class QmfInteropTests(TestBase010): agents = qmf.getObjects(_class="agent") sleep(1) count += 1 - if count > 5: + if count > 10: self.fail("Timed out waiting for remote agent") - def test_B_basic_types(self): + def test_B_basic_method_invocation(self): self.startQmf(); qmf = self.qmf parents = qmf.getObjects(_class="parent") self.assertEqual(len(parents), 1) - self.assertEqual(parents[0].uint32val, 0xA5A5A5A5) + parent = parents[0] + for seq in range(10): + result = parent.echo(seq) + self.assertEqual(result.status, 0) + self.assertEqual(result.text, "OK") + self.assertEqual(result.sequence, seq) + + result = parent.set_numerics("bogus") + self.assertEqual(result.status, 1) + self.assertEqual(result.text, "Invalid argument value for test") + + def test_C_basic_types_numeric_big(self): + self.startQmf(); + qmf = self.qmf + + parents = qmf.getObjects(_class="parent") + self.assertEqual(len(parents), 1) + parent = parents[0] + + result = parent.set_numerics("big") + self.assertEqual(result.status, 0) + self.assertEqual(result.text, "OK") + + parent.update() + + self.assertEqual(parent.uint64val, 0x9494949449494949) + self.assertEqual(parent.uint32val, 0xA5A55A5A) + self.assertEqual(parent.uint16val, 0xB66B) + self.assertEqual(parent.uint8val, 0xC7) + + self.assertEqual(parent.int64val, 1000000000000000000) + self.assertEqual(parent.int32val, 1000000000) + self.assertEqual(parent.int16val, 10000) + self.assertEqual(parent.int8val, 100) + + def test_C_basic_types_numeric_small(self): + self.startQmf(); + qmf = self.qmf + + parents = qmf.getObjects(_class="parent") + self.assertEqual(len(parents), 1) + parent = parents[0] + + result = parent.set_numerics("small") + self.assertEqual(result.status, 0) + self.assertEqual(result.text, "OK") + + parent.update() + + self.assertEqual(parent.uint64val, 4) + self.assertEqual(parent.uint32val, 5) + self.assertEqual(parent.uint16val, 6) + self.assertEqual(parent.uint8val, 7) + + self.assertEqual(parent.int64val, 8) + self.assertEqual(parent.int32val, 9) + self.assertEqual(parent.int16val, 10) + self.assertEqual(parent.int8val, 11) + + def test_C_basic_types_numeric_negative(self): + self.startQmf(); + qmf = self.qmf + + parents = qmf.getObjects(_class="parent") + self.assertEqual(len(parents), 1) + parent = parents[0] + + result = parent.set_numerics("negative") + self.assertEqual(result.status, 0) + self.assertEqual(result.text, "OK") + + parent.update() + + self.assertEqual(parent.uint64val, 0) + self.assertEqual(parent.uint32val, 0) + self.assertEqual(parent.uint16val, 0) + self.assertEqual(parent.uint8val, 0) + + self.assertEqual(parent.int64val, -10000000000) + self.assertEqual(parent.int32val, -100000) + self.assertEqual(parent.int16val, -1000) + self.assertEqual(parent.int8val, -100) + + def test_D_userid_for_method(self): + self.startQmf(); + qmf = self.qmf + + parents = qmf.getObjects(_class="parent") + self.assertEqual(len(parents), 1) + parent = parents[0] + + result = parent.probe_userid() + self.assertEqual(result.status, 0) + self.assertEqual(result.userid, "guest") def getProperty(self, msg, name): for h in msg.headers: diff --git a/qpid/cpp/bindings/qmf/tests/ruby_console.rb b/qpid/cpp/bindings/qmf/tests/ruby_console.rb new file mode 100755 index 0000000000..fb48c29566 --- /dev/null +++ b/qpid/cpp/bindings/qmf/tests/ruby_console.rb @@ -0,0 +1,61 @@ +#!/usr/bin/ruby + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'qmf' +require 'socket' + +class App < Qmf::ConsoleHandler + + def main + @settings = Qmf::ConnectionSettings.new + @settings.set_attr("host", ARGV[0]) if ARGV.size > 0 + @settings.set_attr("port", ARGV[1].to_i) if ARGV.size > 1 + @connection = Qmf::Connection.new(@settings) + @qmf = Qmf::Console.new + + @broker = @qmf.add_connection(@connection) + @broker.waitForStable + + packages = @qmf.get_packages + puts "----- Packages -----" + packages.each do |p| + puts p + puts " ----- Object Classes -----" + classes = @qmf.get_classes(p) + classes.each do |c| + puts " #{c.name}" + end + puts " ----- Event Classes -----" + classes = @qmf.get_classes(p, Qmf::CLASS_EVENT) + classes.each do |c| + puts " #{c.name}" + end + end + puts "-----" + + sleep + end +end + +app = App.new +app.main + + diff --git a/qpid/cpp/bindings/qmf/tests/run_interop_tests b/qpid/cpp/bindings/qmf/tests/run_interop_tests index e6fc872dbb..01d7221ac6 100755 --- a/qpid/cpp/bindings/qmf/tests/run_interop_tests +++ b/qpid/cpp/bindings/qmf/tests/run_interop_tests @@ -28,6 +28,9 @@ BROKER_DIR=${BUILD_DIR}/src API_DIR=${BUILD_DIR}/bindings/qmf SPEC_DIR=${QPID_DIR}/specs +RUBY_LIB_DIR=${API_DIR}/ruby/.libs +PYTHON_LIB_DIR=${API_DIR}/python/.libs + trap stop_broker INT TERM QUIT start_broker() { @@ -41,7 +44,7 @@ stop_broker() { } start_ruby_agent() { - ruby -I${MY_DIR}/../ruby -I${API_DIR}/ruby/.libs ${MY_DIR}/agent_ruby.rb localhost $BROKER_PORT & + ruby -I${MY_DIR}/../ruby -I${RUBY_LIB_DIR} ${MY_DIR}/agent_ruby.rb localhost $BROKER_PORT & AGENT_PID=$! } @@ -49,19 +52,62 @@ stop_ruby_agent() { kill $AGENT_PID } +start_python_agent() { + PYTHONPATH="${MY_DIR}/../python:${API_DIR}/python:${PYTHON_LIB_DIR}" python ${MY_DIR}/python_agent.py localhost $BROKER_PORT & + PY_AGENT_PID=$! +} + +stop_python_agent() { + kill $PY_AGENT_PID +} + +TESTS_FAILED=0 + if test -d ${PYTHON_DIR} ; then start_broker echo "Running qmf interop tests using broker on port $BROKER_PORT" PYTHONPATH=${PYTHON_DIR}:${MY_DIR} export PYTHONPATH - echo " Ruby Agent vs. Pure-Python Console" - start_ruby_agent - echo " Ruby agent started at pid $AGENT_PID" - ${PYTHON_DIR}/qpid-python-test -m python_console -b localhost:$BROKER_PORT $@ - RETCODE=$? - stop_ruby_agent + + if test -d ${PYTHON_LIB_DIR} ; then + echo " Python Agent (external storage) vs. Pure-Python Console" + start_python_agent + echo " Python agent started at pid $PY_AGENT_PID" + ${PYTHON_DIR}/qpid-python-test -m python_console -b localhost:$BROKER_PORT $@ + RETCODE=$? + stop_python_agent + if test x$RETCODE != x0; then + echo "FAIL qmf interop tests (Python Agent)"; + TESTS_FAILED=1 + fi + fi + + if test -d ${RUBY_LIB_DIR} ; then + echo " Ruby Agent (external storage) vs. Pure-Python Console" + start_ruby_agent + echo " Ruby agent started at pid $AGENT_PID" + ${PYTHON_DIR}/qpid-python-test -m python_console -b localhost:$BROKER_PORT $@ + RETCODE=$? + stop_ruby_agent + if test x$RETCODE != x0; then + echo "FAIL qmf interop tests (Ruby Agent)"; + TESTS_FAILED=1 + fi + fi + + # Also against the Pure-Python console: + # Ruby agent (internal storage) + # Python agent (external and internal) + # C++ agent (external and internal) + # + # Other consoles against the same set of agents: + # Wrapped Python console + # Ruby console + # C++ console + stop_broker - if test x$RETCODE != x0; then - echo "FAIL qmf interop tests"; exit 1; + if test x$TESTS_FAILED != x0; then + echo "TEST FAILED!" + exit 1 fi fi diff --git a/qpid/cpp/configure.ac b/qpid/cpp/configure.ac index adf3da06b8..7eb1e99159 100644 --- a/qpid/cpp/configure.ac +++ b/qpid/cpp/configure.ac @@ -512,8 +512,10 @@ AC_CONFIG_FILES([ examples/xml-exchange/Makefile examples/qmf-console/Makefile examples/tradedemo/Makefile + examples/messaging/Makefile bindings/qmf/Makefile bindings/qmf/ruby/Makefile + bindings/qmf/python/Makefile bindings/qmf/tests/Makefile managementgen/Makefile etc/Makefile diff --git a/qpid/cpp/docs/api/doxygen_mainpage.h b/qpid/cpp/docs/api/doxygen_mainpage.h index cb59cfa260..83efaba31d 100644 --- a/qpid/cpp/docs/api/doxygen_mainpage.h +++ b/qpid/cpp/docs/api/doxygen_mainpage.h @@ -26,6 +26,7 @@ *

Messaging Client API classes

*
    *
  • \ref clientapi

  • + *
  • \ref qmfapi

  • *
* *

Code for common tasks

@@ -127,5 +128,6 @@ /** * \defgroup clientapi Qpid C++ Client API + * \defgroup qmfapi Qpid Management Framework C++ API * */ diff --git a/qpid/cpp/docs/api/user.doxygen.in b/qpid/cpp/docs/api/user.doxygen.in index 6ade9ab846..f6f8c161b7 100644 --- a/qpid/cpp/docs/api/user.doxygen.in +++ b/qpid/cpp/docs/api/user.doxygen.in @@ -456,7 +456,7 @@ WARN_LOGFILE = doxygen.log # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = @top_srcdir@/include @top_builddir@/include +INPUT = @top_srcdir@/docs/api @top_srcdir@/include @top_builddir@/include # If the value of the INPUT tag contains directories, you can use the @@ -471,7 +471,7 @@ FILE_PATTERNS = *.h # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. -RECURSIVE = NO +RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a diff --git a/qpid/cpp/examples/CMakeLists.txt b/qpid/cpp/examples/CMakeLists.txt index 7d0a4d0d21..126adab32e 100644 --- a/qpid/cpp/examples/CMakeLists.txt +++ b/qpid/cpp/examples/CMakeLists.txt @@ -62,3 +62,4 @@ add_subdirectory(qmf-console) add_subdirectory(request-response) add_subdirectory(tradedemo) add_subdirectory(xml-exchange) +add_subdirectory(messaging) diff --git a/qpid/cpp/examples/Makefile.am b/qpid/cpp/examples/Makefile.am index 9c355b84e4..b526424e2a 100644 --- a/qpid/cpp/examples/Makefile.am +++ b/qpid/cpp/examples/Makefile.am @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. # -SUBDIRS = direct fanout pub-sub request-response failover qmf-console tradedemo +SUBDIRS = direct fanout pub-sub request-response failover qmf-console tradedemo messaging if HAVE_XML SUBDIRS += xml-exchange broker_args = "--no-module-dir --data-dir \"\" --auth no --load-module $(top_builddir)/src/.libs/xml.so" diff --git a/qpid/cpp/examples/messaging/CMakeLists.txt b/qpid/cpp/examples/messaging/CMakeLists.txt new file mode 100644 index 0000000000..e7885d0b50 --- /dev/null +++ b/qpid/cpp/examples/messaging/CMakeLists.txt @@ -0,0 +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. +# + +add_example(messaging queue_listener) +add_example(messaging queue_receiver) +add_example(messaging queue_sender) + +add_example(messaging topic_listener) +add_example(messaging topic_receiver) +add_example(messaging topic_sender) + +add_example(messaging map_receiver) +add_example(messaging map_sender) + +add_example(messaging client) +add_example(messaging server) diff --git a/qpid/cpp/examples/messaging/Makefile.am b/qpid/cpp/examples/messaging/Makefile.am new file mode 100644 index 0000000000..250e549e82 --- /dev/null +++ b/qpid/cpp/examples/messaging/Makefile.am @@ -0,0 +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. +# +examplesdir=$(pkgdatadir)/examples/messaging + +MAKELDFLAGS=$(CLIENTFLAGS) +include $(top_srcdir)/examples/makedist.mk + +noinst_PROGRAMS=queue_sender queue_listener queue_receiver topic_sender topic_listener topic_receiver client server map_sender map_receiver + +queue_sender_SOURCES=queue_sender.cpp +queue_sender_LDADD=$(CLIENT_LIB) + +queue_listener_SOURCES=queue_listener.cpp +queue_listener_LDADD=$(CLIENT_LIB) + +queue_receiver_SOURCES=queue_receiver.cpp +queue_receiver_LDADD=$(CLIENT_LIB) + +topic_sender_SOURCES=topic_sender.cpp +topic_sender_LDADD=$(CLIENT_LIB) + +topic_listener_SOURCES=topic_listener.cpp +topic_listener_LDADD=$(CLIENT_LIB) + +topic_receiver_SOURCES=topic_receiver.cpp +topic_receiver_LDADD=$(CLIENT_LIB) + +client_SOURCES=client.cpp +client_LDADD=$(CLIENT_LIB) + +server_SOURCES=server.cpp +server_LDADD=$(CLIENT_LIB) + +map_sender_SOURCES=map_sender.cpp +map_sender_LDADD=$(CLIENT_LIB) + +map_receiver_SOURCES=map_receiver.cpp +map_receiver_LDADD=$(CLIENT_LIB) diff --git a/qpid/cpp/examples/messaging/client.cpp b/qpid/cpp/examples/messaging/client.cpp new file mode 100644 index 0000000000..45c065880b --- /dev/null +++ b/qpid/cpp/examples/messaging/client.cpp @@ -0,0 +1,76 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +using namespace qpid::messaging; + +using std::stringstream; +using std::string; + +int main(int argc, char** argv) { + const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; + + try { + Connection connection = Connection::open(url); + Session session = connection.newSession(); + + Sender sender = session.createSender("service_queue"); + + //create temp queue & receiver... + Address responseQueue = session.createTempQueue(); + Receiver receiver = session.createReceiver(responseQueue); + + // Now send some messages ... + string s[] = { + "Twas brillig, and the slithy toves", + "Did gire and gymble in the wabe.", + "All mimsy were the borogroves,", + "And the mome raths outgrabe." + }; + + Message request; + request.setReplyTo(responseQueue); + for (int i=0; i<4; i++) { + request.setContent(s[i]); + sender.send(request); + Message response = receiver.fetch(); + std::cout << request.getContent().asString() << " -> " << response.getContent().asString() << std::endl; + } + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} + + diff --git a/qpid/cpp/examples/messaging/map_receiver.cpp b/qpid/cpp/examples/messaging/map_receiver.cpp new file mode 100644 index 0000000000..e6557b1560 --- /dev/null +++ b/qpid/cpp/examples/messaging/map_receiver.cpp @@ -0,0 +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. + * + */ + +#include +#include +#include +#include + +#include +#include + +#include + +using namespace qpid::messaging; + +using std::stringstream; +using std::string; + +int main(int argc, char** argv) { + const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; + + try { + Connection connection = Connection::open(url); + Session session = connection.newSession(); + Receiver receiver = session.createReceiver("message_queue"); + Message message = receiver.fetch(); + std::cout << message.getContent().asMap() << std::endl; + session.acknowledge(); + receiver.cancel(); + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} diff --git a/qpid/cpp/examples/messaging/map_sender.cpp b/qpid/cpp/examples/messaging/map_sender.cpp new file mode 100644 index 0000000000..9301c1fe1f --- /dev/null +++ b/qpid/cpp/examples/messaging/map_sender.cpp @@ -0,0 +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. + * + */ + +#include +#include +#include +#include + +#include +#include + +#include + +using namespace qpid::messaging; + +using std::stringstream; +using std::string; + +int main(int argc, char** argv) { + const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; + + try { + Connection connection = Connection::open(url); + Session session = connection.newSession(); + Sender sender = session.createSender("message_queue"); + + Message message; + message.getContent()["id"] = 987654321; + message.getContent()["name"] = "Widget"; + message.getContent()["price"] = 0.99;//bad use of floating point number, just an example! + Variant::List colours; + colours.push_back(Variant("red")); + colours.push_back(Variant("green")); + colours.push_back(Variant("white")); + message.getContent()["colours"] = colours; + + sender.send(message); + session.sync(); + + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} + + diff --git a/qpid/cpp/examples/messaging/queue_listener.cpp b/qpid/cpp/examples/messaging/queue_listener.cpp new file mode 100644 index 0000000000..099e8e145a --- /dev/null +++ b/qpid/cpp/examples/messaging/queue_listener.cpp @@ -0,0 +1,82 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +using namespace qpid::messaging; + +class Listener : public MessageListener +{ + public: + Listener(const Receiver& receiver); + void received(Message& message); + bool isFinished(); + private: + Receiver receiver; + bool finished; +}; + +Listener::Listener(const Receiver& r) : receiver(r), finished(false) {} + +bool Listener::isFinished() { return finished; } + +void Listener::received(Message& message) +{ + std::cout << "Message: " << message.getContent().asString() << std::endl; + if (message.getContent().asString() == "That's all, folks!") { + std::cout << "Shutting down listener" << std::endl; + receiver.cancel(); + finished = true; + } +} + +int main(int argc, char** argv) { + const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; + + try { + Connection connection = Connection::open(url); + Session session = connection.newSession(); + + Receiver receiver = session.createReceiver("message_queue"); + Listener listener(receiver); + receiver.setListener(&listener); + receiver.setCapacity(1); + receiver.start(); + while (session.dispatch()) { + session.acknowledge(); + if (listener.isFinished()) break; + } + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} + + diff --git a/qpid/cpp/examples/messaging/queue_receiver.cpp b/qpid/cpp/examples/messaging/queue_receiver.cpp new file mode 100644 index 0000000000..83a44b2ca9 --- /dev/null +++ b/qpid/cpp/examples/messaging/queue_receiver.cpp @@ -0,0 +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. + * + */ + +#include +#include +#include +#include + +#include +#include + +#include + +using namespace qpid::messaging; + +using std::stringstream; +using std::string; + +int main(int argc, char** argv) { + const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; + + try { + Variant::Map options; + if (argc>2) parseOptionString(argv[2], options); + Connection connection = Connection::open(url, options); + Session session = connection.newSession(); + Receiver receiver = session.createReceiver("message_queue"); + while (true) { + Message message = receiver.fetch(); + std::cout << "Message: " << message.getContent() << std::endl; + session.acknowledge(); + if (message.getContent().asString() == "That's all, folks!") { + std::cout << "Cancelling receiver" << std::endl; + receiver.cancel(); + break; + } + } + + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} + + diff --git a/qpid/cpp/examples/messaging/queue_sender.cpp b/qpid/cpp/examples/messaging/queue_sender.cpp new file mode 100644 index 0000000000..637e7eb8e4 --- /dev/null +++ b/qpid/cpp/examples/messaging/queue_sender.cpp @@ -0,0 +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. + * + */ + +#include +#include +#include +#include + +#include +#include + +#include + +using namespace qpid::messaging; + +using std::stringstream; +using std::string; + +int main(int argc, char** argv) { + const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; + int count = argc>2 ? atoi(argv[2]) : 10; + + try { + Connection connection = Connection::open(url); + Session session = connection.newSession(); + Sender sender = session.createSender("message_queue"); + + // Now send some messages ... + for (int i=0; i +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace qpid::messaging; + +using std::stringstream; +using std::string; + +int main(int argc, char** argv) { + const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; + + try { + Connection connection = Connection::open(url); + Session session = connection.newSession(); + Receiver receiver = session.createReceiver("service_queue"); + + while (true) { + Message request = receiver.fetch(); + const Address& address = request.getReplyTo(); + if (address) { + Sender sender = session.createSender(address); + std::string s = request.getContent().asString(); + std::transform(s.begin(), s.end(), s.begin(), toupper); + Message response(s); + sender.send(response); + std::cout << "Processed request: " + << request.getContent().asString() + << " -> " + << response.getContent().asString() << std::endl; + session.acknowledge(); + } else { + std::cerr << "Error: no reply address specified for request: " << request.getContent().asString() << std::endl; + session.reject(request); + } + } + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} + + diff --git a/qpid/cpp/examples/messaging/topic_listener.cpp b/qpid/cpp/examples/messaging/topic_listener.cpp new file mode 100644 index 0000000000..700e03cdf9 --- /dev/null +++ b/qpid/cpp/examples/messaging/topic_listener.cpp @@ -0,0 +1,82 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace qpid::messaging; + +class Listener : public MessageListener +{ + public: + Listener(const Receiver& receiver); + void received(Message& message); + bool isFinished(); + private: + Receiver receiver; + bool finished; +}; + +Listener::Listener(const Receiver& r) : receiver(r), finished(false) {} + +bool Listener::isFinished() { return finished; } + +void Listener::received(Message& message) +{ + std::cout << "Message: " << message.getContent().asString() << std::endl; + if (message.getContent().asString() == "That's all, folks!") { + std::cout << "Shutting down listener" << std::endl; + receiver.cancel(); + finished = true; + } +} + +int main(int argc, char** argv) { + const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; + const char* pattern = argc>2 ? argv[2] : "#.#"; + + try { + Connection connection = Connection::open(url); + Session session = connection.newSession(); + + Filter filter(Filter::WILDCARD, pattern, "control"); + Receiver receiver = session.createReceiver("news_service", filter); + Listener listener(receiver); + receiver.setListener(&listener); + receiver.setCapacity(1); + receiver.start(); + while (session.dispatch() && !listener.isFinished()) ; + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} + + diff --git a/qpid/cpp/examples/messaging/topic_receiver.cpp b/qpid/cpp/examples/messaging/topic_receiver.cpp new file mode 100644 index 0000000000..063f0d9cb0 --- /dev/null +++ b/qpid/cpp/examples/messaging/topic_receiver.cpp @@ -0,0 +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. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +using namespace qpid::messaging; + +using std::stringstream; +using std::string; + +int main(int argc, char** argv) { + const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; + const char* pattern = argc>2 ? argv[2] : "#.#"; + + try { + Connection connection = Connection::open(url); + Session session = connection.newSession(); + Filter filter(Filter::WILDCARD, pattern, "control"); + Receiver receiver = session.createReceiver(Address("news_service", "topic"), filter); + while (true) { + Message message = receiver.fetch(); + std::cout << "Message: " << message.getContent().asString() << std::endl; + if (message.getContent().asString() == "That's all, folks!") { + std::cout << "Cancelling receiver" << std::endl; + receiver.cancel(); + break; + } + } + + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} + + diff --git a/qpid/cpp/examples/messaging/topic_sender.cpp b/qpid/cpp/examples/messaging/topic_sender.cpp new file mode 100644 index 0000000000..5665fc45f9 --- /dev/null +++ b/qpid/cpp/examples/messaging/topic_sender.cpp @@ -0,0 +1,78 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include +#include +#include +#include + +#include +#include + +#include + +using namespace qpid::messaging; + +using std::stringstream; +using std::string; + +void sendMessages(Sender& sender, int count, const std::string& subject, const std::string& text) +{ + Message message; + message.setSubject(subject); + for (int i=0; i1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; + int count = argc>2 ? atoi(argv[2]) : 10; + + try { + Connection connection = Connection::open(url); + Session session = connection.newSession(); + Sender sender = session.createSender("news_service"); + + // Now send some messages to each topic... + sendMessages(sender, count, "usa.news", "news about the usa"); + sendMessages(sender, count, "usa.weather", "weather report for the usa"); + sendMessages(sender, count, "europe.news", "news about europe"); + sendMessages(sender, count, "europe.weather", "weather report for europe"); + + // And send a final message to indicate termination. + Message message("That's all, folks!"); + message.setSubject("control"); + sender.send(message); + session.sync(); + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} + + diff --git a/qpid/cpp/include/qmf/Agent.h b/qpid/cpp/include/qmf/Agent.h new file mode 100644 index 0000000000..e61cd737d0 --- /dev/null +++ b/qpid/cpp/include/qmf/Agent.h @@ -0,0 +1,287 @@ +#ifndef _QmfAgent_ +#define _QmfAgent_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/QmfImportExport.h" + +namespace qmf { + + class AgentImpl; + class Connection; + class ObjectId; + class AgentObject; + class Value; + class Event; + class SchemaObjectClass; + + /** + * AgentListener is used by agents that select the internalStore=false option (see Agent + * constructor) or by agents that wish to provide access control for queries and methods. + * + * \ingroup qmfapi + */ + class AgentListener { + QMF_EXTERN virtual ~AgentListener(); + + /** + * allowQuery is called before a query operation is executed. If true is returned + * by this function, the query will proceed. If false is returned, the query will + * be forbidden. + * + * @param q The query being requested. + * @param userId The authenticated identity of the user requesting the query. + */ + virtual bool allowQuery(const Query& q, const char* userId); + + /** + * allowMethod is called before a method call is executed. If true is returned + * by this function, the method call will proceed. If false is returned, the method + * call will be forbidden. + * + * @param name The name of the method being called. + * @param args A value object (of type "map") that contains both input and output arguments. + * @param oid The objectId that identifies the instance of the object being called. + * @param cls The Schema describing the object being called. + * @param userId The authenticated identity of the requesting user. + */ + virtual bool allowMethod(const char* name, const Value& args, const ObjectId& oid, + const SchemaObjectClass& cls, const char* userId); + + /** + * query is called when the agent receives a query request. The handler must invoke + * Agent::queryResponse zero or more times (using the supplied context) followed by + * a single invocation of Agent::queryComplete. These calls do not need to be made + * synchronously in the context of this function. They may occur before or after this + * function returns. + * + * This function will only be invoked if internalStore=false in the Agent's constructor. + * + * @param context A context value to use in resulting calls to queryResponse and quertComplete. + * @param q The query requested by the console. + * @param userId the authenticated identity of the user requesting the query. + */ + virtual void query(uint32_t context, const Query& q, const char* userId); + + /** + * syncStart is called when a console requests a standing query. This function must + * behave exactly like AgentListener::query (i.e. send zero or more responses followed + * by a queryComplete) except it then remembers the context and the query and makes + * subsequent queryResponse calls whenever appropriate according the the query. + * + * The standing query shall stay in effect until syncStop is called with the same context + * value or until a specified period of time elapses without receiving a syncTouch for the + * context. + * + * This function will only be invoked if internalStore=false in the Agent's constructor. + * + * @param context A context value to use in resulting calls to queryResponse and queryComplete. + * @param q The query requested by the console. + * @param userId the authenticated identity of the user requesting the query. + */ + virtual void syncStart(uint32_t context, const Query& q, const char* userId); + + /** + * syncTouch is called when the console that requested a standing query refreshes its + * interest in the query. The console must periodically "touch" a standing query to keep + * it alive. This prevents standing queries from accumulating when the console disconnects + * before it can stop the query. + * + * This function will only be invoked if internalStore=false in the Agent's constructor. + * + * @param context The context supplied in a previous call to syncStart. + * @param userId The authenticated identity of the requesting user. + */ + virtual void syncTouch(uint32_t context, const char* userId); + + /** + * syncStop is called when the console that requested a standing query no longer wishes to + * receive data associated with that query. The application shall stop processing this + * query and shall remove its record of the context value. + * + * This function will only be invoked if internalStore=false in the Agent's constructor. + * + * @param context The context supplied in a previous call to syncStart. + * @param userId The authenticated identity of the requesting user. + */ + virtual void syncStop(uint32_t context, const char* userId); + + /** + * methodCall is called when a console invokes a method on a QMF object. The application + * must call Agent::methodResponse once in response to this function. The response does + * not need to be called synchronously in the context of this function. It may be called + * before or after this function returns. + * + * This function will only be invoked if internalStore=false in the Agent's constructor. + * + * @param context A context value to use in resulting call to methodResponse. + * @param name The name of the method being called. + * @param args A value object (of type "map") that contains both input and output arguments. + * @param oid The objectId that identifies the instance of the object being called. + * @param cls The Schema describing the object being called. + * @param userId The authenticated identity of the requesting user. + */ + virtual void methodCall(uint32_t context, const char* name, Value& args, + const ObjectId& oid, const SchemaObjectClass& cls, const char* userId); + }; + + /** + * The Agent class is the QMF Agent portal. It should be instantiated once and associated with a + * Connection (setConnection) to connect an agent to the QMF infrastructure. + * + * \ingroup qmfapi + */ + class Agent { + public: + /** + * Create an instance of the Agent class. + * + * @param label An optional string label that can be used to identify the agent. + * + * @param internalStore If true, objects shall be tracked internally by the agent. + * If false, the user of the agent must track the objects. + * If the agent is tracking the objects, queries and syncs are handled by + * the agent. The only involvement the user has is to optionally authorize + * individual operations. If the user is tracking the objects, the user code + * must implement queries and syncs (standing queries). + * + * @param listener A pointer to a class that implements the AgentListener interface. + * This must be supplied if any of the following conditions are true: + * - The agent model contains methods + * - The user wishes to individually authorize query and sync operations. + * - internalStore = false + */ + QMF_EXTERN Agent(char* label="qmfa", bool internalStore=true, AgentListener* listener=0); + + /** + * Destroy an instance of the Agent class. + */ + QMF_EXTERN ~Agent(); + + /** + * Set the persistent store file. This file, if specified, is used to store state information + * about the Agent. For example, if object-ids must be persistent across restarts of the Agent + * program, this file path must be supplied. + * + * @param path Full path to a file that is both writable and readable by the Agent program. + */ + QMF_EXTERN void setStoreDir(const char* path); + + /** + * Provide a connection (to a Qpid broker) over which the agent can communicate. + * + * @param conn Pointer to a Connection object. + */ + QMF_EXTERN void setConnection(Connection* conn); + + /** + * Register a class schema (object or event) with the agent. The agent must have a registered + * schema for an object class or an event class before it can handle objects or events of that + * class. + * + * @param cls Pointer to the schema structure describing the class. + */ + QMF_EXTERN void registerClass(SchemaObjectClass* cls); + QMF_EXTERN void registerClass(SchemaEventClass* cls); + + /** + * Add an object to the agent (for internal storage mode only). + * + * @param obj Reference to the object to be managed by the agent. + * + * @param persistent Iff true, the object ID assigned to the object shall indicate persistence + * (i.e. the object ID shall be the same across restarts of the agent program). + * + * @param oid 64-bit value for the oid (if zero, the agent will assign the value). + * + * @param oidLo 32-bit value for the lower 32-bits of the oid. + * + * @param oidHi 32-bit value for the upper 32-bits of the oid. + */ + QMF_EXTERN const ObjectId* addObject(AgentObject& obj, bool persistent=false, uint64_t oid=0); + QMF_EXTERN const ObjectId* addObject(AgentObject& obj, bool persistent, uint32_t oidLo, uint32_t oidHi); + + /** + * Allocate an object ID for an object (for external storage mode only). + * + * @param persistent Iff true, the object ID allocated shall indicate persistence + * (i.e. the object ID shall be the same across restarts of the agent program). + * + * @param oid 64-bit value for the oid (if zero, the agent will assign the value). + * + * @param oidLo 32-bit value for the lower 32-bits of the oid. + * + * @param oidHi 32-bit value for the upper 32-bits of the oid. + */ + QMF_EXTERN const ObjectId* allocObjectId(bool persistent=false, uint64_t oid=0); + QMF_EXTERN const ObjectId* allocObjectId(bool persistent, uint32_t oidLo, uint32_t oidHi); + + /** + * Raise a QMF event. + * + * @param event Reference to an event object to be raised to the QMF infrastructure. + */ + QMF_EXTERN void raiseEvent(Event& event); + + /** + * Provide a response to a query (for external storage mode only). + * + * @param context The context value supplied in the query (via the AgentListener interface). + * + * @param object A reference to the agent that matched the query criteria. + * + * @param prop If true, transmit the property attributes of this object. + * + * @param stat If true, transmit the statistic attributes of this object. + */ + QMF_EXTERN void queryResponse(uint32_t context, AgentObject& object, bool prop = true, bool stat = true); + + /** + * Indicate that a query (or the initial dump of a sync) is complete (for external storage mode only). + * + * @param context The context value supplied in the query/sync (via the AgentListener interface). + */ + QMF_EXTERN void queryComplete(uint32_t context); + + /** + * Provide the response to a method call. + * + * @param context The context value supplied in the method request (via the AgentListener interface). + * + * @param args The argument list from the method call. Must include the output arguments (may include + * the input arguments). + * + * @param status Numerical return status: zero indicates no error, non-zero indicates error. + * + * @param exception Pointer to an exception value. If status is non-zero, the exception value is + * sent to the caller. It is optional (i.e. leave the pointer as 0), or may be + * set to any legal value. A string may be supplied, but an unmanaged object of + * any schema may also be passed. + */ + QMF_EXTERN void methodResponse(uint32_t context, const Value& args, uint32_t status=0, + const Value* exception=0); + + private: + AgentImpl* impl; + }; + +} + +#endif diff --git a/qpid/cpp/include/qmf/AgentObject.h b/qpid/cpp/include/qmf/AgentObject.h new file mode 100644 index 0000000000..a1878605eb --- /dev/null +++ b/qpid/cpp/include/qmf/AgentObject.h @@ -0,0 +1,95 @@ +#ifndef _QmfAgentObject_ +#define _QmfAgentObject_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/QmfImportExport.h" + +namespace qmf { + + class AgentObjectImpl; + class SchemaObjectClass; + class ObjectId; + class Value; + class Agent; + + /** + * AgentObject is an extension of Object with agent-specific methods added. + * + * \ingroup qmfapi + */ + class AgentObject : public Object { + public: + /** + * Create a new Object of a specific type. + * + * @param type Pointer to the schema class to use as a type for this object. + */ + QMF_EXTERN AgentObject(const SchemaObjectClass* type); + + /** + * Schedule this object for deletion. Agent objects should never be directly + * destroyed, rather this method should be called and all pointers to this + * object dropped. The agent will clean up and properly delete the object at + * the appropraite time. + */ + QMF_EXTERN void destroy(); + + /** + * Set the object ID for this object if it is to be managed by the agent. + * + * @param oid The new object ID for the managed object. + */ + QMF_EXTERN void setObjectId(ObjectId& oid); + + /** + * Handler for invoked method calls. This will only be called for objects that + * are being managed and stored by an agent (see internalStore argument in Agent::Agent). + * If this function is not overridden in a child class, the default implementation will + * cause AgentListener::methodCall to be invoked in the application program. + * + * If this function is overridden in a sub-class, the implementation must perform + * the actions associated with the method call (i.e. implement the method). Once the + * method execution is complete, it must call Agent::methodResponse with the result + * of the method execution. Agent::methodResponse does not need to be called + * synchronously in the context of this function call. It may be called at a later + * time from a different thread. + * + * @param context Context supplied by the agent and required to be passed in the + * call to Agent::methodResponse + * + * @param name The name of the method. + * + * @param args A Value (of type map) that contains the input and output arguments. + * + * @param userId The authenticated identity of the user who invoked the method. + */ + QMF_EXTERN virtual void methodInvoked(uint32_t context, const char* name, Value& args, + const char* userId); + private: + friend class Agent; + virtual ~AgentObject(); + void setAgent(Agent* agent); + AgentObjectImpl* impl; + }; + +} + +#endif diff --git a/qpid/cpp/include/qmf/Connection.h b/qpid/cpp/include/qmf/Connection.h new file mode 100644 index 0000000000..f648b1427f --- /dev/null +++ b/qpid/cpp/include/qmf/Connection.h @@ -0,0 +1,125 @@ +#ifndef _QmfConnection_ +#define _QmfConnection_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/QmfImportExport.h" +#include "qmf/ConnectionSettings.h" + +namespace qmf { + + /** + * Operational states for Connections. + * + * \ingroup qmfapi + */ + enum ConnectionState { + CONNECTION_UP = 1, + CONNECTION_DOWN = 2 + }; + + /** + * Implement a subclass of ConnectionListener and provide it with the + * Connection constructor to receive notification of changes in the + * connection state. + * + * \ingroup qmfapi + */ + class ConnectionListener { + QMF_EXTERN virtual ~ConnectionListener(); + + /** + * Called each time the state of the connection changes. + * + * @param state the new state + */ + virtual void newState(ConnectionState state); + + /** + * Called if the connection requires input from an interactive client. + * + * @param prompt Text of the prompt - describes what information is required. + * @param answer The interactive user input. + * @param answerLen on Input - the maximum number of bytes that can be copied to answer. + * on Output - the number of bytes copied to answer. + */ + virtual void interactivePrompt(const char* prompt, char* answer, uint32_t answerLen); + }; + + class ConnectionImpl; + + /** + * The Connection class represents a connection to a QPID broker that can + * be used by agents and consoles, possibly multiple at the same time. + * + * \ingroup qmfapi + */ + class Connection { + public: + + /** + * Creates a connection object and begins the process of attempting to + * connect to the QPID broker. + * + * @param settings The settings that control how the connection is set + * up. + * + * @param listener An optional pointer to a subclass of + * ConnectionListener to receive notifications of events related to + * this connection. + */ + QMF_EXTERN Connection(const ConnectionSettings& settings, + const ConnectionListener* listener = 0); + + /** + * Destroys a connection, causing the connection to be closed. + */ + QMF_EXTERN ~Connection(); + + /** + * Set the administrative state of the connection (enabled or disabled). + * + * @param enabled True => enable connection, False => disable connection + */ + QMF_EXTERN void setAdminState(bool enabled); + + /** + * Return the current operational state of the connection (up or down). + * + * @return the current connection state. + */ + QMF_EXTERN ConnectionState getOperState() const; + + /** + * Get the error message from the last failure to connect. + * + * @return Null-terminated string containing the error message. + */ + QMF_EXTERN const char* getLastError() const; + + private: + friend class AgentImpl; + friend class ConsoleImpl; + ConnectionImpl* impl; + }; + +} + +#endif diff --git a/qpid/cpp/include/qmf/ConnectionSettings.h b/qpid/cpp/include/qmf/ConnectionSettings.h new file mode 100644 index 0000000000..9bd6922a56 --- /dev/null +++ b/qpid/cpp/include/qmf/ConnectionSettings.h @@ -0,0 +1,143 @@ +#ifndef _QmfConnectionSettings_ +#define _QmfConnectionSettings_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/QmfImportExport.h" +#include "qpid/sys/IntegerTypes.h" + +namespace qmf { + + class ConnectionSettingsImpl; + class Value; + + /** + * Settings for AMQP connections to the broker. + * + * \ingroup qmfapi + */ + class ConnectionSettings { + public: + + ConnectionSettings(const ConnectionSettings& copy); + + /** + * Create a set of default connection settings. + * + * If no further attributes are set, the settings will cause a connection to be made to + * the default broker (on localhost or at a host/port supplied by service discovery) and + * authentication will be the best-available (GSSAPI/Kerberos, Anonymous, Plain with prompts + * for username and password). + */ + QMF_EXTERN ConnectionSettings(); + + /** + * Create a set of connection settings by URL. + * + * @param url Universal resource locator describing the broker address and additional attributes. + * + * The URL is of the form: + * amqp[s]://host[:port][?key=value[&key=value]*] + * + * For example: + * amqp://localhost + * amqp://broker?transport=rdma&authmech=GSSAPI&authservice=qpidd + * amqps://broker?authmech=PLAIN&authuser=guest&authpass=guest + */ + QMF_EXTERN ConnectionSettings(const char* url); + + /** + * Destroy the connection settings object. + */ + QMF_EXTERN ~ConnectionSettings(); + + /** + * Set an attribute to control connection setup. + * + * @param key A null-terminated string that is an attribute name. + * + * @param value Reference to a value to be stored as the attribute. The type of the value + * is specific to the key. + */ + QMF_EXTERN void setAttr(const char* key, const Value& value); + + /** + * Get the value of an attribute. + * + * @param key A null-terminated attribute name. + * + * @return The value associated with the attribute name. + */ + QMF_EXTERN Value getAttr(const char* key) const; + + /** + * Get the attribute string (the portion of the URL following the '?') for the settings. + * + * @return A pointer to the attribute string. If the content of this string needs to be + * available beyond the scope of the calling function, it should be copied. The + * returned pointer may become invalid if the set of attributes is changed. + */ + QMF_EXTERN const char* getAttrString() const; + + /** + * Shortcuts for setting the transport for the connection. + * + * @param port The port value for the connection address. + */ + QMF_EXTERN void transportTcp(uint16_t port = 5672); + QMF_EXTERN void transportSsl(uint16_t port = 5671); + QMF_EXTERN void transportRdma(uint16_t port = 5672); + + /** + * Shortcuts for setting authentication mechanisms. + * + * @param username Null-terminated authentication user name. + * + * @param password Null-terminated authentication password. + * + * @param serviceName Null-terminated GSSAPI service name (Kerberos service principal) + * + * @param minSsf Minimum security factor for connections. 0 = encryption not required. + * + * @param maxSsf Maximum security factor for connections. 0 = encryption not permitted. + */ + QMF_EXTERN void authAnonymous(const char* username = 0); + QMF_EXTERN void authPlain(const char* username = 0, const char* password = 0); + QMF_EXTERN void authGssapi(const char* serviceName, uint32_t minSsf = 0, uint32_t maxSsf = 256); + + /** + * Shortcut for setting connection retry attributes. + * + * @param delayMin Minimum delay (in seconds) between connection attempts. + * + * @param delaxMax Maximum delay (in seconds) between connection attempts. + * + * @param delayFactor Factor to multiply the delay by between failed connection attempts. + */ + QMF_EXTERN void setRetry(int delayMin = 1, int delayMax = 128, int delayFactor = 2); + + private: + friend class ResilientConnectionImpl; + ConnectionSettingsImpl* impl; + }; + +} + +#endif diff --git a/qpid/cpp/include/qmf/QmfImportExport.h b/qpid/cpp/include/qmf/QmfImportExport.h new file mode 100644 index 0000000000..f5e1d9127c --- /dev/null +++ b/qpid/cpp/include/qmf/QmfImportExport.h @@ -0,0 +1,33 @@ +#ifndef QMF_IMPORT_EXPORT_H +#define QMF_IMPORT_EXPORT_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#if defined(WIN32) && !defined(QPID_DECLARE_STATIC) +# if defined(QMF_EXPORT) || defined (qmfcommon_EXPORTS) +# define QMF_EXTERN __declspec(dllexport) +# else +# define QMF_EXTERN __declspec(dllimport) +# endif +#else +# define QMF_EXTERN +#endif + +#endif diff --git a/qpid/cpp/include/qpid/client/Connection.h b/qpid/cpp/include/qpid/client/Connection.h index b7b967d232..0f5999cdcc 100644 --- a/qpid/cpp/include/qpid/client/Connection.h +++ b/qpid/cpp/include/qpid/client/Connection.h @@ -43,7 +43,21 @@ class ConnectionImpl; * * \ingroup clientapi * + * Some methods use an AMQP 0-10 URL to specify connection parameters. + * This is defined in the AMQP 0-10 specification (http://jira.amqp.org/confluence/display/AMQP/AMQP+Specification). + * + * amqp_url = "amqp:" prot_addr_list + * prot_addr_list = [prot_addr ","]* prot_addr + * prot_addr = tcp_prot_addr | tls_prot_addr + * + * tcp_prot_addr = tcp_id tcp_addr + * tcp_id = "tcp:" | "" + * tcp_addr = [host [":" port] ] + * host = + * port = number]]> + * */ + class Connection { framing::ProtocolVersion version; diff --git a/qpid/cpp/include/qpid/client/amqp0_10/Codecs.h b/qpid/cpp/include/qpid/client/amqp0_10/Codecs.h new file mode 100644 index 0000000000..5ef0b9fffe --- /dev/null +++ b/qpid/cpp/include/qpid/client/amqp0_10/Codecs.h @@ -0,0 +1,61 @@ +#ifndef QPID_CLIENT_AMQP0_10_CODECS_H +#define QPID_CLIENT_AMQP0_10_CODECS_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/Codec.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + + +/** + * Codec for encoding/decoding a map of Variants using the AMQP 0-10 + * map encoding. + */ +class MapCodec : public qpid::messaging::Codec +{ + public: + void encode(const qpid::messaging::Variant&, std::string&); + void decode(const std::string&, qpid::messaging::Variant&); + + static const std::string contentType; + private: +}; + +/** + * Codec for encoding/decoding a list of Variants using the AMQP 0-10 + * list encoding. + */ +class ListCodec : public qpid::messaging::Codec +{ + public: + void encode(const qpid::messaging::Variant&, std::string&); + void decode(const std::string&, qpid::messaging::Variant&); + + static const std::string contentType; + private: +}; + +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_CODECS_H*/ diff --git a/qpid/cpp/include/qpid/framing/FieldTable.h b/qpid/cpp/include/qpid/framing/FieldTable.h index a3a5c8a4ee..fd09cfc6f6 100644 --- a/qpid/cpp/include/qpid/framing/FieldTable.h +++ b/qpid/cpp/include/qpid/framing/FieldTable.h @@ -51,6 +51,9 @@ class FieldTable typedef boost::shared_ptr ValuePtr; typedef std::map ValueMap; typedef ValueMap::iterator iterator; + typedef ValueMap::const_reference const_reference; + typedef ValueMap::reference reference; + typedef ValueMap::value_type value_type; QPID_COMMON_EXTERN FieldTable() {}; QPID_COMMON_EXTERN FieldTable(const FieldTable& ft); @@ -97,12 +100,16 @@ class FieldTable QPID_COMMON_EXTERN bool operator==(const FieldTable& other) const; // Map-like interface. - // TODO: may need to duplicate into versions that return mutable iterator ValueMap::const_iterator begin() const { return values.begin(); } ValueMap::const_iterator end() const { return values.end(); } ValueMap::const_iterator find(const std::string& s) const { return values.find(s); } + ValueMap::iterator begin() { return values.begin(); } + ValueMap::iterator end() { return values.end(); } + ValueMap::iterator find(const std::string& s) { return values.find(s); } + std::pair insert(const ValueMap::value_type&); + QPID_COMMON_EXTERN ValueMap::iterator insert(ValueMap::iterator, const ValueMap::value_type&); void clear() { values.clear(); } // ### Hack Alert diff --git a/qpid/cpp/include/qpid/framing/FieldValue.h b/qpid/cpp/include/qpid/framing/FieldValue.h index 97fc56d606..ce4d06d2c8 100644 --- a/qpid/cpp/include/qpid/framing/FieldValue.h +++ b/qpid/cpp/include/qpid/framing/FieldValue.h @@ -36,7 +36,6 @@ namespace qpid { namespace framing { -//class Array; /** * Exception that is the base exception for all field table errors. * @@ -53,6 +52,8 @@ struct InvalidConversionException : public FieldValueException { InvalidConversionException() {} }; +class List; + /** * Value that can appear in an AMQP field table * @@ -82,7 +83,7 @@ class FieldValue { FieldValue(): data(0) {}; // Default assignment operator is fine void setType(uint8_t type); - uint8_t getType(); + QPID_COMMON_EXTERN uint8_t getType(); Data& getData() { return *data; } uint32_t encodedSize() const { return 1 + data->encodedSize(); }; bool empty() const { return data.get() == 0; } @@ -96,12 +97,19 @@ class FieldValue { template bool convertsTo() const { return false; } template T get() const { throw InvalidConversionException(); } + template T getIntegerValue() const; + template T getFloatingPointValue() const; + template bool get(T&) const; + protected: FieldValue(uint8_t t, Data* d): typeOctet(t), data(d) {} + QPID_COMMON_EXTERN static uint8_t* convertIfRequired(uint8_t* const octets, int width); + private: uint8_t typeOctet; std::auto_ptr data; + }; template <> @@ -165,10 +173,52 @@ class FixedWidthValue : public FieldValue::Data { return v; } uint8_t* rawOctets() { return octets; } + uint8_t* rawOctets() const { return octets; } void print(std::ostream& o) const { o << "F" << width << ":"; }; }; +template +inline T FieldValue::getIntegerValue() const +{ + FixedWidthValue* const fwv = dynamic_cast< FixedWidthValue* const>(data.get()); + if (fwv) { + uint8_t* octets = fwv->rawOctets(); + T v = 0; + for (int i = 0; i < W-1; ++i) { + v |= octets[i]; v <<= 8; + } + v |= octets[W-1]; + return v; + } else { + throw InvalidConversionException(); + } +} + +template +inline T FieldValue::getFloatingPointValue() const { + FixedWidthValue* const fwv = dynamic_cast< FixedWidthValue* const>(data.get()); + if (fwv) { + T value; + uint8_t* const octets = convertIfRequired(fwv->rawOctets(), W); + uint8_t* const target = reinterpret_cast(&value); + for (uint i = 0; i < W; ++i) target[i] = octets[i]; + return value; + } else { + throw InvalidConversionException(); + } +} + +template <> +inline float FieldValue::get() const { + return getFloatingPointValue(); +} + +template <> +inline double FieldValue::get() const { + return getFloatingPointValue(); +} + template <> class FixedWidthValue<0> : public FieldValue::Data { public: @@ -243,6 +293,27 @@ class EncodedValue : public FieldValue::Data { void print(std::ostream& o) const { o << "[" << value << "]"; }; }; +/** + * Accessor that can be used to get values of type FieldTable, Array + * and List. + */ +template +inline bool FieldValue::get(T& t) const +{ + const EncodedValue* v = dynamic_cast< EncodedValue* >(data.get()); + if (v != 0) { + t = v->getValue(); + return true; + } else { + try { + t = get(); + return true; + } catch (const InvalidConversionException&) { + return false; + } + } +} + class Str8Value : public FieldValue { public: QPID_COMMON_EXTERN Str8Value(const std::string& v); @@ -294,6 +365,7 @@ class Unsigned64Value : public FieldValue { class FieldTableValue : public FieldValue { public: + typedef FieldTable ValueType; QPID_COMMON_EXTERN FieldTableValue(const FieldTable&); }; @@ -302,6 +374,49 @@ class ArrayValue : public FieldValue { QPID_COMMON_EXTERN ArrayValue(const Array&); }; +class VoidValue : public FieldValue { + public: + QPID_COMMON_EXTERN VoidValue(); +}; + +class BoolValue : public FieldValue { + public: + QPID_COMMON_EXTERN BoolValue(bool); +}; + +class Unsigned8Value : public FieldValue { + public: + QPID_COMMON_EXTERN Unsigned8Value(uint8_t); +}; + +class Unsigned16Value : public FieldValue { + public: + QPID_COMMON_EXTERN Unsigned16Value(uint16_t); +}; + +class Unsigned32Value : public FieldValue { + public: + QPID_COMMON_EXTERN Unsigned32Value(uint32_t); +}; + +class Integer8Value : public FieldValue { + public: + QPID_COMMON_EXTERN Integer8Value(int8_t); +}; + +class Integer16Value : public FieldValue { + public: + QPID_COMMON_EXTERN Integer16Value(int16_t); +}; + +typedef IntegerValue Integer32Value; + +class ListValue : public FieldValue { + public: + typedef List ValueType; + QPID_COMMON_EXTERN ListValue(const List&); +}; + template bool getEncodedValue(FieldTable::ValuePtr vptr, T& value) { @@ -315,7 +430,6 @@ bool getEncodedValue(FieldTable::ValuePtr vptr, T& value) return false; } - }} // qpid::framing #endif diff --git a/qpid/cpp/include/qpid/framing/List.h b/qpid/cpp/include/qpid/framing/List.h new file mode 100644 index 0000000000..0f17c7884c --- /dev/null +++ b/qpid/cpp/include/qpid/framing/List.h @@ -0,0 +1,77 @@ +#ifndef QPID_FRAMING_LIST_H +#define QPID_FRAMING_LIST_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/CommonImportExport.h" +#include "qpid/framing/amqp_types.h" +#include +#include +#include + +namespace qpid { +namespace framing { + +class Buffer; +class FieldValue; + +/** + * Representation of an AMQP 0-10 list + */ +class List +{ + public: + typedef boost::shared_ptr ValuePtr; + typedef std::list Values; + typedef Values::const_iterator const_iterator; + typedef Values::iterator iterator; + typedef Values::const_reference const_reference; + typedef Values::reference reference; + + QPID_COMMON_EXTERN uint32_t encodedSize() const; + QPID_COMMON_EXTERN void encode(Buffer& buffer) const; + QPID_COMMON_EXTERN void decode(Buffer& buffer); + + QPID_COMMON_EXTERN bool operator==(const List& other) const; + + // std collection interface. + QPID_COMMON_EXTERN const_iterator begin() const { return values.begin(); } + QPID_COMMON_EXTERN const_iterator end() const { return values.end(); } + QPID_COMMON_EXTERN iterator begin() { return values.begin(); } + QPID_COMMON_EXTERN iterator end(){ return values.end(); } + + QPID_COMMON_EXTERN ValuePtr front() const { return values.front(); } + QPID_COMMON_EXTERN ValuePtr back() const { return values.back(); } + QPID_COMMON_EXTERN size_t size() const { return values.size(); } + + QPID_COMMON_EXTERN iterator insert(iterator i, ValuePtr value) { return values.insert(i, value); } + QPID_COMMON_EXTERN void erase(iterator i) { values.erase(i); } + QPID_COMMON_EXTERN void push_back(ValuePtr value) { values.insert(end(), value); } + QPID_COMMON_EXTERN void pop_back() { values.pop_back(); } + + private: + Values values; + + friend QPID_COMMON_EXTERN std::ostream& operator<<(std::ostream& out, const List& list); +}; +}} // namespace qpid::framing + +#endif /*!QPID_FRAMING_LIST_H*/ diff --git a/qpid/cpp/include/qpid/framing/Uuid.h b/qpid/cpp/include/qpid/framing/Uuid.h index 0dfa7a58e7..618515622d 100644 --- a/qpid/cpp/include/qpid/framing/Uuid.h +++ b/qpid/cpp/include/qpid/framing/Uuid.h @@ -20,7 +20,6 @@ */ #include "qpid/CommonImportExport.h" -#include "qpid/sys/uuid.h" #include "qpid/sys/IntegerTypes.h" #include @@ -38,33 +37,31 @@ class Buffer; * * Full value semantics, operators ==, < etc. are provided by * boost::array so Uuid can be the key type in a map etc. + * + * TODO: change this implementation as it leaks boost into the + * client API */ struct Uuid : public boost::array { /** If unique is true, generate a unique ID else a null ID. */ - Uuid(bool unique=false) { if (unique) generate(); else clear(); } + QPID_COMMON_EXTERN Uuid(bool unique=false); /** Copy from 16 bytes of data. */ - Uuid(const uint8_t* data) { assign(data); } + QPID_COMMON_EXTERN Uuid(const uint8_t* data); + + // Default op= and copy ctor are fine. + // boost::array gives us ==, < etc. /** Copy from 16 bytes of data. */ - void assign(const uint8_t* data) { - uuid_copy(c_array(), data); - } + void assign(const uint8_t* data); /** Set to a new unique identifier. */ - void generate() { uuid_generate(c_array()); } + QPID_COMMON_EXTERN void generate(); /** Set to all zeros. */ - void clear() { uuid_clear(c_array()); } + void clear(); /** Test for null (all zeros). */ - // Force int 0/!0 to false/true; avoids compile warnings. - bool isNull() { - return !!uuid_is_null(data()); - } - - // Default op= and copy ctor are fine. - // boost::array gives us ==, < etc. + bool isNull(); QPID_COMMON_EXTERN void encode(framing::Buffer& buf) const; QPID_COMMON_EXTERN void decode(framing::Buffer& buf); diff --git a/qpid/cpp/include/qpid/management/Manageable.h b/qpid/cpp/include/qpid/management/Manageable.h index 8062479ac6..7a72cc1592 100644 --- a/qpid/cpp/include/qpid/management/Manageable.h +++ b/qpid/cpp/include/qpid/management/Manageable.h @@ -43,7 +43,7 @@ class QPID_COMMON_EXTERN Manageable static const status_t STATUS_UNKNOWN_OBJECT = 1; static const status_t STATUS_UNKNOWN_METHOD = 2; static const status_t STATUS_NOT_IMPLEMENTED = 3; - static const status_t STATUS_INVALID_PARAMETER = 4; + static const status_t STATUS_PARAMETER_INVALID = 4; static const status_t STATUS_FEATURE_NOT_IMPLEMENTED = 5; static const status_t STATUS_FORBIDDEN = 6; static const status_t STATUS_EXCEPTION = 7; diff --git a/qpid/cpp/include/qpid/messaging/Address.h b/qpid/cpp/include/qpid/messaging/Address.h new file mode 100644 index 0000000000..e66c52f4c2 --- /dev/null +++ b/qpid/cpp/include/qpid/messaging/Address.h @@ -0,0 +1,58 @@ +#ifndef QPID_MESSAGING_ADDRESS_H +#define QPID_MESSAGING_ADDRESS_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include +#include "qpid/client/ClientImportExport.h" +#include + +namespace qpid { +namespace client { +} + +namespace messaging { + +/** + * Represents an address to which messages can be sent and from which + * messages can be received. Often a simple name is sufficient for + * this. However this struct allows the type of address to be + * specified allowing more sophisticated treatment if necessary. + */ +struct Address +{ + std::string value; + std::string type; + + QPID_CLIENT_EXTERN Address(); + QPID_CLIENT_EXTERN Address(const std::string& address); + QPID_CLIENT_EXTERN Address(const std::string& address, const std::string& type); + QPID_CLIENT_EXTERN operator const std::string&() const; + QPID_CLIENT_EXTERN const std::string& toStr() const; + QPID_CLIENT_EXTERN operator bool() const; + QPID_CLIENT_EXTERN bool operator !() const; +}; + +QPID_CLIENT_EXTERN std::ostream& operator<<(std::ostream& out, const Address& address); + +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_ADDRESS_H*/ diff --git a/qpid/cpp/include/qpid/messaging/Codec.h b/qpid/cpp/include/qpid/messaging/Codec.h new file mode 100644 index 0000000000..bacec5c786 --- /dev/null +++ b/qpid/cpp/include/qpid/messaging/Codec.h @@ -0,0 +1,44 @@ +#ifndef QPID_MESSAGING_CODEC_H +#define QPID_MESSAGING_CODEC_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include +#include "qpid/client/ClientImportExport.h" + +namespace qpid { +namespace messaging { + +class Variant; +/** + * + */ +class Codec +{ + public: + QPID_CLIENT_EXTERN virtual ~Codec() {} + virtual void encode(const Variant&, std::string&) = 0; + virtual void decode(const std::string&, Variant&) = 0; + private: +}; +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_CODEC_H*/ diff --git a/qpid/cpp/include/qpid/messaging/Connection.h b/qpid/cpp/include/qpid/messaging/Connection.h new file mode 100644 index 0000000000..19dae586a4 --- /dev/null +++ b/qpid/cpp/include/qpid/messaging/Connection.h @@ -0,0 +1,67 @@ +#ifndef QPID_MESSAGING_CONNECTION_H +#define QPID_MESSAGING_CONNECTION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include +#include "qpid/client/ClientImportExport.h" +#include "qpid/client/Handle.h" +#include "qpid/messaging/Variant.h" + +namespace qpid { +namespace client { + +template class PrivateImplRef; + +} + +namespace messaging { + +class ConnectionImpl; +class Session; + +class Connection : public qpid::client::Handle +{ + public: + static QPID_CLIENT_EXTERN Connection open(const std::string& url, const Variant::Map& options = Variant::Map()); + + QPID_CLIENT_EXTERN Connection(ConnectionImpl* impl = 0); + QPID_CLIENT_EXTERN Connection(const Connection&); + QPID_CLIENT_EXTERN ~Connection(); + QPID_CLIENT_EXTERN Connection& operator=(const Connection&); + QPID_CLIENT_EXTERN void close(); + QPID_CLIENT_EXTERN Session newSession(); + private: + friend class qpid::client::PrivateImplRef; + +}; + +struct InvalidOptionString : public qpid::Exception +{ + InvalidOptionString(const std::string& msg); +}; + +QPID_CLIENT_EXTERN void parseOptionString(const std::string&, Variant::Map&); +QPID_CLIENT_EXTERN Variant::Map parseOptionString(const std::string&); + +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_CONNECTION_H*/ diff --git a/qpid/cpp/include/qpid/messaging/Filter.h b/qpid/cpp/include/qpid/messaging/Filter.h new file mode 100644 index 0000000000..5cd844cf73 --- /dev/null +++ b/qpid/cpp/include/qpid/messaging/Filter.h @@ -0,0 +1,48 @@ +#ifndef QPID_MESSAGING_FILTER_H +#define QPID_MESSAGING_FILTER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include +#include +#include "qpid/client/ClientImportExport.h" + +namespace qpid { +namespace client { +} + +namespace messaging { + +struct Filter +{ + std::string type; + std::vector patterns; + + QPID_CLIENT_EXTERN Filter(std::string type, std::string pattern); + QPID_CLIENT_EXTERN Filter(std::string type, std::string pattern1, std::string pattern2); + + static QPID_CLIENT_EXTERN const std::string WILDCARD; + static QPID_CLIENT_EXTERN const std::string EXACT_MATCH; +}; + +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_FILTER_H*/ diff --git a/qpid/cpp/include/qpid/messaging/Message.h b/qpid/cpp/include/qpid/messaging/Message.h new file mode 100644 index 0000000000..e68d8a1141 --- /dev/null +++ b/qpid/cpp/include/qpid/messaging/Message.h @@ -0,0 +1,88 @@ +#ifndef QPID_MESSAGING_MESSAGE_H +#define QPID_MESSAGING_MESSAGE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include +#include "qpid/messaging/Variant.h" +#include "qpid/messaging/MessageContent.h" +#include "qpid/client/ClientImportExport.h" + +namespace qpid { +namespace client { +} + +namespace messaging { + +struct Address; +class Codec; +struct MessageImpl; + +/** + * Representation of a message. + */ +class Message +{ + public: + QPID_CLIENT_EXTERN Message(const std::string& bytes = std::string()); + QPID_CLIENT_EXTERN Message(const char*, size_t); + QPID_CLIENT_EXTERN Message(const Message&); + QPID_CLIENT_EXTERN ~Message(); + + QPID_CLIENT_EXTERN Message& operator=(const Message&); + + QPID_CLIENT_EXTERN void setReplyTo(const Address&); + QPID_CLIENT_EXTERN const Address& getReplyTo() const; + + QPID_CLIENT_EXTERN void setSubject(const std::string&); + QPID_CLIENT_EXTERN const std::string& getSubject() const; + + QPID_CLIENT_EXTERN void setContentType(const std::string&); + QPID_CLIENT_EXTERN const std::string& getContentType() const; + + QPID_CLIENT_EXTERN const VariantMap& getHeaders() const; + QPID_CLIENT_EXTERN VariantMap& getHeaders(); + + QPID_CLIENT_EXTERN const std::string& getBytes() const; + QPID_CLIENT_EXTERN std::string& getBytes(); + QPID_CLIENT_EXTERN void setBytes(const std::string&); + QPID_CLIENT_EXTERN void setBytes(const char* chars, size_t count); + QPID_CLIENT_EXTERN const char* getRawContent() const; + QPID_CLIENT_EXTERN size_t getContentSize() const; + + + QPID_CLIENT_EXTERN MessageContent& getContent(); + QPID_CLIENT_EXTERN const MessageContent& getContent() const; + QPID_CLIENT_EXTERN void setContent(const std::string& s); + QPID_CLIENT_EXTERN void setContent(const Variant::Map&); + QPID_CLIENT_EXTERN void setContent(const Variant::List&); + + QPID_CLIENT_EXTERN void encode(Codec&); + QPID_CLIENT_EXTERN void decode(Codec&); + + private: + MessageImpl* impl; + friend struct MessageImplAccess; +}; +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_MESSAGE_H*/ diff --git a/qpid/cpp/include/qpid/messaging/MessageContent.h b/qpid/cpp/include/qpid/messaging/MessageContent.h new file mode 100644 index 0000000000..7c3a636c07 --- /dev/null +++ b/qpid/cpp/include/qpid/messaging/MessageContent.h @@ -0,0 +1,90 @@ +#ifndef QPID_MESSAGING_MESSAGECONTENT_H +#define QPID_MESSAGING_MESSAGECONTENT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/messaging/Variant.h" +#include +#include "qpid/client/ClientImportExport.h" + +namespace qpid { +namespace messaging { + +/** + * + */ +class MessageContent +{ + public: + QPID_CLIENT_EXTERN virtual ~MessageContent() {} + + virtual const std::string& asString() const = 0; + virtual std::string& asString() = 0; + + virtual const char* asChars() const = 0; + virtual size_t size() const = 0; + + virtual const Variant::Map& asMap() const = 0; + virtual Variant::Map& asMap() = 0; + virtual bool isMap() const = 0; + + virtual const Variant::List& asList() const = 0; + virtual Variant::List& asList() = 0; + virtual bool isList() const = 0; + + virtual void clear() = 0; + + virtual Variant& operator[](const std::string&) = 0; + + + virtual std::ostream& print(std::ostream& out) const = 0; + + + //operator<< for variety of types... (is this a good idea?) + virtual MessageContent& operator<<(const std::string&) = 0; + virtual MessageContent& operator<<(const char*) = 0; + virtual MessageContent& operator<<(bool) = 0; + virtual MessageContent& operator<<(int8_t) = 0; + virtual MessageContent& operator<<(int16_t) = 0; + virtual MessageContent& operator<<(int32_t) = 0; + virtual MessageContent& operator<<(int64_t) = 0; + virtual MessageContent& operator<<(uint8_t) = 0; + virtual MessageContent& operator<<(uint16_t) = 0; + virtual MessageContent& operator<<(uint32_t) = 0; + virtual MessageContent& operator<<(uint64_t) = 0; + virtual MessageContent& operator<<(double) = 0; + virtual MessageContent& operator<<(float) = 0; + + //assignment from string, map and list + virtual MessageContent& operator=(const std::string&) = 0; + virtual MessageContent& operator=(const char*) = 0; + virtual MessageContent& operator=(const Variant::Map&) = 0; + virtual MessageContent& operator=(const Variant::List&) = 0; + + private: +}; + +QPID_CLIENT_EXTERN std::ostream& operator<<(std::ostream& out, const MessageContent& content); + +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_MESSAGECONTENT_H*/ diff --git a/qpid/cpp/include/qpid/messaging/MessageListener.h b/qpid/cpp/include/qpid/messaging/MessageListener.h new file mode 100644 index 0000000000..72811e7b9c --- /dev/null +++ b/qpid/cpp/include/qpid/messaging/MessageListener.h @@ -0,0 +1,49 @@ +#ifndef QPID_MESSAGING_MESSAGELISTENER_H +#define QPID_MESSAGING_MESSAGELISTENER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/ClientImportExport.h" + +namespace qpid { +namespace messaging { + +class Message; + +/** + * To use a push style interface for receiving messages, applications + * provide implementations of this interface and pass an implementing + * instance to MessageSource::subscribe(). + * + * Messages arriving for that subscription will then be passed to the + * implementation via the received() method. + */ +class MessageListener +{ + public: + QPID_CLIENT_EXTERN virtual ~MessageListener() {} + virtual void received(Message&) = 0; + private: +}; + +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_MESSAGELISTENER_H*/ diff --git a/qpid/cpp/include/qpid/messaging/Receiver.h b/qpid/cpp/include/qpid/messaging/Receiver.h new file mode 100644 index 0000000000..e51e1093d1 --- /dev/null +++ b/qpid/cpp/include/qpid/messaging/Receiver.h @@ -0,0 +1,115 @@ +#ifndef QPID_MESSAGING_RECEIVER_H +#define QPID_MESSAGING_RECEIVER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/Exception.h" +#include "qpid/client/ClientImportExport.h" +#include "qpid/client/Handle.h" +#include "qpid/sys/Time.h" + +namespace qpid { +namespace client { + +template class PrivateImplRef; + +} + +namespace messaging { + +class Message; +class MessageListener; +class ReceiverImpl; + +/** + * A pull style interface for message retrieval. + */ +class Receiver : public qpid::client::Handle +{ + public: + struct NoMessageAvailable : qpid::Exception {}; + + QPID_CLIENT_EXTERN Receiver(ReceiverImpl* impl = 0); + QPID_CLIENT_EXTERN Receiver(const Receiver&); + QPID_CLIENT_EXTERN ~Receiver(); + QPID_CLIENT_EXTERN Receiver& operator=(const Receiver&); + /** + * Retrieves a message from this receivers local queue, or waits + * for upto the specified timeout for a message to become + * available. Returns false if there is no message to give after + * waiting for the specified timeout. + */ + QPID_CLIENT_EXTERN bool get(Message& message, qpid::sys::Duration timeout=qpid::sys::TIME_INFINITE); + /** + * Retrieves a message from this receivers local queue, or waits + * for upto the specified timeout for a message to become + * available. Throws NoMessageAvailable if there is no + * message to give after waiting for the specified timeout. + */ + QPID_CLIENT_EXTERN Message get(qpid::sys::Duration timeout=qpid::sys::TIME_INFINITE); + /** + * Retrieves a message for this receivers subscription or waits + * for upto the specified timeout for one to become + * available. Unlike get() this method will check with the server + * that there is no message for the subscription this receiver is + * serving before returning false. + */ + QPID_CLIENT_EXTERN bool fetch(Message& message, qpid::sys::Duration timeout=qpid::sys::TIME_INFINITE); + /** + * Retrieves a message for this receivers subscription or waits + * for upto the specified timeout for one to become + * available. Unlike get() this method will check with the server + * that there is no message for the subscription this receiver is + * serving before throwing an exception. + */ + QPID_CLIENT_EXTERN Message fetch(qpid::sys::Duration timeout=qpid::sys::TIME_INFINITE); + + /** + * Enables the message flow for this receiver + */ + QPID_CLIENT_EXTERN void start(); + /** + * Stops the message flow for this receiver (without actually + * cancelling the subscription). + */ + QPID_CLIENT_EXTERN void stop(); + /** + * Sets the capacity for the receiver. The capacity determines how + * many incoming messages can be held in the receiver before being + * requested by a client via fetch() (or pushed to a listener). + */ + QPID_CLIENT_EXTERN void setCapacity(uint32_t); + + /** + * Cancels this receiver + */ + QPID_CLIENT_EXTERN void cancel(); + + /** + * Set a message listener for receiving messages asynchronously. + */ + QPID_CLIENT_EXTERN void setListener(MessageListener* listener); + private: + friend class qpid::client::PrivateImplRef; +}; +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_RECEIVER_H*/ diff --git a/qpid/cpp/include/qpid/messaging/Sender.h b/qpid/cpp/include/qpid/messaging/Sender.h new file mode 100644 index 0000000000..45ec659ecf --- /dev/null +++ b/qpid/cpp/include/qpid/messaging/Sender.h @@ -0,0 +1,57 @@ +#ifndef QPID_MESSAGING_SENDER_H +#define QPID_MESSAGING_SENDER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/ClientImportExport.h" +#include "qpid/client/Handle.h" + +namespace qpid { +namespace client { + +template class PrivateImplRef; + +} + +namespace messaging { + +class Message; +class SenderImpl; + +/** + * Interface through which messages are sent. + */ +class Sender : public qpid::client::Handle +{ + public: + QPID_CLIENT_EXTERN Sender(SenderImpl* impl = 0); + QPID_CLIENT_EXTERN Sender(const Sender&); + QPID_CLIENT_EXTERN ~Sender(); + QPID_CLIENT_EXTERN Sender& operator=(const Sender&); + + QPID_CLIENT_EXTERN void send(const Message& message); + QPID_CLIENT_EXTERN void cancel(); + private: + friend class qpid::client::PrivateImplRef; +}; +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_SENDER_H*/ diff --git a/qpid/cpp/include/qpid/messaging/Session.h b/qpid/cpp/include/qpid/messaging/Session.h new file mode 100644 index 0000000000..1d88882db6 --- /dev/null +++ b/qpid/cpp/include/qpid/messaging/Session.h @@ -0,0 +1,99 @@ +#ifndef QPID_MESSAGING_SESSION_H +#define QPID_MESSAGING_SESSION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/ClientImportExport.h" +#include "qpid/client/Handle.h" +#include "qpid/sys/Time.h" +#include "Variant.h" + +namespace qpid { +namespace client { + +template class PrivateImplRef; + +} + +namespace messaging { + +struct Address; +struct Filter; +class Message; +class MessageListener; +class Sender; +class Receiver; +class SessionImpl; +class Subscription; + +/** + * A session represents a distinct 'conversation' which can involve + * sending and receiving messages from different sources and sinks. + */ +class Session : public qpid::client::Handle +{ + public: + QPID_CLIENT_EXTERN Session(SessionImpl* impl = 0); + QPID_CLIENT_EXTERN Session(const Session&); + QPID_CLIENT_EXTERN ~Session(); + QPID_CLIENT_EXTERN Session& operator=(const Session&); + + QPID_CLIENT_EXTERN void close(); + + QPID_CLIENT_EXTERN void commit(); + QPID_CLIENT_EXTERN void rollback(); + + /** + * Acknowledges all outstanding messages that have been received + * by the application on this session. + */ + QPID_CLIENT_EXTERN void acknowledge(); + /** + * Rejects the specified message. This will prevent the message + * being redelivered. + */ + QPID_CLIENT_EXTERN void reject(Message&); + + QPID_CLIENT_EXTERN void sync(); + QPID_CLIENT_EXTERN void flush(); + + QPID_CLIENT_EXTERN bool fetch(Message& message, qpid::sys::Duration timeout=qpid::sys::TIME_INFINITE); + QPID_CLIENT_EXTERN Message fetch(qpid::sys::Duration timeout=qpid::sys::TIME_INFINITE); + QPID_CLIENT_EXTERN bool dispatch(qpid::sys::Duration timeout=qpid::sys::TIME_INFINITE); + + QPID_CLIENT_EXTERN Sender createSender(const Address& address, const VariantMap& options = VariantMap()); + QPID_CLIENT_EXTERN Sender createSender(const std::string& address, const VariantMap& options = VariantMap()); + + QPID_CLIENT_EXTERN Receiver createReceiver(const Address& address, const VariantMap& options = VariantMap()); + QPID_CLIENT_EXTERN Receiver createReceiver(const Address& address, const Filter& filter, const VariantMap& options = VariantMap()); + QPID_CLIENT_EXTERN Receiver createReceiver(const std::string& address, const VariantMap& options = VariantMap()); + QPID_CLIENT_EXTERN Receiver createReceiver(const std::string& address, const Filter& filter, const VariantMap& options = VariantMap()); + + QPID_CLIENT_EXTERN Address createTempQueue(const std::string& baseName = std::string()); + + QPID_CLIENT_EXTERN void* getLastConfirmedSent(); + QPID_CLIENT_EXTERN void* getLastConfirmedAcknowledged(); + private: + friend class qpid::client::PrivateImplRef; +}; +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_SESSION_H*/ diff --git a/qpid/cpp/include/qpid/messaging/Variant.h b/qpid/cpp/include/qpid/messaging/Variant.h new file mode 100644 index 0000000000..1e51914794 --- /dev/null +++ b/qpid/cpp/include/qpid/messaging/Variant.h @@ -0,0 +1,167 @@ +#ifndef QPID_MESSAGING_VARIANT_H +#define QPID_MESSAGING_VARIANT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include +#include +#include +#include +#include "qpid/Exception.h" +#include "qpid/sys/IntegerTypes.h" +#include "qpid/client/ClientImportExport.h" + +namespace qpid { +namespace client { +} + +namespace messaging { + +/** + * Thrown when an illegal conversion of a variant is attempted. + */ +struct InvalidConversion : public qpid::Exception +{ + InvalidConversion(const std::string& msg); +}; + +enum VariantType { + VAR_VOID = 0, + VAR_BOOL, + VAR_UINT8, + VAR_UINT16, + VAR_UINT32, + VAR_UINT64, + VAR_INT8, + VAR_INT16, + VAR_INT32, + VAR_INT64, + VAR_FLOAT, + VAR_DOUBLE, + VAR_STRING, + VAR_MAP, + VAR_LIST +}; + +class VariantImpl; + +/** + * Represents a value of variable type. + */ +class Variant +{ + public: + typedef std::map Map; + typedef std::list List; + + QPID_CLIENT_EXTERN Variant(); + QPID_CLIENT_EXTERN Variant(bool); + QPID_CLIENT_EXTERN Variant(uint8_t); + QPID_CLIENT_EXTERN Variant(uint16_t); + QPID_CLIENT_EXTERN Variant(uint32_t); + QPID_CLIENT_EXTERN Variant(uint64_t); + QPID_CLIENT_EXTERN Variant(int8_t); + QPID_CLIENT_EXTERN Variant(int16_t); + QPID_CLIENT_EXTERN Variant(int32_t); + QPID_CLIENT_EXTERN Variant(int64_t); + QPID_CLIENT_EXTERN Variant(float); + QPID_CLIENT_EXTERN Variant(double); + QPID_CLIENT_EXTERN Variant(const std::string&); + QPID_CLIENT_EXTERN Variant(const char*); + QPID_CLIENT_EXTERN Variant(const Map&); + QPID_CLIENT_EXTERN Variant(const List&); + QPID_CLIENT_EXTERN Variant(const Variant&); + + QPID_CLIENT_EXTERN ~Variant(); + + QPID_CLIENT_EXTERN VariantType getType() const; + + QPID_CLIENT_EXTERN Variant& operator=(bool); + QPID_CLIENT_EXTERN Variant& operator=(uint8_t); + QPID_CLIENT_EXTERN Variant& operator=(uint16_t); + QPID_CLIENT_EXTERN Variant& operator=(uint32_t); + QPID_CLIENT_EXTERN Variant& operator=(uint64_t); + QPID_CLIENT_EXTERN Variant& operator=(int8_t); + QPID_CLIENT_EXTERN Variant& operator=(int16_t); + QPID_CLIENT_EXTERN Variant& operator=(int32_t); + QPID_CLIENT_EXTERN Variant& operator=(int64_t); + QPID_CLIENT_EXTERN Variant& operator=(float); + QPID_CLIENT_EXTERN Variant& operator=(double); + QPID_CLIENT_EXTERN Variant& operator=(const std::string&); + QPID_CLIENT_EXTERN Variant& operator=(const char*); + QPID_CLIENT_EXTERN Variant& operator=(const Map&); + QPID_CLIENT_EXTERN Variant& operator=(const List&); + QPID_CLIENT_EXTERN Variant& operator=(const Variant&); + + QPID_CLIENT_EXTERN bool asBool() const; + QPID_CLIENT_EXTERN uint8_t asUint8() const; + QPID_CLIENT_EXTERN uint16_t asUint16() const; + QPID_CLIENT_EXTERN uint32_t asUint32() const; + QPID_CLIENT_EXTERN uint64_t asUint64() const; + QPID_CLIENT_EXTERN int8_t asInt8() const; + QPID_CLIENT_EXTERN int16_t asInt16() const; + QPID_CLIENT_EXTERN int32_t asInt32() const; + QPID_CLIENT_EXTERN int64_t asInt64() const; + QPID_CLIENT_EXTERN float asFloat() const; + QPID_CLIENT_EXTERN double asDouble() const; + QPID_CLIENT_EXTERN std::string asString() const; + + QPID_CLIENT_EXTERN operator bool() const; + QPID_CLIENT_EXTERN operator uint8_t() const; + QPID_CLIENT_EXTERN operator uint16_t() const; + QPID_CLIENT_EXTERN operator uint32_t() const; + QPID_CLIENT_EXTERN operator uint64_t() const; + QPID_CLIENT_EXTERN operator int8_t() const; + QPID_CLIENT_EXTERN operator int16_t() const; + QPID_CLIENT_EXTERN operator int32_t() const; + QPID_CLIENT_EXTERN operator int64_t() const; + QPID_CLIENT_EXTERN operator float() const; + QPID_CLIENT_EXTERN operator double() const; + QPID_CLIENT_EXTERN operator const char*() const; + + QPID_CLIENT_EXTERN const Map& asMap() const; + QPID_CLIENT_EXTERN Map& asMap(); + QPID_CLIENT_EXTERN const List& asList() const; + QPID_CLIENT_EXTERN List& asList(); + /** + * Unlike asString(), getString() will not do any conversions and + * will throw InvalidConversion if the type is not STRING. + */ + QPID_CLIENT_EXTERN const std::string& getString() const; + QPID_CLIENT_EXTERN std::string& getString(); + + QPID_CLIENT_EXTERN void setEncoding(const std::string&); + QPID_CLIENT_EXTERN const std::string& getEncoding() const; + + QPID_CLIENT_EXTERN void reset(); + private: + VariantImpl* impl; +}; + +QPID_CLIENT_EXTERN std::ostream& operator<<(std::ostream& out, const Variant& value); +QPID_CLIENT_EXTERN std::ostream& operator<<(std::ostream& out, const Variant::Map& map); +QPID_CLIENT_EXTERN std::ostream& operator<<(std::ostream& out, const Variant::List& list); + +typedef Variant::Map VariantMap; + +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_VARIANT_H*/ diff --git a/qpid/cpp/include/qpid/sys/uuid.h b/qpid/cpp/include/qpid/sys/uuid.h deleted file mode 100644 index 804ab34463..0000000000 --- a/qpid/cpp/include/qpid/sys/uuid.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef _sys_uuid_h -#define _sys_uuid_h - -/* - * - * Copyright (c) 2006 The Apache Software Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#ifdef _WIN32 -# include "qpid/sys/windows/uuid.h" -#else -# include -#endif /* _WIN32 */ - -#endif /* _sys_uuid_h */ diff --git a/qpid/cpp/include/qpid/sys/windows/Condition.h b/qpid/cpp/include/qpid/sys/windows/Condition.h index c31f7b4823..979fae9b0a 100755 --- a/qpid/cpp/include/qpid/sys/windows/Condition.h +++ b/qpid/cpp/include/qpid/sys/windows/Condition.h @@ -30,7 +30,6 @@ #include #include #include -#undef STATUS_INVALID_PARAMETER // Hack for windows.h namespace pollution namespace qpid { namespace sys { diff --git a/qpid/cpp/include/qpid/sys/windows/Mutex.h b/qpid/cpp/include/qpid/sys/windows/Mutex.h index 12768640d5..5dcc69e836 100755 --- a/qpid/cpp/include/qpid/sys/windows/Mutex.h +++ b/qpid/cpp/include/qpid/sys/windows/Mutex.h @@ -31,7 +31,6 @@ #include #include #include -#undef STATUS_INVALID_PARAMETER // Hack for windows.h namespace pollution namespace qpid { namespace sys { diff --git a/qpid/cpp/include/qpid/sys/windows/uuid.h b/qpid/cpp/include/qpid/sys/windows/uuid.h deleted file mode 100644 index c79abe95c6..0000000000 --- a/qpid/cpp/include/qpid/sys/windows/uuid.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef _sys_windows_uuid_h -#define _sys_windows_uuid_h - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include "qpid/CommonImportExport.h" -#include - -namespace qpid { namespace sys { const size_t UuidSize = 16; }} -typedef uint8_t uuid_t[qpid::sys::UuidSize]; - -QPID_COMMON_EXTERN void uuid_clear (uuid_t uu); -QPID_COMMON_EXTERN void uuid_copy (uuid_t dst, const uuid_t src); -QPID_COMMON_EXTERN void uuid_generate (uuid_t out); -QPID_COMMON_EXTERN int uuid_is_null (const uuid_t uu); // Returns 1 if null, else 0 -QPID_COMMON_EXTERN int uuid_parse (const char *in, uuid_t uu); // Returns 0 on success, else -1 -QPID_COMMON_EXTERN void uuid_unparse (const uuid_t uu, char *out); - -#endif /*!_sys_windows_uuid_h*/ diff --git a/qpid/cpp/src/CMakeLists.txt b/qpid/cpp/src/CMakeLists.txt index a1d01ceca4..ff0188890b 100644 --- a/qpid/cpp/src/CMakeLists.txt +++ b/qpid/cpp/src/CMakeLists.txt @@ -451,6 +451,7 @@ set (libqpidcommon_SOURCES qpid/framing/FieldValue.cpp qpid/framing/FrameSet.cpp qpid/framing/FrameDecoder.cpp + qpid/framing/List.cpp qpid/framing/ProtocolInitiation.cpp qpid/framing/ProtocolVersion.cpp qpid/framing/SendContent.cpp @@ -522,6 +523,40 @@ set (libqpidclient_SOURCES qpid/client/SubscriptionImpl.cpp qpid/client/SubscriptionManager.cpp qpid/client/SubscriptionManagerImpl.cpp + qpid/messaging/Address.cpp + qpid/messaging/Connection.cpp + qpid/messaging/ConnectionImpl.h + qpid/messaging/Filter.cpp + qpid/messaging/Message.cpp + qpid/messaging/MessageImpl.h + qpid/messaging/MessageImpl.cpp + qpid/messaging/Receiver.cpp + qpid/messaging/ReceiverImpl.h + qpid/messaging/Session.cpp + qpid/messaging/SessionImpl.h + qpid/messaging/Sender.cpp + qpid/messaging/SenderImpl.h + qpid/messaging/Variant.cpp + qpid/client/amqp0_10/AddressResolution.h + qpid/client/amqp0_10/AddressResolution.cpp + qpid/client/amqp0_10/Codecs.cpp + qpid/client/amqp0_10/CodecsInternal.h + qpid/client/amqp0_10/CompletionTracker.h + qpid/client/amqp0_10/CompletionTracker.cpp + qpid/client/amqp0_10/ConnectionImpl.h + qpid/client/amqp0_10/ConnectionImpl.cpp + qpid/client/amqp0_10/IncomingMessages.h + qpid/client/amqp0_10/IncomingMessages.cpp + qpid/client/amqp0_10/MessageSink.h + qpid/client/amqp0_10/MessageSource.h + qpid/client/amqp0_10/OutgoingMessage.h + qpid/client/amqp0_10/OutgoingMessage.cpp + qpid/client/amqp0_10/ReceiverImpl.h + qpid/client/amqp0_10/ReceiverImpl.cpp + qpid/client/amqp0_10/SessionImpl.h + qpid/client/amqp0_10/SessionImpl.cpp + qpid/client/amqp0_10/SenderImpl.h + qpid/client/amqp0_10/SenderImpl.cpp ) add_library (qpidclient SHARED ${libqpidclient_SOURCES}) target_link_libraries (qpidclient qpidcommon) @@ -615,10 +650,10 @@ target_link_libraries (qpidd qpidbroker qpidcommon ${Boost_PROGRAM_OPTIONS_LIBRA # qpid/agent/ManagementAgent.h \ # qpid/agent/ManagementAgentImpl.h set (qmfagent_SOURCES - ../include/qpid/agent/ManagementAgent.h - qpid/agent/ManagementAgentImpl.cpp - qpid/agent/ManagementAgentImpl.h - qmf/Agent.cpp + qmf/AgentEngine.cpp + qmf/AgentEngine.h + qpid/agent/ManagementAgentImpl.cpp + qpid/agent/ManagementAgentImpl.h ) add_library (qmfagent SHARED ${qmfagent_SOURCES}) target_link_libraries (qmfagent qmfcommon) @@ -626,14 +661,31 @@ set_target_properties (qmfagent PROPERTIES VERSION ${qpidc_version}) set (qmfcommon_SOURCES - qmf/Agent.cpp - qmf/ResilientConnection.cpp + qmf/ConnectionSettingsImpl.cpp + qmf/ConnectionSettingsImpl.h + qmf/ConsoleEngine.h + qmf/Event.h + qmf/Message.h qmf/MessageImpl.cpp - qmf/SchemaImpl.cpp - qmf/ValueImpl.cpp + qmf/MessageImpl.h + qmf/Object.h + qmf/ObjectId.h qmf/ObjectIdImpl.cpp + qmf/ObjectIdImpl.h qmf/ObjectImpl.cpp + qmf/ObjectImpl.h + qmf/Query.h qmf/QueryImpl.cpp + qmf/QueryImpl.h + qmf/ResilientConnection.cpp + qmf/ResilientConnection.h + qmf/Schema.h + qmf/SchemaImpl.cpp + qmf/SchemaImpl.h + qmf/Typecode.h + qmf/Value.h + qmf/ValueImpl.cpp + qmf/ValueImpl.h ) add_library (qmfcommon SHARED ${qmfcommon_SOURCES}) target_link_libraries (qmfcommon qpidclient) diff --git a/qpid/cpp/src/Makefile.am b/qpid/cpp/src/Makefile.am index 0eff526203..75cda31dbb 100644 --- a/qpid/cpp/src/Makefile.am +++ b/qpid/cpp/src/Makefile.am @@ -54,7 +54,7 @@ windows_dist = \ qpid/sys/windows/Time.cpp \ ../include/qpid/sys/windows/Time.h \ qpid/sys/windows/uuid.cpp \ - ../include/qpid/sys/windows/uuid.h \ + qpid/sys/windows/uuid.h \ windows/QpiddBroker.cpp \ qpid/broker/windows/BrokerDefaults.cpp \ qpid/broker/windows/SaslAuthenticator.cpp @@ -378,6 +378,7 @@ libqpidcommon_la_SOURCES += \ qpid/framing/InputHandler.h \ qpid/framing/Invoker.h \ qpid/framing/IsInSequenceSet.h \ + qpid/framing/List.cpp \ qpid/framing/MethodBodyFactory.h \ qpid/framing/MethodContent.h \ qpid/framing/ModelMethod.h \ @@ -460,7 +461,8 @@ libqpidcommon_la_SOURCES += \ qpid/sys/Timer.cpp \ qpid/sys/Timer.h \ qpid/sys/Waitable.h \ - qpid/sys/alloca.h + qpid/sys/alloca.h \ + qpid/sys/uuid.h if HAVE_SASL libqpidcommon_la_SOURCES += qpid/sys/cyrus/CyrusSecurityLayer.h @@ -680,7 +682,41 @@ libqpidclient_la_SOURCES = \ qpid/client/SubscriptionImpl.h \ qpid/client/SubscriptionManager.cpp \ qpid/client/SubscriptionManagerImpl.cpp \ - qpid/client/SubscriptionManagerImpl.h + qpid/client/SubscriptionManagerImpl.h \ + qpid/messaging/Address.cpp \ + qpid/messaging/Connection.cpp \ + qpid/messaging/Filter.cpp \ + qpid/messaging/Message.cpp \ + qpid/messaging/MessageImpl.h \ + qpid/messaging/MessageImpl.cpp \ + qpid/messaging/Sender.cpp \ + qpid/messaging/Receiver.cpp \ + qpid/messaging/Session.cpp \ + qpid/messaging/Variant.cpp \ + qpid/messaging/ConnectionImpl.h \ + qpid/messaging/SenderImpl.h \ + qpid/messaging/ReceiverImpl.h \ + qpid/messaging/SessionImpl.h \ + qpid/client/amqp0_10/AddressResolution.h \ + qpid/client/amqp0_10/AddressResolution.cpp \ + qpid/client/amqp0_10/Codecs.cpp \ + qpid/client/amqp0_10/CodecsInternal.h \ + qpid/client/amqp0_10/ConnectionImpl.h \ + qpid/client/amqp0_10/ConnectionImpl.cpp \ + qpid/client/amqp0_10/CompletionTracker.h \ + qpid/client/amqp0_10/CompletionTracker.cpp \ + qpid/client/amqp0_10/IncomingMessages.h \ + qpid/client/amqp0_10/IncomingMessages.cpp \ + qpid/client/amqp0_10/MessageSink.h \ + qpid/client/amqp0_10/MessageSource.h \ + qpid/client/amqp0_10/OutgoingMessage.h \ + qpid/client/amqp0_10/OutgoingMessage.cpp \ + qpid/client/amqp0_10/ReceiverImpl.h \ + qpid/client/amqp0_10/ReceiverImpl.cpp \ + qpid/client/amqp0_10/SessionImpl.h \ + qpid/client/amqp0_10/SessionImpl.cpp \ + qpid/client/amqp0_10/SenderImpl.h \ + qpid/client/amqp0_10/SenderImpl.cpp # NOTE: only public header files (which should be in ../include) # should go in this list. Private headers should go in the SOURCES @@ -723,6 +759,7 @@ nobase_include_HEADERS += \ ../include/qpid/framing/Buffer.h \ ../include/qpid/framing/FieldTable.h \ ../include/qpid/framing/FieldValue.h \ + ../include/qpid/framing/List.h \ ../include/qpid/framing/ProtocolVersion.h \ ../include/qpid/framing/SequenceNumber.h \ ../include/qpid/framing/SequenceSet.h \ @@ -749,7 +786,18 @@ nobase_include_HEADERS += \ ../include/qpid/sys/SystemInfo.h \ ../include/qpid/sys/Thread.h \ ../include/qpid/sys/Time.h \ - ../include/qpid/sys/uuid.h + ../include/qpid/messaging/Address.h \ + ../include/qpid/messaging/Connection.h \ + ../include/qpid/messaging/Codec.h \ + ../include/qpid/messaging/Filter.h \ + ../include/qpid/messaging/Message.h \ + ../include/qpid/messaging/MessageContent.h \ + ../include/qpid/messaging/MessageListener.h \ + ../include/qpid/messaging/Sender.h \ + ../include/qpid/messaging/Receiver.h \ + ../include/qpid/messaging/Session.h \ + ../include/qpid/messaging/Variant.h \ + ../include/qpid/client/amqp0_10/Codecs.h # Force build of qpidd during dist phase so help2man will work. dist-hook: $(BUILT_SOURCES) diff --git a/qpid/cpp/src/qmf.mk b/qpid/cpp/src/qmf.mk index 62393cdcfb..54110ebaf7 100644 --- a/qpid/cpp/src/qmf.mk +++ b/qpid/cpp/src/qmf.mk @@ -18,7 +18,7 @@ # # -# qmf agent library makefile fragment, to be included in Makefile.am +# qmf library makefile fragment, to be included in Makefile.am # lib_LTLIBRARIES += \ libqmfcommon.la \ @@ -27,13 +27,18 @@ lib_LTLIBRARIES += \ # Public header files nobase_include_HEADERS += \ ../include/qpid/agent/ManagementAgent.h \ - ../include/qpid/agent/QmfAgentImportExport.h - + ../include/qpid/agent/QmfAgentImportExport.h \ + ../include/qmf/Agent.h \ + ../include/qmf/Connection.h \ + ../include/qmf/QmfImportExport.h \ + ../include/qmf/ConnectionSettings.h \ + ../include/qmf/AgentObject.h libqmfcommon_la_SOURCES = \ - qmf/Agent.cpp \ - qmf/Agent.h \ - qmf/Console.h \ + qmf/ConnectionSettingsImpl.cpp \ + qmf/ConnectionSettingsImpl.h \ + qmf/ConsoleEngine.cpp \ + qmf/ConsoleEngine.h \ qmf/Event.h \ qmf/Message.h \ qmf/MessageImpl.cpp \ @@ -44,11 +49,15 @@ libqmfcommon_la_SOURCES = \ qmf/ObjectIdImpl.h \ qmf/ObjectImpl.cpp \ qmf/ObjectImpl.h \ + qmf/Protocol.cpp \ + qmf/Protocol.h \ qmf/Query.h \ qmf/QueryImpl.cpp \ qmf/QueryImpl.h \ qmf/ResilientConnection.cpp \ qmf/ResilientConnection.h \ + qmf/SequenceManager.cpp \ + qmf/SequenceManager.h \ qmf/Schema.h \ qmf/SchemaImpl.cpp \ qmf/SchemaImpl.h \ @@ -58,9 +67,9 @@ libqmfcommon_la_SOURCES = \ qmf/ValueImpl.h libqmfagent_la_SOURCES = \ - ../include/qpid/agent/ManagementAgent.h \ + qmf/AgentEngine.cpp \ + qmf/AgentEngine.h \ qpid/agent/ManagementAgentImpl.cpp \ - qpid/agent/ManagementAgentImpl.h \ - qmf/Agent.cpp + qpid/agent/ManagementAgentImpl.h -libqmfagent_la_LIBADD = libqpidclient.la +libqmfagent_la_LIBADD = libqpidclient.la libqmfcommon.la diff --git a/qpid/cpp/src/qmf/Agent.cpp b/qpid/cpp/src/qmf/Agent.cpp deleted file mode 100644 index 6d59ae2750..0000000000 --- a/qpid/cpp/src/qmf/Agent.cpp +++ /dev/null @@ -1,958 +0,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. - */ - -#include "qmf/Agent.h" -#include "qmf/MessageImpl.h" -#include "qmf/SchemaImpl.h" -#include "qmf/Typecode.h" -#include "qmf/ObjectImpl.h" -#include "qmf/ObjectIdImpl.h" -#include "qmf/QueryImpl.h" -#include "qmf/ValueImpl.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace qmf; -using namespace qpid::framing; -using namespace qpid::sys; - -namespace qmf { - - struct AgentEventImpl { - typedef boost::shared_ptr Ptr; - AgentEvent::EventKind kind; - uint32_t sequence; - string authUserId; - string authToken; - string name; - Object* object; - boost::shared_ptr objectId; - Query query; - boost::shared_ptr arguments; - string exchange; - string bindingKey; - SchemaObjectClass* objectClass; - - AgentEventImpl(AgentEvent::EventKind k) : - kind(k), sequence(0), object(0), objectClass(0) {} - ~AgentEventImpl() {} - AgentEvent copy(); - }; - - struct AgentQueryContext { - typedef boost::shared_ptr Ptr; - uint32_t sequence; - string exchange; - string key; - SchemaMethodImpl* schemaMethod; - AgentQueryContext() : schemaMethod(0) {} - }; - - class AgentImpl { - public: - AgentImpl(char* label, bool internalStore); - ~AgentImpl(); - - void setStoreDir(char* path); - void setTransferDir(char* path); - void handleRcvMessage(Message& message); - bool getXmtMessage(Message& item); - void popXmt(); - bool getEvent(AgentEvent& event); - void popEvent(); - void newSession(); - void startProtocol(); - void heartbeat(); - void methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments); - void queryResponse(uint32_t sequence, Object& object, bool prop, bool stat); - void queryComplete(uint32_t sequence); - void registerClass(SchemaObjectClass* cls); - void registerClass(SchemaEventClass* cls); - const ObjectId* addObject(Object& obj, uint64_t persistId); - const ObjectId* allocObjectId(uint64_t persistId); - const ObjectId* allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi); - void raiseEvent(Event& event); - - private: - Mutex lock; - Mutex addLock; - string label; - string queueName; - string storeDir; - string transferDir; - bool internalStore; - uint64_t nextTransientId; - Uuid systemId; - uint32_t requestedBrokerBank; - uint32_t requestedAgentBank; - uint32_t assignedBrokerBank; - uint32_t assignedAgentBank; - AgentAttachment attachment; - uint16_t bootSequence; - uint64_t nextObjectId; - uint32_t nextContextNum; - deque eventQueue; - deque xmtQueue; - map contextMap; - - static const char* QMF_EXCHANGE; - static const char* DIR_EXCHANGE; - static const char* BROKER_KEY; - static const uint32_t MERR_UNKNOWN_METHOD = 2; - static const uint32_t MERR_UNKNOWN_PACKAGE = 8; - static const uint32_t MERR_UNKNOWN_CLASS = 9; - static const uint32_t MERR_INTERNAL_ERROR = 10; -# define MA_BUFFER_SIZE 65536 - char outputBuffer[MA_BUFFER_SIZE]; - - struct SchemaClassKey { - string name; - uint8_t hash[16]; - SchemaClassKey(const string& n, const uint8_t* h) : name(n) { - memcpy(hash, h, 16); - } - SchemaClassKey(Buffer& buffer) { - buffer.getShortString(name); - buffer.getBin128(hash); - } - string repr() { - return name; - } - }; - - struct SchemaClassKeyComp { - bool operator() (const SchemaClassKey& lhs, const SchemaClassKey& rhs) const - { - if (lhs.name != rhs.name) - return lhs.name < rhs.name; - else - for (int i = 0; i < 16; i++) - if (lhs.hash[i] != rhs.hash[i]) - return lhs.hash[i] < rhs.hash[i]; - return false; - } - }; - - typedef map ObjectClassMap; - typedef map EventClassMap; - - struct ClassMaps { - ObjectClassMap objectClasses; - EventClassMap eventClasses; - }; - - map packages; - - bool checkHeader(Buffer& buf, uint8_t *opcode, uint32_t *seq); - void encodeHeader(Buffer& buf, uint8_t opcode, uint32_t seq = 0); - AgentEventImpl::Ptr eventDeclareQueue(const string& queueName); - AgentEventImpl::Ptr eventBind(const string& exchange, const string& queue, const string& key); - AgentEventImpl::Ptr eventSetupComplete(); - AgentEventImpl::Ptr eventQuery(uint32_t num, const string& userId, const string& package, const string& cls, - boost::shared_ptr oid); - AgentEventImpl::Ptr eventMethod(uint32_t num, const string& userId, const string& method, - boost::shared_ptr oid, boost::shared_ptr argMap, - SchemaObjectClass* objectClass); - void sendBufferLH(Buffer& buf, const string& destination, const string& routingKey); - - void sendPackageIndicationLH(const string& packageName); - void sendClassIndicationLH(ClassKind kind, const string& packageName, const SchemaClassKey& key); - void sendCommandCompleteLH(const string& exchange, const string& key, uint32_t seq, - uint32_t code = 0, const string& text = "OK"); - void sendMethodErrorLH(uint32_t sequence, const string& key, uint32_t code, const string& text=""); - void handleAttachResponse(Buffer& inBuffer); - void handlePackageRequest(Buffer& inBuffer); - void handleClassQuery(Buffer& inBuffer); - void handleSchemaRequest(Buffer& inBuffer, uint32_t sequence, - const string& replyToExchange, const string& replyToKey); - void handleGetQuery(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId); - void handleMethodRequest(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId); - void handleConsoleAddedIndication(); - }; -} - -const char* AgentImpl::QMF_EXCHANGE = "qpid.management"; -const char* AgentImpl::DIR_EXCHANGE = "amq.direct"; -const char* AgentImpl::BROKER_KEY = "broker"; - -#define STRING_REF(s) {if (!s.empty()) item.s = const_cast(s.c_str());} - -AgentEvent AgentEventImpl::copy() -{ - AgentEvent item; - - ::memset(&item, 0, sizeof(AgentEvent)); - item.kind = kind; - item.sequence = sequence; - item.object = object; - item.objectId = objectId.get(); - item.query = &query; - item.arguments = arguments.get(); - item.objectClass = objectClass; - - STRING_REF(authUserId); - STRING_REF(authToken); - STRING_REF(name); - STRING_REF(exchange); - STRING_REF(bindingKey); - - return item; -} - -AgentImpl::AgentImpl(char* _label, bool i) : - label(_label), queueName("qmfa-"), internalStore(i), nextTransientId(1), - requestedBrokerBank(0), requestedAgentBank(0), - assignedBrokerBank(0), assignedAgentBank(0), - bootSequence(1), nextObjectId(1), nextContextNum(1) -{ - queueName += label; -} - -AgentImpl::~AgentImpl() -{ -} - -void AgentImpl::setStoreDir(char* path) -{ - Mutex::ScopedLock _lock(lock); - if (path) - storeDir = path; - else - storeDir.clear(); -} - -void AgentImpl::setTransferDir(char* path) -{ - Mutex::ScopedLock _lock(lock); - if (path) - transferDir = path; - else - transferDir.clear(); -} - -void AgentImpl::handleRcvMessage(Message& message) -{ - Buffer inBuffer(message.body, message.length); - uint8_t opcode; - uint32_t sequence; - string replyToExchange(message.replyExchange ? message.replyExchange : ""); - string replyToKey(message.replyKey ? message.replyKey : ""); - string userId(message.userId ? message.userId : ""); - - if (checkHeader(inBuffer, &opcode, &sequence)) { - if (opcode == 'a') handleAttachResponse(inBuffer); - else if (opcode == 'S') handleSchemaRequest(inBuffer, sequence, replyToExchange, replyToKey); - else if (opcode == 'x') handleConsoleAddedIndication(); - else if (opcode == 'G') handleGetQuery(inBuffer, sequence, replyToKey, userId); - else if (opcode == 'M') handleMethodRequest(inBuffer, sequence, replyToKey, userId); - } -} - -bool AgentImpl::getXmtMessage(Message& item) -{ - Mutex::ScopedLock _lock(lock); - if (xmtQueue.empty()) - return false; - item = xmtQueue.front()->copy(); - return true; -} - -void AgentImpl::popXmt() -{ - Mutex::ScopedLock _lock(lock); - if (!xmtQueue.empty()) - xmtQueue.pop_front(); -} - -bool AgentImpl::getEvent(AgentEvent& event) -{ - Mutex::ScopedLock _lock(lock); - if (eventQueue.empty()) - return false; - event = eventQueue.front()->copy(); - return true; -} - -void AgentImpl::popEvent() -{ - Mutex::ScopedLock _lock(lock); - if (!eventQueue.empty()) - eventQueue.pop_front(); -} - -void AgentImpl::newSession() -{ - Mutex::ScopedLock _lock(lock); - eventQueue.clear(); - xmtQueue.clear(); - eventQueue.push_back(eventDeclareQueue(queueName)); - eventQueue.push_back(eventBind("amq.direct", queueName, queueName)); - eventQueue.push_back(eventSetupComplete()); -} - -void AgentImpl::startProtocol() -{ - Mutex::ScopedLock _lock(lock); - char rawbuffer[512]; - Buffer buffer(rawbuffer, 512); - - encodeHeader(buffer, 'A'); - buffer.putShortString("qmfa"); - systemId.encode(buffer); - buffer.putLong(requestedBrokerBank); - buffer.putLong(requestedAgentBank); - sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); - QPID_LOG(trace, "SENT AttachRequest: reqBroker=" << requestedBrokerBank << - " reqAgent=" << requestedAgentBank); -} - -void AgentImpl::heartbeat() -{ - Mutex::ScopedLock _lock(lock); - Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - - encodeHeader(buffer, 'h'); - buffer.putLongLong(uint64_t(Duration(now()))); - stringstream key; - key << "console.heartbeat." << assignedBrokerBank << "." << assignedAgentBank; - sendBufferLH(buffer, QMF_EXCHANGE, key.str()); - QPID_LOG(trace, "SENT HeartbeatIndication"); -} - -void AgentImpl::methodResponse(uint32_t sequence, uint32_t status, char* text, - const Value& argMap) -{ - Mutex::ScopedLock _lock(lock); - map::iterator iter = contextMap.find(sequence); - if (iter == contextMap.end()) - return; - AgentQueryContext::Ptr context = iter->second; - contextMap.erase(iter); - - Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - encodeHeader(buffer, 'm', context->sequence); - buffer.putLong(status); - buffer.putMediumString(text); - if (status == 0) { - for (vector::const_iterator aIter = context->schemaMethod->arguments.begin(); - aIter != context->schemaMethod->arguments.end(); aIter++) { - const SchemaArgumentImpl* schemaArg = *aIter; - if (schemaArg->dir == DIR_OUT || schemaArg->dir == DIR_IN_OUT) { - if (argMap.keyInMap(schemaArg->name.c_str())) { - const Value* val = argMap.byKey(schemaArg->name.c_str()); - val->impl->encode(buffer); - } else { - Value val(schemaArg->typecode); - val.impl->encode(buffer); - } - } - } - } - sendBufferLH(buffer, context->exchange, context->key); - QPID_LOG(trace, "SENT MethodResponse"); -} - -void AgentImpl::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat) -{ - Mutex::ScopedLock _lock(lock); - map::iterator iter = contextMap.find(sequence); - if (iter == contextMap.end()) - return; - AgentQueryContext::Ptr context = iter->second; - - Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - encodeHeader(buffer, 'g', context->sequence); - - object.impl->encodeSchemaKey(buffer); - object.impl->encodeManagedObjectData(buffer); - if (prop) - object.impl->encodeProperties(buffer); - if (stat) - object.impl->encodeStatistics(buffer); - - sendBufferLH(buffer, context->exchange, context->key); - QPID_LOG(trace, "SENT ContentIndication"); -} - -void AgentImpl::queryComplete(uint32_t sequence) -{ - Mutex::ScopedLock _lock(lock); - map::iterator iter = contextMap.find(sequence); - if (iter == contextMap.end()) - return; - - AgentQueryContext::Ptr context = iter->second; - contextMap.erase(iter); - sendCommandCompleteLH(context->exchange, context->key, context->sequence, 0, "OK"); -} - -void AgentImpl::registerClass(SchemaObjectClass* cls) -{ - Mutex::ScopedLock _lock(lock); - SchemaObjectClassImpl* impl = cls->impl; - - map::iterator iter = packages.find(impl->package); - if (iter == packages.end()) { - packages[impl->package] = ClassMaps(); - iter = packages.find(impl->package); - // TODO: Indicate this package if connected - } - - SchemaClassKey key(impl->name, impl->getHash()); - iter->second.objectClasses[key] = impl; - - // TODO: Indicate this schema if connected. -} - -void AgentImpl::registerClass(SchemaEventClass* cls) -{ - Mutex::ScopedLock _lock(lock); - SchemaEventClassImpl* impl = cls->impl; - - map::iterator iter = packages.find(impl->package); - if (iter == packages.end()) { - packages[impl->package] = ClassMaps(); - iter = packages.find(impl->package); - // TODO: Indicate this package if connected - } - - SchemaClassKey key(impl->name, impl->getHash()); - iter->second.eventClasses[key] = impl; - - // TODO: Indicate this schema if connected. -} - -const ObjectId* AgentImpl::addObject(Object&, uint64_t) -{ - Mutex::ScopedLock _lock(lock); - return 0; -} - -const ObjectId* AgentImpl::allocObjectId(uint64_t persistId) -{ - Mutex::ScopedLock _lock(lock); - uint16_t sequence = persistId ? 0 : bootSequence; - uint64_t objectNum = persistId ? persistId : nextObjectId++; - - ObjectIdImpl* oid = new ObjectIdImpl(&attachment, 0, sequence, objectNum); - return oid->envelope; -} - -const ObjectId* AgentImpl::allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi) -{ - return allocObjectId(((uint64_t) persistIdHi) << 32 | (uint64_t) persistIdLo); -} - -void AgentImpl::raiseEvent(Event&) -{ - Mutex::ScopedLock _lock(lock); -} - -void AgentImpl::encodeHeader(Buffer& buf, uint8_t opcode, uint32_t seq) -{ - buf.putOctet('A'); - buf.putOctet('M'); - buf.putOctet('3'); - buf.putOctet(opcode); - buf.putLong (seq); -} - -bool AgentImpl::checkHeader(Buffer& buf, uint8_t *opcode, uint32_t *seq) -{ - if (buf.getSize() < 8) - return false; - - uint8_t h1 = buf.getOctet(); - uint8_t h2 = buf.getOctet(); - uint8_t h3 = buf.getOctet(); - - *opcode = buf.getOctet(); - *seq = buf.getLong(); - - return h1 == 'A' && h2 == 'M' && h3 == '3'; -} - -AgentEventImpl::Ptr AgentImpl::eventDeclareQueue(const string& name) -{ - AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::DECLARE_QUEUE)); - event->name = name; - - return event; -} - -AgentEventImpl::Ptr AgentImpl::eventBind(const string& exchange, const string& queue, - const string& key) -{ - AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::BIND)); - event->name = queue; - event->exchange = exchange; - event->bindingKey = key; - - return event; -} - -AgentEventImpl::Ptr AgentImpl::eventSetupComplete() -{ - AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::SETUP_COMPLETE)); - return event; -} - -AgentEventImpl::Ptr AgentImpl::eventQuery(uint32_t num, const string& userId, const string& package, - const string& cls, boost::shared_ptr oid) -{ - AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::GET_QUERY)); - event->sequence = num; - event->authUserId = userId; - event->query.impl->packageName = package; - event->query.impl->className = cls; - event->query.impl->oid = oid; - return event; -} - -AgentEventImpl::Ptr AgentImpl::eventMethod(uint32_t num, const string& userId, const string& method, - boost::shared_ptr oid, boost::shared_ptr argMap, - SchemaObjectClass* objectClass) -{ - AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::METHOD_CALL)); - event->sequence = num; - event->authUserId = userId; - event->name = method; - event->objectId = oid; - event->arguments = argMap; - event->objectClass = objectClass; - return event; -} - -void AgentImpl::sendBufferLH(Buffer& buf, const string& destination, const string& routingKey) -{ - uint32_t length = buf.getPosition(); - MessageImpl::Ptr message(new MessageImpl); - - buf.reset(); - buf.getRawData(message->body, length); - message->destination = destination; - message->routingKey = routingKey; - message->replyExchange = "amq.direct"; - message->replyKey = queueName; - - xmtQueue.push_back(message); -} - -void AgentImpl::sendPackageIndicationLH(const string& packageName) -{ - Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - encodeHeader(buffer, 'p'); - buffer.putShortString(packageName); - sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); - QPID_LOG(trace, "SENT PackageIndication: package_name=" << packageName); -} - -void AgentImpl::sendClassIndicationLH(ClassKind kind, const string& packageName, const SchemaClassKey& key) -{ - Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - encodeHeader(buffer, 'q'); - buffer.putOctet((int) kind); - buffer.putShortString(packageName); - buffer.putShortString(key.name); - buffer.putBin128(const_cast(key.hash)); // const_cast needed for older Qpid libraries - sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); - QPID_LOG(trace, "SENT ClassIndication: package_name=" << packageName << " class_name=" << key.name); -} - -void AgentImpl::sendCommandCompleteLH(const string& exchange, const string& replyToKey, - uint32_t sequence, uint32_t code, const string& text) -{ - Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - encodeHeader(buffer, 'z', sequence); - buffer.putLong(code); - buffer.putShortString(text); - sendBufferLH(buffer, exchange, replyToKey); - QPID_LOG(trace, "SENT CommandComplete: seq=" << sequence << " code=" << code << " text=" << text); -} - -void AgentImpl::sendMethodErrorLH(uint32_t sequence, const string& key, uint32_t code, const string& text) -{ - Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - encodeHeader(buffer, 'm', sequence); - buffer.putLong(code); - - string fulltext; - switch (code) { - case MERR_UNKNOWN_PACKAGE: fulltext = "Unknown Package"; break; - case MERR_UNKNOWN_CLASS: fulltext = "Unknown Class"; break; - case MERR_UNKNOWN_METHOD: fulltext = "Unknown Method"; break; - case MERR_INTERNAL_ERROR: fulltext = "Internal Error"; break; - default: fulltext = "Unspecified Error"; break; - } - - if (!text.empty()) { - fulltext += " ("; - fulltext += text; - fulltext += ")"; - } - - buffer.putMediumString(fulltext); - sendBufferLH(buffer, DIR_EXCHANGE, key); - QPID_LOG(trace, "SENT MethodResponse: errorCode=" << code << " text=" << fulltext); -} - -void AgentImpl::handleAttachResponse(Buffer& inBuffer) -{ - Mutex::ScopedLock _lock(lock); - - assignedBrokerBank = inBuffer.getLong(); - assignedAgentBank = inBuffer.getLong(); - - QPID_LOG(trace, "RCVD AttachResponse: broker=" << assignedBrokerBank << " agent=" << assignedAgentBank); - - if ((assignedBrokerBank != requestedBrokerBank) || - (assignedAgentBank != requestedAgentBank)) { - if (requestedAgentBank == 0) { - QPID_LOG(notice, "Initial object-id bank assigned: " << assignedBrokerBank << "." << - assignedAgentBank); - } else { - QPID_LOG(warning, "Collision in object-id! New bank assigned: " << assignedBrokerBank << - "." << assignedAgentBank); - } - //storeData(); // TODO - requestedBrokerBank = assignedBrokerBank; - requestedAgentBank = assignedAgentBank; - } - - attachment.setBanks(assignedBrokerBank, assignedAgentBank); - - // Bind to qpid.management to receive commands - stringstream key; - key << "agent." << assignedBrokerBank << "." << assignedAgentBank; - eventQueue.push_back(eventBind(QMF_EXCHANGE, queueName, key.str())); - - // Send package indications for all local packages - for (map::iterator pIter = packages.begin(); - pIter != packages.end(); - pIter++) { - sendPackageIndicationLH(pIter->first); - - // Send class indications for all local classes - ClassMaps cMap = pIter->second; - for (ObjectClassMap::iterator cIter = cMap.objectClasses.begin(); - cIter != cMap.objectClasses.end(); cIter++) - sendClassIndicationLH(CLASS_OBJECT, pIter->first, cIter->first); - for (EventClassMap::iterator cIter = cMap.eventClasses.begin(); - cIter != cMap.eventClasses.end(); cIter++) - sendClassIndicationLH(CLASS_EVENT, pIter->first, cIter->first); - } -} - -void AgentImpl::handlePackageRequest(Buffer&) -{ - Mutex::ScopedLock _lock(lock); -} - -void AgentImpl::handleClassQuery(Buffer&) -{ - Mutex::ScopedLock _lock(lock); -} - -void AgentImpl::handleSchemaRequest(Buffer& inBuffer, uint32_t sequence, - const string& replyExchange, const string& replyKey) -{ - Mutex::ScopedLock _lock(lock); - string rExchange(replyExchange); - string rKey(replyKey); - string packageName; - inBuffer.getShortString(packageName); - SchemaClassKey key(inBuffer); - - if (rExchange.empty()) - rExchange = QMF_EXCHANGE; - if (rKey.empty()) - rKey = BROKER_KEY; - - QPID_LOG(trace, "RCVD SchemaRequest: package=" << packageName << " class=" << key.name); - - map::iterator pIter = packages.find(packageName); - if (pIter == packages.end()) { - sendCommandCompleteLH(rExchange, rKey, sequence, 1, "package not found"); - return; - } - - ClassMaps cMap = pIter->second; - ObjectClassMap::iterator ocIter = cMap.objectClasses.find(key); - if (ocIter != cMap.objectClasses.end()) { - SchemaObjectClassImpl* oImpl = ocIter->second; - Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - encodeHeader(buffer, 's', sequence); - oImpl->encode(buffer); - sendBufferLH(buffer, rExchange, rKey); - QPID_LOG(trace, "SENT SchemaResponse: (object) package=" << packageName << " class=" << key.name); - return; - } - - EventClassMap::iterator ecIter = cMap.eventClasses.find(key); - if (ecIter != cMap.eventClasses.end()) { - SchemaEventClassImpl* eImpl = ecIter->second; - Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - encodeHeader(buffer, 's', sequence); - eImpl->encode(buffer); - sendBufferLH(buffer, rExchange, rKey); - QPID_LOG(trace, "SENT SchemaResponse: (event) package=" << packageName << " class=" << key.name); - return; - } - - sendCommandCompleteLH(rExchange, rKey, sequence, 1, "class not found"); -} - -void AgentImpl::handleGetQuery(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId) -{ - Mutex::ScopedLock _lock(lock); - FieldTable ft; - FieldTable::ValuePtr value; - map::const_iterator pIter = packages.end(); - string pname; - string cname; - string oidRepr; - boost::shared_ptr oid; - - ft.decode(inBuffer); - - QPID_LOG(trace, "RCVD GetQuery: map=" << ft); - - value = ft.get("_package"); - if (value.get() && value->convertsTo()) { - pname = value->get(); - pIter = packages.find(pname); - if (pIter == packages.end()) { - sendCommandCompleteLH(DIR_EXCHANGE, replyTo, sequence); - return; - } - } - - value = ft.get("_class"); - if (value.get() && value->convertsTo()) { - cname = value->get(); - // TODO - check for validity of class (in package or any package) - if (pIter == packages.end()) { - } else { - - } - } - - value = ft.get("_objectid"); - if (value.get() && value->convertsTo()) { - oidRepr = value->get(); - oid.reset(new ObjectId()); - oid->impl->fromString(oidRepr); - } - - AgentQueryContext::Ptr context(new AgentQueryContext); - uint32_t contextNum = nextContextNum++; - context->sequence = sequence; - context->exchange = DIR_EXCHANGE; - context->key = replyTo; - contextMap[contextNum] = context; - - eventQueue.push_back(eventQuery(contextNum, userId, pname, cname, oid)); -} - -void AgentImpl::handleMethodRequest(Buffer& buffer, uint32_t sequence, const string& replyTo, const string& userId) -{ - Mutex::ScopedLock _lock(lock); - string pname; - string method; - ObjectIdImpl* oidImpl = new ObjectIdImpl(buffer); - boost::shared_ptr oid(oidImpl->envelope); - buffer.getShortString(pname); - SchemaClassKey classKey(buffer); - buffer.getShortString(method); - - map::const_iterator pIter = packages.find(pname); - if (pIter == packages.end()) { - sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_PACKAGE, pname); - return; - } - - ObjectClassMap::const_iterator cIter = pIter->second.objectClasses.find(classKey); - if (cIter == pIter->second.objectClasses.end()) { - sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_CLASS, classKey.repr()); - return; - } - - const SchemaObjectClassImpl* schema = cIter->second; - vector::const_iterator mIter = schema->methods.begin(); - for (; mIter != schema->methods.end(); mIter++) { - if ((*mIter)->name == method) - break; - } - - if (mIter == schema->methods.end()) { - sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_METHOD, method); - return; - } - - SchemaMethodImpl* schemaMethod = *mIter; - boost::shared_ptr argMap(new Value(TYPE_MAP)); - ValueImpl* value; - for (vector::const_iterator aIter = schemaMethod->arguments.begin(); - aIter != schemaMethod->arguments.end(); aIter++) { - const SchemaArgumentImpl* schemaArg = *aIter; - if (schemaArg->dir == DIR_IN || schemaArg->dir == DIR_IN_OUT) - value = new ValueImpl(schemaArg->typecode, buffer); - else - value = new ValueImpl(schemaArg->typecode); - argMap->insert(schemaArg->name.c_str(), value->envelope); - } - - AgentQueryContext::Ptr context(new AgentQueryContext); - uint32_t contextNum = nextContextNum++; - context->sequence = sequence; - context->exchange = DIR_EXCHANGE; - context->key = replyTo; - context->schemaMethod = schemaMethod; - contextMap[contextNum] = context; - - eventQueue.push_back(eventMethod(contextNum, userId, method, oid, argMap, schema->envelope)); -} - -void AgentImpl::handleConsoleAddedIndication() -{ - Mutex::ScopedLock _lock(lock); -} - -//================================================================== -// Wrappers -//================================================================== - -Agent::Agent(char* label, bool internalStore) -{ - impl = new AgentImpl(label, internalStore); -} - -Agent::~Agent() -{ - delete impl; -} - -void Agent::setStoreDir(char* path) -{ - impl->setStoreDir(path); -} - -void Agent::setTransferDir(char* path) -{ - impl->setTransferDir(path); -} - -void Agent::handleRcvMessage(Message& message) -{ - impl->handleRcvMessage(message); -} - -bool Agent::getXmtMessage(Message& item) -{ - return impl->getXmtMessage(item); -} - -void Agent::popXmt() -{ - impl->popXmt(); -} - -bool Agent::getEvent(AgentEvent& event) -{ - return impl->getEvent(event); -} - -void Agent::popEvent() -{ - impl->popEvent(); -} - -void Agent::newSession() -{ - impl->newSession(); -} - -void Agent::startProtocol() -{ - impl->startProtocol(); -} - -void Agent::heartbeat() -{ - impl->heartbeat(); -} - -void Agent::methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments) -{ - impl->methodResponse(sequence, status, text, arguments); -} - -void Agent::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat) -{ - impl->queryResponse(sequence, object, prop, stat); -} - -void Agent::queryComplete(uint32_t sequence) -{ - impl->queryComplete(sequence); -} - -void Agent::registerClass(SchemaObjectClass* cls) -{ - impl->registerClass(cls); -} - -void Agent::registerClass(SchemaEventClass* cls) -{ - impl->registerClass(cls); -} - -const ObjectId* Agent::addObject(Object& obj, uint64_t persistId) -{ - return impl->addObject(obj, persistId); -} - -const ObjectId* Agent::allocObjectId(uint64_t persistId) -{ - return impl->allocObjectId(persistId); -} - -const ObjectId* Agent::allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi) -{ - return impl->allocObjectId(persistIdLo, persistIdHi); -} - -void Agent::raiseEvent(Event& event) -{ - impl->raiseEvent(event); -} - diff --git a/qpid/cpp/src/qmf/Agent.h b/qpid/cpp/src/qmf/Agent.h deleted file mode 100644 index d8f784e9d8..0000000000 --- a/qpid/cpp/src/qmf/Agent.h +++ /dev/null @@ -1,206 +0,0 @@ -#ifndef _QmfAgent_ -#define _QmfAgent_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace qmf { - - /** - * AgentEvent - * - * This structure represents a QMF event coming from the agent to - * the application. - */ - struct AgentEvent { - enum EventKind { - GET_QUERY = 1, - START_SYNC = 2, - END_SYNC = 3, - METHOD_CALL = 4, - DECLARE_QUEUE = 5, - DELETE_QUEUE = 6, - BIND = 7, - UNBIND = 8, - SETUP_COMPLETE = 9 - }; - - EventKind kind; - uint32_t sequence; // Protocol sequence (for all kinds) - char* authUserId; // Authenticated user ID (for all kinds) - char* authToken; // Authentication token if issued (for all kinds) - char* name; // Name of the method/sync query - // (METHOD_CALL, START_SYNC, END_SYNC, DECLARE_QUEUE, BIND, UNBIND) - Object* object; // Object involved in method call (METHOD_CALL) - ObjectId* objectId; // ObjectId for method call (METHOD_CALL) - Query* query; // Query parameters (GET_QUERY, START_SYNC) - Value* arguments; // Method parameters (METHOD_CALL) - char* exchange; // Exchange for bind (BIND, UNBIND) - char* bindingKey; // Key for bind (BIND, UNBIND) - SchemaObjectClass* objectClass; // (METHOD_CALL) - }; - - class AgentImpl; - - /** - * Agent - Protocol engine for the QMF agent - */ - class Agent { - public: - Agent(char* label, bool internalStore=true); - ~Agent(); - - /** - * Configure the directory path for storing persistent data. - *@param path Null-terminated string containing a directory path where files can be - * created, written, and read. If NULL, no persistent storage will be - * attempted. - */ - void setStoreDir(char* path); - - /** - * Configure the directory path for files transferred over QMF. - *@param path Null-terminated string containing a directory path where files can be - * created, deleted, written, and read. If NULL, file transfers shall not - * be permitted. - */ - void setTransferDir(char* path); - - /** - * Pass messages received from the AMQP session to the Agent engine. - *@param message AMQP messages received on the agent session. - */ - void handleRcvMessage(Message& message); - - /** - * Get the next message to be sent to the AMQP network. - *@param item The Message structure describing the message to be produced. - *@return true if the Message is valid, false if there are no messages to send. - */ - bool getXmtMessage(Message& item); - - /** - * Remove and discard one message from the head of the transmit queue. - */ - void popXmt(); - - /** - * Get the next application event from the agent engine. - *@param event The event iff the return value is true - *@return true if event is valid, false if there are no events to process - */ - bool getEvent(AgentEvent& event); - - /** - * Remove and discard one event from the head of the event queue. - */ - void popEvent(); - - /** - * A new AMQP session has been established for Agent communication. - */ - void newSession(); - - /** - * Start the QMF Agent protocol. This should be invoked after a SETUP_COMPLETE event - * is received from the Agent engine. - */ - void startProtocol(); - - /** - * This method is called periodically so the agent can supply a heartbeat. - */ - void heartbeat(); - - /** - * Respond to a method request. - *@param sequence The sequence number from the method request event. - *@param status The method's completion status. - *@param text Status text ("OK" or an error message) - *@param arguments The list of output arguments from the method call. - */ - void methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments); - - /** - * Send a content indication to the QMF bus. This is only needed for objects that are - * managed by the application. This is *NOT* needed for objects managed by the Agent - * (inserted using addObject). - *@param sequence The sequence number of the GET request or the SYNC_START request. - *@param object The object (annotated with "changed" flags) for publication. - *@param prop If true, changed object properties are transmitted. - *@param stat If true, changed object statistics are transmitted. - */ - void queryResponse(uint32_t sequence, Object& object, bool prop = true, bool stat = true); - - /** - * Indicate the completion of a query. This is not used for SYNC_START requests. - *@param sequence The sequence number of the GET request. - */ - void queryComplete(uint32_t sequence); - - /** - * Register a schema class with the Agent. - *@param cls A SchemaObejctClass object that defines data managed by the agent. - */ - void registerClass(SchemaObjectClass* cls); - - /** - * Register a schema class with the Agent. - *@param cls A SchemaEventClass object that defines events sent by the agent. - */ - void registerClass(SchemaEventClass* cls); - - /** - * Give an object to the Agent for storage and management. Once added, the agent takes - * responsibility for the life cycle of the object. - *@param obj The object to be managed by the Agent. - *@param persistId A unique non-zero value if the object-id is to be persistent. - *@return The objectId of the managed object. - */ - const ObjectId* addObject(Object& obj, uint64_t persistId); - - /** - * Allocate an objecc-id for an object that will be managed by the application. - *@param persistId A unique non-zero value if the object-id is to be persistent. - @return The objectId structure for the allocated ID. - */ - const ObjectId* allocObjectId(uint64_t persistId); - const ObjectId* allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi); - - /** - * Raise an event into the QMF network.. - *@param event The event object for the event to be raised. - */ - void raiseEvent(Event& event); - - private: - AgentImpl* impl; - }; -} - -#endif - diff --git a/qpid/cpp/src/qmf/AgentEngine.cpp b/qpid/cpp/src/qmf/AgentEngine.cpp new file mode 100644 index 0000000000..d3204042d5 --- /dev/null +++ b/qpid/cpp/src/qmf/AgentEngine.cpp @@ -0,0 +1,854 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/AgentEngine.h" +#include "qmf/MessageImpl.h" +#include "qmf/SchemaImpl.h" +#include "qmf/Typecode.h" +#include "qmf/ObjectImpl.h" +#include "qmf/ObjectIdImpl.h" +#include "qmf/QueryImpl.h" +#include "qmf/ValueImpl.h" +#include "qmf/Protocol.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace qmf; +using namespace qpid::framing; +using namespace qpid::sys; + +namespace qmf { + + struct AgentEventImpl { + typedef boost::shared_ptr Ptr; + AgentEvent::EventKind kind; + uint32_t sequence; + string authUserId; + string authToken; + string name; + Object* object; + boost::shared_ptr objectId; + Query query; + boost::shared_ptr arguments; + string exchange; + string bindingKey; + SchemaObjectClass* objectClass; + + AgentEventImpl(AgentEvent::EventKind k) : + kind(k), sequence(0), object(0), objectClass(0) {} + ~AgentEventImpl() {} + AgentEvent copy(); + }; + + struct AgentQueryContext { + typedef boost::shared_ptr Ptr; + uint32_t sequence; + string exchange; + string key; + SchemaMethodImpl* schemaMethod; + AgentQueryContext() : schemaMethod(0) {} + }; + + class AgentEngineImpl { + public: + AgentEngineImpl(char* label, bool internalStore); + ~AgentEngineImpl(); + + void setStoreDir(const char* path); + void setTransferDir(const char* path); + void handleRcvMessage(Message& message); + bool getXmtMessage(Message& item) const; + void popXmt(); + bool getEvent(AgentEvent& event) const; + void popEvent(); + void newSession(); + void startProtocol(); + void heartbeat(); + void methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments); + void queryResponse(uint32_t sequence, Object& object, bool prop, bool stat); + void queryComplete(uint32_t sequence); + void registerClass(SchemaObjectClass* cls); + void registerClass(SchemaEventClass* cls); + const ObjectId* addObject(Object& obj, uint64_t persistId); + const ObjectId* allocObjectId(uint64_t persistId); + const ObjectId* allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi); + void raiseEvent(Event& event); + + private: + mutable Mutex lock; + Mutex addLock; + string label; + string queueName; + string storeDir; + string transferDir; + bool internalStore; + uint64_t nextTransientId; + Uuid systemId; + uint32_t requestedBrokerBank; + uint32_t requestedAgentBank; + uint32_t assignedBrokerBank; + uint32_t assignedAgentBank; + AgentAttachment attachment; + uint16_t bootSequence; + uint64_t nextObjectId; + uint32_t nextContextNum; + deque eventQueue; + deque xmtQueue; + map contextMap; + + static const char* QMF_EXCHANGE; + static const char* DIR_EXCHANGE; + static const char* BROKER_KEY; + static const uint32_t MERR_UNKNOWN_METHOD = 2; + static const uint32_t MERR_UNKNOWN_PACKAGE = 8; + static const uint32_t MERR_UNKNOWN_CLASS = 9; + static const uint32_t MERR_INTERNAL_ERROR = 10; +# define MA_BUFFER_SIZE 65536 + char outputBuffer[MA_BUFFER_SIZE]; + + struct AgentClassKey { + string name; + uint8_t hash[16]; + AgentClassKey(const string& n, const uint8_t* h) : name(n) { + memcpy(hash, h, 16); + } + AgentClassKey(Buffer& buffer) { + buffer.getShortString(name); + buffer.getBin128(hash); + } + string repr() { + return name; + } + }; + + struct AgentClassKeyComp { + bool operator() (const AgentClassKey& lhs, const AgentClassKey& rhs) const + { + if (lhs.name != rhs.name) + return lhs.name < rhs.name; + else + for (int i = 0; i < 16; i++) + if (lhs.hash[i] != rhs.hash[i]) + return lhs.hash[i] < rhs.hash[i]; + return false; + } + }; + + typedef map ObjectClassMap; + typedef map EventClassMap; + + struct ClassMaps { + ObjectClassMap objectClasses; + EventClassMap eventClasses; + }; + + map packages; + + AgentEventImpl::Ptr eventDeclareQueue(const string& queueName); + AgentEventImpl::Ptr eventBind(const string& exchange, const string& queue, const string& key); + AgentEventImpl::Ptr eventSetupComplete(); + AgentEventImpl::Ptr eventQuery(uint32_t num, const string& userId, const string& package, const string& cls, + boost::shared_ptr oid); + AgentEventImpl::Ptr eventMethod(uint32_t num, const string& userId, const string& method, + boost::shared_ptr oid, boost::shared_ptr argMap, + SchemaObjectClass* objectClass); + void sendBufferLH(Buffer& buf, const string& destination, const string& routingKey); + + void sendPackageIndicationLH(const string& packageName); + void sendClassIndicationLH(ClassKind kind, const string& packageName, const AgentClassKey& key); + void sendCommandCompleteLH(const string& exchange, const string& key, uint32_t seq, + uint32_t code = 0, const string& text = "OK"); + void sendMethodErrorLH(uint32_t sequence, const string& key, uint32_t code, const string& text=""); + void handleAttachResponse(Buffer& inBuffer); + void handlePackageRequest(Buffer& inBuffer); + void handleClassQuery(Buffer& inBuffer); + void handleSchemaRequest(Buffer& inBuffer, uint32_t sequence, + const string& replyToExchange, const string& replyToKey); + void handleGetQuery(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId); + void handleMethodRequest(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId); + void handleConsoleAddedIndication(); + }; +} + +const char* AgentEngineImpl::QMF_EXCHANGE = "qpid.management"; +const char* AgentEngineImpl::DIR_EXCHANGE = "amq.direct"; +const char* AgentEngineImpl::BROKER_KEY = "broker"; + +#define STRING_REF(s) {if (!s.empty()) item.s = const_cast(s.c_str());} + +AgentEvent AgentEventImpl::copy() +{ + AgentEvent item; + + ::memset(&item, 0, sizeof(AgentEvent)); + item.kind = kind; + item.sequence = sequence; + item.object = object; + item.objectId = objectId.get(); + item.query = &query; + item.arguments = arguments.get(); + item.objectClass = objectClass; + + STRING_REF(authUserId); + STRING_REF(authToken); + STRING_REF(name); + STRING_REF(exchange); + STRING_REF(bindingKey); + + return item; +} + +AgentEngineImpl::AgentEngineImpl(char* _label, bool i) : + label(_label), queueName("qmfa-"), internalStore(i), nextTransientId(1), + requestedBrokerBank(0), requestedAgentBank(0), + assignedBrokerBank(0), assignedAgentBank(0), + bootSequence(1), nextObjectId(1), nextContextNum(1) +{ + queueName += label; +} + +AgentEngineImpl::~AgentEngineImpl() +{ +} + +void AgentEngineImpl::setStoreDir(const char* path) +{ + Mutex::ScopedLock _lock(lock); + if (path) + storeDir = path; + else + storeDir.clear(); +} + +void AgentEngineImpl::setTransferDir(const char* path) +{ + Mutex::ScopedLock _lock(lock); + if (path) + transferDir = path; + else + transferDir.clear(); +} + +void AgentEngineImpl::handleRcvMessage(Message& message) +{ + Buffer inBuffer(message.body, message.length); + uint8_t opcode; + uint32_t sequence; + string replyToExchange(message.replyExchange ? message.replyExchange : ""); + string replyToKey(message.replyKey ? message.replyKey : ""); + string userId(message.userId ? message.userId : ""); + + while (Protocol::checkHeader(inBuffer, &opcode, &sequence)) { + if (opcode == Protocol::OP_ATTACH_RESPONSE) handleAttachResponse(inBuffer); + else if (opcode == Protocol::OP_SCHEMA_REQUEST) handleSchemaRequest(inBuffer, sequence, replyToExchange, replyToKey); + else if (opcode == Protocol::OP_CONSOLE_ADDED_INDICATION) handleConsoleAddedIndication(); + else if (opcode == Protocol::OP_GET_QUERY) handleGetQuery(inBuffer, sequence, replyToKey, userId); + else if (opcode == Protocol::OP_METHOD_REQUEST) handleMethodRequest(inBuffer, sequence, replyToKey, userId); + else { + QPID_LOG(error, "AgentEngineImpl::handleRcvMessage invalid opcode=" << opcode); + break; + } + } +} + +bool AgentEngineImpl::getXmtMessage(Message& item) const +{ + Mutex::ScopedLock _lock(lock); + if (xmtQueue.empty()) + return false; + item = xmtQueue.front()->copy(); + return true; +} + +void AgentEngineImpl::popXmt() +{ + Mutex::ScopedLock _lock(lock); + if (!xmtQueue.empty()) + xmtQueue.pop_front(); +} + +bool AgentEngineImpl::getEvent(AgentEvent& event) const +{ + Mutex::ScopedLock _lock(lock); + if (eventQueue.empty()) + return false; + event = eventQueue.front()->copy(); + return true; +} + +void AgentEngineImpl::popEvent() +{ + Mutex::ScopedLock _lock(lock); + if (!eventQueue.empty()) + eventQueue.pop_front(); +} + +void AgentEngineImpl::newSession() +{ + Mutex::ScopedLock _lock(lock); + eventQueue.clear(); + xmtQueue.clear(); + eventQueue.push_back(eventDeclareQueue(queueName)); + eventQueue.push_back(eventBind("amq.direct", queueName, queueName)); + eventQueue.push_back(eventSetupComplete()); +} + +void AgentEngineImpl::startProtocol() +{ + Mutex::ScopedLock _lock(lock); + char rawbuffer[512]; + Buffer buffer(rawbuffer, 512); + + Protocol::encodeHeader(buffer, Protocol::OP_ATTACH_REQUEST); + buffer.putShortString("qmfa"); + systemId.encode(buffer); + buffer.putLong(requestedBrokerBank); + buffer.putLong(requestedAgentBank); + sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT AttachRequest: reqBroker=" << requestedBrokerBank << + " reqAgent=" << requestedAgentBank); +} + +void AgentEngineImpl::heartbeat() +{ + Mutex::ScopedLock _lock(lock); + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + + Protocol::encodeHeader(buffer, Protocol::OP_HEARTBEAT_INDICATION); + buffer.putLongLong(uint64_t(Duration(now()))); + stringstream key; + key << "console.heartbeat." << assignedBrokerBank << "." << assignedAgentBank; + sendBufferLH(buffer, QMF_EXCHANGE, key.str()); + QPID_LOG(trace, "SENT HeartbeatIndication"); +} + +void AgentEngineImpl::methodResponse(uint32_t sequence, uint32_t status, char* text, + const Value& argMap) +{ + Mutex::ScopedLock _lock(lock); + map::iterator iter = contextMap.find(sequence); + if (iter == contextMap.end()) + return; + AgentQueryContext::Ptr context = iter->second; + contextMap.erase(iter); + + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_METHOD_RESPONSE, context->sequence); + buffer.putLong(status); + buffer.putMediumString(text); + if (status == 0) { + for (vector::const_iterator aIter = context->schemaMethod->arguments.begin(); + aIter != context->schemaMethod->arguments.end(); aIter++) { + const SchemaArgumentImpl* schemaArg = *aIter; + if (schemaArg->dir == DIR_OUT || schemaArg->dir == DIR_IN_OUT) { + if (argMap.keyInMap(schemaArg->name.c_str())) { + const Value* val = argMap.byKey(schemaArg->name.c_str()); + val->impl->encode(buffer); + } else { + Value val(schemaArg->typecode); + val.impl->encode(buffer); + } + } + } + } + sendBufferLH(buffer, context->exchange, context->key); + QPID_LOG(trace, "SENT MethodResponse"); +} + +void AgentEngineImpl::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat) +{ + Mutex::ScopedLock _lock(lock); + map::iterator iter = contextMap.find(sequence); + if (iter == contextMap.end()) + return; + AgentQueryContext::Ptr context = iter->second; + + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_OBJECT_INDICATION, context->sequence); + + object.impl->encodeSchemaKey(buffer); + object.impl->encodeManagedObjectData(buffer); + if (prop) + object.impl->encodeProperties(buffer); + if (stat) + object.impl->encodeStatistics(buffer); + + sendBufferLH(buffer, context->exchange, context->key); + QPID_LOG(trace, "SENT ContentIndication"); +} + +void AgentEngineImpl::queryComplete(uint32_t sequence) +{ + Mutex::ScopedLock _lock(lock); + map::iterator iter = contextMap.find(sequence); + if (iter == contextMap.end()) + return; + + AgentQueryContext::Ptr context = iter->second; + contextMap.erase(iter); + sendCommandCompleteLH(context->exchange, context->key, context->sequence, 0, "OK"); +} + +void AgentEngineImpl::registerClass(SchemaObjectClass* cls) +{ + Mutex::ScopedLock _lock(lock); + SchemaObjectClassImpl* impl = cls->impl; + + map::iterator iter = packages.find(impl->package); + if (iter == packages.end()) { + packages[impl->package] = ClassMaps(); + iter = packages.find(impl->getClassKey()->getPackageName()); + // TODO: Indicate this package if connected + } + + AgentClassKey key(impl->getClassKey()->getClassName(), impl->getClassKey()->getHash()); + iter->second.objectClasses[key] = impl; + + // TODO: Indicate this schema if connected. +} + +void AgentEngineImpl::registerClass(SchemaEventClass* cls) +{ + Mutex::ScopedLock _lock(lock); + SchemaEventClassImpl* impl = cls->impl; + + map::iterator iter = packages.find(impl->package); + if (iter == packages.end()) { + packages[impl->package] = ClassMaps(); + iter = packages.find(impl->getClassKey()->getPackageName()); + // TODO: Indicate this package if connected + } + + AgentClassKey key(impl->getClassKey()->getClassName(), impl->getClassKey()->getHash()); + iter->second.eventClasses[key] = impl; + + // TODO: Indicate this schema if connected. +} + +const ObjectId* AgentEngineImpl::addObject(Object&, uint64_t) +{ + Mutex::ScopedLock _lock(lock); + return 0; +} + +const ObjectId* AgentEngineImpl::allocObjectId(uint64_t persistId) +{ + Mutex::ScopedLock _lock(lock); + uint16_t sequence = persistId ? 0 : bootSequence; + uint64_t objectNum = persistId ? persistId : nextObjectId++; + + ObjectIdImpl* oid = new ObjectIdImpl(&attachment, 0, sequence, objectNum); + return oid->envelope; +} + +const ObjectId* AgentEngineImpl::allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi) +{ + return allocObjectId(((uint64_t) persistIdHi) << 32 | (uint64_t) persistIdLo); +} + +void AgentEngineImpl::raiseEvent(Event&) +{ + Mutex::ScopedLock _lock(lock); +} + +AgentEventImpl::Ptr AgentEngineImpl::eventDeclareQueue(const string& name) +{ + AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::DECLARE_QUEUE)); + event->name = name; + + return event; +} + +AgentEventImpl::Ptr AgentEngineImpl::eventBind(const string& exchange, const string& queue, + const string& key) +{ + AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::BIND)); + event->name = queue; + event->exchange = exchange; + event->bindingKey = key; + + return event; +} + +AgentEventImpl::Ptr AgentEngineImpl::eventSetupComplete() +{ + AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::SETUP_COMPLETE)); + return event; +} + +AgentEventImpl::Ptr AgentEngineImpl::eventQuery(uint32_t num, const string& userId, const string& package, + const string& cls, boost::shared_ptr oid) +{ + AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::GET_QUERY)); + event->sequence = num; + event->authUserId = userId; + event->query.impl->packageName = package; + event->query.impl->className = cls; + event->query.impl->oid = oid; + return event; +} + +AgentEventImpl::Ptr AgentEngineImpl::eventMethod(uint32_t num, const string& userId, const string& method, + boost::shared_ptr oid, boost::shared_ptr argMap, + SchemaObjectClass* objectClass) +{ + AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::METHOD_CALL)); + event->sequence = num; + event->authUserId = userId; + event->name = method; + event->objectId = oid; + event->arguments = argMap; + event->objectClass = objectClass; + return event; +} + +void AgentEngineImpl::sendBufferLH(Buffer& buf, const string& destination, const string& routingKey) +{ + uint32_t length = buf.getPosition(); + MessageImpl::Ptr message(new MessageImpl); + + buf.reset(); + buf.getRawData(message->body, length); + message->destination = destination; + message->routingKey = routingKey; + message->replyExchange = "amq.direct"; + message->replyKey = queueName; + + xmtQueue.push_back(message); +} + +void AgentEngineImpl::sendPackageIndicationLH(const string& packageName) +{ + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_PACKAGE_INDICATION); + buffer.putShortString(packageName); + sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT PackageIndication: package_name=" << packageName); +} + +void AgentEngineImpl::sendClassIndicationLH(ClassKind kind, const string& packageName, const AgentClassKey& key) +{ + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_CLASS_INDICATION); + buffer.putOctet((int) kind); + buffer.putShortString(packageName); + buffer.putShortString(key.name); + buffer.putBin128(const_cast(key.hash)); // const_cast needed for older Qpid libraries + sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT ClassIndication: package_name=" << packageName << " class_name=" << key.name); +} + +void AgentEngineImpl::sendCommandCompleteLH(const string& exchange, const string& replyToKey, + uint32_t sequence, uint32_t code, const string& text) +{ + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_COMMAND_COMPLETE, sequence); + buffer.putLong(code); + buffer.putShortString(text); + sendBufferLH(buffer, exchange, replyToKey); + QPID_LOG(trace, "SENT CommandComplete: seq=" << sequence << " code=" << code << " text=" << text); +} + +void AgentEngineImpl::sendMethodErrorLH(uint32_t sequence, const string& key, uint32_t code, const string& text) +{ + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_METHOD_RESPONSE, sequence); + buffer.putLong(code); + + string fulltext; + switch (code) { + case MERR_UNKNOWN_PACKAGE: fulltext = "Unknown Package"; break; + case MERR_UNKNOWN_CLASS: fulltext = "Unknown Class"; break; + case MERR_UNKNOWN_METHOD: fulltext = "Unknown Method"; break; + case MERR_INTERNAL_ERROR: fulltext = "Internal Error"; break; + default: fulltext = "Unspecified Error"; break; + } + + if (!text.empty()) { + fulltext += " ("; + fulltext += text; + fulltext += ")"; + } + + buffer.putMediumString(fulltext); + sendBufferLH(buffer, DIR_EXCHANGE, key); + QPID_LOG(trace, "SENT MethodResponse: errorCode=" << code << " text=" << fulltext); +} + +void AgentEngineImpl::handleAttachResponse(Buffer& inBuffer) +{ + Mutex::ScopedLock _lock(lock); + + assignedBrokerBank = inBuffer.getLong(); + assignedAgentBank = inBuffer.getLong(); + + QPID_LOG(trace, "RCVD AttachResponse: broker=" << assignedBrokerBank << " agent=" << assignedAgentBank); + + if ((assignedBrokerBank != requestedBrokerBank) || + (assignedAgentBank != requestedAgentBank)) { + if (requestedAgentBank == 0) { + QPID_LOG(notice, "Initial object-id bank assigned: " << assignedBrokerBank << "." << + assignedAgentBank); + } else { + QPID_LOG(warning, "Collision in object-id! New bank assigned: " << assignedBrokerBank << + "." << assignedAgentBank); + } + //storeData(); // TODO + requestedBrokerBank = assignedBrokerBank; + requestedAgentBank = assignedAgentBank; + } + + attachment.setBanks(assignedBrokerBank, assignedAgentBank); + + // Bind to qpid.management to receive commands + stringstream key; + key << "agent." << assignedBrokerBank << "." << assignedAgentBank; + eventQueue.push_back(eventBind(QMF_EXCHANGE, queueName, key.str())); + + // Send package indications for all local packages + for (map::iterator pIter = packages.begin(); + pIter != packages.end(); + pIter++) { + sendPackageIndicationLH(pIter->first); + + // Send class indications for all local classes + ClassMaps cMap = pIter->second; + for (ObjectClassMap::iterator cIter = cMap.objectClasses.begin(); + cIter != cMap.objectClasses.end(); cIter++) + sendClassIndicationLH(CLASS_OBJECT, pIter->first, cIter->first); + for (EventClassMap::iterator cIter = cMap.eventClasses.begin(); + cIter != cMap.eventClasses.end(); cIter++) + sendClassIndicationLH(CLASS_EVENT, pIter->first, cIter->first); + } +} + +void AgentEngineImpl::handlePackageRequest(Buffer&) +{ + Mutex::ScopedLock _lock(lock); +} + +void AgentEngineImpl::handleClassQuery(Buffer&) +{ + Mutex::ScopedLock _lock(lock); +} + +void AgentEngineImpl::handleSchemaRequest(Buffer& inBuffer, uint32_t sequence, + const string& replyExchange, const string& replyKey) +{ + Mutex::ScopedLock _lock(lock); + string rExchange(replyExchange); + string rKey(replyKey); + string packageName; + inBuffer.getShortString(packageName); + AgentClassKey key(inBuffer); + + if (rExchange.empty()) + rExchange = QMF_EXCHANGE; + if (rKey.empty()) + rKey = BROKER_KEY; + + QPID_LOG(trace, "RCVD SchemaRequest: package=" << packageName << " class=" << key.name); + + map::iterator pIter = packages.find(packageName); + if (pIter == packages.end()) { + sendCommandCompleteLH(rExchange, rKey, sequence, 1, "package not found"); + return; + } + + ClassMaps cMap = pIter->second; + ObjectClassMap::iterator ocIter = cMap.objectClasses.find(key); + if (ocIter != cMap.objectClasses.end()) { + SchemaObjectClassImpl* oImpl = ocIter->second; + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_SCHEMA_RESPONSE, sequence); + oImpl->encode(buffer); + sendBufferLH(buffer, rExchange, rKey); + QPID_LOG(trace, "SENT SchemaResponse: (object) package=" << packageName << " class=" << key.name); + return; + } + + EventClassMap::iterator ecIter = cMap.eventClasses.find(key); + if (ecIter != cMap.eventClasses.end()) { + SchemaEventClassImpl* eImpl = ecIter->second; + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_SCHEMA_RESPONSE, sequence); + eImpl->encode(buffer); + sendBufferLH(buffer, rExchange, rKey); + QPID_LOG(trace, "SENT SchemaResponse: (event) package=" << packageName << " class=" << key.name); + return; + } + + sendCommandCompleteLH(rExchange, rKey, sequence, 1, "class not found"); +} + +void AgentEngineImpl::handleGetQuery(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId) +{ + Mutex::ScopedLock _lock(lock); + FieldTable ft; + FieldTable::ValuePtr value; + map::const_iterator pIter = packages.end(); + string pname; + string cname; + string oidRepr; + boost::shared_ptr oid; + + ft.decode(inBuffer); + + QPID_LOG(trace, "RCVD GetQuery: map=" << ft); + + value = ft.get("_package"); + if (value.get() && value->convertsTo()) { + pname = value->get(); + pIter = packages.find(pname); + if (pIter == packages.end()) { + sendCommandCompleteLH(DIR_EXCHANGE, replyTo, sequence); + return; + } + } + + value = ft.get("_class"); + if (value.get() && value->convertsTo()) { + cname = value->get(); + // TODO - check for validity of class (in package or any package) + if (pIter == packages.end()) { + } else { + + } + } + + value = ft.get("_objectid"); + if (value.get() && value->convertsTo()) { + oidRepr = value->get(); + oid.reset(new ObjectId()); + oid->impl->fromString(oidRepr); + } + + AgentQueryContext::Ptr context(new AgentQueryContext); + uint32_t contextNum = nextContextNum++; + context->sequence = sequence; + context->exchange = DIR_EXCHANGE; + context->key = replyTo; + contextMap[contextNum] = context; + + eventQueue.push_back(eventQuery(contextNum, userId, pname, cname, oid)); +} + +void AgentEngineImpl::handleMethodRequest(Buffer& buffer, uint32_t sequence, const string& replyTo, const string& userId) +{ + Mutex::ScopedLock _lock(lock); + string pname; + string method; + ObjectIdImpl* oidImpl = new ObjectIdImpl(buffer); + boost::shared_ptr oid(oidImpl->envelope); + buffer.getShortString(pname); + AgentClassKey classKey(buffer); + buffer.getShortString(method); + + map::const_iterator pIter = packages.find(pname); + if (pIter == packages.end()) { + sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_PACKAGE, pname); + return; + } + + ObjectClassMap::const_iterator cIter = pIter->second.objectClasses.find(classKey); + if (cIter == pIter->second.objectClasses.end()) { + sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_CLASS, classKey.repr()); + return; + } + + const SchemaObjectClassImpl* schema = cIter->second; + vector::const_iterator mIter = schema->methods.begin(); + for (; mIter != schema->methods.end(); mIter++) { + if ((*mIter)->name == method) + break; + } + + if (mIter == schema->methods.end()) { + sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_METHOD, method); + return; + } + + SchemaMethodImpl* schemaMethod = *mIter; + boost::shared_ptr argMap(new Value(TYPE_MAP)); + ValueImpl* value; + for (vector::const_iterator aIter = schemaMethod->arguments.begin(); + aIter != schemaMethod->arguments.end(); aIter++) { + const SchemaArgumentImpl* schemaArg = *aIter; + if (schemaArg->dir == DIR_IN || schemaArg->dir == DIR_IN_OUT) + value = new ValueImpl(schemaArg->typecode, buffer); + else + value = new ValueImpl(schemaArg->typecode); + argMap->insert(schemaArg->name.c_str(), value->envelope); + } + + AgentQueryContext::Ptr context(new AgentQueryContext); + uint32_t contextNum = nextContextNum++; + context->sequence = sequence; + context->exchange = DIR_EXCHANGE; + context->key = replyTo; + context->schemaMethod = schemaMethod; + contextMap[contextNum] = context; + + eventQueue.push_back(eventMethod(contextNum, userId, method, oid, argMap, schema->envelope)); +} + +void AgentEngineImpl::handleConsoleAddedIndication() +{ + Mutex::ScopedLock _lock(lock); +} + +//================================================================== +// Wrappers +//================================================================== + +AgentEngine::AgentEngine(char* label, bool internalStore) { impl = new AgentEngineImpl(label, internalStore); } +AgentEngine::~AgentEngine() { delete impl; } +void AgentEngine::setStoreDir(const char* path) { impl->setStoreDir(path); } +void AgentEngine::setTransferDir(const char* path) { impl->setTransferDir(path); } +void AgentEngine::handleRcvMessage(Message& message) { impl->handleRcvMessage(message); } +bool AgentEngine::getXmtMessage(Message& item) const { return impl->getXmtMessage(item); } +void AgentEngine::popXmt() { impl->popXmt(); } +bool AgentEngine::getEvent(AgentEvent& event) const { return impl->getEvent(event); } +void AgentEngine::popEvent() { impl->popEvent(); } +void AgentEngine::newSession() { impl->newSession(); } +void AgentEngine::startProtocol() { impl->startProtocol(); } +void AgentEngine::heartbeat() { impl->heartbeat(); } +void AgentEngine::methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments) { impl->methodResponse(sequence, status, text, arguments); } +void AgentEngine::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat) { impl->queryResponse(sequence, object, prop, stat); } +void AgentEngine::queryComplete(uint32_t sequence) { impl->queryComplete(sequence); } +void AgentEngine::registerClass(SchemaObjectClass* cls) { impl->registerClass(cls); } +void AgentEngine::registerClass(SchemaEventClass* cls) { impl->registerClass(cls); } +const ObjectId* AgentEngine::addObject(Object& obj, uint64_t persistId) { return impl->addObject(obj, persistId); } +const ObjectId* AgentEngine::allocObjectId(uint64_t persistId) { return impl->allocObjectId(persistId); } +const ObjectId* AgentEngine::allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi) { return impl->allocObjectId(persistIdLo, persistIdHi); } +void AgentEngine::raiseEvent(Event& event) { impl->raiseEvent(event); } + diff --git a/qpid/cpp/src/qmf/AgentEngine.h b/qpid/cpp/src/qmf/AgentEngine.h new file mode 100644 index 0000000000..c88ef33657 --- /dev/null +++ b/qpid/cpp/src/qmf/AgentEngine.h @@ -0,0 +1,207 @@ +#ifndef _QmfAgentEngine_ +#define _QmfAgentEngine_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace qmf { + + /** + * AgentEvent + * + * This structure represents a QMF event coming from the agent to + * the application. + */ + struct AgentEvent { + enum EventKind { + GET_QUERY = 1, + START_SYNC = 2, + END_SYNC = 3, + METHOD_CALL = 4, + DECLARE_QUEUE = 5, + DELETE_QUEUE = 6, + BIND = 7, + UNBIND = 8, + SETUP_COMPLETE = 9 + }; + + EventKind kind; + uint32_t sequence; // Protocol sequence (for all kinds) + char* authUserId; // Authenticated user ID (for all kinds) + char* authToken; // Authentication token if issued (for all kinds) + char* name; // Name of the method/sync query + // (METHOD_CALL, START_SYNC, END_SYNC, DECLARE_QUEUE, BIND, UNBIND) + Object* object; // Object involved in method call (METHOD_CALL) + ObjectId* objectId; // ObjectId for method call (METHOD_CALL) + Query* query; // Query parameters (GET_QUERY, START_SYNC) + Value* arguments; // Method parameters (METHOD_CALL) + char* exchange; // Exchange for bind (BIND, UNBIND) + char* bindingKey; // Key for bind (BIND, UNBIND) + SchemaObjectClass* objectClass; // (METHOD_CALL) + }; + + class AgentEngineImpl; + + /** + * AgentEngine - Protocol engine for the QMF agent + */ + class AgentEngine { + public: + AgentEngine(char* label, bool internalStore=true); + ~AgentEngine(); + + /** + * Configure the directory path for storing persistent data. + *@param path Null-terminated string containing a directory path where files can be + * created, written, and read. If NULL, no persistent storage will be + * attempted. + */ + void setStoreDir(const char* path); + + /** + * Configure the directory path for files transferred over QMF. + *@param path Null-terminated string containing a directory path where files can be + * created, deleted, written, and read. If NULL, file transfers shall not + * be permitted. + */ + void setTransferDir(const char* path); + + /** + * Pass messages received from the AMQP session to the Agent engine. + *@param message AMQP messages received on the agent session. + */ + void handleRcvMessage(Message& message); + + /** + * Get the next message to be sent to the AMQP network. + *@param item The Message structure describing the message to be produced. + *@return true if the Message is valid, false if there are no messages to send. + */ + bool getXmtMessage(Message& item) const; + + /** + * Remove and discard one message from the head of the transmit queue. + */ + void popXmt(); + + /** + * Get the next application event from the agent engine. + *@param event The event iff the return value is true + *@return true if event is valid, false if there are no events to process + */ + bool getEvent(AgentEvent& event) const; + + /** + * Remove and discard one event from the head of the event queue. + */ + void popEvent(); + + /** + * A new AMQP session has been established for Agent communication. + */ + void newSession(); + + /** + * Start the QMF Agent protocol. This should be invoked after a SETUP_COMPLETE event + * is received from the Agent engine. + */ + void startProtocol(); + + /** + * This method is called periodically so the agent can supply a heartbeat. + */ + void heartbeat(); + + /** + * Respond to a method request. + *@param sequence The sequence number from the method request event. + *@param status The method's completion status. + *@param text Status text ("OK" or an error message) + *@param arguments The list of output arguments from the method call. + */ + void methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments); + + /** + * Send a content indication to the QMF bus. This is only needed for objects that are + * managed by the application. This is *NOT* needed for objects managed by the Agent + * (inserted using addObject). + *@param sequence The sequence number of the GET request or the SYNC_START request. + *@param object The object (annotated with "changed" flags) for publication. + *@param prop If true, changed object properties are transmitted. + *@param stat If true, changed object statistics are transmitted. + */ + void queryResponse(uint32_t sequence, Object& object, bool prop = true, bool stat = true); + + /** + * Indicate the completion of a query. This is not used for SYNC_START requests. + *@param sequence The sequence number of the GET request. + */ + void queryComplete(uint32_t sequence); + + /** + * Register a schema class with the Agent. + *@param cls A SchemaObejctClass object that defines data managed by the agent. + */ + void registerClass(SchemaObjectClass* cls); + + /** + * Register a schema class with the Agent. + *@param cls A SchemaEventClass object that defines events sent by the agent. + */ + void registerClass(SchemaEventClass* cls); + + /** + * Give an object to the Agent for storage and management. Once added, the agent takes + * responsibility for the life cycle of the object. + *@param obj The object to be managed by the Agent. + *@param persistId A unique non-zero value if the object-id is to be persistent. + *@return The objectId of the managed object. + */ + const ObjectId* addObject(Object& obj, uint64_t persistId); + // const ObjectId* addObject(Object& obj, uint32_t persistIdLo, uint32_t persistIdHi); + + /** + * Allocate an object-id for an object that will be managed by the application. + *@param persistId A unique non-zero value if the object-id is to be persistent. + *@return The objectId structure for the allocated ID. + */ + const ObjectId* allocObjectId(uint64_t persistId); + const ObjectId* allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi); + + /** + * Raise an event into the QMF network.. + *@param event The event object for the event to be raised. + */ + void raiseEvent(Event& event); + + private: + AgentEngineImpl* impl; + }; +} + +#endif + diff --git a/qpid/cpp/src/qmf/ConnectionSettingsImpl.cpp b/qpid/cpp/src/qmf/ConnectionSettingsImpl.cpp new file mode 100644 index 0000000000..034ab18395 --- /dev/null +++ b/qpid/cpp/src/qmf/ConnectionSettingsImpl.cpp @@ -0,0 +1,323 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/ConnectionSettingsImpl.h" +#include "qmf/Typecode.h" + +using namespace std; +using namespace qmf; +using namespace qpid; + +const string attrProtocol("protocol"); +const string attrHost("host"); +const string attrPort("port"); +const string attrVirtualhost("virtualhost"); +const string attrUsername("username"); +const string attrPassword("password"); +const string attrMechanism("mechanism"); +const string attrLocale("locale"); +const string attrHeartbeat("heartbeat"); +const string attrMaxChannels("maxChannels"); +const string attrMaxFrameSize("maxFrameSize"); +const string attrBounds("bounds"); +const string attrTcpNoDelay("tcpNoDelay"); +const string attrService("service"); +const string attrMinSsf("minSsf"); +const string attrMaxSsf("maxSsf"); +const string attrRetryDelayMin("retryDelayMin"); +const string attrRetryDelayMax("retryDelayMax"); +const string attrRetryDelayFactor("retryDelayFactor"); + +ConnectionSettingsImpl::ConnectionSettingsImpl(ConnectionSettings* e) : + envelope(e), retryDelayMin(1), retryDelayMax(64), retryDelayFactor(2) +{ +} + +ConnectionSettingsImpl::ConnectionSettingsImpl(ConnectionSettings* e, const string& /*url*/) : + envelope(e), retryDelayMin(1), retryDelayMax(64), retryDelayFactor(2) +{ + // TODO: Parse the URL +} + +void ConnectionSettingsImpl::setAttr(const string& key, const Value& value) +{ + if (key == attrProtocol) clientSettings.protocol = value.asString(); + else if (key == attrHost) clientSettings.host = value.asString(); + else if (key == attrPort) clientSettings.port = value.asUint(); + else if (key == attrVirtualhost) clientSettings.virtualhost = value.asString(); + else if (key == attrUsername) clientSettings.username = value.asString(); + else if (key == attrPassword) clientSettings.password = value.asString(); + else if (key == attrMechanism) clientSettings.mechanism = value.asString(); + else if (key == attrLocale) clientSettings.locale = value.asString(); + else if (key == attrHeartbeat) clientSettings.heartbeat = value.asUint(); + else if (key == attrMaxChannels) clientSettings.maxChannels = value.asUint(); + else if (key == attrMaxFrameSize) clientSettings.maxFrameSize = value.asUint(); + else if (key == attrBounds) clientSettings.bounds = value.asUint(); + else if (key == attrTcpNoDelay) clientSettings.tcpNoDelay = value.asBool(); + else if (key == attrService) clientSettings.service = value.asString(); + else if (key == attrMinSsf) clientSettings.minSsf = value.asUint(); + else if (key == attrMaxSsf) clientSettings.maxSsf = value.asUint(); + + else if (key == attrRetryDelayMin) retryDelayMin = value.asUint(); + else if (key == attrRetryDelayMax) retryDelayMax = value.asUint(); + else if (key == attrRetryDelayFactor) retryDelayFactor = value.asUint(); +} + +Value ConnectionSettingsImpl::getAttr(const string& key) const +{ + Value strval(TYPE_LSTR); + Value intval(TYPE_UINT32); + Value boolval(TYPE_BOOL); + + if (key == attrProtocol) { + strval.setString(clientSettings.protocol.c_str()); + return strval; + } + + if (key == attrHost) { + strval.setString(clientSettings.host.c_str()); + return strval; + } + + if (key == attrPort) { + intval.setUint(clientSettings.port); + return intval; + } + + if (key == attrVirtualhost) { + strval.setString(clientSettings.virtualhost.c_str()); + return strval; + } + + if (key == attrUsername) { + strval.setString(clientSettings.username.c_str()); + return strval; + } + + if (key == attrPassword) { + strval.setString(clientSettings.password.c_str()); + return strval; + } + + if (key == attrMechanism) { + strval.setString(clientSettings.mechanism.c_str()); + return strval; + } + + if (key == attrLocale) { + strval.setString(clientSettings.locale.c_str()); + return strval; + } + + if (key == attrHeartbeat) { + intval.setUint(clientSettings.heartbeat); + return intval; + } + + if (key == attrMaxChannels) { + intval.setUint(clientSettings.maxChannels); + return intval; + } + + if (key == attrMaxFrameSize) { + intval.setUint(clientSettings.maxFrameSize); + return intval; + } + + if (key == attrBounds) { + intval.setUint(clientSettings.bounds); + return intval; + } + + if (key == attrTcpNoDelay) { + boolval.setBool(clientSettings.tcpNoDelay); + return boolval; + } + + if (key == attrService) { + strval.setString(clientSettings.service.c_str()); + return strval; + } + + if (key == attrMinSsf) { + intval.setUint(clientSettings.minSsf); + return intval; + } + + if (key == attrMaxSsf) { + intval.setUint(clientSettings.maxSsf); + return intval; + } + + if (key == attrRetryDelayMin) { + intval.setUint(retryDelayMin); + return intval; + } + + if (key == attrRetryDelayMax) { + intval.setUint(retryDelayMax); + return intval; + } + + if (key == attrRetryDelayFactor) { + intval.setUint(retryDelayFactor); + return intval; + } + + return strval; +} + +const string& ConnectionSettingsImpl::getAttrString() const +{ + // TODO: build and return attribute string + return attrString; +} + +void ConnectionSettingsImpl::transportTcp(uint16_t port) +{ + clientSettings.protocol = "tcp"; + clientSettings.port = port; +} + +void ConnectionSettingsImpl::transportSsl(uint16_t port) +{ + clientSettings.protocol = "ssl"; + clientSettings.port = port; +} + +void ConnectionSettingsImpl::transportRdma(uint16_t port) +{ + clientSettings.protocol = "rdma"; + clientSettings.port = port; +} + +void ConnectionSettingsImpl::authAnonymous(const string& username) +{ + clientSettings.mechanism = "ANONYMOUS"; + clientSettings.username = username; +} + +void ConnectionSettingsImpl::authPlain(const string& username, const string& password) +{ + clientSettings.mechanism = "PLAIN"; + clientSettings.username = username; + clientSettings.password = password; +} + +void ConnectionSettingsImpl::authGssapi(const string& serviceName, uint32_t minSsf, uint32_t maxSsf) +{ + clientSettings.mechanism = "GSSAPI"; + clientSettings.service = serviceName; + clientSettings.minSsf = minSsf; + clientSettings.maxSsf = maxSsf; +} + +void ConnectionSettingsImpl::setRetry(int delayMin, int delayMax, int delayFactor) +{ + retryDelayMin = delayMin; + retryDelayMax = delayMax; + retryDelayFactor = delayFactor; +} + +const client::ConnectionSettings& ConnectionSettingsImpl::getClientSettings() const +{ + return clientSettings; +} + +void ConnectionSettingsImpl::getRetrySettings(int* min, int* max, int* factor) const +{ + *min = retryDelayMin; + *max = retryDelayMax; + *factor = retryDelayFactor; +} + +//================================================================== +// Wrappers +//================================================================== + +ConnectionSettings::ConnectionSettings(const ConnectionSettings& from) +{ + impl = new ConnectionSettingsImpl(*from.impl); +} + +ConnectionSettings::ConnectionSettings() +{ + impl = new ConnectionSettingsImpl(this); +} + +ConnectionSettings::ConnectionSettings(const char* url) +{ + impl = new ConnectionSettingsImpl(this, url); +} + +ConnectionSettings::~ConnectionSettings() +{ + delete impl; +} + +void ConnectionSettings::setAttr(const char* key, const Value& value) +{ + impl->setAttr(key, value); +} + +Value ConnectionSettings::getAttr(const char* key) const +{ + return impl->getAttr(key); +} + +const char* ConnectionSettings::getAttrString() const +{ + return impl->getAttrString().c_str(); +} + +void ConnectionSettings::transportTcp(uint16_t port) +{ + impl->transportTcp(port); +} + +void ConnectionSettings::transportSsl(uint16_t port) +{ + impl->transportSsl(port); +} + +void ConnectionSettings::transportRdma(uint16_t port) +{ + impl->transportRdma(port); +} + +void ConnectionSettings::authAnonymous(const char* username) +{ + impl->authAnonymous(username); +} + +void ConnectionSettings::authPlain(const char* username, const char* password) +{ + impl->authPlain(username, password); +} + +void ConnectionSettings::authGssapi(const char* serviceName, uint32_t minSsf, uint32_t maxSsf) +{ + impl->authGssapi(serviceName, minSsf, maxSsf); +} + +void ConnectionSettings::setRetry(int delayMin, int delayMax, int delayFactor) +{ + impl->setRetry(delayMin, delayMax, delayFactor); +} + diff --git a/qpid/cpp/src/qmf/ConnectionSettingsImpl.h b/qpid/cpp/src/qmf/ConnectionSettingsImpl.h new file mode 100644 index 0000000000..a177233cf3 --- /dev/null +++ b/qpid/cpp/src/qmf/ConnectionSettingsImpl.h @@ -0,0 +1,60 @@ +#ifndef _QmfConnectionSettingsImpl_ +#define _QmfConnectionSettingsImpl_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/ConnectionSettings.h" +#include "qmf/Value.h" +#include "qpid/client/ConnectionSettings.h" +#include +#include + +namespace qmf { + + class ConnectionSettingsImpl { + ConnectionSettings* envelope; + qpid::client::ConnectionSettings clientSettings; + mutable std::string attrString; + int retryDelayMin; + int retryDelayMax; + int retryDelayFactor; + + public: + ConnectionSettingsImpl(ConnectionSettings* e); + ConnectionSettingsImpl(ConnectionSettings* e, const std::string& url); + ~ConnectionSettingsImpl() {} + void setAttr(const std::string& key, const Value& value); + Value getAttr(const std::string& key) const; + const std::string& getAttrString() const; + void transportTcp(uint16_t port); + void transportSsl(uint16_t port); + void transportRdma(uint16_t port); + void authAnonymous(const std::string& username); + void authPlain(const std::string& username, const std::string& password); + void authGssapi(const std::string& serviceName, uint32_t minSsf, uint32_t maxSsf); + void setRetry(int delayMin, int delayMax, int delayFactor); + + const qpid::client::ConnectionSettings& getClientSettings() const; + void getRetrySettings(int* delayMin, int* delayMax, int* delayFactor) const; + }; + +} + +#endif diff --git a/qpid/cpp/src/qmf/Console.h b/qpid/cpp/src/qmf/Console.h deleted file mode 100644 index de7949e1de..0000000000 --- a/qpid/cpp/src/qmf/Console.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef _QmfConsole_ -#define _QmfConsole_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace qmf { - - struct ConsoleSettings { - bool rcvObjects; - bool rcvEvents; - bool rcvHeartbeats; - bool userBindings; - uint32_t methodTimeout; - uint32_t getTimeout; - - ConsoleSettings() : - rcvObjects(true), - rcvEvents(true), - rcvHeartbeats(true), - userBindings(false), - methodTimeout(20), - getTimeout(20) {} - }; - - class Console { - public: - Console(ConsoleHandler* handler = 0, ConsoleSettings settings = ConsoleSettings()); - ~Console(); - - Broker* addConnection(ManagedConnection& connection); - void delConnection(Broker* broker); - void delConnection(ManagedConnection& connection); - - const PackageMap& getPackages() const; - - void bindPackage(const Package& package); - void bindPackage(const std::string& packageName); - void bindClass(const SchemaClass& otype); - void bindClass(const std::string& packageName, const std::string& className); - - void getAgents(std::set& agents, Broker* = 0); - void getObjects(std::vector& objects, const std::string& typeName, - const std::string& packageName = "", - Broker* broker = 0, - Agent* agent = 0); - void getObjects(std::vector& objects, - const std::map& query, - Broker* broker = 0, - Agent* agent = 0); - }; -} - -#endif - diff --git a/qpid/cpp/src/qmf/ConsoleEngine.cpp b/qpid/cpp/src/qmf/ConsoleEngine.cpp new file mode 100644 index 0000000000..3d1b378b68 --- /dev/null +++ b/qpid/cpp/src/qmf/ConsoleEngine.cpp @@ -0,0 +1,886 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/ConsoleEngine.h" +#include "qmf/MessageImpl.h" +#include "qmf/SchemaImpl.h" +#include "qmf/Typecode.h" +#include "qmf/ObjectImpl.h" +#include "qmf/ObjectIdImpl.h" +#include "qmf/QueryImpl.h" +#include "qmf/ValueImpl.h" +#include "qmf/Protocol.h" +#include "qmf/SequenceManager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace qmf; +using namespace qpid::framing; +using namespace qpid::sys; + +namespace qmf { + + struct MethodResponseImpl { + typedef boost::shared_ptr Ptr; + MethodResponse* envelope; + uint32_t status; + auto_ptr exception; + auto_ptr arguments; + + MethodResponseImpl(Buffer& buf); + ~MethodResponseImpl() {} + uint32_t getStatus() const { return status; } + const Value* getException() const { return exception.get(); } + const Value* getArgs() const { return arguments.get(); } + }; + + struct ConsoleEventImpl { + typedef boost::shared_ptr Ptr; + ConsoleEvent::EventKind kind; + boost::shared_ptr agent; + string name; + boost::shared_ptr classKey; + Object* object; + void* context; + Event* event; + uint64_t timestamp; + uint32_t methodHandle; + MethodResponseImpl::Ptr methodResponse; + + ConsoleEventImpl(ConsoleEvent::EventKind k) : + kind(k), object(0), context(0), event(0), timestamp(0), methodHandle(0) {} + ~ConsoleEventImpl() {} + ConsoleEvent copy(); + }; + + struct BrokerEventImpl { + typedef boost::shared_ptr Ptr; + BrokerEvent::EventKind kind; + string name; + string exchange; + string bindingKey; + + BrokerEventImpl(BrokerEvent::EventKind k) : kind(k) {} + ~BrokerEventImpl() {} + BrokerEvent copy(); + }; + + class BrokerProxyImpl : public SequenceContext { + public: + typedef boost::shared_ptr Ptr; + + BrokerProxyImpl(BrokerProxy* e, ConsoleEngine& _console); + ~BrokerProxyImpl() {} + + void sessionOpened(SessionHandle& sh); + void sessionClosed(); + void startProtocol(); + + void sendBufferLH(Buffer& buf, const string& destination, const string& routingKey); + void handleRcvMessage(Message& message); + bool getXmtMessage(Message& item) const; + void popXmt(); + + bool getEvent(BrokerEvent& event) const; + void popEvent(); + + // From SequenceContext + void complete(); + + void addBinding(const string& exchange, const string& key); + + private: + mutable Mutex lock; + BrokerProxy* envelope; + ConsoleEngineImpl* console; + string queueName; + Uuid brokerId; + SequenceManager seqMgr; + uint32_t requestsOutstanding; + bool topicBound; + deque xmtQueue; + deque eventQueue; + +# define MA_BUFFER_SIZE 65536 + char outputBuffer[MA_BUFFER_SIZE]; + + BrokerEventImpl::Ptr eventDeclareQueue(const string& queueName); + BrokerEventImpl::Ptr eventBind(const string& exchange, const string& queue, const string& key); + BrokerEventImpl::Ptr eventSetupComplete(); + BrokerEventImpl::Ptr eventStable(); + + void handleBrokerResponse(Buffer& inBuffer, uint32_t seq); + void handlePackageIndication(Buffer& inBuffer, uint32_t seq); + void handleCommandComplete(Buffer& inBuffer, uint32_t seq); + void handleClassIndication(Buffer& inBuffer, uint32_t seq); + void handleMethodResponse(Buffer& inBuffer, uint32_t seq); + void handleHeartbeatIndication(Buffer& inBuffer, uint32_t seq); + void handleEventIndication(Buffer& inBuffer, uint32_t seq); + void handleSchemaResponse(Buffer& inBuffer, uint32_t seq); + void handleObjectIndication(Buffer& inBuffer, uint32_t seq, bool prop, bool stat); + void incOutstandingLH(); + void decOutstanding(); + }; + + struct AgentProxyImpl { + typedef boost::shared_ptr Ptr; + AgentProxy* envelope; + ConsoleEngineImpl* console; + + AgentProxyImpl(AgentProxy* e, ConsoleEngine& _console) : + envelope(e), console(_console.impl) {} + ~AgentProxyImpl() {} + }; + + class ConsoleEngineImpl { + public: + ConsoleEngineImpl(ConsoleEngine* e, const ConsoleSettings& settings = ConsoleSettings()); + ~ConsoleEngineImpl(); + + bool getEvent(ConsoleEvent& event) const; + void popEvent(); + + void addConnection(BrokerProxy& broker, void* context); + void delConnection(BrokerProxy& broker); + + uint32_t packageCount() const; + const string& getPackageName(uint32_t idx) const; + + uint32_t classCount(const char* packageName) const; + const SchemaClassKey* getClass(const char* packageName, uint32_t idx) const; + + ClassKind getClassKind(const SchemaClassKey* key) const; + const SchemaObjectClass* getObjectClass(const SchemaClassKey* key) const; + const SchemaEventClass* getEventClass(const SchemaClassKey* key) const; + + void bindPackage(const char* packageName); + void bindClass(const SchemaClassKey* key); + void bindClass(const char* packageName, const char* className); + + uint32_t agentCount() const; + const AgentProxy* getAgent(uint32_t idx) const; + + void sendQuery(const Query& query, void* context); + + /* + void startSync(const Query& query, void* context, SyncQuery& sync); + void touchSync(SyncQuery& sync); + void endSync(SyncQuery& sync); + */ + + private: + friend class BrokerProxyImpl; + ConsoleEngine* envelope; + const ConsoleSettings& settings; + mutable Mutex lock; + deque eventQueue; + vector brokerList; + vector > bindingList; // exchange/key (empty exchange => QMF_EXCHANGE) + + // Declare a compare class for the class maps that compares the dereferenced + // class key pointers. The default behavior would be to compare the pointer + // addresses themselves. + struct KeyCompare { + bool operator()(const SchemaClassKeyImpl* left, const SchemaClassKeyImpl* right) const { + return *left < *right; + } + }; + + typedef map ObjectClassList; + typedef map EventClassList; + typedef map > PackageList; + + PackageList packages; + + void learnPackage(const string& packageName); + void learnClass(SchemaObjectClassImpl::Ptr cls); + void learnClass(SchemaEventClassImpl::Ptr cls); + bool haveClass(const SchemaClassKeyImpl& key) const; + }; +} + +namespace { +const char* QMF_EXCHANGE = "qpid.management"; +const char* DIR_EXCHANGE = "amq.direct"; +const char* BROKER_KEY = "broker"; +} + +#define STRING_REF(s) {if (!s.empty()) item.s = const_cast(s.c_str());} + +ConsoleEvent ConsoleEventImpl::copy() +{ + ConsoleEvent item; + + ::memset(&item, 0, sizeof(ConsoleEvent)); + item.kind = kind; + item.agent = agent.get() ? agent->envelope : 0; + item.classKey = classKey.get(); + item.object = object; + item.context = context; + item.event = event; + item.timestamp = timestamp; + item.methodHandle = methodHandle; + item.methodResponse = methodResponse.get() ? methodResponse->envelope : 0; + + STRING_REF(name); + + return item; +} + +BrokerEvent BrokerEventImpl::copy() +{ + BrokerEvent item; + + ::memset(&item, 0, sizeof(BrokerEvent)); + item.kind = kind; + + STRING_REF(name); + STRING_REF(exchange); + STRING_REF(bindingKey); + + return item; +} + +BrokerProxyImpl::BrokerProxyImpl(BrokerProxy* e, ConsoleEngine& _console) : + envelope(e), console(_console.impl), queueName("qmfc-") +{ + // TODO: Give the queue name a unique suffix +} + +void BrokerProxyImpl::sessionOpened(SessionHandle& /*sh*/) +{ + Mutex::ScopedLock _lock(lock); + eventQueue.clear(); + xmtQueue.clear(); + eventQueue.push_back(eventDeclareQueue(queueName)); + eventQueue.push_back(eventBind(DIR_EXCHANGE, queueName, queueName)); + eventQueue.push_back(eventSetupComplete()); + + // TODO: Store session handle +} + +void BrokerProxyImpl::sessionClosed() +{ + Mutex::ScopedLock _lock(lock); + eventQueue.clear(); + xmtQueue.clear(); +} + +void BrokerProxyImpl::startProtocol() +{ + Mutex::ScopedLock _lock(lock); + char rawbuffer[512]; + Buffer buffer(rawbuffer, 512); + + requestsOutstanding = 1; + topicBound = false; + Protocol::encodeHeader(buffer, Protocol::OP_BROKER_REQUEST); + sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT BrokerRequest"); +} + +void BrokerProxyImpl::sendBufferLH(Buffer& buf, const string& destination, const string& routingKey) +{ + uint32_t length = buf.getPosition(); + MessageImpl::Ptr message(new MessageImpl); + + buf.reset(); + buf.getRawData(message->body, length); + message->destination = destination; + message->routingKey = routingKey; + message->replyExchange = DIR_EXCHANGE; + message->replyKey = queueName; + + xmtQueue.push_back(message); +} + +void BrokerProxyImpl::handleRcvMessage(Message& message) +{ + Buffer inBuffer(message.body, message.length); + uint8_t opcode; + uint32_t sequence; + + while (Protocol::checkHeader(inBuffer, &opcode, &sequence)) { + if (opcode == Protocol::OP_BROKER_RESPONSE) handleBrokerResponse(inBuffer, sequence); + else if (opcode == Protocol::OP_PACKAGE_INDICATION) handlePackageIndication(inBuffer, sequence); + else if (opcode == Protocol::OP_COMMAND_COMPLETE) handleCommandComplete(inBuffer, sequence); + else if (opcode == Protocol::OP_CLASS_INDICATION) handleClassIndication(inBuffer, sequence); + else if (opcode == Protocol::OP_METHOD_RESPONSE) handleMethodResponse(inBuffer, sequence); + else if (opcode == Protocol::OP_HEARTBEAT_INDICATION) handleHeartbeatIndication(inBuffer, sequence); + else if (opcode == Protocol::OP_EVENT_INDICATION) handleEventIndication(inBuffer, sequence); + else if (opcode == Protocol::OP_SCHEMA_RESPONSE) handleSchemaResponse(inBuffer, sequence); + else if (opcode == Protocol::OP_PROPERTY_INDICATION) handleObjectIndication(inBuffer, sequence, true, false); + else if (opcode == Protocol::OP_STATISTIC_INDICATION) handleObjectIndication(inBuffer, sequence, false, true); + else if (opcode == Protocol::OP_OBJECT_INDICATION) handleObjectIndication(inBuffer, sequence, true, true); + else { + QPID_LOG(trace, "BrokerProxyImpl::handleRcvMessage invalid opcode: " << opcode); + break; + } + } +} + +bool BrokerProxyImpl::getXmtMessage(Message& item) const +{ + Mutex::ScopedLock _lock(lock); + if (xmtQueue.empty()) + return false; + item = xmtQueue.front()->copy(); + return true; +} + +void BrokerProxyImpl::popXmt() +{ + Mutex::ScopedLock _lock(lock); + if (!xmtQueue.empty()) + xmtQueue.pop_front(); +} + +bool BrokerProxyImpl::getEvent(BrokerEvent& event) const +{ + Mutex::ScopedLock _lock(lock); + if (eventQueue.empty()) + return false; + event = eventQueue.front()->copy(); + return true; +} + +void BrokerProxyImpl::popEvent() +{ + Mutex::ScopedLock _lock(lock); + if (!eventQueue.empty()) + eventQueue.pop_front(); +} + +void BrokerProxyImpl::complete() +{ + decOutstanding(); +} + +void BrokerProxyImpl::addBinding(const string& exchange, const string& key) +{ + eventQueue.push_back(eventBind(exchange, queueName, key)); +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventDeclareQueue(const string& queueName) +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::DECLARE_QUEUE)); + event->name = queueName; + return event; +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventBind(const string& exchange, const string& queue, const string& key) +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::BIND)); + event->name = queue; + event->exchange = exchange; + event->bindingKey = key; + + return event; +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventSetupComplete() +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::SETUP_COMPLETE)); + return event; +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventStable() +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::STABLE)); + return event; +} + +void BrokerProxyImpl::handleBrokerResponse(Buffer& inBuffer, uint32_t seq) +{ + // Note that this function doesn't touch requestsOutstanding. This is because + // it accounts for one request completed (the BrokerRequest) and one request + // started (the PackageRequest) which cancel each other out. + + brokerId.decode(inBuffer); + QPID_LOG(trace, "RCVD BrokerResponse seq=" << seq << " brokerId=" << brokerId); + Mutex::ScopedLock _lock(lock); + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve(this)); + Protocol::encodeHeader(outBuffer, Protocol::OP_PACKAGE_REQUEST, sequence); + sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT PackageRequest seq=" << sequence); +} + +void BrokerProxyImpl::handlePackageIndication(Buffer& inBuffer, uint32_t seq) +{ + string package; + + inBuffer.getShortString(package); + QPID_LOG(trace, "RCVD PackageIndication seq=" << seq << " package=" << package); + console->learnPackage(package); + + Mutex::ScopedLock _lock(lock); + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve(this)); + incOutstandingLH(); + Protocol::encodeHeader(outBuffer, Protocol::OP_CLASS_QUERY, sequence); + outBuffer.putShortString(package); + sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT ClassQuery seq=" << sequence << " package=" << package); +} + +void BrokerProxyImpl::handleCommandComplete(Buffer& inBuffer, uint32_t seq) +{ + string text; + uint32_t code = inBuffer.getLong(); + inBuffer.getShortString(text); + QPID_LOG(trace, "RCVD CommandComplete seq=" << seq << " code=" << code << " text=" << text); + seqMgr.release(seq); +} + +void BrokerProxyImpl::handleClassIndication(Buffer& inBuffer, uint32_t seq) +{ + string package; + string clsName; + SchemaHash hash; + uint8_t kind = inBuffer.getOctet(); + inBuffer.getShortString(package); + inBuffer.getShortString(clsName); + hash.decode(inBuffer); + Uuid printableHash(hash.get()); + SchemaClassKeyImpl classKey(package, clsName, hash); + + QPID_LOG(trace, "RCVD ClassIndication seq=" << seq << " kind=" << (int) kind << " key=" << classKey.str()); + + if (!console->haveClass(classKey)) { + Mutex::ScopedLock _lock(lock); + incOutstandingLH(); + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve(this)); + Protocol::encodeHeader(outBuffer, Protocol::OP_SCHEMA_REQUEST, sequence); + classKey.encode(outBuffer); + sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT SchemaRequest seq=" << sequence <<" key=" << classKey.str()); + } +} + +void BrokerProxyImpl::handleMethodResponse(Buffer& /*inBuffer*/, uint32_t /*seq*/) +{ + // TODO +} + +void BrokerProxyImpl::handleHeartbeatIndication(Buffer& /*inBuffer*/, uint32_t /*seq*/) +{ + // TODO +} + +void BrokerProxyImpl::handleEventIndication(Buffer& /*inBuffer*/, uint32_t /*seq*/) +{ + // TODO +} + +void BrokerProxyImpl::handleSchemaResponse(Buffer& inBuffer, uint32_t seq) +{ + SchemaObjectClassImpl::Ptr oClassPtr; + SchemaEventClassImpl::Ptr eClassPtr; + uint8_t kind = inBuffer.getOctet(); + const SchemaClassKeyImpl* key; + if (kind == CLASS_OBJECT) { + oClassPtr.reset(new SchemaObjectClassImpl(inBuffer)); + console->learnClass(oClassPtr); + key = oClassPtr->getClassKey()->impl; + QPID_LOG(trace, "RCVD SchemaResponse seq=" << seq << " kind=object key=" << key->str()); + } else if (kind == CLASS_EVENT) { + eClassPtr.reset(new SchemaEventClassImpl(inBuffer)); + console->learnClass(eClassPtr); + key = eClassPtr->getClassKey()->impl; + QPID_LOG(trace, "RCVD SchemaResponse seq=" << seq << " kind=event key=" << key->str()); + } + else { + QPID_LOG(error, "BrokerProxyImpl::handleSchemaResponse received unknown class kind: " << (int) kind); + } + + decOutstanding(); +} + +void BrokerProxyImpl::handleObjectIndication(Buffer& /*inBuffer*/, uint32_t /*seq*/, bool /*prop*/, bool /*stat*/) +{ + // TODO +} + +void BrokerProxyImpl::incOutstandingLH() +{ + requestsOutstanding++; +} + +void BrokerProxyImpl::decOutstanding() +{ + Mutex::ScopedLock _lock(lock); + requestsOutstanding--; + if (requestsOutstanding == 0 && !topicBound) { + topicBound = true; + for (vector >::const_iterator iter = console->bindingList.begin(); + iter != console->bindingList.end(); iter++) { + string exchange(iter->first.empty() ? QMF_EXCHANGE : iter->first); + string key(iter->second); + eventQueue.push_back(eventBind(exchange, queueName, key)); + } + eventQueue.push_back(eventStable()); + } +} + +MethodResponseImpl::MethodResponseImpl(Buffer& buf) : envelope(new MethodResponse(this)) +{ + string text; + + status = buf.getLong(); + buf.getMediumString(text); + exception.reset(new Value(TYPE_LSTR)); + exception->setString(text.c_str()); + + // TODO: Parse schema-specific output arguments. + arguments.reset(new Value(TYPE_MAP)); +} + +ConsoleEngineImpl::ConsoleEngineImpl(ConsoleEngine* e, const ConsoleSettings& s) : + envelope(e), settings(s) +{ + bindingList.push_back(pair(string(), "schema.#")); + if (settings.rcvObjects && settings.rcvEvents && settings.rcvHeartbeats && !settings.userBindings) { + bindingList.push_back(pair(string(), "console.#")); + } else { + if (settings.rcvObjects && !settings.userBindings) + bindingList.push_back(pair(string(), "console.obj.#")); + else + bindingList.push_back(pair(string(), "console.obj.*.*.org.apache.qpid.broker.agent")); + if (settings.rcvEvents) + bindingList.push_back(pair(string(), "console.event.#")); + if (settings.rcvHeartbeats) + bindingList.push_back(pair(string(), "console.heartbeat.#")); + } +} + +ConsoleEngineImpl::~ConsoleEngineImpl() +{ + // This function intentionally left blank. +} + +bool ConsoleEngineImpl::getEvent(ConsoleEvent& event) const +{ + Mutex::ScopedLock _lock(lock); + if (eventQueue.empty()) + return false; + event = eventQueue.front()->copy(); + return true; +} + +void ConsoleEngineImpl::popEvent() +{ + Mutex::ScopedLock _lock(lock); + if (!eventQueue.empty()) + eventQueue.pop_front(); +} + +void ConsoleEngineImpl::addConnection(BrokerProxy& broker, void* /*context*/) +{ + Mutex::ScopedLock _lock(lock); + brokerList.push_back(broker.impl); +} + +void ConsoleEngineImpl::delConnection(BrokerProxy& broker) +{ + Mutex::ScopedLock _lock(lock); + for (vector::iterator iter = brokerList.begin(); + iter != brokerList.end(); iter++) + if (*iter == broker.impl) { + brokerList.erase(iter); + break; + } +} + +uint32_t ConsoleEngineImpl::packageCount() const +{ + Mutex::ScopedLock _lock(lock); + return packages.size(); +} + +const string& ConsoleEngineImpl::getPackageName(uint32_t idx) const +{ + const static string empty; + + Mutex::ScopedLock _lock(lock); + if (idx >= packages.size()) + return empty; + + PackageList::const_iterator iter = packages.begin(); + for (uint32_t i = 0; i < idx; i++) iter++; + return iter->first; +} + +uint32_t ConsoleEngineImpl::classCount(const char* packageName) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(packageName); + if (pIter == packages.end()) + return 0; + + const ObjectClassList& oList = pIter->second.first; + const EventClassList& eList = pIter->second.second; + + return oList.size() + eList.size(); +} + +const SchemaClassKey* ConsoleEngineImpl::getClass(const char* packageName, uint32_t idx) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(packageName); + if (pIter == packages.end()) + return 0; + + const ObjectClassList& oList = pIter->second.first; + const EventClassList& eList = pIter->second.second; + uint32_t count = 0; + + for (ObjectClassList::const_iterator oIter = oList.begin(); + oIter != oList.end(); oIter++) { + if (count == idx) + return oIter->second->getClassKey(); + count++; + } + + for (EventClassList::const_iterator eIter = eList.begin(); + eIter != eList.end(); eIter++) { + if (count == idx) + return eIter->second->getClassKey(); + count++; + } + + return 0; +} + +ClassKind ConsoleEngineImpl::getClassKind(const SchemaClassKey* key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return CLASS_OBJECT; + + const EventClassList& eList = pIter->second.second; + if (eList.find(key->impl) != eList.end()) + return CLASS_EVENT; + return CLASS_OBJECT; +} + +const SchemaObjectClass* ConsoleEngineImpl::getObjectClass(const SchemaClassKey* key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return 0; + + const ObjectClassList& oList = pIter->second.first; + ObjectClassList::const_iterator iter = oList.find(key->impl); + if (iter == oList.end()) + return 0; + return iter->second->envelope; +} + +const SchemaEventClass* ConsoleEngineImpl::getEventClass(const SchemaClassKey* key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return 0; + + const EventClassList& eList = pIter->second.second; + EventClassList::const_iterator iter = eList.find(key->impl); + if (iter == eList.end()) + return 0; + return iter->second->envelope; +} + +void ConsoleEngineImpl::bindPackage(const char* packageName) +{ + stringstream key; + key << "console.obj.*.*." << packageName << ".#"; + Mutex::ScopedLock _lock(lock); + bindingList.push_back(pair(string(), key.str())); + for (vector::iterator iter = brokerList.begin(); + iter != brokerList.end(); iter++) + (*iter)->addBinding(QMF_EXCHANGE, key.str()); +} + +void ConsoleEngineImpl::bindClass(const SchemaClassKey* classKey) +{ + stringstream key; + key << "console.obj.*.*." << classKey->getPackageName() << "." << classKey->getClassName() << ".#"; + Mutex::ScopedLock _lock(lock); + bindingList.push_back(pair(string(), key.str())); + for (vector::iterator iter = brokerList.begin(); + iter != brokerList.end(); iter++) + (*iter)->addBinding(QMF_EXCHANGE, key.str()); +} + +void ConsoleEngineImpl::bindClass(const char* packageName, const char* className) +{ + stringstream key; + key << "console.obj.*.*." << packageName << "." << className << ".#"; + Mutex::ScopedLock _lock(lock); + bindingList.push_back(pair(string(), key.str())); + for (vector::iterator iter = brokerList.begin(); + iter != brokerList.end(); iter++) + (*iter)->addBinding(QMF_EXCHANGE, key.str()); +} + +uint32_t ConsoleEngineImpl::agentCount() const +{ + // TODO + return 0; +} + +const AgentProxy* ConsoleEngineImpl::getAgent(uint32_t /*idx*/) const +{ + // TODO + return 0; +} + +void ConsoleEngineImpl::sendQuery(const Query& /*query*/, void* /*context*/) +{ + // TODO +} + +/* +void ConsoleEngineImpl::startSync(const Query& query, void* context, SyncQuery& sync) +{ +} + +void ConsoleEngineImpl::touchSync(SyncQuery& sync) +{ +} + +void ConsoleEngineImpl::endSync(SyncQuery& sync) +{ +} +*/ + +void ConsoleEngineImpl::learnPackage(const string& packageName) +{ + Mutex::ScopedLock _lock(lock); + if (packages.find(packageName) == packages.end()) + packages.insert(pair > + (packageName, pair(ObjectClassList(), EventClassList()))); +} + +void ConsoleEngineImpl::learnClass(SchemaObjectClassImpl::Ptr cls) +{ + Mutex::ScopedLock _lock(lock); + const SchemaClassKey* key = cls->getClassKey(); + PackageList::iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return; + + ObjectClassList& list = pIter->second.first; + if (list.find(key->impl) == list.end()) + list[key->impl] = cls; +} + +void ConsoleEngineImpl::learnClass(SchemaEventClassImpl::Ptr cls) +{ + Mutex::ScopedLock _lock(lock); + const SchemaClassKey* key = cls->getClassKey(); + PackageList::iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return; + + EventClassList& list = pIter->second.second; + if (list.find(key->impl) == list.end()) + list[key->impl] = cls; +} + +bool ConsoleEngineImpl::haveClass(const SchemaClassKeyImpl& key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key.getPackageName()); + if (pIter == packages.end()) + return false; + + const ObjectClassList& oList = pIter->second.first; + const EventClassList& eList = pIter->second.second; + + return oList.find(&key) != oList.end() || eList.find(&key) != eList.end(); +} + + +//================================================================== +// Wrappers +//================================================================== + +BrokerProxy::BrokerProxy(ConsoleEngine& console) : impl(new BrokerProxyImpl(this, console)) {} +BrokerProxy::~BrokerProxy() { delete impl; } +void BrokerProxy::sessionOpened(SessionHandle& sh) { impl->sessionOpened(sh); } +void BrokerProxy::sessionClosed() { impl->sessionClosed(); } +void BrokerProxy::startProtocol() { impl->startProtocol(); } +void BrokerProxy::handleRcvMessage(Message& message) { impl->handleRcvMessage(message); } +bool BrokerProxy::getXmtMessage(Message& item) const { return impl->getXmtMessage(item); } +void BrokerProxy::popXmt() { impl->popXmt(); } +bool BrokerProxy::getEvent(BrokerEvent& event) const { return impl->getEvent(event); } +void BrokerProxy::popEvent() { impl->popEvent(); } + +AgentProxy::AgentProxy(ConsoleEngine& console) : impl(new AgentProxyImpl(this, console)) {} +AgentProxy::~AgentProxy() { delete impl; } + +MethodResponse::MethodResponse(MethodResponseImpl* i) : impl(i) {} +MethodResponse::~MethodResponse() { delete impl; } // TODO: correct to delete here? +uint32_t MethodResponse::getStatus() const { return impl->getStatus(); } +const Value* MethodResponse::getException() const { return impl->getException(); } +const Value* MethodResponse::getArgs() const { return impl->getArgs(); } + +ConsoleEngine::ConsoleEngine(const ConsoleSettings& settings) : impl(new ConsoleEngineImpl(this, settings)) {} +ConsoleEngine::~ConsoleEngine() { delete impl; } +bool ConsoleEngine::getEvent(ConsoleEvent& event) const { return impl->getEvent(event); } +void ConsoleEngine::popEvent() { impl->popEvent(); } +void ConsoleEngine::addConnection(BrokerProxy& broker, void* context) { impl->addConnection(broker, context); } +void ConsoleEngine::delConnection(BrokerProxy& broker) { impl->delConnection(broker); } +uint32_t ConsoleEngine::packageCount() const { return impl->packageCount(); } +const char* ConsoleEngine::getPackageName(uint32_t idx) const { return impl->getPackageName(idx).c_str(); } +uint32_t ConsoleEngine::classCount(const char* packageName) const { return impl->classCount(packageName); } +const SchemaClassKey* ConsoleEngine::getClass(const char* packageName, uint32_t idx) const { return impl->getClass(packageName, idx); } +ClassKind ConsoleEngine::getClassKind(const SchemaClassKey* key) const { return impl->getClassKind(key); } +const SchemaObjectClass* ConsoleEngine::getObjectClass(const SchemaClassKey* key) const { return impl->getObjectClass(key); } +const SchemaEventClass* ConsoleEngine::getEventClass(const SchemaClassKey* key) const { return impl->getEventClass(key); } +void ConsoleEngine::bindPackage(const char* packageName) { impl->bindPackage(packageName); } +void ConsoleEngine::bindClass(const SchemaClassKey* key) { impl->bindClass(key); } +void ConsoleEngine::bindClass(const char* packageName, const char* className) { impl->bindClass(packageName, className); } +uint32_t ConsoleEngine::agentCount() const { return impl->agentCount(); } +const AgentProxy* ConsoleEngine::getAgent(uint32_t idx) const { return impl->getAgent(idx); } +void ConsoleEngine::sendQuery(const Query& query, void* context) { impl->sendQuery(query, context); } +//void ConsoleEngine::startSync(const Query& query, void* context, SyncQuery& sync) { impl->startSync(query, context, sync); } +//void ConsoleEngine::touchSync(SyncQuery& sync) { impl->touchSync(sync); } +//void ConsoleEngine::endSync(SyncQuery& sync) { impl->endSync(sync); } + + diff --git a/qpid/cpp/src/qmf/ConsoleEngine.h b/qpid/cpp/src/qmf/ConsoleEngine.h new file mode 100644 index 0000000000..84ac78cd69 --- /dev/null +++ b/qpid/cpp/src/qmf/ConsoleEngine.h @@ -0,0 +1,200 @@ +#ifndef _QmfConsoleEngine_ +#define _QmfConsoleEngine_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace qmf { + + class ConsoleEngine; + class ConsoleEngineImpl; + class BrokerProxyImpl; + class AgentProxy; + class AgentProxyImpl; + class MethodResponseImpl; + + /** + * + */ + class MethodResponse { + public: + MethodResponse(MethodResponseImpl* impl); + ~MethodResponse(); + uint32_t getStatus() const; + const Value* getException() const; + const Value* getArgs() const; + + private: + friend class ConsoleEngineImpl; + MethodResponseImpl* impl; + }; + + /** + * + */ + struct ConsoleEvent { + enum EventKind { + AGENT_ADDED = 1, + AGENT_DELETED = 2, + NEW_PACKAGE = 3, + NEW_CLASS = 4, + OBJECT_UPDATE = 5, + QUERY_COMPLETE = 6, + EVENT_RECEIVED = 7, + AGENT_HEARTBEAT = 8, + METHOD_RESPONSE = 9 + }; + + EventKind kind; + AgentProxy* agent; // (AGENT_[ADDED|DELETED|HEARTBEAT]) + char* name; // (NEW_PACKAGE) + SchemaClassKey* classKey; // (NEW_CLASS) + Object* object; // (OBJECT_UPDATE) + void* context; // (OBJECT_UPDATE, QUERY_COMPLETE) + Event* event; // (EVENT_RECEIVED) + uint64_t timestamp; // (AGENT_HEARTBEAT) + uint32_t methodHandle; // (METHOD_RESPONSE) + MethodResponse* methodResponse; // (METHOD_RESPONSE) + }; + + /** + * + */ + struct BrokerEvent { + enum EventKind { + BROKER_INFO = 10, + DECLARE_QUEUE = 11, + DELETE_QUEUE = 12, + BIND = 13, + UNBIND = 14, + SETUP_COMPLETE = 15, + STABLE = 16 + }; + + EventKind kind; + char* name; // ([DECLARE|DELETE]_QUEUE, [UN]BIND) + char* exchange; // ([UN]BIND) + char* bindingKey; // ([UN]BIND) + }; + + /** + * + */ + class BrokerProxy { + public: + BrokerProxy(ConsoleEngine& console); + ~BrokerProxy(); + + void sessionOpened(SessionHandle& sh); + void sessionClosed(); + void startProtocol(); + + void handleRcvMessage(Message& message); + bool getXmtMessage(Message& item) const; + void popXmt(); + + bool getEvent(BrokerEvent& event) const; + void popEvent(); + + private: + friend class ConsoleEngineImpl; + BrokerProxyImpl* impl; + }; + + /** + * + */ + class AgentProxy { + public: + AgentProxy(ConsoleEngine& console); + ~AgentProxy(); + + private: + friend class ConsoleEngineImpl; + AgentProxyImpl* impl; + }; + + // TODO - move this to a public header + struct ConsoleSettings { + bool rcvObjects; + bool rcvEvents; + bool rcvHeartbeats; + bool userBindings; + + ConsoleSettings() : + rcvObjects(true), + rcvEvents(true), + rcvHeartbeats(true), + userBindings(false) {} + }; + + class ConsoleEngine { + public: + ConsoleEngine(const ConsoleSettings& settings = ConsoleSettings()); + ~ConsoleEngine(); + + bool getEvent(ConsoleEvent& event) const; + void popEvent(); + + void addConnection(BrokerProxy& broker, void* context); + void delConnection(BrokerProxy& broker); + + uint32_t packageCount() const; + const char* getPackageName(uint32_t idx) const; + + uint32_t classCount(const char* packageName) const; + const SchemaClassKey* getClass(const char* packageName, uint32_t idx) const; + + ClassKind getClassKind(const SchemaClassKey* key) const; + const SchemaObjectClass* getObjectClass(const SchemaClassKey* key) const; + const SchemaEventClass* getEventClass(const SchemaClassKey* key) const; + + void bindPackage(const char* packageName); + void bindClass(const SchemaClassKey* key); + void bindClass(const char* packageName, const char* className); + + uint32_t agentCount() const; + const AgentProxy* getAgent(uint32_t idx) const; + + void sendQuery(const Query& query, void* context); + + /* + void startSync(const Query& query, void* context, SyncQuery& sync); + void touchSync(SyncQuery& sync); + void endSync(SyncQuery& sync); + */ + + private: + friend class BrokerProxyImpl; + friend class AgentProxyImpl; + ConsoleEngineImpl* impl; + }; +} + +#endif + diff --git a/qpid/cpp/src/qmf/Object.h b/qpid/cpp/src/qmf/Object.h index 8caab8d6dc..eb92cbbe45 100644 --- a/qpid/cpp/src/qmf/Object.h +++ b/qpid/cpp/src/qmf/Object.h @@ -31,7 +31,7 @@ namespace qmf { public: Object(const SchemaObjectClass* type); Object(ObjectImpl* impl); - ~Object(); + virtual ~Object(); void destroy(); const ObjectId* getObjectId() const; diff --git a/qpid/cpp/src/qmf/ObjectId.h b/qpid/cpp/src/qmf/ObjectId.h index 1ceae20bd8..ffd1b6978b 100644 --- a/qpid/cpp/src/qmf/ObjectId.h +++ b/qpid/cpp/src/qmf/ObjectId.h @@ -39,7 +39,6 @@ namespace qmf { bool isDurable() const; bool operator==(const ObjectId& other) const; - bool operator!=(const ObjectId& other) const; bool operator<(const ObjectId& other) const; bool operator>(const ObjectId& other) const; bool operator<=(const ObjectId& other) const; diff --git a/qpid/cpp/src/qmf/ObjectIdImpl.cpp b/qpid/cpp/src/qmf/ObjectIdImpl.cpp index efa8e7119b..75661fdb47 100644 --- a/qpid/cpp/src/qmf/ObjectIdImpl.cpp +++ b/qpid/cpp/src/qmf/ObjectIdImpl.cpp @@ -166,11 +166,6 @@ bool ObjectId::operator==(const ObjectId& other) const return *impl == *other.impl; } -bool ObjectId::operator!=(const ObjectId& other) const -{ - return !(*impl == *other.impl); -} - bool ObjectId::operator<(const ObjectId& other) const { return *impl < *other.impl; diff --git a/qpid/cpp/src/qmf/ObjectImpl.cpp b/qpid/cpp/src/qmf/ObjectImpl.cpp index d3882935e4..645ccd5c81 100644 --- a/qpid/cpp/src/qmf/ObjectImpl.cpp +++ b/qpid/cpp/src/qmf/ObjectImpl.cpp @@ -123,9 +123,9 @@ void ObjectImpl::parsePresenceMasks(Buffer& buffer, set& excludeList) void ObjectImpl::encodeSchemaKey(qpid::framing::Buffer& buffer) const { - buffer.putShortString(objectClass->getPackage()); - buffer.putShortString(objectClass->getName()); - buffer.putBin128(const_cast(objectClass->getHash())); + buffer.putShortString(objectClass->getClassKey()->getPackageName()); + buffer.putShortString(objectClass->getClassKey()->getClassName()); + buffer.putBin128(const_cast(objectClass->getClassKey()->getHash())); } void ObjectImpl::encodeManagedObjectData(qpid::framing::Buffer& buffer) const diff --git a/qpid/cpp/src/qmf/Protocol.cpp b/qpid/cpp/src/qmf/Protocol.cpp new file mode 100644 index 0000000000..0a3beeb276 --- /dev/null +++ b/qpid/cpp/src/qmf/Protocol.cpp @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/Protocol.h" +#include "qpid/framing/Buffer.h" + +using namespace std; +using namespace qmf; +using namespace qpid::framing; + + +bool Protocol::checkHeader(Buffer& buf, uint8_t *opcode, uint32_t *seq) +{ + if (buf.available() < 8) + return false; + + uint8_t h1 = buf.getOctet(); + uint8_t h2 = buf.getOctet(); + uint8_t h3 = buf.getOctet(); + + *opcode = buf.getOctet(); + *seq = buf.getLong(); + + return h1 == 'A' && h2 == 'M' && h3 == '3'; +} + +void Protocol::encodeHeader(qpid::framing::Buffer& buf, uint8_t opcode, uint32_t seq) +{ + buf.putOctet('A'); + buf.putOctet('M'); + buf.putOctet('3'); + buf.putOctet(opcode); + buf.putLong (seq); +} + + diff --git a/qpid/cpp/src/qmf/Protocol.h b/qpid/cpp/src/qmf/Protocol.h new file mode 100644 index 0000000000..d5da08c1db --- /dev/null +++ b/qpid/cpp/src/qmf/Protocol.h @@ -0,0 +1,67 @@ +#ifndef _QmfProtocol_ +#define _QmfProtocol_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +namespace qpid { + namespace framing { + class Buffer; + } +} + +namespace qmf { + + class Protocol { + public: + static bool checkHeader(qpid::framing::Buffer& buf, uint8_t *opcode, uint32_t *seq); + static void encodeHeader(qpid::framing::Buffer& buf, uint8_t opcode, uint32_t seq = 0); + + const static uint8_t OP_ATTACH_REQUEST = 'A'; + const static uint8_t OP_ATTACH_RESPONSE = 'a'; + + const static uint8_t OP_BROKER_REQUEST = 'B'; + const static uint8_t OP_BROKER_RESPONSE = 'b'; + + const static uint8_t OP_CONSOLE_ADDED_INDICATION = 'x'; + const static uint8_t OP_COMMAND_COMPLETE = 'z'; + const static uint8_t OP_HEARTBEAT_INDICATION = 'h'; + + const static uint8_t OP_PACKAGE_REQUEST = 'P'; + const static uint8_t OP_PACKAGE_INDICATION = 'p'; + const static uint8_t OP_CLASS_QUERY = 'Q'; + const static uint8_t OP_CLASS_INDICATION = 'q'; + const static uint8_t OP_SCHEMA_REQUEST = 'S'; + const static uint8_t OP_SCHEMA_RESPONSE = 's'; + + const static uint8_t OP_METHOD_REQUEST = 'M'; + const static uint8_t OP_METHOD_RESPONSE = 'm'; + const static uint8_t OP_GET_QUERY = 'G'; + const static uint8_t OP_OBJECT_INDICATION = 'g'; + const static uint8_t OP_PROPERTY_INDICATION = 'c'; + const static uint8_t OP_STATISTIC_INDICATION = 'i'; + const static uint8_t OP_EVENT_INDICATION = 'e'; + }; + +} + +#endif + diff --git a/qpid/cpp/src/qmf/ResilientConnection.cpp b/qpid/cpp/src/qmf/ResilientConnection.cpp index 610306f896..7ec03cf4da 100644 --- a/qpid/cpp/src/qmf/ResilientConnection.cpp +++ b/qpid/cpp/src/qmf/ResilientConnection.cpp @@ -19,6 +19,8 @@ #include "qmf/ResilientConnection.h" #include "qmf/MessageImpl.h" +#include "qmf/ConnectionSettingsImpl.h" +#include #include #include #include @@ -39,7 +41,7 @@ using namespace std; using namespace qmf; -using namespace qpid::client; +using namespace qpid; using qpid::sys::Mutex; namespace qmf { @@ -55,30 +57,29 @@ namespace qmf { ResilientConnectionEvent copy(); }; - struct RCSession : public MessageListener, public qpid::sys::Runnable, public qpid::RefCounted { + struct RCSession : public client::MessageListener, public qpid::sys::Runnable, public qpid::RefCounted { typedef boost::intrusive_ptr Ptr; ResilientConnectionImpl& connImpl; string name; - Connection& connection; - Session session; - SubscriptionManager* subscriptions; + client::Connection& connection; + client::Session session; + client::SubscriptionManager* subscriptions; void* userContext; vector dests; qpid::sys::Thread thread; - RCSession(ResilientConnectionImpl& ci, const string& n, Connection& c, void* uc) : + RCSession(ResilientConnectionImpl& ci, const string& n, client::Connection& c, void* uc) : connImpl(ci), name(n), connection(c), session(connection.newSession(name)), - subscriptions(new SubscriptionManager(session)), userContext(uc), thread(*this) {} + subscriptions(new client::SubscriptionManager(session)), userContext(uc), thread(*this) {} ~RCSession(); - void received(qpid::client::Message& msg); + void received(client::Message& msg); void run(); void stop(); }; class ResilientConnectionImpl : public qpid::sys::Runnable { public: - ResilientConnectionImpl(ConnectionSettings& settings, - int dmin, int dmax, int dfactor); + ResilientConnectionImpl(const ConnectionSettings& settings); ~ResilientConnectionImpl(); bool isConnected() const; @@ -107,8 +108,8 @@ namespace qmf { bool connected; bool shutdown; string lastError; - ConnectionSettings settings; - Connection connection; + const ConnectionSettings settings; + client::Connection connection; mutable qpid::sys::Mutex lock; int delayMin; int delayMax; @@ -155,7 +156,7 @@ void RCSession::stop() subscriptions->stop(); } -void RCSession::received(qpid::client::Message& msg) +void RCSession::received(client::Message& msg) { qmf::MessageImpl qmsg; qmsg.body = msg.getData(); @@ -174,12 +175,11 @@ void RCSession::received(qpid::client::Message& msg) connImpl.EnqueueEvent(ResilientConnectionEvent::RECV, userContext, qmsg); } -ResilientConnectionImpl::ResilientConnectionImpl(ConnectionSettings& _settings, - int dmin, int dmax, int dfactor) : - notifyFd(-1), connected(false), shutdown(false), settings(_settings), - delayMin(dmin), delayMax(dmax), delayFactor(dfactor), connThread(*this) +ResilientConnectionImpl::ResilientConnectionImpl(const ConnectionSettings& _settings) : + notifyFd(-1), connected(false), shutdown(false), settings(_settings), delayMin(1), connThread(*this) { connection.registerFailureCallback(boost::bind(&ResilientConnectionImpl::failure, this)); + settings.impl->getRetrySettings(&delayMin, &delayMax, &delayFactor); } ResilientConnectionImpl::~ResilientConnectionImpl() @@ -222,7 +222,7 @@ bool ResilientConnectionImpl::createSession(const char* name, void* sessionConte RCSession::Ptr sess = RCSession::Ptr(new RCSession(*this, name, connection, sessionContext)); - handle.handle = (void*) sess.get(); + handle.impl = (void*) sess.get(); sessions.insert(sess); return true; @@ -231,7 +231,7 @@ bool ResilientConnectionImpl::createSession(const char* name, void* sessionConte void ResilientConnectionImpl::destroySession(SessionHandle handle) { Mutex::ScopedLock _lock(lock); - RCSession::Ptr sess = RCSession::Ptr((RCSession*) handle.handle); + RCSession::Ptr sess = RCSession::Ptr((RCSession*) handle.impl); set::iterator iter = sessions.find(sess); if (iter != sessions.end()) { for (vector::iterator dIter = sess->dests.begin(); dIter != sess->dests.end(); dIter++) @@ -247,7 +247,7 @@ void ResilientConnectionImpl::destroySession(SessionHandle handle) void ResilientConnectionImpl::sendMessage(SessionHandle handle, qmf::Message& message) { Mutex::ScopedLock _lock(lock); - RCSession::Ptr sess = RCSession::Ptr((RCSession*) handle.handle); + RCSession::Ptr sess = RCSession::Ptr((RCSession*) handle.impl); set::iterator iter = sessions.find(sess); qpid::client::Message msg; string data(message.body, message.length); @@ -256,7 +256,7 @@ void ResilientConnectionImpl::sendMessage(SessionHandle handle, qmf::Message& me msg.setData(data); try { - sess->session.messageTransfer(arg::content=msg, arg::destination=message.destination); + sess->session.messageTransfer(client::arg::content=msg, client::arg::destination=message.destination); } catch(exception& e) { QPID_LOG(error, "Session Exception during message-transfer: " << e.what()); sessions.erase(iter); @@ -267,19 +267,22 @@ void ResilientConnectionImpl::sendMessage(SessionHandle handle, qmf::Message& me void ResilientConnectionImpl::declareQueue(SessionHandle handle, char* queue) { Mutex::ScopedLock _lock(lock); - RCSession* sess = (RCSession*) handle.handle; + RCSession* sess = (RCSession*) handle.impl; - sess->session.queueDeclare(arg::queue=queue, arg::autoDelete=true, arg::exclusive=true); + sess->session.queueDeclare(client::arg::queue=queue, client::arg::autoDelete=true, client::arg::exclusive=true); + sess->subscriptions->setAcceptMode(client::ACCEPT_MODE_NONE); + sess->subscriptions->setAcquireMode(client::ACQUIRE_MODE_PRE_ACQUIRED); sess->subscriptions->subscribe(*sess, queue, queue); + sess->subscriptions->setFlowControl(queue, client::FlowControl::unlimited()); sess->dests.push_back(string(queue)); } void ResilientConnectionImpl::deleteQueue(SessionHandle handle, char* queue) { Mutex::ScopedLock _lock(lock); - RCSession* sess = (RCSession*) handle.handle; + RCSession* sess = (RCSession*) handle.impl; - sess->session.queueDelete(arg::queue=queue); + sess->session.queueDelete(client::arg::queue=queue); for (vector::iterator iter = sess->dests.begin(); iter != sess->dests.end(); iter++) if (*iter == queue) { @@ -293,18 +296,18 @@ void ResilientConnectionImpl::bind(SessionHandle handle, char* exchange, char* queue, char* key) { Mutex::ScopedLock _lock(lock); - RCSession* sess = (RCSession*) handle.handle; + RCSession* sess = (RCSession*) handle.impl; - sess->session.exchangeBind(arg::exchange=exchange, arg::queue=queue, arg::bindingKey=key); + sess->session.exchangeBind(client::arg::exchange=exchange, client::arg::queue=queue, client::arg::bindingKey=key); } void ResilientConnectionImpl::unbind(SessionHandle handle, char* exchange, char* queue, char* key) { Mutex::ScopedLock _lock(lock); - RCSession* sess = (RCSession*) handle.handle; + RCSession* sess = (RCSession*) handle.impl; - sess->session.exchangeUnbind(arg::exchange=exchange, arg::queue=queue, arg::bindingKey=key); + sess->session.exchangeUnbind(client::arg::exchange=exchange, client::arg::queue=queue, client::arg::bindingKey=key); } void ResilientConnectionImpl::setNotifyFd(int fd) @@ -318,7 +321,8 @@ void ResilientConnectionImpl::run() while (true) { try { - connection.open(settings); + QPID_LOG(trace, "Trying to open connection..."); + connection.open(settings.impl->getClientSettings()); { Mutex::ScopedLock _lock(lock); connected = true; @@ -326,6 +330,7 @@ void ResilientConnectionImpl::run() while (connected) cond.wait(lock); + delay = delayMin; while (!sessions.empty()) { set::iterator iter = sessions.begin(); @@ -334,6 +339,11 @@ void ResilientConnectionImpl::run() EnqueueEvent(ResilientConnectionEvent::SESSION_CLOSED, sess->userContext); Mutex::ScopedUnlock _u(lock); sess->stop(); + + // Nullify the intrusive pointer within the scoped unlock, otherwise, + // the reference is held until overwritted above (under lock) which causes + // the session destructor to be called with the lock held. + sess = 0; } EnqueueEvent(ResilientConnectionEvent::DISCONNECTED); @@ -341,7 +351,6 @@ void ResilientConnectionImpl::run() if (shutdown) return; } - delay = delayMin; connection.close(); } catch (exception &e) { QPID_LOG(debug, "connection.open exception: " << e.what()); @@ -396,10 +405,9 @@ void ResilientConnectionImpl::EnqueueEvent(ResilientConnectionEvent::EventKind k // Wrappers //================================================================== -ResilientConnection::ResilientConnection(ConnectionSettings& settings, - int delayMin, int delayMax, int delayFactor) +ResilientConnection::ResilientConnection(const ConnectionSettings& settings) { - impl = new ResilientConnectionImpl(settings, delayMin, delayMax, delayFactor); + impl = new ResilientConnectionImpl(settings); } ResilientConnection::~ResilientConnection() diff --git a/qpid/cpp/src/qmf/ResilientConnection.h b/qpid/cpp/src/qmf/ResilientConnection.h index bb565e27ae..03f1b9c0d5 100644 --- a/qpid/cpp/src/qmf/ResilientConnection.h +++ b/qpid/cpp/src/qmf/ResilientConnection.h @@ -21,12 +21,13 @@ */ #include -#include -#include +#include #include namespace qmf { + class ResilientConnectionImpl; + /** * Represents events that occur, unsolicited, from ResilientConnection. */ @@ -44,12 +45,11 @@ namespace qmf { Message message; // RECV }; - struct SessionHandle { - void* handle; + class SessionHandle { + friend class ResilientConnectionImpl; + void* impl; }; - class ResilientConnectionImpl; - /** * ResilientConnection represents a Qpid connection that is resilient. * @@ -68,10 +68,7 @@ namespace qmf { *@param delayMax Maximum delay (in seconds) between retries. *@param delayFactor Factor to multiply retry delay by after each failure. */ - ResilientConnection(qpid::client::ConnectionSettings& settings, - int delayMin = 1, - int delayMax = 128, - int delayFactor = 2); + ResilientConnection(const ConnectionSettings& settings); ~ResilientConnection(); /** diff --git a/qpid/cpp/src/qmf/Schema.h b/qpid/cpp/src/qmf/Schema.h index e3ab90e3e3..1123acc3b8 100644 --- a/qpid/cpp/src/qmf/Schema.h +++ b/qpid/cpp/src/qmf/Schema.h @@ -35,6 +35,7 @@ namespace qmf { struct SchemaStatisticImpl; struct SchemaObjectClassImpl; struct SchemaEventClassImpl; + struct SchemaClassKeyImpl; /** */ @@ -112,6 +113,20 @@ namespace qmf { SchemaStatisticImpl* impl; }; + /** + */ + class SchemaClassKey { + public: + SchemaClassKey(SchemaClassKeyImpl* impl); + ~SchemaClassKey(); + + const char* getPackageName() const; + const char* getClassName() const; + const uint8_t* getHash() const; + + SchemaClassKeyImpl* impl; + }; + /** */ class SchemaObjectClass { @@ -123,9 +138,7 @@ namespace qmf { void addStatistic(const SchemaStatistic& statistic); void addMethod(const SchemaMethod& method); - const char* getPackage() const; - const char* getName() const; - const uint8_t* getHash() const; + const SchemaClassKey* getClassKey() const; int getPropertyCount() const; int getStatisticCount() const; int getMethodCount() const; @@ -146,9 +159,7 @@ namespace qmf { void addArgument(const SchemaArgument& argument); void setDesc(const char* desc); - const char* getPackage() const; - const char* getName() const; - const uint8_t* getHash() const; + const SchemaClassKey* getClassKey() const; int getArgumentCount() const; const SchemaArgument* getArgument(int idx) const; diff --git a/qpid/cpp/src/qmf/SchemaImpl.cpp b/qpid/cpp/src/qmf/SchemaImpl.cpp index 665c94f2a1..ae7d6ca689 100644 --- a/qpid/cpp/src/qmf/SchemaImpl.cpp +++ b/qpid/cpp/src/qmf/SchemaImpl.cpp @@ -20,6 +20,8 @@ #include "qmf/SchemaImpl.h" #include #include +#include +#include #include #include @@ -27,6 +29,7 @@ using namespace std; using namespace qmf; using qpid::framing::Buffer; using qpid::framing::FieldTable; +using qpid::framing::Uuid; SchemaHash::SchemaHash() { @@ -34,7 +37,7 @@ SchemaHash::SchemaHash() hash[idx] = 0x5A; } -void SchemaHash::encode(Buffer& buffer) +void SchemaHash::encode(Buffer& buffer) const { buffer.putBin128(hash); } @@ -63,6 +66,21 @@ void SchemaHash::update(const char* data, uint32_t len) } } +bool SchemaHash::operator==(const SchemaHash& other) const +{ + return ::memcmp(&hash, &other.hash, 16) == 0; +} + +bool SchemaHash::operator<(const SchemaHash& other) const +{ + return ::memcmp(&hash, &other.hash, 16) < 0; +} + +bool SchemaHash::operator>(const SchemaHash& other) const +{ + return ::memcmp(&hash, &other.hash, 16) > 0; +} + SchemaArgumentImpl::SchemaArgumentImpl(Buffer& buffer) : envelope(new SchemaArgument(this)) { FieldTable map; @@ -240,15 +258,51 @@ void SchemaStatisticImpl::updateHash(SchemaHash& hash) const hash.update(description); } -SchemaObjectClassImpl::SchemaObjectClassImpl(Buffer& buffer) : envelope(new SchemaObjectClass(this)), hasHash(true) +SchemaClassKeyImpl::SchemaClassKeyImpl(const string& p, const string& n, const SchemaHash& h) : + envelope(new SchemaClassKey(this)), package(p), name(n), hash(h) {} + +void SchemaClassKeyImpl::encode(qpid::framing::Buffer& buffer) const +{ + buffer.putShortString(package); + buffer.putShortString(name); + hash.encode(buffer); +} + +bool SchemaClassKeyImpl::operator==(const SchemaClassKeyImpl& other) const +{ + return package == other.package && + name == other.name && + hash == other.hash; +} + +bool SchemaClassKeyImpl::operator<(const SchemaClassKeyImpl& other) const +{ + if (package < other.package) return true; + if (package > other.package) return false; + if (name < other.name) return true; + if (name > other.name) return false; + return hash < other.hash; +} + +string SchemaClassKeyImpl::str() const +{ + Uuid printableHash(hash.get()); + stringstream str; + str << package << ":" << name << "(" << printableHash << ")"; + return str.str(); +} + +SchemaObjectClassImpl::SchemaObjectClassImpl(Buffer& buffer) : + envelope(new SchemaObjectClass(this)), hasHash(true), classKey(package, name, hash) { buffer.getShortString(package); buffer.getShortString(name); hash.decode(buffer); - uint16_t propCount = buffer.getShort(); - uint16_t statCount = buffer.getShort(); - uint16_t methodCount = buffer.getShort(); + /*uint8_t hasParentClass =*/ buffer.getOctet(); // TODO: Parse parent-class indicator + uint16_t propCount = buffer.getShort(); + uint16_t statCount = buffer.getShort(); + uint16_t methodCount = buffer.getShort(); for (uint16_t idx = 0; idx < propCount; idx++) { SchemaPropertyImpl* property = new SchemaPropertyImpl(buffer); @@ -288,7 +342,7 @@ void SchemaObjectClassImpl::encode(Buffer& buffer) const (*iter)->encode(buffer); } -const uint8_t* SchemaObjectClassImpl::getHash() const +const SchemaClassKey* SchemaObjectClassImpl::getClassKey() const { if (!hasHash) { hasHash = true; @@ -305,7 +359,7 @@ const uint8_t* SchemaObjectClassImpl::getHash() const (*iter)->updateHash(hash); } - return hash.get(); + return classKey.envelope; } void SchemaObjectClassImpl::addProperty(const SchemaProperty& property) @@ -353,7 +407,8 @@ const SchemaMethod* SchemaObjectClassImpl::getMethod(int idx) const return 0; } -SchemaEventClassImpl::SchemaEventClassImpl(Buffer& buffer) : envelope(new SchemaEventClass(this)), hasHash(true) +SchemaEventClassImpl::SchemaEventClassImpl(Buffer& buffer) : + envelope(new SchemaEventClass(this)), hasHash(true), classKey(package, name, hash) { buffer.getShortString(package); buffer.getShortString(name); @@ -380,7 +435,7 @@ void SchemaEventClassImpl::encode(Buffer& buffer) const (*iter)->encode(buffer); } -const uint8_t* SchemaEventClassImpl::getHash() const +const SchemaClassKey* SchemaEventClassImpl::getClassKey() const { if (!hasHash) { hasHash = true; @@ -390,7 +445,7 @@ const uint8_t* SchemaEventClassImpl::getHash() const iter != arguments.end(); iter++) (*iter)->updateHash(hash); } - return hash.get(); + return classKey.envelope; } void SchemaEventClassImpl::addArgument(const SchemaArgument& argument) @@ -408,334 +463,79 @@ const SchemaArgument* SchemaEventClassImpl::getArgument(int idx) const return 0; } + //================================================================== // Wrappers //================================================================== -SchemaArgument::SchemaArgument(const char* name, Typecode typecode) -{ - impl = new SchemaArgumentImpl(this, name, typecode); -} - +SchemaArgument::SchemaArgument(const char* name, Typecode typecode) { impl = new SchemaArgumentImpl(this, name, typecode); } SchemaArgument::SchemaArgument(SchemaArgumentImpl* i) : impl(i) {} - -SchemaArgument::~SchemaArgument() -{ - delete impl; -} - -void SchemaArgument::setDirection(Direction dir) -{ - impl->setDirection(dir); -} - -void SchemaArgument::setUnit(const char* val) -{ - impl->setUnit(val); -} - -void SchemaArgument::setDesc(const char* desc) -{ - impl->setDesc(desc); -} - -const char* SchemaArgument::getName() const -{ - return impl->getName().c_str(); -} - -Typecode SchemaArgument::getType() const -{ - return impl->getType(); -} - -Direction SchemaArgument::getDirection() const -{ - return impl->getDirection(); -} - -const char* SchemaArgument::getUnit() const -{ - return impl->getUnit().c_str(); -} - -const char* SchemaArgument::getDesc() const -{ - return impl->getDesc().c_str(); -} - -SchemaMethod::SchemaMethod(const char* name) -{ - impl = new SchemaMethodImpl(this, name); -} - +SchemaArgument::~SchemaArgument() { delete impl; } +void SchemaArgument::setDirection(Direction dir) { impl->setDirection(dir); } +void SchemaArgument::setUnit(const char* val) { impl->setUnit(val); } +void SchemaArgument::setDesc(const char* desc) { impl->setDesc(desc); } +const char* SchemaArgument::getName() const { return impl->getName().c_str(); } +Typecode SchemaArgument::getType() const { return impl->getType(); } +Direction SchemaArgument::getDirection() const { return impl->getDirection(); } +const char* SchemaArgument::getUnit() const { return impl->getUnit().c_str(); } +const char* SchemaArgument::getDesc() const { return impl->getDesc().c_str(); } +SchemaMethod::SchemaMethod(const char* name) { impl = new SchemaMethodImpl(this, name); } SchemaMethod::SchemaMethod(SchemaMethodImpl* i) : impl(i) {} - -SchemaMethod::~SchemaMethod() -{ - delete impl; -} - -void SchemaMethod::addArgument(const SchemaArgument& argument) -{ - impl->addArgument(argument); -} - -void SchemaMethod::setDesc(const char* desc) -{ - impl->setDesc(desc); -} - -const char* SchemaMethod::getName() const -{ - return impl->getName().c_str(); -} - -const char* SchemaMethod::getDesc() const -{ - return impl->getDesc().c_str(); -} - -int SchemaMethod::getArgumentCount() const -{ - return impl->getArgumentCount(); -} - -const SchemaArgument* SchemaMethod::getArgument(int idx) const -{ - return impl->getArgument(idx); -} - -SchemaProperty::SchemaProperty(const char* name, Typecode typecode) -{ - impl = new SchemaPropertyImpl(this, name, typecode); -} - +SchemaMethod::~SchemaMethod() { delete impl; } +void SchemaMethod::addArgument(const SchemaArgument& argument) { impl->addArgument(argument); } +void SchemaMethod::setDesc(const char* desc) { impl->setDesc(desc); } +const char* SchemaMethod::getName() const { return impl->getName().c_str(); } +const char* SchemaMethod::getDesc() const { return impl->getDesc().c_str(); } +int SchemaMethod::getArgumentCount() const { return impl->getArgumentCount(); } +const SchemaArgument* SchemaMethod::getArgument(int idx) const { return impl->getArgument(idx); } +SchemaProperty::SchemaProperty(const char* name, Typecode typecode) { impl = new SchemaPropertyImpl(this, name, typecode); } SchemaProperty::SchemaProperty(SchemaPropertyImpl* i) : impl(i) {} - -SchemaProperty::~SchemaProperty() -{ - delete impl; -} - -void SchemaProperty::setAccess(Access access) -{ - impl->setAccess(access); -} - -void SchemaProperty::setIndex(bool val) -{ - impl->setIndex(val); -} - -void SchemaProperty::setOptional(bool val) -{ - impl->setOptional(val); -} - -void SchemaProperty::setUnit(const char* val) -{ - impl->setUnit(val); -} - -void SchemaProperty::setDesc(const char* desc) -{ - impl->setDesc(desc); -} - -const char* SchemaProperty::getName() const -{ - return impl->getName().c_str(); -} - -Typecode SchemaProperty::getType() const -{ - return impl->getType(); -} - -Access SchemaProperty::getAccess() const -{ - return impl->getAccess(); -} - -bool SchemaProperty::isIndex() const -{ - return impl->isIndex(); -} - -bool SchemaProperty::isOptional() const -{ - return impl->isOptional(); -} - -const char* SchemaProperty::getUnit() const -{ - return impl->getUnit().c_str(); -} - -const char* SchemaProperty::getDesc() const -{ - return impl->getDesc().c_str(); -} - -SchemaStatistic::SchemaStatistic(const char* name, Typecode typecode) -{ - impl = new SchemaStatisticImpl(this, name, typecode); -} - +SchemaProperty::~SchemaProperty() { delete impl; } +void SchemaProperty::setAccess(Access access) { impl->setAccess(access); } +void SchemaProperty::setIndex(bool val) { impl->setIndex(val); } +void SchemaProperty::setOptional(bool val) { impl->setOptional(val); } +void SchemaProperty::setUnit(const char* val) { impl->setUnit(val); } +void SchemaProperty::setDesc(const char* desc) { impl->setDesc(desc); } +const char* SchemaProperty::getName() const { return impl->getName().c_str(); } +Typecode SchemaProperty::getType() const { return impl->getType(); } +Access SchemaProperty::getAccess() const { return impl->getAccess(); } +bool SchemaProperty::isIndex() const { return impl->isIndex(); } +bool SchemaProperty::isOptional() const { return impl->isOptional(); } +const char* SchemaProperty::getUnit() const { return impl->getUnit().c_str(); } +const char* SchemaProperty::getDesc() const { return impl->getDesc().c_str(); } +SchemaStatistic::SchemaStatistic(const char* name, Typecode typecode) { impl = new SchemaStatisticImpl(this, name, typecode); } SchemaStatistic::SchemaStatistic(SchemaStatisticImpl* i) : impl(i) {} - -SchemaStatistic::~SchemaStatistic() -{ - delete impl; -} - -void SchemaStatistic::setUnit(const char* val) -{ - impl->setUnit(val); -} - -void SchemaStatistic::setDesc(const char* desc) -{ - impl->setDesc(desc); -} - -const char* SchemaStatistic::getName() const -{ - return impl->getName().c_str(); -} - -Typecode SchemaStatistic::getType() const -{ - return impl->getType(); -} - -const char* SchemaStatistic::getUnit() const -{ - return impl->getUnit().c_str(); -} - -const char* SchemaStatistic::getDesc() const -{ - return impl->getDesc().c_str(); -} - -SchemaObjectClass::SchemaObjectClass(const char* package, const char* name) -{ - impl = new SchemaObjectClassImpl(this, package, name); -} - +SchemaStatistic::~SchemaStatistic() { delete impl; } +void SchemaStatistic::setUnit(const char* val) { impl->setUnit(val); } +void SchemaStatistic::setDesc(const char* desc) { impl->setDesc(desc); } +const char* SchemaStatistic::getName() const { return impl->getName().c_str(); } +Typecode SchemaStatistic::getType() const { return impl->getType(); } +const char* SchemaStatistic::getUnit() const { return impl->getUnit().c_str(); } +const char* SchemaStatistic::getDesc() const { return impl->getDesc().c_str(); } +SchemaClassKey::SchemaClassKey(SchemaClassKeyImpl* i) : impl(i) {} +SchemaClassKey::~SchemaClassKey() { delete impl; } +const char* SchemaClassKey::getPackageName() const { return impl->getPackageName().c_str(); } +const char* SchemaClassKey::getClassName() const { return impl->getClassName().c_str(); } +const uint8_t* SchemaClassKey::getHash() const { return impl->getHash(); } +SchemaObjectClass::SchemaObjectClass(const char* package, const char* name) { impl = new SchemaObjectClassImpl(this, package, name); } SchemaObjectClass::SchemaObjectClass(SchemaObjectClassImpl* i) : impl(i) {} - -SchemaObjectClass::~SchemaObjectClass() -{ - delete impl; -} - -void SchemaObjectClass::addProperty(const SchemaProperty& property) -{ - impl->addProperty(property); -} - -void SchemaObjectClass::addStatistic(const SchemaStatistic& statistic) -{ - impl->addStatistic(statistic); -} - -void SchemaObjectClass::addMethod(const SchemaMethod& method) -{ - impl->addMethod(method); -} - -const char* SchemaObjectClass::getPackage() const -{ - return impl->getPackage().c_str(); -} - -const char* SchemaObjectClass::getName() const -{ - return impl->getName().c_str(); -} - -const uint8_t* SchemaObjectClass::getHash() const -{ - return impl->getHash(); -} - -int SchemaObjectClass::getPropertyCount() const -{ - return impl->getPropertyCount(); -} - -int SchemaObjectClass::getStatisticCount() const -{ - return impl->getStatisticCount(); -} - -int SchemaObjectClass::getMethodCount() const -{ - return impl->getMethodCount(); -} - -const SchemaProperty* SchemaObjectClass::getProperty(int idx) const -{ - return impl->getProperty(idx); -} - -const SchemaStatistic* SchemaObjectClass::getStatistic(int idx) const -{ - return impl->getStatistic(idx); -} - -const SchemaMethod* SchemaObjectClass::getMethod(int idx) const -{ - return impl->getMethod(idx); -} - -SchemaEventClass::SchemaEventClass(const char* package, const char* name) -{ - impl = new SchemaEventClassImpl(this, package, name); -} - +SchemaObjectClass::~SchemaObjectClass() { delete impl; } +void SchemaObjectClass::addProperty(const SchemaProperty& property) { impl->addProperty(property); } +void SchemaObjectClass::addStatistic(const SchemaStatistic& statistic) { impl->addStatistic(statistic); } +void SchemaObjectClass::addMethod(const SchemaMethod& method) { impl->addMethod(method); } +const SchemaClassKey* SchemaObjectClass::getClassKey() const { return impl->getClassKey(); } +int SchemaObjectClass::getPropertyCount() const { return impl->getPropertyCount(); } +int SchemaObjectClass::getStatisticCount() const { return impl->getStatisticCount(); } +int SchemaObjectClass::getMethodCount() const { return impl->getMethodCount(); } +const SchemaProperty* SchemaObjectClass::getProperty(int idx) const { return impl->getProperty(idx); } +const SchemaStatistic* SchemaObjectClass::getStatistic(int idx) const { return impl->getStatistic(idx); } +const SchemaMethod* SchemaObjectClass::getMethod(int idx) const { return impl->getMethod(idx); } +SchemaEventClass::SchemaEventClass(const char* package, const char* name) { impl = new SchemaEventClassImpl(this, package, name); } SchemaEventClass::SchemaEventClass(SchemaEventClassImpl* i) : impl(i) {} - -SchemaEventClass::~SchemaEventClass() -{ - delete impl; -} - -void SchemaEventClass::addArgument(const SchemaArgument& argument) -{ - impl->addArgument(argument); -} - -void SchemaEventClass::setDesc(const char* desc) -{ - impl->setDesc(desc); -} - -const char* SchemaEventClass::getPackage() const -{ - return impl->getPackage().c_str(); -} - -const char* SchemaEventClass::getName() const -{ - return impl->getName().c_str(); -} - -const uint8_t* SchemaEventClass::getHash() const -{ - return impl->getHash(); -} - -int SchemaEventClass::getArgumentCount() const -{ - return impl->getArgumentCount(); -} - -const SchemaArgument* SchemaEventClass::getArgument(int idx) const -{ - return impl->getArgument(idx); -} +SchemaEventClass::~SchemaEventClass() { delete impl; } +void SchemaEventClass::addArgument(const SchemaArgument& argument) { impl->addArgument(argument); } +void SchemaEventClass::setDesc(const char* desc) { impl->setDesc(desc); } +const SchemaClassKey* SchemaEventClass::getClassKey() const { return impl->getClassKey(); } +int SchemaEventClass::getArgumentCount() const { return impl->getArgumentCount(); } +const SchemaArgument* SchemaEventClass::getArgument(int idx) const { return impl->getArgument(idx); } diff --git a/qpid/cpp/src/qmf/SchemaImpl.h b/qpid/cpp/src/qmf/SchemaImpl.h index 2c30a8851f..3e9677d1fa 100644 --- a/qpid/cpp/src/qmf/SchemaImpl.h +++ b/qpid/cpp/src/qmf/SchemaImpl.h @@ -21,6 +21,7 @@ */ #include "qmf/Schema.h" +#include #include #include #include @@ -35,7 +36,7 @@ namespace qmf { uint8_t hash[16]; public: SchemaHash(); - void encode(qpid::framing::Buffer& buffer); + void encode(qpid::framing::Buffer& buffer) const; void decode(qpid::framing::Buffer& buffer); void update(const char* data, uint32_t len); void update(uint8_t data); @@ -45,6 +46,9 @@ namespace qmf { void update(Access a) { update((uint8_t) a); } void update(bool b) { update((uint8_t) (b ? 1 : 0)); } const uint8_t* get() const { return hash; } + bool operator==(const SchemaHash& other) const; + bool operator<(const SchemaHash& other) const; + bool operator>(const SchemaHash& other) const; }; struct SchemaArgumentImpl { @@ -138,27 +142,45 @@ namespace qmf { void updateHash(SchemaHash& hash) const; }; + struct SchemaClassKeyImpl { + const SchemaClassKey* envelope; + const std::string& package; + const std::string& name; + const SchemaHash& hash; + + SchemaClassKeyImpl(const std::string& package, const std::string& name, const SchemaHash& hash); + + const std::string& getPackageName() const { return package; } + const std::string& getClassName() const { return name; } + const uint8_t* getHash() const { return hash.get(); } + + void encode(qpid::framing::Buffer& buffer) const; + bool operator==(const SchemaClassKeyImpl& other) const; + bool operator<(const SchemaClassKeyImpl& other) const; + std::string str() const; + }; + struct SchemaObjectClassImpl { + typedef boost::shared_ptr Ptr; SchemaObjectClass* envelope; std::string package; std::string name; mutable SchemaHash hash; mutable bool hasHash; + SchemaClassKeyImpl classKey; std::vector properties; std::vector statistics; std::vector methods; SchemaObjectClassImpl(SchemaObjectClass* e, const char* p, const char* n) : - envelope(e), package(p), name(n), hasHash(false) {} + envelope(e), package(p), name(n), hasHash(false), classKey(package, name, hash) {} SchemaObjectClassImpl(qpid::framing::Buffer& buffer); void encode(qpid::framing::Buffer& buffer) const; void addProperty(const SchemaProperty& property); void addStatistic(const SchemaStatistic& statistic); void addMethod(const SchemaMethod& method); - const std::string& getPackage() const { return package; } - const std::string& getName() const { return name; } - const uint8_t* getHash() const; + const SchemaClassKey* getClassKey() const; int getPropertyCount() const { return properties.size(); } int getStatisticCount() const { return statistics.size(); } int getMethodCount() const { return methods.size(); } @@ -168,24 +190,24 @@ namespace qmf { }; struct SchemaEventClassImpl { + typedef boost::shared_ptr Ptr; SchemaEventClass* envelope; std::string package; std::string name; mutable SchemaHash hash; mutable bool hasHash; + SchemaClassKeyImpl classKey; std::string description; std::vector arguments; SchemaEventClassImpl(SchemaEventClass* e, const char* p, const char* n) : - envelope(e), package(p), name(n), hasHash(false) {} + envelope(e), package(p), name(n), hasHash(false), classKey(package, name, hash) {} SchemaEventClassImpl(qpid::framing::Buffer& buffer); void encode(qpid::framing::Buffer& buffer) const; void addArgument(const SchemaArgument& argument); void setDesc(const char* desc) { description = desc; } - const std::string& getPackage() const { return package; } - const std::string& getName() const { return name; } - const uint8_t* getHash() const; + const SchemaClassKey* getClassKey() const; int getArgumentCount() const { return arguments.size(); } const SchemaArgument* getArgument(int idx) const; }; diff --git a/qpid/cpp/src/qmf/SequenceManager.cpp b/qpid/cpp/src/qmf/SequenceManager.cpp new file mode 100644 index 0000000000..f51ce9d8b8 --- /dev/null +++ b/qpid/cpp/src/qmf/SequenceManager.cpp @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/SequenceManager.h" + +using namespace std; +using namespace qmf; +using namespace qpid::sys; + +SequenceManager::SequenceManager() : nextSequence(1) {} + +uint32_t SequenceManager::reserve(SequenceContext* ctx) +{ + Mutex::ScopedLock _lock(lock); + uint32_t seq = nextSequence; + while (contextMap.find(seq) != contextMap.end()) + seq = seq < 0xFFFFFFFF ? seq + 1 : 1; + nextSequence = seq < 0xFFFFFFFF ? seq + 1 : 1; + contextMap[seq] = ctx; + return seq; +} + +void SequenceManager::release(uint32_t sequence) +{ + Mutex::ScopedLock _lock(lock); + map::iterator iter = contextMap.find(sequence); + if (iter != contextMap.end()) { + if (iter->second != 0) + iter->second->complete(); + contextMap.erase(iter); + } +} + + diff --git a/qpid/cpp/src/qmf/SequenceManager.h b/qpid/cpp/src/qmf/SequenceManager.h new file mode 100644 index 0000000000..c027872313 --- /dev/null +++ b/qpid/cpp/src/qmf/SequenceManager.h @@ -0,0 +1,52 @@ +#ifndef _QmfSequenceManager_ +#define _QmfSequenceManager_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qpid/sys/Mutex.h" +#include + +namespace qmf { + + class SequenceContext { + public: + SequenceContext() {} + virtual ~SequenceContext() {} + + virtual void complete() = 0; + }; + + class SequenceManager { + public: + SequenceManager(); + + uint32_t reserve(SequenceContext* ctx); + void release(uint32_t sequence); + + private: + mutable qpid::sys::Mutex lock; + uint32_t nextSequence; + std::map contextMap; + }; + +} + +#endif + diff --git a/qpid/cpp/src/qmf/Value.h b/qpid/cpp/src/qmf/Value.h index a45df14ea9..bb946d31d3 100644 --- a/qpid/cpp/src/qmf/Value.h +++ b/qpid/cpp/src/qmf/Value.h @@ -30,7 +30,7 @@ namespace qmf { class Value { public: - Value(); + // Value(); Value(Typecode t, Typecode arrayType = TYPE_UINT8); Value(ValueImpl* impl); ~Value(); diff --git a/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp index 4a6590fb5f..093e9cea32 100644 --- a/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp +++ b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp @@ -435,8 +435,8 @@ void ManagementAgentImpl::invokeMethodRequest(Buffer& inBuffer, uint32_t sequenc } else { if ((iter->second->getPackageName() != packageName) || (iter->second->getClassName() != className)) { - outBuffer.putLong (Manageable::STATUS_INVALID_PARAMETER); - outBuffer.putMediumString(Manageable::StatusText (Manageable::STATUS_INVALID_PARAMETER)); + outBuffer.putLong (Manageable::STATUS_PARAMETER_INVALID); + outBuffer.putMediumString(Manageable::StatusText (Manageable::STATUS_PARAMETER_INVALID)); } else try { diff --git a/qpid/cpp/src/qpid/broker/Broker.cpp b/qpid/cpp/src/qpid/broker/Broker.cpp index 8b6cb5e049..13cf88fb11 100644 --- a/qpid/cpp/src/qpid/broker/Broker.cpp +++ b/qpid/cpp/src/qpid/broker/Broker.cpp @@ -386,7 +386,7 @@ Manageable::status_t Broker::ManagementMethod (uint32_t methodId, if (queueMoveMessages(moveArgs.i_srcQueue, moveArgs.i_destQueue, moveArgs.i_qty)) status = Manageable::STATUS_OK; else - return Manageable::STATUS_INVALID_PARAMETER; + return Manageable::STATUS_PARAMETER_INVALID; break; } default: diff --git a/qpid/cpp/src/qpid/broker/Connection.cpp b/qpid/cpp/src/qpid/broker/Connection.cpp index a1a3c6ada7..3b8a6c71d4 100644 --- a/qpid/cpp/src/qpid/broker/Connection.cpp +++ b/qpid/cpp/src/qpid/broker/Connection.cpp @@ -269,11 +269,13 @@ bool Connection::doOutput() { cb(); // Lend the IO thread for management processing } } - if (mgmtClosing) + if (mgmtClosing) { + closed(); close(connection::CLOSE_CODE_CONNECTION_FORCED, "Closed by Management Request"); - else + } else { //then do other output as needed: return outputTasks.doOutput(); + } }catch(ConnectionException& e){ close(e.code, e.getMessage()); }catch(std::exception& e){ diff --git a/qpid/cpp/src/qpid/broker/Daemon.cpp b/qpid/cpp/src/qpid/broker/Daemon.cpp index aa8dd8855b..21848e23de 100644 --- a/qpid/cpp/src/qpid/broker/Daemon.cpp +++ b/qpid/cpp/src/qpid/broker/Daemon.cpp @@ -85,7 +85,7 @@ void Daemon::fork() child(); } catch (const exception& e) { - QPID_LOG(critical, "Daemon startup failed: " << e.what()); + QPID_LOG(critical, "Unexpected error: " << e.what()); uint16_t port = 0; int unused_ret; //Supress warning about ignoring return value. unused_ret = write(pipeFds[1], &port, sizeof(uint16_t)); diff --git a/qpid/cpp/src/qpid/broker/Exchange.cpp b/qpid/cpp/src/qpid/broker/Exchange.cpp index 17fc0e23ca..b1fc1295f3 100644 --- a/qpid/cpp/src/qpid/broker/Exchange.cpp +++ b/qpid/cpp/src/qpid/broker/Exchange.cpp @@ -91,7 +91,9 @@ Exchange::Exchange (const string& _name, Manageable* parent, Broker* b) : ManagementAgent* agent = broker->getManagementAgent(); if (agent != 0) { - mgmtExchange = new _qmf::Exchange (agent, this, parent, _name, durable); + mgmtExchange = new _qmf::Exchange (agent, this, parent, _name); + mgmtExchange->set_durable(durable); + mgmtExchange->set_autoDelete(false); agent->addObject (mgmtExchange); } } @@ -109,7 +111,9 @@ Exchange::Exchange(const string& _name, bool _durable, const qpid::framing::Fiel ManagementAgent* agent = broker->getManagementAgent(); if (agent != 0) { - mgmtExchange = new _qmf::Exchange (agent, this, parent, _name, durable); + mgmtExchange = new _qmf::Exchange (agent, this, parent, _name); + mgmtExchange->set_durable(durable); + mgmtExchange->set_autoDelete(false); mgmtExchange->set_arguments(args); if (!durable) { if (name.empty()) { @@ -139,6 +143,17 @@ Exchange::~Exchange () mgmtExchange->resourceDestroy (); } +void Exchange::setAlternate(Exchange::shared_ptr _alternate) +{ + alternate = _alternate; + if (mgmtExchange != 0) { + if (alternate.get() != 0) + mgmtExchange->set_altExchange(alternate->GetManagementObject()->getObjectId()); + else + mgmtExchange->clr_altExchange(); + } +} + void Exchange::setPersistenceId(uint64_t id) const { if (mgmtExchange != 0 && persistenceId == 0) diff --git a/qpid/cpp/src/qpid/broker/Exchange.h b/qpid/cpp/src/qpid/broker/Exchange.h index ca8525d55e..c1e878200f 100644 --- a/qpid/cpp/src/qpid/broker/Exchange.h +++ b/qpid/cpp/src/qpid/broker/Exchange.h @@ -132,7 +132,7 @@ public: qpid::framing::FieldTable& getArgs() { return args; } Exchange::shared_ptr getAlternate() { return alternate; } - void setAlternate(Exchange::shared_ptr _alternate) { alternate = _alternate; } + void setAlternate(Exchange::shared_ptr _alternate); void incAlternateUsers() { alternateUsers++; } void decAlternateUsers() { alternateUsers--; } bool inUseAsAlternate() { return alternateUsers > 0; } diff --git a/qpid/cpp/src/qpid/broker/Link.cpp b/qpid/cpp/src/qpid/broker/Link.cpp index 9ce0c710bd..cdba18ccf9 100644 --- a/qpid/cpp/src/qpid/broker/Link.cpp +++ b/qpid/cpp/src/qpid/broker/Link.cpp @@ -200,8 +200,10 @@ void Link::destroy () // Move the bridges to be deleted into a local vector so there is no // corruption of the iterator caused by bridge deletion. - for (Bridges::iterator i = active.begin(); i != active.end(); i++) + for (Bridges::iterator i = active.begin(); i != active.end(); i++) { + (*i)->closed(); toDelete.push_back(*i); + } active.clear(); for (Bridges::iterator i = created.begin(); i != created.end(); i++) @@ -264,7 +266,6 @@ void Link::ioThreadProcessing() } if (!cancellations.empty()) { for (Bridges::iterator i = cancellations.begin(); i != cancellations.end(); ++i) { - active.push_back(*i); (*i)->cancel(*connection); } cancellations.clear(); diff --git a/qpid/cpp/src/qpid/broker/SessionAdapter.cpp b/qpid/cpp/src/qpid/broker/SessionAdapter.cpp index b0c5e9ea00..af07605552 100644 --- a/qpid/cpp/src/qpid/broker/SessionAdapter.cpp +++ b/qpid/cpp/src/qpid/broker/SessionAdapter.cpp @@ -121,9 +121,11 @@ void SessionAdapter::ExchangeHandlerImpl::checkType(Exchange::shared_ptr exchang void SessionAdapter::ExchangeHandlerImpl::checkAlternate(Exchange::shared_ptr exchange, Exchange::shared_ptr alternate) { - if (alternate && alternate != exchange->getAlternate()) + if (alternate && ((exchange->getAlternate() && alternate != exchange->getAlternate()) + || !exchange->getAlternate())) throw NotAllowedException(QPID_MSG("Exchange declared with alternate-exchange " - << exchange->getAlternate()->getName() << ", requested " + << (exchange->getAlternate() ? exchange->getAlternate()->getName() : "") + << ", requested " << alternate->getName())); } diff --git a/qpid/cpp/src/qpid/client/Demux.h b/qpid/cpp/src/qpid/client/Demux.h index 7304e799bb..31dc3f9c06 100644 --- a/qpid/cpp/src/qpid/client/Demux.h +++ b/qpid/cpp/src/qpid/client/Demux.h @@ -25,6 +25,7 @@ #include "qpid/framing/FrameSet.h" #include "qpid/sys/Mutex.h" #include "qpid/sys/BlockingQueue.h" +#include "qpid/client/ClientImportExport.h" #ifndef _Demux_ #define _Demux_ @@ -49,17 +50,17 @@ public: typedef sys::BlockingQueue Queue; typedef boost::shared_ptr QueuePtr; - Demux(); - ~Demux(); + QPID_CLIENT_EXTERN Demux(); + QPID_CLIENT_EXTERN ~Demux(); - void handle(framing::FrameSet::shared_ptr); - void close(const sys::ExceptionHolder& ex); - void open(); - - QueuePtr add(const std::string& name, Condition); - void remove(const std::string& name); - QueuePtr get(const std::string& name); - QueuePtr getDefault(); + QPID_CLIENT_EXTERN void handle(framing::FrameSet::shared_ptr); + QPID_CLIENT_EXTERN void close(const sys::ExceptionHolder& ex); + QPID_CLIENT_EXTERN void open(); + + QPID_CLIENT_EXTERN QueuePtr add(const std::string& name, Condition); + QPID_CLIENT_EXTERN void remove(const std::string& name); + QPID_CLIENT_EXTERN QueuePtr get(const std::string& name); + QPID_CLIENT_EXTERN QueuePtr getDefault(); private: struct Record diff --git a/qpid/cpp/src/qpid/client/Results.cpp b/qpid/cpp/src/qpid/client/Results.cpp index acb2684727..0de3e8bd04 100644 --- a/qpid/cpp/src/qpid/client/Results.cpp +++ b/qpid/cpp/src/qpid/client/Results.cpp @@ -24,7 +24,6 @@ #include "qpid/framing/SequenceSet.h" using namespace qpid::framing; -using namespace boost; namespace qpid { namespace client { diff --git a/qpid/cpp/src/qpid/client/SessionImpl.cpp b/qpid/cpp/src/qpid/client/SessionImpl.cpp index 43b62ec6dc..8ead44a172 100644 --- a/qpid/cpp/src/qpid/client/SessionImpl.cpp +++ b/qpid/cpp/src/qpid/client/SessionImpl.cpp @@ -202,6 +202,16 @@ bool SessionImpl::isCompleteUpTo(const SequenceNumber& id) return f.result; } +framing::SequenceNumber SessionImpl::getCompleteUpTo() +{ + SequenceNumber firstIncomplete; + { + Lock l(state); + firstIncomplete = incompleteIn.front(); + } + return --firstIncomplete; +} + struct MarkCompleted { const SequenceNumber& id; @@ -319,7 +329,7 @@ struct MethodContentAdaptor : MethodContent } -Future SessionImpl::send(const AMQBody& command, const FrameSet& content) { +Future SessionImpl::send(const AMQBody& command, const FrameSet& content, bool reframe) { Acquire a(sendLock); SequenceNumber id = nextOut++; { @@ -337,7 +347,7 @@ Future SessionImpl::send(const AMQBody& command, const FrameSet& content) { frame.setEof(false); handleOut(frame); - if (content.isComplete()) { + if (reframe) { MethodContentAdaptor c(content); sendContent(c); } else { diff --git a/qpid/cpp/src/qpid/client/SessionImpl.h b/qpid/cpp/src/qpid/client/SessionImpl.h index cae1148e9f..49d268c44d 100644 --- a/qpid/cpp/src/qpid/client/SessionImpl.h +++ b/qpid/cpp/src/qpid/client/SessionImpl.h @@ -25,6 +25,7 @@ #include "qpid/client/Demux.h" #include "qpid/client/Execution.h" #include "qpid/client/Results.h" +#include "qpid/client/ClientImportExport.h" #include "qpid/SessionId.h" #include "qpid/SessionState.h" @@ -86,7 +87,15 @@ public: Future send(const framing::AMQBody& command); Future send(const framing::AMQBody& command, const framing::MethodContent& content); - Future send(const framing::AMQBody& command, const framing::FrameSet& content); + /** + * This method takes the content as a FrameSet; if reframe=false, + * the caller is resposnible for ensuring that the header and + * content frames in that set are correct for this connection + * (right flags, right fragmentation etc). If reframe=true, then + * the header and content from the frameset will be copied and + * reframed correctly for the connection. + */ + QPID_CLIENT_EXTERN Future send(const framing::AMQBody& command, const framing::FrameSet& content, bool reframe=false); void sendRawFrame(framing::AMQFrame& frame); Demux& getDemux(); @@ -94,6 +103,7 @@ public: void markCompleted(const framing::SequenceSet& ids, bool notifyPeer); bool isComplete(const framing::SequenceNumber& id); bool isCompleteUpTo(const framing::SequenceNumber& id); + framing::SequenceNumber getCompleteUpTo(); void waitForCompletion(const framing::SequenceNumber& id); void sendCompletion(); void sendFlush(); diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp new file mode 100644 index 0000000000..9b9f06ec57 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp @@ -0,0 +1,461 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/amqp0_10/AddressResolution.h" +#include "qpid/client/amqp0_10/Codecs.h" +#include "qpid/client/amqp0_10/MessageSource.h" +#include "qpid/client/amqp0_10/MessageSink.h" +#include "qpid/client/amqp0_10/OutgoingMessage.h" +#include "qpid/messaging/Address.h" +#include "qpid/messaging/Filter.h" +#include "qpid/messaging/Message.h" +#include "qpid/Exception.h" +#include "qpid/log/Statement.h" +#include "qpid/framing/enum.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/ReplyTo.h" +#include "qpid/framing/reply_exceptions.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + +using qpid::Exception; +using qpid::messaging::Address; +using qpid::messaging::Filter; +using qpid::messaging::Variant; +using qpid::framing::FieldTable; +using qpid::framing::ReplyTo; +using namespace qpid::framing::message; + + +namespace{ +const Variant EMPTY_VARIANT; +const FieldTable EMPTY_FIELD_TABLE; +const std::string EMPTY_STRING; + +//option names +const std::string BROWSE("browse"); +const std::string EXCLUSIVE("exclusive"); +const std::string MODE("mode"); +const std::string NAME("name"); +const std::string UNACKNOWLEDGED("unacknowledged"); + +const std::string QUEUE_ADDRESS("queue"); +const std::string TOPIC_ADDRESS("topic"); +const std::string TOPIC_ADDRESS_AND_SUBJECT("topic+"); +const std::string DIVIDER("/"); + +const std::string SIMPLE_SUBSCRIPTION("simple"); +const std::string RELIABLE_SUBSCRIPTION("reliable"); +const std::string DURABLE_SUBSCRIPTION("durable"); +} + +class QueueSource : public MessageSource +{ + public: + QueueSource(const std::string& name, AcceptMode=ACCEPT_MODE_EXPLICIT, AcquireMode=ACQUIRE_MODE_PRE_ACQUIRED, + bool exclusive = false, const FieldTable& options = EMPTY_FIELD_TABLE); + void subscribe(qpid::client::AsyncSession& session, const std::string& destination); + void cancel(qpid::client::AsyncSession& session, const std::string& destination); + private: + const std::string name; + const AcceptMode acceptMode; + const AcquireMode acquireMode; + const bool exclusive; + const FieldTable options; +}; + +class Subscription : public MessageSource +{ + public: + enum SubscriptionMode {SIMPLE, RELIABLE, DURABLE}; + + Subscription(const std::string& name, SubscriptionMode mode = SIMPLE, + const FieldTable& queueOptions = EMPTY_FIELD_TABLE, const FieldTable& subscriptionOptions = EMPTY_FIELD_TABLE); + void add(const std::string& exchange, const std::string& key, const FieldTable& options = EMPTY_FIELD_TABLE); + void subscribe(qpid::client::AsyncSession& session, const std::string& destination); + void cancel(qpid::client::AsyncSession& session, const std::string& destination); + + static SubscriptionMode getMode(const std::string& mode); + private: + struct Binding + { + Binding(const std::string& exchange, const std::string& key, const FieldTable& options = EMPTY_FIELD_TABLE); + + std::string exchange; + std::string key; + FieldTable options; + }; + + typedef std::vector Bindings; + + const std::string name; + const bool autoDelete; + const bool durable; + const FieldTable queueOptions; + const FieldTable subscriptionOptions; + Bindings bindings; + std::string queue; +}; + +class Exchange : public MessageSink +{ + public: + Exchange(const std::string& name, const std::string& defaultSubject = EMPTY_STRING, + bool passive = true, const std::string& type = EMPTY_STRING, bool durable = false, + const FieldTable& options = EMPTY_FIELD_TABLE); + void declare(qpid::client::AsyncSession& session, const std::string& name); + void send(qpid::client::AsyncSession& session, const std::string& name, OutgoingMessage& message); + void cancel(qpid::client::AsyncSession& session, const std::string& name); + private: + const std::string name; + const std::string defaultSubject; + const bool passive; + const std::string type; + const bool durable; + const FieldTable options; +}; + +class QueueSink : public MessageSink +{ + public: + QueueSink(const std::string& name, bool passive=true, bool exclusive=false, + bool autoDelete=false, bool durable=false, const FieldTable& options = EMPTY_FIELD_TABLE); + void declare(qpid::client::AsyncSession& session, const std::string& name); + void send(qpid::client::AsyncSession& session, const std::string& name, OutgoingMessage& message); + void cancel(qpid::client::AsyncSession& session, const std::string& name); + private: + const std::string name; + const bool passive; + const bool exclusive; + const bool autoDelete; + const bool durable; + const FieldTable options; +}; + + +bool isQueue(qpid::client::Session session, const qpid::messaging::Address& address); +bool isTopic(qpid::client::Session session, const qpid::messaging::Address& address, std::string& subject); + +const Variant& getOption(const std::string& key, const Variant::Map& options) +{ + Variant::Map::const_iterator i = options.find(key); + if (i == options.end()) return EMPTY_VARIANT; + else return i->second; +} + +std::auto_ptr AddressResolution::resolveSource(qpid::client::Session session, + const Address& address, + const Filter* filter, + const Variant::Map& options) +{ + //TODO: handle case where there exists a queue and an exchange of + //the same name (hence an unqualified address is ambiguous) + + //TODO: make sure specified address type gives sane error message + //if it does npt match the configuration on server + + if (isQueue(session, address)) { + //TODO: support auto-created queue as source, if requested by specific option + + AcceptMode accept = getOption(UNACKNOWLEDGED, options).asBool() ? ACCEPT_MODE_NONE : ACCEPT_MODE_EXPLICIT; + AcquireMode acquire = getOption(BROWSE, options).asBool() ? ACQUIRE_MODE_NOT_ACQUIRED : ACQUIRE_MODE_PRE_ACQUIRED; + bool exclusive = getOption(EXCLUSIVE, options).asBool(); + FieldTable arguments; + //TODO: extract subscribe arguments from options (e.g. either + //filter out already processed keys and send the rest, or have + //a nested map) + + std::auto_ptr source = + std::auto_ptr(new QueueSource(address.value, accept, acquire, exclusive, arguments)); + return source; + } else { + //TODO: extract queue options (e.g. no-local) and subscription options (e.g. less important) + std::auto_ptr bindings = + std::auto_ptr(new Subscription(getOption(NAME, options).asString(), + Subscription::getMode(getOption(MODE, options).asString()))); + + qpid::framing::ExchangeQueryResult result = session.exchangeQuery(address.value); + if (result.getNotFound()) { + throw qpid::framing::NotFoundException(QPID_MSG("Address not known: " << address)); + } else if (result.getType() == "topic") { + if (filter) { + if (filter->type != Filter::WILDCARD) { + throw qpid::framing::NotImplementedException( + QPID_MSG("Filters of type " << filter->type << " not supported by address " << address)); + + } + for (std::vector::const_iterator i = filter->patterns.begin(); i != filter->patterns.end(); i++) { + bindings->add(address.value, *i, qpid::framing::FieldTable()); + } + } else { + //default is to receive all messages + bindings->add(address.value, "*", qpid::framing::FieldTable()); + } + } else if (result.getType() == "fanout") { + if (filter) { + throw qpid::framing::NotImplementedException(QPID_MSG("Filters are not supported by address " << address)); + } + bindings->add(address.value, address.value, qpid::framing::FieldTable()); + } else if (result.getType() == "direct") { + //TODO: ???? + } else { + //TODO: xml and headers exchanges + throw qpid::framing::NotImplementedException(QPID_MSG("Address type not recognised for " << address)); + } + std::auto_ptr source = std::auto_ptr(bindings.release()); + return source; + } +} + + +std::auto_ptr AddressResolution::resolveSink(qpid::client::Session session, + const qpid::messaging::Address& address, + const qpid::messaging::Variant::Map& /*options*/) +{ + std::auto_ptr sink; + if (isQueue(session, address)) { + //TODO: support for auto-created queues as sink + sink = std::auto_ptr(new QueueSink(address.value)); + } else { + std::string subject; + if (isTopic(session, address, subject)) { + //TODO: support for auto-created exchanges as sink + sink = std::auto_ptr(new Exchange(address.value, subject)); + } else { + if (address.type.empty()) { + throw qpid::framing::NotFoundException(QPID_MSG("Address not known: " << address)); + } else { + throw qpid::framing::NotImplementedException(QPID_MSG("Address type not recognised: " << address.type)); + } + } + } + return sink; +} + +QueueSource::QueueSource(const std::string& _name, AcceptMode _acceptMode, AcquireMode _acquireMode, bool _exclusive, const FieldTable& _options) : + name(_name), acceptMode(_acceptMode), acquireMode(_acquireMode), exclusive(_exclusive), options(_options) {} + +void QueueSource::subscribe(qpid::client::AsyncSession& session, const std::string& destination) +{ + session.messageSubscribe(arg::queue=name, + arg::destination=destination, + arg::acceptMode=acceptMode, + arg::acquireMode=acquireMode, + arg::exclusive=exclusive, + arg::arguments=options); +} + +void QueueSource::cancel(qpid::client::AsyncSession& session, const std::string& destination) +{ + session.messageCancel(destination); +} + +Subscription::Subscription(const std::string& _name, SubscriptionMode mode, const FieldTable& qOptions, const FieldTable& sOptions) + : name(_name), autoDelete(mode == SIMPLE), durable(mode == DURABLE), + queueOptions(qOptions), subscriptionOptions(sOptions) {} + +void Subscription::add(const std::string& exchange, const std::string& key, const FieldTable& options) +{ + bindings.push_back(Binding(exchange, key, options)); +} + +void Subscription::subscribe(qpid::client::AsyncSession& session, const std::string& destination) +{ + if (name.empty()) { + //TODO: use same scheme as JMS client for subscription queue name generation? + queue = session.getId().getName() + destination; + } else { + queue = name; + } + session.queueDeclare(arg::queue=queue, arg::exclusive=true, + arg::autoDelete=autoDelete, arg::durable=durable, arg::arguments=queueOptions); + for (Bindings::const_iterator i = bindings.begin(); i != bindings.end(); ++i) { + session.exchangeBind(arg::queue=queue, arg::exchange=i->exchange, arg::bindingKey=i->key, arg::arguments=i->options); + } + AcceptMode accept = autoDelete ? ACCEPT_MODE_NONE : ACCEPT_MODE_EXPLICIT; + session.messageSubscribe(arg::queue=queue, arg::destination=destination, + arg::exclusive=true, arg::acceptMode=accept, arg::arguments=subscriptionOptions); +} + +void Subscription::cancel(qpid::client::AsyncSession& session, const std::string& destination) +{ + session.messageCancel(destination); + session.queueDelete(arg::queue=queue); +} + +Subscription::Binding::Binding(const std::string& e, const std::string& k, const FieldTable& o): + exchange(e), key(k), options(o) {} + +Subscription::SubscriptionMode Subscription::getMode(const std::string& s) +{ + if (s.empty() || s == SIMPLE_SUBSCRIPTION) return SIMPLE; + else if (s == RELIABLE_SUBSCRIPTION) return RELIABLE; + else if (s == DURABLE_SUBSCRIPTION) return DURABLE; + else throw Exception(QPID_MSG("Unrecognised subscription mode: " << s)); +} + +void convert(qpid::messaging::Message& from, qpid::client::Message& to); + +Exchange::Exchange(const std::string& _name, const std::string& _defaultSubject, + bool _passive, const std::string& _type, bool _durable, const FieldTable& _options) : + name(_name), defaultSubject(_defaultSubject), passive(_passive), type(_type), durable(_durable), options(_options) {} + +void Exchange::declare(qpid::client::AsyncSession& session, const std::string&) +{ + //TODO: should this really by synchronous? want to get error if not valid... + if (passive) { + sync(session).exchangeDeclare(arg::exchange=name, arg::passive=true); + } else { + sync(session).exchangeDeclare(arg::exchange=name, arg::type=type, arg::durable=durable, arg::arguments=options); + } +} + +void Exchange::send(qpid::client::AsyncSession& session, const std::string&, OutgoingMessage& m) +{ + if (m.message.getDeliveryProperties().getRoutingKey().empty() && !defaultSubject.empty()) { + m.message.getDeliveryProperties().setRoutingKey(defaultSubject); + } + m.status = session.messageTransfer(arg::destination=name, arg::content=m.message); +} + +void Exchange::cancel(qpid::client::AsyncSession&, const std::string&) {} + +QueueSink::QueueSink(const std::string& _name, bool _passive, bool _exclusive, + bool _autoDelete, bool _durable, const FieldTable& _options) : + name(_name), passive(_passive), exclusive(_exclusive), + autoDelete(_autoDelete), durable(_durable), options(_options) {} + +void QueueSink::declare(qpid::client::AsyncSession& session, const std::string&) +{ + //TODO: should this really by synchronous? + if (passive) { + sync(session).queueDeclare(arg::queue=name, arg::passive=true); + } else { + sync(session).queueDeclare(arg::queue=name, arg::exclusive=exclusive, arg::durable=durable, + arg::autoDelete=autoDelete, arg::arguments=options); + } +} +void QueueSink::send(qpid::client::AsyncSession& session, const std::string&, OutgoingMessage& m) +{ + m.message.getDeliveryProperties().setRoutingKey(name); + m.status = session.messageTransfer(arg::content=m.message); +} + +void QueueSink::cancel(qpid::client::AsyncSession&, const std::string&) {} + +template void encode(qpid::messaging::Message& from) +{ + T codec; + from.encode(codec); + from.setContentType(T::contentType); +} + +void translate(const Variant::Map& from, FieldTable& to);//implementation in Codecs.cpp + +void convert(qpid::messaging::Message& from, qpid::client::Message& to) +{ + //TODO: need to avoid copying as much as possible + if (from.getContent().isList()) encode(from); + if (from.getContent().isMap()) encode(from); + to.setData(from.getBytes()); + to.getDeliveryProperties().setRoutingKey(from.getSubject()); + //TODO: set other delivery properties + to.getMessageProperties().setContentType(from.getContentType()); + const Address& address = from.getReplyTo(); + if (!address.value.empty()) { + to.getMessageProperties().setReplyTo(AddressResolution::convert(address)); + } + translate(from.getHeaders(), to.getMessageProperties().getApplicationHeaders()); + //TODO: set other message properties +} + +Address AddressResolution::convert(const qpid::framing::ReplyTo& rt) +{ + if (rt.getExchange().empty()) { + if (rt.getRoutingKey().empty()) { + return Address();//empty address + } else { + return Address(rt.getRoutingKey(), QUEUE_ADDRESS); + } + } else { + if (rt.getRoutingKey().empty()) { + return Address(rt.getExchange(), TOPIC_ADDRESS); + } else { + return Address(rt.getExchange() + DIVIDER + rt.getRoutingKey(), TOPIC_ADDRESS_AND_SUBJECT); + } + } +} + +qpid::framing::ReplyTo AddressResolution::convert(const Address& address) +{ + if (address.type == QUEUE_ADDRESS || address.type.empty()) { + return ReplyTo(EMPTY_STRING, address.value); + } else if (address.type == TOPIC_ADDRESS) { + return ReplyTo(address.value, EMPTY_STRING); + } else if (address.type == TOPIC_ADDRESS_AND_SUBJECT) { + //need to split the value + string::size_type i = address.value.find(DIVIDER); + if (i != string::npos) { + std::string exchange = address.value.substr(0, i); + std::string routingKey; + if (i+1 < address.value.size()) { + routingKey = address.value.substr(i+1); + } + return ReplyTo(exchange, routingKey); + } else { + return ReplyTo(address.value, EMPTY_STRING); + } + } else { + QPID_LOG(notice, "Unrecognised type for reply-to: " << address.type); + //treat as queue + return ReplyTo(EMPTY_STRING, address.value); + } +} + +bool isQueue(qpid::client::Session session, const qpid::messaging::Address& address) +{ + return address.type == QUEUE_ADDRESS || + (address.type.empty() && session.queueQuery(address.value).getQueue() == address.value); +} + +bool isTopic(qpid::client::Session session, const qpid::messaging::Address& address, std::string& subject) +{ + if (address.type.empty()) { + return !session.exchangeQuery(address.value).getNotFound(); + } else if (address.type == TOPIC_ADDRESS) { + return true; + } else if (address.type == TOPIC_ADDRESS_AND_SUBJECT) { + string::size_type i = address.value.find(DIVIDER); + if (i != string::npos) { + std::string exchange = address.value.substr(0, i); + if (i+1 < address.value.size()) { + subject = address.value.substr(i+1); + } + } + return true; + } else { + return false; + } +} + + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.h b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.h new file mode 100644 index 0000000000..9d5657450d --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.h @@ -0,0 +1,68 @@ +#ifndef QPID_CLIENT_AMQP0_10_ADDRESSRESOLUTION_H +#define QPID_CLIENT_AMQP0_10_ADDRESSRESOLUTION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/Variant.h" +#include "qpid/client/Session.h" + +namespace qpid { + +namespace framing{ +class ReplyTo; +} + +namespace messaging { +struct Address; +struct Filter; +} + +namespace client { +namespace amqp0_10 { + +class MessageSource; +class MessageSink; + +/** + * Maps from a generic Address and optional Filter to an AMQP 0-10 + * MessageSource which will then be used by a ReceiverImpl instance + * created for the address. + */ +class AddressResolution +{ + public: + std::auto_ptr resolveSource(qpid::client::Session session, + const qpid::messaging::Address& address, + const qpid::messaging::Filter* filter, + const qpid::messaging::Variant::Map& options); + + std::auto_ptr resolveSink(qpid::client::Session session, + const qpid::messaging::Address& address, + const qpid::messaging::Variant::Map& options); + + static qpid::messaging::Address convert(const qpid::framing::ReplyTo&); + static qpid::framing::ReplyTo convert(const qpid::messaging::Address&); + + private: +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_ADDRESSRESOLUTION_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp b/qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp new file mode 100644 index 0000000000..57184e3937 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp @@ -0,0 +1,299 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/amqp0_10/Codecs.h" +#include "qpid/messaging/Variant.h" +#include "qpid/framing/Array.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/FieldValue.h" +#include "qpid/framing/List.h" +#include +#include + +using namespace qpid::framing; +using namespace qpid::messaging; + +namespace qpid { +namespace client { +namespace amqp0_10 { + +namespace { +const std::string iso885915("iso-8859-15"); +const std::string utf8("utf8"); +const std::string utf16("utf16"); +const std::string amqp0_10_binary("amqp0-10:binary"); +const std::string amqp0_10_bit("amqp0-10:bit"); +const std::string amqp0_10_datetime("amqp0-10:datetime"); +const std::string amqp0_10_struct("amqp0-10:struct"); +} + +template void convert(const T& from, U& to, F f) +{ + std::transform(from.begin(), from.end(), std::inserter(to, to.begin()), f); +} + +Variant::Map::value_type toVariantMapEntry(const FieldTable::value_type& in); +FieldTable::value_type toFieldTableEntry(const Variant::Map::value_type& in); +Variant toVariant(boost::shared_ptr in); +boost::shared_ptr toFieldValue(const Variant& in); + +template void translate(boost::shared_ptr in, U& u, F f) +{ + T t; + getEncodedValue(in, t); + convert(t, u, f); +} + +template T* toFieldValueCollection(const U& u, F f) +{ + typename T::ValueType t; + convert(u, t, f); + return new T(t); +} + +FieldTableValue* toFieldTableValue(const Variant::Map& map) +{ + FieldTable ft; + convert(map, ft, &toFieldTableEntry); + return new FieldTableValue(ft); +} + +ListValue* toListValue(const Variant::List& list) +{ + List l; + convert(list, l, &toFieldValue); + return new ListValue(l); +} + +void setEncodingFor(Variant& out, uint8_t code) +{ + switch(code){ + case 0x80: + case 0x90: + case 0xa0: + out.setEncoding(amqp0_10_binary); + break; + case 0x84: + case 0x94: + out.setEncoding(iso885915); + break; + case 0x85: + case 0x95: + out.setEncoding(utf8); + break; + case 0x86: + case 0x96: + out.setEncoding(utf16); + break; + case 0xab: + out.setEncoding(amqp0_10_struct); + break; + default: + //do nothing + break; + } +} + +Variant toVariant(boost::shared_ptr in) +{ + Variant out; + //based on AMQP 0-10 typecode, pick most appropriate variant type + switch (in->getType()) { + //Fixed Width types: + case 0x01: out.setEncoding(amqp0_10_binary); + case 0x02: out = in->getIntegerValue(); break; + case 0x03: out = in->getIntegerValue(); break; + case 0x04: break; //TODO: iso-8859-15 char + case 0x08: out = in->getIntegerValue(); break; + case 0x010: out.setEncoding(amqp0_10_binary); + case 0x011: out = in->getIntegerValue(); break; + case 0x012: out = in->getIntegerValue(); break; + case 0x020: out.setEncoding(amqp0_10_binary); + case 0x021: out = in->getIntegerValue(); break; + case 0x022: out = in->getIntegerValue(); break; + case 0x023: out = in->get(); break; + case 0x027: break; //TODO: utf-32 char + case 0x030: out.setEncoding(amqp0_10_binary); + case 0x031: out = in->getIntegerValue(); break; + case 0x038: out.setEncoding(amqp0_10_datetime); //treat datetime as uint64_t, but set encoding + case 0x032: out = in->getIntegerValue(); break; + case 0x033:out = in->get(); break; + + //TODO: figure out whether and how to map values with codes 0x40-0xd8 + + case 0xf0: break;//void, which is the default value for Variant + case 0xf1: out.setEncoding(amqp0_10_bit); break;//treat 'bit' as void, which is the default value for Variant + + //Variable Width types: + //strings: + case 0x80: + case 0x84: + case 0x85: + case 0x86: + case 0x90: + case 0x94: + case 0x95: + case 0x96: + case 0xa0: + case 0xab: + setEncodingFor(out, in->getType()); + out = in->get(); + break; + + case 0xa8: + out = Variant::Map(); + translate(in, out.asMap(), &toVariantMapEntry); + break; + + case 0xa9: + out = Variant::List(); + translate(in, out.asList(), &toVariant); + break; + case 0xaa: //convert amqp0-10 array into variant list + out = Variant::List(); + translate(in, out.asList(), &toVariant); + break; + + default: + //error? + break; + } + return out; +} + +boost::shared_ptr toFieldValue(const Variant& in) +{ + boost::shared_ptr out; + switch (in.getType()) { + case VAR_VOID: out = boost::shared_ptr(new VoidValue()); break; + case VAR_BOOL: out = boost::shared_ptr(new BoolValue(in.asBool())); break; + case VAR_UINT8: out = boost::shared_ptr(new Unsigned8Value(in.asUint8())); break; + case VAR_UINT16: out = boost::shared_ptr(new Unsigned16Value(in.asUint16())); break; + case VAR_UINT32: out = boost::shared_ptr(new Unsigned32Value(in.asUint32())); break; + case VAR_UINT64: out = boost::shared_ptr(new Unsigned64Value(in.asUint64())); break; + case VAR_INT8: out = boost::shared_ptr(new Integer8Value(in.asInt8())); break; + case VAR_INT16: out = boost::shared_ptr(new Integer16Value(in.asInt16())); break; + case VAR_INT32: out = boost::shared_ptr(new Integer32Value(in.asInt32())); break; + case VAR_INT64: out = boost::shared_ptr(new Integer64Value(in.asInt64())); break; + case VAR_FLOAT: out = boost::shared_ptr(new FloatValue(in.asFloat())); break; + case VAR_DOUBLE: out = boost::shared_ptr(new DoubleValue(in.asDouble())); break; + //TODO: check encoding (and length?) when deciding what AMQP type to treat string as + case VAR_STRING: out = boost::shared_ptr(new Str16Value(in.asString())); break; + case VAR_MAP: + //out = boost::shared_ptr(toFieldValueCollection(in.asMap(), &toFieldTableEntry)); + out = boost::shared_ptr(toFieldTableValue(in.asMap())); + break; + case VAR_LIST: + //out = boost::shared_ptr(toFieldValueCollection(in.asList(), &toFieldValue)); + out = boost::shared_ptr(toListValue(in.asList())); + break; + } + return out; +} + +Variant::Map::value_type toVariantMapEntry(const FieldTable::value_type& in) +{ + return Variant::Map::value_type(in.first, toVariant(in.second)); +} + +FieldTable::value_type toFieldTableEntry(const Variant::Map::value_type& in) +{ + return FieldTable::value_type(in.first, toFieldValue(in.second)); +} + +struct EncodeBuffer +{ + char* data; + Buffer buffer; + + EncodeBuffer(size_t size) : data(new char[size]), buffer(data, size) {} + ~EncodeBuffer() { delete[] data; } + + template void encode(T& t) { t.encode(buffer); } + + void getData(std::string& s) { + s.assign(data, buffer.getSize()); + } +}; + +struct DecodeBuffer +{ + Buffer buffer; + + DecodeBuffer(const std::string& s) : buffer(const_cast(s.data()), s.size()) {} + + template void decode(T& t) { t.decode(buffer); } + +}; + +template void _encode(const U& value, std::string& data, F f) +{ + T t; + convert(value, t, f); + EncodeBuffer buffer(t.encodedSize()); + buffer.encode(t); + buffer.getData(data); +} + +template void _decode(const std::string& data, U& value, F f) +{ + T t; + DecodeBuffer buffer(data); + buffer.decode(t); + convert(t, value, f); +} + +void MapCodec::encode(const Variant& value, std::string& data) +{ + _encode(value.asMap(), data, &toFieldTableEntry); +} + +void MapCodec::decode(const std::string& data, Variant& value) +{ + value = Variant::Map(); + _decode(data, value.asMap(), &toVariantMapEntry); +} + +void ListCodec::encode(const Variant& value, std::string& data) +{ + _encode(value.asList(), data, &toFieldValue); +} + +void ListCodec::decode(const std::string& data, Variant& value) +{ + value = Variant::List(); + _decode(data, value.asList(), &toVariant); +} + +void translate(const Variant::Map& from, FieldTable& to) +{ + convert(from, to, &toFieldTableEntry); +} + +void translate(const FieldTable& from, Variant::Map& to) +{ + convert(from, to, &toVariantMapEntry); +} + +const std::string ListCodec::contentType("amqp0_10/list"); +const std::string MapCodec::contentType("amqp0_10/map"); + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/CodecsInternal.h b/qpid/cpp/src/qpid/client/amqp0_10/CodecsInternal.h new file mode 100644 index 0000000000..b5a561a9c3 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/CodecsInternal.h @@ -0,0 +1,41 @@ +#ifndef QPID_CLIENT_AMQP0_10_CODECSINTERNAL_H +#define QPID_CLIENT_AMQP0_10_CODECSINTERNAL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/Variant.h" +#include "qpid/framing/FieldTable.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + +/** + * Declarations of a couple of conversion functions implemented in + * Codecs.cpp but not exposed through API + */ + +void translate(const qpid::messaging::Variant::Map& from, qpid::framing::FieldTable& to); +void translate(const qpid::framing::FieldTable& from, qpid::messaging::Variant::Map& to); + +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_CODECSINTERNAL_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.cpp b/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.cpp new file mode 100644 index 0000000000..52b623b65c --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.cpp @@ -0,0 +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. + * + */ +#include "CompletionTracker.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + +using qpid::framing::SequenceNumber; + +void CompletionTracker::track(SequenceNumber command, void* token) +{ + tokens[command] = token; +} + +void CompletionTracker::completedTo(SequenceNumber command) +{ + Tokens::iterator i = tokens.lower_bound(command); + if (i != tokens.end()) { + lastCompleted = i->second; + tokens.erase(tokens.begin(), ++i); + } +} + +void* CompletionTracker::getLastCompletedToken() +{ + return lastCompleted; +} + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.h b/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.h new file mode 100644 index 0000000000..6147c5682e --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.h @@ -0,0 +1,50 @@ +#ifndef QPID_CLIENT_AMQP0_10_COMPLETIONTRACKER_H +#define QPID_CLIENT_AMQP0_10_COMPLETIONTRACKER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/framing/SequenceNumber.h" +#include + +namespace qpid { +namespace client { +namespace amqp0_10 { + +/** + * Provides a mapping from command ids to application supplied + * 'tokens', and is used to determine when the sending or + * acknowledging of a specific message is complete. + */ +class CompletionTracker +{ + public: + void track(qpid::framing::SequenceNumber command, void* token); + void completedTo(qpid::framing::SequenceNumber command); + void* getLastCompletedToken(); + private: + typedef std::map Tokens; + Tokens tokens; + void* lastCompleted; +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_COMPLETIONTRACKER_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp new file mode 100644 index 0000000000..3a735b5698 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp @@ -0,0 +1,181 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "ConnectionImpl.h" +#include "SessionImpl.h" +#include "qpid/messaging/Session.h" +#include "qpid/client/PrivateImplRef.h" +#include "qpid/log/Statement.h" +#include + +namespace qpid { +namespace client { +namespace amqp0_10 { + +using qpid::messaging::Variant; +using namespace qpid::sys; + +template void setIfFound(const Variant::Map& map, const std::string& key, T& value) +{ + Variant::Map::const_iterator i = map.find(key); + if (i != map.end()) { + value = (T) i->second; + } +} + +void convert(const Variant::Map& from, ConnectionSettings& to) +{ + setIfFound(from, "username", to.username); + setIfFound(from, "password", to.password); + setIfFound(from, "sasl-mechanism", to.mechanism); + setIfFound(from, "sasl-service", to.service); + setIfFound(from, "sasl-min-ssf", to.minSsf); + setIfFound(from, "sasl-max-ssf", to.maxSsf); + + setIfFound(from, "heartbeat", to.heartbeat); + setIfFound(from, "tcp-nodelay", to.tcpNoDelay); + + setIfFound(from, "locale", to.locale); + setIfFound(from, "max-channels", to.maxChannels); + setIfFound(from, "max-frame-size", to.maxFrameSize); + setIfFound(from, "bounds", to.bounds); +} + +ConnectionImpl::ConnectionImpl(const std::string& u, const Variant::Map& options) : + url(u), reconnectionEnabled(true), timeout(-1), + minRetryInterval(1), maxRetryInterval(30) +{ + QPID_LOG(debug, "Opening connection to " << url << " with " << options); + convert(options, settings); + setIfFound(options, "reconnection-enabled", reconnectionEnabled); + setIfFound(options, "reconnection-timeout", timeout); + setIfFound(options, "min-retry-interval", minRetryInterval); + setIfFound(options, "max-retry-interval", maxRetryInterval); + connection.open(url, settings); +} + +void ConnectionImpl::close() +{ + qpid::sys::Mutex::ScopedLock l(lock); + connection.close(); +} + +boost::intrusive_ptr getImplPtr(qpid::messaging::Session& session) +{ + return boost::dynamic_pointer_cast( + qpid::client::PrivateImplRef::get(session) + ); +} + +void ConnectionImpl::closed(SessionImpl& s) +{ + qpid::sys::Mutex::ScopedLock l(lock); + for (Sessions::iterator i = sessions.begin(); i != sessions.end(); ++i) { + if (getImplPtr(*i).get() == &s) { + sessions.erase(i); + break; + } + } +} + +qpid::messaging::Session ConnectionImpl::newSession() +{ + qpid::messaging::Session impl(new SessionImpl(*this)); + { + qpid::sys::Mutex::ScopedLock l(lock); + sessions.push_back(impl); + } + try { + getImplPtr(impl)->setSession(connection.newSession()); + } catch (const TransportFailure&) { + reconnect(); + } + return impl; +} + +void ConnectionImpl::reconnect() +{ + AbsTime start = now(); + ScopedLock l(semaphore); + if (!connection.isOpen()) connect(start); +} + +bool expired(const AbsTime& start, int timeout) +{ + if (timeout == 0) return true; + if (timeout < 0) return false; + Duration used(start, now()); + Duration allowed = timeout * TIME_SEC; + return allowed > used; +} + +void ConnectionImpl::connect(const AbsTime& started) +{ + for (int i = minRetryInterval; !tryConnect(); i = std::min(i * 2, maxRetryInterval)) { + if (expired(started, timeout)) throw TransportFailure(); + else qpid::sys::sleep(i); + } +} + +bool ConnectionImpl::tryConnect() +{ + if (tryConnect(url) || tryConnect(connection.getKnownBrokers())) { + return resetSessions(); + } else { + return false; + } +} + +bool ConnectionImpl::tryConnect(const Url& u) +{ + try { + QPID_LOG(info, "Trying to connect to " << url << "..."); + connection.open(u, settings); + return true; + } catch (const Exception& e) { + //TODO: need to fix timeout on open so that it throws TransportFailure + QPID_LOG(info, "Failed to connect to " << u << ": " << e.what()); + } + return false; +} + +bool ConnectionImpl::tryConnect(const std::vector& urls) +{ + for (std::vector::const_iterator i = urls.begin(); i != urls.end(); ++i) { + if (tryConnect(*i)) return true; + } + return false; +} + +bool ConnectionImpl::resetSessions() +{ + try { + qpid::sys::Mutex::ScopedLock l(lock); + for (Sessions::iterator i = sessions.begin(); i != sessions.end(); ++i) { + getImplPtr(*i)->setSession(connection.newSession()); + } + return true; + } catch (const TransportFailure&) { + QPID_LOG(debug, "Connection failed while re-inialising sessions"); + return false; + } +} + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h new file mode 100644 index 0000000000..565f2ec7ec --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h @@ -0,0 +1,69 @@ +#ifndef QPID_CLIENT_AMQP0_10_CONNECTIONIMPL_H +#define QPID_CLIENT_AMQP0_10_CONNECTIONIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/ConnectionImpl.h" +#include "qpid/messaging/Variant.h" +#include "qpid/Url.h" +#include "qpid/client/Connection.h" +#include "qpid/client/ConnectionSettings.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Semaphore.h" +#include + +namespace qpid { +namespace client { +namespace amqp0_10 { + +class SessionImpl; + +class ConnectionImpl : public qpid::messaging::ConnectionImpl +{ + public: + ConnectionImpl(const std::string& url, const qpid::messaging::Variant::Map& options); + void close(); + qpid::messaging::Session newSession(); + void closed(SessionImpl&); + void reconnect(); + private: + typedef std::vector Sessions; + + qpid::sys::Mutex lock;//used to protect data structures + qpid::sys::Semaphore semaphore;//used to coordinate reconnection + qpid::client::Connection connection; + qpid::Url url; + qpid::client::ConnectionSettings settings; + Sessions sessions; + bool reconnectionEnabled; + int timeout; + int minRetryInterval; + int maxRetryInterval; + + void connect(const qpid::sys::AbsTime& started); + bool tryConnect(); + bool tryConnect(const std::vector& urls); + bool tryConnect(const Url&); + bool resetSessions(); +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_CONNECTIONIMPL_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp new file mode 100644 index 0000000000..b0a16674e1 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp @@ -0,0 +1,244 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/amqp0_10/IncomingMessages.h" +#include "qpid/client/amqp0_10/AddressResolution.h" +#include "qpid/client/amqp0_10/Codecs.h" +#include "qpid/client/amqp0_10/CodecsInternal.h" +#include "qpid/client/SessionImpl.h" +#include "qpid/client/SessionBase_0_10Access.h" +#include "qpid/log/Statement.h" +#include "qpid/messaging/Address.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/MessageImpl.h" +#include "qpid/messaging/Variant.h" +#include "qpid/framing/DeliveryProperties.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/framing/MessageProperties.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/enum.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + +using namespace qpid::framing; +using namespace qpid::framing::message; +using qpid::sys::AbsTime; +using qpid::sys::Duration; +using qpid::messaging::MessageImplAccess; +using qpid::messaging::Variant; + +namespace { +const std::string EMPTY_STRING; + + +struct GetNone : IncomingMessages::Handler +{ + bool accept(IncomingMessages::MessageTransfer&) { return false; } +}; + +struct GetAny : IncomingMessages::Handler +{ + bool accept(IncomingMessages::MessageTransfer& transfer) + { + transfer.retrieve(0); + return true; + } +}; + +struct MatchAndTrack +{ + const std::string destination; + SequenceSet ids; + + MatchAndTrack(const std::string& d) : destination(d) {} + + bool operator()(boost::shared_ptr command) + { + if (command->as()->getDestination() == destination) { + ids.add(command->getId()); + return true; + } else { + return false; + } + } +}; +} + +void IncomingMessages::setSession(qpid::client::AsyncSession s) +{ + session = s; + incoming = SessionBase_0_10Access(session).get()->getDemux().getDefault(); +} + +bool IncomingMessages::get(Handler& handler, Duration timeout) +{ + //search through received list for any transfer of interest: + for (FrameSetQueue::iterator i = received.begin(); i != received.end(); i++) + { + MessageTransfer transfer(*i, *this); + if (handler.accept(transfer)) { + received.erase(i); + return true; + } + } + //none found, check incoming: + return process(&handler, timeout); +} + +void IncomingMessages::accept() +{ + session.messageAccept(unaccepted); + unaccepted.clear(); +} + +void IncomingMessages::releaseAll() +{ + //first process any received messages... + while (!received.empty()) { + retrieve(received.front(), 0); + received.pop_front(); + } + //then pump out any available messages from incoming queue... + GetAny handler; + while (process(&handler, 0)) ; + //now release all messages + session.messageRelease(unaccepted); + unaccepted.clear(); +} + +void IncomingMessages::releasePending(const std::string& destination) +{ + //first pump all available messages from incoming to received... + while (process(0, 0)) ; + + //now remove all messages for this destination from received list, recording their ids... + MatchAndTrack match(destination); + for (FrameSetQueue::iterator i = received.begin(); i != received.end(); i = match(*i) ? received.erase(i) : ++i) ; + //now release those messages + session.messageRelease(match.ids); +} + +/** + * Get a frameset from session queue, waiting for up to the specified + * duration and returning true if this could be achieved, false + * otherwise. If a destination is supplied, only return a message for + * that destination. In this case messages from other destinations + * will be held on a received queue. + */ +bool IncomingMessages::process(Handler* handler, qpid::sys::Duration duration) +{ + AbsTime deadline(AbsTime::now(), duration); + FrameSet::shared_ptr content; + for (Duration timeout = duration; incoming->pop(content, timeout); timeout = Duration(AbsTime::now(), deadline)) { + if (content->isA()) { + MessageTransfer transfer(content, *this); + if (handler && handler->accept(transfer)) { + QPID_LOG(debug, "Delivered " << *content->getMethod()); + return true; + } else { + //received message for another destination, keep for later + QPID_LOG(debug, "Pushed " << *content->getMethod() << " to received queue"); + received.push_back(content); + } + } else { + //TODO: handle other types of commands (e.g. message-accept, message-flow etc) + } + } + return false; +} + +void populate(qpid::messaging::Message& message, FrameSet& command); + +/** + * Called when message is retrieved; records retrieval for subsequent + * acceptance, marks the command as completed and converts command to + * message if message is required + */ +void IncomingMessages::retrieve(FrameSetPtr command, qpid::messaging::Message* message) +{ + if (message) { + populate(*message, *command); + } + const MessageTransferBody* transfer = command->as(); + if (transfer->getAcquireMode() == ACQUIRE_MODE_PRE_ACQUIRED && transfer->getAcceptMode() == ACCEPT_MODE_EXPLICIT) { + unaccepted.add(command->getId()); + } + session.markCompleted(command->getId(), false, false); +} + +IncomingMessages::MessageTransfer::MessageTransfer(FrameSetPtr c, IncomingMessages& p) : content(c), parent(p) {} + +const std::string& IncomingMessages::MessageTransfer::getDestination() +{ + return content->as()->getDestination(); +} +void IncomingMessages::MessageTransfer::retrieve(qpid::messaging::Message* message) +{ + parent.retrieve(content, message); +} + +void populateHeaders(qpid::messaging::Message& message, + const DeliveryProperties* deliveryProperties, + const MessageProperties* messageProperties) +{ + if (deliveryProperties) { + message.setSubject(deliveryProperties->getRoutingKey()); + //TODO: convert other delivery properties + } + if (messageProperties) { + message.setContentType(messageProperties->getContentType()); + if (messageProperties->hasReplyTo()) { + message.setReplyTo(AddressResolution::convert(messageProperties->getReplyTo())); + } + message.getHeaders().clear(); + translate(messageProperties->getApplicationHeaders(), message.getHeaders()); + //TODO: convert other message properties + } +} + +void populateHeaders(qpid::messaging::Message& message, const AMQHeaderBody* headers) +{ + populateHeaders(message, headers->get(), headers->get()); +} + +void populate(qpid::messaging::Message& message, FrameSet& command) +{ + //need to be able to link the message back to the transfer it was delivered by + //e.g. for rejecting. + MessageImplAccess::get(message).setInternalId(command.getId()); + + command.getContent(message.getBytes()); + + populateHeaders(message, command.getHeaders()); + + //decode content if necessary + if (message.getContentType() == ListCodec::contentType) { + ListCodec codec; + message.decode(codec); + } else if (message.getContentType() == MapCodec::contentType) { + MapCodec codec; + message.decode(codec); + } +} + + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h new file mode 100644 index 0000000000..5e28877305 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h @@ -0,0 +1,91 @@ +#ifndef QPID_CLIENT_AMQP0_10_INCOMINGMESSAGES_H +#define QPID_CLIENT_AMQP0_10_INCOMINGMESSAGES_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include +#include +#include "qpid/client/AsyncSession.h" +#include "qpid/framing/SequenceSet.h" +#include "qpid/sys/BlockingQueue.h" +#include "qpid/sys/Time.h" + +namespace qpid { + +namespace framing{ +class FrameSet; +} + +namespace messaging { +class Message; +} + +namespace client { +namespace amqp0_10 { + +/** + * + */ +class IncomingMessages +{ + public: + typedef boost::shared_ptr FrameSetPtr; + class MessageTransfer + { + public: + const std::string& getDestination(); + void retrieve(qpid::messaging::Message* message); + private: + FrameSetPtr content; + IncomingMessages& parent; + + MessageTransfer(FrameSetPtr, IncomingMessages&); + friend class IncomingMessages; + }; + + struct Handler + { + virtual ~Handler() {} + virtual bool accept(MessageTransfer& transfer) = 0; + }; + + void setSession(qpid::client::AsyncSession session); + bool get(Handler& handler, qpid::sys::Duration timeout); + //bool get(qpid::messaging::Message& message, qpid::sys::Duration timeout); + //bool get(const std::string& destination, qpid::messaging::Message& message, qpid::sys::Duration timeout); + void accept(); + void releaseAll(); + void releasePending(const std::string& destination); + private: + typedef std::deque FrameSetQueue; + + qpid::client::AsyncSession session; + qpid::framing::SequenceSet unaccepted; + boost::shared_ptr< sys::BlockingQueue > incoming; + FrameSetQueue received; + + bool process(Handler*, qpid::sys::Duration); + void retrieve(FrameSetPtr, qpid::messaging::Message*); + +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_INCOMINGMESSAGES_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h b/qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h new file mode 100644 index 0000000000..d66d2ecb3c --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h @@ -0,0 +1,52 @@ +#ifndef QPID_CLIENT_AMQP0_10_MESSAGESINK_H +#define QPID_CLIENT_AMQP0_10_MESSAGESINK_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include +#include "qpid/client/AsyncSession.h" + +namespace qpid { + +namespace messaging { +class Message; +} + +namespace client { +namespace amqp0_10 { + +class OutgoingMessage; + +/** + * + */ +class MessageSink +{ + public: + virtual ~MessageSink() {} + virtual void declare(qpid::client::AsyncSession& session, const std::string& name) = 0; + virtual void send(qpid::client::AsyncSession& session, const std::string& name, OutgoingMessage& message) = 0; + virtual void cancel(qpid::client::AsyncSession& session, const std::string& name) = 0; + private: +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_MESSAGESINK_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/MessageSource.h b/qpid/cpp/src/qpid/client/amqp0_10/MessageSource.h new file mode 100644 index 0000000000..74f2732f59 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/MessageSource.h @@ -0,0 +1,47 @@ +#ifndef QPID_CLIENT_AMQP0_10_MESSAGESOURCE_H +#define QPID_CLIENT_AMQP0_10_MESSAGESOURCE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include +#include "qpid/client/AsyncSession.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + +/** + * Abstraction behind which the AMQP 0-10 commands required to + * establish (and tear down) an incoming stream of messages from a + * given address are hidden. + */ +class MessageSource +{ + public: + virtual ~MessageSource() {} + virtual void subscribe(qpid::client::AsyncSession& session, const std::string& destination) = 0; + virtual void cancel(qpid::client::AsyncSession& session, const std::string& destination) = 0; + + private: +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_MESSAGESOURCE_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp b/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp new file mode 100644 index 0000000000..716f955f98 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp @@ -0,0 +1,64 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/amqp0_10/OutgoingMessage.h" +#include "qpid/client/amqp0_10/AddressResolution.h" +#include "qpid/client/amqp0_10/Codecs.h" +#include "qpid/client/amqp0_10/CodecsInternal.h" +#include "qpid/messaging/Address.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/MessageImpl.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + +using qpid::messaging::Address; +using qpid::messaging::MessageImplAccess; + +template void encode(const qpid::messaging::Message& from, qpid::client::Message& to) +{ + T codec; + MessageImplAccess::get(from).getEncodedContent(codec, to.getData()); + to.getMessageProperties().setContentType(T::contentType); +} + +void OutgoingMessage::convert(const qpid::messaging::Message& from) +{ + //TODO: need to avoid copying as much as possible + if (from.getContent().isList()) { + encode(from, message); + } else if (from.getContent().isMap()) { + encode(from, message); + } else { + message.setData(from.getBytes()); + message.getMessageProperties().setContentType(from.getContentType()); + } + const Address& address = from.getReplyTo(); + if (!address.value.empty()) { + message.getMessageProperties().setReplyTo(AddressResolution::convert(address)); + } + translate(from.getHeaders(), message.getMessageProperties().getApplicationHeaders()); + //TODO: set other message properties + message.getDeliveryProperties().setRoutingKey(from.getSubject()); + //TODO: set other delivery properties +} + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.h b/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.h new file mode 100644 index 0000000000..8801e4e769 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.h @@ -0,0 +1,46 @@ +#ifndef QPID_CLIENT_AMQP0_10_OUTGOINGMESSAGE_H +#define QPID_CLIENT_AMQP0_10_OUTGOINGMESSAGE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/Completion.h" +#include "qpid/client/Message.h" + +namespace qpid { +namespace messaging { +class Message; +} +namespace client { +namespace amqp0_10 { + +struct OutgoingMessage +{ + qpid::client::Message message; + qpid::client::Completion status; + + void convert(const qpid::messaging::Message&); +}; + + + +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_OUTGOINGMESSAGE_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp new file mode 100644 index 0000000000..31efff38a6 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp @@ -0,0 +1,190 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "ReceiverImpl.h" +#include "AddressResolution.h" +#include "MessageSource.h" +#include "SessionImpl.h" +#include "qpid/messaging/MessageListener.h" +#include "qpid/messaging/Receiver.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + +using qpid::messaging::Receiver; + +void ReceiverImpl::received(qpid::messaging::Message&) +{ + //TODO: should this be configurable + if (capacity && --window <= capacity/2) { + session.sendCompletion(); + window = capacity; + } +} + +qpid::messaging::Message ReceiverImpl::get(qpid::sys::Duration timeout) +{ + qpid::messaging::Message result; + if (!get(result, timeout)) throw Receiver::NoMessageAvailable(); + return result; +} + +qpid::messaging::Message ReceiverImpl::fetch(qpid::sys::Duration timeout) +{ + qpid::messaging::Message result; + if (!fetch(result, timeout)) throw Receiver::NoMessageAvailable(); + return result; +} + +bool ReceiverImpl::get(qpid::messaging::Message& message, qpid::sys::Duration timeout) +{ + Get f(*this, message, timeout); + while (!parent.execute(f)) {} + return f.result; +} + +bool ReceiverImpl::fetch(qpid::messaging::Message& message, qpid::sys::Duration timeout) +{ + Fetch f(*this, message, timeout); + while (!parent.execute(f)) {} + return f.result; +} + +void ReceiverImpl::cancel() +{ + execute(); +} + +void ReceiverImpl::start() +{ + execute(); +} + +void ReceiverImpl::stop() +{ + execute(); +} + +void ReceiverImpl::setCapacity(uint32_t c) +{ + execute1(c); +} + +void ReceiverImpl::startFlow() +{ + if (capacity > 0) { + session.messageSetFlowMode(destination, FLOW_MODE_WINDOW); + session.messageFlow(destination, CREDIT_UNIT_MESSAGE, capacity); + session.messageFlow(destination, CREDIT_UNIT_BYTE, byteCredit); + window = capacity; + } +} + +void ReceiverImpl::init(qpid::client::AsyncSession s, AddressResolution& resolver) +{ + + session = s; + if (state == UNRESOLVED) { + source = resolver.resolveSource(session, address, filter, options); + state = STOPPED;//TODO: if session is started, go straight to started + } + if (state == CANCELLED) { + source->cancel(session, destination); + parent.receiverCancelled(destination); + } else { + source->subscribe(session, destination); + if (state == STARTED) start(); + } +} + +void ReceiverImpl::setListener(qpid::messaging::MessageListener* l) { listener = l; } +qpid::messaging::MessageListener* ReceiverImpl::getListener() { return listener; } + +const std::string& ReceiverImpl::getName() const { return destination; } + +ReceiverImpl::ReceiverImpl(SessionImpl& p, const std::string& name, + const qpid::messaging::Address& a, + const qpid::messaging::Filter* f, + const qpid::messaging::Variant::Map& o) : + + parent(p), destination(name), address(a), filter(f), options(o), byteCredit(0xFFFFFFFF), + state(UNRESOLVED), capacity(0), listener(0), window(0) {} + +bool ReceiverImpl::getImpl(qpid::messaging::Message& message, qpid::sys::Duration timeout) +{ + return parent.get(*this, message, timeout); +} + +bool ReceiverImpl::fetchImpl(qpid::messaging::Message& message, qpid::sys::Duration timeout) +{ + if (state == CANCELLED) return false;//TODO: or should this be an error? + + if (capacity == 0 || state != STARTED) { + session.messageSetFlowMode(destination, FLOW_MODE_CREDIT); + session.messageFlow(destination, CREDIT_UNIT_MESSAGE, 1); + session.messageFlow(destination, CREDIT_UNIT_BYTE, 0xFFFFFFFF); + } + + if (getImpl(message, timeout)) { + return true; + } else { + sync(session).messageFlush(destination); + startFlow();//reallocate credit + return getImpl(message, 0); + } +} + +void ReceiverImpl::cancelImpl() +{ + if (state != CANCELLED) { + state = CANCELLED; + source->cancel(session, destination); + parent.receiverCancelled(destination); + } +} + +void ReceiverImpl::startImpl() +{ + if (state == STOPPED) { + state = STARTED; + startFlow(); + } +} + +void ReceiverImpl::stopImpl() +{ + state = STOPPED; + session.messageStop(destination); +} + +void ReceiverImpl::setCapacityImpl(uint32_t c) +{ + if (c != capacity) { + capacity = c; + if (state == STARTED) { + session.messageStop(destination); + startFlow(); + } + } +} + + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h new file mode 100644 index 0000000000..509c784513 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h @@ -0,0 +1,165 @@ +#ifndef QPID_CLIENT_AMQP0_10_RECEIVERIMPL_H +#define QPID_CLIENT_AMQP0_10_RECEIVERIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/Address.h" +#include "qpid/messaging/Filter.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/ReceiverImpl.h" +#include "qpid/messaging/Variant.h" +#include "qpid/client/AsyncSession.h" +#include "qpid/client/amqp0_10/SessionImpl.h" +#include "qpid/sys/Time.h" +#include + +namespace qpid { +namespace client { +namespace amqp0_10 { + +class AddressResolution; +class MessageSource; + +/** + * A receiver implementation based on an AMQP 0-10 subscription. + */ +class ReceiverImpl : public qpid::messaging::ReceiverImpl +{ + public: + + enum State {UNRESOLVED, STOPPED, STARTED, CANCELLED}; + + ReceiverImpl(SessionImpl& parent, const std::string& name, + const qpid::messaging::Address& address, + const qpid::messaging::Filter* filter, + const qpid::messaging::Variant::Map& options); + + void init(qpid::client::AsyncSession session, AddressResolution& resolver); + bool get(qpid::messaging::Message& message, qpid::sys::Duration timeout); + qpid::messaging::Message get(qpid::sys::Duration timeout); + bool fetch(qpid::messaging::Message& message, qpid::sys::Duration timeout); + qpid::messaging::Message fetch(qpid::sys::Duration timeout); + void cancel(); + void start(); + void stop(); + const std::string& getName() const; + void setCapacity(uint32_t); + void setListener(qpid::messaging::MessageListener* listener); + qpid::messaging::MessageListener* getListener(); + void received(qpid::messaging::Message& message); + private: + SessionImpl& parent; + const std::string destination; + const qpid::messaging::Address address; + const qpid::messaging::Filter* filter; + const qpid::messaging::Variant::Map options; + const uint32_t byteCredit; + State state; + + std::auto_ptr source; + uint32_t capacity; + qpid::client::AsyncSession session; + qpid::messaging::MessageListener* listener; + uint32_t window; + + void startFlow(); + //implementation of public facing methods + bool fetchImpl(qpid::messaging::Message& message, qpid::sys::Duration timeout); + bool getImpl(qpid::messaging::Message& message, qpid::sys::Duration timeout); + void startImpl(); + void stopImpl(); + void cancelImpl(); + void setCapacityImpl(uint32_t); + + //functors for public facing methods (allows locking and retry + //logic to be centralised) + struct Command + { + ReceiverImpl& impl; + + Command(ReceiverImpl& i) : impl(i) {} + }; + + struct Get : Command + { + qpid::messaging::Message& message; + qpid::sys::Duration timeout; + bool result; + + Get(ReceiverImpl& i, qpid::messaging::Message& m, qpid::sys::Duration t) : + Command(i), message(m), timeout(t), result(false) {} + void operator()() { result = impl.getImpl(message, timeout); } + }; + + struct Fetch : Command + { + qpid::messaging::Message& message; + qpid::sys::Duration timeout; + bool result; + + Fetch(ReceiverImpl& i, qpid::messaging::Message& m, qpid::sys::Duration t) : + Command(i), message(m), timeout(t), result(false) {} + void operator()() { result = impl.fetchImpl(message, timeout); } + }; + + struct Stop : Command + { + Stop(ReceiverImpl& i) : Command(i) {} + void operator()() { impl.stopImpl(); } + }; + + struct Start : Command + { + Start(ReceiverImpl& i) : Command(i) {} + void operator()() { impl.startImpl(); } + }; + + struct Cancel : Command + { + Cancel(ReceiverImpl& i) : Command(i) {} + void operator()() { impl.cancelImpl(); } + }; + + struct SetCapacity : Command + { + uint32_t capacity; + + SetCapacity(ReceiverImpl& i, uint32_t c) : Command(i), capacity(c) {} + void operator()() { impl.setCapacityImpl(capacity); } + }; + + //helper templates for some common patterns + template void execute() + { + F f(*this); + parent.execute(f); + } + + template void execute1(P p) + { + F f(*this, p); + parent.execute(f); + } +}; + +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_RECEIVERIMPL_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp new file mode 100644 index 0000000000..c619d1226a --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp @@ -0,0 +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. + * + */ +#include "SenderImpl.h" +#include "MessageSink.h" +#include "SessionImpl.h" +#include "AddressResolution.h" +#include "OutgoingMessage.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + +SenderImpl::SenderImpl(SessionImpl& _parent, const std::string& _name, + const qpid::messaging::Address& _address, + const qpid::messaging::Variant::Map& _options) : + parent(_parent), name(_name), address(_address), options(_options), state(UNRESOLVED), + capacity(50), window(0) {} + +void SenderImpl::send(const qpid::messaging::Message& m) +{ + execute1(&m); +} + +void SenderImpl::cancel() +{ + execute(); +} + +void SenderImpl::init(qpid::client::AsyncSession s, AddressResolution& resolver) +{ + session = s; + if (state == UNRESOLVED) { + sink = resolver.resolveSink(session, address, options); + state = ACTIVE; + } + if (state == CANCELLED) { + sink->cancel(session, name); + parent.senderCancelled(name); + } else { + sink->declare(session, name); + replay(); + } +} + +void SenderImpl::sendImpl(const qpid::messaging::Message& m) +{ + //TODO: make recoding for replay optional + std::auto_ptr msg(new OutgoingMessage()); + msg->convert(m); + outgoing.push_back(msg.release()); + sink->send(session, name, outgoing.back()); + if (++window > (capacity / 2)) {//TODO: make this configurable? + session.flush(); + checkPendingSends(); + window = 0; + } +} + +void SenderImpl::replay() +{ + for (OutgoingMessages::iterator i = outgoing.begin(); i != outgoing.end(); ++i) { + sink->send(session, name, *i); + } +} + +void SenderImpl::checkPendingSends() +{ + while (!outgoing.empty() && outgoing.front().status.isComplete()) { + outgoing.pop_front(); + } +} + +void SenderImpl::cancelImpl() +{ + state = CANCELLED; + sink->cancel(session, name); + parent.senderCancelled(name); +} + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h new file mode 100644 index 0000000000..4ba793d71c --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h @@ -0,0 +1,118 @@ +#ifndef QPID_CLIENT_AMQP0_10_SENDERIMPL_H +#define QPID_CLIENT_AMQP0_10_SENDERIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/Address.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/SenderImpl.h" +#include "qpid/messaging/Variant.h" +#include "qpid/client/AsyncSession.h" +#include "qpid/client/amqp0_10/SessionImpl.h" +#include +#include + +namespace qpid { +namespace client { +namespace amqp0_10 { + +class AddressResolution; +class MessageSink; +class OutgoingMessage; + +/** + * + */ +class SenderImpl : public qpid::messaging::SenderImpl +{ + public: + enum State {UNRESOLVED, ACTIVE, CANCELLED}; + + SenderImpl(SessionImpl& parent, const std::string& name, + const qpid::messaging::Address& address, + const qpid::messaging::Variant::Map& options); + void send(const qpid::messaging::Message&); + void cancel(); + void init(qpid::client::AsyncSession, AddressResolution&); + + private: + SessionImpl& parent; + const std::string name; + const qpid::messaging::Address address; + const qpid::messaging::Variant::Map options; + State state; + std::auto_ptr sink; + + qpid::client::AsyncSession session; + std::string destination; + std::string routingKey; + + typedef boost::ptr_deque OutgoingMessages; + OutgoingMessages outgoing; + uint32_t capacity; + uint32_t window; + + void checkPendingSends(); + void replay(); + + //logic for application visible methods: + void sendImpl(const qpid::messaging::Message&); + void cancelImpl(); + + //functors for application visible methods (allowing locking and + //retry to be centralised): + struct Command + { + SenderImpl& impl; + + Command(SenderImpl& i) : impl(i) {} + }; + + struct Send : Command + { + const qpid::messaging::Message* message; + + Send(SenderImpl& i, const qpid::messaging::Message* m) : Command(i), message(m) {} + void operator()() { impl.sendImpl(*message); } + }; + + struct Cancel : Command + { + Cancel(SenderImpl& i) : Command(i) {} + void operator()() { impl.cancelImpl(); } + }; + + //helper templates for some common patterns + template void execute() + { + F f(*this); + parent.execute(f); + } + + template void execute1(P p) + { + F f(*this, p); + parent.execute(f); + } +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_SENDERIMPL_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp new file mode 100644 index 0000000000..0e6c430d89 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp @@ -0,0 +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. + * + */ +#include "qpid/client/amqp0_10/SessionImpl.h" +#include "qpid/client/amqp0_10/ConnectionImpl.h" +#include "qpid/client/amqp0_10/ReceiverImpl.h" +#include "qpid/client/amqp0_10/SenderImpl.h" +#include "qpid/client/amqp0_10/MessageSource.h" +#include "qpid/client/amqp0_10/MessageSink.h" +#include "qpid/client/PrivateImplRef.h" +#include "qpid/Exception.h" +#include "qpid/log/Statement.h" +#include "qpid/messaging/Address.h" +#include "qpid/messaging/Filter.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/MessageImpl.h" +#include "qpid/messaging/MessageListener.h" +#include "qpid/messaging/Sender.h" +#include "qpid/messaging/Receiver.h" +#include "qpid/messaging/Session.h" +#include "qpid/framing/reply_exceptions.h" +#include +#include +#include + +using qpid::messaging::Filter; +using qpid::messaging::MessageImplAccess; +using qpid::messaging::Sender; +using qpid::messaging::Receiver; +using qpid::messaging::VariantMap; + +namespace qpid { +namespace client { +namespace amqp0_10 { + +SessionImpl::SessionImpl(ConnectionImpl& c) : connection(c) {} + + +void SessionImpl::sync() +{ + retry(); +} + +void SessionImpl::flush() +{ + retry(); +} + +void SessionImpl::commit() +{ + if (!execute()) { + throw Exception();//TODO: what type? + } +} + +void SessionImpl::rollback() +{ + //If the session fails during this operation, the transaction will + //be rolled back anyway. + execute(); +} + +void SessionImpl::acknowledge() +{ + //Should probably throw an exception on failure here, or indicate + //it through a return type at least. Failure means that the + //message may be redelivered; i.e. the application cannot delete + //any state necessary for preventing reprocessing of the message + execute(); +} + +void SessionImpl::reject(qpid::messaging::Message& m) +{ + //Possibly want to somehow indicate failure here as well. Less + //clear need as compared to acknowledge however. + execute1(m); +} + +void SessionImpl::close() +{ + connection.closed(*this); + session.close(); +} + +template boost::intrusive_ptr getImplPtr(T& t) +{ + return boost::dynamic_pointer_cast(qpid::client::PrivateImplRef::get(t)); +} + +template void getFreeKey(std::string& key, T& map) +{ + std::string name = key; + int count = 1; + for (typename T::const_iterator i = map.find(name); i != map.end(); i = map.find(name)) { + name = (boost::format("%1%_%2%") % key % ++count).str(); + } + key = name; +} + + +void SessionImpl::setSession(qpid::client::Session s) +{ + qpid::sys::Mutex::ScopedLock l(lock); + session = s; + incoming.setSession(session); + for (Receivers::iterator i = receivers.begin(); i != receivers.end(); ++i) { + getImplPtr(i->second)->init(session, resolver); + } + for (Senders::iterator i = senders.begin(); i != senders.end(); ++i) { + getImplPtr(i->second)->init(session, resolver); + } +} + +struct SessionImpl::CreateReceiver : Command +{ + qpid::messaging::Receiver result; + const qpid::messaging::Address& address; + const Filter* filter; + const qpid::messaging::Variant::Map& options; + + CreateReceiver(SessionImpl& i, const qpid::messaging::Address& a, const Filter* f, + const qpid::messaging::Variant::Map& o) : + Command(i), address(a), filter(f), options(o) {} + void operator()() { result = impl.createReceiverImpl(address, filter, options); } +}; + +Receiver SessionImpl::createReceiver(const qpid::messaging::Address& address, const VariantMap& options) +{ + CreateReceiver f(*this, address, 0, options); + while (!execute(f)) {} + return f.result; +} + +Receiver SessionImpl::createReceiver(const qpid::messaging::Address& address, + const Filter& filter, const VariantMap& options) +{ + CreateReceiver f(*this, address, &filter, options); + while (!execute(f)) {} + return f.result; +} + +Receiver SessionImpl::createReceiverImpl(const qpid::messaging::Address& address, + const Filter* filter, const VariantMap& options) +{ + std::string name = address; + getFreeKey(name, receivers); + Receiver receiver(new ReceiverImpl(*this, name, address, filter, options)); + getImplPtr(receiver)->init(session, resolver); + receivers[name] = receiver; + return receiver; +} + +struct SessionImpl::CreateSender : Command +{ + qpid::messaging::Sender result; + const qpid::messaging::Address& address; + const qpid::messaging::Variant::Map& options; + + CreateSender(SessionImpl& i, const qpid::messaging::Address& a, + const qpid::messaging::Variant::Map& o) : + Command(i), address(a), options(o) {} + void operator()() { result = impl.createSenderImpl(address, options); } +}; + +Sender SessionImpl::createSender(const qpid::messaging::Address& address, const VariantMap& options) +{ + CreateSender f(*this, address, options); + while (!execute(f)) {} + return f.result; +} + +Sender SessionImpl::createSenderImpl(const qpid::messaging::Address& address, const VariantMap& options) +{ + std::string name = address; + getFreeKey(name, senders); + Sender sender(new SenderImpl(*this, name, address, options)); + getImplPtr(sender)->init(session, resolver); + senders[name] = sender; + return sender; +} + +qpid::messaging::Address SessionImpl::createTempQueue(const std::string& baseName) +{ + std::string name = baseName + std::string("_") + session.getId().getName(); + session.queueDeclare(arg::queue=name, arg::exclusive=true, arg::autoDelete=true); + return qpid::messaging::Address(name); +} + +SessionImpl& SessionImpl::convert(qpid::messaging::Session& s) +{ + boost::intrusive_ptr impl = getImplPtr(s); + if (!impl) { + throw qpid::Exception(QPID_MSG("Configuration error; require qpid::client::amqp0_10::SessionImpl")); + } + return *impl; +} + +namespace { + +struct IncomingMessageHandler : IncomingMessages::Handler +{ + typedef boost::function1 Callback; + Callback callback; + + IncomingMessageHandler(Callback c) : callback(c) {} + + bool accept(IncomingMessages::MessageTransfer& transfer) + { + return callback(transfer); + } +}; + +} + +bool SessionImpl::accept(ReceiverImpl* receiver, + qpid::messaging::Message* message, + bool isDispatch, + IncomingMessages::MessageTransfer& transfer) +{ + if (receiver->getName() == transfer.getDestination()) { + transfer.retrieve(message); + if (isDispatch) { + qpid::sys::Mutex::ScopedUnlock u(lock); + qpid::messaging::MessageListener* listener = receiver->getListener(); + if (listener) listener->received(*message); + } + receiver->received(*message); + return true; + } else { + return false; + } +} + +bool SessionImpl::acceptAny(qpid::messaging::Message* message, bool isDispatch, IncomingMessages::MessageTransfer& transfer) +{ + Receivers::iterator i = receivers.find(transfer.getDestination()); + if (i == receivers.end()) { + QPID_LOG(error, "Received message for unknown destination " << transfer.getDestination()); + return false; + } else { + boost::intrusive_ptr receiver = getImplPtr(i->second); + return receiver && (!isDispatch || receiver->getListener()) && accept(receiver.get(), message, isDispatch, transfer); + } +} + +bool SessionImpl::getIncoming(IncomingMessages::Handler& handler, qpid::sys::Duration timeout) +{ + return incoming.get(handler, timeout); +} + +bool SessionImpl::get(ReceiverImpl& receiver, qpid::messaging::Message& message, qpid::sys::Duration timeout) +{ + IncomingMessageHandler handler(boost::bind(&SessionImpl::accept, this, &receiver, &message, false, _1)); + return getIncoming(handler, timeout); +} + +bool SessionImpl::dispatch(qpid::sys::Duration timeout) +{ + qpid::sys::Mutex::ScopedLock l(lock); + while (true) { + try { + qpid::messaging::Message message; + IncomingMessageHandler handler(boost::bind(&SessionImpl::acceptAny, this, &message, true, _1)); + return getIncoming(handler, timeout); + } catch (TransportFailure&) { + reconnect(); + } + } +} + +bool SessionImpl::fetch(qpid::messaging::Message& message, qpid::sys::Duration timeout) +{ + qpid::sys::Mutex::ScopedLock l(lock); + while (true) { + try { + IncomingMessageHandler handler(boost::bind(&SessionImpl::acceptAny, this, &message, false, _1)); + return getIncoming(handler, timeout); + } catch (TransportFailure&) { + reconnect(); + } + } +} + +void SessionImpl::syncImpl() +{ + session.sync(); +} + +void SessionImpl::flushImpl() +{ + session.flush(); +} + + +void SessionImpl::commitImpl() +{ + incoming.accept(); + session.txCommit(); +} + +void SessionImpl::rollbackImpl() +{ + for (Receivers::iterator i = receivers.begin(); i != receivers.end(); ++i) i->second.stop(); + //ensure that stop has been processed and all previously sent + //messages are available for release: + session.sync(); + incoming.releaseAll(); + session.txRollback(); + for (Receivers::iterator i = receivers.begin(); i != receivers.end(); ++i) i->second.start(); +} + +void SessionImpl::acknowledgeImpl() +{ + incoming.accept(); +} + +void SessionImpl::rejectImpl(qpid::messaging::Message& m) +{ + SequenceSet set; + set.add(MessageImplAccess::get(m).getInternalId()); + session.messageReject(set); +} + +qpid::messaging::Message SessionImpl::fetch(qpid::sys::Duration timeout) +{ + qpid::messaging::Message result; + if (!fetch(result, timeout)) throw Receiver::NoMessageAvailable(); + return result; +} + +void SessionImpl::receiverCancelled(const std::string& name) +{ + receivers.erase(name); + session.sync(); + incoming.releasePending(name); +} + +void SessionImpl::senderCancelled(const std::string& name) +{ + senders.erase(name); +} + +void SessionImpl::reconnect() +{ + connection.reconnect(); +} + +void* SessionImpl::getLastConfirmedSent() +{ + return 0; +} + +void* SessionImpl::getLastConfirmedAcknowledged() +{ + return 0; +} + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h new file mode 100644 index 0000000000..1c7db17bbb --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h @@ -0,0 +1,202 @@ +#ifndef QPID_CLIENT_AMQP0_10_SESSIONIMPL_H +#define QPID_CLIENT_AMQP0_10_SESSIONIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/SessionImpl.h" +#include "qpid/messaging/Variant.h" +#include "qpid/client/Session.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/amqp0_10/AddressResolution.h" +#include "qpid/client/amqp0_10/IncomingMessages.h" +#include "qpid/sys/Mutex.h" + +namespace qpid { + +namespace messaging { +struct Address; +struct Filter; +class Message; +class Receiver; +class Sender; +class Session; +} + +namespace client { +namespace amqp0_10 { + +class ConnectionImpl; +class ReceiverImpl; +class SenderImpl; + +/** + * Implementation of the protocol independent Session interface using + * AMQP 0-10. + */ +class SessionImpl : public qpid::messaging::SessionImpl +{ + public: + SessionImpl(ConnectionImpl&); + void commit(); + void rollback(); + void acknowledge(); + void reject(qpid::messaging::Message&); + void close(); + void sync(); + void flush(); + qpid::messaging::Address createTempQueue(const std::string& baseName); + qpid::messaging::Sender createSender(const qpid::messaging::Address& address, + const qpid::messaging::VariantMap& options); + qpid::messaging::Receiver createReceiver(const qpid::messaging::Address& address, + const qpid::messaging::VariantMap& options); + qpid::messaging::Receiver createReceiver(const qpid::messaging::Address& address, + const qpid::messaging::Filter& filter, + const qpid::messaging::VariantMap& options); + + void* getLastConfirmedSent(); + void* getLastConfirmedAcknowledged(); + + bool fetch(qpid::messaging::Message& message, qpid::sys::Duration timeout); + qpid::messaging::Message fetch(qpid::sys::Duration timeout); + bool dispatch(qpid::sys::Duration timeout); + + bool get(ReceiverImpl& receiver, qpid::messaging::Message& message, qpid::sys::Duration timeout); + + void receiverCancelled(const std::string& name); + void senderCancelled(const std::string& name); + + void setSession(qpid::client::Session); + + template bool execute(T& f) + { + try { + qpid::sys::Mutex::ScopedLock l(lock); + f(); + return true; + } catch (TransportFailure&) { + reconnect(); + return false; + } + } + + static SessionImpl& convert(qpid::messaging::Session&); + + private: + typedef std::map Receivers; + typedef std::map Senders; + + qpid::sys::Mutex lock; + ConnectionImpl& connection; + qpid::client::Session session; + AddressResolution resolver; + IncomingMessages incoming; + Receivers receivers; + Senders senders; + + bool acceptAny(qpid::messaging::Message*, bool, IncomingMessages::MessageTransfer&); + bool accept(ReceiverImpl*, qpid::messaging::Message*, bool, IncomingMessages::MessageTransfer&); + bool getIncoming(IncomingMessages::Handler& handler, qpid::sys::Duration timeout); + void reconnect(); + + void commitImpl(); + void rollbackImpl(); + void acknowledgeImpl(); + void rejectImpl(qpid::messaging::Message&); + void closeImpl(); + void syncImpl(); + void flushImpl(); + qpid::messaging::Sender createSenderImpl(const qpid::messaging::Address& address, + const qpid::messaging::VariantMap& options); + qpid::messaging::Receiver createReceiverImpl(const qpid::messaging::Address& address, + const qpid::messaging::Filter* filter, + const qpid::messaging::VariantMap& options); + + //functors for public facing methods (allows locking and retry + //logic to be centralised) + struct Command + { + SessionImpl& impl; + + Command(SessionImpl& i) : impl(i) {} + }; + + struct Commit : Command + { + Commit(SessionImpl& i) : Command(i) {} + void operator()() { impl.commitImpl(); } + }; + + struct Rollback : Command + { + Rollback(SessionImpl& i) : Command(i) {} + void operator()() { impl.rollbackImpl(); } + }; + + struct Acknowledge : Command + { + Acknowledge(SessionImpl& i) : Command(i) {} + void operator()() { impl.acknowledgeImpl(); } + }; + + struct Sync : Command + { + Sync(SessionImpl& i) : Command(i) {} + void operator()() { impl.syncImpl(); } + }; + + struct Flush : Command + { + Flush(SessionImpl& i) : Command(i) {} + void operator()() { impl.flushImpl(); } + }; + + struct Reject : Command + { + qpid::messaging::Message& message; + + Reject(SessionImpl& i, qpid::messaging::Message& m) : Command(i), message(m) {} + void operator()() { impl.rejectImpl(message); } + }; + + struct CreateSender; + struct CreateReceiver; + + //helper templates for some common patterns + template bool execute() + { + F f(*this); + return execute(f); + } + + template void retry() + { + while (!execute()) {} + } + + template bool execute1(P p) + { + F f(*this, p); + return execute(f); + } +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_SESSIONIMPL_H*/ diff --git a/qpid/cpp/src/qpid/cluster/UpdateClient.cpp b/qpid/cpp/src/qpid/cluster/UpdateClient.cpp index ac418ffbb6..2e557f2ab6 100644 --- a/qpid/cpp/src/qpid/cluster/UpdateClient.cpp +++ b/qpid/cpp/src/qpid/cluster/UpdateClient.cpp @@ -213,7 +213,7 @@ class MessageUpdater { framing::MessageTransferBody transfer( framing::ProtocolVersion(), UpdateClient::UPDATE, message::ACCEPT_MODE_NONE, message::ACQUIRE_MODE_PRE_ACQUIRED); - sb.get()->send(transfer, message.payload->getFrames()); + sb.get()->send(transfer, message.payload->getFrames(), !message.payload->isContentReleased()); if (message.payload->isContentReleased()){ uint16_t maxFrameSize = sb.get()->getConnection()->getNegotiatedSettings().maxFrameSize; uint16_t maxContentSize = maxFrameSize - AMQFrame::frameOverhead(); diff --git a/qpid/cpp/src/qpid/framing/FieldTable.cpp b/qpid/cpp/src/qpid/framing/FieldTable.cpp index bc832e9db4..255a3b74a4 100644 --- a/qpid/cpp/src/qpid/framing/FieldTable.cpp +++ b/qpid/cpp/src/qpid/framing/FieldTable.cpp @@ -185,13 +185,8 @@ template bool getRawFixedWidthValue(FieldTable::ValuePtr vptr, T& value) { if (vptr && vptr->getType() == typecode) { - FixedWidthValue* fwv = dynamic_cast< FixedWidthValue* >(&vptr->getData()); - if (fwv) { - uint8_t* const octets = Endian::convertIfRequired(fwv->rawOctets(), width); - uint8_t* const target = reinterpret_cast(&value); - for (uint i = 0; i < width; ++i) target[i] = octets[i]; - return true; - } + value = vptr->get(); + return true; } return false; } @@ -370,5 +365,16 @@ void FieldTable::erase(const std::string& name) values.erase(name); } +std::pair FieldTable::insert(const ValueMap::value_type& value) +{ + return values.insert(value); +} + +FieldTable::ValueMap::iterator FieldTable::insert(ValueMap::iterator position, const ValueMap::value_type& value) +{ + return values.insert(position, value); +} + + } } diff --git a/qpid/cpp/src/qpid/framing/FieldValue.cpp b/qpid/cpp/src/qpid/framing/FieldValue.cpp index 5f7248a751..5bac931b83 100644 --- a/qpid/cpp/src/qpid/framing/FieldValue.cpp +++ b/qpid/cpp/src/qpid/framing/FieldValue.cpp @@ -22,6 +22,7 @@ #include "qpid/framing/Array.h" #include "qpid/framing/Buffer.h" #include "qpid/framing/Endian.h" +#include "qpid/framing/List.h" #include "qpid/framing/reply_exceptions.h" namespace qpid { @@ -37,6 +38,8 @@ void FieldValue::setType(uint8_t type) typeOctet = type; if (typeOctet == 0xA8) { data.reset(new EncodedValue()); + } else if (typeOctet == 0xA9) { + data.reset(new EncodedValue()); } else if (typeOctet == 0xAA) { data.reset(new EncodedValue()); } else { @@ -164,10 +167,37 @@ FieldTableValue::FieldTableValue(const FieldTable& f) : FieldValue(0xa8, new Enc { } +ListValue::ListValue(const List& l) : FieldValue(0xa9, new EncodedValue(l)) +{ +} + ArrayValue::ArrayValue(const Array& a) : FieldValue(0xaa, new EncodedValue(a)) { } +VoidValue::VoidValue() : FieldValue(0xf0, new FixedWidthValue<0>()) {} + +BoolValue::BoolValue(bool b) : + FieldValue(0x08, new FixedWidthValue<1>(b)) +{} + +Unsigned8Value::Unsigned8Value(uint8_t v) : + FieldValue(0x02, new FixedWidthValue<1>(v)) +{} +Unsigned16Value::Unsigned16Value(uint16_t v) : + FieldValue(0x12, new FixedWidthValue<2>(v)) +{} +Unsigned32Value::Unsigned32Value(uint32_t v) : + FieldValue(0x22, new FixedWidthValue<4>(v)) +{} + +Integer8Value::Integer8Value(int8_t v) : + FieldValue(0x01, new FixedWidthValue<1>(v)) +{} +Integer16Value::Integer16Value(int16_t v) : + FieldValue(0x11, new FixedWidthValue<2>(v)) +{} + void FieldValue::print(std::ostream& out) const { data->print(out); out << TypeCode(typeOctet) << '('; @@ -177,4 +207,9 @@ void FieldValue::print(std::ostream& out) const { out << ')'; } +uint8_t* FieldValue::convertIfRequired(uint8_t* const octets, int width) +{ + return Endian::convertIfRequired(octets, width); +} + }} diff --git a/qpid/cpp/src/qpid/framing/List.cpp b/qpid/cpp/src/qpid/framing/List.cpp new file mode 100644 index 0000000000..bde7dabbac --- /dev/null +++ b/qpid/cpp/src/qpid/framing/List.cpp @@ -0,0 +1,83 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/framing/List.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/FieldValue.h" +#include "qpid/Exception.h" +#include "qpid/framing/reply_exceptions.h" + +namespace qpid { +namespace framing { + +uint32_t List::encodedSize() const +{ + uint32_t len(4/*size*/ + 4/*count*/); + for(Values::const_iterator i = values.begin(); i != values.end(); ++i) { + len += (*i)->encodedSize(); + } + return len; +} + +void List::encode(Buffer& buffer) const +{ + buffer.putLong(encodedSize() - 4); + buffer.putLong(size()); + for (Values::const_iterator i = values.begin(); i!=values.end(); ++i) { + (*i)->encode(buffer); + } +} + +void List::decode(Buffer& buffer) +{ + values.clear(); + uint32_t size = buffer.getLong(); + uint32_t available = buffer.available(); + if (available < size) { + throw IllegalArgumentException(QPID_MSG("Not enough data for list, expected " + << size << " bytes but only " << available << " available")); + } + if (size) { + uint32_t count = buffer.getLong(); + for (uint32_t i = 0; i < count; i++) { + ValuePtr value(new FieldValue); + value->decode(buffer); + values.push_back(value); + } + } +} + + +bool List::operator==(const List& other) const { + return values.size() == other.values.size() && + std::equal(values.begin(), values.end(), other.values.begin()); +} + +std::ostream& operator<<(std::ostream& out, const List& l) +{ + out << "{"; + for(List::Values::const_iterator i = l.values.begin(); i != l.values.end(); ++i) { + if (i != l.values.begin()) out << ", "; + (*i)->print(out); + } + return out << "}"; +} + +}} // namespace qpid::framing diff --git a/qpid/cpp/src/qpid/framing/Uuid.cpp b/qpid/cpp/src/qpid/framing/Uuid.cpp index c0b41c6906..71fa6a7329 100644 --- a/qpid/cpp/src/qpid/framing/Uuid.cpp +++ b/qpid/cpp/src/qpid/framing/Uuid.cpp @@ -17,6 +17,8 @@ */ #include "qpid/framing/Uuid.h" + +#include "qpid/sys/uuid.h" #include "qpid/Exception.h" #include "qpid/framing/Buffer.h" #include "qpid/framing/reply_exceptions.h" @@ -28,6 +30,35 @@ using namespace std; static const size_t UNPARSED_SIZE=36; +Uuid::Uuid(bool unique) { + if (unique) { + generate(); + } else { + clear(); + } +} + +Uuid::Uuid(const uint8_t* data) { + assign(data); +} + +void Uuid::assign(const uint8_t* data) { + uuid_copy(c_array(), data); +} + +void Uuid::generate() { + uuid_generate(c_array()); +} + +void Uuid::clear() { + uuid_clear(c_array()); +} + +// Force int 0/!0 to false/true; avoids compile warnings. +bool Uuid::isNull() { + return !!uuid_is_null(data()); +} + void Uuid::encode(Buffer& buf) const { buf.putRawData(data(), size()); } diff --git a/qpid/cpp/src/qpid/management/Manageable.cpp b/qpid/cpp/src/qpid/management/Manageable.cpp index e487dfc455..a3593e73e3 100644 --- a/qpid/cpp/src/qpid/management/Manageable.cpp +++ b/qpid/cpp/src/qpid/management/Manageable.cpp @@ -33,7 +33,7 @@ string Manageable::StatusText (status_t status, string text) case STATUS_UNKNOWN_OBJECT : return "UnknownObject"; case STATUS_UNKNOWN_METHOD : return "UnknownMethod"; case STATUS_NOT_IMPLEMENTED : return "NotImplemented"; - case STATUS_INVALID_PARAMETER : return "InvalidParameter"; + case STATUS_PARAMETER_INVALID : return "InvalidParameter"; case STATUS_FEATURE_NOT_IMPLEMENTED : return "FeatureNotImplemented"; case STATUS_FORBIDDEN : return "Forbidden"; } diff --git a/qpid/cpp/src/qpid/management/ManagementAgent.cpp b/qpid/cpp/src/qpid/management/ManagementAgent.cpp index 2df10b1e95..0e462342d4 100644 --- a/qpid/cpp/src/qpid/management/ManagementAgent.cpp +++ b/qpid/cpp/src/qpid/management/ManagementAgent.cpp @@ -535,8 +535,8 @@ void ManagementAgent::handleMethodRequestLH (Buffer& inBuffer, string replyToKey } else { if ((iter->second->getPackageName() != packageName) || (iter->second->getClassName() != className)) { - outBuffer.putLong (Manageable::STATUS_INVALID_PARAMETER); - outBuffer.putMediumString(Manageable::StatusText (Manageable::STATUS_INVALID_PARAMETER)); + outBuffer.putLong (Manageable::STATUS_PARAMETER_INVALID); + outBuffer.putMediumString(Manageable::StatusText (Manageable::STATUS_PARAMETER_INVALID)); } else try { diff --git a/qpid/cpp/src/qpid/messaging/Address.cpp b/qpid/cpp/src/qpid/messaging/Address.cpp new file mode 100644 index 0000000000..ed35054a00 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/Address.cpp @@ -0,0 +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. + * + */ +#include "qpid/messaging/Address.h" + +namespace qpid { +namespace client { +} + +namespace messaging { + +Address::Address() {} +Address::Address(const std::string& address) : value(address) {} +Address::Address(const std::string& address, const std::string& t) : value(address), type(t) {} +Address::operator const std::string&() const { return value; } +const std::string& Address::toStr() const { return value; } +Address::operator bool() const { return !value.empty(); } +bool Address::operator !() const { return value.empty(); } + +const std::string TYPE_SEPARATOR(":"); + +std::ostream& operator<<(std::ostream& out, const Address& address) +{ + if (!address.type.empty()) { + out << address.type; + out << TYPE_SEPARATOR; + } + out << address.value; + return out; +} + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/Connection.cpp b/qpid/cpp/src/qpid/messaging/Connection.cpp new file mode 100644 index 0000000000..feb6566008 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/Connection.cpp @@ -0,0 +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. + * + */ +#include "qpid/messaging/Connection.h" +#include "qpid/messaging/ConnectionImpl.h" +#include "qpid/messaging/Session.h" +#include "qpid/messaging/SessionImpl.h" +#include "qpid/client/PrivateImplRef.h" +#include "qpid/client/amqp0_10/ConnectionImpl.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace client { + +typedef PrivateImplRef PI; + +} + +namespace messaging { + +using qpid::client::PI; + +Connection Connection::open(const std::string& url, const Variant::Map& options) +{ + //only support amqp 0-10 at present + Connection connection(new qpid::client::amqp0_10::ConnectionImpl(url, options)); + return connection; +} + +Connection::Connection(ConnectionImpl* impl) { PI::ctor(*this, impl); } +Connection::Connection(const Connection& c) : qpid::client::Handle() { PI::copy(*this, c); } +Connection& Connection::operator=(const Connection& c) { return PI::assign(*this, c); } +Connection::~Connection() { PI::dtor(*this); } + +void Connection::close() { impl->close(); } +Session Connection::newSession() { return impl->newSession(); } + +InvalidOptionString::InvalidOptionString(const std::string& msg) : Exception(msg) {} + +void parseKeyValuePair(const std::string& in, Variant::Map& out) +{ + std::string::size_type i = in.find('='); + if (i == std::string::npos || i == in.size() || in.find('=', i+1) != std::string::npos) { + throw InvalidOptionString(QPID_MSG("Cannot parse name-value pair from " << in)); + } else { + out[in.substr(0, i)] = in.substr(i+1); + } +} + +void parseOptionString(const std::string& in, Variant::Map& out) +{ + std::string::size_type start = 0; + std::string::size_type i = in.find('&'); + while (i != std::string::npos) { + parseKeyValuePair(in.substr(start, i-start), out); + if (i < in.size()) { + start = i+1; + i = in.find('&', start); + } else { + i = std::string::npos; + } + } + parseKeyValuePair(in.substr(start), out); +} + +Variant::Map parseOptionString(const std::string& in) +{ + Variant::Map map; + parseOptionString(in, map); + return map; +} + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/ConnectionImpl.h b/qpid/cpp/src/qpid/messaging/ConnectionImpl.h new file mode 100644 index 0000000000..aa9e5b5fbe --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/ConnectionImpl.h @@ -0,0 +1,45 @@ +#ifndef QPID_MESSAGING_CONNECTIONIMPL_H +#define QPID_MESSAGING_CONNECTIONIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include +#include "qpid/RefCounted.h" + +namespace qpid { +namespace client { +} + +namespace messaging { + +class Session; + +class ConnectionImpl : public virtual qpid::RefCounted +{ + public: + virtual ~ConnectionImpl() {} + virtual void close() = 0; + virtual Session newSession() = 0; + private: +}; +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_CONNECTIONIMPL_H*/ diff --git a/qpid/cpp/src/qpid/messaging/Filter.cpp b/qpid/cpp/src/qpid/messaging/Filter.cpp new file mode 100644 index 0000000000..b06cbdb373 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/Filter.cpp @@ -0,0 +1,39 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/Filter.h" + +namespace qpid { +namespace client { +} + +namespace messaging { + +Filter::Filter(std::string t, std::string pattern) : type(t) { patterns.push_back(pattern); } +Filter::Filter(std::string t, std::string pattern1, std::string pattern2) : type(t) +{ + patterns.push_back(pattern1); + patterns.push_back(pattern2); +} + +const std::string Filter::WILDCARD("WILDCARD"); +const std::string Filter::EXACT_MATCH("EXACT_MATCH"); + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/Message.cpp b/qpid/cpp/src/qpid/messaging/Message.cpp new file mode 100644 index 0000000000..1d844b3027 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/Message.cpp @@ -0,0 +1,70 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/Message.h" +#include "qpid/messaging/MessageImpl.h" + +namespace qpid { +namespace messaging { + +Message::Message(const std::string& bytes) : impl(new MessageImpl(bytes)) {} +Message::Message(const char* bytes, size_t count) : impl(new MessageImpl(bytes, count)) {} + +Message::Message(const Message& m) : impl(new MessageImpl(m.getBytes())) {} +Message::~Message() { delete impl; } + +Message& Message::operator=(const Message& m) { *impl = *m.impl; return *this; } + +void Message::setReplyTo(const Address& d) { impl->setReplyTo(d); } +const Address& Message::getReplyTo() const { return impl->getReplyTo(); } + +void Message::setSubject(const std::string& s) { impl->setSubject(s); } +const std::string& Message::getSubject() const { return impl->getSubject(); } + +void Message::setContentType(const std::string& s) { impl->setContentType(s); } +const std::string& Message::getContentType() const { return impl->getContentType(); } + +const VariantMap& Message::getHeaders() const { return impl->getHeaders(); } +VariantMap& Message::getHeaders() { return impl->getHeaders(); } + +void Message::setBytes(const std::string& c) { impl->setBytes(c); } +void Message::setBytes(const char* chars, size_t count) { impl->setBytes(chars, count); } +const std::string& Message::getBytes() const { return impl->getBytes(); } +std::string& Message::getBytes() { return impl->getBytes(); } + +const char* Message::getRawContent() const { return impl->getBytes().data(); } +size_t Message::getContentSize() const { return impl->getBytes().size(); } + +MessageContent& Message::getContent() { return *impl; } +const MessageContent& Message::getContent() const { return *impl; } +void Message::setContent(const std::string& s) { *impl = s; } +void Message::setContent(const Variant::Map& m) { *impl = m; } +void Message::setContent(const Variant::List& l) { *impl = l; } + +void Message::encode(Codec& codec) { impl->encode(codec); } + +void Message::decode(Codec& codec) { impl->decode(codec); } + +std::ostream& operator<<(std::ostream& out, const MessageContent& content) +{ + return content.print(out); +} + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/MessageImpl.cpp b/qpid/cpp/src/qpid/messaging/MessageImpl.cpp new file mode 100644 index 0000000000..5df9218e03 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/MessageImpl.cpp @@ -0,0 +1,205 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "MessageImpl.h" +#include "qpid/messaging/Message.h" + +namespace qpid { +namespace messaging { + +namespace { +const std::string EMPTY_STRING = ""; +} + +MessageImpl::MessageImpl(const std::string& c) : bytes(c), type(VAR_VOID), internalId(0) {} +MessageImpl::MessageImpl(const char* chars, size_t count) : bytes(chars, count), type(VAR_VOID), internalId(0) {} + +void MessageImpl::setReplyTo(const Address& d) { replyTo = d; } +const Address& MessageImpl::getReplyTo() const { return replyTo; } + +void MessageImpl::setSubject(const std::string& s) { subject = s; } +const std::string& MessageImpl::getSubject() const { return subject; } + +void MessageImpl::setContentType(const std::string& s) { contentType = s; } +const std::string& MessageImpl::getContentType() const { return contentType; } + +const VariantMap& MessageImpl::getHeaders() const { return headers; } +VariantMap& MessageImpl::getHeaders() { return headers; } + +//should these methods be on MessageContent? +void MessageImpl::setBytes(const std::string& c) { clear(); bytes = c; } +void MessageImpl::setBytes(const char* chars, size_t count) { clear(); bytes.assign(chars, count); } +const std::string& MessageImpl::getBytes() const { return bytes; } +std::string& MessageImpl::getBytes() { return bytes; } + + +Variant& MessageImpl::operator[](const std::string& key) { return asMap()[key]; } + +std::ostream& MessageImpl::print(std::ostream& out) const +{ + if (type == VAR_MAP) { + return out << content.asMap(); + } else if (type == VAR_LIST) { + return out << content.asList(); + } else { + return out << bytes; + } +} + +template MessageContent& MessageImpl::append(T& t) +{ + if (type == VAR_VOID) { + //TODO: this is inefficient, probably want to hold on to the stream object + std::stringstream s; + s << bytes; + s << t; + bytes = s.str(); + } else if (type == VAR_LIST) { + content.asList().push_back(Variant(t)); + } else { + throw InvalidConversion("<< operator only valid on strings and lists"); + } + return *this; +} + +MessageContent& MessageImpl::operator<<(const std::string& v) { return append(v); } +MessageContent& MessageImpl::operator<<(const char* v) { return append(v); } +MessageContent& MessageImpl::operator<<(bool v) { return append(v); } +MessageContent& MessageImpl::operator<<(int8_t v) { return append(v); } +MessageContent& MessageImpl::operator<<(int16_t v) { return append(v); } +MessageContent& MessageImpl::operator<<(int32_t v) { return append(v); } +MessageContent& MessageImpl::operator<<(int64_t v) { return append(v); } +MessageContent& MessageImpl::operator<<(uint8_t v) { return append(v); } +MessageContent& MessageImpl::operator<<(uint16_t v) { return append(v); } +MessageContent& MessageImpl::operator<<(uint32_t v) { return append(v); } +MessageContent& MessageImpl::operator<<(uint64_t v) { return append(v); } +MessageContent& MessageImpl::operator<<(double v) { return append(v); } +MessageContent& MessageImpl::operator<<(float v) { return append(v); } +MessageContent& MessageImpl::operator=(const std::string& s) +{ + type = VAR_VOID; + bytes = s; + return *this; +} +MessageContent& MessageImpl::operator=(const char* c) +{ + type = VAR_VOID; + bytes = c; + return *this; +} +MessageContent& MessageImpl::operator=(const Variant::Map& m) +{ + type = VAR_MAP; + content = m; + return *this; +} + +MessageContent& MessageImpl::operator=(const Variant::List& l) +{ + type = VAR_LIST; + content = l; + return *this; +} + +void MessageImpl::encode(Codec& codec) +{ + if (content.getType() != VAR_VOID) { + bytes = EMPTY_STRING; + codec.encode(content, bytes); + } +} + +void MessageImpl::getEncodedContent(Codec& codec, std::string& out) const +{ + if (content.getType() != VAR_VOID) { + codec.encode(content, out); + } else { + out = bytes; + } +} + +void MessageImpl::decode(Codec& codec) +{ + codec.decode(bytes, content); + if (content.getType() == VAR_MAP) type = VAR_MAP; + else if (content.getType() == VAR_LIST) type = VAR_LIST; + else type = VAR_VOID;//TODO: what if codec set some type other than map or list?? +} + +void MessageImpl::setInternalId(qpid::framing::SequenceNumber i) { internalId = i; } +qpid::framing::SequenceNumber MessageImpl::getInternalId() { return internalId; } + +bool MessageImpl::isVoid() const { return type == VAR_VOID; } + +const std::string& MessageImpl::asString() const +{ + if (isVoid()) return getBytes(); + else return content.getString();//will throw an error +} +std::string& MessageImpl::asString() +{ + if (isVoid()) return getBytes(); + else return content.getString();//will throw an error +} + +const char* MessageImpl::asChars() const +{ + if (!isVoid()) throw InvalidConversion("Content is of structured type."); + return bytes.data(); +} +size_t MessageImpl::size() const +{ + return bytes.size(); +} + +const Variant::Map& MessageImpl::asMap() const { return content.asMap(); } +Variant::Map& MessageImpl::asMap() +{ + if (isVoid()) { + content = Variant::Map(); + type = VAR_MAP; + } + return content.asMap(); +} +bool MessageImpl::isMap() const { return type == VAR_MAP; } + +const Variant::List& MessageImpl::asList() const { return content.asList(); } +Variant::List& MessageImpl::asList() +{ + if (isVoid()) { + content = Variant::List(); + type = VAR_LIST; + } + return content.asList(); +} +bool MessageImpl::isList() const { return type == VAR_LIST; } + +void MessageImpl::clear() { bytes = EMPTY_STRING; content.reset(); type = VAR_VOID; } + +MessageImpl& MessageImplAccess::get(Message& msg) +{ + return *msg.impl; +} +const MessageImpl& MessageImplAccess::get(const Message& msg) +{ + return *msg.impl; +} + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/MessageImpl.h b/qpid/cpp/src/qpid/messaging/MessageImpl.h new file mode 100644 index 0000000000..1173e7570a --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/MessageImpl.h @@ -0,0 +1,134 @@ +#ifndef QPID_MESSAGING_MESSAGEIMPL_H +#define QPID_MESSAGING_MESSAGEIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/Address.h" +#include "qpid/messaging/Codec.h" +#include "qpid/messaging/MessageContent.h" +#include "qpid/messaging/Variant.h" +#include "qpid/framing/SequenceNumber.h" + +namespace qpid { +namespace messaging { + +struct MessageImpl : MessageContent +{ + Address replyTo; + std::string subject; + std::string contentType; + Variant::Map headers; + + std::string bytes; + Variant content;//used only for LIST and MAP + VariantType type;//if LIST, MAP content holds the value; if VOID bytes holds the value + + qpid::framing::SequenceNumber internalId; + + MessageImpl(const std::string& c); + MessageImpl(const char* chars, size_t count); + + void setReplyTo(const Address& d); + const Address& getReplyTo() const; + + void setSubject(const std::string& s); + const std::string& getSubject() const; + + void setContentType(const std::string& s); + const std::string& getContentType() const; + + const Variant::Map& getHeaders() const; + Variant::Map& getHeaders(); + + void setBytes(const std::string& bytes); + void setBytes(const char* chars, size_t count); + const std::string& getBytes() const; + std::string& getBytes(); + + void setInternalId(qpid::framing::SequenceNumber id); + qpid::framing::SequenceNumber getInternalId(); + + bool isVoid() const; + + const std::string& asString() const; + std::string& asString(); + + const char* asChars() const; + size_t size() const; + + const Variant::Map& asMap() const; + Variant::Map& asMap(); + bool isMap() const; + + const Variant::List& asList() const; + Variant::List& asList(); + bool isList() const; + + void clear(); + + void getEncodedContent(Codec& codec, std::string&) const; + void encode(Codec& codec); + void decode(Codec& codec); + + Variant& operator[](const std::string&); + + std::ostream& print(std::ostream& out) const; + + //operator<< for variety of types... + MessageContent& operator<<(const std::string&); + MessageContent& operator<<(const char*); + MessageContent& operator<<(bool); + MessageContent& operator<<(int8_t); + MessageContent& operator<<(int16_t); + MessageContent& operator<<(int32_t); + MessageContent& operator<<(int64_t); + MessageContent& operator<<(uint8_t); + MessageContent& operator<<(uint16_t); + MessageContent& operator<<(uint32_t); + MessageContent& operator<<(uint64_t); + MessageContent& operator<<(double); + MessageContent& operator<<(float); + + //assignment from string, map and list + MessageContent& operator=(const std::string&); + MessageContent& operator=(const char*); + MessageContent& operator=(const Variant::Map&); + MessageContent& operator=(const Variant::List&); + + template MessageContent& append(T& t); +}; + +class Message; + +/** + * Provides access to the internal MessageImpl for a message which is + * useful when accessing any message state not exposed directly + * through the public API. + */ +struct MessageImplAccess +{ + static MessageImpl& get(Message&); + static const MessageImpl& get(const Message&); +}; + +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_MESSAGEIMPL_H*/ diff --git a/qpid/cpp/src/qpid/messaging/Receiver.cpp b/qpid/cpp/src/qpid/messaging/Receiver.cpp new file mode 100644 index 0000000000..2e8b89d27f --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/Receiver.cpp @@ -0,0 +1,51 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/Receiver.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/ReceiverImpl.h" +#include "qpid/client/PrivateImplRef.h" + +namespace qpid { +namespace client { + +typedef PrivateImplRef PI; + +} + +namespace messaging { + +using qpid::client::PI; + +Receiver::Receiver(ReceiverImpl* impl) { PI::ctor(*this, impl); } +Receiver::Receiver(const Receiver& s) : qpid::client::Handle() { PI::copy(*this, s); } +Receiver::~Receiver() { PI::dtor(*this); } +Receiver& Receiver::operator=(const Receiver& s) { return PI::assign(*this, s); } +bool Receiver::get(Message& message, qpid::sys::Duration timeout) { return impl->get(message, timeout); } +Message Receiver::get(qpid::sys::Duration timeout) { return impl->get(timeout); } +bool Receiver::fetch(Message& message, qpid::sys::Duration timeout) { return impl->fetch(message, timeout); } +Message Receiver::fetch(qpid::sys::Duration timeout) { return impl->fetch(timeout); } +void Receiver::start() { impl->start(); } +void Receiver::stop() { impl->stop(); } +void Receiver::setCapacity(uint32_t c) { impl->setCapacity(c); } +void Receiver::cancel() { impl->cancel(); } +void Receiver::setListener(MessageListener* listener) { impl->setListener(listener); } + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/ReceiverImpl.h b/qpid/cpp/src/qpid/messaging/ReceiverImpl.h new file mode 100644 index 0000000000..77697b730c --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/ReceiverImpl.h @@ -0,0 +1,52 @@ +#ifndef QPID_MESSAGING_RECEIVERIMPL_H +#define QPID_MESSAGING_RECEIVERIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/RefCounted.h" +#include "qpid/sys/Time.h" + +namespace qpid { +namespace client { +} + +namespace messaging { + +class Message; +class MessageListener; + +class ReceiverImpl : public virtual qpid::RefCounted +{ + public: + virtual ~ReceiverImpl() {} + virtual bool get(Message& message, qpid::sys::Duration timeout) = 0; + virtual Message get(qpid::sys::Duration timeout) = 0; + virtual bool fetch(Message& message, qpid::sys::Duration timeout) = 0; + virtual Message fetch(qpid::sys::Duration timeout) = 0; + virtual void start() = 0; + virtual void stop() = 0; + virtual void setCapacity(uint32_t) = 0; + virtual void cancel() = 0; + virtual void setListener(MessageListener*) = 0; +}; +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_RECEIVERIMPL_H*/ diff --git a/qpid/cpp/src/qpid/messaging/Sender.cpp b/qpid/cpp/src/qpid/messaging/Sender.cpp new file mode 100644 index 0000000000..8db700b060 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/Sender.cpp @@ -0,0 +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. + * + */ +#include "qpid/messaging/Sender.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/SenderImpl.h" +#include "qpid/client/PrivateImplRef.h" + +namespace qpid { +namespace client { + +typedef PrivateImplRef PI; + +} + +namespace messaging { + +using qpid::client::PI; + +Sender::Sender(SenderImpl* impl) { PI::ctor(*this, impl); } +Sender::Sender(const Sender& s) : qpid::client::Handle() { PI::copy(*this, s); } +Sender::~Sender() { PI::dtor(*this); } +Sender& Sender::operator=(const Sender& s) { return PI::assign(*this, s); } +void Sender::send(const Message& message) { impl->send(message); } +void Sender::cancel() { impl->cancel(); } + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/SenderImpl.h b/qpid/cpp/src/qpid/messaging/SenderImpl.h new file mode 100644 index 0000000000..77d2cfaeaf --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/SenderImpl.h @@ -0,0 +1,44 @@ +#ifndef QPID_MESSAGING_SENDERIMPL_H +#define QPID_MESSAGING_SENDERIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/RefCounted.h" + +namespace qpid { +namespace client { +} + +namespace messaging { + +class Message; + +class SenderImpl : public virtual qpid::RefCounted +{ + public: + virtual ~SenderImpl() {} + virtual void send(const Message& message) = 0; + virtual void cancel() = 0; + private: +}; +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_SENDERIMPL_H*/ diff --git a/qpid/cpp/src/qpid/messaging/Session.cpp b/qpid/cpp/src/qpid/messaging/Session.cpp new file mode 100644 index 0000000000..284b20dacc --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/Session.cpp @@ -0,0 +1,117 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/Session.h" +#include "qpid/messaging/Address.h" +#include "qpid/messaging/Filter.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/Sender.h" +#include "qpid/messaging/Receiver.h" +#include "qpid/messaging/SessionImpl.h" +#include "qpid/client/PrivateImplRef.h" + +namespace qpid { +namespace client { + +typedef PrivateImplRef PI; + +} + +namespace messaging { + +using qpid::client::PI; + +Session::Session(SessionImpl* impl) { PI::ctor(*this, impl); } +Session::Session(const Session& s) : qpid::client::Handle() { PI::copy(*this, s); } +Session::~Session() { PI::dtor(*this); } +Session& Session::operator=(const Session& s) { return PI::assign(*this, s); } +void Session::commit() { impl->commit(); } +void Session::rollback() { impl->rollback(); } +void Session::acknowledge() { impl->acknowledge(); } +void Session::reject(Message& m) { impl->reject(m); } +void Session::close() { impl->close(); } + +Sender Session::createSender(const Address& address, const VariantMap& options) +{ + return impl->createSender(address, options); +} +Receiver Session::createReceiver(const Address& address, const VariantMap& options) +{ + return impl->createReceiver(address, options); +} +Receiver Session::createReceiver(const Address& address, const Filter& filter, const VariantMap& options) +{ + return impl->createReceiver(address, filter, options); +} + +Sender Session::createSender(const std::string& address, const VariantMap& options) +{ + return impl->createSender(Address(address), options); +} +Receiver Session::createReceiver(const std::string& address, const VariantMap& options) +{ + return impl->createReceiver(Address(address), options); +} +Receiver Session::createReceiver(const std::string& address, const Filter& filter, const VariantMap& options) +{ + return impl->createReceiver(Address(address), filter, options); +} + +Address Session::createTempQueue(const std::string& baseName) +{ + return impl->createTempQueue(baseName); +} + +void Session::sync() +{ + impl->sync(); +} + +void Session::flush() +{ + impl->flush(); +} + +bool Session::fetch(Message& message, qpid::sys::Duration timeout) +{ + return impl->fetch(message, timeout); +} + +Message Session::fetch(qpid::sys::Duration timeout) +{ + return impl->fetch(timeout); +} + +bool Session::dispatch(qpid::sys::Duration timeout) +{ + return impl->dispatch(timeout); +} + +void* Session::getLastConfirmedSent() +{ + return impl->getLastConfirmedSent(); +} + +void* Session::getLastConfirmedAcknowledged() +{ + return impl->getLastConfirmedAcknowledged(); +} + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/SessionImpl.h b/qpid/cpp/src/qpid/messaging/SessionImpl.h new file mode 100644 index 0000000000..9b122a24bc --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/SessionImpl.h @@ -0,0 +1,65 @@ +#ifndef QPID_MESSAGING_SESSIONIMPL_H +#define QPID_MESSAGING_SESSIONIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/RefCounted.h" +#include +#include "qpid/messaging/Variant.h" +#include "qpid/sys/Time.h" + +namespace qpid { +namespace client { +} + +namespace messaging { + +struct Address; +struct Filter; +class Message; +class Sender; +class Receiver; + +class SessionImpl : public virtual qpid::RefCounted +{ + public: + virtual ~SessionImpl() {} + virtual void commit() = 0; + virtual void rollback() = 0; + virtual void acknowledge() = 0; + virtual void reject(Message&) = 0; + virtual void close() = 0; + virtual void sync() = 0; + virtual void flush() = 0; + virtual bool fetch(Message& message, qpid::sys::Duration timeout) = 0; + virtual Message fetch(qpid::sys::Duration timeout) = 0; + virtual bool dispatch(qpid::sys::Duration timeout) = 0; + virtual Address createTempQueue(const std::string& baseName) = 0; + virtual Sender createSender(const Address& address, const VariantMap& options) = 0; + virtual Receiver createReceiver(const Address& address, const VariantMap& options) = 0; + virtual Receiver createReceiver(const Address& address, const Filter& filter, const VariantMap& options) = 0; + virtual void* getLastConfirmedSent() = 0; + virtual void* getLastConfirmedAcknowledged() = 0; + private: +}; +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_SESSIONIMPL_H*/ diff --git a/qpid/cpp/src/qpid/messaging/Variant.cpp b/qpid/cpp/src/qpid/messaging/Variant.cpp new file mode 100644 index 0000000000..4e37134b39 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/Variant.cpp @@ -0,0 +1,603 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/Variant.h" +#include +#include + +namespace qpid { +namespace client { +} + +namespace messaging { + +InvalidConversion::InvalidConversion(const std::string& msg) : Exception(msg) {} + + +namespace { +std::string EMPTY; +} + +class VariantImpl +{ + public: + VariantImpl(); + VariantImpl(bool); + VariantImpl(uint8_t); + VariantImpl(uint16_t); + VariantImpl(uint32_t); + VariantImpl(uint64_t); + VariantImpl(int8_t); + VariantImpl(int16_t); + VariantImpl(int32_t); + VariantImpl(int64_t); + VariantImpl(float); + VariantImpl(double); + VariantImpl(const std::string&); + VariantImpl(const Variant::Map&); + VariantImpl(const Variant::List&); + ~VariantImpl(); + + VariantType getType() const; + + bool asBool() const; + uint8_t asUint8() const; + uint16_t asUint16() const; + uint32_t asUint32() const; + uint64_t asUint64() const; + int8_t asInt8() const; + int16_t asInt16() const; + int32_t asInt32() const; + int64_t asInt64() const; + float asFloat() const; + double asDouble() const; + std::string asString() const; + + const Variant::Map& asMap() const; + Variant::Map& asMap(); + const Variant::List& asList() const; + Variant::List& asList(); + + const std::string& getString() const; + std::string& getString(); + + void setEncoding(const std::string&); + const std::string& getEncoding() const; + + static VariantImpl* create(const Variant&); + private: + const VariantType type; + union { + bool b; + uint8_t ui8; + uint16_t ui16; + uint32_t ui32; + uint64_t ui64; + int8_t i8; + int16_t i16; + int32_t i32; + int64_t i64; + float f; + double d; + void* v;//variable width data + } value; + std::string encoding;//optional encoding for variable length data + + std::string getTypeName(VariantType type) const; + template T convertFromString() const + { + std::string* s = reinterpret_cast(value.v); + try { + return boost::lexical_cast(*s); + } catch(const boost::bad_lexical_cast&) { + throw InvalidConversion(QPID_MSG("Cannot convert " << *s)); + } + } +}; + + +VariantImpl::VariantImpl() : type(VAR_VOID) { value.i64 = 0; } +VariantImpl::VariantImpl(bool b) : type(VAR_BOOL) { value.b = b; } +VariantImpl::VariantImpl(uint8_t i) : type(VAR_UINT8) { value.ui8 = i; } +VariantImpl::VariantImpl(uint16_t i) : type(VAR_UINT16) { value.ui16 = i; } +VariantImpl::VariantImpl(uint32_t i) : type(VAR_UINT32) { value.ui32 = i; } +VariantImpl::VariantImpl(uint64_t i) : type(VAR_UINT64) { value.ui64 = i; } +VariantImpl::VariantImpl(int8_t i) : type(VAR_INT8) { value.i8 = i; } +VariantImpl::VariantImpl(int16_t i) : type(VAR_INT16) { value.i16 = i; } +VariantImpl::VariantImpl(int32_t i) : type(VAR_INT32) { value.i32 = i; } +VariantImpl::VariantImpl(int64_t i) : type(VAR_INT64) { value.i64 = i; } +VariantImpl::VariantImpl(float f) : type(VAR_FLOAT) { value.f = f; } +VariantImpl::VariantImpl(double d) : type(VAR_DOUBLE) { value.d = d; } +VariantImpl::VariantImpl(const std::string& s) : type(VAR_STRING) { value.v = new std::string(s); } +VariantImpl::VariantImpl(const Variant::Map& m) : type(VAR_MAP) { value.v = new Variant::Map(m); } +VariantImpl::VariantImpl(const Variant::List& l) : type(VAR_LIST) { value.v = new Variant::List(l); } + +VariantImpl::~VariantImpl() { + switch (type) { + case VAR_STRING: + delete reinterpret_cast(value.v); + break; + case VAR_MAP: + delete reinterpret_cast(value.v); + break; + case VAR_LIST: + delete reinterpret_cast(value.v); + break; + default: + break; + } +} + +VariantType VariantImpl::getType() const { return type; } + +namespace { + +bool same_char(char a, char b) +{ + return toupper(a) == toupper(b); +} + +bool caseInsensitiveMatch(const std::string& a, const std::string& b) +{ + return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin(), &same_char); +} + +const std::string TRUE("True"); +const std::string FALSE("False"); + +bool toBool(const std::string& s) +{ + if (caseInsensitiveMatch(s, TRUE)) return true; + if (caseInsensitiveMatch(s, FALSE)) return false; + try { return boost::lexical_cast(s); } catch(const boost::bad_lexical_cast&) {} + throw InvalidConversion(QPID_MSG("Cannot convert " << s << " to bool")); +} + +} + +bool VariantImpl::asBool() const +{ + switch(type) { + case VAR_VOID: return false; + case VAR_BOOL: return value.b; + case VAR_UINT8: return value.ui8; + case VAR_UINT16: return value.ui16; + case VAR_UINT32: return value.ui32; + case VAR_UINT64: return value.ui64; + case VAR_INT8: return value.i8; + case VAR_INT16: return value.i16; + case VAR_INT32: return value.i32; + case VAR_INT64: return value.i64; + case VAR_STRING: return toBool(*reinterpret_cast(value.v)); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_BOOL))); + } +} +uint8_t VariantImpl::asUint8() const +{ + switch(type) { + case VAR_UINT8: return value.ui8; + case VAR_STRING: return convertFromString(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_UINT8))); + } +} +uint16_t VariantImpl::asUint16() const +{ + switch(type) { + case VAR_UINT8: return value.ui8; + case VAR_UINT16: return value.ui16; + case VAR_STRING: return convertFromString(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_UINT16))); + } +} +uint32_t VariantImpl::asUint32() const +{ + switch(type) { + case VAR_UINT8: return value.ui8; + case VAR_UINT16: return value.ui16; + case VAR_UINT32: return value.ui32; + case VAR_STRING: return convertFromString(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_UINT32))); + } +} +uint64_t VariantImpl::asUint64() const +{ + switch(type) { + case VAR_UINT8: return value.ui8; + case VAR_UINT16: return value.ui16; + case VAR_UINT32: return value.ui32; + case VAR_UINT64: return value.ui64; + case VAR_STRING: return convertFromString(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_UINT64))); + } +} +int8_t VariantImpl::asInt8() const +{ + switch(type) { + case VAR_INT8: return value.i8; + case VAR_STRING: return convertFromString(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_INT8))); + } +} +int16_t VariantImpl::asInt16() const +{ + switch(type) { + case VAR_INT8: return value.i8; + case VAR_INT16: return value.i16; + case VAR_STRING: return convertFromString(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_INT16))); + } +} +int32_t VariantImpl::asInt32() const +{ + switch(type) { + case VAR_INT8: return value.i8; + case VAR_INT16: return value.i16; + case VAR_INT32: return value.i32; + case VAR_STRING: return convertFromString(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_INT32))); + } +} +int64_t VariantImpl::asInt64() const +{ + switch(type) { + case VAR_INT8: return value.i8; + case VAR_INT16: return value.i16; + case VAR_INT32: return value.i32; + case VAR_INT64: return value.i64; + case VAR_STRING: return convertFromString(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_INT64))); + } +} +float VariantImpl::asFloat() const +{ + switch(type) { + case VAR_FLOAT: return value.f; + case VAR_STRING: return convertFromString(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_FLOAT))); + } +} +double VariantImpl::asDouble() const +{ + switch(type) { + case VAR_FLOAT: return value.f; + case VAR_DOUBLE: return value.d; + case VAR_STRING: return convertFromString(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_DOUBLE))); + } +} +std::string VariantImpl::asString() const +{ + switch(type) { + case VAR_VOID: return EMPTY; + case VAR_BOOL: return value.b ? TRUE : FALSE; + case VAR_UINT8: return boost::lexical_cast((int) value.ui8); + case VAR_UINT16: return boost::lexical_cast(value.ui16); + case VAR_UINT32: return boost::lexical_cast(value.ui32); + case VAR_UINT64: return boost::lexical_cast(value.ui64); + case VAR_INT8: return boost::lexical_cast((int) value.i8); + case VAR_INT16: return boost::lexical_cast(value.i16); + case VAR_INT32: return boost::lexical_cast(value.i32); + case VAR_INT64: return boost::lexical_cast(value.i64); + case VAR_DOUBLE: return boost::lexical_cast(value.d); + case VAR_FLOAT: return boost::lexical_cast(value.f); + case VAR_STRING: return *reinterpret_cast(value.v); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_STRING))); + } +} + +const Variant::Map& VariantImpl::asMap() const +{ + switch(type) { + case VAR_MAP: return *reinterpret_cast(value.v); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_MAP))); + } +} + +Variant::Map& VariantImpl::asMap() +{ + switch(type) { + case VAR_MAP: return *reinterpret_cast(value.v); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_MAP))); + } +} + +const Variant::List& VariantImpl::asList() const +{ + switch(type) { + case VAR_LIST: return *reinterpret_cast(value.v); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_LIST))); + } +} + +Variant::List& VariantImpl::asList() +{ + switch(type) { + case VAR_LIST: return *reinterpret_cast(value.v); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_LIST))); + } +} + +std::string& VariantImpl::getString() +{ + switch(type) { + case VAR_STRING: return *reinterpret_cast(value.v); + default: throw InvalidConversion(QPID_MSG("Variant is not a string; use asString() if conversion is required.")); + } +} + +const std::string& VariantImpl::getString() const +{ + switch(type) { + case VAR_STRING: return *reinterpret_cast(value.v); + default: throw InvalidConversion(QPID_MSG("Variant is not a string; use asString() if conversion is required.")); + } +} + +void VariantImpl::setEncoding(const std::string& s) { encoding = s; } +const std::string& VariantImpl::getEncoding() const { return encoding; } + +std::string VariantImpl::getTypeName(VariantType type) const +{ + switch (type) { + case VAR_VOID: return "void"; + case VAR_BOOL: return "bool"; + case VAR_UINT8: return "uint8"; + case VAR_UINT16: return "uint16"; + case VAR_UINT32: return "uint32"; + case VAR_UINT64: return "uint64"; + case VAR_INT8: return "int8"; + case VAR_INT16: return "int16"; + case VAR_INT32: return "int32"; + case VAR_INT64: return "int64"; + case VAR_FLOAT: return "float"; + case VAR_DOUBLE: return "double"; + case VAR_STRING: return "string"; + case VAR_MAP: return "map"; + case VAR_LIST: return "list"; + } + return "";//should never happen +} + +VariantImpl* VariantImpl::create(const Variant& v) +{ + switch (v.getType()) { + case VAR_BOOL: return new VariantImpl(v.asBool()); + case VAR_UINT8: return new VariantImpl(v.asUint8()); + case VAR_UINT16: return new VariantImpl(v.asUint16()); + case VAR_UINT32: return new VariantImpl(v.asUint32()); + case VAR_UINT64: return new VariantImpl(v.asUint64()); + case VAR_INT8: return new VariantImpl(v.asInt8()); + case VAR_INT16: return new VariantImpl(v.asInt16()); + case VAR_INT32: return new VariantImpl(v.asInt32()); + case VAR_INT64: return new VariantImpl(v.asInt64()); + case VAR_FLOAT: return new VariantImpl(v.asFloat()); + case VAR_DOUBLE: return new VariantImpl(v.asDouble()); + case VAR_STRING: return new VariantImpl(v.asString()); + case VAR_MAP: return new VariantImpl(v.asMap()); + case VAR_LIST: return new VariantImpl(v.asList()); + default: return new VariantImpl(); + } +} + +Variant::Variant() : impl(new VariantImpl()) {} +Variant::Variant(bool b) : impl(new VariantImpl(b)) {} +Variant::Variant(uint8_t i) : impl(new VariantImpl(i)) {} +Variant::Variant(uint16_t i) : impl(new VariantImpl(i)) {} +Variant::Variant(uint32_t i) : impl(new VariantImpl(i)) {} +Variant::Variant(uint64_t i) : impl(new VariantImpl(i)) {} +Variant::Variant(int8_t i) : impl(new VariantImpl(i)) {} +Variant::Variant(int16_t i) : impl(new VariantImpl(i)) {} +Variant::Variant(int32_t i) : impl(new VariantImpl(i)) {} +Variant::Variant(int64_t i) : impl(new VariantImpl(i)) {} +Variant::Variant(float f) : impl(new VariantImpl(f)) {} +Variant::Variant(double d) : impl(new VariantImpl(d)) {} +Variant::Variant(const std::string& s) : impl(new VariantImpl(s)) {} +Variant::Variant(const char* s) : impl(new VariantImpl(std::string(s))) {} +Variant::Variant(const Map& m) : impl(new VariantImpl(m)) {} +Variant::Variant(const List& l) : impl(new VariantImpl(l)) {} +Variant::Variant(const Variant& v) : impl(VariantImpl::create(v)) {} + +Variant::~Variant() { if (impl) delete impl; } + +void Variant::reset() +{ + if (impl) delete impl; + impl = new VariantImpl(); +} + + +Variant& Variant::operator=(bool b) +{ + if (impl) delete impl; + impl = new VariantImpl(b); + return *this; +} + +Variant& Variant::operator=(uint8_t i) +{ + if (impl) delete impl; + impl = new VariantImpl(i); + return *this; +} +Variant& Variant::operator=(uint16_t i) +{ + if (impl) delete impl; + impl = new VariantImpl(i); + return *this; +} +Variant& Variant::operator=(uint32_t i) +{ + if (impl) delete impl; + impl = new VariantImpl(i); + return *this; +} +Variant& Variant::operator=(uint64_t i) +{ + if (impl) delete impl; + impl = new VariantImpl(i); + return *this; +} + +Variant& Variant::operator=(int8_t i) +{ + if (impl) delete impl; + impl = new VariantImpl(i); + return *this; +} +Variant& Variant::operator=(int16_t i) +{ + if (impl) delete impl; + impl = new VariantImpl(i); + return *this; +} +Variant& Variant::operator=(int32_t i) +{ + if (impl) delete impl; + impl = new VariantImpl(i); + return *this; +} +Variant& Variant::operator=(int64_t i) +{ + if (impl) delete impl; + impl = new VariantImpl(i); + return *this; +} + +Variant& Variant::operator=(float f) +{ + if (impl) delete impl; + impl = new VariantImpl(f); + return *this; +} +Variant& Variant::operator=(double d) +{ + if (impl) delete impl; + impl = new VariantImpl(d); + return *this; +} + +Variant& Variant::operator=(const std::string& s) +{ + if (impl) delete impl; + impl = new VariantImpl(s); + return *this; +} + +Variant& Variant::operator=(const char* s) +{ + if (impl) delete impl; + impl = new VariantImpl(std::string(s)); + return *this; +} + +Variant& Variant::operator=(const Map& m) +{ + if (impl) delete impl; + impl = new VariantImpl(m); + return *this; +} + +Variant& Variant::operator=(const List& l) +{ + if (impl) delete impl; + impl = new VariantImpl(l); + return *this; +} + +Variant& Variant::operator=(const Variant& v) +{ + if (impl) delete impl; + impl = VariantImpl::create(v); + return *this; +} + +VariantType Variant::getType() const { return impl->getType(); } +bool Variant::asBool() const { return impl->asBool(); } +uint8_t Variant::asUint8() const { return impl->asUint8(); } +uint16_t Variant::asUint16() const { return impl->asUint16(); } +uint32_t Variant::asUint32() const { return impl->asUint32(); } +uint64_t Variant::asUint64() const { return impl->asUint64(); } +int8_t Variant::asInt8() const { return impl->asInt8(); } +int16_t Variant::asInt16() const { return impl->asInt16(); } +int32_t Variant::asInt32() const { return impl->asInt32(); } +int64_t Variant::asInt64() const { return impl->asInt64(); } +float Variant::asFloat() const { return impl->asFloat(); } +double Variant::asDouble() const { return impl->asDouble(); } +std::string Variant::asString() const { return impl->asString(); } +const Variant::Map& Variant::asMap() const { return impl->asMap(); } +Variant::Map& Variant::asMap() { return impl->asMap(); } +const Variant::List& Variant::asList() const { return impl->asList(); } +Variant::List& Variant::asList() { return impl->asList(); } +const std::string& Variant::getString() const { return impl->getString(); } +std::string& Variant::getString() { return impl->getString(); } +void Variant::setEncoding(const std::string& s) { impl->setEncoding(s); } +const std::string& Variant::getEncoding() const { return impl->getEncoding(); } + +Variant::operator bool() const { return asBool(); } +Variant::operator uint8_t() const { return asUint8(); } +Variant::operator uint16_t() const { return asUint16(); } +Variant::operator uint32_t() const { return asUint32(); } +Variant::operator uint64_t() const { return asUint64(); } +Variant::operator int8_t() const { return asInt8(); } +Variant::operator int16_t() const { return asInt16(); } +Variant::operator int32_t() const { return asInt32(); } +Variant::operator int64_t() const { return asInt64(); } +Variant::operator float() const { return asFloat(); } +Variant::operator double() const { return asDouble(); } +Variant::operator const char*() const { return asString().c_str(); } + +std::ostream& operator<<(std::ostream& out, const Variant::Map& map) +{ + for (Variant::Map::const_iterator i = map.begin(); i != map.end(); ++i) { + if (i != map.begin()) out << ", "; + out << i->first << ":" << i->second; + } + return out; +} + +std::ostream& operator<<(std::ostream& out, const Variant::List& list) +{ + for (Variant::List::const_iterator i = list.begin(); i != list.end(); ++i) { + if (i != list.begin()) out << ", "; + out << *i; + } + return out; +} + +std::ostream& operator<<(std::ostream& out, const Variant& value) +{ + switch (value.getType()) { + case VAR_MAP: + out << "{" << value.asMap() << "}"; + break; + case VAR_LIST: + out << "[" << value.asList() << "]"; + break; + case VAR_VOID: + out << ""; + break; + default: + out << value.asString(); + break; + } + return out; +} + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/sys/Timer.cpp b/qpid/cpp/src/qpid/sys/Timer.cpp index d569fb3bb7..3d2a900455 100644 --- a/qpid/cpp/src/qpid/sys/Timer.cpp +++ b/qpid/cpp/src/qpid/sys/Timer.cpp @@ -84,6 +84,7 @@ Timer::~Timer() stop(); } +// TODO AStitcher 21/08/09 The threshholds for emitting warnings are a little arbitrary void Timer::run() { Monitor::ScopedLock l(monitor); @@ -111,8 +112,8 @@ void Timer::run() // Warn on callback overrun AbsTime end(AbsTime::now()); Duration overrun(tasks.top()->nextFireTime, end); - bool late = delay > 1 * TIME_MSEC; - bool overran = overrun > 1 * TIME_MSEC; + bool late = delay > 10 * TIME_MSEC; + bool overran = overrun > 2 * TIME_MSEC; if (late) if (overran) { QPID_LOG(warning, diff --git a/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp b/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp index 5d1ac6d034..8545ebd9cb 100644 --- a/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp +++ b/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp @@ -305,6 +305,13 @@ private: * thread processing this handle. */ volatile bool writePending; + /** + * This records whether we've been reading is flow controlled: + * it's safe as a simple boolean as the only way to be stopped + * is in calls only allowed in the callback context, the only calls + * checking it are also in calls only allowed in callback context. + */ + volatile bool readingStopped; }; AsynchIO::AsynchIO(const Socket& s, @@ -323,7 +330,8 @@ AsynchIO::AsynchIO(const Socket& s, idleCallback(iCb), socket(s), queuedClose(false), - writePending(false) { + writePending(false), + readingStopped(false) { s.setNonblocking(); } @@ -351,8 +359,11 @@ void AsynchIO::queueReadBuffer(BufferBase* buff) { assert(buff); buff->dataStart = 0; buff->dataCount = 0; + + bool queueWasEmpty = bufferQueue.empty(); bufferQueue.push_back(buff); - DispatchHandle::rewatchRead(); + if (queueWasEmpty && !readingStopped) + DispatchHandle::rewatchRead(); } void AsynchIO::unread(BufferBase* buff) { @@ -361,15 +372,18 @@ void AsynchIO::unread(BufferBase* buff) { memmove(buff->bytes, buff->bytes+buff->dataStart, buff->dataCount); buff->dataStart = 0; } + + bool queueWasEmpty = bufferQueue.empty(); bufferQueue.push_front(buff); - DispatchHandle::rewatchRead(); + if (queueWasEmpty && !readingStopped) + DispatchHandle::rewatchRead(); } void AsynchIO::queueWrite(BufferBase* buff) { assert(buff); // If we've already closed the socket then throw the write away if (queuedClose) { - bufferQueue.push_front(buff); + queueReadBuffer(buff); return; } else { writeQueue.push_front(buff); @@ -378,6 +392,7 @@ void AsynchIO::queueWrite(BufferBase* buff) { DispatchHandle::rewatchWrite(); } +// This can happen outside the callback context void AsynchIO::notifyPendingWrite() { writePending = true; DispatchHandle::rewatchWrite(); @@ -392,11 +407,14 @@ bool AsynchIO::writeQueueEmpty() { return writeQueue.empty(); } +// This can happen outside the callback context void AsynchIO::startReading() { + readingStopped = false; DispatchHandle::rewatchRead(); } void AsynchIO::stopReading() { + readingStopped = true; DispatchHandle::unwatchRead(); } diff --git a/qpid/cpp/src/qpid/sys/uuid.h b/qpid/cpp/src/qpid/sys/uuid.h new file mode 100644 index 0000000000..804ab34463 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/uuid.h @@ -0,0 +1,28 @@ +#ifndef _sys_uuid_h +#define _sys_uuid_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifdef _WIN32 +# include "qpid/sys/windows/uuid.h" +#else +# include +#endif /* _WIN32 */ + +#endif /* _sys_uuid_h */ diff --git a/qpid/cpp/src/qpid/sys/windows/uuid.h b/qpid/cpp/src/qpid/sys/windows/uuid.h new file mode 100644 index 0000000000..c79abe95c6 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/windows/uuid.h @@ -0,0 +1,38 @@ +#ifndef _sys_windows_uuid_h +#define _sys_windows_uuid_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/CommonImportExport.h" +#include + +namespace qpid { namespace sys { const size_t UuidSize = 16; }} +typedef uint8_t uuid_t[qpid::sys::UuidSize]; + +QPID_COMMON_EXTERN void uuid_clear (uuid_t uu); +QPID_COMMON_EXTERN void uuid_copy (uuid_t dst, const uuid_t src); +QPID_COMMON_EXTERN void uuid_generate (uuid_t out); +QPID_COMMON_EXTERN int uuid_is_null (const uuid_t uu); // Returns 1 if null, else 0 +QPID_COMMON_EXTERN int uuid_parse (const char *in, uuid_t uu); // Returns 0 on success, else -1 +QPID_COMMON_EXTERN void uuid_unparse (const uuid_t uu, char *out); + +#endif /*!_sys_windows_uuid_h*/ diff --git a/qpid/cpp/src/qpidd.cpp b/qpid/cpp/src/qpidd.cpp index 9c3c6b0c4b..1839a62205 100644 --- a/qpid/cpp/src/qpidd.cpp +++ b/qpid/cpp/src/qpidd.cpp @@ -77,7 +77,7 @@ int main(int argc, char* argv[]) return broker.execute(options.get()); } catch(const exception& e) { - QPID_LOG(critical, "Broker start-up failed: " << e.what()); + QPID_LOG(critical, "Unexpected error: " << e.what()); } return 1; } diff --git a/qpid/cpp/src/tests/CMakeLists.txt b/qpid/cpp/src/tests/CMakeLists.txt index 34f5d35a9a..b1219aad74 100644 --- a/qpid/cpp/src/tests/CMakeLists.txt +++ b/qpid/cpp/src/tests/CMakeLists.txt @@ -95,6 +95,7 @@ set(unit_tests_to_build InlineAllocator InlineVector ClientSessionTest + MessagingSessionTests SequenceSet StringUtils IncompleteMessageList @@ -128,6 +129,7 @@ set(unit_tests_to_build ReplicationTest ClientMessageTest PollableCondition + Variant ${xml_tests} CACHE STRING "Which unit tests to build" ) diff --git a/qpid/cpp/src/tests/FieldTable.cpp b/qpid/cpp/src/tests/FieldTable.cpp index a02bbd5194..5b43871f6d 100644 --- a/qpid/cpp/src/tests/FieldTable.cpp +++ b/qpid/cpp/src/tests/FieldTable.cpp @@ -22,6 +22,7 @@ #include "qpid/framing/Array.h" #include "qpid/framing/FieldTable.h" #include "qpid/framing/FieldValue.h" +#include "qpid/framing/List.h" #include "qpid/sys/alloca.h" #include "unit_test.h" @@ -86,7 +87,9 @@ QPID_AUTO_TEST_CASE(testAssignment) QPID_AUTO_TEST_CASE(testNestedValues) { - char buff[100]; + double d = 1.2345; + uint32_t u = 101; + char buff[1000]; { FieldTable a; FieldTable b; @@ -94,11 +97,17 @@ QPID_AUTO_TEST_CASE(testNestedValues) items.push_back("one"); items.push_back("two"); Array c(items); + List list; + list.push_back(List::ValuePtr(new Str16Value("red"))); + list.push_back(List::ValuePtr(new Unsigned32Value(u))); + list.push_back(List::ValuePtr(new Str8Value("yellow"))); + list.push_back(List::ValuePtr(new DoubleValue(d))); a.setString("id", "A"); b.setString("id", "B"); a.setTable("B", b); a.setArray("C", c); + a.set("my-list", FieldTable::ValuePtr(new ListValue(list))); Buffer wbuffer(buff, 100); @@ -119,6 +128,27 @@ QPID_AUTO_TEST_CASE(testNestedValues) BOOST_CHECK((uint) 2 == items.size()); BOOST_CHECK(string("one") == items[0]); BOOST_CHECK(string("two") == items[1]); + + List list; + BOOST_CHECK(a.get("my-list")->get(list)); + List::const_iterator i = list.begin(); + BOOST_CHECK(i != list.end()); + BOOST_CHECK_EQUAL(std::string("red"), (*i)->get()); + + i++; + BOOST_CHECK(i != list.end()); + BOOST_CHECK_EQUAL(u, (uint32_t) (*i)->get()); + + i++; + BOOST_CHECK(i != list.end()); + BOOST_CHECK_EQUAL(std::string("yellow"), (*i)->get()); + + i++; + BOOST_CHECK(i != list.end()); + BOOST_CHECK_EQUAL(d, (*i)->get()); + + i++; + BOOST_CHECK(i == list.end()); } } diff --git a/qpid/cpp/src/tests/Makefile.am b/qpid/cpp/src/tests/Makefile.am index db4c8ba914..a15ba3578c 100644 --- a/qpid/cpp/src/tests/Makefile.am +++ b/qpid/cpp/src/tests/Makefile.am @@ -65,6 +65,7 @@ unit_test_LDADD=-lboost_unit_test_framework -lboost_regex \ $(lib_client) $(lib_broker) $(lib_console) unit_test_SOURCES= unit_test.cpp unit_test.h \ + MessagingSessionTests.cpp \ ClientSessionTest.cpp \ BrokerFixture.h SocketProxy.h \ exception_test.cpp \ @@ -111,7 +112,8 @@ unit_test_SOURCES= unit_test.cpp unit_test.h \ FrameDecoder.cpp \ ReplicationTest.cpp \ ClientMessageTest.cpp \ - PollableCondition.cpp + PollableCondition.cpp \ + Variant.cpp if HAVE_XML unit_test_SOURCES+= XmlClientSessionTest.cpp @@ -268,6 +270,11 @@ check_PROGRAMS+=qrsh qrsh_SOURCES=qrsh.cpp qrsh_LDADD=$(lib_client) +check_PROGRAMS+=qpid_stream +qpid_stream_INCLUDES=$(PUBLIC_INCLUDES) +qpid_stream_SOURCES=qpid_stream.cpp +qpid_stream_LDADD=$(lib_client) + TESTS_ENVIRONMENT = \ VALGRIND=$(VALGRIND) \ diff --git a/qpid/cpp/src/tests/MessagingSessionTests.cpp b/qpid/cpp/src/tests/MessagingSessionTests.cpp new file mode 100644 index 0000000000..4ee27f0764 --- /dev/null +++ b/qpid/cpp/src/tests/MessagingSessionTests.cpp @@ -0,0 +1,360 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "unit_test.h" +#include "test_tools.h" +#include "BrokerFixture.h" +#include "qpid/messaging/Connection.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/MessageListener.h" +#include "qpid/messaging/Receiver.h" +#include "qpid/messaging/Sender.h" +#include "qpid/messaging/Session.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Session.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/sys/Time.h" +#include +#include +#include +#include + +QPID_AUTO_TEST_SUITE(MessagingSessionTests) + +using namespace qpid::messaging; +using namespace qpid; +using qpid::broker::Broker; + +struct BrokerAdmin +{ + qpid::client::Connection connection; + qpid::client::Session session; + + BrokerAdmin(uint16_t port) + { + connection.open("localhost", port); + session = connection.newSession(); + } + + void createQueue(const std::string& name) + { + session.queueDeclare(qpid::client::arg::queue=name); + } + + void deleteQueue(const std::string& name) + { + session.queueDelete(qpid::client::arg::queue=name); + } + + void createExchange(const std::string& name, const std::string& type) + { + session.exchangeDeclare(qpid::client::arg::exchange=name, qpid::client::arg::type=type); + } + + void deleteExchange(const std::string& name) + { + session.exchangeDelete(qpid::client::arg::exchange=name); + } + + ~BrokerAdmin() + { + session.close(); + connection.close(); + } +}; + +struct MessagingFixture : public BrokerFixture +{ + Connection connection; + Session session; + BrokerAdmin admin; + + MessagingFixture(Broker::Options opts = Broker::Options()) : + BrokerFixture(opts), + connection(Connection::open((boost::format("amqp:tcp:localhost:%1%") % (broker->getPort(Broker::TCP_TRANSPORT))).str())), + session(connection.newSession()), + admin(broker->getPort(Broker::TCP_TRANSPORT)) {} + + ~MessagingFixture() + { + session.close(); + connection.close(); + } +}; + +struct QueueFixture : MessagingFixture +{ + std::string queue; + + QueueFixture(const std::string& name = "test-queue") : queue(name) + { + admin.createQueue(queue); + } + + ~QueueFixture() + { + admin.deleteQueue(queue); + } + +}; + +struct TopicFixture : MessagingFixture +{ + std::string topic; + + TopicFixture(const std::string& name = "test-topic", const std::string& type="fanout") : topic(name) + { + admin.createExchange(topic, type); + } + + ~TopicFixture() + { + admin.deleteExchange(topic); + } + +}; + +struct MultiQueueFixture : MessagingFixture +{ + typedef std::vector::const_iterator const_iterator; + std::vector queues; + + MultiQueueFixture(const std::vector& names = boost::assign::list_of("q1")("q2")("q3")) : queues(names) + { + for (const_iterator i = queues.begin(); i != queues.end(); ++i) { + admin.createQueue(*i); + } + } + + ~MultiQueueFixture() + { + for (const_iterator i = queues.begin(); i != queues.end(); ++i) { + admin.deleteQueue(*i); + } + } + +}; + +struct MessageDataCollector : MessageListener +{ + std::vector messageData; + + void received(Message& message) { + messageData.push_back(message.getBytes()); + } +}; + +std::vector fetch(Receiver& receiver, int count, qpid::sys::Duration timeout=qpid::sys::TIME_SEC*5) +{ + std::vector data; + Message message; + for (int i = 0; i < count && receiver.fetch(message, timeout); i++) { + data.push_back(message.getBytes()); + } + return data; +} + +QPID_AUTO_TEST_CASE(testSimpleSendReceive) +{ + QueueFixture fix; + Sender sender = fix.session.createSender(fix.queue); + Message out("test-message"); + sender.send(out); + Receiver receiver = fix.session.createReceiver(fix.queue); + Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); + fix.session.acknowledge(); + BOOST_CHECK_EQUAL(in.getBytes(), out.getBytes()); +} + +QPID_AUTO_TEST_CASE(testSendReceiveHeaders) +{ + QueueFixture fix; + Sender sender = fix.session.createSender(fix.queue); + Message out("test-message"); + for (uint i = 0; i < 10; ++i) { + out.getHeaders()["a"] = i; + sender.send(out); + } + Receiver receiver = fix.session.createReceiver(fix.queue); + Message in; + for (uint i = 0; i < 10; ++i) { + //Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); + BOOST_CHECK(receiver.fetch(in, 5 * qpid::sys::TIME_SEC)); + BOOST_CHECK_EQUAL(in.getBytes(), out.getBytes()); + BOOST_CHECK_EQUAL(in.getHeaders()["a"].asUint32(), i); + fix.session.acknowledge(); + } +} + +QPID_AUTO_TEST_CASE(testSenderError) +{ + MessagingFixture fix; + //TODO: this is the wrong type for the exception; define explicit set in messaging namespace + BOOST_CHECK_THROW(fix.session.createSender("NonExistentAddress"), qpid::framing::NotFoundException); +} + +QPID_AUTO_TEST_CASE(testReceiverError) +{ + MessagingFixture fix; + //TODO: this is the wrong type for the exception; define explicit set in messaging namespace + BOOST_CHECK_THROW(fix.session.createReceiver("NonExistentAddress"), qpid::framing::NotFoundException); +} + +QPID_AUTO_TEST_CASE(testSimpleTopic) +{ + TopicFixture fix; + + Sender sender = fix.session.createSender(fix.topic); + Message msg("one"); + sender.send(msg); + Receiver sub1 = fix.session.createReceiver(fix.topic); + sub1.setCapacity(10u); + sub1.start(); + msg.setBytes("two"); + sender.send(msg); + Receiver sub2 = fix.session.createReceiver(fix.topic); + sub2.setCapacity(10u); + sub2.start(); + msg.setBytes("three"); + sender.send(msg); + Receiver sub3 = fix.session.createReceiver(fix.topic); + sub3.setCapacity(10u); + sub3.start(); + msg.setBytes("four"); + sender.send(msg); + BOOST_CHECK_EQUAL(fetch(sub2, 2), boost::assign::list_of("three")("four")); + sub2.cancel(); + + msg.setBytes("five"); + sender.send(msg); + BOOST_CHECK_EQUAL(fetch(sub1, 4), boost::assign::list_of("two")("three")("four")("five")); + BOOST_CHECK_EQUAL(fetch(sub3, 2), boost::assign::list_of("four")("five")); + Message in; + BOOST_CHECK(!sub2.fetch(in, 0));//TODO: or should this raise an error? + + + //TODO: check pending messages... +} + +QPID_AUTO_TEST_CASE(testSessionFetch) +{ + MultiQueueFixture fix; + + for (uint i = 0; i < fix.queues.size(); i++) { + Receiver r = fix.session.createReceiver(fix.queues[i]); + r.setCapacity(10u); + r.start();//TODO: add Session::start + } + + for (uint i = 0; i < fix.queues.size(); i++) { + Sender s = fix.session.createSender(fix.queues[i]); + Message msg((boost::format("Message_%1%") % (i+1)).str()); + s.send(msg); + } + + for (uint i = 0; i < fix.queues.size(); i++) { + Message msg; + BOOST_CHECK(fix.session.fetch(msg, qpid::sys::TIME_SEC)); + BOOST_CHECK_EQUAL(msg.getBytes(), (boost::format("Message_%1%") % (i+1)).str()); + } +} + +QPID_AUTO_TEST_CASE(testSessionDispatch) +{ + MultiQueueFixture fix; + + MessageDataCollector collector; + for (uint i = 0; i < fix.queues.size(); i++) { + Receiver r = fix.session.createReceiver(fix.queues[i]); + r.setListener(&collector); + r.setCapacity(10u); + r.start();//TODO: add Session::start + } + + for (uint i = 0; i < fix.queues.size(); i++) { + Sender s = fix.session.createSender(fix.queues[i]); + Message msg((boost::format("Message_%1%") % (i+1)).str()); + s.send(msg); + } + + while (fix.session.dispatch(qpid::sys::TIME_SEC)) ; + + BOOST_CHECK_EQUAL(collector.messageData, boost::assign::list_of("Message_1")("Message_2")("Message_3")); +} + + +QPID_AUTO_TEST_CASE(testMapMessage) +{ + QueueFixture fix; + Sender sender = fix.session.createSender(fix.queue); + Message out; + out.getContent().asMap()["abc"] = "def"; + out.getContent().asMap()["pi"] = 3.14f; + sender.send(out); + Receiver receiver = fix.session.createReceiver(fix.queue); + Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); + BOOST_CHECK_EQUAL(in.getContent().asMap()["abc"].asString(), "def"); + BOOST_CHECK_EQUAL(in.getContent().asMap()["pi"].asFloat(), 3.14f); + fix.session.acknowledge(); +} + +QPID_AUTO_TEST_CASE(testListMessage) +{ + QueueFixture fix; + Sender sender = fix.session.createSender(fix.queue); + Message out; + out.getContent() = Variant::List(); + out.getContent() << "abc"; + out.getContent() << 1234; + out.getContent() << "def"; + out.getContent() << 56.789; + sender.send(out); + Receiver receiver = fix.session.createReceiver(fix.queue); + Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); + Variant::List& list = in.getContent().asList(); + BOOST_CHECK_EQUAL(list.size(), out.getContent().asList().size()); + BOOST_CHECK_EQUAL(list.front().asString(), "abc"); + list.pop_front(); + BOOST_CHECK_EQUAL(list.front().asInt64(), 1234); + list.pop_front(); + BOOST_CHECK_EQUAL(list.front().asString(), "def"); + list.pop_front(); + BOOST_CHECK_EQUAL(list.front().asDouble(), 56.789); + fix.session.acknowledge(); +} + +QPID_AUTO_TEST_CASE(testReject) +{ + QueueFixture fix; + Sender sender = fix.session.createSender(fix.queue); + Message m1("reject-me"); + sender.send(m1); + Message m2("accept-me"); + sender.send(m2); + Receiver receiver = fix.session.createReceiver(fix.queue); + Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); + BOOST_CHECK_EQUAL(in.getBytes(), m1.getBytes()); + fix.session.reject(in); + in = receiver.fetch(5 * qpid::sys::TIME_SEC); + BOOST_CHECK_EQUAL(in.getBytes(), m2.getBytes()); + fix.session.acknowledge(); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/Variant.cpp b/qpid/cpp/src/tests/Variant.cpp new file mode 100644 index 0000000000..b7ce776827 --- /dev/null +++ b/qpid/cpp/src/tests/Variant.cpp @@ -0,0 +1,157 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include +#include "qpid/messaging/Variant.h" + +#include "unit_test.h" + +using namespace qpid::messaging; + +QPID_AUTO_TEST_SUITE(VariantSuite) + +QPID_AUTO_TEST_CASE(testConversions) +{ + Variant value; + + //string to float/double + value = "1.5"; + BOOST_CHECK_EQUAL((float) 1.5, value.asFloat()); + BOOST_CHECK_EQUAL((double) 1.5, value.asDouble()); + + //float to string or double + value = 1.5f; + BOOST_CHECK_EQUAL((float) 1.5, value.asFloat()); + BOOST_CHECK_EQUAL((double) 1.5, value.asDouble()); + BOOST_CHECK_EQUAL(std::string("1.5"), value.asString()); + + //double to string (conversion to float not valid) + value = 1.5; + BOOST_CHECK_THROW(value.asFloat(), InvalidConversion); + BOOST_CHECK_EQUAL((double) 1.5, value.asDouble()); + BOOST_CHECK_EQUAL(std::string("1.5"), value.asString()); + + //uint8 to larger unsigned ints and string + value = (uint8_t) 7; + BOOST_CHECK_EQUAL((uint8_t) 7, value.asUint8()); + BOOST_CHECK_EQUAL((uint16_t) 7, value.asUint16()); + BOOST_CHECK_EQUAL((uint32_t) 7, value.asUint32()); + BOOST_CHECK_EQUAL((uint64_t) 7, value.asUint64()); + BOOST_CHECK_EQUAL(std::string("7"), value.asString()); + BOOST_CHECK_THROW(value.asInt8(), InvalidConversion); + + value = (uint16_t) 8; + BOOST_CHECK_EQUAL(std::string("8"), value.asString()); + value = (uint32_t) 9; + BOOST_CHECK_EQUAL(std::string("9"), value.asString()); + + //uint32 to larger unsigned ints and string + value = (uint32_t) 9999999; + BOOST_CHECK_EQUAL((uint32_t) 9999999, value.asUint32()); + BOOST_CHECK_EQUAL((uint64_t) 9999999, value.asUint64()); + BOOST_CHECK_EQUAL(std::string("9999999"), value.asString()); + BOOST_CHECK_THROW(value.asUint8(), InvalidConversion); + BOOST_CHECK_THROW(value.asUint16(), InvalidConversion); + BOOST_CHECK_THROW(value.asInt32(), InvalidConversion); + + value = "true"; + BOOST_CHECK(value.asBool()); + value = "false"; + BOOST_CHECK(!value.asBool()); + value = "1"; + BOOST_CHECK(value.asBool()); + value = "0"; + BOOST_CHECK(!value.asBool()); + value = "other"; + BOOST_CHECK_THROW(value.asBool(), InvalidConversion); +} + +QPID_AUTO_TEST_CASE(testAssignment) +{ + Variant value("abc"); + Variant other = value; + BOOST_CHECK_EQUAL(VAR_STRING, value.getType()); + BOOST_CHECK_EQUAL(other.getType(), value.getType()); + BOOST_CHECK_EQUAL(other.asString(), value.asString()); + + const uint32_t i(1000); + value = i; + BOOST_CHECK_EQUAL(VAR_UINT32, value.getType()); + BOOST_CHECK_EQUAL(VAR_STRING, other.getType()); +} + +QPID_AUTO_TEST_CASE(testList) +{ + const std::string s("abc"); + const float f(9.876f); + const int16_t x(1000); + + Variant value = Variant::List(); + value.asList().push_back(Variant(s)); + value.asList().push_back(Variant(f)); + value.asList().push_back(Variant(x)); + BOOST_CHECK_EQUAL(3u, value.asList().size()); + Variant::List::const_iterator i = value.asList().begin(); + + BOOST_CHECK(i != value.asList().end()); + BOOST_CHECK_EQUAL(VAR_STRING, i->getType()); + BOOST_CHECK_EQUAL(s, i->asString()); + i++; + + BOOST_CHECK(i != value.asList().end()); + BOOST_CHECK_EQUAL(VAR_FLOAT, i->getType()); + BOOST_CHECK_EQUAL(f, i->asFloat()); + i++; + + BOOST_CHECK(i != value.asList().end()); + BOOST_CHECK_EQUAL(VAR_INT16, i->getType()); + BOOST_CHECK_EQUAL(x, i->asInt16()); + i++; + + BOOST_CHECK(i == value.asList().end()); +} + +QPID_AUTO_TEST_CASE(testMap) +{ + const std::string red("red"); + const float pi(3.14f); + const int16_t x(1000); + + Variant value = Variant::Map(); + value.asMap()["colour"] = red; + value.asMap()["pi"] = pi; + value.asMap()["my-key"] = x; + BOOST_CHECK_EQUAL(3u, value.asMap().size()); + + BOOST_CHECK_EQUAL(VAR_STRING, value.asMap()["colour"].getType()); + BOOST_CHECK_EQUAL(red, value.asMap()["colour"].asString()); + + BOOST_CHECK_EQUAL(VAR_FLOAT, value.asMap()["pi"].getType()); + BOOST_CHECK_EQUAL(pi, value.asMap()["pi"].asFloat()); + + BOOST_CHECK_EQUAL(VAR_INT16, value.asMap()["my-key"].getType()); + BOOST_CHECK_EQUAL(x, value.asMap()["my-key"].asInt16()); + + value.asMap()["my-key"] = "now it's a string"; + BOOST_CHECK_EQUAL(VAR_STRING, value.asMap()["my-key"].getType()); + BOOST_CHECK_EQUAL(std::string("now it's a string"), value.asMap()["my-key"].asString()); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/cli_tests.py b/qpid/cpp/src/tests/cli_tests.py index 2af1a20a87..4309b66271 100755 --- a/qpid/cpp/src/tests/cli_tests.py +++ b/qpid/cpp/src/tests/cli_tests.py @@ -123,6 +123,28 @@ class CliTests(TestBase010): found = True self.assertEqual(found, False) + def test_qpid_config_altex(self): + self.startQmf(); + qmf = self.qmf + exName = "testalt" + altName = "amq.direct" + + ret = os.system(self.command(" add exchange topic %s --alternate-exchange=%s" % (exName, altName))) + self.assertEqual(ret, 0) + + exchanges = qmf.getObjects(_class="exchange") + found = False + for exchange in exchanges: + if exchange.name == altName: + self.assertEqual(exchange.altExchange, None) + + if exchange.name == exName: + found = True + if not exchange.altExchange: + self.fail("Alternate exchange not set") + self.assertEqual(exchange._altExchange_.name, altName) + self.assertEqual(found, True) + def test_qpid_route(self): self.startQmf(); qmf = self.qmf diff --git a/qpid/cpp/src/tests/cluster_python_tests_failing.txt b/qpid/cpp/src/tests/cluster_python_tests_failing.txt index 337fb4a0f2..53b609942d 100644 --- a/qpid/cpp/src/tests/cluster_python_tests_failing.txt +++ b/qpid/cpp/src/tests/cluster_python_tests_failing.txt @@ -1,4 +1,5 @@ tests_0-10.management.ManagementTest.test_purge_queue +tests_0-10.management.ManagementTest.test_connection_close tests_0-10.dtx.DtxTests.test_bad_resume tests_0-10.dtx.DtxTests.test_commit_unknown tests_0-10.dtx.DtxTests.test_end diff --git a/qpid/cpp/src/tests/federation.py b/qpid/cpp/src/tests/federation.py index daf831f3ed..aa68e8198b 100755 --- a/qpid/cpp/src/tests/federation.py +++ b/qpid/cpp/src/tests/federation.py @@ -32,6 +32,17 @@ class FederationTests(TestBase010): def remote_port(self): return int(self.defines["remote-port"]) + def verify_cleanup(self): + attempts = 0 + total = len(self.qmf.getObjects(_class="bridge")) + len(self.qmf.getObjects(_class="link")) + while total > 0: + attempts += 1 + if attempts >= 10: + self.fail("Bridges and links didn't clean up") + return + sleep(1) + total = len(self.qmf.getObjects(_class="bridge")) + len(self.qmf.getObjects(_class="link")) + def test_bridge_create_and_close(self): self.startQmf(); qmf = self.qmf @@ -51,9 +62,7 @@ class FederationTests(TestBase010): result = link.close() self.assertEqual(result.status, 0) - sleep(3) - self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) - self.assertEqual(len(qmf.getObjects(_class="link")), 0) + self.verify_cleanup() def test_pull_from_exchange(self): session = self.session @@ -98,9 +107,7 @@ class FederationTests(TestBase010): result = link.close() self.assertEqual(result.status, 0) - sleep(3) - self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) - self.assertEqual(len(qmf.getObjects(_class="link")), 0) + self.verify_cleanup() def test_push_to_exchange(self): session = self.session @@ -144,9 +151,7 @@ class FederationTests(TestBase010): result = link.close() self.assertEqual(result.status, 0) - sleep(3) - self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) - self.assertEqual(len(qmf.getObjects(_class="link")), 0) + self.verify_cleanup() def test_pull_from_queue(self): session = self.session @@ -199,9 +204,7 @@ class FederationTests(TestBase010): result = link.close() self.assertEqual(result.status, 0) - sleep(3) - self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) - self.assertEqual(len(qmf.getObjects(_class="link")), 0) + self.verify_cleanup() def test_tracing_automatic(self): remoteUrl = "%s:%d" % (self.remote_host(), self.remote_port()) @@ -312,9 +315,7 @@ class FederationTests(TestBase010): result = link.close() self.assertEqual(result.status, 0) - sleep(3) - self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) - self.assertEqual(len(qmf.getObjects(_class="link")), 0) + self.verify_cleanup() def test_dynamic_fanout(self): session = self.session @@ -358,9 +359,7 @@ class FederationTests(TestBase010): result = link.close() self.assertEqual(result.status, 0) - sleep(3) - self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) - self.assertEqual(len(qmf.getObjects(_class="link")), 0) + self.verify_cleanup() def test_dynamic_direct(self): @@ -405,10 +404,7 @@ class FederationTests(TestBase010): result = link.close() self.assertEqual(result.status, 0) - sleep(3) - self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) - self.assertEqual(len(qmf.getObjects(_class="link")), 0) - + self.verify_cleanup() def test_dynamic_topic(self): session = self.session @@ -452,9 +448,7 @@ class FederationTests(TestBase010): result = link.close() self.assertEqual(result.status, 0) - sleep(3) - self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) - self.assertEqual(len(qmf.getObjects(_class="link")), 0) + self.verify_cleanup() def test_dynamic_topic_reorigin(self): session = self.session @@ -509,14 +503,24 @@ class FederationTests(TestBase010): self.assertEqual(result.status, 0) result = bridge2.close() self.assertEqual(result.status, 0) - result = link.close() - self.assertEqual(result.status, 0) - sleep(3) - self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) - self.assertEqual(len(qmf.getObjects(_class="link")), 0) + # extra check: verify we don't leak bridge objects - keep the link + # around and verify the bridge count has gone to zero + + attempts = 0 + bridgeCount = len(qmf.getObjects(_class="bridge")) + while bridgeCount > 0: + attempts += 1 + if attempts >= 5: + self.fail("Bridges didn't clean up") + return + sleep(1) + bridgeCount = len(qmf.getObjects(_class="bridge")) + result = link.close() + self.assertEqual(result.status, 0) + self.verify_cleanup() def test_dynamic_direct_reorigin(self): session = self.session @@ -569,16 +573,17 @@ class FederationTests(TestBase010): result = bridge.close() self.assertEqual(result.status, 0) - result = bridge2.close() - self.assertEqual(result.status, 0) + + # Extra test: don't explicitly close() bridge2. When the link is closed, + # it should clean up bridge2 automagically. verify_cleanup() will detect + # if bridge2 isn't cleaned up and will fail the test. + # + #result = bridge2.close() + #self.assertEqual(result.status, 0) result = link.close() self.assertEqual(result.status, 0) - sleep(3) - self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) - self.assertEqual(len(qmf.getObjects(_class="link")), 0) - - + self.verify_cleanup() def getProperty(self, msg, name): for h in msg.headers: diff --git a/qpid/cpp/src/tests/qpid_stream.cpp b/qpid/cpp/src/tests/qpid_stream.cpp new file mode 100644 index 0000000000..8e02baa8a0 --- /dev/null +++ b/qpid/cpp/src/tests/qpid_stream.cpp @@ -0,0 +1,163 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace qpid::messaging; +using namespace qpid::sys; + +struct Args : public qpid::Options +{ + std::string url; + std::string address; + uint rate; + bool durable; + + Args() : url("amqp:tcp:127.0.0.1:5672"), address("test-queue"), rate(1000), durable(false) + { + addOptions() + ("url", qpid::optValue(url, "URL"), "Url to connect to.") + ("address", qpid::optValue(address, "ADDRESS"), "Address to stream messages through.") + ("rate", qpid::optValue(rate, "msgs/sec"), "Rate at which to stream messages.") + ("durable", qpid::optValue(durable, "true|false"), "Mark messages as durable."); + } +}; + +Args opts; + +const std::string TIMESTAMP = "ts"; + +uint64_t timestamp(const AbsTime& time) +{ + Duration t(time); + return t; +} + +struct Client : Runnable +{ + virtual ~Client() {} + virtual void doWork(Session&) = 0; + + void run() + { + try { + Connection connection = Connection::open(opts.url); + Session session = connection.newSession(); + doWork(session); + session.close(); + connection.close(); + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + } + + Thread thread; + + void start() { thread = Thread(this); } + void join() { thread.join(); } +}; + +struct Publish : Client +{ + void doWork(Session& session) + { + Sender sender = session.createSender(opts.address); + Message msg; + uint64_t interval = TIME_SEC / opts.rate; + uint64_t sent = 0, missedRate = 0; + AbsTime start = now(); + while (true) { + AbsTime sentAt = now(); + msg.getHeaders()[TIMESTAMP] = timestamp(sentAt); + sender.send(msg); + ++sent; + AbsTime waitTill(start, sent*interval); + Duration delay(sentAt, waitTill); + if (delay < 0) { + ++missedRate; + } else { + qpid::sys::usleep(delay / TIME_USEC); + } + } + } +}; + +struct Consume : Client +{ + void doWork(Session& session) + { + Message msg; + uint64_t received = 0; + double minLatency = std::numeric_limits::max(); + double maxLatency = 0; + double totalLatency = 0; + Receiver receiver = session.createReceiver(opts.address); + while (receiver.fetch(msg)) { + session.acknowledge();//TODO: add batching option + ++received; + //calculate latency + uint64_t receivedAt = timestamp(now()); + uint64_t sentAt = msg.getHeaders()[TIMESTAMP].asUint64(); + double latency = ((double) (receivedAt - sentAt)) / TIME_MSEC; + + //update avg, min & max + minLatency = std::min(minLatency, latency); + maxLatency = std::max(maxLatency, latency); + totalLatency += latency; + + if (received % opts.rate == 0) { + std::cout << "count=" << received + << ", avg=" << (totalLatency/received) + << ", min=" << minLatency + << ", max=" << maxLatency << std::endl; + } + } + } +}; + +int main(int argc, char** argv) +{ + try { + opts.parse(argc, argv); + Publish publish; + Consume consume; + publish.start(); + consume.start(); + consume.join(); + publish.join(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} + + diff --git a/qpid/cpp/src/tests/txtest.cpp b/qpid/cpp/src/tests/txtest.cpp index c1ee246e2c..f604df7e21 100644 --- a/qpid/cpp/src/tests/txtest.cpp +++ b/qpid/cpp/src/tests/txtest.cpp @@ -33,7 +33,7 @@ #include "qpid/client/SubscriptionManager.h" #include "qpid/framing/Array.h" #include "qpid/framing/Buffer.h" -#include "qpid/sys/uuid.h" +#include "qpid/framing/Uuid.h" #include "qpid/sys/Thread.h" using namespace qpid; @@ -130,8 +130,6 @@ struct Transfer : public Client, public Runnable std::string src; std::string dest; Thread thread; - uuid_t uuid; - char uuidStr[37]; // Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + trailing \0 framing::Xid xid; Transfer(const std::string& to, const std::string& from) : src(to), dest(from), xid(0x4c414e47, "", from) {} @@ -184,9 +182,8 @@ struct Transfer : public Client, public Runnable } void setNewXid(framing::Xid& xid) { - ::uuid_generate(uuid); - ::uuid_unparse(uuid, uuidStr); - xid.setGlobalId(uuidStr); + framing::Uuid uuid(true); + xid.setGlobalId(uuid.str()); } }; diff --git a/qpid/java/broker/bin/qpid-server b/qpid/java/broker/bin/qpid-server index e5a9e998e2..738fc6e084 100755 --- a/qpid/java/broker/bin/qpid-server +++ b/qpid/java/broker/bin/qpid-server @@ -26,14 +26,19 @@ fi # Set classpath to include Qpid jar with all required jars in manifest QPID_LIBS=$QPID_HOME/lib/qpid-all.jar:$QPID_HOME/lib/bdbstore-launch.jar +# Default Log4j to append to its log file +if [ -z "$QPID_LOG_APPEND" ]; then + export QPID_LOG_APPEND="true" +fi + # Set other variables used by the qpid-run script before calling export JAVA=java \ JAVA_VM=-server \ JAVA_MEM=-Xmx1024m \ JAVA_GC="-XX:+UseConcMarkSweepGC -XX:+HeapDumpOnOutOfMemoryError" \ QPID_CLASSPATH=$QPID_LIBS \ - QPID_RUN_LOG=2 + QPID_RUN_LOG=2 -QPID_OPTS="$QPID_OPTS -Damqj.read_write_pool_size=32" +QPID_OPTS="$QPID_OPTS -Damqj.read_write_pool_size=32 -DQPID_LOG_APPEND=$QPID_LOG_APPEND" . qpid-run org.apache.qpid.server.Main "$@" diff --git a/qpid/java/broker/etc/log4j.xml b/qpid/java/broker/etc/log4j.xml index a395d0fd56..8ca43ededd 100644 --- a/qpid/java/broker/etc/log4j.xml +++ b/qpid/java/broker/etc/log4j.xml @@ -50,7 +50,7 @@ - + diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java index fc667db17b..c5f5cd05e1 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java @@ -204,11 +204,21 @@ public class HeadersExchange extends AbstractExchange for (int i = 0; i < bindings.length; i++) { String[] keyAndValue = bindings[i].split("="); - if (keyAndValue == null || keyAndValue.length < 2) + if (keyAndValue == null || keyAndValue.length == 0 || keyAndValue.length > 2) { throw new JMException("Format for headers binding should be \"=,=\" "); } - bindingMap.setString(keyAndValue[0], keyAndValue[1]); + + if(keyAndValue.length ==1) + { + //no value was given, only a key. Use an empty value + //to signal match on key presence alone + bindingMap.setString(keyAndValue[0], ""); + } + else + { + bindingMap.setString(keyAndValue[0], keyAndValue[1]); + } } _bindings.add(new Registration(new HeadersBinding(bindingMap), queue)); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/StartupRootMessageLogger.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/StartupRootMessageLogger.java index 0dffde50fa..bfb122985b 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/StartupRootMessageLogger.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/StartupRootMessageLogger.java @@ -39,4 +39,11 @@ public class StartupRootMessageLogger extends RootMessageLoggerImpl return true; } + @Override + public boolean isMessageEnabled(LogActor actor) + { + return true; + } + + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java index 42b3b05ac5..aea9ab43ea 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java @@ -379,6 +379,8 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry try { _cs.stop(); + CurrentActor.get().message(ManagementConsoleMessages.MNG_1003("RMI Registry", _cs.getAddress().getPort() - PORT_EXPORT_OFFSET)); + CurrentActor.get().message(ManagementConsoleMessages.MNG_1003("RMI ConnectorServer", _cs.getAddress().getPort())); } catch (IOException e) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicACLPlugin.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicACLPlugin.java index f7e537b02b..a6fae053c2 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicACLPlugin.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicACLPlugin.java @@ -120,10 +120,4 @@ public abstract class BasicACLPlugin implements ACLPlugin // no-op } - public boolean supportsTag(String name) - { - // This plugin doesn't support any tags - return false; - } - } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/network/FirewallFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/network/FirewallFactory.java deleted file mode 100644 index a1a399e5bf..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/network/FirewallFactory.java +++ /dev/null @@ -1,45 +0,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.server.security.access.plugins.network; - -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.ConfigurationException; -import org.apache.qpid.server.security.access.ACLPlugin; -import org.apache.qpid.server.security.access.ACLPluginFactory; - -public class FirewallFactory implements ACLPluginFactory -{ - - @Override - public ACLPlugin newInstance(Configuration config) throws ConfigurationException - { - FirewallPlugin plugin = new FirewallPlugin(); - plugin.setConfiguration(config); - return plugin; - } - - @Override - public boolean supportsTag(String name) - { - return name.equals("firewall"); - } - -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java index b34ef1c382..72d6afc65c 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java @@ -599,7 +599,6 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage if(_state.compareAndSet(State.SUSPENDED, State.ACTIVE)) { _stateListener.stateChange(this, State.SUSPENDED, State.ACTIVE); - CurrentActor.get().message(_logSubject,SubscriptionMessages.SUB_1003(_state.get().toString())); } else { @@ -612,9 +611,9 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage if(_state.compareAndSet(State.ACTIVE, State.SUSPENDED)) { _stateListener.stateChange(this, State.ACTIVE, State.SUSPENDED); - CurrentActor.get().message(_logSubject,SubscriptionMessages.SUB_1003(_state.get().toString())); } } + CurrentActor.get().message(_logSubject,SubscriptionMessages.SUB_1003(_state.get().toString())); } public State getState() diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/ExchangeDenier.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/ExchangeDenier.java index f62b0c6241..317dee2b47 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/ExchangeDenier.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/ExchangeDenier.java @@ -46,17 +46,4 @@ public class ExchangeDenier extends AllowAll { return AuthzResult.DENIED; } - - @Override - public String getPluginName() - { - return getClass().getSimpleName(); - } - - @Override - public boolean supportsTag(String name) - { - return name.equals("exchangeDenier"); - } - } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java index 05ac3dca9e..6bae0166d1 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java @@ -39,9 +39,4 @@ public class AMQAuthenticationException extends AMQException { super(error, msg, cause); } - public boolean isHardError() - { - return true; - } - } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java index 118be75705..2e3e417c95 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java @@ -65,6 +65,7 @@ import org.apache.qpid.AMQDisconnectedException; import org.apache.qpid.AMQException; import org.apache.qpid.AMQInvalidArgumentException; import org.apache.qpid.AMQInvalidRoutingKeyException; +import org.apache.qpid.AMQChannelClosedException; import org.apache.qpid.client.failover.FailoverException; import org.apache.qpid.client.failover.FailoverNoopSupport; import org.apache.qpid.client.failover.FailoverProtectedOperation; @@ -205,9 +206,9 @@ public abstract class AMQSession @@ -86,7 +87,7 @@ public class AMQStateManager implements AMQMethodListener return _currentState; } - public void changeState(AMQState newState) throws AMQException + public void changeState(AMQState newState) { _logger.debug("State changing to " + newState + " from old state " + _currentState); @@ -136,6 +137,22 @@ public class AMQStateManager implements AMQMethodListener */ public void error(Exception error) { + if (error instanceof AMQException) + { + // AMQException should be being notified before closing the + // ProtocolSession. Which will change the State to CLOSED. + // if we have a hard error. + if (((AMQException)error).isHardError()) + { + changeState(AMQState.CONNECTION_CLOSING); + } + } + else + { + // Be on the safe side here and mark the connection closed + changeState(AMQState.CONNECTION_CLOSED); + } + if (_waiters.size() == 0) { _logger.error("No Waiters for error saving as last error:" + error.getMessage()); diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java b/qpid/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java index bddbc329ab..ee7fc533a3 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java @@ -22,10 +22,11 @@ package org.apache.qpid.client.util; import java.util.Iterator; import java.util.Queue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ConcurrentLinkedQueue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * A blocking queue that emits events above a user specified threshold allowing the caller to take action (e.g. flow * control) to try to prevent the queue growing (much) further. The underlying queue itself is not bounded therefore the @@ -36,6 +37,8 @@ import java.util.concurrent.ConcurrentLinkedQueue; */ public class FlowControllingBlockingQueue { + private static final Logger _logger = LoggerFactory.getLogger(FlowControllingBlockingQueue.class); + /** This queue is bounded and is used to store messages before being dispatched to the consumer */ private final Queue _queue = new ConcurrentLinkedQueue(); @@ -46,6 +49,8 @@ public class FlowControllingBlockingQueue /** We require a separate count so we can track whether we have reached the threshold */ private int _count; + + private boolean disableFlowControl; public boolean isEmpty() { @@ -69,6 +74,10 @@ public class FlowControllingBlockingQueue _flowControlHighThreshold = highThreshold; _flowControlLowThreshold = lowThreshold; _listener = listener; + if (highThreshold == 0) + { + disableFlowControl = true; + } } public Object take() throws InterruptedException @@ -84,7 +93,7 @@ public class FlowControllingBlockingQueue } } } - if (_listener != null) + if (!disableFlowControl && _listener != null) { synchronized (_listener) { @@ -93,6 +102,7 @@ public class FlowControllingBlockingQueue _listener.underThreshold(_count); } } + } return o; @@ -106,7 +116,7 @@ public class FlowControllingBlockingQueue notifyAll(); } - if (_listener != null) + if (!disableFlowControl && _listener != null) { synchronized (_listener) { diff --git a/qpid/java/client/src/test/java/org/apache/qpid/client/MockAMQConnection.java b/qpid/java/client/src/test/java/org/apache/qpid/client/MockAMQConnection.java index ce79080e97..da44822ec3 100644 --- a/qpid/java/client/src/test/java/org/apache/qpid/client/MockAMQConnection.java +++ b/qpid/java/client/src/test/java/org/apache/qpid/client/MockAMQConnection.java @@ -85,7 +85,7 @@ public class MockAMQConnection extends AMQConnection } @Override - public ProtocolVersion makeBrokerConnection(BrokerDetails brokerDetail) throws IOException, AMQException + public ProtocolVersion makeBrokerConnection(BrokerDetails brokerDetail) throws IOException { _connected = true; _protocolHandler.getStateManager().changeState(AMQState.CONNECTION_OPEN); diff --git a/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/AMQProtocolHandlerTest.java b/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/AMQProtocolHandlerTest.java index 10ec220d9e..fc7f8148f0 100644 --- a/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/AMQProtocolHandlerTest.java +++ b/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/AMQProtocolHandlerTest.java @@ -200,15 +200,8 @@ public class AMQProtocolHandlerTest extends TestCase _handler.getStateManager().error(trigger); _logger.info("Setting state to be CONNECTION_CLOSED."); - try - { - _handler.getStateManager().changeState(AMQState.CONNECTION_CLOSED); - } - catch (AMQException e) - { - _logger.error("Unable to change the state to closed.", e); - fail("Unable to change the state to closed due to :"+e.getMessage()); - } + + _handler.getStateManager().changeState(AMQState.CONNECTION_CLOSED); _logger.info("Firing exception"); _handler.propagateExceptionToFrameListeners(trigger); diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java index 8df3644929..e34103a944 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java @@ -30,7 +30,6 @@ import java.nio.ByteBuffer; import javax.net.ssl.SSLEngine; -import org.apache.log4j.Logger; import org.apache.mina.common.ConnectFuture; import org.apache.mina.common.IdleStatus; import org.apache.mina.common.IoAcceptor; @@ -59,6 +58,9 @@ import org.apache.qpid.transport.NetworkDriver; import org.apache.qpid.transport.NetworkDriverConfiguration; import org.apache.qpid.transport.OpenException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver { @@ -80,7 +82,7 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver private WriteFuture _lastWriteFuture; - private static final Logger _logger = Logger.getLogger(MINANetworkDriver.class); + private static final Logger _logger = LoggerFactory.getLogger(MINANetworkDriver.class); public MINANetworkDriver(boolean useNIO, int processors, boolean executorPool, boolean protectIO) { diff --git a/qpid/java/doc/broker-priority-queue-subscription.dia b/qpid/java/doc/broker-priority-queue-subscription.dia new file mode 100644 index 0000000000..2289899435 Binary files /dev/null and b/qpid/java/doc/broker-priority-queue-subscription.dia differ diff --git a/qpid/java/doc/broker-queue-subscription.dia b/qpid/java/doc/broker-queue-subscription.dia new file mode 100644 index 0000000000..d146ad136d Binary files /dev/null and b/qpid/java/doc/broker-queue-subscription.dia differ diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientListener.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientListener.java index c3348b32f0..d88e0f38bb 100644 --- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientListener.java +++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientListener.java @@ -59,7 +59,12 @@ public class ClientListener implements NotificationListener else if (JMXConnectionNotification.FAILED.equals(type)) { ApplicationRegistry.serverConnectionClosed(server); - MBeanUtility.printOutput("Recieved notification from " + server.getName() + ": " + type ); + MBeanUtility.printOutput("JMX Connection to " + server.getName() + " failed."); + } + else if (JMXConnectionNotification.CLOSED.equals(type)) + { + ApplicationRegistry.serverConnectionClosed(server); + MBeanUtility.printOutput("JMX Connection to " + server.getName() + " was closed."); } } diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXServerRegistry.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXServerRegistry.java index 2b459c858f..ca07e6acf4 100644 --- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXServerRegistry.java +++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXServerRegistry.java @@ -22,6 +22,7 @@ package org.apache.qpid.management.ui.jmx; import static org.apache.qpid.management.ui.Constants.ALL; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -29,7 +30,6 @@ import java.util.HashMap; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import javax.management.ListenerNotFoundException; import javax.management.MBeanInfo; import javax.management.MBeanServerConnection; import javax.management.Notification; @@ -116,25 +116,54 @@ public class JMXServerRegistry extends ServerRegistry * removes all listeners from the mbean server. This is required when user * disconnects the Qpid server connection */ - public void closeServerConnection() throws Exception + public void closeServerConnection() throws IOException { try { + //remove the listener from the JMXConnector if (_jmxc != null && _clientListener != null) + { _jmxc.removeConnectionNotificationListener(_clientListener); + } + } + catch (Exception e) + { + //ignore + } + try + { + //remove the listener from the MBeanServerDelegate MBean if (_mbsc != null && _clientListener != null) + { _mbsc.removeNotificationListener(_serverObjectName, _clientListener); + } + } + catch (Exception e) + { + //ignore + } - // remove mbean notification listeners + if (_mbsc != null && _clientListener != null) + { + //remove any listeners from the Qpid MBeans for (String mbeanName : _subscribedNotificationMap.keySet()) { - _mbsc.removeNotificationListener(new ObjectName(mbeanName), _notificationListener); + try + { + _mbsc.removeNotificationListener(new ObjectName(mbeanName), _notificationListener); + } + catch (Exception e) + { + //ignore + } } } - catch (ListenerNotFoundException ex) + + //close the JMXConnector + if (_jmxc != null) { - MBeanUtility.printOutput(ex.toString()); + _jmxc.close(); } } diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/AttributesTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/AttributesTabControl.java index 2408faae3a..f21647b2d2 100644 --- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/AttributesTabControl.java +++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/AttributesTabControl.java @@ -882,7 +882,7 @@ public class AttributesTabControl extends TabControl { attribute = (AttributeData) element; if (attribute.isWritable()) - return Display.getCurrent().getSystemColor(SWT.COLOR_DARK_BLUE); + return Display.getCurrent().getSystemColor(SWT.COLOR_BLUE); else return Display.getCurrent().getSystemColor(SWT.COLOR_BLACK); } diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java index 0d290ab1c4..056d365f8e 100644 --- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java +++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java @@ -790,9 +790,11 @@ public class NavigationView extends ViewPart return; } - serverRegistry.closeServerConnection(); // Add server to the closed server list and the worker thread will remove the server from required places. ApplicationRegistry.serverConnectionClosed(managedServer); + + //close the connection + serverRegistry.closeServerConnection(); } /** diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/connection/ConnectionOperationsTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/connection/ConnectionOperationsTabControl.java index e981ec1c3c..f82d37dcd1 100644 --- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/connection/ConnectionOperationsTabControl.java +++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/connection/ConnectionOperationsTabControl.java @@ -28,10 +28,13 @@ import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeDataSupport; import javax.management.openmbean.TabularDataSupport; +import org.apache.qpid.management.ui.ApplicationRegistry; import org.apache.qpid.management.ui.ManagedBean; +import org.apache.qpid.management.ui.ServerRegistry; import org.apache.qpid.management.common.mbeans.ManagedConnection; import org.apache.qpid.management.ui.jmx.JMXManagedObject; import org.apache.qpid.management.ui.jmx.MBeanUtility; +import org.apache.qpid.management.ui.views.MBeanView; import org.apache.qpid.management.ui.views.TabControl; import org.apache.qpid.management.ui.views.ViewUtility; import org.eclipse.jface.viewers.ISelectionChangedListener; @@ -43,6 +46,8 @@ import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Image; @@ -55,6 +60,8 @@ import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; import org.eclipse.ui.forms.widgets.Form; import org.eclipse.ui.forms.widgets.FormToolkit; @@ -199,6 +206,8 @@ public class ConnectionOperationsTabControl extends TabControl _tableViewer.setContentProvider(new ContentProviderImpl()); _tableViewer.setLabelProvider(new LabelProviderImpl()); _tableViewer.setSorter(tableSorter); + _table.setSortColumn(_table.getColumn(0)); + _table.setSortDirection(SWT.UP); Composite buttonsComposite = _toolkit.createComposite(viewChannelsGroup); gridData = new GridData(SWT.RIGHT, SWT.BOTTOM, false, false); @@ -278,6 +287,19 @@ public class ConnectionOperationsTabControl extends TabControl } }); + //listener for double clicking to open the selection mbean + _table.addMouseListener(new MouseListener() + { + // MouseListener implementation + public void mouseDoubleClick(MouseEvent event) + { + openMBean(_table); + } + + public void mouseDown(MouseEvent e){} + public void mouseUp(MouseEvent e){} + }); + _tableViewer.addSelectionChangedListener(new ISelectionChangedListener(){ public void selectionChanged(SelectionChangedEvent evt) { @@ -465,5 +487,43 @@ public class ConnectionOperationsTabControl extends TabControl return comparison; } } + + private void openMBean(Table table) + { + int selectionIndex = table.getSelectionIndex(); + + if (selectionIndex == -1) + { + return; + } + + CompositeData channelResult = (CompositeData) table.getItem(selectionIndex).getData(); + String queueName = (String) channelResult.get(DEFAULT_QUEUE); + + if(queueName == null) + { + return; + } + + ServerRegistry serverRegistry = ApplicationRegistry.getServerRegistry(_mbean); + ManagedBean selectedMBean = serverRegistry.getQueue(queueName, _mbean.getVirtualHostName()); + + if(selectedMBean == null) + { + ViewUtility.popupErrorMessage("Error", "Unable to retrieve the selected MBean to open it"); + return; + } + + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + MBeanView view = (MBeanView) window.getActivePage().findView(MBeanView.ID); + try + { + view.openMBean(selectedMBean); + } + catch (Exception ex) + { + MBeanUtility.handleException(selectedMBean, ex); + } + } } diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/exchange/ExchangeOperationsTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/exchange/ExchangeOperationsTabControl.java index 35b3e8150c..e3dea6e96b 100644 --- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/exchange/ExchangeOperationsTabControl.java +++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/exchange/ExchangeOperationsTabControl.java @@ -237,6 +237,8 @@ public class ExchangeOperationsTabControl extends TabControl _keysTableViewer.setContentProvider(new ContentProviderImpl(BINDING_KEY)); _keysTableViewer.setLabelProvider(new LabelProviderImpl(BINDING_KEY)); _keysTableViewer.setSorter(tableSorter); + _keysTable.setSortColumn(_keysTable.getColumn(0)); + _keysTable.setSortDirection(SWT.UP); _queuesTable = new Table (tablesComposite, SWT.SINGLE | SWT.SCROLL_LINE | SWT.BORDER | SWT.FULL_SELECTION); @@ -287,6 +289,8 @@ public class ExchangeOperationsTabControl extends TabControl _queuesTableViewer.setContentProvider(new ContentProviderImpl(QUEUES)); _queuesTableViewer.setLabelProvider(new LabelProviderImpl(QUEUES)); _queuesTableViewer.setSorter(queuesTableSorter); + _queuesTable.setSortColumn(_queuesTable.getColumn(0)); + _queuesTable.setSortDirection(SWT.UP); _queuesTableViewer.setInput(new String[]{"Select a binding key to view queues"}); //listener for double clicking to open the selection mbean diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/exchange/HeadersExchangeOperationsTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/exchange/HeadersExchangeOperationsTabControl.java index 5146bab74c..fcce0e67b6 100644 --- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/exchange/HeadersExchangeOperationsTabControl.java +++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/exchange/HeadersExchangeOperationsTabControl.java @@ -22,6 +22,7 @@ package org.apache.qpid.management.ui.views.exchange; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; import javax.management.MBeanServerConnection; @@ -48,6 +49,7 @@ import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.SelectionAdapter; @@ -60,6 +62,7 @@ import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.Table; @@ -182,7 +185,7 @@ public class HeadersExchangeOperationsTabControl extends TabControl final TableSorter tableSorter = new TableSorter(BINDING_NUM); String[] titles = {"Binding Number", "Queue Name"}; - int[] bounds = {125, 175}; + int[] bounds = {135, 175}; for (int i = 0; i < titles.length; i++) { final int index = i; @@ -220,6 +223,8 @@ public class HeadersExchangeOperationsTabControl extends TabControl _bindingNumberTableViewer.setContentProvider(new ContentProviderImpl(BINDING_NUM)); _bindingNumberTableViewer.setLabelProvider(new LabelProviderImpl(BINDING_NUM)); _bindingNumberTableViewer.setSorter(tableSorter); + _bindingNumberTable.setSortColumn(_bindingNumberTable.getColumn(0)); + _bindingNumberTable.setSortDirection(SWT.UP); //table of header bindings _headersTable = new Table (tablesComposite, SWT.SINGLE | SWT.SCROLL_LINE | SWT.BORDER | SWT.FULL_SELECTION); @@ -272,6 +277,8 @@ public class HeadersExchangeOperationsTabControl extends TabControl _headersTableViewer.setContentProvider(new ContentProviderImpl(HEADER_BINDINGS)); _headersTableViewer.setLabelProvider(new LabelProviderImpl(HEADER_BINDINGS)); _headersTableViewer.setSorter(queuesTableSorter); + _headersTable.setSortColumn(_headersTable.getColumn(0)); + _headersTable.setSortDirection(SWT.UP); _headersTableViewer.setInput(new String[]{"Select a binding to view key-value pairs"}); _bindingNumberTableViewer.addSelectionChangedListener(new ISelectionChangedListener(){ @@ -490,25 +497,38 @@ public class HeadersExchangeOperationsTabControl extends TabControl private void createNewBinding(Shell parent) { final Shell shell = ViewUtility.createModalDialogShell(parent, "Create New Binding"); - - Composite destinationComposite = _toolkit.createComposite(shell, SWT.NONE); - destinationComposite.setBackground(shell.getBackground()); - destinationComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); - destinationComposite.setLayout(new GridLayout(2,false)); + + Composite queueNameComposite = _toolkit.createComposite(shell, SWT.NONE); + queueNameComposite.setBackground(shell.getBackground()); + GridData layoutData = new GridData(SWT.CENTER, SWT.TOP, true, false); + layoutData.minimumWidth = 300; + queueNameComposite.setLayoutData(layoutData); + queueNameComposite.setLayout(new GridLayout(2,false)); - _toolkit.createLabel(destinationComposite,"Queue:").setBackground(shell.getBackground()); - final Combo destinationCombo = new Combo(destinationComposite,SWT.NONE | SWT.READ_ONLY); + _toolkit.createLabel(queueNameComposite,"Queue:").setBackground(shell.getBackground()); + final Combo destinationCombo = new Combo(queueNameComposite,SWT.NONE | SWT.READ_ONLY); destinationCombo.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); - Composite bindingComposite = _toolkit.createComposite(shell, SWT.NONE); - bindingComposite.setBackground(shell.getBackground()); - bindingComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); - bindingComposite.setLayout(new GridLayout(2,false)); + final ScrolledComposite scrolledComposite = new ScrolledComposite(shell, SWT.V_SCROLL); + scrolledComposite.setExpandHorizontal(true); + scrolledComposite.setLayout(new GridLayout()); + scrolledComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + scrolledComposite.setBackground(shell.getBackground()); + + final Composite bindingComposite = _toolkit.createComposite(scrolledComposite, SWT.NONE); + bindingComposite.setBackground(scrolledComposite.getBackground()); + bindingComposite.setLayout(new GridLayout(2,true)); + layoutData = new GridData(SWT.FILL, SWT.TOP, true, false); + bindingComposite.setLayoutData(layoutData); + scrolledComposite.setContent(bindingComposite); + + Composite addMoreButtonComp = _toolkit.createComposite(shell); + addMoreButtonComp.setBackground(shell.getBackground()); + addMoreButtonComp.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, true, true)); + addMoreButtonComp.setLayout(new GridLayout()); + + final Button addMoreButton = _toolkit.createButton(addMoreButtonComp, "Add additional field", SWT.PUSH); - _toolkit.createLabel(bindingComposite,"Binding:").setBackground(shell.getBackground()); - final Text bindingText = new Text(bindingComposite, SWT.BORDER); - bindingText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); - Composite okCancelButtonsComp = _toolkit.createComposite(shell); okCancelButtonsComp.setBackground(shell.getBackground()); okCancelButtonsComp.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, true, true)); @@ -532,26 +552,106 @@ public class HeadersExchangeOperationsTabControl extends TabControl destinationCombo.setItems(queueList.toArray(new String[0])); } destinationCombo.select(0); + + final HashMap headerBindingHashMap = new HashMap(); + + //add headings + Label keyLabel = _toolkit.createLabel(bindingComposite,"Key:"); + keyLabel.setBackground(bindingComposite.getBackground()); + keyLabel.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false)); + + Label valueLabel = _toolkit.createLabel(bindingComposite,"Value:"); + valueLabel.setBackground(bindingComposite.getBackground()); + valueLabel.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false)); + + //add the x-match key by default and offer a comobo to select its value + final Text xmatchKeyText = new Text(bindingComposite, SWT.BORDER); + xmatchKeyText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + xmatchKeyText.setText("x-match"); + xmatchKeyText.setEditable(false); + + final Combo xmatchValueCombo = new Combo(bindingComposite,SWT.NONE | SWT.READ_ONLY); + xmatchValueCombo.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + xmatchValueCombo.setItems(new String[]{"any", "all"}); + xmatchValueCombo.select(0); + + //make some empty key-value fields + for(int i=0; i < 4; i++) + { + Text keyText = new Text(bindingComposite, SWT.BORDER); + Text valueText = new Text(bindingComposite, SWT.BORDER); + keyText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + valueText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + + headerBindingHashMap.put(keyText, valueText); + } + bindingComposite.setSize(bindingComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT)); + + //allow adding more fields for additional key-value pairs + addMoreButton.addSelectionListener(new SelectionAdapter() + { + public void widgetSelected(SelectionEvent e) + { + Text keyText = new Text(bindingComposite, SWT.BORDER); + Text valueText = new Text(bindingComposite, SWT.BORDER); + keyText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + valueText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + + headerBindingHashMap.put(keyText, valueText); + + bindingComposite.setSize(bindingComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT)); + bindingComposite.layout(true); + scrolledComposite.layout(true); + } + }); okButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { - String binding = bindingText.getText(); + String xMatchString = xmatchValueCombo.getText(); - if (binding == null || binding.length() == 0) + String destQueue = destinationCombo.getItem(destinationCombo.getSelectionIndex()).toString(); + + StringBuffer bindingValue = new StringBuffer(); + + //insert the x-match key-value pair + if (xMatchString.equalsIgnoreCase("any")) { - ViewUtility.popupErrorMessage("Create New Binding", "Please enter a valid binding"); - return; + bindingValue.append("x-match=any"); + } + else + { + bindingValue.append("x-match=all"); } - String destQueue = destinationCombo.getItem(destinationCombo.getSelectionIndex()).toString(); + //insert the other key-value pairs + for (Text keyText : headerBindingHashMap.keySet()) + { + + String key = keyText.getText(); + if(key == null || key.length() == 0) + { + continue; + } + + Text valueText = headerBindingHashMap.get(keyText); + String value = valueText.getText(); + + bindingValue.append(","); + bindingValue.append(key + "="); + //empty values are permitted, signalling only key-presence is required + if(value != null && value.length() > 0) + { + bindingValue.append(value); + } + } shell.dispose(); try { - _emb.createNewBinding(destQueue, binding); + _emb.createNewBinding(destQueue, bindingValue.toString()); ViewUtility.operationResultFeedback(null, "Created new Binding", null); } catch (Exception e4) diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/logging/ConfigurationFileTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/logging/ConfigurationFileTabControl.java index 3e8a5da5b5..1b1d08aa67 100644 --- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/logging/ConfigurationFileTabControl.java +++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/logging/ConfigurationFileTabControl.java @@ -197,8 +197,8 @@ public class ConfigurationFileTabControl extends TabControl "NOTE: These options modify the configuration file. " + "Changes only take effect automatically if LogWatch is enabled."); Label noteLabel2 = _toolkit.createLabel(_headerComposite, - "A Logger set to a non-inherited Level in the Runtime tab " + - "will retain that value after the configuration is reloaded."); + "A child Logger set to a non-inherited Level in the Runtime tab " + + "will retain that value after the file is reloaded."); GridData gridData = new GridData(SWT.FILL, SWT.FILL, false, true); noteLabel.setLayoutData(gridData); gridData = new GridData(SWT.FILL, SWT.FILL, false, true); diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/queue/QueueOperationsTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/queue/QueueOperationsTabControl.java index c7c7a1791a..43d2cfe204 100644 --- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/queue/QueueOperationsTabControl.java +++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/queue/QueueOperationsTabControl.java @@ -299,8 +299,8 @@ public class QueueOperationsTabControl extends TabControl //message table Composite tableAndButtonsComposite = _toolkit.createComposite(messagesGroup); gridData = new GridData(SWT.FILL, SWT.FILL, true, true); - gridData.minimumHeight = 220; - gridData.heightHint = 220; + gridData.minimumHeight = 180; + gridData.heightHint = 180; tableAndButtonsComposite.setLayoutData(gridData); tableAndButtonsComposite.setLayout(new GridLayout(2,false)); @@ -358,6 +358,8 @@ public class QueueOperationsTabControl extends TabControl _tableViewer.setContentProvider(new ContentProviderImpl()); _tableViewer.setLabelProvider(new LabelProviderImpl()); _tableViewer.setSorter(tableSorter); + _table.setSortColumn(_table.getColumn(0)); + _table.setSortDirection(SWT.UP); //Side Buttons Composite buttonsComposite = _toolkit.createComposite(tableAndButtonsComposite); @@ -492,12 +494,12 @@ public class QueueOperationsTabControl extends TabControl headerEtcComposite.setLayoutData(gridData); headerEtcComposite.setLayout(new GridLayout()); - final Text headerText = new Text(headerEtcComposite, SWT.WRAP | SWT.BORDER ); + final Text headerText = new Text(headerEtcComposite, SWT.WRAP | SWT.BORDER | SWT.V_SCROLL); headerText.setText("Select a message to view its header."); headerText.setEditable(false); data = new GridData(SWT.LEFT, SWT.TOP, false, false); - data.minimumHeight = 230; - data.heightHint = 230; + data.minimumHeight = 210; + data.heightHint = 210; data.minimumWidth = 500; data.widthHint = 500; headerText.setLayoutData(data); @@ -570,10 +572,16 @@ public class QueueOperationsTabControl extends TabControl String[] msgHeader = (String[]) selectedMsg.get(MSG_HEADER); headerText.setText(""); String lineSeperator = System.getProperty("line.separator"); - for(String s: msgHeader) + int size = msgHeader.length; + for(int i=0; i < size; i++) { - headerText.append(s + lineSeperator); + headerText.append(msgHeader[i]); + if(!(i == size - 1)) + { + headerText.append(lineSeperator); + } } + headerText.setSelection(0); } if (_table.getSelectionCount() > 1) diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/vhost/VHostTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/vhost/VHostTabControl.java index fef1e9f887..3b03aeaff1 100644 --- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/vhost/VHostTabControl.java +++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/vhost/VHostTabControl.java @@ -205,6 +205,8 @@ public class VHostTabControl extends TabControl _queueTableViewer.setContentProvider(new ContentProviderImpl()); _queueTableViewer.setLabelProvider(new LabelProviderImpl()); _queueTableViewer.setSorter(tableSorter); + _queueTable.setSortColumn(_queueTable.getColumn(0)); + _queueTable.setSortDirection(SWT.UP); Composite queuesRightComposite = _toolkit.createComposite(queuesGroup); gridData = new GridData(SWT.FILL, SWT.FILL, false, true); @@ -314,6 +316,8 @@ public class VHostTabControl extends TabControl _exchangeTableViewer.setContentProvider(new ContentProviderImpl()); _exchangeTableViewer.setLabelProvider(new LabelProviderImpl()); _exchangeTableViewer.setSorter(exchangeTableSorter); + _exchangeTable.setSortColumn(_exchangeTable.getColumn(0)); + _exchangeTable.setSortDirection(SWT.UP); Composite exchangesRightComposite = _toolkit.createComposite(exchangesGroup); gridData = new GridData(SWT.FILL, SWT.FILL, false, true); diff --git a/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini b/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini index 9e3de042d5..312580769e 100644 --- a/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini +++ b/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini @@ -23,14 +23,14 @@ -XX:MaxPermSize=256m -Dosgi.requiredJavaVersion=1.5 -Declipse.consoleLog=true - -#=============================================== -# SSL trust store configuration options. -#=============================================== - -# Uncomment lines below to specify custom truststore for server SSL -# certificate verification, eg when using self-signed server certs. -# -#-Djavax.net.ssl.trustStore= -#-Djavax.net.ssl.trustStorePassword= - + +#=============================================== +# SSL trust store configuration options. +#=============================================== + +# Uncomment lines below to specify custom truststore for server SSL +# certificate verification, eg when using self-signed server certs. +# +#-Djavax.net.ssl.trustStore= +#-Djavax.net.ssl.trustStorePassword= + diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java index 7a2266902b..b4ba6e8156 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java @@ -29,6 +29,8 @@ import org.apache.qpid.server.logging.AbstractTestLogging; import org.apache.qpid.server.logging.subjects.AbstractTestLogSubject; import javax.jms.Connection; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; import javax.management.JMException; import javax.management.MBeanException; import javax.management.MBeanServerConnection; @@ -38,6 +40,8 @@ import javax.management.remote.JMXConnector; import java.io.IOException; import java.util.List; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * Test class to test if any change in the broker JMX code is affesting the management console @@ -161,6 +165,23 @@ public class ManagementActorLoggingTest extends AbstractTestLogging //Create a connection to the broker Connection connection = getConnection(); + // Monitor the connection for an exception being thrown + // this should be a DisconnectionException but it is not this tests + // job to valiate that. Only use the exception as a synchronisation + // to check the log file for the Close message + final CountDownLatch exceptionReceived = new CountDownLatch(1); + connection.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException e) + { + //Failover being attempted. + exceptionReceived.countDown(); + } + }); + + //Remove the connection close from any 0-10 connections + _monitor.reset(); + // Get all active AMQP connections AllObjects allObject = new AllObjects(_mbsc); allObject.querystring = "org.apache.qpid:type=VirtualHost.Connection,*"; @@ -175,16 +196,17 @@ public class ManagementActorLoggingTest extends AbstractTestLogging newProxyInstance(_mbsc, connectionName, ManagedConnection.class, false); - //Remove the connection close from any 0-10 connections - _monitor.reset(); //Close the connection mangedConnection.closeConnection(); + //Wait for the connection to close + assertTrue("Timed out waiting for conneciton to report close", + exceptionReceived.await(2, TimeUnit.SECONDS)); + //Validate results List results = _monitor.findMatches("CON-1002"); - assertEquals("Unexpected Connection Close count", 1, results.size()); } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java index d7209c5660..5dd56fb0f9 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java @@ -29,6 +29,7 @@ import javax.jms.MessageConsumer; import javax.jms.Queue; import javax.jms.Session; import javax.jms.Topic; +import javax.jms.Message; import java.io.IOException; import java.util.List; @@ -327,22 +328,45 @@ public class SubscriptionLoggingTest extends AbstractTestLogging int PREFETCH = 15; //Create new session with small prefetch - _session = ((AMQConnection) _connection).createSession(true, Session.AUTO_ACKNOWLEDGE, PREFETCH); + _session = ((AMQConnection) _connection).createSession(true, Session.SESSION_TRANSACTED, PREFETCH); MessageConsumer consumer = _session.createConsumer(_queue); _connection.start(); + //Start the dispatcher & Unflow the channel. + consumer.receiveNoWait(); + //Fill the prefetch and two extra so that our receive bellow allows the - // subscription to become active then return to a suspended state. - sendMessage(_session, _queue, 17); + // subscription to become active + // Previously we set this to 17 so that it would return to a suspended + // state. However, testing has shown that the state change can occur + // sufficiently quickly that logging does not occur consistently enough + // for testing. + int SEND_COUNT = 16; + sendMessage(_session, _queue, SEND_COUNT); _session.commit(); // Retreive the first message, and start the flow of messages - assertNotNull("First message not retreived", consumer.receive(1000)); + Message msg = consumer.receive(1000); + assertNotNull("First message not retreived", msg); _session.commit(); - - _connection.close(); + // Drain the queue to ensure there is time for the ACTIVE log message + // Check that we can received all the messages + int receivedCount = 0; + while (msg != null) + { + receivedCount++; + msg = consumer.receive(1000); + _session.commit(); + } + + //Validate we received all the messages + assertEquals("Not all sent messages received.", SEND_COUNT, receivedCount); + + // Fill the queue again to suspend the consumer + sendMessage(_session, _queue, SEND_COUNT); + _session.commit(); //Validate List results = _monitor.findMatches("SUB-1003"); @@ -350,15 +374,13 @@ public class SubscriptionLoggingTest extends AbstractTestLogging try { // Validation expects three messages. - // The first will be logged by the QueueActor as part of the processQueue thread -// INFO - MESSAGE [vh(/test)/qu(example.queue)] [sub:6(qu(example.queue))] SUB-1003 : State : SUSPENDED - // The second will be by the connnection as it acknowledges and activates the subscription -// INFO - MESSAGE [con:6(guest@anonymous(26562441)/test)/ch:3] [sub:6(qu(example.queue))] SUB-1003 : State : ACTIVE - // The final one can be the subscription suspending as part of the SubFlushRunner or the processQueue thread - // As a result validating the actor is more complicated and doesn't add anything. The goal of this test is - // to ensure the State is correct not that a particular Actor performs the logging. -// INFO - MESSAGE [sub:6(vh(test)/qu(example.queue))] [sub:6(qu(example.queue))] SUB-1003 : State : SUSPENDED -// INFO - MESSAGE [vh(/test)/qu(example.queue)] [sub:6(qu(example.queue))] SUB-1003 : State : SUSPENDED + // The Actor can be any one of the following depending on the exactly what is going on on the broker. + // Ideally we would test that we can get all of them but setting up + // the timing to do this in a consistent way is not benefitial. + // Ensuring the State is as expected is sufficient. +// INFO - MESSAGE [vh(/test)/qu(example.queue)] [sub:6(qu(example.queue))] SUB-1003 : State : +// INFO - MESSAGE [con:6(guest@anonymous(26562441)/test)/ch:3] [sub:6(qu(example.queue))] SUB-1003 : State : +// INFO - MESSAGE [sub:6(vh(test)/qu(example.queue))] [sub:6(qu(example.queue))] SUB-1003 : State : assertEquals("Result set not expected size:", 3, results.size()); @@ -367,19 +389,10 @@ public class SubscriptionLoggingTest extends AbstractTestLogging String log = getLog(results.get(0)); validateSubscriptionState(log, expectedState); - // Validate that the logActor is the the queue - String actor = fromActor(log); - assertTrue("Actor string does not contain expected queue(" - + _queue.getQueueName() + ") name." + actor, - actor.contains("qu(" + _queue.getQueueName() + ")")); - // After being suspended the subscription should become active. expectedState = "ACTIVE"; log = getLog(results.get(1)); validateSubscriptionState(log, expectedState); - // Validate we have a connection Actor - actor = fromActor(log); - assertTrue("The actor is not a connection actor:" + actor, actor.startsWith("con:")); // Validate that it was re-suspended expectedState = "SUSPENDED"; @@ -396,6 +409,10 @@ public class SubscriptionLoggingTest extends AbstractTestLogging } throw afe; } + _connection.close(); + + //Ensure the queue is drained before the test ends + drainQueue(_queue); } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/DynamicQueueExchangeCreateTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/DynamicQueueExchangeCreateTest.java new file mode 100644 index 0000000000..c9810e7304 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/DynamicQueueExchangeCreateTest.java @@ -0,0 +1,88 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.client; + +import org.apache.qpid.test.utils.QpidTestCase; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Queue; +import javax.jms.Session; + +/** + * QPID-155 + * + * Test to validate that setting the respective qpid.declare_queues, + * qpid.declare_exchanges system properties functions as expected. + * + */ +public class DynamicQueueExchangeCreateTest extends QpidTestCase +{ + + public void testQueueDeclare() throws Exception + { + setSystemProperty("qpid.declare_queues", "false"); + + Connection connection = getConnection(); + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + Queue queue = session.createQueue(getTestQueueName()); + + try + { + session.createConsumer(queue); + fail("JMSException should be thrown as the queue does not exist"); + } + catch (JMSException e) + { + assertTrue("Exception should be that the queue does not exist :" + + e.getMessage(), + e.getMessage().contains("does not exist")); + + } + } + + public void testExchangeDeclare() throws Exception + { + setSystemProperty("qpid.declare_exchanges", "false"); + + Connection connection = getConnection(); + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + String EXCHANGE_TYPE = "test.direct"; + Queue queue = session.createQueue("direct://" + EXCHANGE_TYPE + "/queue/queue"); + + try + { + session.createConsumer(queue); + fail("JMSException should be thrown as the exchange does not exist"); + } + catch (JMSException e) + { + assertTrue("Exception should be that the exchange does not exist :" + + e.getMessage(), + e.getMessage().contains("Exchange " + EXCHANGE_TYPE + " does not exist")); + } + } + +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/close/JavaServerCloseRaceConditionTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/close/JavaServerCloseRaceConditionTest.java new file mode 100644 index 0000000000..3fb6cd3526 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/close/JavaServerCloseRaceConditionTest.java @@ -0,0 +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.unit.close; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ExchangeDeclareBody; +import org.apache.qpid.framing.ExchangeDeclareOkBody; +import org.apache.qpid.test.utils.QpidTestCase; + +import javax.jms.Session; + +/** QPID-1809 + * + * Race condition on error handling and close logic. + * + * See most often with SimpleACLTest as this test is the expects the server to + * shut the connection/channels. This sort of testing is not performed by many, + * if any, of the other system tests. + * + * The problem is that we have two threads + * + * MainThread Exception(Mina)Thread + * | | + * Performs | + * ACtion | + * | Receives Server + * | Close + * Blocks for | + * Response | + * | Starts To Notify + * | client + * | | + * | <----- Notify Main Thread + * Notification | + * wakes client | + * | | + * Client then | + * processes Error. | + * | | + * Potentially Attempting Close Channel/Connection + * Connection Close + * + * The two threads both attempt to close the connection but the main thread does + * so assuming that the connection is open and valid. + * + * The Exception thread must modify the connection so that no furter syncWait + * commands are performed. + * + * This test sends an ExchangeDeclare that is Asynchronous and will fail and + * so cause a ChannelClose error but we perform a syncWait so that we can be + * sure to test that the BlockingWaiter is correctly awoken. + * + */ +public class JavaServerCloseRaceConditionTest extends QpidTestCase +{ + private static final String EXCHANGE_NAME = "NewExchangeNametoFailLookup"; + + public void test() throws Exception + { + + AMQConnection connection = (AMQConnection) getConnection(); + + AMQSession session = (AMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Set no wait true so that we block the connection + // Also set a different exchange class string so the attempt to declare + // the exchange causes an exchange. + ExchangeDeclareBody body = session.getMethodRegistry().createExchangeDeclareBody(session.getTicket(), new AMQShortString(EXCHANGE_NAME), null, + true, false, false, false, true, null); + + AMQFrame exchangeDeclare = body.generateFrame(session.getChannelId()); + + try + { + // block our thread so that can times out + connection.getProtocolHandler().syncWrite(exchangeDeclare, ExchangeDeclareOkBody.class); + } + catch (Exception e) + { + assertTrue("Exception should say the exchange is not known.", e.getMessage().contains("Unknown exchange: " + EXCHANGE_NAME)); + } + + try + { + // Depending on if the notification thread has closed the connection + // or not we may get an exception here when we attempt to close the + // connection. If we do get one then it should be the same as above + // an AMQAuthenticationException. + connection.close(); + } + catch (Exception e) + { + assertTrue("Exception should say the exchange is not known.", e.getMessage().contains("Unknown exchange: " + EXCHANGE_NAME)); + } + + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java index c5cdb83bbf..2a44413ac8 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java @@ -51,7 +51,13 @@ import javax.jms.TopicSubscriber; public class DurableSubscriptionTest extends QpidTestCase { private static final Logger _logger = LoggerFactory.getLogger(DurableSubscriptionTest.class); - + + /** Timeout for receive() if we are expecting a message */ + private static final long POSITIVE_RECEIVE_TIMEOUT = 2000; + + /** Timeout for receive() if we are not expecting a message */ + private static final long NEGATIVE_RECEIVE_TIMEOUT = 1000; + public void testUnsubscribe() throws Exception { AMQConnection con = (AMQConnection) getConnection("guest", "guest"); @@ -76,16 +82,18 @@ public class DurableSubscriptionTest extends QpidTestCase Message msg; _logger.info("Receive message on consumer 1:expecting A"); - msg = consumer1.receive(); + msg = consumer1.receive(POSITIVE_RECEIVE_TIMEOUT); + assertNotNull("Message should have been received",msg); assertEquals("A", ((TextMessage) msg).getText()); _logger.info("Receive message on consumer 1 :expecting null"); - msg = consumer1.receive(1000); + msg = consumer1.receive(NEGATIVE_RECEIVE_TIMEOUT); assertEquals(null, msg); - _logger.info("Receive message on consumer 1:expecting A"); - msg = consumer2.receive(); + _logger.info("Receive message on consumer 2:expecting A"); + msg = consumer2.receive(POSITIVE_RECEIVE_TIMEOUT); + assertNotNull("Message should have been received",msg); assertEquals("A", ((TextMessage) msg).getText()); - msg = consumer2.receive(1000); + msg = consumer2.receive(NEGATIVE_RECEIVE_TIMEOUT); _logger.info("Receive message on consumer 1 :expecting null"); assertEquals(null, msg); @@ -96,14 +104,15 @@ public class DurableSubscriptionTest extends QpidTestCase producer.send(session1.createTextMessage("B")); _logger.info("Receive message on consumer 1 :expecting B"); - msg = consumer1.receive(); + msg = consumer1.receive(POSITIVE_RECEIVE_TIMEOUT); + assertNotNull("Message should have been received",msg); assertEquals("B", ((TextMessage) msg).getText()); _logger.info("Receive message on consumer 1 :expecting null"); - msg = consumer1.receive(1000); + msg = consumer1.receive(NEGATIVE_RECEIVE_TIMEOUT); assertEquals(null, msg); _logger.info("Receive message on consumer 2 :expecting null"); - msg = consumer2.receive(1000); + msg = consumer2.receive(NEGATIVE_RECEIVE_TIMEOUT); assertEquals(null, msg); _logger.info("Close connection"); @@ -143,14 +152,16 @@ public class DurableSubscriptionTest extends QpidTestCase producer.send(session1.createTextMessage("A")); Message msg; - msg = consumer1.receive(); + msg = consumer1.receive(POSITIVE_RECEIVE_TIMEOUT); + assertNotNull("Message should have been received",msg); assertEquals("A", ((TextMessage) msg).getText()); - msg = consumer1.receive(1000); + msg = consumer1.receive(NEGATIVE_RECEIVE_TIMEOUT); assertEquals(null, msg); - msg = consumer2.receive(); + msg = consumer2.receive(POSITIVE_RECEIVE_TIMEOUT); + assertNotNull("Message should have been received",msg); assertEquals("A", ((TextMessage) msg).getText()); - msg = consumer2.receive(1000); + msg = consumer2.receive(NEGATIVE_RECEIVE_TIMEOUT); assertEquals(null, msg); consumer2.close(); @@ -220,8 +231,8 @@ public class DurableSubscriptionTest extends QpidTestCase msg = consumer1.receive(500); assertNull("There should be no more messages for consumption on consumer1.", msg); - msg = consumer2.receive(); - assertNotNull(msg); + msg = consumer2.receive(POSITIVE_RECEIVE_TIMEOUT); + assertNotNull("Message should have been received",msg); assertEquals("Consumer 2 should also received the first msg.", "A", ((TextMessage) msg).getText()); msg = consumer2.receive(500); assertNull("There should be no more messages for consumption on consumer2.", msg); @@ -235,10 +246,10 @@ public class DurableSubscriptionTest extends QpidTestCase producer.send(session0.createTextMessage("B")); _logger.info("Receive message on consumer 1 :expecting B"); - msg = consumer1.receive(1000); + msg = consumer1.receive(NEGATIVE_RECEIVE_TIMEOUT); assertEquals("B", ((TextMessage) msg).getText()); _logger.info("Receive message on consumer 1 :expecting null"); - msg = consumer1.receive(1000); + msg = consumer1.receive(NEGATIVE_RECEIVE_TIMEOUT); assertEquals(null, msg); // Re-attach a new consumer to the durable subscription, and check that it gets the message that it missed. @@ -296,7 +307,7 @@ public class DurableSubscriptionTest extends QpidTestCase producer.send(session.createTextMessage("testDurableWithInvalidSelector2")); - Message msg = liveSubscriber.receive(); + Message msg = liveSubscriber.receive(POSITIVE_RECEIVE_TIMEOUT); assertNotNull ("Message should have been received", msg); assertEquals ("testDurableWithInvalidSelector2", ((TextMessage) msg).getText()); assertNull("Should not receive subsequent message", liveSubscriber.receive(200)); @@ -331,7 +342,7 @@ public class DurableSubscriptionTest extends QpidTestCase assertNotNull("Subscriber should have been created", liveSubscriber); producer.send(session.createTextMessage("testDurableWithInvalidSelector2")); - Message msg = liveSubscriber.receive(); + Message msg = liveSubscriber.receive(POSITIVE_RECEIVE_TIMEOUT); assertNotNull ("Message should have been received", msg); assertEquals ("testDurableWithInvalidSelector2", ((TextMessage) msg).getText()); assertNull("Should not receive subsequent message", liveSubscriber.receive(200)); @@ -360,13 +371,13 @@ public class DurableSubscriptionTest extends QpidTestCase // Send 1 matching message and 1 non-matching message sendMatchingAndNonMatchingMessage(session, producer); - Message rMsg = subA.receive(1000); + Message rMsg = subA.receive(NEGATIVE_RECEIVE_TIMEOUT); assertNotNull(rMsg); assertEquals("Content was wrong", "testResubscribeWithChangedSelector1", ((TextMessage) rMsg).getText()); - rMsg = subA.receive(1000); + rMsg = subA.receive(NEGATIVE_RECEIVE_TIMEOUT); assertNull(rMsg); // Disconnect subscriber @@ -379,13 +390,13 @@ public class DurableSubscriptionTest extends QpidTestCase // Check messages are recieved properly sendMatchingAndNonMatchingMessage(session, producer); - rMsg = subB.receive(1000); + rMsg = subB.receive(NEGATIVE_RECEIVE_TIMEOUT); assertNotNull(rMsg); assertEquals("Content was wrong", "testResubscribeWithChangedSelector2", ((TextMessage) rMsg).getText()); - rMsg = subB.receive(1000); + rMsg = subB.receive(NEGATIVE_RECEIVE_TIMEOUT); assertNull(rMsg); session.unsubscribe("testResubscribeWithChangedSelector"); } @@ -429,5 +440,5 @@ public class DurableSubscriptionTest extends QpidTestCase public static junit.framework.Test suite() { return new junit.framework.TestSuite(DurableSubscriptionTest.class); - } + } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java index 9c755fcb41..b603455644 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java @@ -479,7 +479,7 @@ public class CommitRollbackTest extends QpidTestCase _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT)); _pubSession.commit(); - assertNotNull(_consumer.receive(100)); + assertNotNull(_consumer.receive(1000)); _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT)); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/FailoverBaseCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/FailoverBaseCase.java index cc9cfce34b..1bef07fcd5 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/FailoverBaseCase.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/FailoverBaseCase.java @@ -55,7 +55,7 @@ public class FailoverBaseCase extends QpidTestCase { super.setUp(); setSystemProperty("QPID_WORK", System.getProperty("java.io.tmpdir")+"/"+getFailingPort()); - startBroker(FAILING_PORT); + startBroker(failingPort); } /** @@ -76,7 +76,7 @@ public class FailoverBaseCase extends QpidTestCase public void tearDown() throws Exception { - stopBroker(FAILING_PORT); + stopBroker(_broker.equals(VM)?FAILING_PORT:FAILING_PORT); super.tearDown(); FileUtils.deleteDirectory(System.getProperty("java.io.tmpdir")+"/"+getFailingPort()); } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitor.java b/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitor.java index df8dd0b85b..44ac5b4838 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitor.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitor.java @@ -31,6 +31,7 @@ import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.OutputStreamWriter; +import java.util.ArrayList; import java.util.List; /** @@ -118,7 +119,7 @@ public class LogMonitor * @throws java.io.FileNotFoundException if the Log file can nolonger be found * @throws IOException thrown when reading the log file */ - public boolean waitForMessage(String message, long wait) + public boolean waitForMessage(String message, long wait, boolean printFileOnFailure) throws FileNotFoundException, IOException { // Loop through alerts until we're done or wait ms seconds have passed, @@ -126,20 +127,35 @@ public class LogMonitor BufferedReader reader = new BufferedReader(new FileReader(_logfile)); boolean found = false; long endtime = System.currentTimeMillis() + wait; + ArrayList contents = new ArrayList(); while (!found && System.currentTimeMillis() < endtime) { while (reader.ready()) { String line = reader.readLine(); + contents.add(line); if (line.contains(message)) { found = true; } } } - + if (!found && printFileOnFailure) + { + for (String line : contents) + { + System.out.println(line); + } + } return found; } + + + public boolean waitForMessage(String messageCountAlert, long alertLogWaitPeriod) throws FileNotFoundException, IOException + { + return waitForMessage(messageCountAlert, alertLogWaitPeriod, true); + } + /** * Read the log file in to memory as a String diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitorTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitorTest.java index d1a0df30a9..2b9fe8e039 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitorTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitorTest.java @@ -30,25 +30,25 @@ import java.util.List; public class LogMonitorTest extends TestCase { + private LogMonitor _monitor; + + @Override + public void setUp() throws Exception + { + _monitor = new LogMonitor(); + _monitor.getMonitoredFile().deleteOnExit(); // Make sure we clean up + } + /** * Test that a new file is created when attempting to set up a monitor with * the default constructor. */ public void testMonitor() { - // Validate that a NPE is thrown with null input - try - { - LogMonitor montior = new LogMonitor(); - //Validte that the monitor is now running on a new file - assertTrue("New file does not have correct name:" + montior. - getMonitoredFile().getName(), - montior.getMonitoredFile().getName().contains("LogMonitor")); - } - catch (IOException ioe) - { - fail("IOE thrown:" + ioe); - } + //Validate that the monitor is now running on a new file + assertTrue("New file does not have correct name:" + _monitor. + getMonitoredFile().getName(), + _monitor.getMonitoredFile().getName().contains("LogMonitor")); } /** @@ -63,13 +63,11 @@ public class LogMonitorTest extends TestCase File testFile = File.createTempFile("testMonitorFile", ".log"); testFile.deleteOnExit(); - LogMonitor monitor; - //Ensure that we can create a monitor on a file try { - monitor = new LogMonitor(testFile); - assertEquals(testFile, monitor.getMonitoredFile()); + _monitor = new LogMonitor(testFile); + assertEquals(testFile, _monitor.getMonitoredFile()); } catch (IOException ioe) { @@ -136,13 +134,12 @@ public class LogMonitorTest extends TestCase */ public void testFindMatches_Match() throws IOException { - LogMonitor monitor = new LogMonitor(); String message = getName() + ": Test Message"; Logger.getRootLogger().warn(message); - validateLogContainsMessage(monitor, message); + validateLogContainsMessage(_monitor, message); } /** @@ -152,35 +149,17 @@ public class LogMonitorTest extends TestCase */ public void testFindMatches_NoMatch() throws IOException { - LogMonitor monitor = new LogMonitor(); - String message = getName() + ": Test Message"; Logger.getRootLogger().warn(message); String notLogged = "This text was not logged"; - validateLogDoesNotContainsMessage(monitor, notLogged); - } - - public void testWaitForMessage_Found() throws IOException - { - LogMonitor monitor = new LogMonitor(); - - String message = getName() + ": Test Message"; - - long TIME_OUT = 2000; - - logMessageWithDelay(message, TIME_OUT / 2); - - assertTrue("Message was not logged ", - monitor.waitForMessage(message, TIME_OUT)); + validateLogDoesNotContainsMessage(_monitor, notLogged); } public void testWaitForMessage_Timeout() throws IOException { - LogMonitor monitor = new LogMonitor(); - String message = getName() + ": Test Message"; long TIME_OUT = 2000; @@ -189,41 +168,37 @@ public class LogMonitorTest extends TestCase // Verify that we can time out waiting for a message assertFalse("Message was logged ", - monitor.waitForMessage(message, TIME_OUT / 2)); + _monitor.waitForMessage(message, TIME_OUT / 2, false)); // Verify that the message did eventually get logged. assertTrue("Message was never logged.", - monitor.waitForMessage(message, TIME_OUT)); + _monitor.waitForMessage(message, TIME_OUT)); } public void testReset() throws IOException { - LogMonitor monitor = new LogMonitor(); - String message = getName() + ": Test Message"; Logger.getRootLogger().warn(message); - validateLogContainsMessage(monitor, message); + validateLogContainsMessage(_monitor, message); String LOG_RESET_TEXT = "Log Monitor Reset"; - validateLogDoesNotContainsMessage(monitor, LOG_RESET_TEXT); + validateLogDoesNotContainsMessage(_monitor, LOG_RESET_TEXT); - monitor.reset(); + _monitor.reset(); - assertEquals("", monitor.readFile()); + assertEquals("", _monitor.readFile()); } public void testRead() throws IOException { - LogMonitor monitor = new LogMonitor(); - String message = getName() + ": Test Message"; Logger.getRootLogger().warn(message); - String fileContents = monitor.readFile(); + String fileContents = _monitor.readFile(); assertTrue("Logged message not found when reading file.", fileContents.contains(message)); diff --git a/qpid/java/test-profiles/010Excludes b/qpid/java/test-profiles/010Excludes index 7577ad8da1..36e7317e31 100644 --- a/qpid/java/test-profiles/010Excludes +++ b/qpid/java/test-profiles/010Excludes @@ -4,6 +4,7 @@ org.apache.qpid.client.ResetMessageListenerTest#* //These tests are for the java broker org.apache.qpid.server.security.acl.SimpleACLTest#* org.apache.qpid.server.plugins.PluginTest#* +org.apache.qpid.server.BrokerStartupTest#* // This test is not finished org.apache.qpid.test.testcases.TTLTest#* @@ -82,3 +83,9 @@ org.apache.qpid.server.logging.* // CPP Broker does not have a JMX interface to test org.apache.qpid.management.jmx.* + +// 0-10 is not supported by the MethodRegistry +org.apache.qpid.test.unit.close.JavaServerCloseRaceConditionTest#* + +// QPID-2084 : this test needs more work for 0-10 +org.apache.qpid.test.unit.client.DynamicQueueExchangeCreateTest#* diff --git a/qpid/python/Makefile b/qpid/python/Makefile new file mode 100644 index 0000000000..31547c8f57 --- /dev/null +++ b/qpid/python/Makefile @@ -0,0 +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. +# + +PREFIX=/usr/local +EXEC_PREFIX=$(PREFIX)/bin +DATA_DIR=$(PREFIX)/share + +PYTHON_LIB=$(shell python -c "from distutils.sysconfig import get_python_lib; print get_python_lib(prefix='$(PREFIX)')") +PYTHON_VERSION=$(shell python -c "from distutils.sysconfig import get_python_version; print get_python_version()") + +ddfirst=$(shell ddir=$(DATA_DIR) && echo $${ddir:0:1}) +ifeq ($(ddfirst),/) +AMQP_SPEC_DIR=$(DATA_DIR)/amqp +else +AMQP_SPEC_DIR=$(PWD)/$(DATA_DIR)/amqp +endif + +DIRS=qmf qpid mllib models examples tests tests_0-8 tests_0-9 tests_0-10 +SRCS=$(shell find $(DIRS) -name "*.py") qpid_config.py +BUILD=build +TARGETS=$(SRCS:%.py=$(BUILD)/%.py) + +PYCC=python -c "import compileall, sys; compileall.compile_dir(sys.argv[1])" + +all: build + +$(BUILD)/%.py: %.py + @mkdir -p $(shell dirname $@) + ./preppy $(PYTHON_VERSION) < $< > $@ + +build: $(TARGETS) + +.PHONY: doc + +doc: + @mkdir -p $(BUILD) + epydoc qpid.messaging -o $(BUILD)/doc --no-private --no-sourcecode --include-log + +install: build + install -d $(PYTHON_LIB) + + install -d $(PYTHON_LIB)/mllib + install -pm 0644 LICENSE.txt NOTICE.txt $(BUILD)/mllib/*.* $(PYTHON_LIB)/mllib + $(PYCC) $(PYTHON_LIB)/mllib + + install -d $(PYTHON_LIB)/qpid + install -pm 0644 LICENSE.txt NOTICE.txt README.txt $(BUILD)/qpid/*.* $(PYTHON_LIB)/qpid + TDIR=$(shell mktemp -d) && \ + sed s@AMQP_SPEC_DIR=.*@AMQP_SPEC_DIR='"$(AMQP_SPEC_DIR)"'@ \ + $(BUILD)/qpid_config.py > $${TDIR}/qpid_config.py && \ + install -pm 0644 $${TDIR}/qpid_config.py $(PYTHON_LIB) && \ + rm -rf $${TDIR} + + install -d $(PYTHON_LIB)/qpid/tests + install -pm 0644 $(BUILD)/qpid/tests/*.* $(PYTHON_LIB)/qpid/tests + $(PYCC) $(PYTHON_LIB)/qpid + + install -d $(PYTHON_LIB)/qmf + install -pm 0644 LICENSE.txt NOTICE.txt qmf/*.* $(PYTHON_LIB)/qmf + $(PYCC) $(PYTHON_LIB)/qmf + + install -d $(PYTHON_LIB)/tests + install -pm 0644 $(BUILD)/tests/*.* $(PYTHON_LIB)/tests + $(PYCC) $(PYTHON_LIB)/tests + + install -d $(PYTHON_LIB)/tests_0-8 + install -pm 0644 $(BUILD)/tests_0-8/*.* $(PYTHON_LIB)/tests_0-8 + $(PYCC) $(PYTHON_LIB)/tests_0-8 + + install -d $(PYTHON_LIB)/tests_0-9 + install -pm 0644 $(BUILD)/tests_0-9/*.* $(PYTHON_LIB)/tests_0-9 + $(PYCC) $(PYTHON_LIB)/tests_0-9 + + install -d $(PYTHON_LIB)/tests_0-10 + install -pm 0644 $(BUILD)/tests_0-10/*.* $(PYTHON_LIB)/tests_0-10 + $(PYCC) $(PYTHON_LIB)/tests_0-10 + + install -d $(EXEC_PREFIX) + install -pm 0755 qpid-python-test commands/* $(EXEC_PREFIX) + +clean: + rm -rf $(BUILD) diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index c4ea5c5f2d..4cf9505c58 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -110,6 +110,12 @@ def Usage (): print " --force-if-not-empty Force delete of queue even if it's not empty" print " --force-if-used Force delete of queue even if it's currently used" print + print "Add Exchange values:" + print " direct Direct exchange for point-to-point communication" + print " fanout Fanout exchange for broadcast communication" + print " topic Topic exchange that routes messages using binding keys with wildcards" + print " headers Headers exchange that matches header fields against the binding keys" + print print "Add Exchange Options:" print " --alternate-exchange [name of the alternate exchange]" print " In the event that a message cannot be routed, this is the name of the exchange to" @@ -190,6 +196,8 @@ class BrokerManager: if ex.durable: print "--durable", if MSG_SEQUENCE in args and args[MSG_SEQUENCE] == 1: print "--sequence", if IVE in args and args[IVE] == 1: print "--ive", + if ex.altExchange: + print "--alternate-exchange=%s" % ex._altExchange_.name, print def ExchangeListRecurse (self, filter): diff --git a/qpid/python/preppy b/qpid/python/preppy new file mode 100755 index 0000000000..22893dad03 --- /dev/null +++ b/qpid/python/preppy @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT 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 os, re, sys + +ann = re.compile(r"([ \t]*)@([_a-zA-Z][_a-zA-Z0-9]*)([ \t\n\r]+def[ \t]+)([_a-zA-Z][_a-zA-Z0-9]*)") +line = re.compile(r"\n([ \t]*)[^ \t\n#]+") + +if len(sys.argv) == 2: + major, minor = [int(p) for p in sys.argv[1].split(".")] +elif len(sys.argv) == 1: + major, minor = sys.version_info[0:2] +else: + print "usage: %s [ version ] < input.py > output.py" % sys.argv[0] + sys.exit(-1) + +if major <= 2 and minor <= 3: + def process(input): + output = "" + pos = 0 + while True: + m = ann.search(input, pos) + if m: + indent, decorator, idef, function = m.groups() + output += input[pos:m.start()] + output += "%s#@%s%s%s" % (indent, decorator, idef, function) + pos = m.end() + + subst = "\n%s%s = %s(%s)\n" % (indent, function, decorator, function) + npos = pos + while True: + n = line.search(input, npos) + if not n: + input += subst + break + if len(n.group(1)) <= len(indent): + idx = n.start() + input = input[:idx] + subst + input[idx:] + break + npos = n.end() + else: + break + + output += input[pos:] + return output +else: + def process(input): + return input + +sys.stdout.write(process(sys.stdin.read())) diff --git a/qpid/python/qpid/compat.py b/qpid/python/qpid/compat.py index 26f60fb8aa..49273193df 100644 --- a/qpid/python/qpid/compat.py +++ b/qpid/python/qpid/compat.py @@ -26,3 +26,10 @@ try: from socket import SHUT_RDWR except ImportError: SHUT_RDWR = 2 + +try: + from traceback import format_exc +except ImportError: + import sys, traceback + def format_exc(): + return "".join(traceback.format_exception(*sys.exc_info())) diff --git a/qpid/python/qpid/concurrency.py b/qpid/python/qpid/concurrency.py new file mode 100644 index 0000000000..00cdb6b953 --- /dev/null +++ b/qpid/python/qpid/concurrency.py @@ -0,0 +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. +# + +import inspect, time + +def synchronized(meth): + args, vargs, kwargs, defs = inspect.getargspec(meth) + scope = {} + scope["meth"] = meth + exec """ +def %s%s: + %s + %s._lock.acquire() + try: + return meth%s + finally: + %s._lock.release() +""" % (meth.__name__, inspect.formatargspec(args, vargs, kwargs, defs), + repr(inspect.getdoc(meth)), args[0], + inspect.formatargspec(args, vargs, kwargs, defs, + formatvalue=lambda x: ""), + args[0]) in scope + return scope[meth.__name__] + +class Waiter(object): + + def __init__(self, condition): + self.condition = condition + + def wait(self, predicate, timeout=None): + passed = 0 + start = time.time() + while not predicate(): + if timeout is None: + # using the timed wait prevents keyboard interrupts from being + # blocked while waiting + self.condition.wait(3) + elif passed < timeout: + self.condition.wait(timeout - passed) + else: + return False + passed = time.time() - start + return True + + def notify(self): + self.condition.notify() + + def notifyAll(self): + self.condition.notifyAll() diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index bba3f5b9ab..f832ddae34 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -132,7 +132,7 @@ class Serial: return hash(self.value) def __cmp__(self, other): - if other is None: + if other.__class__ not in (int, long, Serial): return 1 other = serial(other) @@ -150,7 +150,10 @@ class Serial: return Serial(self.value + other) def __sub__(self, other): - return Serial(self.value - other) + if isinstance(other, Serial): + return self.value - other.value + else: + return Serial(self.value - other) def __repr__(self): return "serial(%s)" % self.value @@ -169,7 +172,7 @@ class Range: def __contains__(self, n): return self.lower <= n and n <= self.upper - + def __iter__(self): i = self.lower while i <= self.upper: @@ -230,7 +233,7 @@ class RangedSet: def add(self, lower, upper = None): self.add_range(Range(lower, upper)) - + def __iter__(self): return iter(self.ranges) diff --git a/qpid/python/qpid/driver.py b/qpid/python/qpid/driver.py new file mode 100644 index 0000000000..2e07c82a0d --- /dev/null +++ b/qpid/python/qpid/driver.py @@ -0,0 +1,444 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT 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 compat, connection, socket, sys, time +from concurrency import synchronized +from datatypes import RangedSet, Message as Message010 +from exceptions import Timeout +from logging import getLogger +from messaging import get_codec, ConnectError, Message, Pattern, UNLIMITED +from ops import delivery_mode +from session import Client, INCOMPLETE, SessionDetached +from threading import Condition, Thread +from util import connect + +log = getLogger("qpid.messaging") + +def parse_addr(address): + parts = address.split("/", 1) + if len(parts) == 1: + return parts[0], None + else: + return parts[0], parts[i1] + +def reply_to2addr(reply_to): + if reply_to.routing_key is None: + return reply_to.exchange + elif reply_to.exchange in (None, ""): + return reply_to.routing_key + else: + return "%s/%s" % (reply_to.exchange, reply_to.routing_key) + +class Attachment: + + def __init__(self, target): + self.target = target + +DURABLE_DEFAULT=True + +FILTER_DEFAULTS = { + "topic": Pattern("*") + } + +def delegate(handler, session): + class Delegate(Client): + + def message_transfer(self, cmd): + return handler._message_transfer(session, cmd) + return Delegate + +class Driver: + + def __init__(self, connection): + self.connection = connection + self._lock = self.connection._lock + self._wakeup_cond = Condition() + self._socket = None + self._conn = None + self._connected = False + self._attachments = {} + self._modcount = self.connection._modcount + self.thread = Thread(target=self.run) + self.thread.setDaemon(True) + # XXX: need to figure out how to join on this thread + + def wakeup(self): + self._wakeup_cond.acquire() + try: + self._wakeup_cond.notifyAll() + finally: + self._wakeup_cond.release() + + def start(self): + self.thread.start() + + def run(self): + while True: + self._wakeup_cond.acquire() + try: + if self.connection._modcount <= self._modcount: + self._wakeup_cond.wait(10) + finally: + self._wakeup_cond.release() + self.dispatch(self.connection._modcount) + + @synchronized + def dispatch(self, modcount): + try: + if self._conn is None and self.connection._connected: + self.connect() + elif self._conn is not None and not self.connection._connected: + self.disconnect() + + if self._conn is not None: + for ssn in self.connection.sessions.values(): + self.attach(ssn) + self.process(ssn) + + exi = None + except: + exi = sys.exc_info() + + if exi: + msg = compat.format_exc() + recoverable = ["aborted", "Connection refused", "SessionDetached", "Connection reset by peer", + "Bad file descriptor", "start timed out", "Broken pipe"] + for r in recoverable: + if self.connection.reconnect and r in msg: + print "waiting to retry" + self.reset() + time.sleep(3) + print "retrying..." + return + else: + self.connection.error = (msg,) + + self._modcount = modcount + self.connection._waiter.notifyAll() + + def connect(self): + if self._conn is not None: + return + try: + self._socket = connect(self.connection.host, self.connection.port) + except socket.error, e: + raise ConnectError(e) + self._conn = connection.Connection(self._socket) + try: + self._conn.start(timeout=10) + self._connected = True + except connection.VersionError, e: + raise ConnectError(e) + except Timeout: + print "start timed out" + raise ConnectError("start timed out") + + def disconnect(self): + self._conn.close() + self.reset() + + def reset(self): + self._conn = None + self._connected = False + self._attachments.clear() + for ssn in self.connection.sessions.values(): + for m in ssn.acked + ssn.unacked + ssn.incoming: + m._transfer_id = None + for rcv in ssn.receivers: + rcv.impending = rcv.received + + def connected(self): + return self._conn is not None + + def attach(self, ssn): + _ssn = self._attachments.get(ssn) + if _ssn is None: + _ssn = self._conn.session(ssn.name, delegate=delegate(self, ssn)) + _ssn.auto_sync = False + _ssn.invoke_lock = self._lock + _ssn.lock = self._lock + _ssn.condition = self.connection._condition + if ssn.transactional: + # XXX: adding an attribute to qpid.session.Session + _ssn.acked = [] + _ssn.tx_select() + self._attachments[ssn] = _ssn + + for snd in ssn.senders: + self.link_out(snd) + for rcv in ssn.receivers: + self.link_in(rcv) + + if ssn.closing: + _ssn.close() + del self._attachments[ssn] + ssn.closed = True + + def _exchange_query(self, ssn, address): + # XXX: auto sync hack is to avoid deadlock on future + result = ssn.exchange_query(name=address, sync=True) + ssn.sync() + return result.get() + + def link_out(self, snd): + _ssn = self._attachments[snd.session] + _snd = self._attachments.get(snd) + if _snd is None: + _snd = Attachment(snd) + node, _snd._subject = parse_addr(snd.target) + result = self._exchange_query(_ssn, node) + if result.not_found: + # XXX: should check 'create' option + _ssn.queue_declare(queue=node, durable=DURABLE_DEFAULT, sync=True) + _ssn.sync() + _snd._exchange = "" + _snd._routing_key = node + else: + _snd._exchange = node + _snd._routing_key = _snd._subject + self._attachments[snd] = _snd + + if snd.closed: + del self._attachments[snd] + return None + else: + return _snd + + def link_in(self, rcv): + _ssn = self._attachments[rcv.session] + _rcv = self._attachments.get(rcv) + if _rcv is None: + _rcv = Attachment(rcv) + result = self._exchange_query(_ssn, rcv.source) + if result.not_found: + _rcv._queue = rcv.source + # XXX: should check 'create' option + _ssn.queue_declare(queue=_rcv._queue, durable=DURABLE_DEFAULT) + else: + _rcv._queue = "%s.%s" % (rcv.session.name, rcv.destination) + _ssn.queue_declare(queue=_rcv._queue, durable=DURABLE_DEFAULT, exclusive=True, auto_delete=True) + if rcv.filter is None: + f = FILTER_DEFAULTS[result.type] + else: + f = rcv.filter + f._bind(_ssn, rcv.source, _rcv._queue) + _ssn.message_subscribe(queue=_rcv._queue, destination=rcv.destination) + _ssn.message_set_flow_mode(rcv.destination, _ssn.flow_mode.credit, sync=True) + self._attachments[rcv] = _rcv + # XXX: need to kill syncs + _ssn.sync() + + if rcv.closing: + _ssn.message_cancel(rcv.destination, sync=True) + # XXX: need to kill syncs + _ssn.sync() + del self._attachments[rcv] + rcv.closed = True + return None + else: + return _rcv + + def process(self, ssn): + if ssn.closing: return + + _ssn = self._attachments[ssn] + + while ssn.outgoing: + msg = ssn.outgoing[0] + snd = msg._sender + self.send(snd, msg) + ssn.outgoing.pop(0) + + for rcv in ssn.receivers: + self.process_receiver(rcv) + + if ssn.acked: + messages = ssn.acked[:] + ids = RangedSet(*[m._transfer_id for m in messages if m._transfer_id is not None]) + for range in ids: + _ssn.receiver._completed.add_range(range) + ch = _ssn.channel + if ch is None: + raise SessionDetached() + ch.session_completed(_ssn.receiver._completed) + _ssn.message_accept(ids, sync=True) + # XXX: really need to make this async so that we don't give up the lock + _ssn.sync() + + # XXX: we're ignoring acks that get lost when disconnected + for m in messages: + ssn.acked.remove(m) + if ssn.transactional: + _ssn.acked.append(m) + + if ssn.committing: + _ssn.tx_commit(sync=True) + # XXX: need to kill syncs + _ssn.sync() + del _ssn.acked[:] + ssn.committing = False + ssn.committed = True + ssn.aborting = False + ssn.aborted = False + + if ssn.aborting: + for rcv in ssn.receivers: + _ssn.message_stop(rcv.destination) + _ssn.sync() + + messages = _ssn.acked + ssn.unacked + ssn.incoming + ids = RangedSet(*[m._transfer_id for m in messages]) + for range in ids: + _ssn.receiver._completed.add_range(range) + _ssn.channel.session_completed(_ssn.receiver._completed) + _ssn.message_release(ids) + _ssn.tx_rollback(sync=True) + _ssn.sync() + + del ssn.incoming[:] + del ssn.unacked[:] + del _ssn.acked[:] + + for rcv in ssn.receivers: + rcv.impending = rcv.received + rcv.returned = rcv.received + # XXX: do we need to update granted here as well? + + for rcv in ssn.receivers: + self.process_receiver(rcv) + + ssn.aborting = False + ssn.aborted = True + ssn.committing = False + ssn.committed = False + + def grant(self, rcv): + _ssn = self._attachments[rcv.session] + _rcv = self.link_in(rcv) + + if rcv.granted is UNLIMITED: + if rcv.impending is UNLIMITED: + delta = 0 + else: + delta = UNLIMITED + elif rcv.impending is UNLIMITED: + delta = -1 + else: + delta = max(rcv.granted, rcv.received) - rcv.impending + + if delta is UNLIMITED: + _ssn.message_flow(rcv.destination, _ssn.credit_unit.byte, UNLIMITED.value) + _ssn.message_flow(rcv.destination, _ssn.credit_unit.message, UNLIMITED.value) + rcv.impending = UNLIMITED + elif delta > 0: + _ssn.message_flow(rcv.destination, _ssn.credit_unit.byte, UNLIMITED.value) + _ssn.message_flow(rcv.destination, _ssn.credit_unit.message, delta) + rcv.impending += delta + elif delta < 0: + if rcv.drain: + _ssn.message_flush(rcv.destination, sync=True) + else: + _ssn.message_stop(rcv.destination, sync=True) + # XXX: need to kill syncs + _ssn.sync() + rcv.impending = rcv.received + self.grant(rcv) + + def process_receiver(self, rcv): + if rcv.closed: return + self.grant(rcv) + + def send(self, snd, msg): + _ssn = self._attachments[snd.session] + _snd = self.link_out(snd) + + # XXX: what if subject is specified for a normal queue? + if _snd._routing_key is None: + rk = msg.subject + else: + rk = _snd._routing_key + # XXX: do we need to query to figure out how to create the reply-to interoperably? + if msg.reply_to: + rt = _ssn.reply_to(*parse_addr(msg.reply_to)) + else: + rt = None + dp = _ssn.delivery_properties(routing_key=rk) + mp = _ssn.message_properties(message_id=msg.id, + user_id=msg.user_id, + reply_to=rt, + correlation_id=msg.correlation_id, + content_type=msg.content_type, + application_headers=msg.properties) + if msg.subject is not None: + if mp.application_headers is None: + mp.application_headers = {} + mp.application_headers["subject"] = msg.subject + if msg.to is not None: + if mp.application_headers is None: + mp.application_headers = {} + mp.application_headers["to"] = msg.to + if msg.durable: + dp.delivery_mode = delivery_mode.persistent + enc, dec = get_codec(msg.content_type) + body = enc(msg.content) + _ssn.message_transfer(destination=_snd._exchange, + message=Message010(dp, mp, body), + sync=True) + log.debug("SENT [%s] %s", snd.session, msg) + # XXX: really need to make this async so that we don't give up the lock + _ssn.sync() + # XXX: should we log the ack somehow too? + snd.acked += 1 + + @synchronized + def _message_transfer(self, ssn, cmd): + m = Message010(cmd.payload) + m.headers = cmd.headers + m.id = cmd.id + msg = self._decode(m) + rcv = ssn.receivers[int(cmd.destination)] + msg._receiver = rcv + if rcv.impending is not UNLIMITED: + assert rcv.received < rcv.impending + rcv.received += 1 + log.debug("RECV [%s] %s", ssn, msg) + ssn.incoming.append(msg) + self.connection._waiter.notifyAll() + return INCOMPLETE + + def _decode(self, message): + dp = message.get("delivery_properties") + mp = message.get("message_properties") + ap = mp.application_headers + enc, dec = get_codec(mp.content_type) + content = dec(message.body) + msg = Message(content) + msg.id = mp.message_id + if ap is not None: + msg.to = ap.get("to") + msg.subject = ap.get("subject") + msg.user_id = mp.user_id + if mp.reply_to is not None: + msg.reply_to = reply_to2addr(mp.reply_to) + msg.correlation_id = mp.correlation_id + msg.durable = dp.delivery_mode == delivery_mode.persistent + msg.properties = mp.application_headers + msg.content_type = mp.content_type + msg._transfer_id = message.id + return msg diff --git a/qpid/python/qpid/messaging.py b/qpid/python/qpid/messaging.py index 9b3fecbf9b..d755aa5054 100644 --- a/qpid/python/qpid/messaging.py +++ b/qpid/python/qpid/messaging.py @@ -6,9 +6,9 @@ # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -30,63 +30,18 @@ Areas that still need work: - protocol negotiation/multiprotocol impl """ -import connection, time, socket, sys, traceback from codec010 import StringCodec -from datatypes import timestamp, uuid4, RangedSet, Message as Message010 +from concurrency import synchronized, Waiter +from datatypes import timestamp, uuid4, Serial from logging import getLogger from ops import PRIMITIVE -from session import Client, INCOMPLETE from threading import Thread, RLock, Condition -from util import connect +from util import default log = getLogger("qpid.messaging") static = staticmethod -def synchronized(meth): - def sync_wrapper(self, *args, **kwargs): - self.lock() - try: - return meth(self, *args, **kwargs) - finally: - self.unlock() - return sync_wrapper - -class Lockable(object): - - def lock(self): - self._lock.acquire() - - def unlock(self): - self._lock.release() - - def wait(self, predicate, timeout=None): - passed = 0 - start = time.time() - while not predicate(): - if timeout is None: - # using the timed wait prevents keyboard interrupts from being - # blocked while waiting - self._condition.wait(3) - elif passed < timeout: - self._condition.wait(timeout - passed) - else: - return False - passed = time.time() - start - return True - - def notify(self): - self._condition.notify() - - def notifyAll(self): - self._condition.notifyAll() - -def default(value, default): - if value is None: - return default - else: - return value - AMQP_PORT = 5672 AMQPS_PORT = 5671 @@ -101,10 +56,20 @@ class Constant: UNLIMITED = Constant("UNLIMITED", 0xFFFFFFFFL) -class ConnectError(Exception): +class ConnectionError(Exception): + """ + The base class for all connection related exceptions. + """ pass -class Connection(Lockable): +class ConnectError(ConnectionError): + """ + Exception raised when there is an error connecting to the remote + peer. + """ + pass + +class Connection: """ A Connection manages a group of L{Sessions} and connects @@ -142,12 +107,35 @@ class Connection(Lockable): self.host = host self.port = default(port, AMQP_PORT) self.started = False - self._conn = None self.id = str(uuid4()) self.session_counter = 0 self.sessions = {} + self.reconnect = False + self._connected = False self._lock = RLock() self._condition = Condition(self._lock) + self._waiter = Waiter(self._condition) + self._modcount = Serial(0) + self.error = None + from driver import Driver + self._driver = Driver(self) + self._driver.start() + + def _wait(self, predicate, timeout=None): + return self._waiter.wait(predicate, timeout=timeout) + + def _wakeup(self): + self._modcount += 1 + self._driver.wakeup() + + def _check_error(self, exc=ConnectionError): + if self.error: + raise exc(*self.error) + + def _ewait(self, predicate, timeout=None, exc=ConnectionError): + result = self._wait(lambda: self.error or predicate(), timeout) + self._check_error(exc) + return result @synchronized def session(self, name=None, transactional=False): @@ -173,8 +161,7 @@ class Connection(Lockable): else: ssn = Session(self, name, self.started, transactional=transactional) self.sessions[name] = ssn - if self._conn is not None: - ssn._attach() + self._wakeup() return ssn @synchronized @@ -186,38 +173,25 @@ class Connection(Lockable): """ Connect to the remote endpoint. """ - if self._conn is not None: - return - try: - self._socket = connect(self.host, self.port) - except socket.error, e: - raise ConnectError(e) - self._conn = connection.Connection(self._socket) - try: - self._conn.start() - except connection.VersionError, e: - raise ConnectError(e) - - for ssn in self.sessions.values(): - ssn._attach() + self._connected = True + self._wakeup() + self._ewait(lambda: self._driver._connected, exc=ConnectError) @synchronized def disconnect(self): """ Disconnect from the remote endpoint. """ - if self._conn is not None: - self._conn.close() - self._conn = None - for ssn in self.sessions.values(): - ssn._disconnected() + self._connected = False + self._wakeup() + self._ewait(lambda: not self._driver._connected) @synchronized def connected(self): """ Return true if the connection is connected, false otherwise. """ - return self._conn is not None + return self._connected @synchronized def start(self): @@ -255,22 +229,32 @@ class Pattern: def __init__(self, value): self.value = value + # XXX: this should become part of the driver def _bind(self, ssn, exchange, queue): ssn.exchange_bind(exchange=exchange, queue=queue, binding_key=self.value.replace("*", "#")) -FILTER_DEFAULTS = { - "topic": Pattern("*") - } +class SessionError(Exception): + pass + +class Disconnected(SessionError): + """ + Exception raised when an operation is attempted that is illegal when + disconnected. + """ + pass -def delegate(session): - class Delegate(Client): +class NontransactionalSession(SessionError): + """ + Exception raised when commit or rollback is attempted on a non + transactional session. + """ + pass - def message_transfer(self, cmd): - session._message_transfer(cmd) - return Delegate +class TransactionAborted(SessionError): + pass -class Session(Lockable): +class Session: """ Sessions provide a linear context for sending and receiving @@ -281,18 +265,28 @@ class Session(Lockable): self.connection = connection self.name = name self.started = started + self.transactional = transactional - self._ssn = None + + self.committing = False + self.committed = True + self.aborting = False + self.aborted = False + self.senders = [] self.receivers = [] - self.closing = False + self.outgoing = [] self.incoming = [] - self.closed = False self.unacked = [] - if self.transactional: - self.acked = [] - self._lock = RLock() - self._condition = Condition(self._lock) + self.acked = [] + # XXX: I hate this name. + self.ack_capacity = UNLIMITED + + self.closing = False + self.closed = False + + self._lock = connection._lock + self.running = True self.thread = Thread(target = self.run) self.thread.setDaemon(True) self.thread.start() @@ -300,60 +294,17 @@ class Session(Lockable): def __repr__(self): return "" % self.name - def _attach(self): - self._ssn = self.connection._conn.session(self.name, delegate=delegate(self)) - self._ssn.auto_sync = False - self._ssn.invoke_lock = self._lock - self._ssn.lock = self._lock - self._ssn.condition = self._condition - if self.transactional: - self._ssn.tx_select() - for link in self.senders + self.receivers: - link._link() - - def _disconnected(self): - self._ssn = None - for link in self.senders + self.receivers: - link._disconnected() + def _wait(self, predicate, timeout=None): + return self.connection._wait(predicate, timeout=timeout) - @synchronized - def _message_transfer(self, cmd): - m = Message010(cmd.payload) - m.headers = cmd.headers - m.id = cmd.id - msg = self._decode(m) - rcv = self.receivers[int(cmd.destination)] - msg._receiver = rcv - log.debug("RECV [%s] %s", self, msg) - self.incoming.append(msg) - self.notifyAll() - return INCOMPLETE - - def _decode(self, message): - dp = message.get("delivery_properties") - mp = message.get("message_properties") - ap = mp.application_headers - enc, dec = get_codec(mp.content_type) - content = dec(message.body) - msg = Message(content) - msg.id = mp.message_id - if ap is not None: - msg.to = ap.get("to") - msg.subject = ap.get("subject") - msg.user_id = mp.user_id - if mp.reply_to is not None: - msg.reply_to = reply_to2addr(mp.reply_to) - msg.correlation_id = mp.correlation_id - msg.properties = mp.application_headers - msg.content_type = mp.content_type - msg._transfer_id = message.id - return msg + def _wakeup(self): + self.connection._wakeup() - def _exchange_query(self, address): - # XXX: auto sync hack is to avoid deadlock on future - result = self._ssn.exchange_query(name=address, sync=True) - self._ssn.sync() - return result.get() + def _check_error(self, exc=SessionError): + self.connection._check_error(exc) + + def _ewait(self, predicate, timeout=None, exc=SessionError): + return self.connection._ewait(predicate, timeout, exc) @synchronized def sender(self, target): @@ -368,8 +319,11 @@ class Session(Lockable): """ sender = Sender(self, len(self.senders), target) self.senders.append(sender) - if self._ssn is not None: - sender._link() + self._wakeup() + # XXX: because of the lack of waiting here we can end up getting + # into the driver loop with messages sent for senders that haven't + # been linked yet, something similar can probably happen for + # receivers return sender @synchronized @@ -386,8 +340,7 @@ class Session(Lockable): receiver = Receiver(self, len(self.receivers), source, filter, self.started) self.receivers.append(receiver) - if self._ssn is not None: - receiver._link() + self._wakeup() return receiver @synchronized @@ -415,43 +368,45 @@ class Session(Lockable): @synchronized def _get(self, predicate, timeout=None): - if self.wait(lambda: ((self._peek(predicate) is not None) or self.closing), - timeout): + if self._wait(lambda: ((self._peek(predicate) is not None) or self.closing), + timeout): msg = self._pop(predicate) if msg is not None: + msg._receiver.returned += 1 self.unacked.append(msg) log.debug("RETR [%s] %s", self, msg) return msg return None @synchronized - def acknowledge(self, message=None): + def acknowledge(self, message=None, sync=True): """ Acknowledge the given L{Message}. If message is None, then all unacknowledged messages on the session are acknowledged. @type message: Message @param message: the message to acknowledge or None + @type sync: boolean + @param sync: if true then block until the message(s) are acknowledged """ if message is None: messages = self.unacked[:] else: messages = [message] - ids = RangedSet(*[m._transfer_id for m in messages]) - for range in ids: - self._ssn.receiver._completed.add_range(range) - self._ssn.channel.session_completed(self._ssn.receiver._completed) - self._ssn.message_accept(ids, sync=True) - self._ssn.sync() - for m in messages: - try: - self.unacked.remove(m) - except ValueError: - pass - if self.transactional: - self.acked.append(m) + if self.ack_capacity is not UNLIMITED: + if self.ack_capacity <= 0: + # XXX: this is currently a SendError, maybe it should be a SessionError? + raise InsufficientCapacity("ack_capacity = %s" % self.ack_capacity) + self._wakeup() + self._ewait(lambda: len(self.acked) < self.ack_capacity) + self.unacked.remove(m) + self.acked.append(m) + + self._wakeup() + if sync: + self._ewait(lambda: not [m for m in messages if m in self.acked]) @synchronized def commit(self): @@ -461,11 +416,12 @@ class Session(Lockable): """ if not self.transactional: raise NontransactionalSession() - if self._ssn is None: - raise Disconnected() - self._ssn.tx_commit(sync=True) - del self.acked[:] - self._ssn.sync() + self.committing = True + self._wakeup() + self._ewait(lambda: not self.committing) + if self.aborted: + raise TransactionAborted() + assert self.committed @synchronized def rollback(self): @@ -475,21 +431,10 @@ class Session(Lockable): """ if not self.transactional: raise NontransactionalSession() - if self._ssn is None: - raise Disconnected() - - ids = RangedSet(*[m._transfer_id for m in self.acked + self.unacked + self.incoming]) - for range in ids: - self._ssn.receiver._completed.add_range(range) - self._ssn.channel.session_completed(self._ssn.receiver._completed) - self._ssn.message_release(ids) - self._ssn.tx_rollback(sync=True) - - del self.incoming[:] - del self.unacked[:] - del self.acked[:] - - self._ssn.sync() + self.aborting = True + self._wakeup() + self._ewait(lambda: not self.aborting) + assert self.aborted @synchronized def start(self): @@ -508,7 +453,7 @@ class Session(Lockable): for rcv in self.receivers: rcv.stop() # TODO: think about stopping individual receivers in listen mode - self.wait(lambda: self._peek(self._pred) is None) + self._wait(lambda: self._peek(self._pred) is None) self.started = False def _pred(self, m): @@ -516,6 +461,7 @@ class Session(Lockable): @synchronized def run(self): + self.running = True try: while True: msg = self._get(self._pred) @@ -524,10 +470,10 @@ class Session(Lockable): else: msg._receiver.listener(msg) if self._peek(self._pred) is None: - self.notifyAll() + self.connection._waiter.notifyAll() finally: - self.closed = True - self.notifyAll() + self.running = False + self.connection._waiter.notifyAll() @synchronized def close(self): @@ -538,45 +484,22 @@ class Session(Lockable): link.close() self.closing = True - self.notifyAll() - self.wait(lambda: self.closed) + self._wakeup() + self._ewait(lambda: self.closed and not self.running) while self.thread.isAlive(): self.thread.join(3) self.thread = None - self._ssn.close() - self._ssn = None + # XXX: should be able to express this condition through API calls + self._ewait(lambda: not self.outgoing and not self.acked) self.connection._remove_session(self) -def parse_addr(address): - parts = address.split("/", 1) - if len(parts) == 1: - return parts[0], None - else: - return parts[0], parts[i1] - -def reply_to2addr(reply_to): - if reply_to.routing_key is None: - return reply_to.exchange - elif reply_to.exchange in (None, ""): - return reply_to.routing_key - else: - return "%s/%s" % (reply_to.exchange, reply_to.routing_key) - -class Disconnected(Exception): - """ - Exception raised when an operation is attempted that is illegal when - disconnected. - """ +class SendError(SessionError): pass -class NontransactionalSession(Exception): - """ - Exception raised when commit or rollback is attempted on a non - transactional session. - """ +class InsufficientCapacity(SendError): pass -class Sender(Lockable): +class Sender: """ Sends outgoing messages. @@ -586,100 +509,99 @@ class Sender(Lockable): self.session = session self.index = index self.target = target + self.capacity = UNLIMITED + self.queued = Serial(0) + self.acked = Serial(0) self.closed = False - self._ssn = None - self._exchange = None - self._routing_key = None - self._subject = None self._lock = self.session._lock - self._condition = self.session._condition - - def _link(self): - self._ssn = self.session._ssn - node, self._subject = parse_addr(self.target) - result = self.session._exchange_query(node) - if result.not_found: - # XXX: should check 'create' option - self._ssn.queue_declare(queue=node, durable=False, sync=True) - self._ssn.sync() - self._exchange = "" - self._routing_key = node - else: - self._exchange = node - self._routing_key = self._subject - def _disconnected(self): - self._ssn = None + def _wakeup(self): + self.session._wakeup() + + def _check_error(self, exc=SendError): + self.session._check_error(exc) + + def _ewait(self, predicate, timeout=None, exc=SendError): + return self.session._ewait(predicate, timeout, exc) @synchronized - def send(self, object): + def pending(self): + """ + Returns the number of messages awaiting acknowledgment. + @rtype: int + @return: the number of unacknowledged messages + """ + return self.queued - self.acked + + @synchronized + def send(self, object, sync=True, timeout=None): """ Send a message. If the object passed in is of type L{unicode}, L{str}, L{list}, or L{dict}, it will automatically be wrapped in a L{Message} and sent. If it is of type L{Message}, it will be sent - directly. + directly. If the sender capacity is not L{UNLIMITED} then send + will block until there is available capacity to send the message. + If the timeout parameter is specified, then send will throw an + L{InsufficientCapacity} exception if capacity does not become + available within the specified time. @type object: unicode, str, list, dict, Message @param object: the message or content to send + + @type sync: boolean + @param sync: if true then block until the message is sent + + @type timeout: float + @param timeout: the time to wait for available capacity """ - if self._ssn is None: + if not self.session.connection._connected or self.session.closing: raise Disconnected() if isinstance(object, Message): message = object else: message = Message(object) - # XXX: what if subject is specified for a normal queue? - if self._routing_key is None: - rk = message.subject - else: - rk = self._routing_key - # XXX: do we need to query to figure out how to create the reply-to interoperably? - if message.reply_to: - rt = self._ssn.reply_to(*parse_addr(message.reply_to)) - else: - rt = None - dp = self._ssn.delivery_properties(routing_key=rk) - mp = self._ssn.message_properties(message_id=message.id, - user_id=message.user_id, - reply_to=rt, - correlation_id=message.correlation_id, - content_type=message.content_type, - application_headers=message.properties) - if message.subject is not None: - if mp.application_headers is None: - mp.application_headers = {} - mp.application_headers["subject"] = message.subject - if message.to is not None: - if mp.application_headers is None: - mp.application_headers = {} - mp.application_headers["to"] = message.to - enc, dec = get_codec(message.content_type) - body = enc(message.content) - self._ssn.message_transfer(destination=self._exchange, - message=Message010(dp, mp, body), - sync=True) - log.debug("SENT [%s] %s", self.session, message) - self._ssn.sync() + + if self.capacity is not UNLIMITED: + if self.capacity <= 0: + raise InsufficientCapacity("capacity = %s" % self.capacity) + if not self._ewait(lambda: self.pending() < self.capacity, timeout=timeout): + raise InsufficientCapacity("capacity = %s" % self.capacity) + + # XXX: what if we send the same message to multiple senders? + message._sender = self + self.session.outgoing.append(message) + self.queued += 1 + mno = self.queued + + self._wakeup() + + if sync: + self._ewait(lambda: self.acked >= mno) + assert message not in self.session.outgoing @synchronized def close(self): """ Close the Sender. """ + # XXX: should make driver do something here if not self.closed: self.session.senders.remove(self) self.closed = True -class Empty(Exception): +class ReceiveError(SessionError): + pass + +class Empty(ReceiveError): """ Exception raised by L{Receiver.fetch} when there is no message available within the alloted time. """ pass -class Receiver(Lockable): +class Receiver: """ Receives incoming messages from a remote source. Messages may be @@ -693,43 +615,39 @@ class Receiver(Lockable): self.destination = str(self.index) self.source = source self.filter = filter + self.started = started self.capacity = UNLIMITED + self.granted = Serial(0) + self.drain = False + self.impending = Serial(0) + self.received = Serial(0) + self.returned = Serial(0) + + self.closing = False self.closed = False self.listener = None - self._ssn = None - self._queue = None self._lock = self.session._lock - self._condition = self.session._condition - - def _link(self): - self._ssn = self.session._ssn - result = self.session._exchange_query(self.source) - if result.not_found: - self._queue = self.source - # XXX: should check 'create' option - self._ssn.queue_declare(queue=self._queue, durable=False) - else: - self._queue = "%s.%s" % (self.session.name, self.destination) - self._ssn.queue_declare(queue=self._queue, durable=False, exclusive=True, auto_delete=True) - if self.filter is None: - f = FILTER_DEFAULTS[result.type] - else: - f = self.filter - f._bind(self._ssn, self.source, self._queue) - self._ssn.message_subscribe(queue=self._queue, destination=self.destination, - sync=True) - self._ssn.message_set_flow_mode(self.destination, self._ssn.flow_mode.credit) - self._ssn.sync() - if self.started: - self._start() - def _disconnected(self): - self._ssn = None + def _wakeup(self): + self.session._wakeup() + + def _check_error(self, exc=ReceiveError): + self.session._check_error(exc) + + def _ewait(self, predicate, timeout=None, exc=ReceiveError): + return self.session._ewait(predicate, timeout, exc) @synchronized def pending(self): - return self.session._count(self._pred) + """ + Returns the number of messages available to be fetched by the + application. + + @rtype: int + @return: the number of available messages + """ + return self.received - self.returned def _capacity(self): if not self.started: @@ -762,23 +680,36 @@ class Receiver(Lockable): @type timeout: float @param timeout: the time to wait for a message to be available """ - if self.capacity is not UNLIMITED or not self.started: - self._ssn.message_flow(self.destination, self._ssn.credit_unit.byte, - UNLIMITED.value) - self._ssn.message_flow(self.destination, self._ssn.credit_unit.message, 1) + if self._capacity() == 0: + self.granted = self.returned + 1 + self._wakeup() + self._ewait(lambda: self.impending >= self.granted) msg = self.session._get(self._pred, timeout=timeout) if msg is None: - self._ssn.message_flush(self.destination) - self._start() - self._ssn.sync() + self.drain = True + self.granted = self.received + self._wakeup() + self._ewait(lambda: self.impending == self.received) + self.drain = False + self._grant() + self._wakeup() msg = self.session._get(self._pred, timeout=0) if msg is None: raise Empty() + elif self._capacity() not in (0, UNLIMITED.value): + self.granted += 1 + self._wakeup() return msg - def _start(self): - self._ssn.message_flow(self.destination, self._ssn.credit_unit.byte, UNLIMITED.value) - self._ssn.message_flow(self.destination, self._ssn.credit_unit.message, self._capacity()) + def _grant(self): + if self.started: + if self.capacity is UNLIMITED: + self.granted = UNLIMITED + else: + self.granted = self.received + self._capacity() + else: + self.granted = self.received + @synchronized def start(self): @@ -786,34 +717,31 @@ class Receiver(Lockable): Start incoming message delivery for this receiver. """ self.started = True - if self._ssn is not None: - self._start() - - def _stop(self): - self._ssn.message_stop(self.destination) + self._grant() + self._wakeup() @synchronized def stop(self): """ Stop incoming message delivery for this receiver. """ - if self._ssn is not None: - self._stop() self.started = False + self._grant() + self._wakeup() + self._ewait(lambda: self.impending == self.received) @synchronized def close(self): """ Close the receiver. """ - if not self.closed: - self.closed = True - self._ssn.message_cancel(self.destination, sync=True) - self._ssn.sync() + self.closing = True + self._wakeup() + try: + self._ewait(lambda: self.closed) + finally: self.session.receivers.remove(self) - - def codec(name): type = PRIMITIVE[name] @@ -889,6 +817,7 @@ class Message: self.to = None self.reply_to = None self.correlation_id = None + self.durable = False self.properties = {} self.content_type = get_type(content) self.content = content @@ -896,5 +825,7 @@ class Message: def __repr__(self): return "Message(%r)" % self.content -__all__ = ["Connection", "Pattern", "Session", "Sender", "Receiver", "Message", - "Empty", "timestamp", "uuid4"] +__all__ = ["Connection", "Session", "Sender", "Receiver", "Pattern", "Message", + "ConnectionError", "ConnectError", "SessionError", "Disconnected", + "SendError", "InsufficientCapacity", "ReceiveError", "Empty", + "timestamp", "uuid4", "UNLIMITED", "AMQP_PORT", "AMQPS_PORT"] diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index 4413a22899..2f1bd81bd4 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -146,7 +146,8 @@ class Session(command_invoker()): if self._closing: raise SessionClosed() - if self.channel == None: + ch = self.channel + if ch == None: raise SessionDetached() if op == MessageTransfer: @@ -162,14 +163,12 @@ class Session(command_invoker()): cmd = op(*args, **kwargs) cmd.sync = self.auto_sync or cmd.sync self.need_sync = not cmd.sync - cmd.channel = self.channel.id + cmd.channel = ch.id if op.RESULT: result = Future(exception=SessionException) self.results[self.sender.next_id] = result - log.debug("SENDING %s", cmd) - self.send(cmd) log.debug("SENT %s", cmd) @@ -245,13 +244,16 @@ class Sender: self._completed = RangedSet() def send(self, cmd): + ch = self.session.channel + if ch is None: + raise SessionDetached() cmd.id = self.next_id self.next_id += 1 if self.session.send_id: self.session.send_id = False - self.session.channel.session_command_point(cmd.id, 0) + ch.session_command_point(cmd.id, 0) self.commands.append(cmd) - self.session.channel.connection.write_op(cmd) + ch.connection.write_op(cmd) def completed(self, commands): idx = 0 diff --git a/qpid/python/qpid/tests/messaging.py b/qpid/python/qpid/tests/messaging.py index 7706ebbabe..7623c1f93b 100644 --- a/qpid/python/qpid/tests/messaging.py +++ b/qpid/python/qpid/tests/messaging.py @@ -23,7 +23,8 @@ import time from qpid.tests import Test from qpid.harness import Skipped -from qpid.messaging import Connection, ConnectError, Disconnected, Empty, Message, UNLIMITED, uuid4 +from qpid.messaging import Connection, ConnectError, Disconnected, Empty, \ + InsufficientCapacity, Message, UNLIMITED, uuid4 from Queue import Queue, Empty as QueueEmpty class Base(Test): @@ -71,19 +72,25 @@ class Base(Test): ssn.acknowledge() assert msg.content == content, "expected %r, got %r" % (content, msg.content) - def drain(self, rcv, limit=None): + def drain(self, rcv, limit=None, timeout=0, expected=None): contents = [] try: while limit is None or len(contents) < limit: - contents.append(rcv.fetch(0).content) + contents.append(rcv.fetch(timeout=timeout).content) except Empty: pass + if expected is not None: + assert expected == contents, "expected %s, got %s" % (expected, contents) return contents def assertEmpty(self, rcv): contents = self.drain(rcv) assert len(contents) == 0, "%s is supposed to be empty: %s" % (rcv, contents) + def assertPending(self, rcv, expected): + p = rcv.pending() + assert p == expected, "expected %s, got %s" % (expected, p) + def sleep(self): time.sleep(self.delay()) @@ -107,7 +114,8 @@ class SetupTests(Base): try: self.conn = Connection.open("localhost", 0) assert False, "connect succeeded" - except ConnectError: + except ConnectError, e: + # XXX: should verify that e includes appropriate diagnostic info pass class ConnectionTests(Base): @@ -219,26 +227,27 @@ class SessionTests(Base): # XXX, we need a convenient way to assert that required queues are # empty on setup, and possibly also to drain queues on teardown - def testAcknowledge(self): + def ackTest(self, acker, ack_capacity=None): # send a bunch of messages snd = self.ssn.sender("test-ack-queue") - tid = "a" - contents = ["testAcknowledge[%s, %s]" % (i, tid) for i in range(10)] + contents = [self.content("ackTest", i) for i in range(15)] for c in contents: snd.send(c) # drain the queue, verify the messages are there and then close # without acking rcv = self.ssn.receiver(snd.target) - assert contents == self.drain(rcv) + self.drain(rcv, expected=contents) self.ssn.close() # drain the queue again, verify that they are all the messages # were requeued, and ack this time before closing self.ssn = self.conn.session() + if ack_capacity is not None: + self.ssn.ack_capacity = ack_capacity rcv = self.ssn.receiver("test-ack-queue") - assert contents == self.drain(rcv) - self.ssn.acknowledge() + self.drain(rcv, expected=contents) + acker(self.ssn) self.ssn.close() # drain the queue a final time and verify that the messages were @@ -247,6 +256,33 @@ class SessionTests(Base): rcv = self.ssn.receiver("test-ack-queue") self.assertEmpty(rcv) + def testAcknowledge(self): + self.ackTest(lambda ssn: ssn.acknowledge()) + + def testAcknowledgeAsync(self): + self.ackTest(lambda ssn: ssn.acknowledge(sync=False)) + + def testAcknowledgeAsyncAckCap0(self): + try: + try: + self.ackTest(lambda ssn: ssn.acknowledge(sync=False), 0) + assert False, "acknowledge shouldn't succeed with ack_capacity of zero" + except InsufficientCapacity: + pass + finally: + self.ssn.ack_capacity = UNLIMITED + self.drain(self.ssn.receiver("test-ack-queue")) + self.ssn.acknowledge() + + def testAcknowledgeAsyncAckCap1(self): + self.ackTest(lambda ssn: ssn.acknowledge(sync=False), 1) + + def testAcknowledgeAsyncAckCap5(self): + self.ackTest(lambda ssn: ssn.acknowledge(sync=False), 5) + + def testAcknowledgeAsyncAckCapUNLIMITED(self): + self.ackTest(lambda ssn: ssn.acknowledge(sync=False), UNLIMITED) + def send(self, ssn, queue, base, count=1): snd = ssn.sender(queue) contents = [] @@ -319,7 +355,8 @@ class SessionTests(Base): txssn.acknowledge() else: txssn.rollback() - assert contents == self.drain(txrcv) + drained = self.drain(txrcv) + assert contents == drained, "expected %s, got %s" % (contents, drained) txssn.acknowledge() txssn.rollback() assert contents == self.drain(txrcv) @@ -401,9 +438,9 @@ class ReceiverTests(Base): elapsed = time.time() - start assert elapsed >= self.delay() - one = self.send("testListen", 1) - two = self.send("testListen", 2) - three = self.send("testListen", 3) + one = self.send("testFetch", 1) + two = self.send("testFetch", 2) + three = self.send("testFetch", 3) msg = self.rcv.fetch(0) assert msg.content == one msg = self.rcv.fetch(self.delay()) @@ -467,34 +504,35 @@ class ReceiverTests(Base): def testCapacity(self): self.rcv.capacity = 5 self.rcv.start() - assert self.rcv.pending() == 0 + self.assertPending(self.rcv, 0) for i in range(15): self.send("testCapacity", i) self.sleep() - assert self.rcv.pending() == 5 + self.assertPending(self.rcv, 5) self.drain(self.rcv, limit = 5) self.sleep() - assert self.rcv.pending() == 5 + self.assertPending(self.rcv, 5) - self.drain(self.rcv) - assert self.rcv.pending() == 0 + drained = self.drain(self.rcv) + assert len(drained) == 10 + self.assertPending(self.rcv, 0) self.ssn.acknowledge() def testCapacityUNLIMITED(self): self.rcv.capacity = UNLIMITED self.rcv.start() - assert self.rcv.pending() == 0 + self.assertPending(self.rcv, 0) for i in range(10): self.send("testCapacityUNLIMITED", i) self.sleep() - assert self.rcv.pending() == 10 + self.assertPending(self.rcv, 10) self.drain(self.rcv) - assert self.rcv.pending() == 0 + self.assertPending(self.rcv, 0) self.ssn.acknowledge() @@ -535,6 +573,48 @@ class SenderTests(Base): def testSendMap(self): self.checkContent({"testSendMap": self.test_id, "pie": "blueberry", "pi": 3.14}) + def asyncTest(self, capacity): + self.snd.capacity = capacity + msgs = [self.content("asyncTest", i) for i in range(15)] + for m in msgs: + self.snd.send(m, sync=False) + drained = self.drain(self.rcv, timeout=self.delay()) + assert msgs == drained, "expected %s, got %s" % (msgs, drained) + self.ssn.acknowledge() + + def testSendAsyncCapacity0(self): + try: + self.asyncTest(0) + assert False, "send shouldn't succeed with zero capacity" + except InsufficientCapacity: + # this is expected + pass + + def testSendAsyncCapacity1(self): + self.asyncTest(1) + + def testSendAsyncCapacity5(self): + self.asyncTest(5) + + def testSendAsyncCapacityUNLIMITED(self): + self.asyncTest(UNLIMITED) + + def testCapacityTimeout(self): + self.snd.capacity = 1 + msgs = [] + caught = False + while len(msgs) < 100: + m = self.content("testCapacity", len(msgs)) + try: + self.snd.send(m, sync=False, timeout=0) + msgs.append(m) + except InsufficientCapacity: + caught = True + break + self.drain(self.rcv, expected=msgs) + self.ssn.acknowledge() + assert caught, "did not exceed capacity" + class MessageTests(Base): def testCreateString(self): diff --git a/qpid/python/qpid/util.py b/qpid/python/qpid/util.py index c46716b88f..3409d777f9 100644 --- a/qpid/python/qpid/util.py +++ b/qpid/python/qpid/util.py @@ -134,3 +134,9 @@ class URL: if self.port: s += ":%s" % self.port return s + +def default(value, default): + if value is None: + return default + else: + return value diff --git a/qpid/python/qpid_config.py b/qpid/python/qpid_config.py index 3cf6b69b7e..d740a53dfe 100644 --- a/qpid/python/qpid_config.py +++ b/qpid/python/qpid_config.py @@ -19,7 +19,7 @@ import os -qpid_home = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -amqp_spec = os.path.join(qpid_home, "specs", "amqp.0-10-qpid-errata.xml") -amqp_spec_0_8 = os.path.join(qpid_home, "specs", "amqp.0-8.xml") -amqp_spec_0_9 = os.path.join(qpid_home, "specs", "amqp.0-9.xml") +AMQP_SPEC_DIR=os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "specs") +amqp_spec = os.path.join(AMQP_SPEC_DIR, "amqp.0-10-qpid-errata.xml") +amqp_spec_0_8 = os.path.join(AMQP_SPEC_DIR, "amqp.0-8.xml") +amqp_spec_0_9 = os.path.join(AMQP_SPEC_DIR, "amqp.0-9.xml") diff --git a/qpid/python/tests/datatypes.py b/qpid/python/tests/datatypes.py index 1a60bb4107..b00e5e78f8 100644 --- a/qpid/python/tests/datatypes.py +++ b/qpid/python/tests/datatypes.py @@ -54,6 +54,19 @@ class SerialTest(TestCase): d[serial(0)] = "zero" assert d[0] == "zero" + def testAdd(self): + assert serial(2) + 2 == serial(4) + assert serial(2) + 2 == 4 + + def testSub(self): + delta = serial(4) - serial(2) + assert isinstance(delta, int) or isinstance(delta, long) + assert delta == 2 + + delta = serial(4) - 2 + assert isinstance(delta, Serial) + assert delta == serial(2) + class RangedSetTest(TestCase): def check(self, ranges): diff --git a/qpid/python/tests_0-10/alternate_exchange.py b/qpid/python/tests_0-10/alternate_exchange.py index 3b75145907..4d8617eb8e 100644 --- a/qpid/python/tests_0-10/alternate_exchange.py +++ b/qpid/python/tests_0-10/alternate_exchange.py @@ -141,7 +141,61 @@ class AlternateExchangeTests(TestBase010): session.exchange_delete(exchange="e") session.exchange_delete(exchange="alternate") self.assertEquals(530, e.args[0].error_code) - + + + def test_modify_existing_exchange_alternate(self): + """ + Ensure that attempting to modify an exhange to change + the alternate throws an exception + """ + session = self.session + session.exchange_declare(exchange="alt1", type="direct") + session.exchange_declare(exchange="alt2", type="direct") + session.exchange_declare(exchange="onealternate", type="fanout", alternate_exchange="alt1") + try: + # attempt to change the alternate on an already existing exchange + session.exchange_declare(exchange="onealternate", type="fanout", alternate_exchange="alt2") + self.fail("Expected changing an alternate on an existing exchange to fail") + except SessionException, e: + self.assertEquals(530, e.args[0].error_code) + session = self.conn.session("alternate", 2) + session.exchange_delete(exchange="onealternate") + session.exchange_delete(exchange="alt2") + session.exchange_delete(exchange="alt1") + + + def test_add_alternate_to_exchange(self): + """ + Ensure that attempting to modify an exhange by adding + an alternate throws an exception + """ + session = self.session + session.exchange_declare(exchange="alt1", type="direct") + session.exchange_declare(exchange="noalternate", type="fanout") + try: + # attempt to add an alternate on an already existing exchange + session.exchange_declare(exchange="noalternate", type="fanout", alternate_exchange="alt1") + self.fail("Expected adding an alternate on an existing exchange to fail") + except SessionException, e: + self.assertEquals(530, e.args[0].error_code) + session = self.conn.session("alternate", 2) + session.exchange_delete(exchange="noalternate") + session.exchange_delete(exchange="alt1") + + + def test_del_alternate_to_exchange(self): + """ + Ensure that attempting to modify an exhange by declaring + it again without an alternate does nothing + """ + session = self.session + session.exchange_declare(exchange="alt1", type="direct") + session.exchange_declare(exchange="onealternate", type="fanout", alternate_exchange="alt1") + # attempt to re-declare without an alternate - silently ignore + session.exchange_declare(exchange="onealternate", type="fanout" ) + session.exchange_delete(exchange="onealternate") + session.exchange_delete(exchange="alt1") + def assertEmpty(self, queue): try: diff --git a/qpid/python/tests_0-10/management.py b/qpid/python/tests_0-10/management.py index 51c2a687cb..53573f309e 100644 --- a/qpid/python/tests_0-10/management.py +++ b/qpid/python/tests_0-10/management.py @@ -295,3 +295,25 @@ class ManagementTest (TestBase010): sleep(1) self.assertEqual(handler.check(), "pass") + def test_connection_close(self): + """ + Test management method for closing connection + """ + self.startQmf() + conn = self.connect() + session = conn.session("my-named-session") + + #using qmf find named session and close the corresponding connection: + qmf_ssn_object = self.qmf.getObjects(_class="session", name="my-named-session")[0] + qmf_ssn_object._connectionRef_.close() + + #check that connection is closed + try: + conn.session("another-session") + self.fail("Expected failure from closed connection") + except: None + + #make sure that the named session has been closed and the name can be re-used + conn = self.connect() + session = conn.session("my-named-session") + session.queue_declare(queue="whatever", exclusive=True, auto_delete=True) diff --git a/qpid/python/tests_0-9/queue.py b/qpid/python/tests_0-9/queue.py index de1153307c..e7fe0b3ed4 100644 --- a/qpid/python/tests_0-9/queue.py +++ b/qpid/python/tests_0-9/queue.py @@ -6,9 +6,9 @@ # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -19,11 +19,137 @@ from qpid.client import Client, Closed from qpid.queue import Empty from qpid.content import Content -from qpid.testlib import TestBase +from qpid.testlib import testrunner, TestBase class QueueTests(TestBase): """Tests for 'methods' on the amqp queue 'class'""" + def test_purge(self): + """ + Test that the purge method removes messages from the queue + """ + channel = self.channel + #setup, declare a queue and add some messages to it: + channel.exchange_declare(exchange="test-exchange", type="direct") + channel.queue_declare(queue="test-queue", exclusive=True) + channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") + channel.message_transfer(destination="test-exchange", routing_key="key", body="one") + channel.message_transfer(destination="test-exchange", routing_key="key", body="two") + channel.message_transfer(destination="test-exchange", routing_key="key", body="three") + + #check that the queue now reports 3 messages: + reply = channel.queue_declare(queue="test-queue") + self.assertEqual(3, reply.message_count) + + #now do the purge, then test that three messages are purged and the count drops to 0 + reply = channel.queue_purge(queue="test-queue"); + self.assertEqual(3, reply.message_count) + reply = channel.queue_declare(queue="test-queue") + self.assertEqual(0, reply.message_count) + + #send a further message and consume it, ensuring that the other messages are really gone + channel.message_transfer(destination="test-exchange", routing_key="key", body="four") + channel.message_consume(queue="test-queue", destination="tag", no_ack=True) + queue = self.client.queue("tag") + msg = queue.get(timeout=1) + self.assertEqual("four", msg.body) + + #check error conditions (use new channels): + channel = self.client.channel(2) + channel.channel_open() + try: + #queue specified but doesn't exist: + channel.queue_purge(queue="invalid-queue") + self.fail("Expected failure when purging non-existent queue") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + channel = self.client.channel(3) + channel.channel_open() + try: + #queue not specified and none previously declared for channel: + channel.queue_purge() + self.fail("Expected failure when purging unspecified queue") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + #cleanup + other = self.connect() + channel = other.channel(1) + channel.channel_open() + channel.exchange_delete(exchange="test-exchange") + + def test_declare_exclusive(self): + """ + Test that the exclusive field is honoured in queue.declare + """ + # TestBase.setUp has already opened channel(1) + c1 = self.channel + # Here we open a second separate connection: + other = self.connect() + c2 = other.channel(1) + c2.channel_open() + + #declare an exclusive queue: + c1.queue_declare(queue="exclusive-queue", exclusive="True") + try: + #other connection should not be allowed to declare this: + c2.queue_declare(queue="exclusive-queue", exclusive="True") + self.fail("Expected second exclusive queue_declare to raise a channel exception") + except Closed, e: + self.assertChannelException(405, e.args[0]) + + + def test_declare_passive(self): + """ + Test that the passive field is honoured in queue.declare + """ + channel = self.channel + #declare an exclusive queue: + channel.queue_declare(queue="passive-queue-1", exclusive="True") + channel.queue_declare(queue="passive-queue-1", passive="True") + try: + #other connection should not be allowed to declare this: + channel.queue_declare(queue="passive-queue-2", passive="True") + self.fail("Expected passive declaration of non-existant queue to raise a channel exception") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + + def test_bind(self): + """ + Test various permutations of the queue.bind method + """ + channel = self.channel + channel.queue_declare(queue="queue-1", exclusive="True") + + #straightforward case, both exchange & queue exist so no errors expected: + channel.queue_bind(queue="queue-1", exchange="amq.direct", routing_key="key1") + + #bind the default queue for the channel (i.e. last one declared): + channel.queue_bind(exchange="amq.direct", routing_key="key2") + + #use the queue name where neither routing key nor queue are specified: + channel.queue_bind(exchange="amq.direct") + + #try and bind to non-existant exchange + try: + channel.queue_bind(queue="queue-1", exchange="an-invalid-exchange", routing_key="key1") + self.fail("Expected bind to non-existant exchange to fail") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + #need to reopen a channel: + channel = self.client.channel(2) + channel.channel_open() + + #try and bind non-existant queue: + try: + channel.queue_bind(queue="queue-2", exchange="amq.direct", routing_key="key1") + self.fail("Expected bind of non-existant queue to fail") + except Closed, e: + self.assertChannelException(404, e.args[0]) + def test_unbind_direct(self): self.unbind_test(exchange="amq.direct", routing_key="key") @@ -39,12 +165,12 @@ class QueueTests(TestBase): def unbind_test(self, exchange, routing_key="", args=None, headers={}): #bind two queues and consume from them channel = self.channel - + channel.queue_declare(queue="queue-1", exclusive="True") channel.queue_declare(queue="queue-2", exclusive="True") - channel.basic_consume(queue="queue-1", consumer_tag="queue-1", no_ack=True) - channel.basic_consume(queue="queue-2", consumer_tag="queue-2", no_ack=True) + channel.message_consume(queue="queue-1", destination="queue-1", no_ack=True) + channel.message_consume(queue="queue-2", destination="queue-2", no_ack=True) queue1 = self.client.queue("queue-1") queue2 = self.client.queue("queue-2") @@ -53,29 +179,130 @@ class QueueTests(TestBase): channel.queue_bind(exchange=exchange, queue="queue-2", routing_key=routing_key, arguments=args) #send a message that will match both bindings - channel.basic_publish(exchange=exchange, routing_key=routing_key, - content=Content("one", properties={"headers": headers})) - + channel.message_transfer(destination=exchange, routing_key=routing_key, application_headers=headers, body="one") + #unbind first queue channel.queue_unbind(exchange=exchange, queue="queue-1", routing_key=routing_key, arguments=args) - + #send another message - channel.basic_publish(exchange=exchange, routing_key=routing_key, - content=Content("two", properties={"headers": headers})) + channel.message_transfer(destination=exchange, routing_key=routing_key, application_headers=headers, body="two") #check one queue has both messages and the other has only one - self.assertEquals("one", queue1.get(timeout=1).content.body) + self.assertEquals("one", queue1.get(timeout=1).body) try: msg = queue1.get(timeout=1) self.fail("Got extra message: %s" % msg.body) except Empty: pass - self.assertEquals("one", queue2.get(timeout=1).content.body) - self.assertEquals("two", queue2.get(timeout=1).content.body) + self.assertEquals("one", queue2.get(timeout=1).body) + self.assertEquals("two", queue2.get(timeout=1).body) try: msg = queue2.get(timeout=1) self.fail("Got extra message: " + msg) - except Empty: pass + except Empty: pass + + + def test_delete_simple(self): + """ + Test core queue deletion behaviour + """ + channel = self.channel + + #straight-forward case: + channel.queue_declare(queue="delete-me") + channel.message_transfer(routing_key="delete-me", body="a") + channel.message_transfer(routing_key="delete-me", body="b") + channel.message_transfer(routing_key="delete-me", body="c") + reply = channel.queue_delete(queue="delete-me") + self.assertEqual(3, reply.message_count) + #check that it has gone be declaring passively + try: + channel.queue_declare(queue="delete-me", passive="True") + self.fail("Queue has not been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + #check attempted deletion of non-existant queue is handled correctly: + channel = self.client.channel(2) + channel.channel_open() + try: + channel.queue_delete(queue="i-dont-exist", if_empty="True") + self.fail("Expected delete of non-existant queue to fail") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + + + def test_delete_ifempty(self): + """ + Test that if_empty field of queue_delete is honoured + """ + channel = self.channel + + #create a queue and add a message to it (use default binding): + channel.queue_declare(queue="delete-me-2") + channel.queue_declare(queue="delete-me-2", passive="True") + channel.message_transfer(routing_key="delete-me-2", body="message") + + #try to delete, but only if empty: + try: + channel.queue_delete(queue="delete-me-2", if_empty="True") + self.fail("Expected delete if_empty to fail for non-empty queue") + except Closed, e: + self.assertChannelException(406, e.args[0]) + + #need new channel now: + channel = self.client.channel(2) + channel.channel_open() + + #empty queue: + channel.message_consume(destination="consumer_tag", queue="delete-me-2", no_ack=True) + queue = self.client.queue("consumer_tag") + msg = queue.get(timeout=1) + self.assertEqual("message", msg.body) + channel.message_cancel(destination="consumer_tag") + + #retry deletion on empty queue: + channel.queue_delete(queue="delete-me-2", if_empty="True") + + #check that it has gone by declaring passively: + try: + channel.queue_declare(queue="delete-me-2", passive="True") + self.fail("Queue has not been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + def test_delete_ifunused(self): + """ + Test that if_unused field of queue_delete is honoured + """ + channel = self.channel + + #create a queue and register a consumer: + channel.queue_declare(queue="delete-me-3") + channel.queue_declare(queue="delete-me-3", passive="True") + channel.message_consume(destination="consumer_tag", queue="delete-me-3", no_ack=True) + + #need new channel now: + channel2 = self.client.channel(2) + channel2.channel_open() + #try to delete, but only if empty: + try: + channel2.queue_delete(queue="delete-me-3", if_unused="True") + self.fail("Expected delete if_unused to fail for queue with existing consumer") + except Closed, e: + self.assertChannelException(406, e.args[0]) + + + channel.message_cancel(destination="consumer_tag") + channel.queue_delete(queue="delete-me-3", if_unused="True") + #check that it has gone by declaring passively: + try: + channel.queue_declare(queue="delete-me-3", passive="True") + self.fail("Queue has not been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + def test_autodelete_shared(self): """ @@ -109,3 +336,5 @@ class QueueTests(TestBase): self.fail("Expected queue to have been deleted") except Closed, e: self.assertChannelException(404, e.args[0]) + + diff --git a/qpid/specs/management-schema.xml b/qpid/specs/management-schema.xml index bc38350a45..e72ba1cdd7 100644 --- a/qpid/specs/management-schema.xml +++ b/qpid/specs/management-schema.xml @@ -164,11 +164,13 @@ =============================================================== --> - - - - - + + + + + + + diff --git a/qpid/wcf/QpidWcf.sln b/qpid/wcf/QpidWcf.sln new file mode 100644 index 0000000000..54d8dd7b0b --- /dev/null +++ b/qpid/wcf/QpidWcf.sln @@ -0,0 +1,114 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License +# + +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AmqpTypes", "src\Apache\Qpid\AmqpTypes\AmqpTypes.csproj", "{820BFC34-A40F-46BA-B86B-05334854CA17}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Interop", "src\Apache\Qpid\Interop\Interop.vcproj", "{C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}" + ProjectSection(ProjectDependencies) = postProject + {820BFC34-A40F-46BA-B86B-05334854CA17} = {820BFC34-A40F-46BA-B86B-05334854CA17} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FunctionalTests", "test\Apache\Qpid\Test\Channel\Functional\FunctionalTests.csproj", "{E2D8C779-E417-40BA-BEE1-EE034268482F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Channel", "src\Apache\Qpid\Channel\Channel.csproj", "{8AABAB30-7D1E-4539-B7D1-05450262BAD2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {820BFC34-A40F-46BA-B86B-05334854CA17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {820BFC34-A40F-46BA-B86B-05334854CA17}.Debug|Any CPU.Build.0 = Debug|Any CPU + {820BFC34-A40F-46BA-B86B-05334854CA17}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {820BFC34-A40F-46BA-B86B-05334854CA17}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {820BFC34-A40F-46BA-B86B-05334854CA17}.Debug|Win32.ActiveCfg = Debug|Any CPU + {820BFC34-A40F-46BA-B86B-05334854CA17}.Debug|x64.ActiveCfg = Debug|Any CPU + {820BFC34-A40F-46BA-B86B-05334854CA17}.Debug|x86.ActiveCfg = Debug|Any CPU + {820BFC34-A40F-46BA-B86B-05334854CA17}.Release|Any CPU.ActiveCfg = Release|Any CPU + {820BFC34-A40F-46BA-B86B-05334854CA17}.Release|Any CPU.Build.0 = Release|Any CPU + {820BFC34-A40F-46BA-B86B-05334854CA17}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {820BFC34-A40F-46BA-B86B-05334854CA17}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {820BFC34-A40F-46BA-B86B-05334854CA17}.Release|Win32.ActiveCfg = Release|Any CPU + {820BFC34-A40F-46BA-B86B-05334854CA17}.Release|x64.ActiveCfg = Release|Any CPU + {820BFC34-A40F-46BA-B86B-05334854CA17}.Release|x86.ActiveCfg = Release|Any CPU + {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Debug|Any CPU.Build.0 = Debug|Win32 + {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Debug|Win32.ActiveCfg = Debug|Win32 + {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Debug|Win32.Build.0 = Debug|Win32 + {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Debug|x64.ActiveCfg = Debug|Win32 + {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Debug|x86.ActiveCfg = Debug|Win32 + {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Release|Any CPU.ActiveCfg = Release|Win32 + {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Release|Mixed Platforms.Build.0 = Release|Win32 + {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Release|Win32.ActiveCfg = Release|Win32 + {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Release|Win32.Build.0 = Release|Win32 + {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Release|x64.ActiveCfg = Release|Win32 + {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Release|x86.ActiveCfg = Release|Win32 + {E2D8C779-E417-40BA-BEE1-EE034268482F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2D8C779-E417-40BA-BEE1-EE034268482F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2D8C779-E417-40BA-BEE1-EE034268482F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {E2D8C779-E417-40BA-BEE1-EE034268482F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {E2D8C779-E417-40BA-BEE1-EE034268482F}.Debug|Win32.ActiveCfg = Debug|Any CPU + {E2D8C779-E417-40BA-BEE1-EE034268482F}.Debug|x64.ActiveCfg = Debug|Any CPU + {E2D8C779-E417-40BA-BEE1-EE034268482F}.Debug|x64.Build.0 = Debug|Any CPU + {E2D8C779-E417-40BA-BEE1-EE034268482F}.Debug|x86.ActiveCfg = Debug|Any CPU + {E2D8C779-E417-40BA-BEE1-EE034268482F}.Debug|x86.Build.0 = Debug|Any CPU + {E2D8C779-E417-40BA-BEE1-EE034268482F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2D8C779-E417-40BA-BEE1-EE034268482F}.Release|Any CPU.Build.0 = Release|Any CPU + {E2D8C779-E417-40BA-BEE1-EE034268482F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {E2D8C779-E417-40BA-BEE1-EE034268482F}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {E2D8C779-E417-40BA-BEE1-EE034268482F}.Release|Win32.ActiveCfg = Release|Any CPU + {E2D8C779-E417-40BA-BEE1-EE034268482F}.Release|x64.ActiveCfg = Release|Any CPU + {E2D8C779-E417-40BA-BEE1-EE034268482F}.Release|x64.Build.0 = Release|Any CPU + {E2D8C779-E417-40BA-BEE1-EE034268482F}.Release|x86.ActiveCfg = Release|Any CPU + {E2D8C779-E417-40BA-BEE1-EE034268482F}.Release|x86.Build.0 = Release|Any CPU + {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Debug|Win32.ActiveCfg = Debug|Any CPU + {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Debug|x64.ActiveCfg = Debug|Any CPU + {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Debug|x86.ActiveCfg = Debug|Any CPU + {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Release|Any CPU.Build.0 = Release|Any CPU + {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Release|Win32.ActiveCfg = Release|Any CPU + {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Release|x64.ActiveCfg = Release|Any CPU + {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Release|x86.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/qpid/wcf/ReadMe.txt b/qpid/wcf/ReadMe.txt new file mode 100644 index 0000000000..0ef3e06ce5 --- /dev/null +++ b/qpid/wcf/ReadMe.txt @@ -0,0 +1,162 @@ +1. WCF supported features +========================= + +1. WCF service model programming using one way contracts +2. WCF channel model programming using IInputChannel and IOutputChannel based factories +3. Programmatic access to AMQP message properties on WCF messages +4. AMQP version 0-10 (as provided by the Qpid C++ native client library) +5. Shared connections for multiple channels based on binding parameters +6. WCF to WCF applications (using SOAP message encoders) +7. WCF to non-WCF applications (using raw content encoders) +8. Rudimentary AMQP type support for headers (Int and String) +9. Channel functional tests using NUnit +10. Programming samples + + +2. Planned features (not yet available) +======================================= + +1. Full AMQP type support, including maps and arrays +2. System.Transactions integration (local and distributed with dynamic escalation) +3. Prefetch window for inbound messages +4. Shared sessions +5. Connection failover with AMQP broker clusters +6. Temporary queues +7. Broker management +8. System logging and tracing +9. CMake build system support +10. Transport and message based security + + +3. Prerequisites +================ + +1. Qpid C++ client and common libraries for Windows including BOOST +Ensure the location of the Boost library (e.g. %BOOST_ROOT%\lib) is +included in your PATH environment variable. + +2. .NET Framework 3.5 SP1 +Install the .NET Framework from http://www.microsoft.com/net/ + +3. Windows SDK +Install the Windows SDK for the version of Windows that you are using +from http://msdn.microsoft.com/en-us/windows/bb980924.aspx + +4. NUnit +Install NUnit from http://www.nunit.org + +NOTE: In the following instructions %QPID_ROOT% refers to the root of +qpid source code location e.g. C:\trunk\qpid + +5. Build Qpid cpp +Run CMake and choose "%QPID_ROOT%\cpp\build" as the location for "Where to +build the binaries". Build at least the "qpidd", "qpidclient" and +"qpidcommon" projects. + + +4. Building the solution file +============================= + +Option 1: Using MSBuild + +1. %systemroot%\Microsoft.NET\Framework\v3.5\MSBuild.exe %QPID_ROOT%\wcf\QpidWcf.sln +2. %systemroot%\Microsoft.NET\Framework\v3.5\MSBuild.exe %QPID_ROOT%\wcf\tools\QCreate\QCreate.sln + + +Option 2: Using Visual Studio 2008 (the Professional Edition, Team +System Development Edition, or Team System Team Suite SKU) + +1. Open the solution file QpidWcf.sln in Visual Studio. +2. Make sure that the reference to 'nunit.framework.dll' by the 'FunctionalTests' + project is appropriately resolved. +3. Select the Debug configuration. +3. Right-click the solution file in the Solution Explorer and select 'Build Solution'. +4. Follow the above steps to build %QPID_ROOT%\wcf\tools\QCreate.sln as well. + + +5. Executing tests +================== + +1. Make sure that the batch file + %QPID_ROOT%\wcf\test\Apache\Qpid\Test\Channel\Functional\RunTests.bat has the correct + values for the nunit_exe, qpid_dll_location and configuration_name variables as per + your installation. +2. Start the qpid broker from the qpid build folder e.g. %QPID_ROOT%\cpp\build\src\Debug. +3. Execute RunTests.bat from its location e.g. %QPID_ROOT%\wcf\test\Apache\Qpid\Test\Channel\Functional. + + +6. Building and executing samples +================================= + +WCFToWCFDirect + +1. Copy the dlls Apache.Qpid.Channel.dll and Apache.Qpid.Interop.dll that you built + in step 2 to the %QPID_ROOT%\wcf\samples\Channel\WCFToWCFDirect folder. + +2. Build the solution WCFToWCFDirect.sln. + +3. Copy qpidclient.dll and qpidcommon.dll from the Qpid build folder + e.g. %QPID_ROOT%\cpp\build\src\Debug to the same location as the exe files + e.g. bin\Debug of each of the projects. These dlls are needed at runtime. + +4. Copy qpidclient.dll and qpidcommon.dll to %QPID_ROOT%\wcf\tools\QCreate\Debug folder. + +5. Start the qpid broker from the qpid build folder e.g. %QPID_ROOT%\cpp\build\src\Debug. + +6. Create queue required using the QCreate tool located at + %QPID_ROOT%\wcf\tools\QCreate\Debug. The syntax is QCreate %QPID_ROOT%. For + this sample you should do + + QCreate amq.direct routing_key message_queue + +7. Start Service.exe from + %QPID_ROOT%\wcf\samples\Channel\WCFToWCFDirect\Service\bin\Debug. + +8. Start Client.exe from + %QPID_ROOT%\wcf\samples\Channel\WCFToWCFDirect\Client\bin\Debug. + + +WCFToWCFPubSub + +1. Copy the dlls Apache.Qpid.Channel.dll and Apache.Qpid.Interop.dll that you built + in step 2 to the %QPID_ROOT%\wcf\samples\Channel\WCFToWCFPubSub folder. + +2. Build the solution WCFToWCFPubSub.sln. + +3. Copy qpidclient.dll and qpidcommon.dll from the Qpid build folder + e.g. %QPID_ROOT%\cpp\build\src\Debug to the same location as the exe files + e.g. bin\Debug of each of the projects. These dlls are needed at runtime. + +4. Copy qpidclient.dll and qpidcommon.dll to %QPID_ROOT%\wcf\tools\QCreate\Debug folder. + +5. Start the qpid broker from the qpid build folder e.g. %QPID_ROOT%\cpp\build\src\Debug. + +6. Create queues required using the QCreate tool located at + \wcf\tools\QCreate\Debug. The syntax is QCreate %QPID_ROOT%. For this sample you + should do + + QCreate amq.topic usa.# usa + QCreate amq.topic #.news news + +7. Start Topic_Consumer.exe from + %QPID_ROOT%\wcf\samples\Channel\WCFToWCFPubSub\Topic_Consumer\bin\Debug. + +8. Start Another_Topic_Consumer.exe from + %QPID_ROOT%\wcf\samples\Channel\WCFToWCFPubSub\Another_Topic_Consumer\bin\Debug. + +9. Start Topic_Producer.exe from + %QPID_ROOT%\wcf\samples\Channel\WCFToWCFPubSub\Topic_Producer\bin\Debug. + + +7. Known Issues +=============== + +1. The Release configuration of the build (specified using the + /p:Configuration=Release switch with MSBuild) fails. + +2. The AmqpChannelListener is limited to single threaded use and the async methods + throw NotImplementedException. + +3. The AmqpChannelListener can hang on close for 60 seconds. + + diff --git a/qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Client.cs b/qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Client.cs new file mode 100644 index 0000000000..93ac97bc66 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Client.cs @@ -0,0 +1,68 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +namespace Apache.Qpid.Samples.Channel.WCFToWCFDirect +{ + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using Apache.Qpid.Channel; + + class Client + { + static void Main(string[] args) + { + try + { + // Create binding for the service endpoint. + CustomBinding amqpBinding = new CustomBinding(); + amqpBinding.Elements.Add(new BinaryMessageEncodingBindingElement()); + amqpBinding.Elements.Add(new AmqpTransportBindingElement()); + + // Create endpoint address. + Uri amqpClientUri = new Uri("amqp:amq.direct?routingkey=routing_key"); + EndpointAddress endpointAddress = new EndpointAddress(amqpClientUri); + + // Create a client with given client endpoint configuration. + ChannelFactory channelFactory = new ChannelFactory(amqpBinding, endpointAddress); + IHelloService clientProxy = channelFactory.CreateChannel(); + + Console.WriteLine(); + + string name = "name"; + for (int i = 0; i < 5; i++) + { + Console.WriteLine("Sending message: " + name + (i + 1)); + clientProxy.SayHello(name + (i + 1)); + } + + Console.WriteLine(); + Console.WriteLine("Press to terminate client."); + Console.ReadLine(); + + channelFactory.Close(); + } + catch (Exception e) + { + Console.WriteLine("Exception: {0}", e); + } + } + } +} diff --git a/qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Client.csproj b/qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Client.csproj new file mode 100644 index 0000000000..7e1d2d9f5d --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Client.csproj @@ -0,0 +1,90 @@ + + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {0CCD5711-2072-47B8-B902-07EC12C04159} + Exe + Properties + Client + Client + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\Apache.Qpid.Channel.dll + + + + 3.5 + + + 3.0 + + + 3.5 + + + 3.5 + + + + + + + + + + + {D0A46136-B4E3-4C50-AB6D-FB2BC6683D6E} + Service + + + + + \ No newline at end of file diff --git a/qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Properties/AssemblyInfo.cs b/qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..414a3b5858 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Client")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("MSIT")] +[assembly: AssemblyProduct("Client")] +[assembly: AssemblyCopyright("Copyright © MSIT 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("c3743ce0-3054-4188-8cd7-3a22734ee313")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Properties/AssemblyInfo.cs b/qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..2b75210ce3 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Service")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("MSIT")] +[assembly: AssemblyProduct("Service")] +[assembly: AssemblyCopyright("Copyright © MSIT 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5447546e-8547-4b0c-981a-1757ab8d9ec5")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Service.cs b/qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Service.cs new file mode 100644 index 0000000000..0342097ed9 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Service.cs @@ -0,0 +1,83 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +namespace Apache.Qpid.Samples.Channel.WCFToWCFDirect +{ + using System; + using System.ServiceModel; + using System.ServiceModel.Description; + using Apache.Qpid.Channel; + + // Define a service contract. + [ServiceContract] + public interface IHelloService + { + [OperationContract(IsOneWay = true, Action="*")] + void SayHello(string name); + } + + // Service class which implements the service contract. + [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)] + public class HelloService : IHelloService + { + [OperationBehavior] + public void SayHello(string name) + { + Console.WriteLine("Hello " + name); + } + } + + class Service + { + static void Main(string[] args) + { + // Create binding for the service endpoint. + AmqpBinding amqpBinding = new AmqpBinding(); + + // Create ServiceHost. + ServiceHost serviceHost = new ServiceHost(typeof(HelloService), new Uri[] { new Uri("http://localhost:8080/HelloService") }); + + // Add behavior for our MEX endpoint. + ServiceMetadataBehavior mexBehavior = new ServiceMetadataBehavior(); + mexBehavior.HttpGetEnabled = true; + serviceHost.Description.Behaviors.Add(mexBehavior); + + // Add MEX endpoint. + serviceHost.AddServiceEndpoint(typeof(IMetadataExchange), new BasicHttpBinding(), "MEX"); + + // Add AMQP endpoint. + Uri amqpUri = new Uri("amqp:message_queue"); + serviceHost.AddServiceEndpoint(typeof(IHelloService), amqpBinding, amqpUri.ToString()); + + serviceHost.Open(); + + Console.WriteLine(); + Console.WriteLine("The service is ready."); + Console.WriteLine("Press to terminate service."); + Console.WriteLine(); + Console.ReadLine(); + + if (serviceHost.State != CommunicationState.Faulted) + { + serviceHost.Close(); + } + } + } +} diff --git a/qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Service.csproj b/qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Service.csproj new file mode 100644 index 0000000000..3252380c98 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Service.csproj @@ -0,0 +1,85 @@ + + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {D0A46136-B4E3-4C50-AB6D-FB2BC6683D6E} + Exe + Properties + Service + Service + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\Apache.Qpid.Channel.dll + + + + 3.5 + + + + 3.0 + + + 3.5 + + + 3.5 + + + + + + + + + + + \ No newline at end of file diff --git a/qpid/wcf/samples/Channel/WCFToWCFDirect/WCFToWCFDirect.sln b/qpid/wcf/samples/Channel/WCFToWCFDirect/WCFToWCFDirect.sln new file mode 100644 index 0000000000..6f30a5e053 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFDirect/WCFToWCFDirect.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License +# + +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{0CCD5711-2072-47B8-B902-07EC12C04159}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Service", "Service\Service.csproj", "{D0A46136-B4E3-4C50-AB6D-FB2BC6683D6E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0CCD5711-2072-47B8-B902-07EC12C04159}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0CCD5711-2072-47B8-B902-07EC12C04159}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0CCD5711-2072-47B8-B902-07EC12C04159}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0CCD5711-2072-47B8-B902-07EC12C04159}.Release|Any CPU.Build.0 = Release|Any CPU + {D0A46136-B4E3-4C50-AB6D-FB2BC6683D6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D0A46136-B4E3-4C50-AB6D-FB2BC6683D6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D0A46136-B4E3-4C50-AB6D-FB2BC6683D6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D0A46136-B4E3-4C50-AB6D-FB2BC6683D6E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Another_Topic_Consumer.cs b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Another_Topic_Consumer.cs new file mode 100644 index 0000000000..c1e3ebbc88 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Another_Topic_Consumer.cs @@ -0,0 +1,67 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +namespace Apache.Qpid.Samples.Channel.WCFToWCFPubSub +{ + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.ServiceModel.Description; + using Apache.Qpid.Channel; + + class Another_Topic_Consumer + { + static void Main(string[] args) + { + // Create binding for the service endpoint. + CustomBinding amqpBinding = new CustomBinding(); + amqpBinding.Elements.Add(new BinaryMessageEncodingBindingElement()); + amqpBinding.Elements.Add(new AmqpTransportBindingElement()); + + // Create ServiceHost. + ServiceHost serviceHost = new ServiceHost(typeof(HelloService), new Uri[] { new Uri("http://localhost:8080/HelloService2") }); + + // Add behavior for our MEX endpoint. + ServiceMetadataBehavior mexBehavior = new ServiceMetadataBehavior(); + mexBehavior.HttpGetEnabled = true; + serviceHost.Description.Behaviors.Add(mexBehavior); + + // Add MEX endpoint. + serviceHost.AddServiceEndpoint(typeof(IMetadataExchange), new BasicHttpBinding(), "MEX"); + + // Add AMQP endpoint. + Uri amqpUri = new Uri("amqp:news"); + serviceHost.AddServiceEndpoint(typeof(IHelloService), amqpBinding, amqpUri.ToString()); + + serviceHost.Open(); + + Console.WriteLine(); + Console.WriteLine("The consumer is now listening on the queue \"news\"."); + Console.WriteLine("Press to terminate service."); + Console.WriteLine(); + Console.ReadLine(); + + if (serviceHost.State != CommunicationState.Faulted) + { + serviceHost.Close(); + } + } + } +} diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Another_Topic_Consumer.csproj b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Another_Topic_Consumer.csproj new file mode 100644 index 0000000000..47769e086d --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Another_Topic_Consumer.csproj @@ -0,0 +1,90 @@ + + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {6AC32E9D-EFB2-4DEF-81D7-F70A0D7A606F} + Exe + Properties + Another_Topic_Consumer + Another_Topic_Consumer + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\Apache.Qpid.Channel.dll + + + + 3.5 + + + 3.0 + + + 3.5 + + + 3.5 + + + + + + + + + + + {248A3A0B-FDC4-4E70-8428-BE0AF5AB021B} + Topic_Consumer + + + + + \ No newline at end of file diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Properties/AssemblyInfo.cs b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..8c22cb6d1f --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Another_Topic_Consumer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("MSIT")] +[assembly: AssemblyProduct("Another_Topic_Consumer")] +[assembly: AssemblyCopyright("Copyright © MSIT 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ba584c88-26a8-4910-a9a1-b4632b9adf01")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Properties/AssemblyInfo.cs b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..19fea85618 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Topic_Consumer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("MSIT")] +[assembly: AssemblyProduct("Topic_Consumer")] +[assembly: AssemblyCopyright("Copyright © MSIT 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("3facd6d1-f604-4ac9-ace3-7b7acff471eb")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Topic_Consumer.cs b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Topic_Consumer.cs new file mode 100644 index 0000000000..c4dd1e2256 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Topic_Consumer.cs @@ -0,0 +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. +*/ + + +namespace Apache.Qpid.Samples.Channel.WCFToWCFPubSub +{ + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.ServiceModel.Description; + using Apache.Qpid.Channel; + + // Define a service contract. + [ServiceContract] + public interface IHelloService + { + [OperationContract(IsOneWay = true)] + void SayHello(string name); + } + + // Service class which implements the service contract. + public class HelloService : IHelloService + { + [OperationBehavior] + public void SayHello(string name) + { + Console.WriteLine("Hello " + name); + } + } + + class Consumer + { + static void Main(string[] args) + { + // Create binding for the service endpoint. + CustomBinding amqpBinding = new CustomBinding(); + amqpBinding.Elements.Add(new BinaryMessageEncodingBindingElement()); + amqpBinding.Elements.Add(new AmqpTransportBindingElement()); + + // Create ServiceHost. + ServiceHost serviceHost = new ServiceHost(typeof(HelloService), new Uri[] { new Uri("http://localhost:8080/HelloService1") }); + + // Add behavior for our MEX endpoint. + ServiceMetadataBehavior mexBehavior = new ServiceMetadataBehavior(); + mexBehavior.HttpGetEnabled = true; + serviceHost.Description.Behaviors.Add(mexBehavior); + + // Add MEX endpoint. + serviceHost.AddServiceEndpoint(typeof(IMetadataExchange), new BasicHttpBinding(), "MEX"); + + // Add AMQP endpoint. + Uri amqpUri = new Uri("amqp:usa"); + serviceHost.AddServiceEndpoint(typeof(IHelloService), amqpBinding, amqpUri.ToString()); + + serviceHost.Open(); + + Console.WriteLine(); + Console.WriteLine("The consumer is now listening on the queue \"usa\"."); + Console.WriteLine("Press to terminate service."); + Console.WriteLine(); + Console.ReadLine(); + + if (serviceHost.State != CommunicationState.Faulted) + { + serviceHost.Close(); + } + } + } +} diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Topic_Consumer.csproj b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Topic_Consumer.csproj new file mode 100644 index 0000000000..b2151c0631 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Topic_Consumer.csproj @@ -0,0 +1,84 @@ + + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {248A3A0B-FDC4-4E70-8428-BE0AF5AB021B} + Exe + Properties + Topic_Consumer + Topic_Consumer + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\Apache.Qpid.Channel.dll + + + + 3.5 + + + 3.0 + + + 3.5 + + + 3.5 + + + + + + + + + + + \ No newline at end of file diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Properties/AssemblyInfo.cs b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..87310bf92a --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Topic_Producer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("MSIT")] +[assembly: AssemblyProduct("Topic_Producer")] +[assembly: AssemblyCopyright("Copyright © MSIT 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a70e852d-a510-4e00-af72-68bb8547696f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Topic_Producer.cs b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Topic_Producer.cs new file mode 100644 index 0000000000..e3850eb4c0 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Topic_Producer.cs @@ -0,0 +1,68 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +namespace Apache.Qpid.Samples.Channel.WCFToWCFPubSub +{ + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using Apache.Qpid.Channel; + + class Topic_Producer + { + static void Main(string[] args) + { + try + { + // Create binding for the service endpoint. + CustomBinding amqpBinding = new CustomBinding(); + amqpBinding.Elements.Add(new BinaryMessageEncodingBindingElement()); + amqpBinding.Elements.Add(new AmqpTransportBindingElement()); + + // Create endpoint address. + Uri amqpClientUri = new Uri("amqp:amq.topic?routingkey=usa.news"); + EndpointAddress endpointAddress = new EndpointAddress(amqpClientUri); + + // Create a client with given client endpoint configuration. + ChannelFactory channelFactory = new ChannelFactory(amqpBinding, endpointAddress); + IHelloService clientProxy = channelFactory.CreateChannel(); + + Console.WriteLine(); + + string name = "name"; + for (int i = 0; i < 5; i++) + { + Console.WriteLine("Sending message: " + name + (i + 1)); + clientProxy.SayHello(name + (i+1)); + } + + Console.WriteLine(); + Console.WriteLine("Press to terminate client."); + Console.ReadLine(); + + channelFactory.Close(); + } + catch (Exception e) + { + Console.WriteLine("Exception: {0}", e); + } + } + } +} diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Topic_Producer.csproj b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Topic_Producer.csproj new file mode 100644 index 0000000000..b4318ead3f --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Topic_Producer.csproj @@ -0,0 +1,90 @@ + + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {67B413EF-3B9C-4988-87DE-0386C209D368} + Exe + Properties + Topic_Producer + Topic_Producer + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\Apache.Qpid.Channel.dll + + + + 3.5 + + + 3.0 + + + 3.5 + + + 3.5 + + + + + + + + + + + {248A3A0B-FDC4-4E70-8428-BE0AF5AB021B} + Topic_Consumer + + + + + \ No newline at end of file diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/WCFToWCFPubSub.sln b/qpid/wcf/samples/Channel/WCFToWCFPubSub/WCFToWCFPubSub.sln new file mode 100644 index 0000000000..d8a56ea8db --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/WCFToWCFPubSub.sln @@ -0,0 +1,52 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License +# + +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Topic_Consumer", "Topic_Consumer\Topic_Consumer.csproj", "{248A3A0B-FDC4-4E70-8428-BE0AF5AB021B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Topic_Producer", "Topic_Producer\Topic_Producer.csproj", "{67B413EF-3B9C-4988-87DE-0386C209D368}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Another_Topic_Consumer", "Another_Topic_Consumer\Another_Topic_Consumer.csproj", "{6AC32E9D-EFB2-4DEF-81D7-F70A0D7A606F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {248A3A0B-FDC4-4E70-8428-BE0AF5AB021B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {248A3A0B-FDC4-4E70-8428-BE0AF5AB021B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {248A3A0B-FDC4-4E70-8428-BE0AF5AB021B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {248A3A0B-FDC4-4E70-8428-BE0AF5AB021B}.Release|Any CPU.Build.0 = Release|Any CPU + {67B413EF-3B9C-4988-87DE-0386C209D368}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {67B413EF-3B9C-4988-87DE-0386C209D368}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67B413EF-3B9C-4988-87DE-0386C209D368}.Release|Any CPU.ActiveCfg = Release|Any CPU + {67B413EF-3B9C-4988-87DE-0386C209D368}.Release|Any CPU.Build.0 = Release|Any CPU + {6AC32E9D-EFB2-4DEF-81D7-F70A0D7A606F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6AC32E9D-EFB2-4DEF-81D7-F70A0D7A606F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6AC32E9D-EFB2-4DEF-81D7-F70A0D7A606F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6AC32E9D-EFB2-4DEF-81D7-F70A0D7A606F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpBoolean.cs b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpBoolean.cs new file mode 100644 index 0000000000..980ae78361 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpBoolean.cs @@ -0,0 +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. +*/ + +namespace Apache.Qpid.AmqpTypes +{ + using System; + using System.IO; + using System.Collections.Generic; + using System.Text; + + public class AmqpBoolean : AmqpType + { + bool value; + + public AmqpBoolean(bool i) + { + this.value = i; + } + + public override void Encode(byte[] bufer, int offset, int count) + { + throw new NotImplementedException(); + } + + public override int EncodedSize + { + get { throw new NotImplementedException(); } + } + + public override AmqpType Clone() + { + return new AmqpBoolean(this.value); + } + + public bool Value + { + get { return this.value; } + set { this.value = value; } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpInt.cs b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpInt.cs new file mode 100644 index 0000000000..c114e98a71 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpInt.cs @@ -0,0 +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. +*/ + +namespace Apache.Qpid.AmqpTypes +{ + using System; + using System.IO; + using System.Collections.Generic; + using System.Text; + + public class AmqpInt : AmqpType + { + int value; + + public AmqpInt(int i) + { + this.value = i; + } + + public override void Encode(byte[] bufer, int offset, int count) + { + throw new NotImplementedException(); + } + + public override int EncodedSize + { + get { throw new NotImplementedException(); } + } + + public override AmqpType Clone() + { + return new AmqpInt(this.value); + } + + public int Value + { + get { return this.value; } + set { this.value = value; } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpProperties.cs b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpProperties.cs new file mode 100644 index 0000000000..0f649dcd36 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpProperties.cs @@ -0,0 +1,292 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.AmqpTypes +{ + using System; + using System.IO; + using System.Collections.Generic; + using System.Text; + + public class AmqpProperties + { + // AMQP 0-10 delivery properties + private bool durable; + private Nullable timeToLive; + private string routingKey; + + // AMQP 0-10 message properties + private string replyToExchange; + private string replyToRoutingKey; + private byte[] userId; + private byte[] correlationId; + private string contentType; + + // for application and vendor properties + Dictionary propertyMap; + + public AmqpProperties() + { + } + + // AMQP 0-10 "message.delivery-properties + internal bool HasDeliveryProperties + { + get + { + return ((this.routingKey != null) || this.durable || this.timeToLive.HasValue); + } + } + + internal bool HasMappedProperties + { + get + { + if (this.propertyMap != null) + { + if (this.propertyMap.Count > 0) + { + return true; + } + } + + return false; + } + } + + // AMQP 0-10 "message.message-properties" + internal bool HasMessageProperties + { + get + { + if ((this.replyToExchange != null) || + (this.replyToRoutingKey != null) || + (this.userId != null) || + (this.correlationId != null) || + (this.contentType != null)) + { + return true; + } + + if (this.propertyMap == null) + { + return false; + } + + return (this.propertyMap.Count != 0); + } + } + + public Dictionary PropertyMap + { + get + { + if (this.propertyMap == null) + { + this.propertyMap = new Dictionary(); + } + return propertyMap; + } + set { this.propertyMap = value; } + } + + internal bool Empty + { + get + { + if (this.HasDeliveryProperties || this.HasMessageProperties) + { + return true; + } + return false; + } + } + + public string ContentType + { + get { return contentType; } + // TODO: validate + set { contentType = value; } + } + + public byte[] CorrelationId + { + get { return correlationId; } + set + { + if (value != null) + { + if (value.Length > 65535) + { + throw new ArgumentException("CorrelationId too big"); + } + } + correlationId = value; + } + } + + public byte[] UserId + { + get { return userId; } + set + { + if (value != null) + { + if (value.Length > 65535) + { + throw new ArgumentException("UserId too big"); + } + } + userId = value; + } + } + + public TimeSpan? TimeToLive + { + get { return this.timeToLive; } + set { this.timeToLive = value; } + } + + public string RoutingKey + { + get { return this.routingKey; } + set { this.routingKey = value; } + } + + public string ReplyToExchange + { + get { return this.replyToExchange; } + } + + public string ReplyToRoutingKey + { + get { return this.replyToRoutingKey; } + } + + // this changes from 0-10 to 1.0 + public void SetReplyTo(string exchange, string routingKey) + { + if ((exchange == null && routingKey == null)) + { + throw new ArgumentNullException("SetReplyTo"); + } + + this.replyToExchange = exchange; + this.replyToRoutingKey = routingKey; + } + + public bool Durable + { + get { return durable; } + set { durable = value; } + } + + public void Clear() + { + this.timeToLive = null; + this.routingKey = null; + this.replyToRoutingKey = null; + this.replyToExchange = null; + this.durable = false; + this.contentType = null; + this.userId = null; + this.correlationId = null; + this.propertyMap = null; + } + + public AmqpProperties Clone() + { + // memberwise clone ok for string, byte[], and value types + AmqpProperties clonedProps = (AmqpProperties)this.MemberwiseClone(); + + // deeper copy for the dictionary + if (this.propertyMap != null) + { + if (this.propertyMap.Count > 0) + { + Dictionary clonedDictionary = new Dictionary(this.propertyMap.Count); + foreach (KeyValuePair original in this.propertyMap) + { + clonedDictionary.Add(original.Key, original.Value.Clone()); + } + + clonedProps.propertyMap = clonedDictionary; + } + else + { + clonedProps.propertyMap = null; + } + } + return clonedProps; + } + + // adds/replaces from the other AmqpProperty object. + // just inserts references, i.e. provides shallow copy semantics (see Clone for deep copy) + public void MergeFrom(AmqpProperties other) + { + if (other.timeToLive.HasValue) + { + this.timeToLive = other.timeToLive; + } + + if ((other.replyToRoutingKey != null) || (other.replyToExchange != null)) + { + this.replyToExchange = other.replyToExchange; + this.replyToRoutingKey = other.replyToRoutingKey; + } + + if (other.routingKey != null) + { + this.routingKey = other.routingKey; + } + + if (other.durable) + { + this.durable = true; + } + + if (other.contentType != null) + { + this.contentType = other.contentType; + } + + if (other.correlationId != null) + { + this.correlationId = other.correlationId; + } + + if (other.userId != null) + { + this.userId = other.userId; + } + + if (other.propertyMap != null) + { + if (other.propertyMap.Count > 0) + { + Dictionary thisMap = this.PropertyMap; + foreach (KeyValuePair kvp in other.propertyMap) + { + thisMap[kvp.Key] = kvp.Value; + } + } + } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpString.cs b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpString.cs new file mode 100644 index 0000000000..87cebe878c --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpString.cs @@ -0,0 +1,91 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.AmqpTypes +{ + using System; + using System.IO; + using System.Collections.Generic; + using System.Text; + + // for big strings: str16 in 0-10 and str32 in 1.0 + + public class AmqpString : AmqpType + { + string value; + Encoding encoding; + + public AmqpString(string s) + { + this.value = s; + this.encoding = Encoding.UTF8; + } + + public AmqpString(string s, Encoding enc) + { + ValidateEncoding(enc); + this.value = s; + this.encoding = enc; + } + + public Encoding Encoding + { + get { return encoding; } + set + { + ValidateEncoding(value); + encoding = value; + } + } + + private void ValidateEncoding(Encoding enc) + { + if (value == null) + { + throw new ArgumentNullException("Encoding"); + } + + if ((enc != Encoding.UTF8) && (enc != Encoding.Unicode)) + { + throw new ArgumentException("Encoding not one of UTF8 or Unicode"); + } + } + + public override void Encode(byte[] bufer, int offset, int count) + { + throw new NotImplementedException(); + } + + public override int EncodedSize + { + get { throw new NotImplementedException(); } + } + + public override AmqpType Clone() + { + return new AmqpString(this.value); + } + + public string Value + { + get { return this.value; } + set { this.value = value; } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpType.cs b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpType.cs new file mode 100644 index 0000000000..8cd3ac9e4a --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpType.cs @@ -0,0 +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. +*/ + +namespace Apache.Qpid.AmqpTypes +{ + using System; + using System.IO; + using System.Collections.Generic; + using System.Text; + + public abstract class AmqpType + { + public abstract void Encode(byte[] bufer, int offset, int count); + public abstract int EncodedSize { get; } + public abstract AmqpType Clone(); + } +} diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpTypes.csproj b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpTypes.csproj new file mode 100644 index 0000000000..9c13d47296 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpTypes.csproj @@ -0,0 +1,153 @@ + + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {820BFC34-A40F-46BA-B86B-05334854CA17} + Library + Properties + Apache.Qpid.AmqpTypes + Apache.Qpid.AmqpTypes + v3.5 + 512 + OnBuildSuccess + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + + + + + + + + cd "$(ProjectDir)bin\$(ConfigurationName)" +del $(AssemblyName).dll +del $(AssemblyName).pdb +cd "$(ProjectDir)obj\$(ConfigurationName)" +del $(AssemblyName).dll +del $(AssemblyName).pdb +cd "$(ProjectDir)" +CreateNetModule.bat + + \ No newline at end of file diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpUbyte.cs b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpUbyte.cs new file mode 100644 index 0000000000..5ec8a732cf --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpUbyte.cs @@ -0,0 +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. +*/ + +namespace Apache.Qpid.AmqpTypes +{ + using System; + using System.IO; + using System.Collections.Generic; + using System.Text; + + public class AmqpUbyte : AmqpType + { + byte value; + + public AmqpUbyte(byte i) + { + this.value = i; + } + + public override void Encode(byte[] bufer, int offset, int count) + { + throw new NotImplementedException(); + } + + public override int EncodedSize + { + get { throw new NotImplementedException(); } + } + + public override AmqpType Clone() + { + return new AmqpUbyte(this.value); + } + + public byte Value + { + get { return this.value; } + set { this.value = value; } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/CreateNetModule.bat b/qpid/wcf/src/Apache/Qpid/AmqpTypes/CreateNetModule.bat new file mode 100755 index 0000000000..ddbe1407a7 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/CreateNetModule.bat @@ -0,0 +1,19 @@ + +REM Licensed to the Apache Software Foundation (ASF) under one +REM or more contributor license agreements. See the NOTICE file +REM distributed with this work for additional information +REM regarding copyright ownership. The ASF licenses this file +REM to you under the Apache License, Version 2.0 (the +REM "License"); you may not use this file except in compliance +REM with the License. You may obtain a copy of the License at +REM +REM http://www.apache.org/licenses/LICENSE-2.0 +REM +REM Unless required by applicable law or agreed to in writing, +REM software distributed under the License is distributed on an +REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +REM KIND, either express or implied. See the License for the +REM specific language governing permissions and limitations +REM under the License. + +%systemroot%\Microsoft.NET\Framework\v3.5\Csc.exe /noconfig /nowarn:1701,1702 /errorreport:prompt /warn:4 /define:DEBUG;TRACE /reference:"%programfiles%\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll" /reference:"%programfiles%\Reference Assemblies\Microsoft\Framework\v3.5\System.Data.DataSetExtensions.dll" /reference:%systemroot%\Microsoft.NET\Framework\v2.0.50727\System.Data.dll /reference:%systemroot%\Microsoft.NET\Framework\v2.0.50727\System.dll /reference:%systemroot%\Microsoft.NET\Framework\v2.0.50727\System.Xml.dll /reference:"%programfiles%\Reference Assemblies\Microsoft\Framework\v3.5\System.Xml.Linq.dll" /debug+ /debug:full /filealign:512 /optimize- /out:obj\Debug\Apache.Qpid.AmqpTypes.netmodule /target:module *.cs \ No newline at end of file diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/Properties/AssemblyInfo.cs b/qpid/wcf/src/Apache/Qpid/AmqpTypes/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..0bce6f9795 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Apache.Qpid.AmqpTypes")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("79b8b5d9-047d-4f3b-8610-7fe112ce6416")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/PropertyName.cs b/qpid/wcf/src/Apache/Qpid/AmqpTypes/PropertyName.cs new file mode 100644 index 0000000000..b80f8b9e9e --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/PropertyName.cs @@ -0,0 +1,35 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.AmqpTypes +{ + using System; + using System.IO; + using System.Collections.Generic; + using System.Text; + + public sealed class PropertyName + { + public const string Priority = "amqpx.priority"; + public const string ContentType = "amqp.content-type"; + public const string ReplyTo = "amqp.reply-to"; + public const string ReplyToExchange = "amqpx.qpid0-10.reply-to-exchange"; + public const string RoutingKey = "amqpx.qpid0-10.routing-key"; + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBinding.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBinding.cs new file mode 100644 index 0000000000..e207f2fe45 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBinding.cs @@ -0,0 +1,60 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Configuration; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.ServiceModel.Configuration; + + using Apache.Qpid.AmqpTypes; + + public class AmqpBinaryBinding : AmqpBinding + { + public AmqpBinaryBinding() +: base (new RawMessageEncodingBindingElement()) + { + } + + public AmqpBinaryBinding(string configurationName) + : this() + { + ApplyConfiguration(configurationName); + } + + private void ApplyConfiguration(string configurationName) + { + AmqpBinaryBindingCollectionElement section = (AmqpBinaryBindingCollectionElement)ConfigurationManager.GetSection(AmqpConstants.AmqpBinaryBindingSectionName); + AmqpBinaryBindingConfigurationElement element = section.Bindings[configurationName]; + if (element == null) + { + throw new ConfigurationErrorsException(string.Format(System.Globalization.CultureInfo.CurrentCulture, + "There is no binding named {0} at {1}.", configurationName, section.BindingName)); + } + else + { + element.ApplyConfiguration(this); + } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBindingCollectionElement.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBindingCollectionElement.cs new file mode 100644 index 0000000000..de263bc4ef --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBindingCollectionElement.cs @@ -0,0 +1,29 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + /// + /// Implement application configuration of bindingExtensions for AmqpBinaryBinding + /// + public class AmqpBinaryBindingCollectionElement + : System.ServiceModel.Configuration.StandardBindingCollectionElement + { + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBindingConfigurationElement.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBindingConfigurationElement.cs new file mode 100644 index 0000000000..a537a6c6c3 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBindingConfigurationElement.cs @@ -0,0 +1,79 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Configuration; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.ServiceModel.Configuration; + using Apache.Qpid.AmqpTypes; + + public class AmqpBinaryBindingConfigurationElement : AmqpBindingConfigurationElement + { + public AmqpBinaryBindingConfigurationElement(string configurationName) + : base(configurationName) + { + } + + public AmqpBinaryBindingConfigurationElement() + : this(null) + { + } + + protected override Type BindingElementType + { + get { return typeof(AmqpBinaryBinding); } + } + + protected override ConfigurationPropertyCollection Properties + { + get + { + ConfigurationPropertyCollection properties = base.Properties; + + return properties; + } + } + + protected override void InitializeFrom(Binding binding) + { + base.InitializeFrom(binding); + AmqpBinaryBinding amqpBinding = (AmqpBinaryBinding)binding; + } + + protected override void OnApplyConfiguration(Binding binding) + { + if (binding == null) + throw new ArgumentNullException("binding"); + + if (binding.GetType() != typeof(AmqpBinaryBinding)) + { + throw new ArgumentException(string.Format("Invalid type for configuring an AMQP binding. Expected type: {0}. Type passed in: {1}.", + typeof(AmqpBinaryBinding).AssemblyQualifiedName, + binding.GetType().AssemblyQualifiedName)); + } + + base.OnApplyConfiguration(binding); + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinding.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinding.cs new file mode 100644 index 0000000000..b952faf9e5 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinding.cs @@ -0,0 +1,115 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Configuration; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.ServiceModel.Configuration; + + using Apache.Qpid.AmqpTypes; + + public class AmqpBinding : Binding + { + protected AmqpTransportBindingElement transport; + protected MessageEncodingBindingElement encoding; + + public AmqpBinding() + { + transport = new AmqpTransportBindingElement(); + encoding = new BinaryMessageEncodingBindingElement(); + } + + protected AmqpBinding(MessageEncodingBindingElement encoding) + { + this.encoding = encoding; + transport = new AmqpTransportBindingElement(); + } + + public AmqpBinding(string configurationName) + : this() + { + ApplyConfiguration(configurationName); + } + + public string BrokerHost + { + get { return transport.BrokerHost; } + set { transport.BrokerHost = value; } + } + + public int BrokerPort + { + get { return transport.BrokerPort; } + set { transport.BrokerPort = value; } + } + + public bool Shared + { + get { return transport.Shared; } + set { transport.Shared = value; } + } + + public TransferMode TransferMode + { + get { return transport.TransferMode; } + set { transport.TransferMode = value; } + } + + public AmqpProperties DefaultMessageProperties + { + get { return transport.DefaultMessageProperties; } + set { transport.DefaultMessageProperties = value; } + } + + public override string Scheme + { + get { return AmqpConstants.Scheme; } + } + + public override BindingElementCollection CreateBindingElements() + { + BindingElementCollection bindingElements = new BindingElementCollection(); + + bindingElements.Add(encoding); + bindingElements.Add(transport); + + return bindingElements.Clone(); + } + + private void ApplyConfiguration(string configurationName) + { + AmqpBindingCollectionElement section = (AmqpBindingCollectionElement)ConfigurationManager.GetSection(AmqpConstants.AmqpBindingSectionName); + AmqpBindingConfigurationElement element = section.Bindings[configurationName]; + if (element == null) + { + throw new ConfigurationErrorsException(string.Format(System.Globalization.CultureInfo.CurrentCulture, + "There is no binding named {0} at {1}.", configurationName, section.BindingName)); + } + else + { + element.ApplyConfiguration(this); + } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpBindingCollectionElement.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBindingCollectionElement.cs new file mode 100644 index 0000000000..e8d3b6fad4 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBindingCollectionElement.cs @@ -0,0 +1,29 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + /// + /// Implement application configuration of bindingExtensions for AmqpBinding + /// + public class AmqpBindingCollectionElement + : System.ServiceModel.Configuration.StandardBindingCollectionElement + { + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpBindingConfigurationElement.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBindingConfigurationElement.cs new file mode 100644 index 0000000000..3ec62e809d --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBindingConfigurationElement.cs @@ -0,0 +1,258 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Configuration; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.ServiceModel.Configuration; + using Apache.Qpid.AmqpTypes; + + public class AmqpBindingConfigurationElement : StandardBindingElement + { + // not regular config elements. See PostDeserialize + string brokerHost; + int brokerPort; + + public AmqpBindingConfigurationElement(string configurationName) + : base(configurationName) + { + brokerHost = AmqpDefaults.BrokerHost; + brokerPort = AmqpDefaults.BrokerPort; + } + + public AmqpBindingConfigurationElement() + : this(null) + { + } + + protected override Type BindingElementType + { + get { return typeof(AmqpBinding); } + } + + public string BrokerHost + { + get { return brokerHost; } + set { brokerHost = value; } + } + + public int BrokerPort + { + get { return brokerPort; } + set { brokerPort = value; } + } + + [ConfigurationProperty(AmqpConfigurationStrings.Shared, DefaultValue = false)] + public bool Shared + { + get { return (bool)base[AmqpConfigurationStrings.Shared]; } + set { base[AmqpConfigurationStrings.Shared] = value; } + } + + [ConfigurationProperty(AmqpConfigurationStrings.TransferMode, DefaultValue = AmqpDefaults.TransferMode)] + public TransferMode TransferMode + { + get { return (TransferMode)base[AmqpConfigurationStrings.TransferMode]; } + set { base[AmqpConfigurationStrings.TransferMode] = value; } + } + + [ConfigurationProperty(AmqpConfigurationStrings.Brokers)] + public BrokerCollection Brokers + { + get + { + return (BrokerCollection)base[AmqpConfigurationStrings.Brokers]; + } + set + { + base[AmqpConfigurationStrings.Brokers] = value; + } + } + + protected override ConfigurationPropertyCollection Properties + { + get + { + ConfigurationPropertyCollection properties = base.Properties; + properties.Add(new ConfigurationProperty(AmqpConfigurationStrings.Shared, + typeof(bool), false, null, null, ConfigurationPropertyOptions.None)); + properties.Add(new ConfigurationProperty(AmqpConfigurationStrings.TransferMode, + typeof(TransferMode), AmqpDefaults.TransferMode, null, null, ConfigurationPropertyOptions.None)); + properties.Add(new ConfigurationProperty("brokers", typeof(BrokerCollection), null)); + return properties; + } + } + + protected override void InitializeFrom(Binding binding) + { + base.InitializeFrom(binding); + AmqpBinding amqpBinding = (AmqpBinding)binding; + this.BrokerHost = amqpBinding.BrokerHost; + this.BrokerPort = amqpBinding.BrokerPort; + this.TransferMode = amqpBinding.TransferMode; + this.Shared = amqpBinding.Shared; + + AmqpProperties props = amqpBinding.DefaultMessageProperties; + } + + protected override void OnApplyConfiguration(Binding binding) + { + if (binding == null) + throw new ArgumentNullException("binding"); + + if (!(binding is AmqpBinding)) + { + throw new ArgumentException(string.Format("Invalid type for configuring an AMQP binding. Expected type: {0}. Type passed in: {1}.", + typeof(AmqpBinding).AssemblyQualifiedName, + binding.GetType().AssemblyQualifiedName)); + } + + AmqpBinding amqpBinding = (AmqpBinding)binding; + amqpBinding.BrokerHost = this.BrokerHost; + amqpBinding.BrokerPort = this.BrokerPort; + amqpBinding.TransferMode = this.TransferMode; + amqpBinding.Shared = this.Shared; + } + + protected override void PostDeserialize() + { + base.PostDeserialize(); + + BrokerCollection brokers = Brokers; + if (brokers != null) + { + if (brokers.Count > 0) + { + // just grab the first element until failover is supported + System.Collections.IEnumerator brokersEnum = brokers.GetEnumerator(); + // move to first element + brokersEnum.MoveNext(); + BrokerElement be = (BrokerElement)brokersEnum.Current; + this.BrokerHost = be.Host; + this.BrokerPort = be.Port; + } + } + } + } + + public class BrokerCollection : ConfigurationElementCollection + { + public BrokerCollection() + { + //this.AddElementName = "broker"; + } + + protected override ConfigurationElement CreateNewElement() + { + return new BrokerElement(); + } + + protected override void BaseAdd(ConfigurationElement element) + { + BrokerElement be = (BrokerElement)element; + if (this.BaseGet((Object)be.Key) != null) + { + throw new ConfigurationErrorsException("duplicate broker definition at line " + element.ElementInformation.LineNumber); + } + base.BaseAdd(element); + } + + protected override Object GetElementKey(ConfigurationElement element) + { + BrokerElement be = (BrokerElement) element; + return be.Key; + } + + protected override void PostDeserialize() + { + base.PostDeserialize(); + if (this.Count == 0) + { + throw new ArgumentException("Brokers collection requires at least one broker"); + } + if (this.Count > 1) + { + Console.WriteLine("Warning: multiple brokers not supported, selecting first instance"); + } + BrokerElement be = (BrokerElement)this.BaseGet(0); + } + + protected override string ElementName + { + get + { + return "broker"; + } + } + + public override ConfigurationElementCollectionType CollectionType + { + get + { + return ConfigurationElementCollectionType.BasicMap; + } + } + } + + public class BrokerElement : ConfigurationElement + { + string key; + + public BrokerElement() + { + Properties.Add(new ConfigurationProperty(AmqpConfigurationStrings.BrokerHost, + typeof(string), AmqpDefaults.BrokerHost, null, null, ConfigurationPropertyOptions.None)); + Properties.Add(new ConfigurationProperty(AmqpConfigurationStrings.BrokerPort, + typeof(int), AmqpDefaults.BrokerPort, null, null, ConfigurationPropertyOptions.None)); + + } + + [ConfigurationProperty(AmqpConfigurationStrings.BrokerHost, DefaultValue = AmqpDefaults.BrokerHost)] + public string Host + { + get { return (string)base[AmqpConfigurationStrings.BrokerHost]; } + set { base[AmqpConfigurationStrings.BrokerHost] = value; } + } + + [ConfigurationProperty(AmqpConfigurationStrings.BrokerPort, DefaultValue = AmqpDefaults.BrokerPort)] + public int Port + { + get { return (int)base[AmqpConfigurationStrings.BrokerPort]; } + set { base[AmqpConfigurationStrings.BrokerPort] = value; } + } + + public string Key + { + get + { + if (this.key == null) + { + this.key = this.Host + ':' + this.Port; + } + return this.key; + } + } + + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelFactory.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelFactory.cs new file mode 100644 index 0000000000..b8e2811527 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelFactory.cs @@ -0,0 +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. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.Collections.Generic; + using System.Collections.ObjectModel; + + class AmqpChannelFactory : ChannelFactoryBase + { + MessageEncoderFactory messageEncoderFactory; + AmqpTransportBindingElement bindingElement; + AmqpChannelProperties channelProperties; + long maxBufferPoolSize; + bool shared; + + internal AmqpChannelFactory(AmqpTransportBindingElement bindingElement, BindingContext context) + : base(context.Binding) + { + this.bindingElement = bindingElement; + this.channelProperties = bindingElement.ChannelProperties.Clone(); + this.shared = bindingElement.Shared; + this.maxBufferPoolSize = bindingElement.MaxBufferPoolSize; + Collection messageEncoderBindingElements + = context.BindingParameters.FindAll(); + + if(messageEncoderBindingElements.Count > 1) + { + throw new InvalidOperationException("More than one MessageEncodingBindingElement was found in the BindingParameters of the BindingContext"); + } + else if (messageEncoderBindingElements.Count == 1) + { + this.messageEncoderFactory = messageEncoderBindingElements[0].CreateMessageEncoderFactory(); + } + else + { + this.messageEncoderFactory = new TextMessageEncodingBindingElement().CreateMessageEncoderFactory(); + } + } + + + public override T GetProperty() + { + T mep = messageEncoderFactory.Encoder.GetProperty(); + if (mep != null) + { + return mep; + } + + if (typeof(T) == typeof(MessageVersion)) + { + return (T)(object)messageEncoderFactory.Encoder.MessageVersion; + } + + return base.GetProperty(); + } + + protected override void OnOpen(TimeSpan timeout) + { + } + + protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state) + { + throw new NotImplementedException("AmqpChannelFactory OnBeginOpen"); + //// return null; + } + + protected override void OnEndOpen(IAsyncResult result) + { + throw new NotImplementedException("AmqpChannelFactory OnEndOpen"); + } + + protected override TChannel OnCreateChannel(EndpointAddress remoteAddress, Uri via) + { + return (TChannel)(object) new AmqpTransportChannel(this, this.channelProperties, remoteAddress, this.messageEncoderFactory.Encoder, this.maxBufferPoolSize, this.shared); + } + + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelHelpers.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelHelpers.cs new file mode 100644 index 0000000000..f1de30406a --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelHelpers.cs @@ -0,0 +1,142 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.Net; + using System.Net.Sockets; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.Globalization; + + using Apache.Qpid.AmqpTypes; + + /// + /// Collection of constants used by the Amqp Channel classes + /// + static class AmqpConstants + { + internal const string Scheme = "amqp"; + internal const string AmqpBindingSectionName = "system.serviceModel/bindings/amqpBinding"; + internal const string AmqpBinaryBindingSectionName = "system.serviceModel/bindings/amqpBinaryBinding"; + internal const string AmqpTransportSectionName = "amqpTransport"; + } + + static class AmqpConfigurationStrings + { + public const string BrokerHost = "host"; + public const string BrokerPort = "port"; + public const string TransferMode = "transferMode"; + public const string Brokers = "brokers"; + public const string Shared = "shared"; + public const string MaxBufferPoolSize = "maxBufferPoolSize"; + public const string MaxReceivedMessageSize = "maxReceivedMessageSize"; + } + + static class AmqpDefaults + { + internal const string BrokerHost = "localhost"; + internal const int BrokerPort = 5672; + internal const TransferMode TransferMode = System.ServiceModel.TransferMode.Buffered; + internal const byte Priority = 4; + internal const long MaxBufferPoolSize = 64 * 1024; + internal const int MaxReceivedMessageSize = 5 * 1024 * 1024; //64 * 1024; + } + + // parking spot for properties that may be shared by separate channels on a single AMQP connection + internal class AmqpChannelProperties + { + string brokerHost; + int brokerPort; + TransferMode transferMode; + AmqpProperties defaultMessageProperties; + + long maxBufferPoolSize; + int maxReceivedMessageSize; + + internal AmqpChannelProperties() + { + this.brokerHost = AmqpDefaults.BrokerHost; + this.brokerPort = AmqpDefaults.BrokerPort; + this.transferMode = AmqpDefaults.TransferMode; + this.defaultMessageProperties = null; + this.maxBufferPoolSize = AmqpDefaults.MaxBufferPoolSize; + this.maxReceivedMessageSize = AmqpDefaults.MaxReceivedMessageSize; + } + + public AmqpChannelProperties Clone() + { + AmqpChannelProperties props = (AmqpChannelProperties) this.MemberwiseClone(); + if (this.defaultMessageProperties != null) + { + props.defaultMessageProperties = this.defaultMessageProperties.Clone(); + } + + return props; + } + + internal string BrokerHost + { + get { return this.brokerHost; } + set { this.brokerHost = value; } + } + + internal int BrokerPort + { + get { return this.brokerPort; } + set { this.brokerPort = value; } + } + + internal TransferMode TransferMode + { + get { return this.transferMode; } + set { this.transferMode = value; } + } + + internal AmqpProperties DefaultMessageProperties + { + get { return this.defaultMessageProperties; } + set { this.defaultMessageProperties = value; } + } + + internal long MaxBufferPoolSize + { + get { return this.maxBufferPoolSize; } + set { this.maxBufferPoolSize = value; } + } + + internal int MaxReceivedMessageSize + { + get { return this.maxReceivedMessageSize; } + set { this.maxReceivedMessageSize = value; } + } + } + + static class AmqpChannelHelpers + { + internal static void ValidateTimeout(TimeSpan timeout) + { + if (timeout < TimeSpan.Zero) + { + throw new ArgumentOutOfRangeException("timeout", timeout, "Timeout must be greater than or equal to TimeSpan.Zero. To disable timeout, specify TimeSpan.MaxValue."); + } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelListener.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelListener.cs new file mode 100644 index 0000000000..44fecdaf62 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelListener.cs @@ -0,0 +1,174 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.Threading; + using System.Collections.Generic; + using System.Collections.ObjectModel; + + class AmqpChannelListener : ChannelListenerBase + { + MessageEncoderFactory messageEncoderFactory; + AmqpTransportBindingElement bindingElement; + AmqpChannelProperties channelProperties; + bool shared; + long maxBufferPoolSize; + Uri uri; + AmqpTransportChannel amqpTransportChannel; + delegate IInputChannel AsyncOnAcceptCaller (TimeSpan timeout); + AsyncOnAcceptCaller asyncOnAcceptCaller; + ManualResetEvent acceptWaitEvent; + + internal AmqpChannelListener(AmqpTransportBindingElement bindingElement, BindingContext context) + : base(context.Binding) + { + this.bindingElement = bindingElement; + this.channelProperties = bindingElement.ChannelProperties.Clone(); + this.shared = bindingElement.Shared; + + this.maxBufferPoolSize = bindingElement.MaxBufferPoolSize; + + // TODO: review this. Should be unique hostname based + this.uri = context.ListenUriBaseAddress; + this.asyncOnAcceptCaller = new AsyncOnAcceptCaller(this.OnAcceptChannel); + this.acceptWaitEvent = new ManualResetEvent(false); + + Collection messageEncoderBindingElements + = context.BindingParameters.FindAll(); + + if(messageEncoderBindingElements.Count > 1) + { + throw new InvalidOperationException("More than one MessageEncodingBindingElement was found in the BindingParameters of the BindingContext"); + } + else if (messageEncoderBindingElements.Count == 1) + { + this.messageEncoderFactory = messageEncoderBindingElements[0].CreateMessageEncoderFactory(); + } + else + { + this.messageEncoderFactory = new TextMessageEncodingBindingElement().CreateMessageEncoderFactory(); + } + } + + public override Uri Uri + { + get + { + return this.uri; + } + } + + + + public override T GetProperty() + { + T mep = messageEncoderFactory.Encoder.GetProperty(); + if (mep != null) + { + return mep; + } + + if (typeof(T) == typeof(MessageVersion)) + { + return (T)(object)messageEncoderFactory.Encoder.MessageVersion; + } + + return base.GetProperty(); + } + + protected override void OnOpen(TimeSpan timeout) + { + } + + protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state) + { + throw new NotImplementedException("AmqpChannelListener OnBeginOpen"); + //// return null; + } + + protected override void OnEndOpen(IAsyncResult result) + { + throw new NotImplementedException("AmqpChannelListener OnEndOpen"); + } + + protected override bool OnWaitForChannel(TimeSpan timeout) + { + throw new NotImplementedException("AmqpChannelListener OnWaitForChannel"); + } + + protected override IAsyncResult OnBeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state) + { + throw new NotImplementedException("AmqpChannelListener OnBeginWaitForChannel"); + } + + protected override bool OnEndWaitForChannel(IAsyncResult result) + { + throw new NotImplementedException("AmqpChannelListener OnEndWaitForChannel"); + } + + protected override IInputChannel OnAcceptChannel(TimeSpan timeout) + { + if (amqpTransportChannel == null) + { + amqpTransportChannel = new AmqpTransportChannel(this, this.channelProperties, + new EndpointAddress(uri), messageEncoderFactory.Encoder, + maxBufferPoolSize, this.shared); + return (IInputChannel)(object) amqpTransportChannel; + } + + // TODO: remove "max one channel" restriction, add timeout processing + acceptWaitEvent.WaitOne(); + return null; + } + + protected override IAsyncResult OnBeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state) + { + return asyncOnAcceptCaller.BeginInvoke(timeout, callback, state); + } + + protected override IInputChannel OnEndAcceptChannel(IAsyncResult result) + { + return asyncOnAcceptCaller.EndInvoke(result); + } + + protected override void OnClose(TimeSpan timeout) + { + // TODO: (+ OnAbort) + } + + protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state) + { + throw new NotImplementedException("AmqpChannelListener OnBeginClose"); + } + + protected override void OnEndClose(IAsyncResult result) + { + throw new NotImplementedException("AmqpChannelListener OnEndClose"); + } + + protected override void OnAbort() + { + // TODO: + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportBindingElement.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportBindingElement.cs new file mode 100644 index 0000000000..f23b8072e9 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportBindingElement.cs @@ -0,0 +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. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.ServiceModel.Description; + using Apache.Qpid.AmqpTypes; + + public class AmqpTransportBindingElement : TransportBindingElement + { + AmqpChannelProperties channelProperties; + bool shared; + + public AmqpTransportBindingElement() + { + // start with default properties + channelProperties = new AmqpChannelProperties(); + } + + protected AmqpTransportBindingElement(AmqpTransportBindingElement other) + : base(other) + { + this.channelProperties = other.channelProperties.Clone(); + this.shared = other.shared; + } + + public override IChannelFactory BuildChannelFactory(BindingContext context) + { + if (context == null) + { + throw new ArgumentNullException("context"); + } + + return (IChannelFactory)(object)new AmqpChannelFactory(this, context); + } + + public override IChannelListener BuildChannelListener(BindingContext context) + { + if (context == null) + { + throw new ArgumentNullException("context"); + } + + return (IChannelListener)(object)new AmqpChannelListener(this, context); + } + + + + public override bool CanBuildChannelFactory(BindingContext context) + { + return ((typeof(TChannel) == typeof(IOutputChannel)) || + (typeof(TChannel) == typeof(IInputChannel))); + } + + public override bool CanBuildChannelListener(BindingContext context) + { + return ((typeof(TChannel) == typeof(IInputChannel))); + } + + public override BindingElement Clone() + { + return new AmqpTransportBindingElement(this); + } + + internal AmqpChannelProperties ChannelProperties + { + get { return channelProperties; } + } + + public string BrokerHost + { + get { return this.channelProperties.BrokerHost; } + set { this.channelProperties.BrokerHost = value; } + } + + public int BrokerPort + { + get { return this.channelProperties.BrokerPort; } + set { this.channelProperties.BrokerPort = value; } + } + + public bool Shared + { + get { return this.shared; } + set { this.shared = value; } + } + + public TransferMode TransferMode + { + get { return this.channelProperties.TransferMode; } + set { this.channelProperties.TransferMode = value; } + } + + public AmqpProperties DefaultMessageProperties + { + get { return this.channelProperties.DefaultMessageProperties; } + + set { this.channelProperties.DefaultMessageProperties = value; } + } + + public override T GetProperty(BindingContext context) + { + if (context == null) + { + throw new ArgumentNullException("context"); + } + + if (typeof(T) == typeof(MessageVersion)) + { + return (T)(object)MessageVersion.Default; + } + + + return context.GetInnerProperty(); + } + + public override string Scheme + { + get + { + return AmqpConstants.Scheme; + } + } + + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportChannel.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportChannel.cs new file mode 100644 index 0000000000..ca9c10be69 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportChannel.cs @@ -0,0 +1,592 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// TODO: flow control +// timeout handling +// transactions +// check if should split into separate input and output classes (little overlap) + +namespace Apache.Qpid.Channel +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.Text; + using System.Threading; + using System.Globalization; + using System.Xml; + + // the thin interop layer that provides access to the Qpid AMQP client libraries + using Apache.Qpid.Interop; + using Apache.Qpid.AmqpTypes; + + /// + /// WCF client transport channel for accessing AMQP brokers using the Qpid C++ library + /// + public class AmqpTransportChannel : ChannelBase, IOutputChannel, IInputChannel + { + private static readonly EndpointAddress AnonymousAddress = + new EndpointAddress("http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous"); + + private EndpointAddress remoteAddress; + private MessageEncoder encoder; + private AmqpChannelProperties factoryChannelProperties; + private bool shared; + private string encoderContentType; + + // input = 0-10 queue, output = 0-10 exchange + private string queueName; + + private String routingKey; + private BufferManager bufferManager; + private AmqpProperties outputMessageProperties; + + private InputLink inputLink; + private OutputLink outputLink; + + private bool isInputChannel; + private bool streamed; + + private AsyncTimeSpanCaller asyncOpenCaller; + private AsyncTimeSpanCaller asyncCloseCaller; + + internal AmqpTransportChannel(ChannelManagerBase factory, AmqpChannelProperties channelProperties, EndpointAddress remoteAddress, MessageEncoder msgEncoder, long maxBufferPoolSize, bool sharedConnection) + : base(factory) + { + this.isInputChannel = (factory is ChannelListenerBase) || (factory is AmqpChannelFactory); + + if (remoteAddress == null) + { + throw new ArgumentException("Null Endpoint Address"); + } + + this.factoryChannelProperties = channelProperties; + this.shared = sharedConnection; + this.remoteAddress = remoteAddress; + + // pull out host, port, queue, and connection arguments + this.ParseAmqpUri(remoteAddress.Uri); + + this.encoder = msgEncoder; + string ct = String.Empty; + if (this.encoder != null) + { + ct = this.encoder.ContentType; + if (ct != null) + { + int pos = ct.IndexOf(';'); + if (pos != -1) + { + ct = ct.Substring(0, pos).Trim(); + } + } + else + { + ct = "application/octet-stream"; + } + } + + this.encoderContentType = ct; + + if (this.factoryChannelProperties.TransferMode == TransferMode.Streamed) + { + this.streamed = true; + } + else + { + if (!(this.factoryChannelProperties.TransferMode == TransferMode.Buffered)) + { + throw new ArgumentException("TransferMode mode must be \"Streamed\" or \"Buffered\""); + } + + this.streamed = false; + } + + this.bufferManager = BufferManager.CreateBufferManager(maxBufferPoolSize, int.MaxValue); + + this.asyncOpenCaller = new AsyncTimeSpanCaller(this.OnOpen); + this.asyncCloseCaller = new AsyncTimeSpanCaller(this.OnClose); + + if (this.isInputChannel) + { + this.inputLink = ConnectionManager.GetInputLink(this.factoryChannelProperties, shared, false, this.queueName); + } + else + { + this.outputLink = ConnectionManager.GetOutputLink(this.factoryChannelProperties, shared, false, this.queueName); + } + } + + private delegate bool AsyncTryReceiveCaller(TimeSpan timeout, out Message message); + + private delegate void AsyncTimeSpanCaller(TimeSpan timeout); + + EndpointAddress IOutputChannel.RemoteAddress + { + get + { + return this.remoteAddress; + } + } + + // i.e what you would insert into a ReplyTo header to reach + // here. Presumably should be exchange/link and routing info, + // rather than the actual input queue name. + EndpointAddress IInputChannel.LocalAddress + { + get + { + // TODO: something better + return AnonymousAddress; + } + } + + AmqpProperties OutputMessageProperties + { + get + { + if (this.outputMessageProperties == null) + { + this.outputMessageProperties = this.factoryChannelProperties.DefaultMessageProperties; + if (this.outputMessageProperties == null) + { + this.outputMessageProperties = new AmqpProperties(); + } + } + + return this.outputMessageProperties; + } + } + + Uri IOutputChannel.Via + { + get + { + return this.remoteAddress.Uri; + } + } + + public override T GetProperty() + { + if (typeof(T) == typeof(IInputChannel)) + { + if (this.isInputChannel) + { + return (T)(object)this; + } + } + else if (typeof(T) == typeof(IOutputChannel)) + { + if (!this.isInputChannel) + { + return (T)(object)this; + } + } + + return base.GetProperty(); + } + + public void Send(Message message, TimeSpan timeout) + { + this.ThrowIfDisposedOrNotOpen(); + AmqpChannelHelpers.ValidateTimeout(timeout); + + try + { + using (AmqpMessage amqpMessage = this.WcfToQpid(message)) + { + this.outputLink.Send(amqpMessage, timeout); + } + } + finally + { + message.Close(); + } + } + + public void Send(Message message) + { + this.Send(message, this.DefaultSendTimeout); + } + + public IAsyncResult BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state) + { + this.ThrowIfDisposedOrNotOpen(); + AmqpChannelHelpers.ValidateTimeout(timeout); + + try + { + using (AmqpMessage amqpMessage = this.WcfToQpid(message)) + { + return this.outputLink.BeginSend(amqpMessage, timeout, callback, state); + } + } + finally + { + message.Close(); + } + } + + public IAsyncResult BeginSend(Message message, AsyncCallback callback, object state) + { + return this.BeginSend(message, this.DefaultSendTimeout, callback, state); + } + + public void EndSend(IAsyncResult result) + { + this.outputLink.EndSend(result); + } + + public Message Receive(TimeSpan timeout) + { + Message message; + if (this.TryReceive(timeout, out message)) + { + return message; + } + else + { + throw new TimeoutException("Receive"); + } + } + + public Message Receive() + { + return this.Receive(this.DefaultReceiveTimeout); + } + + public bool TryReceive(TimeSpan timeout, out Message message) + { + this.ThrowIfDisposedOrNotOpen(); + AmqpMessage amqpMessage; + message = null; + + if (this.inputLink.TryReceive(timeout, out amqpMessage)) + { + message = this.QpidToWcf(amqpMessage); + return true; + } + + return false; + } + + public IAsyncResult BeginTryReceive(TimeSpan timeout, AsyncCallback callback, object state) + { + return this.inputLink.BeginTryReceive(timeout, callback, state); + } + + public bool EndTryReceive(IAsyncResult result, out Message message) + { + AmqpMessage amqpMessage = null; + if (!this.inputLink.EndTryReceive(result, out amqpMessage)) + { + message = null; + return false; + } + message = QpidToWcf(amqpMessage); + return true; + } + + public bool WaitForMessage(TimeSpan timeout) + { + return this.inputLink.WaitForMessage(timeout); + } + + public IAsyncResult BeginReceive(TimeSpan timeout, AsyncCallback callback, object state) + { + return this.inputLink.BeginTryReceive(timeout, callback, state); + } + + public IAsyncResult BeginReceive(AsyncCallback callback, object state) + { + return this.BeginReceive(this.DefaultReceiveTimeout, callback, state); + } + + public IAsyncResult BeginWaitForMessage(TimeSpan timeout, AsyncCallback callback, object state) + { + return this.inputLink.BeginWaitForMessage(timeout, callback, state); + } + + public Message EndReceive(IAsyncResult result) + { + Message message; + if (this.EndTryReceive(result, out message)) + { + return message; + } + else + { + throw new TimeoutException("EndReceive"); + } + } + + public bool EndWaitForMessage(IAsyncResult result) + { + return this.inputLink.EndWaitForMessage(result); + } + + public void CloseEndPoint() + { + if (this.inputLink != null) + { + this.inputLink.Close(); + } + if (this.outputLink != null) + { + this.outputLink.Close(); + } + } + + /// + /// Open connection to Broker + /// + protected override void OnOpen(TimeSpan timeout) + { + // TODO: move open logic to here from constructor + } + + protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state) + { + return this.asyncOpenCaller.BeginInvoke(timeout, callback, state); + } + + protected override void OnEndOpen(IAsyncResult result) + { + this.asyncOpenCaller.EndInvoke(result); + } + + protected override void OnAbort() + { + //// TODO: check for network-less qpid teardown or launch special thread + this.Cleanup(); + } + + /// + /// Shutdown gracefully + /// + protected override void OnClose(TimeSpan timeout) + { + this.CloseEndPoint(); + this.Cleanup(); + } + + protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state) + { + return this.asyncCloseCaller.BeginInvoke(timeout, callback, state); + } + + protected override void OnEndClose(IAsyncResult result) + { + this.asyncCloseCaller.EndInvoke(result); + } + + private AmqpMessage WcfToQpid(Message wcfMessage) + { + object obj; + AmqpProperties applicationProperties = null; + bool success = false; + AmqpMessage amqpMessage = null; + + if (wcfMessage.Properties.TryGetValue("AmqpProperties", out obj)) + { + applicationProperties = obj as AmqpProperties; + } + + try + { + AmqpProperties outgoingProperties = new AmqpProperties(); + + // Start with AMQP properties from the binding and the URI + if (this.factoryChannelProperties.DefaultMessageProperties != null) + { + outgoingProperties.MergeFrom(this.factoryChannelProperties.DefaultMessageProperties); + } + + if (this.routingKey != null) + { + outgoingProperties.RoutingKey = this.routingKey; + } + + // Add the Properties set by the application on this particular message. + // Application properties trump channel properties + if (applicationProperties != null) + { + outgoingProperties.MergeFrom(applicationProperties); + } + + amqpMessage = this.outputLink.CreateMessage(); + amqpMessage.Properties = outgoingProperties; + + // copy the WCF message body to the AMQP message body + if (this.streamed) + { + this.encoder.WriteMessage(wcfMessage, amqpMessage.BodyStream); + } + else + { + ArraySegment encodedBody = this.encoder.WriteMessage(wcfMessage, int.MaxValue, this.bufferManager); + try + { + amqpMessage.BodyStream.Write(encodedBody.Array, encodedBody.Offset, encodedBody.Count); + } + finally + { + this.bufferManager.ReturnBuffer(encodedBody.Array); + } + } + + success = true; + } + finally + { + if (!success && (amqpMessage != null)) + { + amqpMessage.Dispose(); + } + } + return amqpMessage; + } + + + private Message QpidToWcf(AmqpMessage amqpMessage) + { + if (amqpMessage == null) + { + return null; + } + + Message wcfMessage = null; + byte[] managedBuffer = null; + + try + { + if (this.streamed) + { + wcfMessage = this.encoder.ReadMessage(amqpMessage.BodyStream, int.MaxValue); + } + else + { + int count = (int)amqpMessage.BodyStream.Length; + managedBuffer = this.bufferManager.TakeBuffer(count); + int nr = amqpMessage.BodyStream.Read(managedBuffer, 0, count); + ArraySegment bufseg = new ArraySegment(managedBuffer, 0, count); + + wcfMessage = this.encoder.ReadMessage(bufseg, this.bufferManager); + + // set to null for finally{} block, since the encoder is now responsible for + // returning the BufferManager memory + managedBuffer = null; + } + + // This message will be discarded unless the "To" header matches + // the WCF endpoint dispatcher's address filter (or the service is + // AddressFilterMode=AddressFilterMode.Any). + + this.remoteAddress.ApplyTo(wcfMessage); + + if (amqpMessage.Properties != null) + { + wcfMessage.Properties.Add("AmqpProperties", amqpMessage.Properties); + } + } + catch (XmlException xmlException) + { + throw new ProtocolException( + "There is a problem with the XML that was received from the network. See inner exception for more details.", + xmlException); + } + catch (Exception e) + { + // TODO: logging + Console.WriteLine("TX channel encoder exception " + e); + } + finally + { + // close the amqpMessage unless the body will be read at a later time. + if (!this.streamed || wcfMessage == null) + { + amqpMessage.Close(); + } + + // the handoff to the encoder failed + if (managedBuffer != null) + { + this.bufferManager.ReturnBuffer(managedBuffer); + } + } + + return wcfMessage; + } + + private void Cleanup() + { + this.bufferManager.Clear(); + } + + // "amqp:queue1" | "amqp:stocks@broker1.com" | "amqp:queue3?routingkey=key" + private void ParseAmqpUri(Uri uri) + { + if (uri.Scheme != AmqpConstants.Scheme) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, + "The scheme {0} specified in address is not supported.", uri.Scheme), "uri"); + } + + this.queueName = uri.LocalPath; + + if ((this.queueName.IndexOf('@') != -1) && this.isInputChannel) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, + "Invalid input queue name: \"{0}\" specified.", this.queueName), "uri"); + } + + // search out session parameters in the query portion of the URI + + string routingParseKey = "routingkey="; + char[] charSeparators = new char[] { '?', ';' }; + string[] args = uri.Query.Split(charSeparators, StringSplitOptions.RemoveEmptyEntries); + foreach (string s in args) + { + if (s.StartsWith(routingParseKey)) + { + this.routingKey = s.Substring(routingParseKey.Length); + } + } + + if (this.queueName == String.Empty) + { + if (this.isInputChannel) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, + "Empty queue target specifier not allowed."), "uri"); + } + else + { + if (this.routingKey == null) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, + "No target queue or routing key specified."), "uri"); + } + } + } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/Channel.csproj b/qpid/wcf/src/Apache/Qpid/Channel/Channel.csproj new file mode 100644 index 0000000000..0b04eba986 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/Channel.csproj @@ -0,0 +1,102 @@ + + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {8AABAB30-7D1E-4539-B7D1-05450262BAD2} + Library + Properties + Apache.Qpid.Channel + Apache.Qpid.Channel + v3.5 + 512 + + + false + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 3.0 + + + 3.0 + + + + + + {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34} + Interop + + + \ No newline at end of file diff --git a/qpid/wcf/src/Apache/Qpid/Channel/ConnectionManager.cs b/qpid/wcf/src/Apache/Qpid/Channel/ConnectionManager.cs new file mode 100644 index 0000000000..a63e5333f4 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/ConnectionManager.cs @@ -0,0 +1,266 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Threading; + + using Apache.Qpid.Interop; + + // The ConnectionManager looks after a shareable pool of AmqpConnection and AmqpSession + // objects. If two connection requests could be shared (see MakeKey() properties), and + // are designated as shareable, then they will be paired up. Each shared connection is + // a separate instance of a ManagedConnection. All unshared connections use a single + // instance of ManagedConnection with locking turned off. The ManagedConnection object + // registers for notifictation when a connection goes idle (all grandchild InputLink and + // OutputLink objects have been closed), and closes the connection. + + // TODO: the session sharing is roughed-in via comments but needs completing. + + internal sealed class ConnectionManager + { + // A side effect of creating InputLinks and OutputLinks is that counters + // in the respective AmqpSession and AmqpConnection are updated, so care + // must be taken to hold the lock across acquiring a session and opening + // a link on it. + + // one for each shared connection + private static Dictionary sharedInstances; + + // this one creates and releases connections that are not shared. No locking required. + private static ManagedConnection unsharedInstance; + + // lock for finding or creating ManagedConnection instances + private static Object connectionLock; + + static ConnectionManager() + { + unsharedInstance = null; + sharedInstances = new Dictionary(); + connectionLock = new Object(); + } + + private static string MakeKey(AmqpChannelProperties props) + { + return props.BrokerHost + ':' + props.BrokerPort + ':' + props.TransferMode; + } + + private static ManagedConnection GetManagedConnection(AmqpChannelProperties channelProperties, bool connectionSharing) + { + if (connectionSharing) + { + string key = MakeKey(channelProperties); + lock (connectionLock) + { + ManagedConnection mc = null; + if (!sharedInstances.TryGetValue(key, out mc)) + { + mc = new ManagedConnection(true); + sharedInstances.Add(key, mc); + } + return mc; + } + } + else + { + lock (connectionLock) + { + if (unsharedInstance == null) + { + unsharedInstance = new ManagedConnection(false); + } + return unsharedInstance; + } + } + } + + public static OutputLink GetOutputLink(AmqpChannelProperties channelProperties, bool connectionSharing, bool sessionSharing, string qname) + { + ManagedConnection mc = GetManagedConnection(channelProperties, connectionSharing); + return (OutputLink)mc.GetLink(channelProperties, sessionSharing, null, qname); + } + + public static InputLink GetInputLink(AmqpChannelProperties channelProperties, bool connectionSharing, bool sessionSharing, string qname) + { + ManagedConnection mc = GetManagedConnection(channelProperties, connectionSharing); + return (InputLink)mc.GetLink(channelProperties, sessionSharing, qname, null); + } + + + + class ManagedConnection + { + private Boolean shared; + private AmqpConnection sharedConnection; + //private Dictionary sharedSessions; + + public ManagedConnection(bool shared) + { + this.shared = shared; + } + + + public object GetLink(AmqpChannelProperties channelProperties, bool sessionSharing, string inputQueue, string outputQueue) + { + AmqpConnection connection = null; + AmqpSession session = null; + Object link = null; + bool newConnection = false; + //bool newSession = false; + bool success = false; + + // when called in the non-shared case, only stack variables should be used for holding connections/sessions/links + + if (this.shared) + { + Monitor.Enter(this); // lock + } + + try + { + if (this.shared) + { + // TODO: check shared connection not closed (i.e. network drop) and refresh this instance if needed + if (sessionSharing) + { + throw new NotImplementedException("shared session"); + /* * ... once we have a defined shared session config parameter: + + // lazilly create + if (this.sharedSessions == null) + { + this.sharedSessions = new Dictionary(); + } + + alreadydeclaredstring sessionKey = channelProperties.name_of_key_goes_here; + this.sharedSessions.TryGetValue(sessionKey, out session); + + * */ + } + + if (this.sharedConnection != null) + { + connection = this.sharedConnection; + } + } + + if (connection == null) + { + connection = new AmqpConnection(channelProperties.BrokerHost, channelProperties.BrokerPort); + newConnection = true; + if (this.shared) + { + connection.OnConnectionIdle += new ConnectionIdleEventHandler(this.IdleConnectionHandler); + } + else + { + connection.OnConnectionIdle += new ConnectionIdleEventHandler(UnsharedIdleConnectionHandler); + } + } + + if (session == null) + { + session = connection.CreateSession(); + //newSession = true; + } + + if (inputQueue != null) + { + link = session.CreateInputLink(inputQueue); + } + else + { + link = session.CreateOutputLink(outputQueue); + } + + if (this.shared) + { + if (newConnection) + { + this.sharedConnection = connection; + } + /* + if (newSession) + { + sharedSessions.Add(foo, session); + } + * */ + } + + success = true; + } + finally + { + if (this.shared) + { + Monitor.Exit(this); + } + if (!success) + { + /* + if (newSession) + { + session.Close(); + } + */ + if (newConnection) + { + connection.Close(); + } + } + } + + return link; + } + + + static void UnsharedIdleConnectionHandler(Object sender, EventArgs empty) + { + if (sender is AmqpConnection) + { + AmqpConnection connection = (AmqpConnection)sender; + connection.Close(); + } + } + + void IdleConnectionHandler(Object sender, EventArgs empty) + { + lock (this) + { + if (sharedConnection != sender || sharedConnection == null) + { + return; + } + if (!sharedConnection.IsIdle) + { + // Another thread made the connection busy again. + // That's OK. Another idle event will come along later. + return; + } + sharedConnection.Close(); // also closes all child sessions + sharedConnection = null; + //sharedSessions = null; + } + } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/Properties/AssemblyInfo.cs b/qpid/wcf/src/Apache/Qpid/Channel/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..bc047d59b3 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/Properties/AssemblyInfo.cs @@ -0,0 +1,52 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Apache.Qpid.Channel")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ac02bbb0-2c19-43fb-a36c-b1b0a50eaf1a")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/qpid/wcf/src/Apache/Qpid/Channel/RawMessage.cs b/qpid/wcf/src/Apache/Qpid/Channel/RawMessage.cs new file mode 100644 index 0000000000..5925fa47dc --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/RawMessage.cs @@ -0,0 +1,374 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.IO; + using System.ServiceModel.Channels; + using System.Xml; + + // This incoming Message is backed either by a Stream (bodyStream) or a byte array (bodyBytes). + // If bodyBytes belongs to a BufferManager, we must return it when done. + // The pay-off is OnGetReaderAtBodyContents(). + // Most of the complexity is dealing with the OnCreateBufferedCopy() machinery. + internal class RawMessage : Message + { + private MessageHeaders headers; + private MessageProperties properties; + private XmlDictionaryReaderQuotas readerQuotas; + private Stream bodyStream; + private byte[] bodyBytes; + private int index; + private int count; + private BufferManager bufferManager; + + public RawMessage(byte[] buffer, int index, int count, BufferManager bufferManager, XmlDictionaryReaderQuotas quotas) + { + // this constructor supports MessageEncoder.ReadMessage(ArraySegment b, BufferManager mgr, string contentType) + if (quotas == null) + { + quotas = new XmlDictionaryReaderQuotas(); + } + + this.headers = new MessageHeaders(MessageVersion.None); + this.properties = new MessageProperties(); + this.readerQuotas = quotas; + this.bodyBytes = buffer; + this.index = index; + this.count = count; + this.bufferManager = bufferManager; + } + + public RawMessage(Stream stream, XmlDictionaryReaderQuotas quotas) + { + // this constructor supports MessageEncoder.ReadMessage(System.IO.Stream s, int max, string contentType) + if (quotas == null) + { + quotas = new XmlDictionaryReaderQuotas(); + } + + this.headers = new MessageHeaders(MessageVersion.None); + this.properties = new MessageProperties(); + this.bodyStream = stream; + } + + public RawMessage(MessageHeaders headers, MessageProperties properties, byte[] bytes, int index, int count, XmlDictionaryReaderQuotas quotas) + { + // this constructor supports internal needs for CreateBufferedCopy().CreateMessage() + this.headers = new MessageHeaders(headers); + this.properties = new MessageProperties(properties); + this.bodyBytes = bytes; + this.index = index; + this.count = count; + this.readerQuotas = quotas; + } + + public override MessageHeaders Headers + { + get + { + if (this.IsDisposed) + { + throw new ObjectDisposedException("message"); + } + + return this.headers; + } + } + + public override bool IsEmpty + { + get + { + if (this.IsDisposed) + { + throw new ObjectDisposedException("message"); + } + + return false; + } + } + + public override bool IsFault + { + get + { + if (this.IsDisposed) + { + throw new ObjectDisposedException("message"); + } + + return false; + } + } + + public override MessageProperties Properties + { + get + { + if (this.IsDisposed) + { + throw new ObjectDisposedException("message"); + } + + return this.properties; + } + } + + public override MessageVersion Version + { + get + { + if (this.IsDisposed) + { + throw new ObjectDisposedException("message"); + } + + return MessageVersion.None; + } + } + + protected override void OnBodyToString(XmlDictionaryWriter writer) + { + if (this.bodyStream != null) + { + writer.WriteString("Stream"); + } + else + { + writer.WriteStartElement(RawMessageEncoder.StreamElementName, string.Empty); + writer.WriteBase64(this.bodyBytes, this.index, this.count); + writer.WriteEndElement(); + } + } + + protected override void OnClose() + { + Exception deferEx = null; + try + { + base.OnClose(); + } + catch (Exception e) + { + deferEx = e; + } + + try + { + if (this.properties != null) + { + this.properties.Dispose(); + } + } + catch (Exception e) + { + if (deferEx == null) + { + deferEx = e; + } + } + + try + { + if (this.bufferManager != null) + { + this.bufferManager.ReturnBuffer(this.bodyBytes); + this.bufferManager = null; + } + } + catch (Exception e) + { + if (deferEx == null) + { + deferEx = e; + } + } + + if (deferEx != null) + { + throw deferEx; + } + } + + protected override MessageBuffer OnCreateBufferedCopy(int maxBufferSize) + { + if (this.bodyStream != null) + { + int len = (int)this.bodyStream.Length; + byte[] buf = new byte[len]; + this.bodyStream.Read(buf, 0, len); + this.bodyStream = null; + this.bodyBytes = buf; + this.count = len; + this.index = 0; + } + else + { + if (this.bufferManager != null) + { + // we could take steps to share the buffer among copies and release the memory + // after the last user finishes by a reference count or such, but we are already + // far from the intended optimized use. Make one GC managed memory copy that is + // shared by all. + byte[] buf = new byte[this.count]; + + Buffer.BlockCopy(this.bodyBytes, this.index, buf, 0, this.count); + this.bufferManager.ReturnBuffer(this.bodyBytes); + this.bufferManager = null; + this.bodyBytes = buf; + this.index = 0; + } + } + + return new RawMessageBuffer(this.headers, this.properties, this.bodyBytes, this.index, this.count, this.readerQuotas); + } + + protected override XmlDictionaryReader OnGetReaderAtBodyContents() + { + Stream readerStream = null; + bool ownsStream; + + if (this.bodyStream != null) + { + readerStream = this.bodyStream; + ownsStream = false; + } + else + { + // create stream for duration of XmlReader. + ownsStream = true; + if (this.bufferManager != null) + { + readerStream = new RawMemoryStream(this.bodyBytes, this.index, this.count, this.bufferManager); + this.bufferManager = null; + } + else + { + readerStream = new MemoryStream(this.bodyBytes, this.index, this.count, false); + } + } + + return new RawXmlReader(readerStream, this.readerQuotas, ownsStream); + } + + protected override void OnWriteBodyContents(XmlDictionaryWriter writer) + { + writer.WriteStartElement(RawMessageEncoder.StreamElementName, string.Empty); + if (this.bodyStream != null) + { + int len = (int)this.bodyStream.Length; + byte[] buf = new byte[len]; + this.bodyStream.Read(buf, 0, len); + writer.WriteBase64(buf, 0, len); + } + else + { + writer.WriteBase64(this.bodyBytes, this.index, this.count); + } + + writer.WriteEndElement(); + } + + private class RawMemoryStream : MemoryStream + { + private BufferManager bufferManager; + private byte[] buffer; + + public RawMemoryStream(byte[] bytes, int index, int count, BufferManager mgr) + : base(bytes, index, count, false) + { + this.bufferManager = mgr; + this.buffer = bytes; + } + + protected override void Dispose(bool disposing) + { + if (this.bufferManager != null) + { + try + { + this.bufferManager.ReturnBuffer(this.buffer); + } + finally + { + this.bufferManager = null; + base.Dispose(disposing); + } + } + } + } + + private class RawMessageBuffer : MessageBuffer + { + private bool closed; + private MessageHeaders headers; + private MessageProperties properties; + private byte[] bodyBytes; + private int index; + private int count; + private XmlDictionaryReaderQuotas readerQuotas; + + public RawMessageBuffer(MessageHeaders headers, MessageProperties properties, byte[] bytes, int index, int count, XmlDictionaryReaderQuotas quotas) + : base() + { + this.headers = new MessageHeaders(headers); + this.properties = new MessageProperties(properties); + this.bodyBytes = bytes; + this.index = index; + this.count = count; + this.readerQuotas = new XmlDictionaryReaderQuotas(); + quotas.CopyTo(this.readerQuotas); + } + + public override int BufferSize + { + get { return this.count; } + } + + public override void Close() + { + if (!this.closed) + { + this.closed = true; + this.headers = null; + if (this.properties != null) + { + this.properties.Dispose(); + this.properties = null; + } + + this.bodyBytes = null; + this.readerQuotas = null; + } + } + + public override Message CreateMessage() + { + if (this.closed) + { + throw new ObjectDisposedException("message"); + } + + return new RawMessage(this.headers, this.properties, this.bodyBytes, this.index, this.count, this.readerQuotas); + } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncoder.cs b/qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncoder.cs new file mode 100644 index 0000000000..76dae6f6c7 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncoder.cs @@ -0,0 +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. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.IO; + using System.ServiceModel.Channels; + using System.ServiceModel; + using System.Xml; + + + class RawMessageEncoder : MessageEncoder + { + public const string StreamElementName = "Binary"; + + XmlDictionaryReaderQuotas readerQuotas; + + public RawMessageEncoder(XmlDictionaryReaderQuotas quotas) + { + this.readerQuotas = new XmlDictionaryReaderQuotas(); + if (quotas != null) + { + quotas.CopyTo(this.readerQuotas); + } + } + + public override string ContentType + { + get { return null; } + } + + public override bool IsContentTypeSupported(string contentType) + { + return true; + } + + public override string MediaType + { + get { return null; } + } + + public override MessageVersion MessageVersion + { + get { return MessageVersion.None; } + } + + public override Message ReadMessage(ArraySegment buffer, BufferManager bufferManager, string contentType) + { + RawMessage message = new RawMessage(buffer.Array, buffer.Offset, buffer.Count, bufferManager, readerQuotas); + message.Properties.Encoder = this; + return message; + } + + public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType) + { + RawMessage message = new RawMessage(stream, readerQuotas); + message.Properties.Encoder = this; + return message; + } + + private void CheckType(XmlDictionaryReader reader, XmlNodeType type) + { + if (reader.NodeType != type) + { + throw new System.IO.InvalidDataException(String.Format("RawMessageEncoder xml check {0} type should be {1}", type, reader.NodeType)); + } + } + + public override ArraySegment WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset) + { + MemoryStream tempStream = new MemoryStream(); + this.WriteMessage(message, tempStream); + int len = messageOffset + (int)tempStream.Length; + byte[] buf = bufferManager.TakeBuffer(len); + MemoryStream targetStream = new MemoryStream(buf); + if (messageOffset > 0) + { + targetStream.Seek(messageOffset, SeekOrigin.Begin); + } + + tempStream.WriteTo(targetStream); + targetStream.Close(); + + return new ArraySegment(buf, messageOffset, len - messageOffset); + } + + public override void WriteMessage(Message message, Stream stream) + { + using (XmlWriter writer = new RawXmlWriter(stream)) + { + message.WriteMessage(writer); + writer.Flush(); + } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncoderFactory.cs b/qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncoderFactory.cs new file mode 100644 index 0000000000..5c015f9a1b --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncoderFactory.cs @@ -0,0 +1,45 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.Xml; + using System.ServiceModel.Channels; + + internal class RawMessageEncoderFactory : MessageEncoderFactory + { + RawMessageEncoder encoder; + + public RawMessageEncoderFactory(XmlDictionaryReaderQuotas quotas) + { + this.encoder = new RawMessageEncoder(quotas); + } + + public override MessageEncoder Encoder + { + get { return this.encoder; } + } + + public override MessageVersion MessageVersion + { + get { return encoder.MessageVersion; } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncodingBindingElement.cs b/qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncodingBindingElement.cs new file mode 100644 index 0000000000..5ec10a976d --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncodingBindingElement.cs @@ -0,0 +1,102 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.ServiceModel.Channels; + + public class RawMessageEncodingBindingElement : MessageEncodingBindingElement + { + + public RawMessageEncodingBindingElement() + : base() + { + } + + RawMessageEncodingBindingElement(RawMessageEncodingBindingElement originalBindingElement) + { + } + + public override MessageEncoderFactory CreateMessageEncoderFactory() + { + return new RawMessageEncoderFactory(null); + } + + + public override IChannelFactory BuildChannelFactory(BindingContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + + context.BindingParameters.Add(this); + return context.BuildInnerChannelFactory(); + } + + public override bool CanBuildChannelFactory(BindingContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + + return context.CanBuildInnerChannelFactory(); + } + + public override IChannelListener BuildChannelListener(BindingContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + + context.BindingParameters.Add(this); + return context.BuildInnerChannelListener(); + } + + public override bool CanBuildChannelListener(BindingContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + + context.BindingParameters.Add(this); + return context.CanBuildInnerChannelListener(); + } + + + public override BindingElement Clone() + { + return new RawMessageEncodingBindingElement(this); + } + + + + public override MessageVersion MessageVersion + { + get + { + return MessageVersion.None; + } + + set + { + if (value != MessageVersion.None) + throw new ArgumentException("Unsupported message version"); + } + } + + + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/RawXmlReader.cs b/qpid/wcf/src/Apache/Qpid/Channel/RawXmlReader.cs new file mode 100644 index 0000000000..8fadfce441 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/RawXmlReader.cs @@ -0,0 +1,353 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.IO; + using System.Xml; + + internal class RawXmlReader : XmlDictionaryReader + { + ////this class presents a hardcoded XML InfoSet: "X" where X is the entire stream content + + private Stream stream; + private bool closed; + private bool streamOwner; + private ReaderPosition position; + private string contentAsBase64; + private XmlNameTable xmlNameTable; + private XmlDictionaryReaderQuotas readerQuotas; + + public RawXmlReader(Stream stream, XmlDictionaryReaderQuotas quotas, bool streamOwner) + { + this.stream = stream; + this.streamOwner = streamOwner; + if (quotas == null) + { + this.readerQuotas = new XmlDictionaryReaderQuotas(); + } + else + { + this.readerQuotas = quotas; + } + } + + private enum ReaderPosition + { + None, + StartElement, + Content, + EndElement, + EOF + } + + public override int AttributeCount + { + get { return 0; } + } + + public override string BaseURI + { + get { return string.Empty; } + } + + public override int Depth + { + get { return (this.position == ReaderPosition.Content) ? 1 : 0; } + } + + public override bool EOF + { + get { return this.position == ReaderPosition.EOF; } + } + + public override bool HasAttributes + { + get { return false; } + } + + public override bool HasValue + { + get { return this.position == ReaderPosition.Content; } + } + + public override bool IsEmptyElement + { + get { return false; } + } + + public override string LocalName + { + get + { + if (this.position == ReaderPosition.StartElement) + { + return RawMessageEncoder.StreamElementName; + } + + return null; + } + } + + public override string NamespaceURI + { + get { return string.Empty; } + } + + public override XmlNameTable NameTable + { + get + { + if (this.xmlNameTable == null) + { + this.xmlNameTable = new NameTable(); + this.xmlNameTable.Add(RawMessageEncoder.StreamElementName); + } + + return this.xmlNameTable; + } + } + + public override XmlNodeType NodeType + { + get + { + switch (this.position) + { + case ReaderPosition.StartElement: + return XmlNodeType.Element; + case ReaderPosition.Content: + return XmlNodeType.Text; + case ReaderPosition.EndElement: + return XmlNodeType.EndElement; + default: + // and StreamPosition.EOF + return XmlNodeType.None; + } + } + } + + public override string Prefix + { + get { return string.Empty; } + } + + public override ReadState ReadState + { + get + { + switch (this.position) + { + case ReaderPosition.None: + return ReadState.Initial; + case ReaderPosition.StartElement: + case ReaderPosition.Content: + case ReaderPosition.EndElement: + return ReadState.Interactive; + case ReaderPosition.EOF: + return ReadState.Closed; + default: + return ReadState.Error; + } + } + } + + public override string Value + { + get + { + switch (this.position) + { + case ReaderPosition.Content: + if (this.contentAsBase64 == null) + { + this.contentAsBase64 = Convert.ToBase64String(this.ReadContentAsBase64()); + } + + return this.contentAsBase64; + + default: + return string.Empty; + } + } + } + + public override void Close() + { + if (!this.closed) + { + this.closed = true; + this.position = ReaderPosition.EOF; + this.readerQuotas = null; + if (this.streamOwner) + { + this.stream.Close(); + } + } + } + + public override string GetAttribute(int i) + { + throw new ArgumentOutOfRangeException("i", i, "Argument not in set of valid values"); + } + + public override string GetAttribute(string name, string namespaceURI) + { + return null; + } + + public override string GetAttribute(string name) + { + return null; + } + + public override string LookupNamespace(string prefix) + { + if (prefix == string.Empty) + { + return string.Empty; + } + else if (prefix == "xml") + { + return "http://www.w3.org/XML/1998/namespace"; + } + else if (prefix == "xmlns") + { + return "http://www.w3.org/2000/xmlns/"; + } + else + { + return null; + } + } + + public override bool MoveToAttribute(string name, string ns) + { + return false; + } + + public override bool MoveToAttribute(string name) + { + return false; + } + + public override bool MoveToElement() + { + if (this.position == ReaderPosition.None) + { + this.position = ReaderPosition.StartElement; + return true; + } + + return false; + } + + public override bool MoveToFirstAttribute() + { + return false; + } + + public override bool MoveToNextAttribute() + { + return false; + } + + public override bool Read() + { + switch (this.position) + { + case ReaderPosition.None: + this.position = ReaderPosition.StartElement; + return true; + case ReaderPosition.StartElement: + this.position = ReaderPosition.Content; + return true; + case ReaderPosition.Content: + this.position = ReaderPosition.EndElement; + return true; + case ReaderPosition.EndElement: + this.position = ReaderPosition.EOF; + return false; + case ReaderPosition.EOF: + return false; + default: + return false; + } + } + + public override bool ReadAttributeValue() + { + return false; + } + + public override int ReadContentAsBase64(byte[] buffer, int index, int count) + { + if (buffer == null) + { + throw new ArgumentNullException("buffer"); + } + + if (this.position != ReaderPosition.Content) + { + throw new InvalidOperationException("XML reader not in Element"); + } + + if (count == 0) + { + return 0; + } + + int readCount = this.stream.Read(buffer, index, count); + if (readCount == 0) + { + this.position = ReaderPosition.EndElement; + } + + return readCount; + } + + public override int ReadContentAsBinHex(byte[] buffer, int index, int count) + { + throw new NotSupportedException(); + } + + public override void ResolveEntity() + { + throw new NotSupportedException(); + } + + public override bool TryGetBase64ContentLength(out int length) + { + // The whole stream is this one element + if (!this.closed && this.stream.CanSeek) + { + long streamLength = this.stream.Length; + if (streamLength <= int.MaxValue) + { + length = (int)streamLength; + return true; + } + } + + length = -1; + return false; + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/RawXmlWriter.cs b/qpid/wcf/src/Apache/Qpid/Channel/RawXmlWriter.cs new file mode 100644 index 0000000000..7d05b70807 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/RawXmlWriter.cs @@ -0,0 +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. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.IO; + using System.Xml; + + internal sealed class RawXmlWriter : XmlDictionaryWriter + { + + WriteState state; + Stream stream; + bool closed; + bool rawWritingEnabled; + + public RawXmlWriter(Stream stream) + { + if (stream == null) + { + throw new ArgumentNullException("Stream"); + } + + this.stream = stream; + this.state = WriteState.Start; + } + + public override WriteState WriteState + { + get + { + return this.state; + } + } + + public override void Close() + { + if (!this.closed) + { + this.closed = true; + this.state = WriteState.Closed; + this.rawWritingEnabled = false; + } + } + + public override void Flush() + { + this.ThrowIfClosed(); + this.stream.Flush(); + } + + public override string LookupPrefix(string ns) + { + return null; + } + + public override void WriteBase64(byte[] buffer, int index, int count) + { + if (buffer == null) + { + throw new ArgumentNullException("buffer"); + } + + ThrowIfClosed(); + + if (!this.rawWritingEnabled) + { + throw new InvalidOperationException("XmlWriter not in Element"); + } + + this.stream.Write(buffer, index, count); + this.state = WriteState.Content; + } + + public override void WriteStartElement(string prefix, string localName, string ns) + { + ThrowIfClosed(); + if (this.state != WriteState.Start) + { + throw new InvalidOperationException("Start Element Already Called"); + } + + if (!string.IsNullOrEmpty(prefix) || !string.IsNullOrEmpty(ns) || localName != RawMessageEncoder.StreamElementName) + { + throw new XmlException("Wrong XML Start Element Name"); + } + this.state = WriteState.Element; + this.rawWritingEnabled = true; + } + + public override void WriteEndElement() + { + ThrowIfClosed(); + if (!this.rawWritingEnabled) + { + throw new InvalidOperationException("Unexpected End Element"); + } + this.rawWritingEnabled = false; + } + + public override void WriteFullEndElement() + { + this.WriteEndElement(); + } + + public override void WriteEndDocument() + { + this.rawWritingEnabled = false; + this.ThrowIfClosed(); + } + + public override void WriteStartDocument() + { + this.rawWritingEnabled = false; + this.ThrowIfClosed(); + } + + public override void WriteStartDocument(bool standalone) + { + this.rawWritingEnabled = false; + this.ThrowIfClosed(); + } + + private void ThrowIfClosed() + { + if (this.closed) + { + throw new InvalidOperationException("XML Writer closed"); + } + } + + + public override void WriteString(string text) + { + throw new NotSupportedException(); + } + + public override void WriteCData(string text) + { + throw new NotSupportedException(); + } + + public override void WriteCharEntity(char ch) + { + throw new NotSupportedException(); + } + + public override void WriteChars(char[] buffer, int index, int count) + { + throw new NotSupportedException(); + } + + public override void WriteComment(string text) + { + throw new NotSupportedException(); + } + + public override void WriteDocType(string name, string pubid, string sysid, string subset) + { + throw new NotSupportedException(); + } + + public override void WriteEndAttribute() + { + throw new NotSupportedException(); + } + + public override void WriteEntityRef(string name) + { + throw new NotSupportedException(); + } + + + public override void WriteProcessingInstruction(string name, string text) + { + throw new NotSupportedException(); + } + + public override void WriteRaw(string data) + { + throw new NotSupportedException(); + } + + public override void WriteRaw(char[] buffer, int index, int count) + { + throw new NotSupportedException(); + } + + public override void WriteStartAttribute(string prefix, string localName, string ns) + { + throw new NotSupportedException(); + } + + public override void WriteSurrogateCharEntity(char lowChar, char highChar) + { + throw new NotSupportedException(); + } + + public override void WriteWhitespace(string ws) + { + throw new NotSupportedException(); + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Interop/AmqpConnection.cpp b/qpid/wcf/src/Apache/Qpid/Interop/AmqpConnection.cpp new file mode 100644 index 0000000000..02d6c7ab18 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/AmqpConnection.cpp @@ -0,0 +1,165 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#include +#include + +#include "qpid/client/AsyncSession.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/MessageListener.h" +#include "qpid/framing/FrameSet.h" + +#include "AmqpConnection.h" +#include "AmqpSession.h" +#include "QpidMarshal.h" +#include "QpidException.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Runtime::InteropServices; +using namespace msclr; + +using namespace qpid::client; +using namespace std; + + +// Note on locks: Use "this" for fast counting and idle/busy +// notifications. Use the "sessions" list to serialize session +// creation/reaping and overall tear down. +// TODO: switch "this" lock to separate non-visible Object. + + +AmqpConnection::AmqpConnection(String^ server, int port) : + connectionp(NULL), + busyCount(0), + disposed(false) +{ + bool success = false; + System::Exception^ openException = nullptr; + sessions = gcnew Collections::Generic::List(); + + try { + connectionp = new Connection; + connectionp->open (QpidMarshal::ToNative(server), port); + // TODO: registerFailureCallback for failover + success = true; + const ConnectionSettings& settings = connectionp->getNegotiatedSettings(); + this->maxFrameSize = settings.maxFrameSize; + } catch (const qpid::Exception& error) { + String^ errmsg = gcnew String(error.what()); + openException = gcnew QpidException(errmsg); + } finally { + if (!success) { + Cleanup(); + if (openException == nullptr) { + openException = gcnew QpidException ("unknown connection failure"); + } + throw openException; + } + } +} + +void AmqpConnection::Cleanup() +{ + { + lock l(sessions); + if (disposed) + return; + disposed = true; + } + + try { + // let the child sessions clean up + for each(AmqpSession^ s in sessions) { + s->ConnectionClosed(); + } + } + finally + { + if (connectionp != NULL) { + connectionp->close(); + delete connectionp; + connectionp = NULL; + } + } +} + +AmqpConnection::~AmqpConnection() +{ + Cleanup(); +} + +AmqpConnection::!AmqpConnection() +{ + Cleanup(); +} + +void AmqpConnection::Close() +{ + // Simulate Dispose()... + Cleanup(); + GC::SuppressFinalize(this); +} + +AmqpSession^ AmqpConnection::CreateSession() +{ + lock l(sessions); + if (disposed) { + throw gcnew ObjectDisposedException("AmqpConnection"); + } + AmqpSession^ session = gcnew AmqpSession(this, connectionp); + sessions->Add(session); + return session; +} + +// called whenever a child session becomes newly busy (a first reader or writer since last idle) + +void AmqpConnection::NotifyBusy() +{ + bool changed = false; + { + lock l(this); + if (busyCount++ == 0) + changed = true; + } +} + +// called whenever a child session becomes newly idle (a last reader or writer has closed) +// The connection is idle when none of its child sessions are busy + +void AmqpConnection::NotifyIdle() +{ + bool connectionIdle = false; + { + lock l(this); + if (--busyCount == 0) + connectionIdle = true; + } + if (connectionIdle) { + OnConnectionIdle(this, System::EventArgs::Empty); + } +} + + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/AmqpConnection.h b/qpid/wcf/src/Apache/Qpid/Interop/AmqpConnection.h new file mode 100644 index 0000000000..2641391e82 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/AmqpConnection.h @@ -0,0 +1,71 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#pragma once + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace std; +using namespace qpid::client; + +ref class AmqpSession; + +public delegate void ConnectionIdleEventHandler(Object^ sender, EventArgs^ eventArgs); + +public ref class AmqpConnection +{ +private: + Connection* connectionp; + void Cleanup(); + bool disposed; + Collections::Generic::List^ sessions; + bool isOpen; + int busyCount; + int maxFrameSize; + + internal: + void NotifyBusy(); + void NotifyIdle(); + + property int MaxFrameSize { + int get () { return maxFrameSize; } + } + +public: + AmqpConnection(System::String^ server, int port); + ~AmqpConnection(); + !AmqpConnection(); + void Close(); + AmqpSession^ CreateSession(); + event ConnectionIdleEventHandler^ OnConnectionIdle; + + property bool IsOpen { + bool get() { return isOpen; } + }; + + property bool IsIdle { + bool get() { return (busyCount == 0); } + } +}; + + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/AmqpMessage.cpp b/qpid/wcf/src/Apache/Qpid/Interop/AmqpMessage.cpp new file mode 100644 index 0000000000..5c333aff60 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/AmqpMessage.cpp @@ -0,0 +1,76 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#include +#include + +#include "qpid/client/AsyncSession.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/framing/AMQFrame.h" + +#include "MessageBodyStream.h" +#include "AmqpMessage.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Threading; +using namespace msclr; + +using namespace Apache::Qpid::AmqpTypes; + +AmqpMessage::AmqpMessage(MessageBodyStream ^mbs) : + messageBodyStream(mbs), + disposed(false) +{ +} + +void AmqpMessage::Cleanup() +{ + { + lock l(this); + if (disposed) + return; + + disposed = true; + } + + messageBodyStream->Close(); +} + +AmqpMessage::~AmqpMessage() +{ + Cleanup(); +} + +AmqpMessage::!AmqpMessage() +{ + Cleanup(); +} + +void AmqpMessage::Close() +{ + // Simulate Dispose()... + Cleanup(); + GC::SuppressFinalize(this); +} + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/AmqpMessage.h b/qpid/wcf/src/Apache/Qpid/Interop/AmqpMessage.h new file mode 100644 index 0000000000..f0801d30dc --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/AmqpMessage.h @@ -0,0 +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. +*/ + +#pragma once + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Runtime::InteropServices; + +using namespace qpid::client; +using namespace std; + + + +public ref class AmqpMessage +{ +private: + MessageBodyStream^ messageBodyStream; + AmqpTypes::AmqpProperties^ amqpProperties; + bool disposed; + void Cleanup(); + +internal: + AmqpMessage(MessageBodyStream ^bstream); + +public: + ~AmqpMessage(); + !AmqpMessage(); + void Close(); + + property AmqpTypes::AmqpProperties^ Properties { + AmqpTypes::AmqpProperties^ get () { return amqpProperties; } + void set(AmqpTypes::AmqpProperties^ p) { amqpProperties = p; } + } + + property System::IO::Stream^ BodyStream { + System::IO::Stream^ get() { return messageBodyStream; } + } +}; + + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.cpp b/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.cpp new file mode 100644 index 0000000000..bab73da74e --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.cpp @@ -0,0 +1,287 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#include +#include + +#include "qpid/client/AsyncSession.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/Connection.h" +#include "qpid/client/SessionImpl.h" +#include "qpid/client/SessionBase_0_10Access.h" +#include "qpid/client/Message.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/client/Future.h" + +#include "AmqpConnection.h" +#include "AmqpSession.h" +#include "AmqpMessage.h" +#include "MessageBodyStream.h" +#include "InputLink.h" +#include "OutputLink.h" +#include "QpidMarshal.h" +#include "QpidException.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Runtime::InteropServices; +using namespace msclr; + +using namespace qpid::client; +using namespace std; + + +AmqpSession::AmqpSession(AmqpConnection^ conn, qpid::client::Connection* qpidConnectionp) : + connection(conn), + sessionp(NULL), + sessionImplp(NULL), + subs_mgrp(NULL), + openCount(0) +{ + bool success = false; + + try { + sessionp = new qpid::client::AsyncSession; + *sessionp = qpidConnectionp->newSession(); + subs_mgrp = new SubscriptionManager (*sessionp); + success = true; + waiters = gcnew Collections::Generic::List(); + } finally { + if (!success) { + Cleanup(); + throw gcnew QpidException ("session creation failure"); + } + } +} + + +void AmqpSession::Cleanup() +{ + if (subscriptionp != NULL) { + subscriptionp->cancel(); + delete subscriptionp; + subscriptionp=NULL; + } + + if (subs_mgrp != NULL) { + subs_mgrp->stop(); + delete subs_mgrp; + subs_mgrp = NULL; + } + + if (localQueuep != NULL) { + delete localQueuep; + localQueuep = NULL; + } + + if (sessionp != NULL) { + sessionp->close(); + delete sessionp; + sessionp = NULL; + sessionImplp = NULL; + } + + if (connectionp != NULL) { + connectionp->close(); + delete connectionp; + connectionp = NULL; + } +} + + +// Called by the parent AmqpConnection + +void AmqpSession::ConnectionClosed() +{ + Cleanup(); +} + +InputLink^ AmqpSession::CreateInputLink(System::String^ sourceQueue) +{ + return CreateInputLink(sourceQueue, true, false, nullptr, nullptr); +} + +InputLink^ AmqpSession::CreateInputLink(System::String^ sourceQueue, bool exclusive, bool temporary, + System::String^ filterKey, System::String^ exchange) +{ + InputLink^ link = gcnew InputLink (this, sourceQueue, sessionp, subs_mgrp, exclusive, temporary, filterKey, exchange); + { + lock l(waiters); + if (openCount == 0) { + connection->NotifyBusy(); + } + openCount++; + } + return link; +} + +OutputLink^ AmqpSession::CreateOutputLink(System::String^ targetQueue) +{ + OutputLink^ link = gcnew OutputLink (this, targetQueue); + + lock l(waiters); + + if (sessionImplp == NULL) { + // not needed unless sending messages + SessionBase_0_10Access sa(*sessionp); + boost::shared_ptr sip = sa.get(); + sessionImplp = sip.get(); + } + + if (openCount == 0) { + connection->NotifyBusy(); + } + openCount++; + + return link; +} + + +// called whenever a child InputLink or OutputLink is closed or finalized +void AmqpSession::NotifyClosed() +{ + lock l(waiters); + openCount--; + if (openCount == 0) { + connection->NotifyIdle(); + } +} + + +CompletionWaiter^ AmqpSession::SendMessage (System::String^ queue, MessageBodyStream ^mbody, TimeSpan timeout, bool async, AsyncCallback^ callback, Object^ state) +{ + lock l(waiters); + if (sessionp == NULL) + throw gcnew ObjectDisposedException("Send"); + + // create an AMQP message.transfer command to use with the partial frameset from the MessageBodyStream + + std::string exname = QpidMarshal::ToNative(queue); + FrameSet *framesetp = (FrameSet *) mbody->GetFrameSet().ToPointer(); + uint8_t acceptMode=1; + uint8_t acquireMode=0; + MessageTransferBody mtcmd(ProtocolVersion(0,10), exname, acceptMode, acquireMode); + // ask for a command completion + mtcmd.setSync(true); + + //send it + + Future *futurep = NULL; + try { + futurep = new Future(sessionImplp->send(mtcmd, *framesetp)); + + CompletionWaiter^ waiter = nullptr; + if (async || (timeout != TimeSpan::MaxValue)) { + waiter = gcnew CompletionWaiter(this, timeout, (IntPtr) futurep, callback, state); + // waiter is responsible for releasing the Future native resource + futurep = NULL; + addWaiter(waiter); + } + + l.release(); + + if (waiter != nullptr) + return waiter; + + // synchronous send with no timeout: no need to involve the asyncHelper thread + + internalWaitForCompletion((IntPtr) futurep); + } + finally { + if (futurep != NULL) + delete (futurep); + } + return nullptr; +} + +void AmqpSession::Bind(System::String^ queue, System::String^ exchange, System::String^ filterKey) +{ + sessionp->exchangeBind(arg::queue=QpidMarshal::ToNative(queue), + arg::exchange=QpidMarshal::ToNative(exchange), + arg::bindingKey=QpidMarshal::ToNative(filterKey)); + +} + + +void AmqpSession::internalWaitForCompletion(IntPtr fp) +{ + lock l(waiters); + if (sessionp == NULL) + throw gcnew ObjectDisposedException("AmqpSession"); + + // increment the smart pointer count to sessionImplp to guard agains async close + Session sessionCopy(*sessionp); + + l.release(); + // Qpid native lib call to wait for the command completion + ((Future *)fp.ToPointer())->wait(*sessionImplp); +} + +// call with lock held +void AmqpSession::addWaiter(CompletionWaiter^ waiter) +{ + waiters->Add(waiter); + if (!helperRunning) { + helperRunning = true; + ThreadPool::QueueUserWorkItem(gcnew WaitCallback(this, &AmqpSession::asyncHelper)); + } +} + + +void AmqpSession::removeWaiter(CompletionWaiter^ waiter) +{ + // a waiter can be removed from anywhere in the list if timed out + + lock l(waiters); + int idx = waiters->IndexOf(waiter); + if (idx == -1) { + // TODO: assert or log + } + else { + waiters->RemoveAt(idx); + } +} + + +// process CompletionWaiter list one at a time. + +void AmqpSession::asyncHelper(Object ^unused) +{ + lock l(waiters); + + while (true) { + if (waiters->Count == 0) { + helperRunning = false; + return; + } + + CompletionWaiter^ waiter = waiters[0]; + l.release(); + // can block, but for short time + // the waiter removes itself from the list, possibly as the timer thread on timeout + waiter->Run(); + l.acquire(); + } +} + + +}}} // namespace Apache::Qpid::Cli diff --git a/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.h b/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.h new file mode 100644 index 0000000000..b959a4123a --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.h @@ -0,0 +1,80 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#pragma once + +#include "AmqpConnection.h" +#include "MessageBodyStream.h" +#include "CompletionWaiter.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Runtime::InteropServices; + +using namespace qpid::client; +using namespace std; + +ref class InputLink; +ref class OutputLink; + +public ref class AmqpSession +{ +private: + AmqpConnection^ connection; + Connection* connectionp; + AsyncSession* sessionp; + SessionImpl* sessionImplp; + SubscriptionManager* subs_mgrp; + Subscription* subscriptionp; + LocalQueue* localQueuep; + Collections::Generic::List^ waiters; + bool helperRunning; + int openCount; + + void Cleanup(); + void asyncHelper(Object ^); + void addWaiter(CompletionWaiter^ waiter); + +public: + OutputLink^ CreateOutputLink(System::String^ targetQueue); + InputLink^ CreateInputLink(System::String^ sourceQueue); + + // 0-10 specific support + InputLink^ CreateInputLink(System::String^ sourceQueue, bool exclusive, bool temporary, System::String^ filterKey, System::String^ exchange); + void Bind(System::String^ queue, System::String^ exchange, System::String^ filterKey); + +internal: + AmqpSession(AmqpConnection^ connection, qpid::client::Connection* qpidConnection); + void NotifyClosed(); + CompletionWaiter^ SendMessage (System::String^ queue, MessageBodyStream ^mbody, TimeSpan timeout, bool async, AsyncCallback^ callback, Object^ state); + void ConnectionClosed(); + void internalWaitForCompletion(IntPtr Future); + void removeWaiter(CompletionWaiter^ waiter); + + property AmqpConnection^ Connection { + AmqpConnection^ get () { return connection; } + } + + +}; + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/AssemblyInfo.cpp b/qpid/wcf/src/Apache/Qpid/Interop/AssemblyInfo.cpp new file mode 100644 index 0000000000..91c23ae30a --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/AssemblyInfo.cpp @@ -0,0 +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. +*/ + +using namespace System; +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; +using namespace System::Runtime::InteropServices; +using namespace System::Security::Permissions; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly:AssemblyTitleAttribute("Apache.Qpid.Interop")]; +[assembly:AssemblyDescriptionAttribute("")]; +[assembly:AssemblyConfigurationAttribute("")]; +[assembly:AssemblyCompanyAttribute("")]; +[assembly:AssemblyProductAttribute("")]; +[assembly:AssemblyCopyrightAttribute("")]; +[assembly:AssemblyTrademarkAttribute("")]; +[assembly:AssemblyCultureAttribute("")]; + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the value or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly:AssemblyVersionAttribute("1.0.*")]; + +[assembly:ComVisible(false)]; + +[assembly:CLSCompliantAttribute(true)]; + +[assembly:SecurityPermission(SecurityAction::RequestMinimum, UnmanagedCode = true)]; diff --git a/qpid/wcf/src/Apache/Qpid/Interop/CompletionWaiter.cpp b/qpid/wcf/src/Apache/Qpid/Interop/CompletionWaiter.cpp new file mode 100644 index 0000000000..e39ee1b1ae --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/CompletionWaiter.cpp @@ -0,0 +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. +*/ + +#include +#include + +#include "qpid/client/AsyncSession.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/MessageListener.h" +#include "qpid/client/Demux.h" +#include "qpid/client/SessionImpl.h" +#include "qpid/client/SessionBase_0_10Access.h" + +#include "MessageBodyStream.h" +#include "AmqpMessage.h" +#include "AmqpSession.h" +#include "InputLink.h" +#include "CompletionWaiter.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Threading; +using namespace msclr; + +// A class to provide IAsyncResult semantics for a qpid AsyncSession command (i.e. 0-10 messageTransfer) +// when the client session receives a "Completion" notification from the Broker. + + +CompletionWaiter::CompletionWaiter(AmqpSession^ parent, TimeSpan timeSpan, IntPtr future, AsyncCallback^ callback, Object^ state) +{ + this->qpidFuture = future; + this->asyncCallback = callback; + this->state = state; + this->parent = parent; + this->thisLock = gcnew Object(); + // do this after the Completion Waiter is fully initialized, in case of + // very small timespan + if (timeSpan != TimeSpan::MaxValue) { + this->timer = gcnew Timer(timeoutCallback, this, timeSpan, TimeSpan::FromMilliseconds(-1)); + } +} + + +void CompletionWaiter::WaitForCompletion() +{ + if (isCompleted) + return; + + lock l(thisLock); + while (!isCompleted) { + Monitor::Wait(thisLock); + } +} + +void CompletionWaiter::Run() +{ + // no locks required in this method + if (isCompleted) + return; + + try { + // Wait for the arrival of the "AMQP Completion" indication from the Broker + parent->internalWaitForCompletion(qpidFuture); + } + catch (System::Exception^ e) { + runException = e; + } + finally { + delete(qpidFuture.ToPointer()); + qpidFuture = (IntPtr) NULL; + } + + if (timer != nullptr) { + timer->~Timer(); + timer = nullptr; + } + + Complete(false); +} + + +// "Complete" here means complete the AsyncResult, which may precede broker "command completion" if timed out + +void CompletionWaiter::Complete(bool isTimerThread) +{ + lock l(thisLock); + if (isCompleted) + return; + + isCompleted = true; + if (isTimerThread) + timedOut = true; + + Monitor::PulseAll(thisLock); + + // do this check and signal while locked + if (asyncWaitHandle != nullptr) + asyncWaitHandle->Set(); + + l.release(); + + parent->removeWaiter(this); + + if (asyncCallback != nullptr) { + // guard against application callback exception + try { + asyncCallback(this); + } + catch (System::Exception^) { + // log it? + } + } +} + + +void CompletionWaiter::TimeoutCallback(Object^ state) +{ + CompletionWaiter^ waiter = (CompletionWaiter^) state; + waiter->Complete(true); +} + + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/CompletionWaiter.h b/qpid/wcf/src/Apache/Qpid/Interop/CompletionWaiter.h new file mode 100644 index 0000000000..197ac632b0 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/CompletionWaiter.h @@ -0,0 +1,99 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#pragma once + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Threading; + +public ref class CompletionWaiter : IAsyncResult +{ +private: + bool timedOut; + // has an owner thread + bool assigned; + // can Run (i.e. earlier CompletionWaiters in the queue have completed) + System::Exception^ runException; + AsyncCallback^ asyncCallback; + Threading::Timer ^timer; + bool isCompleted; + Object^ state; + Object^ thisLock; + ManualResetEvent^ asyncWaitHandle; + AmqpSession^ parent; + IntPtr qpidFuture; + void Complete(bool isTimerThread); + static void TimeoutCallback(Object^ state); + static TimerCallback^ timeoutCallback = gcnew TimerCallback(CompletionWaiter::TimeoutCallback); + + internal: + CompletionWaiter(AmqpSession^ parent, TimeSpan timeSpan, IntPtr future, AsyncCallback ^callback, Object^ state); + + void Run(); + void WaitForCompletion(); + + property bool Assigned { + bool get () { return assigned; } + } + + property bool TimedOut { + bool get () { return timedOut; } + } + + + public: + + virtual property bool IsCompleted { + bool get () { return isCompleted; } + } + + virtual property bool CompletedSynchronously { + bool get () { return false; } + } + + virtual property WaitHandle^ AsyncWaitHandle { + WaitHandle^ get () { + if (asyncWaitHandle != nullptr) { + return asyncWaitHandle; + } + + msclr::lock l(thisLock); + if (asyncWaitHandle == nullptr) { + asyncWaitHandle = gcnew ManualResetEvent(isCompleted); + } + return asyncWaitHandle; + } + } + + + virtual property Object^ AsyncState { + Object^ get () { return state; } + } + + + + +}; + +}}} // namespace Apache::Qpid::Interop + diff --git a/qpid/wcf/src/Apache/Qpid/Interop/InputLink.cpp b/qpid/wcf/src/Apache/Qpid/Interop/InputLink.cpp new file mode 100644 index 0000000000..cee394b05d --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/InputLink.cpp @@ -0,0 +1,685 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#include +#include + +#include "qpid/client/AsyncSession.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/MessageListener.h" +#include "qpid/client/Demux.h" +#include "qpid/client/SessionImpl.h" +#include "qpid/client/SessionBase_0_10Access.h" + +#include "MessageBodyStream.h" +#include "AmqpMessage.h" +#include "AmqpSession.h" +#include "InputLink.h" +#include "QpidMarshal.h" +#include "QpidException.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + + +using namespace System; +using namespace System::Runtime::InteropServices; +using namespace System::Threading; +using namespace msclr; + +using namespace qpid::client; +using namespace qpid::framing; + +using namespace std; + +using namespace Apache::Qpid::AmqpTypes; + +// Scalability note: When using async methods, an async helper thread is created +// to block on the Demux BlockingQueue. This design should be revised in line +// with proposed changes to the native library to reduce the number of servicing +// threads for large numbers of subscriptions. + + +// The folowing def must match the "Frames" private typedef. +// TODO, make Qpid-cpp "Frames" definition visible. +typedef qpid::InlineVector FrameSetFrames; + +InputLink::InputLink(AmqpSession^ session, System::String^ sourceQueue, + qpid::client::AsyncSession *qpidSessionp, qpid::client::SubscriptionManager *qpidSubsMgrp, + bool exclusive, + bool temporary, System::String^ filterKey, System::String^ exchange) : + amqpSession(session), + subscriptionp(NULL), + localQueuep(NULL), + queuePtrp(NULL), + dequeuedFrameSetpp(NULL), + disposed(false), + finalizing(false) +{ + bool success = false; + System::Exception^ linkException = nullptr; + + waiters = gcnew Collections::Generic::List(); + + try { + std::string qname = QpidMarshal::ToNative(sourceQueue); + + if (temporary) { + qpidSessionp->queueDeclare(arg::queue=qname, arg::durable=false, arg::autoDelete=true, arg::exclusive=true); + qpidSessionp->exchangeBind(arg::exchange=QpidMarshal::ToNative(exchange), + arg::queue=qname, arg::bindingKey=QpidMarshal::ToNative(filterKey)); + qpidSessionp->sync(); + } + + localQueuep = new LocalQueue; + SubscriptionSettings settings; + settings.flowControl = FlowControl::messageCredit(0); + Subscription sub = qpidSubsMgrp->subscribe(*localQueuep, qname, settings); + subscriptionp = new Subscription (sub); // copy smart pointer for later IDisposable cleanup + + // the roundabout way to obtain localQueuep->queue + SessionBase_0_10Access sa(*qpidSessionp); + boost::shared_ptr simpl = sa.get(); + queuePtrp = new Demux::QueuePtr(simpl->getDemux().get(sub.getName())); + + success = true; + } finally { + if (!success) { + Cleanup(); + linkException = gcnew QpidException ("InputLink creation failure"); + throw linkException; + } + } +} + +void InputLink::ReleaseNative() +{ + // involves talking to the Broker unless the connection is broken + if (subscriptionp != NULL) { + try { + subscriptionp->cancel(); + } + catch (const std::exception& error) { + // TODO: log this properly + std::cout << "shutdown error " << error.what() << std::endl; + } + } + + // free native mem (or smart pointers) that we own + if (subscriptionp != NULL) + delete subscriptionp; + if (queuePtrp != NULL) + delete queuePtrp; + if (localQueuep != NULL) + delete localQueuep; + if (dequeuedFrameSetpp != NULL) + delete dequeuedFrameSetpp; +} + +void InputLink::Cleanup() +{ + { + lock l(waiters); + if (disposed) + return; + + disposed = true; + + // if the asyncHelper exists and is idle, unblock it + if (asyncHelperWaitHandle != nullptr) { + asyncHelperWaitHandle->Set(); + } + + // wakeup anyone waiting for messages + if (queuePtrp != NULL) + (*queuePtrp)->close(); + + try {} + finally + { + ReleaseNative(); + } + + } + amqpSession->NotifyClosed(); +} + +InputLink::~InputLink() +{ + Cleanup(); +} + +InputLink::!InputLink() +{ + Cleanup(); +} + +void InputLink::Close() +{ + // Simulate Dispose()... + Cleanup(); + GC::SuppressFinalize(this); +} + +// call with lock held +bool InputLink::haveMessage() +{ + if (dequeuedFrameSetpp != NULL) + return true; + + if (queuePtrp != NULL) { + if ((*queuePtrp)->size() > 0) + return true; + } + return false; +} + +IntPtr InputLink::nextLocalMessage() +{ + lock l(waiters); + if (disposed) + return (IntPtr) NULL; + + // A message already pulled off BlockingQueue? + if (dequeuedFrameSetpp != NULL) { + QpidFrameSetPtr* rv = dequeuedFrameSetpp; + dequeuedFrameSetpp = NULL; + return (IntPtr) rv; + } + + if ((*queuePtrp)->empty()) + return (IntPtr) NULL; + + bool received = false; + QpidFrameSetPtr* frameSetpp = new QpidFrameSetPtr; + + try { + received = (*queuePtrp)->pop(*frameSetpp, qpid::sys::TIME_INFINITE); + if (received) { + QpidFrameSetPtr* rv = frameSetpp; + // no need to free native in finally block + frameSetpp = NULL; + return (IntPtr) rv; + } + } catch(const std::exception& error) { + // should be no async tampering with queue since we hold the lock and have a + // smart pointer ref to the native LocalQueue, even if the network connection fails... + cout << "unknown exception in InputLink.nextLocalMessage() " << error.what() <close(); +} + + + +// Set things right after unblockWaiter(). Closing and opening a Qpid BlockingQueue unsticks +// a blocking thread without interefering with queue contents or the ability to push +// new incoming messages. + +void InputLink::resetQueue() +{ + lock l(waiters); + if (disposed) + return; + if ((*queuePtrp)->isClosed()) { + (*queuePtrp)->open(); + } +} + + +// returns true if there is a message to consume, i.e. nextLocalMessage() won't block + +bool InputLink::internalWaitForMessage() +{ + Demux::QueuePtr demuxQueuePtr; + + bool received = false; + QpidFrameSetPtr* frameSetpp = NULL; + try { + lock l(waiters); + if (disposed) + return false; + if (haveMessage()) + return true; + + // TODO: prefetch window of messages, compatible with both 0-10 and 1.0. + subscriptionp->grantMessageCredit(1); + + // get a scoped smart ptr ref to guard against async close or hangup + demuxQueuePtr = *queuePtrp; + frameSetpp = new QpidFrameSetPtr; + + l.release(); + // Async cleanup is now possible. Only use demuxQueuePtr until lock reacquired. + received = demuxQueuePtr->pop(*frameSetpp, qpid::sys::TIME_INFINITE); + l.acquire(); + + if (received) { + dequeuedFrameSetpp = frameSetpp; + frameSetpp = NULL; // native will eventually be freed in Cleanup or MessageBodyStream + } + + return true; + } catch(const std::exception& ) { + // timeout or connection closed + return false; + } + finally { + if (frameSetpp != NULL) { + delete frameSetpp; + } + } + + return false; +} + + +// call with lock held +void InputLink::addWaiter(MessageWaiter^ waiter) +{ + waiters->Add(waiter); + if (waiters->Count == 1) { + // mark this waiter as ready to run + // Only the waiter at the head of the queue is active. + waiter->Activate(); + } + + if (waiter->Assigned) + return; + + if (asyncHelperWaitHandle == nullptr) { + asyncHelperWaitHandle = gcnew ManualResetEvent(false); + ThreadStart^ threadDelegate = gcnew ThreadStart(this, &InputLink::asyncHelper); + (gcnew Thread(threadDelegate))->Start(); + } + + if (waiters->Count == 1) { + // wake up the asyncHelper + asyncHelperWaitHandle->Set(); + } +} + + +void InputLink::removeWaiter(MessageWaiter^ waiter) { + // a waiter can be removed from anywhere in the list if timed out + + lock l(waiters); + int idx = waiters->IndexOf(waiter); + if (idx == -1) { + // TODO: assert or log + if (asyncHelperWaitHandle != nullptr) { + // just in case. + asyncHelperWaitHandle->Set(); + } + return; + } + waiters->RemoveAt(idx); + + // let the next waiter know it's his turn. + if (waiters->Count > 0) { + MessageWaiter^ nextWaiter = waiters[0]; + + // wakeup the asyncHelper thread to help out if necessary. + if (!nextWaiter->Assigned) { + asyncHelperWaitHandle->Set(); + } + + l.release(); + nextWaiter->Activate(); + return; + } + else { + if (disposed && (asyncHelperWaitHandle != nullptr)) { + asyncHelperWaitHandle->Set(); + } + } +} + + +void InputLink::asyncHelper() +{ + lock l(waiters); + + while (true) { + if (disposed && (waiters->Count == 0)) { + asyncHelperWaitHandle = nullptr; + return; + } + + if (waiters->Count > 0) { + MessageWaiter^ waiter = waiters[0]; + + l.release(); + if (waiter->AcceptForWork()) { + waiter->Run(); + } + l.acquire(); + } + + // sleep if more work may be coming or it is currently someone else's turn + if (((waiters->Count == 0) && !disposed) || ((waiters->Count != 0) && waiters[0]->Assigned)) { + // wait for something to do + asyncHelperWaitHandle->Reset(); + l.release(); + asyncHelperWaitHandle->WaitOne(); + l.acquire(); + } + } +} + +void InputLink::sync() +{ + // for the timeout thread + lock l(waiters); +} + + +AmqpMessage^ InputLink::createAmqpMessage(IntPtr msgp) +{ + QpidFrameSetPtr* fspp = (QpidFrameSetPtr*) msgp.ToPointer(); + bool ownFrameSet = true; + bool haveProperties = false; + + try { + MessageBodyStream^ mstream = gcnew MessageBodyStream(fspp); + ownFrameSet = false; // stream releases on close/dispose + + AmqpMessage^ amqpMessage = gcnew AmqpMessage(mstream); + + AMQHeaderBody* headerBodyp = (*fspp)->getHeaders(); + uint64_t contentSize = (*fspp)->getContentSize(); + SequenceSet frameSetID((*fspp)->getId()); + + // target managed representation + AmqpProperties^ amqpProperties = gcnew AmqpProperties(); + + // source native representation + const DeliveryProperties* deliveryProperties = headerBodyp->get(); + const qpid::framing::MessageProperties* messageProperties = headerBodyp->get(); + + if (deliveryProperties) { + if (deliveryProperties->hasRoutingKey()) { + haveProperties = true; + + amqpProperties->RoutingKey = gcnew String(deliveryProperties->getRoutingKey().c_str()); + } + + if (deliveryProperties->hasDeliveryMode()) { + if (deliveryProperties->getDeliveryMode() == qpid::framing::PERSISTENT) + amqpProperties->Durable = true; + } + + if (deliveryProperties->hasTtl()) { + long long ticks = deliveryProperties->getTtl() * TimeSpan::TicksPerMillisecond; + amqpProperties->TimeToLive = Nullable(TimeSpan::FromTicks(ticks)); + } + } + + if (messageProperties) { + + if (messageProperties->hasReplyTo()) { + haveProperties = true; + const ReplyTo& rpto = messageProperties->getReplyTo(); + String^ rk = nullptr; + String^ ex = nullptr; + if (rpto.hasRoutingKey()) { + rk = gcnew String(rpto.getRoutingKey().c_str()); + } + if (rpto.hasExchange()) { + ex = gcnew String(rpto.getExchange().c_str()); + } + amqpProperties->SetReplyTo(ex,rk); + } + + if (messageProperties->hasContentType()) { + haveProperties = true; + amqpProperties->ContentType = gcnew String(messageProperties->getContentType().c_str()); + + if (messageProperties->hasContentEncoding()) { + String^ enc = gcnew String(messageProperties->getContentEncoding().c_str()); + if (!String::IsNullOrEmpty(enc)) { + // TODO: properly assemble 1.0 style to 0-10 for all cases + amqpProperties->ContentType += "; charset=" + enc; + } + } + } + + if (messageProperties->hasCorrelationId()) { + haveProperties = true; + const std::string& ncid = messageProperties->getCorrelationId(); + int len = ncid.size(); + array^ mcid = gcnew array(len); + Marshal::Copy ((IntPtr) (void *) ncid.data(), mcid, 0, len); + amqpProperties->CorrelationId = mcid; + } + + if (messageProperties->hasUserId()) { + haveProperties = true; + const std::string& nuid = messageProperties->getUserId(); + int len = nuid.size(); + array^ muid = gcnew array(len); + Marshal::Copy ((IntPtr) (void *) nuid.data(), muid, 0, len); + amqpProperties->UserId = muid; + } + + if (messageProperties->hasApplicationHeaders()) { + haveProperties = true; + const qpid::framing::FieldTable& fieldTable = messageProperties->getApplicationHeaders(); + int count = fieldTable.count(); + + if (count > 0) { + haveProperties = true; + Collections::Generic::Dictionary^ mmap = + gcnew Collections::Generic::Dictionary(count); + + for(qpid::framing::FieldTable::ValueMap::const_iterator i = fieldTable.begin(); i != fieldTable.end(); i++) { + + qpid::framing::FieldValue::Data &data = i->second->getData(); + + // TODO: replace these generic int/string conversions with handler for each AMQP specific type: + // uint8_t dataType = i->second->getType(); + // switch (dataType) { case TYPE_CODE_STR8: ... } + + if (data.convertsToInt()) { + mmap->Add (gcnew String(i->first.data()), gcnew AmqpInt((int) i->second->getData().getInt())); + } + if (data.convertsToString()) { + std::string ns = data.getString(); + String^ ms = gcnew String(ns.data(), 0, ns.size()); + mmap->Add (gcnew String(i->first.data()), gcnew AmqpString(ms)); + } + } + + amqpProperties->PropertyMap = mmap; + } + + } + } + + if (haveProperties) { + amqpMessage->Properties = amqpProperties; + } + + // We have a message we can return to the caller. + // Tell the broker we got it. + subscriptionp->accept(frameSetID); + return amqpMessage; + } + finally { + if (ownFrameSet) + delete (fspp); + } +} + + // As for IInputChannel: + // if success, return true + amqpMessage + // elseif timeout, return false + // elseif closed/EOF, return true and amqpMessage = null + // else throw an Exception + +bool InputLink::TryReceive(TimeSpan timeout, [Out] AmqpMessage^% amqpMessage) +{ + lock l(waiters); + + if (waiters->Count == 0) { + // see if there is a message already available without blocking + IntPtr fspp = nextLocalMessage(); + if (fspp.ToPointer() != NULL) { + amqpMessage = createAmqpMessage(fspp); + return true; + } + } + + MessageWaiter^ waiter = gcnew MessageWaiter(this, timeout, true, false, nullptr, nullptr); + addWaiter(waiter); + + l.release(); + waiter->Run(); + l.acquire(); + + if (waiter->TimedOut) { + return false; + } + + IntPtr waiterMsg = waiter->Message; + if (waiterMsg.ToPointer() == NULL) { + if (disposed) { + // indicate normal EOF on channel + amqpMessage = nullptr; + return true; + } + } + + amqpMessage = createAmqpMessage(waiterMsg); + return true; +} + +IAsyncResult^ InputLink::BeginTryReceive(TimeSpan timeout, AsyncCallback^ callback, Object^ state) +{ + + //TODO: if haveMessage() complete synchronously + + lock l(waiters); + MessageWaiter^ waiter = gcnew MessageWaiter(this, timeout, true, true, callback, state); + addWaiter(waiter); + return waiter; +} + +bool InputLink::EndTryReceive(IAsyncResult^ result, [Out] AmqpMessage^% amqpMessage) +{ + + // TODO: validate result + + MessageWaiter^ waiter = (MessageWaiter ^) result; + + waiter->WaitForCompletion(); + + if (waiter->RunException != nullptr) + throw waiter->RunException; + + if (waiter->TimedOut) { + amqpMessage = nullptr; + return false; + } + + IntPtr waiterMsg = waiter->Message; + if (waiterMsg.ToPointer() == NULL) { + if (disposed) { + // indicate normal EOF on channel + amqpMessage = nullptr; + return true; + } + } + + amqpMessage = createAmqpMessage(waiterMsg); + return true; +} + + +bool InputLink::WaitForMessage(TimeSpan timeout) +{ + lock l(waiters); + + if (waiters->Count == 0) { + // see if there is a message already available without blocking + if (haveMessage()) + return true; + } + + // Same as for TryReceive, except consuming = false + MessageWaiter^ waiter = gcnew MessageWaiter(this, timeout, false, false, nullptr, nullptr); + addWaiter(waiter); + + l.release(); + waiter->Run(); + l.acquire(); + + if (waiter->TimedOut) { + return false; + } + + return true; +} + +IAsyncResult^ InputLink::BeginWaitForMessage(TimeSpan timeout, AsyncCallback^ callback, Object^ state) +{ + lock l(waiters); + + // Same as for BeginTryReceive, except consuming = false + MessageWaiter^ waiter = gcnew MessageWaiter(this, timeout, false, true, callback, state); + addWaiter(waiter); + return waiter; +} + +bool InputLink::EndWaitForMessage(IAsyncResult^ result) +{ + MessageWaiter^ waiter = (MessageWaiter ^) result; + + waiter->WaitForCompletion(); + + if (waiter->TimedOut) { + return false; + } + + return true; +} + + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/InputLink.h b/qpid/wcf/src/Apache/Qpid/Interop/InputLink.h new file mode 100644 index 0000000000..366780c137 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/InputLink.h @@ -0,0 +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. +*/ + +#pragma once + +#include "MessageWaiter.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Threading; +using namespace System::Runtime::InteropServices; + +using namespace qpid::client; +using namespace std; + +// smart pointer to the low level AMQP 0-10 frames of the message +typedef qpid::framing::FrameSet::shared_ptr QpidFrameSetPtr; + +public ref class InputLink +{ +private: + AmqpSession^ amqpSession; + Subscription* subscriptionp; + LocalQueue* localQueuep; + Demux::QueuePtr* queuePtrp; + Collections::Generic::List^ waiters; + bool disposed; + bool finalizing; + QpidFrameSetPtr* dequeuedFrameSetpp; + ManualResetEvent^ asyncHelperWaitHandle; + + void Cleanup(); + void ReleaseNative(); + bool haveMessage(); + void addWaiter(MessageWaiter^ waiter); + void asyncHelper(); + AmqpMessage^ createAmqpMessage(IntPtr msgp); + +internal: + InputLink(AmqpSession^ session, System::String^ sourceQueue, qpid::client::AsyncSession *qpidSessionp, + qpid::client::SubscriptionManager *qpidSubsMgrp, bool exclusive, bool temporary, System::String^ filterKey, + System::String^ exchange); + + bool internalWaitForMessage(); + void unblockWaiter(); + void resetQueue(); + IntPtr nextLocalMessage(); + void removeWaiter(MessageWaiter^ waiter); + void sync(); + +public: + ~InputLink(); + !InputLink(); + void Close(); + + bool TryReceive(TimeSpan timeout, [Out] AmqpMessage ^% amqpMessage); + IAsyncResult^ BeginTryReceive(TimeSpan timeout, AsyncCallback^ callback, Object^ state); + bool EndTryReceive(IAsyncResult^ result, [Out] AmqpMessage^% amqpMessage); + + bool WaitForMessage(TimeSpan timeout); + IAsyncResult^ BeginWaitForMessage(TimeSpan timeout, AsyncCallback^ callback, Object^ state); + bool EndWaitForMessage(IAsyncResult^ result); + +}; + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/Interop.vcproj b/qpid/wcf/src/Apache/Qpid/Interop/Interop.vcproj new file mode 100644 index 0000000000..32f78c8344 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/Interop.vcproj @@ -0,0 +1,302 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qpid/wcf/src/Apache/Qpid/Interop/MessageBodyStream.cpp b/qpid/wcf/src/Apache/Qpid/Interop/MessageBodyStream.cpp new file mode 100644 index 0000000000..f2cb5740d3 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/MessageBodyStream.cpp @@ -0,0 +1,337 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#include +#include + +#include "qpid/client/AsyncSession.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/framing/AMQFrame.h" + +#include "MessageBodyStream.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Runtime::InteropServices; +using namespace System::Threading; +using namespace msclr; + +using namespace qpid::client; +using namespace qpid::framing; + +// Thefolowing def must match "Frames" private typedef. +// TODO: make "Frames" publicly visible. +typedef qpid::InlineVector FrameSetFrames; + +using namespace std; + +static void ThrowIfBadArgs (array^ buffer, int offset, int count) +{ + if (buffer == nullptr) + throw gcnew ArgumentNullException("buffer"); + + if (offset < 0) + throw gcnew ArgumentOutOfRangeException("offset"); + + if (count < 0) + throw gcnew ArgumentOutOfRangeException("count"); + + if ((offset + count) > buffer->Length) + throw gcnew ArgumentException("offset + count"); +} + + +// Input stream constructor + +MessageBodyStream::MessageBodyStream(FrameSet::shared_ptr *fspp) +{ + isInputStream = true; + frameSetpp = fspp; + fragmentCount = 0; + length = 0; + position = 0; + currentFramep = NULL; + + const std::string *datap; // pointer to the fragment's string variable that holds the content + + for(FrameSetFrames::const_iterator i = (*frameSetpp)->begin(); i != (*frameSetpp)->end(); i++) { + if (i->getBody()->type() == CONTENT_BODY) { + fragmentCount++; + datap = &(i->castBody()->getData()); + length += datap->size(); + } + } + + // fragmentCount can be zero for an empty message + + fragmentIndex = 0; + fragmentPosition = 0; + + if (fragmentCount == 0) { + currentFragment = NULL; + fragmentLength = 0; + } + else if (fragmentCount == 1) { + currentFragment = datap->data(); + fragmentLength = (int) length; + } + else { + fragments = gcnew array(fragmentCount); + fragmentIndex = 0; + for(FrameSetFrames::const_iterator i = (*frameSetpp)->begin(); i != (*frameSetpp)->end(); i++) { + if (i->getBody()->type() == CONTENT_BODY) { + datap = &(i->castBody()->getData()); + fragments[fragmentIndex++] = (IntPtr) (void *) datap; + } + } + fragmentIndex = 0; + datap = (const std::string *) fragments[0].ToPointer(); + currentFragment = datap->data(); + fragmentLength = datap->size(); + } +} + + +int MessageBodyStream::Read(array^ buffer, int offset, int count) +{ + if (!isInputStream) + throw gcnew NotSupportedException(); + if (disposed) + throw gcnew ObjectDisposedException("Stream"); + if (count == 0) + return 0; + ThrowIfBadArgs(buffer, offset, count); + + int nRead = 0; + int remaining = count; + + while (nRead < count) { + int fragAvail = fragmentLength - fragmentPosition; + int copyCount = min (fragAvail, remaining); + if (copyCount == 0) { + // no more to read + return nRead; + } + + // copy from native space + IntPtr nativep = (IntPtr) (void *) (currentFragment + fragmentPosition); + Marshal::Copy (nativep, buffer, offset, copyCount); + nRead += copyCount; + remaining -= copyCount; + fragmentPosition += copyCount; + offset += copyCount; + + // advance to next fragment? + if (fragmentPosition == fragmentLength) { + if (++fragmentIndex < fragmentCount) { + const std::string *datap = (const std::string *) fragments[fragmentIndex].ToPointer(); + currentFragment = datap->data(); + fragmentLength = datap->size(); + fragmentPosition = 0; + } + } + } + + return nRead; +} + + +void MessageBodyStream::pushCurrentFrame(bool lastFrame) +{ + // set flags as in SessionImpl::sendContent. + if (currentFramep->getBody()->type() == CONTENT_BODY) { + + if ((fragmentCount == 1) && lastFrame) { + // only one content frame + currentFramep->setFirstSegment(false); + } + else { + currentFramep->setFirstSegment(false); + currentFramep->setLastSegment(true); + if (fragmentCount != 1) { + currentFramep->setFirstFrame(false); + } + if (!lastFrame) { + currentFramep->setLastFrame(false); + } + } + } + else { + // the header frame + currentFramep->setFirstSegment(false); + if (!lastFrame) { + // there will be at least one content frame + currentFramep->setLastSegment(false); + } + } + + // add to frame set. This makes a copy and ref counts the body + (*frameSetpp)->append(*currentFramep); + + delete currentFramep; + + currentFramep = NULL; +} + + +IntPtr MessageBodyStream::GetFrameSet() +{ + if (currentFramep != NULL) { + // No more content. Tidy up the pending (possibly single header) frame. + pushCurrentFrame(true); + } + + if (frameSetpp == NULL) { + return (IntPtr) NULL; + } + + // shared_ptr.get() + return (IntPtr) (void *) (*frameSetpp).get(); +} + +IntPtr MessageBodyStream::GetHeader() +{ + return (IntPtr) headerBodyp; +} + + +// Ouput stream constructor + +MessageBodyStream::MessageBodyStream(int maxFrameSize) +{ + isInputStream = false; + + maxFrameContentSize = maxFrameSize - AMQFrame::frameOverhead(); + SequenceNumber unused; // only meaningful on incoming frames + frameSetpp = new FrameSet::shared_ptr(new FrameSet(unused)); + fragmentCount = 0; + length = 0; + position = 0; + + // header goes first in the outgoing frameset + + boost::intrusive_ptr headerBody(new AMQHeaderBody); + currentFramep = new AMQFrame(headerBody); + headerBodyp = static_cast(headerBody.get()); + + // mark this header frame as "full" to force the first write to create a new content frame + fragmentPosition = maxFrameContentSize; +} + +void MessageBodyStream::Write(array^ buffer, int offset, int count) +{ + if (isInputStream) + throw gcnew NotSupportedException(); + if (disposed) + throw gcnew ObjectDisposedException("Stream"); + if (count == 0) + return; + ThrowIfBadArgs(buffer, offset, count); + + if (currentFramep == NULL) { + // GetFrameSet() has been called and we no longer exclusively own the underlying frames. + throw gcnew InvalidOperationException ("Mesage Body output already completed"); + } + + if (count <= 0) + return; + + // keep GC memory movement at bay while copying to native space + pin_ptr pinnedBuf = &buffer[0]; + + string *datap; + + int remaining = count; + while (remaining > 0) { + if (fragmentPosition == maxFrameContentSize) { + // move to a new frame, but not until ready to add new content. + // zero content is valid, or the final write may exactly fill to maxFrameContentSize + + pushCurrentFrame(false); + + currentFramep = new AMQFrame(AMQContentBody()); + fragmentPosition = 0; + fragmentCount++; + } + + int copyCount = min (remaining, (maxFrameContentSize - fragmentPosition)); + datap = &(currentFramep->castBody()->getData()); + + char *outp = (char *) pinnedBuf + offset; + if (fragmentPosition == 0) { + datap->assign(outp, copyCount); + } + else { + datap->append(outp, copyCount); + } + + position += copyCount; + fragmentPosition += copyCount; + remaining -= copyCount; + offset += copyCount; + } +} + + +void MessageBodyStream::Cleanup() +{ + { + lock l(this); + if (disposed) + return; + + disposed = true; + } + + try {} + finally + { + if (frameSetpp != NULL) { + delete frameSetpp; + frameSetpp = NULL; + } + if (currentFramep != NULL) { + delete currentFramep; + currentFramep = NULL; + } + } +} + +MessageBodyStream::~MessageBodyStream() +{ + Cleanup(); +} + +MessageBodyStream::!MessageBodyStream() +{ + Cleanup(); +} + +void MessageBodyStream::Close() +{ + // Simulate Dispose()... + Cleanup(); + GC::SuppressFinalize(this); +} + + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/MessageBodyStream.h b/qpid/wcf/src/Apache/Qpid/Interop/MessageBodyStream.h new file mode 100644 index 0000000000..fa8e3f6bde --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/MessageBodyStream.h @@ -0,0 +1,131 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#pragma once + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Runtime::InteropServices; + +using namespace qpid::client; +using namespace qpid::framing; +using namespace std; + + +// This class provides memory streaming of the message body contents +// between native and managed space. To avoid additional memory copies +// in native space, it reads and writes directly to the low level Qpid +// frames. + +public ref class MessageBodyStream : System::IO::Stream +{ +private: + bool isInputStream; + long long length; + long long position; + + // the boost smart pointer that keeps the message body frames in memory + FrameSet::shared_ptr *frameSetpp; + + int fragmentCount; + int fragmentIndex; + const char* currentFragment; + int fragmentPosition; + int fragmentLength; + array^ fragments; + + int maxFrameContentSize; + AMQFrame* currentFramep; + void* headerBodyp; + bool disposed; + bool finalizing; + void Cleanup(); + +internal: + // incoming message + MessageBodyStream(FrameSet::shared_ptr *fspp); + // outgoing message + MessageBodyStream(int maxFrameSize); + void pushCurrentFrame(bool last); +public: + ~MessageBodyStream(); + !MessageBodyStream(); + virtual void Close() override; + virtual int Read( + [InAttribute] [OutAttribute] array^ buffer, + int offset, + int count) override; + + virtual void Write( + array^ buffer, + int offset, + int count) override; + + + IntPtr GetFrameSet(); + IntPtr GetHeader(); + + virtual void Flush() override {} // noop + + + // TODO: see CanSeek below. + virtual long long Seek( + long long offset, + System::IO::SeekOrigin origin) override {throw gcnew System::NotSupportedException(); } + + // TODO: see CanSeek below. + virtual void SetLength( + long long value) override {throw gcnew System::NotSupportedException(); } + + virtual property long long Length { + long long get() override { return length; } + }; + + virtual property long long Position { + long long get() override { return position; } + void set(long long p) override { throw gcnew System::NotSupportedException(); } + }; + + + virtual property bool CanRead { + bool get () override { return isInputStream; } + } + + virtual property bool CanWrite { + bool get () override { return !isInputStream; } + } + + // Note: this class must return true to signal that the Length property works. + // Required by the raw message encoder. + // "If a class derived from Stream does not support seeking, calls to Length, + // SetLength, Position, and Seek throw a NotSupportedException". + + virtual property bool CanSeek { + bool get () override { return true; } + } + + virtual property bool CanTimeout { + bool get () override { return isInputStream; } + } +}; + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/MessageWaiter.cpp b/qpid/wcf/src/Apache/Qpid/Interop/MessageWaiter.cpp new file mode 100644 index 0000000000..f7a28b0692 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/MessageWaiter.cpp @@ -0,0 +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. +*/ + +#include +#include + +#include "qpid/client/AsyncSession.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/MessageListener.h" +#include "qpid/client/Demux.h" +#include "qpid/client/SessionImpl.h" +#include "qpid/client/SessionBase_0_10Access.h" + +#include "MessageBodyStream.h" +#include "AmqpMessage.h" +#include "AmqpSession.h" +#include "InputLink.h" +#include "MessageWaiter.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Threading; +using namespace msclr; + + +MessageWaiter::MessageWaiter(InputLink^ parent, TimeSpan timeSpan, bool consuming, bool async, AsyncCallback ^callback, Object^ state) +{ + this->consuming = consuming; + if (!consuming) { + GC::SuppressFinalize(this); + } + + if (async) { + this->async = true; + this->asyncCallback = callback; + this->state = state; + } + else { + this->assigned = true; + } + this->parent = parent; + this->thisLock = gcnew Object(); + + // do this after the Message Waiter is fully initialized, in case of + // very small timespan + if (timeSpan != TimeSpan::MaxValue) { + this->timer = gcnew Timer(timeoutCallback, this, timeSpan, TimeSpan::FromMilliseconds(-1)); + } +} + +MessageWaiter::~MessageWaiter() +{ + if (message != IntPtr::Zero) { + try{} + finally { + delete message.ToPointer(); + message = IntPtr::Zero; + } + } +} + +MessageWaiter::!MessageWaiter() +{ + this->~MessageWaiter(); +} + + +void MessageWaiter::WaitForCompletion() +{ + if (isCompleted) + return; + + lock l(thisLock); + while (!isCompleted) { + Monitor::Wait(thisLock); + } +} + +void MessageWaiter::Activate() +{ + if (activated) + return; + + lock l(thisLock); + if (!activated) { + activated = true; + Monitor::PulseAll(thisLock); + } +} + + +void MessageWaiter::Run() +{ + lock l(thisLock); + + // wait until Activate(), i.e. our turn in the waiter list or a timeout + while (!activated) { + Monitor::Wait(thisLock); + } + bool haveMessage = false; + bool mustReset = false; + + if (!timedOut) + blocking = true; + + if (blocking) { + l.release(); + + try { + haveMessage = parent->internalWaitForMessage(); + } + catch (System::Exception^ e) { + runException = e; + } + + l.acquire(); + blocking = false; + if (timedOut) { + // TimeoutCallback() called parent->unblockWaiter() + mustReset = true; + // let the timer thread move past critical region + while (processingTimeout) { + Monitor::Wait(thisLock); + } + } + } + + if (timer != nullptr) { + timer->~Timer(); + timer = nullptr; + } + + if (haveMessage) { + timedOut = false; // for the case timeout and message arrival are essentially tied + if (!consuming) { + // just waiting + haveMessage = false; + } + } + + if (haveMessage || mustReset) { + l.release(); + if (haveMessage) { + // hang on to it for when the async caller gets around to retrieving + message = parent->nextLocalMessage(); + } + if (mustReset) { + parent->resetQueue(); + } + l.acquire(); + } + + isCompleted = true; + Monitor::PulseAll(thisLock); + + // do this check and signal while locked + if (asyncWaitHandle != nullptr) + asyncWaitHandle->Set(); + + l.release(); + parent->removeWaiter(this); + + + if (asyncCallback != nullptr) { + // guard against application callback exception + try { + asyncCallback(this); + } + catch (System::Exception^) { + // log it? + } + } + +} + +bool MessageWaiter::AcceptForWork() +{ + lock l(thisLock); + if (!assigned) { + assigned = true; + return true; + } + return false; +} + +void MessageWaiter::TimeoutCallback(Object^ state) +{ + MessageWaiter^ waiter = (MessageWaiter^) state; + if (waiter->isCompleted) + return; + + // make sure parent has finished initializing us before we get going + waiter->parent->sync(); + + lock l(waiter->thisLock); + if (waiter->timer == nullptr) { + // the waiter is in the clean up phase and doesn't need a wakeup + return; + } + + // timedOut, blocking and processingTimeout work as a unit + waiter->timedOut = true; + if (waiter->blocking) { + // let the waiter know that we are busy with an upcoming unblock operation + waiter->processingTimeout = true; + } + + waiter->Activate(); + + if (waiter->processingTimeout) { + // call with lock off + l.release(); + waiter->parent->unblockWaiter(); + + // synchronize with blocked thread + l.acquire(); + waiter->processingTimeout = false; + Monitor::PulseAll(waiter->thisLock); + } + + l.release(); + + // If waiter has no associated thread, we must move it to completion + if (waiter->AcceptForWork()) { + waiter->Run(); // does not block since timedOut == true + } +} + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/MessageWaiter.h b/qpid/wcf/src/Apache/Qpid/Interop/MessageWaiter.h new file mode 100644 index 0000000000..3eb4919646 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/MessageWaiter.h @@ -0,0 +1,127 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#pragma once + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Threading; + +public ref class MessageWaiter : IAsyncResult +{ +private: + // Receive() or WaitForMessage() + bool consuming; + bool consumed; + bool timedOut; + bool async; + // has an owner thread + bool assigned; + // can Run (i.e. earlier MessageWaiters in the queue have completed) + bool activated; + // is making a call to internalWaitForMessage() which (usually) blocks + bool blocking; + // the timeout timer thread is lurking + bool processingTimeout; + // the saved exception from within Run() for async delivery + System::Exception^ runException; + AsyncCallback^ asyncCallback; + Threading::Timer ^timer; + bool isCompleted; + bool completedSynchronously; + Object^ state; + Object^ thisLock; + ManualResetEvent^ asyncWaitHandle; + InputLink^ parent; + static void TimeoutCallback(Object^ state); + static TimerCallback^ timeoutCallback = gcnew TimerCallback(MessageWaiter::TimeoutCallback); + IntPtr message; + !MessageWaiter(); + ~MessageWaiter(); + + internal: + MessageWaiter(InputLink^ parent, TimeSpan timeSpan, bool consuming, bool async, AsyncCallback ^callback, Object^ state); + + void Run(); + bool AcceptForWork(); + void Activate(); + void WaitForCompletion(); + +// inline void SetCompletedSynchronously (bool v) { completedSynchronously = v; } + + property IntPtr Message { + IntPtr get () { + if (!consuming || consumed) + throw gcnew InvalidOperationException("Message property"); + consumed = true; + IntPtr v = message; + message = IntPtr::Zero; + GC::SuppressFinalize(this); + return v; + } + // void set (IntPtr v) { message = v; } + } + + property bool Assigned { + bool get () { return assigned; } + } + + property bool TimedOut { + bool get () { return timedOut; } + } + + + property System::Exception^ RunException { + System::Exception^ get() { return runException; } + } + + public: + + virtual property bool IsCompleted { + bool get () { return isCompleted; } + } + + virtual property bool CompletedSynchronously { + bool get () { return completedSynchronously; } + } + + virtual property WaitHandle^ AsyncWaitHandle { + WaitHandle^ get () { + if (asyncWaitHandle != nullptr) { + return asyncWaitHandle; + } + + msclr::lock l(thisLock); + if (asyncWaitHandle == nullptr) { + asyncWaitHandle = gcnew ManualResetEvent(isCompleted); + } + return asyncWaitHandle; + } + } + + virtual property Object^ AsyncState { + Object^ get () { return state; } + } +}; + +}}} // namespace Apache::Qpid::Interop + diff --git a/qpid/wcf/src/Apache/Qpid/Interop/OutputLink.cpp b/qpid/wcf/src/Apache/Qpid/Interop/OutputLink.cpp new file mode 100644 index 0000000000..27725b8207 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/OutputLink.cpp @@ -0,0 +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. +*/ + +#include +#include + +#include "qpid/client/AsyncSession.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/MessageListener.h" + + +#include "AmqpSession.h" +#include "AmqpMessage.h" +#include "OutputLink.h" +#include "QpidMarshal.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Runtime::InteropServices; +using namespace System::Threading; +using namespace msclr; + +using namespace qpid::client; +using namespace std; + +using namespace Apache::Qpid::AmqpTypes; + + +OutputLink::OutputLink(AmqpSession^ session, String^ defaultQueue) : + amqpSession(session), + queue(defaultQueue), + disposed(false), + maxFrameSize(session->Connection->MaxFrameSize), + finalizing(false) +{ +} + +void OutputLink::Cleanup() +{ + { + lock l(this); + if (disposed) + return; + + disposed = true; + } + + amqpSession->NotifyClosed(); +} + +OutputLink::~OutputLink() +{ + Cleanup(); +} + +OutputLink::!OutputLink() +{ + Cleanup(); +} + +void OutputLink::Close() +{ + // Simulate Dispose()... + Cleanup(); + GC::SuppressFinalize(this); +} + + +AmqpMessage^ OutputLink::CreateMessage() +{ + MessageBodyStream ^mbody = gcnew MessageBodyStream(maxFrameSize); + AmqpMessage ^amqpm = gcnew AmqpMessage(mbody); + return amqpm; +} + + +void OutputLink::ManagedToNative(AmqpMessage^ m) +{ + MessageBodyStream^ messageBodyStream = (MessageBodyStream^ ) m->BodyStream; + + AmqpProperties^ mprops = m->Properties; + + if (mprops != nullptr) { + AMQHeaderBody* bodyp = (AMQHeaderBody*) messageBodyStream->GetHeader().ToPointer(); + + if (mprops->HasDeliveryProperties) { + DeliveryProperties* deliveryPropertiesp = bodyp->get(true); + + if (mprops->RoutingKey != nullptr) { + deliveryPropertiesp->setRoutingKey(QpidMarshal::ToNative(mprops->RoutingKey)); + } + + if (mprops->Durable) { + deliveryPropertiesp->setDeliveryMode(qpid::framing::PERSISTENT); + } + + if (mprops->TimeToLive.HasValue) { + long long ttl = mprops->TimeToLive.Value.Ticks; + bool was_positive = (ttl > 0); + if (ttl < 0) + ttl = 0; + ttl = ttl / TimeSpan::TicksPerMillisecond; + if ((ttl == 0) && was_positive) + ttl = 1; + deliveryPropertiesp->setTtl(ttl); + } + } + + if (mprops->HasMessageProperties) { + qpid::framing::MessageProperties* messagePropertiesp = + bodyp->get(true); + + String^ replyToExchange = mprops->ReplyToExchange; + String^ replyToRoutingKey = mprops->ReplyToRoutingKey; + if ((replyToExchange != nullptr) || (replyToRoutingKey != nullptr)) { + qpid::framing::ReplyTo nReplyTo; + if (replyToExchange != nullptr) { + nReplyTo.setExchange(QpidMarshal::ToNative(replyToExchange)); + } + if (replyToRoutingKey != nullptr) { + nReplyTo.setRoutingKey(QpidMarshal::ToNative(replyToRoutingKey)); + } + messagePropertiesp->setReplyTo(nReplyTo); + } + + // TODO: properly split 1.0 style to 0-10 content type + encoding + + String^ contentType = mprops->ContentType; + if (contentType != nullptr) { + String^ type = nullptr; + String^ enc = nullptr; + int idx = contentType->IndexOf(';'); + if (idx == -1) { + type = contentType; + } + else { + type = contentType->Substring(0, idx); + contentType = contentType->Substring(idx + 1); + idx = contentType->IndexOf('='); + if (idx != -1) { + enc = contentType->Substring(idx + 1); + enc = enc->Trim(); + } + } + if (!String::IsNullOrEmpty(type)) { + messagePropertiesp->setContentType(QpidMarshal::ToNative(type)); + } + if (!String::IsNullOrEmpty(enc)) { + messagePropertiesp->setContentEncoding(QpidMarshal::ToNative(enc)); + } + } + + + array^ mbytes = mprops->CorrelationId; + if (mbytes != nullptr) { + pin_ptr pinnedBuf = &mbytes[0]; + std::string s((char *) pinnedBuf, mbytes->Length); + messagePropertiesp->setCorrelationId(s); + } + + mbytes = mprops->UserId; + if (mbytes != nullptr) { + pin_ptr pinnedBuf = &mbytes[0]; + std::string s((char *) pinnedBuf, mbytes->Length); + messagePropertiesp->setUserId(s); + } + + if (mprops->HasMappedProperties) { + qpid::framing::FieldTable fieldTable; + // TODO: add support for abitrary AMQP types + for each (Collections::Generic::KeyValuePair kvp in mprops->PropertyMap) { + Type^ type = kvp.Value->GetType(); + if (type == AmqpInt::typeid) { + fieldTable.setInt(QpidMarshal::ToNative(kvp.Key), + ((AmqpInt ^) kvp.Value)->Value); + } + else if (type == AmqpString::typeid) { + AmqpString^ str = (AmqpString ^) kvp.Value; + // For now, FieldTable supports a single string type + fieldTable.setString(QpidMarshal::ToNative(kvp.Key), QpidMarshal::ToNative(str->Value)); + } + } + + messagePropertiesp->setApplicationHeaders(fieldTable); + } + } + } +} + + + +void OutputLink::Send(AmqpMessage^ amqpMessage, TimeSpan timeout) +{ + // copy properties from managed space to the native counterparts + ManagedToNative(amqpMessage); + + MessageBodyStream^ messageBodyStream = (MessageBodyStream^ ) amqpMessage->BodyStream; + CompletionWaiter^ waiter = amqpSession->SendMessage(queue, messageBodyStream, timeout, false, nullptr, nullptr); + + if (waiter != nullptr) { + waiter->WaitForCompletion(); + if (waiter->TimedOut) { + throw gcnew TimeoutException("Receive"); + } + } + // else: SendMessage() has already waited for the Completion + +} + +IAsyncResult^ OutputLink::BeginSend(AmqpMessage^ amqpMessage, TimeSpan timeout, AsyncCallback^ callback, Object^ state) +{ + ManagedToNative(amqpMessage); + + MessageBodyStream^ messageBodyStream = (MessageBodyStream^ ) amqpMessage->BodyStream; + CompletionWaiter^ waiter = amqpSession->SendMessage(queue, messageBodyStream, timeout, true, callback, state); + return waiter; +} + +void OutputLink::EndSend(IAsyncResult^ result) +{ + CompletionWaiter^ waiter = (CompletionWaiter ^) result; + waiter->WaitForCompletion(); + if (waiter->TimedOut) { + throw gcnew TimeoutException("Receive"); + } +} + + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/OutputLink.h b/qpid/wcf/src/Apache/Qpid/Interop/OutputLink.h new file mode 100644 index 0000000000..1f049a7412 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/OutputLink.h @@ -0,0 +1,64 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#pragma once + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Runtime::InteropServices; + +using namespace qpid::client; +using namespace std; + + +public ref class OutputLink +{ +private: + AmqpSession^ amqpSession; + String^ queue; + bool disposed; + bool finalizing; + void Cleanup(); + AmqpTypes::AmqpProperties^ defaultProperties; + void ManagedToNative(AmqpMessage^ m); + int maxFrameSize; + +internal: + OutputLink(AmqpSession^ session, String^ defaultQueue); + +public: + ~OutputLink(); + !OutputLink(); + void Close(); + AmqpMessage^ CreateMessage(); + void Send(AmqpMessage^ m, TimeSpan timeout); + IAsyncResult^ BeginSend(AmqpMessage^ amqpMessage, TimeSpan timeout, AsyncCallback^ callback, Object^ state); + void EndSend(IAsyncResult^ result); + + property AmqpTypes::AmqpProperties^ DefaultProperties { + AmqpTypes::AmqpProperties^ get () { return defaultProperties; } + void set(AmqpTypes::AmqpProperties^ p) { defaultProperties = p; } + } +}; + + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/QpidException.h b/qpid/wcf/src/Apache/Qpid/Interop/QpidException.h new file mode 100644 index 0000000000..91677a5e73 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/QpidException.h @@ -0,0 +1,37 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#pragma once + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; + +public ref class QpidException : System::Exception +{ + public: + + QpidException() : System::Exception() {} + QpidException(String^ estring) : System::Exception(estring) {} + +}; + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/QpidMarshal.h b/qpid/wcf/src/Apache/Qpid/Interop/QpidMarshal.h new file mode 100644 index 0000000000..3e22af7b39 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/QpidMarshal.h @@ -0,0 +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. +*/ + +#pragma once + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Text; + + +// Helper functions for marshaling. + +private ref class QpidMarshal +{ + public: + + // marshal_as not available in all Visual Studio editions. + + static std::string ToNative (System::String^ managed) { + if (managed->Length == 0) { + return std::string(); + } + array^ mbytes = Encoding::UTF8->GetBytes(managed); + if (mbytes->Length == 0) { + return std::string(); + } + + pin_ptr pinnedBuf = &mbytes[0]; + std::string native((char *) pinnedBuf, mbytes->Length); + return native; + } +}; + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/AsyncTest.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/AsyncTest.cs new file mode 100644 index 0000000000..bf20b5083d --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/AsyncTest.cs @@ -0,0 +1,190 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.Threading; + using NUnit.Framework; + + [TestFixture] + public class AsyncTest + { + private const int MessageCount = 20; + private const string Queue = "amqp:amq.direct?routingkey=routing_key"; + private Uri endpoint = new Uri("amqp:message_queue"); + private TimeSpan standardTimeout = TimeSpan.FromSeconds(10.0); // seconds + + [Test] + public void NonTryReceives() + { + this.SendMessages(this.standardTimeout, this.standardTimeout); + this.ReceiveNonTryMessages(this.standardTimeout, this.standardTimeout); + } + + [Test] + public void TryReceives() + { + this.SendMessages(this.standardTimeout, this.standardTimeout); + this.ReceiveTryMessages(this.standardTimeout, this.standardTimeout); + } + + [Test] + public void SmallTimeout() + { + // This code is commented out due to a bug in asynchronous channel open. + ////IChannelListener parentListener; + ////try + ////{ + //// this.RetrieveAsyncChannel(new Uri("amqp:fake_queue_do_not_create"), TimeSpan.FromMilliseconds(10.0), out parentListener); + //// parentListener.Close(); + //// Assert.Fail("Accepting the channel did not time out."); + ////} + ////catch (TimeoutException) + ////{ + //// // Intended exception. + ////} + + try + { + this.ReceiveNonTryMessages(this.standardTimeout, TimeSpan.FromMilliseconds(10.0)); + Assert.Fail("Receiving a message did not time out."); + } + catch (TimeoutException) + { + // Intended exception. + } + } + + private void SendMessages(TimeSpan channelTimeout, TimeSpan messageSendTimeout) + { + ChannelFactory channelFactory = + new ChannelFactory(Util.GetBinding(), Queue); + IOutputChannel proxy = channelFactory.CreateChannel(); + IAsyncResult[] resultArray = new IAsyncResult[MessageCount]; + + for (int i = 0; i < MessageCount; i++) + { + Message toSend = Message.CreateMessage(MessageVersion.Default, string.Empty, i); + resultArray[i] = proxy.BeginSend(toSend, messageSendTimeout, null, null); + } + + for (int j = 0; j < MessageCount; j++) + { + proxy.EndSend(resultArray[j]); + } + + IAsyncResult iocCloseResult = proxy.BeginClose(channelTimeout, null, null); + Thread.Sleep(TimeSpan.FromMilliseconds(50.0)); // Dummy work + proxy.EndClose(iocCloseResult); + + IAsyncResult chanFactCloseResult = channelFactory.BeginClose(channelTimeout, null, null); + Thread.Sleep(TimeSpan.FromMilliseconds(50.0)); // Dummy work + channelFactory.EndClose(chanFactCloseResult); + } + + private void ReceiveNonTryMessages(TimeSpan channelTimeout, TimeSpan messageTimeout) + { + IChannelListener inputChannelParentListener; + IInputChannel inputChannel = this.RetrieveAsyncChannel(this.endpoint, channelTimeout, out inputChannelParentListener); + + inputChannel.Open(); + + IAsyncResult[] resultArray = new IAsyncResult[MessageCount]; + try + { + for (int i = 0; i < MessageCount; i++) + { + resultArray[i] = inputChannel.BeginReceive(messageTimeout, null, null); + } + + for (int j = 0; j < MessageCount; j++) + { + inputChannel.EndReceive(resultArray[j]); + } + } + finally + { + IAsyncResult channelCloseResult = inputChannel.BeginClose(channelTimeout, null, null); + Thread.Sleep(TimeSpan.FromMilliseconds(50.0)); // Dummy work + inputChannel.EndClose(channelCloseResult); + + // Asynchronous listener close has not been implemented. + ////IAsyncResult listenerCloseResult = inputChannelParentListener.BeginClose(channelTimeout, null, null); + ////Thread.Sleep(TimeSpan.FromMilliseconds(50.0)); // Dummy work + ////inputChannelParentListener.EndClose(listenerCloseResult); + + inputChannelParentListener.Close(); + } + } + + private void ReceiveTryMessages(TimeSpan channelAcceptTimeout, TimeSpan messageReceiveTimeout) + { + IChannelListener listener = Util.GetBinding().BuildChannelListener(this.endpoint, new BindingParameterCollection()); + listener.Open(); + IInputChannel inputChannel = listener.AcceptChannel(channelAcceptTimeout); + IAsyncResult channelResult = inputChannel.BeginOpen(channelAcceptTimeout, null, null); + Thread.Sleep(TimeSpan.FromMilliseconds(50.0)); + inputChannel.EndOpen(channelResult); + + IAsyncResult[] resultArray = new IAsyncResult[MessageCount]; + + for (int i = 0; i < MessageCount; i++) + { + resultArray[i] = inputChannel.BeginTryReceive(messageReceiveTimeout, null, null); + } + + for (int j = 0; j < MessageCount; j++) + { + Message tempMessage; + Assert.True(inputChannel.EndTryReceive(resultArray[j], out tempMessage), "Did not successfully receive message #{0}", j); + } + + inputChannel.Close(); + listener.Close(); + } + + private IInputChannel RetrieveAsyncChannel(Uri queue, TimeSpan timeout, out IChannelListener parentListener) + { + IChannelListener listener = + Util.GetBinding().BuildChannelListener(queue, new BindingParameterCollection()); + listener.Open(); + IInputChannel inputChannel; + + try + { + IAsyncResult acceptResult = listener.BeginAcceptChannel(timeout, null, null); + Thread.Sleep(TimeSpan.FromMilliseconds(300.0)); // Dummy work + inputChannel = listener.EndAcceptChannel(acceptResult); + } + catch (TimeoutException e) + { + listener.Close(); + throw e; + } + finally + { + parentListener = listener; + } + return inputChannel; + } + } +} diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/CustomAmqpBindingTest.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/CustomAmqpBindingTest.cs new file mode 100644 index 0000000000..45a926ce4d --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/CustomAmqpBindingTest.cs @@ -0,0 +1,77 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System; + using System.Collections.Generic; + using System.Reflection; + using System.ServiceModel; + using System.Threading; + using NUnit.Framework; + + [TestFixture] + public class CustomAmqpBindingTest + { + private MessageClient client; + + [SetUp] + public void Setup() + { + // Create client + this.client = new MessageClient(); + this.client.NumberOfMessages = 3; + this.client.NumberOfIterations = 3; + + // Setup and start service + MessageService.EndpointAddress = "amqp:message_queue"; + MessageService.ContractTypes = new List(); + MessageService.ContractTypes.Add(typeof(IInteropService)); + MessageService.CompletionHandle = new EventWaitHandle(false, EventResetMode.AutoReset); + MessageService.IntendedInvocationCount = this.client.NumberOfIterations * this.client.NumberOfMessages * MessageService.ContractTypes.Count; + MessageService.StartService(Util.GetCustomBinding()); + } + + [Test] + public void Run() + { + // Create the WCF AMQP channel and send messages + MethodInfo runClientMethod = this.client.GetType().GetMethod("RunInteropClient"); + EndpointAddress address = new EndpointAddress("amqp:amq.direct?routingkey=routing_key"); + foreach (Type contractType in MessageService.ContractTypes) + { + MethodInfo runClientT = runClientMethod.MakeGenericMethod(contractType); + runClientT.Invoke(this.client, new object[] { address }); + } + + // Allow the WCF service to process all the messages before validation + MessageService.CompletionHandle.WaitOne(TimeSpan.FromSeconds(10.0), false); + + // Validation + int expectedMethodCallCount = this.client.NumberOfIterations * this.client.NumberOfMessages * MessageService.ContractTypes.Count; + Assert.AreEqual(expectedMethodCallCount, MessageService.TotalMethodCallCount); + } + + [TearDown] + public void Cleanup() + { + MessageService.StopService(); + } + } +} diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/FunctionalTests.csproj b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/FunctionalTests.csproj new file mode 100644 index 0000000000..b7f4ed5d0a --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/FunctionalTests.csproj @@ -0,0 +1,110 @@ + + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {E2D8C779-E417-40BA-BEE1-EE034268482F} + Library + Properties + Apache.Qpid.Test.Channel.Functional + Apache.Qpid.Test.Channel.Functional + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + 3.5 + + + 3.0 + + + 3.0 + + + 3.5 + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + {8AABAB30-7D1E-4539-B7D1-05450262BAD2} + Channel + + + {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34} + Interop + + + + + + + + + \ No newline at end of file diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IGenericObjectService.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IGenericObjectService.cs new file mode 100644 index 0000000000..a1ffac50b3 --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IGenericObjectService.cs @@ -0,0 +1,30 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System.ServiceModel; + + [ServiceContract(SessionMode = SessionMode.NotAllowed)] + public interface IGenericObjectService + { + [OperationContract(IsOneWay = true)] + void SendObject(object message); + } +} diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IInteropService.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IInteropService.cs new file mode 100644 index 0000000000..25f7010a89 --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IInteropService.cs @@ -0,0 +1,31 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System.ServiceModel; + using System.ServiceModel.Channels; + + [ServiceContract] + public interface IInteropService + { + [OperationContract(IsOneWay = true, Action = "*")] + void Hello(Message message); + } +} \ No newline at end of file diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService1.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService1.cs new file mode 100644 index 0000000000..bdbbb82f7e --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService1.cs @@ -0,0 +1,34 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System.ServiceModel; + using System.ServiceModel.Channels; + + [ServiceContract(SessionMode = SessionMode.NotAllowed)] + public interface IQueuedDatagramService1 + { + [OperationContract(IsOneWay = true)] + void Hello(string message); + + [OperationContract(IsOneWay = true)] + void Goodbye(); + } +} \ No newline at end of file diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService2.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService2.cs new file mode 100644 index 0000000000..565f7aa27b --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService2.cs @@ -0,0 +1,34 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System.ServiceModel; + using System.ServiceModel.Channels; + + [ServiceContract(SessionMode = SessionMode.NotAllowed)] + public interface IQueuedDatagramService2 + { + [OperationContract(IsOneWay = true)] + void Hello(string message); + + [OperationContract(IsOneWay = true)] + void Goodbye(); + } +} \ No newline at end of file diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService3.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService3.cs new file mode 100644 index 0000000000..3ff2085557 --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService3.cs @@ -0,0 +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. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System.ServiceModel; + + [ServiceContract(SessionMode = SessionMode.NotAllowed)] + public interface IQueuedDatagramService3 + { + [OperationContract(IsOneWay = true)] + void Hello(string message); + + [OperationContract(IsOneWay = true)] + void Goodbye(); + } +} \ No newline at end of file diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageBodyTest.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageBodyTest.cs new file mode 100644 index 0000000000..3fad6b336d --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageBodyTest.cs @@ -0,0 +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. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System; + using System.Collections.Generic; + using System.Runtime.Serialization; + using System.ServiceModel; + using System.ServiceModel.Channels; + using NUnit.Framework; + + [TestFixture] + public class MessageBodyTest + { + private const string Queue = "amqp:amq.direct?routingkey=routing_key"; + + [Test] + public void DateVariation() + { + DateTime rightNow = DateTime.UtcNow; + this.SendMessage(rightNow); + this.ReceiveMessage(rightNow); + } + + [Test] + public void EmptyStringVariation() + { + const string TestString = ""; + this.SendMessage(TestString); + this.ReceiveMessage(TestString); + } + + [Test] + public void IntPrimitiveVariation() + { + const int TheAnswer = 42; + this.SendMessage(TheAnswer); + this.ReceiveMessage(TheAnswer); + } + + [Test] + public void MultipleIntVariation() + { + const int NumberOfMessages = 20; + int[] listOfNumbers = new int[NumberOfMessages]; + + for (int i = 0; i < NumberOfMessages; i++) + { + this.SendMessage(i); + listOfNumbers[i] = i; + } + + Assert.True(listOfNumbers[NumberOfMessages - 1] != 0, "Not all messages were sent."); + + for (int j = 0; j < NumberOfMessages; j++) + { + int receivedNumber = this.ReceiveMessage(); + Assert.True(listOfNumbers[j].Equals(receivedNumber), "Received {0} - this number is unknown or has been received more than once.", receivedNumber); + } + } + + [Test] + public void StringVariation() + { + const string TestString = "The darkest of dim, dreary days dost draw deathly deeds. どーも"; + this.SendMessage(TestString); + this.ReceiveMessage(TestString); + } + + private void SendMessage(object objectToSend) + { + IChannelFactory channelFactory = + Util.GetBinding().BuildChannelFactory(); + channelFactory.Open(); + IOutputChannel proxy = channelFactory.CreateChannel(new EndpointAddress(Queue)); + proxy.Open(); + Message toSend = Message.CreateMessage(MessageVersion.Default, string.Empty, objectToSend); + proxy.Send(toSend); + toSend.Close(); + channelFactory.Close(); + } + + private TObjectType ReceiveMessage() + { + Uri endpoint = new Uri("amqp:message_queue"); + IChannelListener listener = Util.GetBinding().BuildChannelListener(endpoint, new BindingParameterCollection()); + listener.Open(); + IInputChannel service = listener.AcceptChannel(TimeSpan.FromSeconds(10)); + service.Open(); + Message receivedMessage = service.Receive(TimeSpan.FromSeconds(10)); + Assert.NotNull(receivedMessage, "Message was not received"); + try + { + TObjectType receivedObject = receivedMessage.GetBody(); + return receivedObject; + } + catch (SerializationException) + { + Assert.Fail("Deserialized object not of correct type"); + } + finally + { + receivedMessage.Close(); + service.Close(); + listener.Close(); + } + + return default(TObjectType); + } + + private TObjectType ReceiveMessage(TObjectType objectToMatch) + { + TObjectType receivedObject = this.ReceiveMessage(); + Assert.True(objectToMatch.Equals(receivedObject), "Original and deserialized objects do not match"); + return receivedObject; + } + } +} diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageClient.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageClient.cs new file mode 100644 index 0000000000..8f867551b1 --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageClient.cs @@ -0,0 +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. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System; + using System.Reflection; + using System.ServiceModel; + using System.ServiceModel.Channels; + + public class MessageClient + { + public int NumberOfMessages + { + get; + set; + } + + public int NumberOfIterations + { + get; + set; + } + + public void RunClient(EndpointAddress address) + { + Binding amqpBinding = Util.GetBinding(); + Type proxyType = typeof(TServiceContract); + MethodInfo helloMethod = proxyType.GetMethod("Hello"); + MethodInfo goodbyeMethod = proxyType.GetMethod("Goodbye"); + + string[] messages = new string[this.NumberOfMessages]; + for (int i = 0; i < this.NumberOfMessages; ++i) + { + messages[i] = "Message " + i; + } + + for (int i = 0; i < this.NumberOfIterations; ++i) + { + this.CreateChannelAndSendMessages(address, amqpBinding, helloMethod, goodbyeMethod, messages); + } + } + + public void RunInteropClient(EndpointAddress address) + { + Binding amqpBinding = Util.GetBinding(); + Type proxyType = typeof(TServiceContract); + MethodInfo helloMethod = proxyType.GetMethod("Hello"); + + Message[] messages = new Message[this.NumberOfMessages]; + + for (int i = 0; i < this.NumberOfIterations; ++i) + { + this.CreateInteropChannelAndSendMessages(address, amqpBinding, helloMethod, this.NumberOfMessages); + } + } + + private void CreateChannelAndSendMessages(EndpointAddress address, Binding amqpBinding, MethodInfo helloMethod, MethodInfo goodbyeMethod, object[] messages) + { + ChannelFactory channelFactory = new ChannelFactory(amqpBinding, address); + TServiceContract proxy = channelFactory.CreateChannel(); + + foreach (object message in messages) + { + helloMethod.Invoke(proxy, new object[] { message }); + } + + goodbyeMethod.Invoke(proxy, new object[0]); + channelFactory.Close(); + } + + private void CreateInteropChannelAndSendMessages(EndpointAddress address, Binding amqpBinding, MethodInfo helloMethod, int messageCount) + { + ChannelFactory channelFactory = new ChannelFactory(amqpBinding, address); + TServiceContract proxy = channelFactory.CreateChannel(); + + for (int i = 0; i < messageCount; i++) + { + helloMethod.Invoke(proxy, new object[] { Message.CreateMessage(MessageVersion.Soap12WSAddressing10, "*") }); + } + + channelFactory.Close(); + } + } +} diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageProperties.txt b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageProperties.txt new file mode 100644 index 0000000000..bd6459ccb9 --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageProperties.txt @@ -0,0 +1,22 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +ContentType=Text +Durable=true +RoutingKey=routing_key +TimeToLive=00:00:10 \ No newline at end of file diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessagePropertiesTest.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessagePropertiesTest.cs new file mode 100644 index 0000000000..8e192e90f1 --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessagePropertiesTest.cs @@ -0,0 +1,131 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System; + using System.Collections.Generic; + using System.Reflection; + using System.Runtime.Serialization; + using System.ServiceModel; + using System.ServiceModel.Channels; + using Apache.Qpid.AmqpTypes; + using NUnit.Framework; + + [TestFixture] + public class MessagePropertiesTest + { + private const string RoutingKey = "routing_key"; + private const string SendToUri = "amqp:amq.direct?routingkey=" + RoutingKey; + + [Test] + public void DefaultAmqpProperties() + { + const string TestString = "Test Message"; + AmqpProperties messageProperties = new AmqpProperties(); + + this.SendMessage(TestString, messageProperties); + this.ReceiveMessage(TestString, messageProperties); + } + + [Test] + public void NonDefaultAmqpProperties() + { + const string TestString = "Test Message"; + AmqpProperties messageProperties = this.CreateMessageProperties(); + + this.SendMessage(TestString, messageProperties); + this.ReceiveMessage(TestString, messageProperties); + } + + private AmqpProperties CreateMessageProperties() + { + Dictionary messageProperties = Util.GetProperties("..\\..\\MessageProperties.txt"); + + AmqpProperties amqpProperties = new AmqpProperties(); + amqpProperties.ContentType = (string)messageProperties["ContentType"]; + amqpProperties.Durable = Convert.ToBoolean((string)messageProperties["Durable"]); + amqpProperties.RoutingKey = (string)messageProperties["RoutingKey"]; + amqpProperties.TimeToLive = TimeSpan.Parse((string)messageProperties["TimeToLive"]); + + return amqpProperties; + } + + private void SendMessage(object objectToSend, AmqpProperties propertiesToSend) + { + ChannelFactory channelFactory = + new ChannelFactory(Util.GetBinding(), SendToUri); + IOutputChannel proxy = channelFactory.CreateChannel(); + proxy.Open(); + + Message toSend = Message.CreateMessage(MessageVersion.Default, string.Empty, objectToSend); + toSend.Properties["AmqpProperties"] = propertiesToSend; + proxy.Send(toSend); + + toSend.Close(); + proxy.Close(); + channelFactory.Close(); + } + + private void ReceiveMessage(TObjectType objectToMatch, AmqpProperties expectedProperties) + { + Uri receiveFromUri = new Uri("amqp:message_queue"); + IChannelListener listener = Util.GetBinding().BuildChannelListener(receiveFromUri, new BindingParameterCollection()); + listener.Open(); + IInputChannel service = listener.AcceptChannel(TimeSpan.FromSeconds(10)); + service.Open(); + Message receivedMessage = service.Receive(TimeSpan.FromSeconds(10)); + try + { + TObjectType receivedObject = receivedMessage.GetBody(); + Assert.True(receivedObject.Equals(objectToMatch), "Original and deserialized objects do not match"); + + AmqpProperties receivedProperties = (AmqpProperties)receivedMessage.Properties["AmqpProperties"]; + PropertyInfo[] propInfo = typeof(AmqpProperties).GetProperties(); + + for (int i = 0; i < propInfo.Length; i++) + { + string propertyName = propInfo[i].Name; + if (propertyName.Equals("RoutingKey", StringComparison.InvariantCultureIgnoreCase)) + { + Assert.AreEqual(RoutingKey, Convert.ToString(propInfo[i].GetValue(receivedProperties, null))); + } + else + { + Assert.AreEqual(Convert.ToString(propInfo[i].GetValue(expectedProperties, null)), Convert.ToString(propInfo[i].GetValue(receivedProperties, null))); + } + } + } + catch (NullReferenceException) + { + Assert.Fail("Message not received"); + } + catch (SerializationException) + { + Assert.Fail("Deserialized object not of correct type"); + } + finally + { + receivedMessage.Close(); + service.Close(); + listener.Close(); + } + } + } +} diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageService.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageService.cs new file mode 100644 index 0000000000..a473cad986 --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageService.cs @@ -0,0 +1,158 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System; + using System.Collections.Generic; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.Threading; + + public class MessageService : IQueuedDatagramService1, IQueuedDatagramService2, IQueuedDatagramService3, IInteropService + { + private static Dictionary methodCallCount = new Dictionary(); + private static ServiceHost serviceHost; + + public static EventWaitHandle CompletionHandle + { + get; + set; + } + + public static int IntendedInvocationCount + { + get; + set; + } + + public static int TotalMethodCallCount + { + get; + set; + } + + // The test must set these paramters + public static List ContractTypes + { + get; + set; + } + + public static string EndpointAddress + { + get; + set; + } + + public static void DisplayCounts() + { + Console.WriteLine("Method calls:"); + foreach (string key in methodCallCount.Keys) + { + Console.WriteLine(" {0}: {1}", key, methodCallCount[key]); + } + + Console.WriteLine("Total: {0}", TotalMethodCallCount); + } + + public static void StartService(Binding amqpBinding) + { + MessageService.methodCallCount.Clear(); + MessageService.TotalMethodCallCount = 0; + + serviceHost = new ServiceHost(typeof(MessageService)); + + foreach (Type contractType in ContractTypes) + { + serviceHost.AddServiceEndpoint(contractType, amqpBinding, EndpointAddress); + } + + serviceHost.Open(); + } + + public static void StopService() + { + if (serviceHost.State != CommunicationState.Faulted) + { + serviceHost.Close(); + } + } + + public void UpdateCounts(string method) + { + lock (methodCallCount) + { + if (!methodCallCount.ContainsKey(method)) + { + methodCallCount[method] = 0; + } + + ++methodCallCount[method]; + ++TotalMethodCallCount; + if (TotalMethodCallCount >= IntendedInvocationCount && CompletionHandle != null) + { + CompletionHandle.Set(); + } + } + } + + [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] + void IQueuedDatagramService1.Hello(string message) + { + this.UpdateCounts("IQueuedDatagramService1.Hello"); + } + + [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] + void IQueuedDatagramService1.Goodbye() + { + this.UpdateCounts("IQueuedDatagramService1.Goodbye"); + } + + [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] + void IQueuedDatagramService2.Hello(string message) + { + this.UpdateCounts("IQueuedDatagramService2.Hello"); + } + + [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] + void IQueuedDatagramService2.Goodbye() + { + this.UpdateCounts("IQueuedDatagramService2.Goodbye"); + } + + [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] + void IQueuedDatagramService3.Hello(string message) + { + this.UpdateCounts("IQueuedDatagramService3.Hello"); + } + + [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] + void IQueuedDatagramService3.Goodbye() + { + this.UpdateCounts("IQueuedDatagramService3.Goodbye"); + } + + [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] + void IInteropService.Hello(Message message) + { + this.UpdateCounts("IInteropService.Hello"); + } + } +} diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MultipleEndpointsSameQueueTest.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MultipleEndpointsSameQueueTest.cs new file mode 100644 index 0000000000..d09832757a --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MultipleEndpointsSameQueueTest.cs @@ -0,0 +1,83 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System; + using System.Collections.Generic; + using System.Reflection; + using System.ServiceModel; + using System.Threading; + using NUnit.Framework; + + [TestFixture] + public class MultipleEndpointsSameQueueTest + { + private MessageClient client; + + [SetUp] + public void Setup() + { + // Create client + this.client = new MessageClient(); + this.client.NumberOfMessages = 3; + this.client.NumberOfIterations = 5; + + // Setup and start service + MessageService.EndpointAddress = "amqp:message_queue"; + + MessageService.ContractTypes = new List(); + MessageService.ContractTypes.Add(typeof(IQueuedDatagramService1)); + MessageService.ContractTypes.Add(typeof(IQueuedDatagramService2)); + MessageService.ContractTypes.Add(typeof(IQueuedDatagramService3)); + MessageService.CompletionHandle = new EventWaitHandle(false, EventResetMode.AutoReset); + MessageService.IntendedInvocationCount = this.client.NumberOfIterations * (this.client.NumberOfMessages + 1) * MessageService.ContractTypes.Count; + + MessageService.StartService(Util.GetBinding()); + } + + [Test] + public void Run() + { + // Create wcf amqpchannel and send messages + MethodInfo runClientMethod = this.client.GetType().GetMethod("RunClient"); + EndpointAddress address = new EndpointAddress("amqp:amq.direct?routingkey=routing_key"); + + foreach (Type contractType in MessageService.ContractTypes) + { + MethodInfo runClientT = runClientMethod.MakeGenericMethod(contractType); + runClientT.Invoke(this.client, new object[] { address }); + } + + // Allow the wcf service to process all the messages before validation + MessageService.CompletionHandle.WaitOne(TimeSpan.FromSeconds(10.0), false); + + // Validation + int expectedMethodCallCount = this.client.NumberOfIterations * (this.client.NumberOfMessages + 1) * MessageService.ContractTypes.Count; + + Assert.AreEqual(expectedMethodCallCount, MessageService.TotalMethodCallCount); + } + + [TearDown] + public void Cleanup() + { + MessageService.StopService(); + } + } +} diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/Properties/AssemblyInfo.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..b47a25494f --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Apache.Qpid.Test.Channel.Functional")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("552dca74-b5a3-4ad3-a718-4a1dd03db039")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/RunTests.bat b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/RunTests.bat new file mode 100755 index 0000000000..4b83993257 --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/RunTests.bat @@ -0,0 +1,34 @@ +@echo OFF + +REM Licensed to the Apache Software Foundation (ASF) under one +REM or more contributor license agreements. See the NOTICE file +REM distributed with this work for additional information +REM regarding copyright ownership. The ASF licenses this file +REM to you under the Apache License, Version 2.0 (the +REM "License"); you may not use this file except in compliance +REM with the License. You may obtain a copy of the License at +REM +REM http://www.apache.org/licenses/LICENSE-2.0 +REM +REM Unless required by applicable law or agreed to in writing, +REM software distributed under the License is distributed on an +REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +REM KIND, either express or implied. See the License for the +REM specific language governing permissions and limitations +REM under the License. + + +set nunit_exe=%programfiles%\NUnit 2.5.1\bin\net-2.0\nunit-console.exe +set qpid_dll_location=..\..\..\..\..\..\..\cpp\build\src\Debug +set configuration_name=bin\Debug +set qcreate_location=..\..\..\..\..\..\tools\QCreate\Debug + +copy %qpid_dll_location%\qpidclient.dll %configuration_name% +copy %qpid_dll_location%\qpidcommon.dll %configuration_name% + +copy %qpid_dll_location%\qpidclient.dll %qcreate_location% +copy %qpid_dll_location%\qpidcommon.dll %qcreate_location% + +%qcreate_location%\QCreate.exe amq.direct routing_key message_queue + +"%nunit_exe%" %configuration_name%\Apache.Qpid.Test.Channel.Functional.dll diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/Util.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/Util.cs new file mode 100644 index 0000000000..97be1fb925 --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/Util.cs @@ -0,0 +1,74 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System.Collections.Generic; + using System.IO; + using System.ServiceModel; + using System.ServiceModel.Channels; + using Apache.Qpid.Channel; + + internal class Util + { + public static Dictionary GetProperties(string path) + { + string fileData = string.Empty; + using (StreamReader sr = new StreamReader(path)) + { + fileData = sr.ReadToEnd().Replace("\r", string.Empty); + } + + Dictionary properties = new Dictionary(); + string[] kvp; + string[] records = fileData.Split("\n".ToCharArray()); + foreach (string record in records) + { + if (record[0] == '/' || record[0] == '*') + { + continue; + } + + kvp = record.Split("=".ToCharArray()); + properties.Add(kvp[0], kvp[1]); + } + + return properties; + } + + public static Binding GetBinding() + { + return new AmqpBinding(); + } + + public static Binding GetCustomBinding() + { + AmqpTransportBindingElement transportElement = new AmqpTransportBindingElement(); + RawMessageEncodingBindingElement encodingElement = new RawMessageEncodingBindingElement(); + transportElement.BrokerHost = "127.0.0.1"; + transportElement.TransferMode = TransferMode.Streamed; + + CustomBinding brokerBinding = new CustomBinding(); + brokerBinding.Elements.Add(encodingElement); + brokerBinding.Elements.Add(transportElement); + + return brokerBinding; + } + } +} diff --git a/qpid/wcf/tools/QCreate/QCreate.cpp b/qpid/wcf/tools/QCreate/QCreate.cpp new file mode 100644 index 0000000000..7b0231f339 --- /dev/null +++ b/qpid/wcf/tools/QCreate/QCreate.cpp @@ -0,0 +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. +*/ + +#include "stdafx.h" + +#include +#include + +#include +#include + +using namespace qpid::client; +using namespace qpid::framing; + + +int main(int argc, char** argv) { + + std::string exchange = argc>1 ? argv[1] : "amq.direct"; + std::string bindingKey = argc>2 ? argv[2] : "routing_key"; + std::string queue = argc>3 ? argv[3] : "message_queue"; + + const char* host = "127.0.0.1"; + int port = 5672; + Connection connection; + + try { + connection.open(host, port); + Session session = connection.newSession(); + + + //--------- Main body of program -------------------------------------------- + + // Create a queue and route all messages whose + // routing key is "routing_key" to this newly created queue. + + session.queueDeclare(arg::queue=queue); + session.exchangeBind(arg::exchange=exchange, arg::queue=queue, arg::bindingKey=bindingKey); + + //----------------------------------------------------------------------------- + + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; + +} + diff --git a/qpid/wcf/tools/QCreate/QCreate.sln b/qpid/wcf/tools/QCreate/QCreate.sln new file mode 100644 index 0000000000..c01675d53a --- /dev/null +++ b/qpid/wcf/tools/QCreate/QCreate.sln @@ -0,0 +1,40 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License +# + +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QCreate", "QCreate.vcproj", "{7CA88318-485A-4D95-A321-43DFBB6EA28B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7CA88318-485A-4D95-A321-43DFBB6EA28B}.Debug|Win32.ActiveCfg = Debug|Win32 + {7CA88318-485A-4D95-A321-43DFBB6EA28B}.Debug|Win32.Build.0 = Debug|Win32 + {7CA88318-485A-4D95-A321-43DFBB6EA28B}.Release|Win32.ActiveCfg = Release|Win32 + {7CA88318-485A-4D95-A321-43DFBB6EA28B}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/qpid/wcf/tools/QCreate/QCreate.vcproj b/qpid/wcf/tools/QCreate/QCreate.vcproj new file mode 100644 index 0000000000..e58077d78c --- /dev/null +++ b/qpid/wcf/tools/QCreate/QCreate.vcproj @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qpid/wcf/tools/QCreate/ReadMe.txt b/qpid/wcf/tools/QCreate/ReadMe.txt new file mode 100644 index 0000000000..b3efb84503 --- /dev/null +++ b/qpid/wcf/tools/QCreate/ReadMe.txt @@ -0,0 +1,52 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +======================================================================== + CONSOLE APPLICATION : QCreate Project Overview +======================================================================== + +AppWizard has created this QCreate application for you. + +This file contains a summary of what you will find in each of the files that +make up your QCreate application. + + +QCreate.vcproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +QCreate.cpp + This is the main application source file. + +///////////////////////////////////////////////////////////////////////////// +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named QCreate.pch and a precompiled types file named StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/qpid/wcf/tools/QCreate/stdafx.cpp b/qpid/wcf/tools/QCreate/stdafx.cpp new file mode 100644 index 0000000000..568cd3b7d6 --- /dev/null +++ b/qpid/wcf/tools/QCreate/stdafx.cpp @@ -0,0 +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. +*/ + +// stdafx.cpp : source file that includes just the standard includes +// QCreate.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/qpid/wcf/tools/QCreate/stdafx.h b/qpid/wcf/tools/QCreate/stdafx.h new file mode 100644 index 0000000000..a516e19a10 --- /dev/null +++ b/qpid/wcf/tools/QCreate/stdafx.h @@ -0,0 +1,34 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#include +#include + + + +// TODO: reference additional headers your program requires here diff --git a/qpid/wcf/tools/QCreate/targetver.h b/qpid/wcf/tools/QCreate/targetver.h new file mode 100644 index 0000000000..9cfb78641b --- /dev/null +++ b/qpid/wcf/tools/QCreate/targetver.h @@ -0,0 +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. +*/ + +#pragma once + +// The following macros define the minimum required platform. The minimum required platform +// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run +// your application. The macros work by enabling all features available on platform versions up to and +// including the version specified. + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. +#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. +#endif + -- cgit v1.2.1 From 9c4ecc45da929750ff7f0e0a5d7ada4e674b9105 Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Wed, 16 Sep 2009 10:06:55 +0000 Subject: QPID-2105: Make NetworkDriver.open use a SSLContextFactory, not an SSLEngine. Allow an existing SocketConnector to be passed into a MINANetworkDriver, for use with the ExistingSocket bit of TransportConnection. Move the ExistingSocket stuff to one place, use MINANetworkDriver in TransportConnection and make AMQProtocolHandler implement ProtocolEngine. Remove MINA specific gubbins from AMQProtocolHandler and AMQProtocolSession. Move fireAsynchEvent to Job Add getLocalAddress to AMQProtocolEngine Move TestNetworkDriver to common Use correct class for logger in AMQProtocolEngine Check the exception is thrown properly in SimpleACLTest, make it a little less prone to obscure race conditions. git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-network-refactor@815704 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/broker/build.xml | 1 + .../qpid/server/protocol/AMQProtocolEngine.java | 55 +--- .../configuration/ServerConfigurationTest.java | 10 +- .../protocol/InternalTestProtocolSession.java | 1 + .../qpid/server/protocol/TestNetworkDriver.java | 126 ------- .../access/plugins/network/FirewallPluginTest.java | 24 +- qpid/java/client/build.xml | 1 + .../qpid/client/AMQConnectionDelegate_8_0.java | 2 +- .../qpid/client/failover/FailoverHandler.java | 10 +- .../qpid/client/protocol/AMQProtocolHandler.java | 363 +++++++++++---------- .../qpid/client/protocol/AMQProtocolSession.java | 110 +------ .../transport/SocketTransportConnection.java | 62 ++-- .../qpid/client/transport/TransportConnection.java | 19 +- .../transport/VmPipeTransportConnection.java | 11 +- .../client/protocol/AMQProtocolHandlerTest.java | 5 +- .../src/main/java/org/apache/qpid/pool/Job.java | 40 +++ .../org/apache/qpid/protocol/ProtocolEngine.java | 3 + .../org/apache/qpid/transport/NetworkDriver.java | 9 +- .../org/apache/qpid/transport/OpenException.java | 4 +- .../transport/network/mina/MINANetworkDriver.java | 108 +++--- .../apache/qpid/transport/TestNetworkDriver.java | 122 +++++++ .../network/mina/MINANetworkDriverTest.java | 13 + qpid/java/systests/build.xml | 2 +- .../qpid/server/security/acl/SimpleACLTest.java | 54 +-- .../client/protocol/AMQProtocolSessionTest.java | 29 +- 25 files changed, 588 insertions(+), 596 deletions(-) delete mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/TestNetworkDriver.java create mode 100644 qpid/java/common/src/test/java/org/apache/qpid/transport/TestNetworkDriver.java (limited to 'qpid') diff --git a/qpid/java/broker/build.xml b/qpid/java/broker/build.xml index 3c63c459be..ae133d1a96 100644 --- a/qpid/java/broker/build.xml +++ b/qpid/java/broker/build.xml @@ -21,6 +21,7 @@ + diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java index 19d98161c6..16ebc76185 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java @@ -31,8 +31,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicLong; import javax.management.JMException; @@ -51,7 +49,6 @@ import org.apache.qpid.framing.AMQDataBlock; import org.apache.qpid.framing.AMQFrame; import org.apache.qpid.framing.AMQMethodBody; import org.apache.qpid.framing.AMQProtocolHeaderException; -import org.apache.qpid.framing.AMQProtocolVersionException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.ChannelCloseOkBody; import org.apache.qpid.framing.ConnectionCloseBody; @@ -94,7 +91,7 @@ import org.apache.qpid.transport.Sender; public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocolSession { - private static final Logger _logger = Logger.getLogger(AMQProtocolSession.class); + private static final Logger _logger = Logger.getLogger(AMQProtocolEngine.class); private static final String CLIENT_PROPERTIES_INSTANCE = ClientProperties.instance.toString(); @@ -180,6 +177,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol _actor = new AMQPConnectionActor(this, virtualHostRegistry.getApplicationRegistry().getRootMessageLogger()); _actor.message(ConnectionMessages.CON_1001(null, null, false, false)); + _poolReference.acquireExecutorService(); } private AMQProtocolSessionMBean createMBean() throws AMQException @@ -212,7 +210,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol try { final ArrayList dataBlocks = _codecFactory.getDecoder().decodeBuffer(msg); - fireAsynchEvent(_readJob, new Event(new Runnable() + Job.fireAsynchEvent(_poolReference.getPool(), _readJob, new Event(new Runnable() { @Override public void run() @@ -463,7 +461,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol final ByteBuffer buf = frame.toNioByteBuffer(); _lastIoTime = System.currentTimeMillis(); _writtenBytes += buf.remaining(); - fireAsynchEvent(_writeJob, new Event(new Runnable() + Job.fireAsynchEvent(_poolReference.getPool(), _writeJob, new Event(new Runnable() { @Override public void run() @@ -687,6 +685,10 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol /** This must be called when the session is _closed in order to free up any resources managed by the session. */ public void closeSession() throws AMQException { + if (CurrentActor.get() == null) + { + CurrentActor.set(_actor); + } if (!_closed) { if (_virtualHost != null) @@ -907,6 +909,11 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol public SocketAddress getRemoteAddress() { return _networkDriver.getRemoteAddress(); + } + + public SocketAddress getLocalAddress() + { + return _networkDriver.getLocalAddress(); } public MethodRegistry getMethodRegistry() @@ -990,7 +997,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol { // Do nothing } - + @Override public long getReadBytes() { @@ -1017,38 +1024,6 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol return (_clientVersion == null) ? null : _clientVersion.toString(); } - /** - * Adds an {@link Event} to a {@link Job}, triggering the execution of the job if it is not already running. - * - * @param job The job. - * @param event The event to hand off asynchronously. - */ - void fireAsynchEvent(Job job, Event event) - { - - job.add(event); - - final ExecutorService pool = _poolReference .getPool(); - - if(pool == null) - { - return; - } - - // rather than perform additional checks on pool to check that it hasn't shutdown. - // catch the RejectedExecutionException that will result from executing on a shutdown pool - if (job.activate()) - { - try - { - pool.execute(job); - } - catch(RejectedExecutionException e) - { - _logger.warn("Thread pool shutdown while tasks still outstanding"); - } - } - - } + } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java index 1162687741..2285f5256e 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java @@ -35,11 +35,11 @@ import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.XMLConfiguration; import org.apache.qpid.server.protocol.AMQProtocolEngine; import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.protocol.TestNetworkDriver; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; +import org.apache.qpid.transport.TestNetworkDriver; public class ServerConfigurationTest extends TestCase { @@ -793,12 +793,12 @@ public class ServerConfigurationTest extends TestCase VirtualHost virtualHost = virtualHostRegistry.getVirtualHost("test"); TestNetworkDriver testDriver = new TestNetworkDriver(); - testDriver.setAddress("127.0.0.1"); + testDriver.setRemoteAddress("127.0.0.1"); AMQProtocolEngine session = new AMQProtocolEngine(virtualHostRegistry, testDriver); assertFalse(reg.getAccessManager().authoriseConnect(session, virtualHost)); - testDriver.setAddress("127.1.2.3"); + testDriver.setRemoteAddress("127.1.2.3"); session = new AMQProtocolEngine(virtualHostRegistry, testDriver); assertTrue(reg.getAccessManager().authoriseConnect(session, virtualHost)); } @@ -867,7 +867,7 @@ public class ServerConfigurationTest extends TestCase VirtualHost virtualHost = virtualHostRegistry.getVirtualHost("test"); TestNetworkDriver testDriver = new TestNetworkDriver(); - testDriver.setAddress("127.0.0.1"); + testDriver.setRemoteAddress("127.0.0.1"); AMQProtocolEngine session = new AMQProtocolEngine(virtualHostRegistry, testDriver); session.setNetworkDriver(testDriver); @@ -935,7 +935,7 @@ public class ServerConfigurationTest extends TestCase // Test config TestNetworkDriver testDriver = new TestNetworkDriver(); - testDriver.setAddress("127.0.0.1"); + testDriver.setRemoteAddress("127.0.0.1"); VirtualHostRegistry virtualHostRegistry = reg.getVirtualHostRegistry(); VirtualHost virtualHost = virtualHostRegistry.getVirtualHost("test"); AMQProtocolSession session = new AMQProtocolEngine(virtualHostRegistry, testDriver); diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java index c4362f2c60..ec7bf1cb72 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java @@ -34,6 +34,7 @@ import org.apache.qpid.server.output.ProtocolOutputConverter; import org.apache.qpid.server.queue.AMQMessage; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.transport.TestNetworkDriver; public class InternalTestProtocolSession extends AMQProtocolEngine implements ProtocolOutputConverter { diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/TestNetworkDriver.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/TestNetworkDriver.java deleted file mode 100644 index 098843d315..0000000000 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/TestNetworkDriver.java +++ /dev/null @@ -1,126 +0,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.server.protocol; - -import java.net.BindException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.ByteBuffer; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import javax.net.ssl.SSLEngine; - -import org.apache.qpid.protocol.ProtocolEngine; -import org.apache.qpid.protocol.ProtocolEngineFactory; -import org.apache.qpid.ssl.SSLContextFactory; -import org.apache.qpid.transport.NetworkDriver; -import org.apache.qpid.transport.NetworkDriverConfiguration; -import org.apache.qpid.transport.OpenException; - -/** - * Test implementation of IoSession, which is required for some tests. Methods not being used are not implemented, - * so if this class is being used and some methods are to be used, then please update those. - */ -public class TestNetworkDriver implements NetworkDriver -{ - private final ConcurrentMap attributes = new ConcurrentHashMap(); - private String _address = "127.0.0.1"; - private int _port = 1; - - public TestNetworkDriver() - { - } - - public void setAddress(String string) - { - this._address = string; - } - - public String getAddress() - { - return _address; - } - - public void setPort(int _port) - { - this._port = _port; - } - - public int getPort() - { - return _port; - } - - public void bind(int port, InetAddress[] addresses, ProtocolEngineFactory protocolFactory, - NetworkDriverConfiguration config, SSLContextFactory sslFactory) throws BindException - { - - } - - public SocketAddress getLocalAddress() - { - return new InetSocketAddress(_address, _port); - } - - public SocketAddress getRemoteAddress() - { - return new InetSocketAddress(_address, _port); - } - - public void open(int port, InetAddress destination, ProtocolEngine engine, NetworkDriverConfiguration config, - SSLEngine sslEngine) throws OpenException - { - - } - - public void setMaxReadIdle(int idleTime) - { - - } - - public void setMaxWriteIdle(int idleTime) - { - - } - - public void close() - { - - } - - public void flush() - { - - } - - public void send(ByteBuffer msg) - { - - } - - public void setIdleTimeout(long l) - { - - } - -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/plugins/network/FirewallPluginTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/plugins/network/FirewallPluginTest.java index bda816f5ab..5d3335c001 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/plugins/network/FirewallPluginTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/plugins/network/FirewallPluginTest.java @@ -32,12 +32,12 @@ import junit.framework.TestCase; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.XMLConfiguration; import org.apache.qpid.server.protocol.AMQProtocolEngine; -import org.apache.qpid.server.protocol.TestNetworkDriver; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.security.access.ACLPlugin.AuthzResult; import org.apache.qpid.server.store.TestableMemoryMessageStore; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; +import org.apache.qpid.transport.TestNetworkDriver; public class FirewallPluginTest extends TestCase { @@ -90,7 +90,7 @@ public class FirewallPluginTest extends TestCase super.setUp(); _store = new TestableMemoryMessageStore(); _testDriver = new TestNetworkDriver(); - _testDriver.setAddress("127.0.0.1"); + _testDriver.setRemoteAddress("127.0.0.1"); // Retreive VirtualHost from the Registry VirtualHostRegistry virtualHostRegistry = ApplicationRegistry.getInstance().getVirtualHostRegistry(); @@ -167,7 +167,7 @@ public class FirewallPluginTest extends TestCase assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); // Set session IP so that we're connected from the right address - _testDriver.setAddress("192.168.23.23"); + _testDriver.setRemoteAddress("192.168.23.23"); assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); } @@ -182,7 +182,7 @@ public class FirewallPluginTest extends TestCase assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); // Set session IP so that we're connected from the right address - _testDriver.setAddress("192.168.23.23"); + _testDriver.setRemoteAddress("192.168.23.23"); assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); } @@ -195,7 +195,7 @@ public class FirewallPluginTest extends TestCase FirewallPlugin plugin = initialisePlugin("deny", new RuleInfo[]{rule}); // Set session IP so that we're connected from the right address - _testDriver.setAddress("127.0.0.1"); + _testDriver.setRemoteAddress("127.0.0.1"); assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); } @@ -208,7 +208,7 @@ public class FirewallPluginTest extends TestCase FirewallPlugin plugin = initialisePlugin("deny", new RuleInfo[]{rule}); // Set session IP so that we're connected from the right address - _testDriver.setAddress("127.0.0.1"); + _testDriver.setRemoteAddress("127.0.0.1"); assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); } @@ -231,7 +231,7 @@ public class FirewallPluginTest extends TestCase assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); // Set session IP so that we're connected from the right address - _testDriver.setAddress("192.168.23.23"); + _testDriver.setRemoteAddress("192.168.23.23"); assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); } @@ -254,7 +254,7 @@ public class FirewallPluginTest extends TestCase assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); // Set session IP so that we're connected from the right address - _testDriver.setAddress("192.168.23.23"); + _testDriver.setRemoteAddress("192.168.23.23"); assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); } @@ -268,7 +268,7 @@ public class FirewallPluginTest extends TestCase assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); // Set session IP so that we're connected from the right address - _testDriver.setAddress("192.168.23.23"); + _testDriver.setRemoteAddress("192.168.23.23"); assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); } @@ -282,7 +282,7 @@ public class FirewallPluginTest extends TestCase assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); // Set session IP so that we're connected from the right address - _testDriver.setAddress("192.168.23.23"); + _testDriver.setRemoteAddress("192.168.23.23"); assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); } @@ -292,11 +292,11 @@ public class FirewallPluginTest extends TestCase firstRule.setAccess("allow"); firstRule.setHostname("foo, bar, "+new InetSocketAddress("127.0.0.1", 5672).getHostName()); FirewallPlugin plugin = initialisePlugin("deny", new RuleInfo[]{firstRule}); - _testDriver.setAddress("10.0.0.1"); + _testDriver.setRemoteAddress("10.0.0.1"); assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); // Set session IP so that we're connected from the right address - _testDriver.setAddress("127.0.0.1"); + _testDriver.setRemoteAddress("127.0.0.1"); assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); } diff --git a/qpid/java/client/build.xml b/qpid/java/client/build.xml index 321e613d94..3c6132dc5b 100644 --- a/qpid/java/client/build.xml +++ b/qpid/java/client/build.xml @@ -21,6 +21,7 @@ + diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java index a0b69b5493..9876393d4c 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java @@ -97,7 +97,7 @@ public class AMQConnectionDelegate_8_0 implements AMQConnectionDelegate { _conn.getProtocolHandler().createIoTransportSession(brokerDetail); } - + _conn._protocolHandler.getProtocolSession().init(); // this blocks until the connection has been set up or when an error // has prevented the connection being set up diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java index 927f660932..8223cd5394 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java @@ -20,8 +20,6 @@ */ package org.apache.qpid.client.failover; -import org.apache.mina.common.IoSession; - import org.apache.qpid.AMQDisconnectedException; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.client.state.AMQStateManager; @@ -81,9 +79,6 @@ public class FailoverHandler implements Runnable /** Used for debugging. */ private static final Logger _logger = LoggerFactory.getLogger(FailoverHandler.class); - /** Holds the MINA session for the connection that has failed, not the connection that is being failed onto. */ - private final IoSession _session; - /** Holds the protocol handler for the failed connection, upon which the new connection is to be set up. */ private AMQProtocolHandler _amqProtocolHandler; @@ -99,10 +94,9 @@ public class FailoverHandler implements Runnable * @param amqProtocolHandler The protocol handler that spans the failover. * @param session The MINA session, for the failing connection. */ - public FailoverHandler(AMQProtocolHandler amqProtocolHandler, IoSession session) + public FailoverHandler(AMQProtocolHandler amqProtocolHandler) { _amqProtocolHandler = amqProtocolHandler; - _session = session; } /** @@ -221,7 +215,7 @@ public class FailoverHandler implements Runnable _amqProtocolHandler.setFailoverState(FailoverState.FAILED); /*try {*/ - _amqProtocolHandler.exceptionCaught(_session, e); + _amqProtocolHandler.exception(e); /*} catch (Exception ex) { diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java index ab3ff8ecb0..c7e2493025 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java @@ -20,10 +20,6 @@ */ package org.apache.qpid.client.protocol; -import org.apache.mina.common.IdleStatus; -import org.apache.mina.common.IoFilterChain; -import org.apache.mina.common.IoHandlerAdapter; -import org.apache.mina.common.IoSession; import org.apache.mina.filter.ReadThrottleFilterBuilder; import org.apache.mina.filter.SSLFilter; import org.apache.mina.filter.WriteBufferLimitFilterBuilder; @@ -48,16 +44,25 @@ import org.apache.qpid.client.state.listener.SpecificMethodFrameListener; import org.apache.qpid.codec.AMQCodecFactory; import org.apache.qpid.framing.*; import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.pool.Event; +import org.apache.qpid.pool.Job; +import org.apache.qpid.pool.PoolingFilter; import org.apache.qpid.pool.ReadWriteThreadModel; +import org.apache.qpid.pool.ReferenceCountingExecutorService; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.AMQMethodEvent; import org.apache.qpid.protocol.AMQMethodListener; +import org.apache.qpid.protocol.ProtocolEngine; import org.apache.qpid.ssl.SSLContextFactory; +import org.apache.qpid.transport.NetworkDriver; import org.apache.qpid.transport.network.io.IoTransport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.Iterator; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; @@ -120,7 +125,7 @@ import java.util.concurrent.CountDownLatch; * held per protocol handler, per protocol session, per network connection, per channel, in seperate classes, so * that lifecycles of the fields match lifecycles of their containing objects. */ -public class AMQProtocolHandler extends IoHandlerAdapter +public class AMQProtocolHandler implements ProtocolEngine { /** Used for debugging. */ private static final Logger _logger = LoggerFactory.getLogger(AMQProtocolHandler.class); @@ -137,7 +142,7 @@ public class AMQProtocolHandler extends IoHandlerAdapter private volatile AMQProtocolSession _protocolSession; /** Holds the state of the protocol session. */ - private AMQStateManager _stateManager = new AMQStateManager(); + private AMQStateManager _stateManager; /** Holds the method listeners, */ private final CopyOnWriteArraySet _frameListeners = new CopyOnWriteArraySet(); @@ -166,7 +171,15 @@ public class AMQProtocolHandler extends IoHandlerAdapter /** Object to lock on when changing the latch */ private Object _failoverLatchChange = new Object(); - + private AMQCodecFactory _codecFactory; + private Job _readJob; + private Job _writeJob; + private ReferenceCountingExecutorService _poolReference = ReferenceCountingExecutorService.getInstance(); + private NetworkDriver _networkDriver; + + private long _writtenBytes; + private long _readBytes; + /** * Creates a new protocol handler, associated with the specified client connection instance. * @@ -175,86 +188,14 @@ public class AMQProtocolHandler extends IoHandlerAdapter public AMQProtocolHandler(AMQConnection con) { _connection = con; - } - - /** - * Invoked by MINA when a MINA session for a new connection is created. This method sets up the filter chain on the - * session, which filters the events handled by this handler. The filter chain consists of, handing off events - * to an asynchronous thread pool, optionally encoding/decoding ssl, encoding/decoding AMQP. - * - * @param session The MINA session. - * - * @throws Exception Any underlying exceptions are allowed to fall through to MINA. - */ - public void sessionCreated(IoSession session) throws Exception - { - _logger.debug("Protocol session created for session " + System.identityHashCode(session)); - _failoverHandler = new FailoverHandler(this, session); - - final ProtocolCodecFilter pcf = new ProtocolCodecFilter(new AMQCodecFactory(false, _protocolSession)); - - if (Boolean.getBoolean("amqj.shared_read_write_pool")) - { - session.getFilterChain().addBefore("AsynchronousWriteFilter", "protocolFilter", pcf); - } - else - { - session.getFilterChain().addLast("protocolFilter", pcf); - } - // we only add the SSL filter where we have an SSL connection - if (_connection.getSSLConfiguration() != null) - { - SSLConfiguration sslConfig = _connection.getSSLConfiguration(); - SSLContextFactory sslFactory = - new SSLContextFactory(sslConfig.getKeystorePath(), sslConfig.getKeystorePassword(), sslConfig.getCertType()); - SSLFilter sslFilter = new SSLFilter(sslFactory.buildClientContext()); - sslFilter.setUseClientMode(true); - session.getFilterChain().addBefore("protocolFilter", "ssl", sslFilter); - } - - try - { - ReadWriteThreadModel threadModel = ReadWriteThreadModel.getInstance(); - threadModel.getAsynchronousReadFilter().createNewJobForSession(session); - threadModel.getAsynchronousWriteFilter().createNewJobForSession(session); - } - catch (RuntimeException e) - { - _logger.error(e.getMessage(), e); - } - - if (Boolean.getBoolean(ClientProperties.PROTECTIO_PROP_NAME)) - { - try - { - //Add IO Protection Filters - IoFilterChain chain = session.getFilterChain(); - - session.getFilterChain().addLast("tempExecutorFilterForFilterBuilder", new ExecutorFilter()); - - ReadThrottleFilterBuilder readfilter = new ReadThrottleFilterBuilder(); - readfilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty( - ClientProperties.READ_BUFFER_LIMIT_PROP_NAME, ClientProperties.READ_BUFFER_LIMIT_DEFAULT))); - readfilter.attach(chain); - - WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder(); - writefilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty( - ClientProperties.WRITE_BUFFER_LIMIT_PROP_NAME, ClientProperties.WRITE_BUFFER_LIMIT_DEFAULT))); - writefilter.attach(chain); - session.getFilterChain().remove("tempExecutorFilterForFilterBuilder"); - - _logger.info("Using IO Read/Write Filter Protection"); - } - catch (Exception e) - { - _logger.error("Unable to attach IO Read/Write Filter Protection :" + e.getMessage()); - } - } - _protocolSession = new AMQProtocolSession(this, session, _connection); - - _stateManager.setProtocolSession(_protocolSession); - - _protocolSession.init(); + _protocolSession = new AMQProtocolSession(this, _connection); + _stateManager = new AMQStateManager(_protocolSession); + _codecFactory = new AMQCodecFactory(false, _protocolSession); + ReadWriteThreadModel threadModel = ReadWriteThreadModel.getInstance(); + _readJob = new Job(threadModel.getAsynchronousReadFilter(), PoolingFilter.MAX_JOB_EVENTS, true); + _writeJob = new Job(threadModel.getAsynchronousWriteFilter(), PoolingFilter.MAX_JOB_EVENTS, false); + _poolReference.acquireExecutorService(); + _failoverHandler = new FailoverHandler(this); } /** @@ -283,12 +224,10 @@ public class AMQProtocolHandler extends IoHandlerAdapter * may be called first followed by this method. This depends on whether the client was trying to send data at the * time of the failure. * - * @param session The MINA session. - * * @todo Clarify: presumably exceptionCaught is called when the client is sending during a connection failure and * not otherwise? The above comment doesn't make that clear. */ - public void sessionClosed(IoSession session) + public void closed() { if (_connection.isClosed()) { @@ -327,7 +266,8 @@ public class AMQProtocolHandler extends IoHandlerAdapter { _logger.debug("sessionClose() not allowed to failover"); _connection.exceptionReceived(new AMQDisconnectedException( - "Server closed connection and reconnection " + "not permitted.", null)); + "Server closed connection and reconnection " + "not permitted.", + _stateManager.getLastException())); } else { @@ -350,43 +290,39 @@ public class AMQProtocolHandler extends IoHandlerAdapter failoverThread.start(); } - public void sessionIdle(IoSession session, IdleStatus status) throws Exception + @Override + public void readerIdle() { - _logger.debug("Protocol Session [" + this + ":" + session + "] idle: " + status); - if (IdleStatus.WRITER_IDLE.equals(status)) - { - // write heartbeat frame: - _logger.debug("Sent heartbeat"); - session.write(HeartbeatBody.FRAME); - HeartbeatDiagnostics.sent(); - } - else if (IdleStatus.READER_IDLE.equals(status)) - { - // failover: - HeartbeatDiagnostics.timeout(); - _logger.warn("Timed out while waiting for heartbeat from peer."); - session.close(); - } + _logger.debug("Protocol Session [" + this + "] idle: reader"); + // failover: + HeartbeatDiagnostics.timeout(); + _logger.warn("Timed out while waiting for heartbeat from peer."); + _networkDriver.close(); + } + + @Override + public void writerIdle() + { + _logger.debug("Protocol Session [" + this + "] idle: reader"); + writeFrame(HeartbeatBody.FRAME); + HeartbeatDiagnostics.sent(); } /** - * Invoked when any exception is thrown by a user IoHandler implementation or by MINA. If the cause is an - * IOException, MINA will close the connection automatically. - * - * @param session The MINA session. - * @param cause The exception that triggered this event. + * Invoked when any exception is thrown by the NetworkDriver */ - public void exceptionCaught(IoSession session, Throwable cause) + public void exception(Throwable cause) { + _logger.info("AS: HELLO"); if (_failoverState == FailoverState.NOT_STARTED) { // if (!(cause instanceof AMQUndeliveredException) && (!(cause instanceof AMQAuthenticationException))) if ((cause instanceof AMQConnectionClosedException) || cause instanceof IOException) { _logger.info("Exception caught therefore going to attempt failover: " + cause, cause); - // this will attemp failover - - sessionClosed(session); + // this will attempt failover + _networkDriver.close(); + closed(); } else { @@ -437,6 +373,7 @@ public class AMQProtocolHandler extends IoHandlerAdapter public void propagateExceptionToAllWaiters(Exception e) { getStateManager().error(e); + propagateExceptionToFrameListeners(e); } @@ -490,48 +427,84 @@ public class AMQProtocolHandler extends IoHandlerAdapter private static int _messageReceivedCount; - public void messageReceived(IoSession session, Object message) throws Exception - { - if (PROTOCOL_DEBUG) - { - _protocolLogger.info(String.format("RECV: [%s] %s", this, message)); - } - if(message instanceof AMQFrame) + @Override + public void received(ByteBuffer msg) + { + try { - final boolean debug = _logger.isDebugEnabled(); - final long msgNumber = ++_messageReceivedCount; + _readBytes += msg.remaining(); + final ArrayList dataBlocks = _codecFactory.getDecoder().decodeBuffer(msg); - if (debug && ((msgNumber % 1000) == 0)) + Job.fireAsynchEvent(_poolReference.getPool(), _readJob, new Event(new Runnable() { - _logger.debug("Received " + _messageReceivedCount + " protocol messages"); - } - - AMQFrame frame = (AMQFrame) message; - - final AMQBody bodyFrame = frame.getBodyFrame(); - - HeartbeatDiagnostics.received(bodyFrame instanceof HeartbeatBody); + @Override + public void run() + { + // Decode buffer - bodyFrame.handle(frame.getChannel(), _protocolSession); + for (AMQDataBlock message : dataBlocks) + { - _connection.bytesReceived(_protocolSession.getIoSession().getReadBytes()); + try + { + if (PROTOCOL_DEBUG) + { + _protocolLogger.info(String.format("RECV: [%s] %s", this, message)); + } + + if(message instanceof AMQFrame) + { + final boolean debug = _logger.isDebugEnabled(); + final long msgNumber = ++_messageReceivedCount; + + if (debug && ((msgNumber % 1000) == 0)) + { + _logger.debug("Received " + _messageReceivedCount + " protocol messages"); + } + + AMQFrame frame = (AMQFrame) message; + + final AMQBody bodyFrame = frame.getBodyFrame(); + + HeartbeatDiagnostics.received(bodyFrame instanceof HeartbeatBody); + + bodyFrame.handle(frame.getChannel(), _protocolSession); + + _connection.bytesReceived(_readBytes); + } + else if (message instanceof ProtocolInitiation) + { + // We get here if the server sends a response to our initial protocol header + // suggesting an alternate ProtocolVersion; the server will then close the + // connection. + ProtocolInitiation protocolInit = (ProtocolInitiation) message; + ProtocolVersion pv = protocolInit.checkVersion(); + getConnection().setProtocolVersion(pv); + + // get round a bug in old versions of qpid whereby the connection is not closed + _stateManager.changeState(AMQState.CONNECTION_CLOSED); + } + } + catch (Exception e) + { + e.printStackTrace(); + _logger.error("Exception processing frame", e); + propagateExceptionToFrameListeners(e); + exception(e); + } + } + } + })); } - else if (message instanceof ProtocolInitiation) + catch (Exception e) { - // We get here if the server sends a response to our initial protocol header - // suggesting an alternate ProtocolVersion; the server will then close the - // connection. - ProtocolInitiation protocolInit = (ProtocolInitiation) message; - ProtocolVersion pv = protocolInit.checkVersion(); - getConnection().setProtocolVersion(pv); - - // get round a bug in old versions of qpid whereby the connection is not closed - _stateManager.changeState(AMQState.CONNECTION_CLOSED); + propagateExceptionToFrameListeners(e); + exception(e); } } - public void methodBodyReceived(final int channelId, final AMQBody bodyFrame, IoSession session)//, final IoSession session) + public void methodBodyReceived(final int channelId, final AMQBody bodyFrame) throws AMQException { @@ -571,32 +544,13 @@ public class AMQProtocolHandler extends IoHandlerAdapter { propagateExceptionToFrameListeners(e); - exceptionCaught(session, e); + exception(e); } } private static int _messagesOut; - public void messageSent(IoSession session, Object message) throws Exception - { - if (PROTOCOL_DEBUG) - { - _protocolLogger.debug(String.format("SEND: [%s] %s", this, message)); - } - - final long sentMessages = _messagesOut++; - - final boolean debug = _logger.isDebugEnabled(); - - if (debug && ((sentMessages % 1000) == 0)) - { - _logger.debug("Sent " + _messagesOut + " protocol messages"); - } - - _connection.bytesSent(session.getWrittenBytes()); - } - public StateWaiter createWaiter(Set states) throws AMQException { return getStateManager().createWaiter(states); @@ -610,12 +564,34 @@ public class AMQProtocolHandler extends IoHandlerAdapter */ public void writeFrame(AMQDataBlock frame) { - _protocolSession.writeFrame(frame); + writeFrame(frame, false); } public void writeFrame(AMQDataBlock frame, boolean wait) { - _protocolSession.writeFrame(frame, wait); + ByteBuffer buf = frame.toNioByteBuffer(); + _writtenBytes += buf.remaining(); + _networkDriver.send(buf); + if (PROTOCOL_DEBUG) + { + _protocolLogger.debug(String.format("SEND: [%s] %s", this, frame)); + } + + final long sentMessages = _messagesOut++; + + final boolean debug = _logger.isDebugEnabled(); + + if (debug && ((sentMessages % 1000) == 0)) + { + _logger.debug("Sent " + _messagesOut + " protocol messages"); + } + + _connection.bytesSent(_writtenBytes); + + if (wait) + { + _networkDriver.flush(); + } } /** @@ -673,7 +649,7 @@ public class AMQProtocolHandler extends IoHandlerAdapter //FIXME: At this point here we should check or before add we should check _stateManager is in an open // state so as we don't check we are likely just to time out here as I believe is being seen in QPID-1255 } - _protocolSession.writeFrame(frame); + writeFrame(frame); return listener.blockForFrame(timeout); // When control resumes before this line, a reply will have been received @@ -723,20 +699,17 @@ public class AMQProtocolHandler extends IoHandlerAdapter final AMQFrame frame = body.generateFrame(0); //If the connection is already closed then don't do a syncWrite - if (getStateManager().getCurrentState().equals(AMQState.CONNECTION_CLOSED)) - { - _protocolSession.closeProtocolSession(false); - } - else + if (!getStateManager().getCurrentState().equals(AMQState.CONNECTION_CLOSED)) { try { syncWrite(frame, ConnectionCloseOkBody.class, timeout); - _protocolSession.closeProtocolSession(); + _networkDriver.close(); + closed(); } catch (AMQTimeoutException e) { - _protocolSession.closeProtocolSession(false); + closed(); } catch (FailoverException e) { @@ -748,13 +721,13 @@ public class AMQProtocolHandler extends IoHandlerAdapter /** @return the number of bytes read from this protocol session */ public long getReadBytes() { - return _protocolSession.getIoSession().getReadBytes(); + return _readBytes; } /** @return the number of bytes written to this protocol session */ public long getWrittenBytes() { - return _protocolSession.getIoSession().getWrittenBytes(); + return _writtenBytes; } public void failover(String host, int port) @@ -807,6 +780,7 @@ public class AMQProtocolHandler extends IoHandlerAdapter public void setStateManager(AMQStateManager stateManager) { _stateManager = stateManager; + _stateManager.setProtocolSession(_protocolSession); } public AMQProtocolSession getProtocolSession() @@ -843,4 +817,35 @@ public class AMQProtocolHandler extends IoHandlerAdapter { return _protocolSession.getProtocolVersion(); } + + public SocketAddress getRemoteAddress() + { + return _networkDriver.getRemoteAddress(); + } + + public SocketAddress getLocalAddress() + { + return _networkDriver.getLocalAddress(); + } + + public void setNetworkDriver(NetworkDriver driver) + { + _networkDriver = driver; + } + + /** @param delay delay in seconds (not ms) */ + void initHeartbeats(int delay) + { + if (delay > 0) + { + getNetworkDriver().setMaxWriteIdle(delay); + getNetworkDriver().setMaxReadIdle(HeartbeatConfig.CONFIG.getTimeout(delay)); + HeartbeatDiagnostics.init(delay, HeartbeatConfig.CONFIG.getTimeout(delay)); + } + } + + public NetworkDriver getNetworkDriver() + { + return _networkDriver; + } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java index 0e872170aa..cd049c24a1 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java @@ -20,11 +20,6 @@ */ package org.apache.qpid.client.protocol; -import org.apache.commons.lang.StringUtils; -import org.apache.mina.common.CloseFuture; -import org.apache.mina.common.IdleStatus; -import org.apache.mina.common.IoSession; -import org.apache.mina.common.WriteFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,6 +28,7 @@ import javax.security.sasl.SaslClient; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import org.apache.commons.lang.StringUtils; import org.apache.qpid.AMQException; import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQSession; @@ -65,10 +61,6 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession protected static final String SASL_CLIENT = "SASLClient"; - protected final IoSession _minaProtocolSession; - - protected WriteFuture _lastWriteFuture; - /** * The handler from which this session was created and which is used to handle protocol events. We send failover * events to the handler. @@ -102,28 +94,15 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession protected final AMQConnection _connection; - private static final int FAST_CHANNEL_ACCESS_MASK = 0xFFFFFFF0; + private ConnectionTuneParameters _connectionTuneParameters; - public AMQProtocolSession(AMQProtocolHandler protocolHandler, IoSession protocolSession, AMQConnection connection) - { - _protocolHandler = protocolHandler; - _minaProtocolSession = protocolSession; - _minaProtocolSession.setAttachment(this); - // properties of the connection are made available to the event handlers - _minaProtocolSession.setAttribute(AMQ_CONNECTION, connection); - // fixme - real value needed - _minaProtocolSession.setWriteTimeout(LAST_WRITE_FUTURE_JOIN_TIMEOUT); - _protocolVersion = connection.getProtocolVersion(); - _methodDispatcher = ClientMethodDispatcherImpl.newMethodDispatcher(ProtocolVersion.getLatestSupportedVersion(), - this); - _connection = connection; + private SaslClient _saslClient; - } + private static final int FAST_CHANNEL_ACCESS_MASK = 0xFFFFFFF0; public AMQProtocolSession(AMQProtocolHandler protocolHandler, AMQConnection connection) { - _protocolHandler = protocolHandler; - _minaProtocolSession = null; + _protocolHandler = protocolHandler; _protocolVersion = connection.getProtocolVersion(); _methodDispatcher = ClientMethodDispatcherImpl.newMethodDispatcher(ProtocolVersion.getLatestSupportedVersion(), this); @@ -134,7 +113,7 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession { // start the process of setting up the connection. This is the first place that // data is written to the server. - _minaProtocolSession.write(new ProtocolInitiation(_connection.getProtocolVersion())); + _protocolHandler.writeFrame(new ProtocolInitiation(_connection.getProtocolVersion())); } public String getClientID() @@ -175,14 +154,9 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession return getAMQConnection().getPassword(); } - public IoSession getIoSession() - { - return _minaProtocolSession; - } - public SaslClient getSaslClient() { - return (SaslClient) _minaProtocolSession.getAttribute(SASL_CLIENT); + return _saslClient; } /** @@ -192,28 +166,21 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession */ public void setSaslClient(SaslClient client) { - if (client == null) - { - _minaProtocolSession.removeAttribute(SASL_CLIENT); - } - else - { - _minaProtocolSession.setAttribute(SASL_CLIENT, client); - } + _saslClient = client; } public ConnectionTuneParameters getConnectionTuneParameters() { - return (ConnectionTuneParameters) _minaProtocolSession.getAttribute(CONNECTION_TUNE_PARAMETERS); + return _connectionTuneParameters; } public void setConnectionTuneParameters(ConnectionTuneParameters params) { - _minaProtocolSession.setAttribute(CONNECTION_TUNE_PARAMETERS, params); + _connectionTuneParameters = params; AMQConnection con = getAMQConnection(); con.setMaximumChannelCount(params.getChannelMax()); con.setMaximumFrameSize(params.getFrameMax()); - initHeartbeats((int) params.getHeartbeat()); + _protocolHandler.initHeartbeats((int) params.getHeartbeat()); } /** @@ -335,21 +302,12 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession */ public void writeFrame(AMQDataBlock frame) { - writeFrame(frame, false); + _protocolHandler.writeFrame(frame); } public void writeFrame(AMQDataBlock frame, boolean wait) { - WriteFuture f = _minaProtocolSession.write(frame); - if (wait) - { - // fixme -- time out? - f.join(); - } - else - { - _lastWriteFuture = f; - } + _protocolHandler.writeFrame(frame, wait); } /** @@ -407,33 +365,12 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession public AMQConnection getAMQConnection() { - return (AMQConnection) _minaProtocolSession.getAttribute(AMQ_CONNECTION); + return _connection; } - public void closeProtocolSession() + public void closeProtocolSession() throws AMQException { - closeProtocolSession(true); - } - - public void closeProtocolSession(boolean waitLast) - { - _logger.debug("Waiting for last write to join."); - if (waitLast && (_lastWriteFuture != null)) - { - _lastWriteFuture.join(LAST_WRITE_FUTURE_JOIN_TIMEOUT); - } - - _logger.debug("Closing protocol session"); - - final CloseFuture future = _minaProtocolSession.close(); - - // There is no recovery we can do if the join on the close failes so simply mark the connection CLOSED - // then wait for the connection to close. - // ritchiem: Could this release BlockingWaiters to early? The close has been done as much as possible so any - // error now shouldn't matter. - - _protocolHandler.getStateManager().changeState(AMQState.CONNECTION_CLOSED); - future.join(LAST_WRITE_FUTURE_JOIN_TIMEOUT); + _protocolHandler.closeConnection(0); } public void failover(String host, int port) @@ -449,22 +386,11 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession id = _queueId++; } // get rid of / and : and ; from address for spec conformance - String localAddress = StringUtils.replaceChars(_minaProtocolSession.getLocalAddress().toString(), "/;:", ""); + String localAddress = StringUtils.replaceChars(_protocolHandler.getLocalAddress().toString(), "/;:", ""); return new AMQShortString("tmp_" + localAddress + "_" + id); } - /** @param delay delay in seconds (not ms) */ - void initHeartbeats(int delay) - { - if (delay > 0) - { - _minaProtocolSession.setIdleTime(IdleStatus.WRITER_IDLE, delay); - _minaProtocolSession.setIdleTime(IdleStatus.READER_IDLE, HeartbeatConfig.CONFIG.getTimeout(delay)); - HeartbeatDiagnostics.init(delay, HeartbeatConfig.CONFIG.getTimeout(delay)); - } - } - public void confirmConsumerCancelled(int channelId, AMQShortString consumerTag) { final AMQSession session = getSession(channelId); @@ -530,7 +456,7 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession public void methodFrameReceived(final int channel, final AMQMethodBody amqMethodBody) throws AMQException { - _protocolHandler.methodBodyReceived(channel, amqMethodBody, _minaProtocolSession); + _protocolHandler.methodBodyReceived(channel, amqMethodBody); } public void notifyError(Exception error) diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java index b2f7ae8395..77c9c40e82 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java @@ -24,23 +24,33 @@ import org.apache.mina.common.ByteBuffer; import org.apache.mina.common.ConnectFuture; import org.apache.mina.common.IoConnector; import org.apache.mina.common.SimpleByteBufferAllocator; +import org.apache.mina.filter.SSLFilter; import org.apache.mina.transport.socket.nio.ExistingSocketConnector; import org.apache.mina.transport.socket.nio.SocketConnectorConfig; import org.apache.mina.transport.socket.nio.SocketSessionConfig; +import org.apache.qpid.client.SSLConfiguration; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.pool.ReadWriteThreadModel; +import org.apache.qpid.ssl.SSLContextFactory; +import org.apache.qpid.transport.network.mina.MINANetworkDriver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sun.net.InetAddressCachePolicy; + import java.io.IOException; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; +import java.security.GeneralSecurityException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import javax.net.ssl.SSLEngine; + public class SocketTransportConnection implements ITransportConnection { private static final Logger _logger = LoggerFactory.getLogger(SocketTransportConnection.class); @@ -71,61 +81,27 @@ public class SocketTransportConnection implements ITransportConnection } final IoConnector ioConnector = _socketConnectorFactory.newSocketConnector(); - SocketConnectorConfig cfg = (SocketConnectorConfig) ioConnector.getDefaultConfig(); - - // if we do not use our own thread model we get the MINA default which is to use - // its own leader-follower model - boolean readWriteThreading = Boolean.getBoolean("amqj.shared_read_write_pool"); - if (readWriteThreading) - { - cfg.setThreadModel(ReadWriteThreadModel.getInstance()); - } - - SocketSessionConfig scfg = (SocketSessionConfig) cfg.getSessionConfig(); - scfg.setTcpNoDelay("true".equalsIgnoreCase(System.getProperty("amqj.tcpNoDelay", "true"))); - scfg.setSendBufferSize(Integer.getInteger("amqj.sendBufferSize", DEFAULT_BUFFER_SIZE)); - _logger.info("send-buffer-size = " + scfg.getSendBufferSize()); - scfg.setReceiveBufferSize(Integer.getInteger("amqj.receiveBufferSize", DEFAULT_BUFFER_SIZE)); - _logger.info("recv-buffer-size = " + scfg.getReceiveBufferSize()); - final InetSocketAddress address; if (brokerDetail.getTransport().equals(BrokerDetails.SOCKET)) { address = null; - - Socket socket = TransportConnection.removeOpenSocket(brokerDetail.getHost()); - - if (socket != null) - { - _logger.info("Using existing Socket:" + socket); - - ((ExistingSocketConnector) ioConnector).setOpenSocket(socket); - } - else - { - throw new IllegalArgumentException("Active Socket must be provided for broker " + - "with 'socket://' transport:" + brokerDetail); - } } else { address = new InetSocketAddress(brokerDetail.getHost(), brokerDetail.getPort()); _logger.info("Attempting connection to " + address); } - - - ConnectFuture future = ioConnector.connect(address, protocolHandler); - - // wait for connection to complete - if (future.join(brokerDetail.getTimeout())) - { - // we call getSession which throws an IOException if there has been an error connecting - future.getSession(); - } - else + + SSLConfiguration sslConfig = protocolHandler.getConnection().getSSLConfiguration(); + SSLContextFactory sslFactory = null; + if (sslConfig != null) { - throw new IOException("Timeout waiting for connection."); + sslFactory = new SSLContextFactory(sslConfig.getKeystorePath(), sslConfig.getKeystorePassword(), sslConfig.getCertType()); } + + MINANetworkDriver driver = new MINANetworkDriver(ioConnector); + driver.open(brokerDetail.getPort(), address.getAddress(), protocolHandler, null, sslFactory); + protocolHandler.setNetworkDriver(driver); } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java index 4ff24c3607..45194750dc 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java @@ -79,7 +79,7 @@ public class TransportConnection return _openSocketRegister.remove(socketID); } - public static synchronized ITransportConnection getInstance(BrokerDetails details) throws AMQTransportConnectionException + public static synchronized ITransportConnection getInstance(final BrokerDetails details) throws AMQTransportConnectionException { int transport = getTransport(details.getTransport()); @@ -95,7 +95,22 @@ public class TransportConnection { public IoConnector newSocketConnector() { - return new ExistingSocketConnector(1,new QpidThreadExecutor()); + ExistingSocketConnector connector = new ExistingSocketConnector(1,new QpidThreadExecutor()); + + Socket socket = TransportConnection.removeOpenSocket(details.getHost()); + + if (socket != null) + { + _logger.info("Using existing Socket:" + socket); + + ((ExistingSocketConnector) connector).setOpenSocket(socket); + } + else + { + throw new IllegalArgumentException("Active Socket must be provided for broker " + + "with 'socket://' transport:" + details); + } + return connector; } }); case TCP: diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java index dca6efba67..3de6f9b9ea 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java @@ -28,6 +28,7 @@ import org.apache.mina.transport.vmpipe.VmPipeConnector; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.pool.ReadWriteThreadModel; +import org.apache.qpid.transport.network.mina.MINANetworkDriver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,6 +40,8 @@ public class VmPipeTransportConnection implements ITransportConnection private static int _port; + private MINANetworkDriver _networkDriver; + public VmPipeTransportConnection(int port) { _port = port; @@ -47,16 +50,16 @@ public class VmPipeTransportConnection implements ITransportConnection public void connect(AMQProtocolHandler protocolHandler, BrokerDetails brokerDetail) throws IOException { final VmPipeConnector ioConnector = new QpidVmPipeConnector(); - final IoServiceConfig cfg = ioConnector.getDefaultConfig(); - - cfg.setThreadModel(ReadWriteThreadModel.getInstance()); final VmPipeAddress address = new VmPipeAddress(_port); _logger.info("Attempting connection to " + address); - ConnectFuture future = ioConnector.connect(address, protocolHandler); + _networkDriver = new MINANetworkDriver(ioConnector, protocolHandler); + protocolHandler.setNetworkDriver(_networkDriver); + ConnectFuture future = ioConnector.connect(address, _networkDriver); // wait for connection to complete future.join(); // we call getSession which throws an IOException if there has been an error connecting future.getSession(); + _networkDriver.setProtocolEngine(protocolHandler); } } diff --git a/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/AMQProtocolHandlerTest.java b/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/AMQProtocolHandlerTest.java index fc7f8148f0..f520a21ba0 100644 --- a/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/AMQProtocolHandlerTest.java +++ b/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/AMQProtocolHandlerTest.java @@ -27,6 +27,7 @@ import org.apache.qpid.framing.AMQMethodBody; import org.apache.qpid.framing.amqp_8_0.BasicRecoverOkBodyImpl; import org.apache.qpid.AMQException; import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.transport.TestNetworkDriver; import org.apache.qpid.client.MockAMQConnection; import org.apache.qpid.client.AMQAuthenticationException; import org.apache.qpid.client.state.AMQState; @@ -72,9 +73,7 @@ public class AMQProtocolHandlerTest extends TestCase { //Create a new ProtocolHandler with a fake connection. _handler = new AMQProtocolHandler(new MockAMQConnection("amqp://guest:guest@client/test?brokerlist='vm://:1'")); - - _handler.sessionCreated(new MockIoSession()); - + _handler.setNetworkDriver(new TestNetworkDriver()); AMQBody body = BasicRecoverOkBodyImpl.getFactory().newInstance(null, 1); _blockFrame = new AMQFrame(0, body); diff --git a/qpid/java/common/src/main/java/org/apache/qpid/pool/Job.java b/qpid/java/common/src/main/java/org/apache/qpid/pool/Job.java index 4e4192dbe3..15d1c20ff1 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/pool/Job.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/pool/Job.java @@ -21,9 +21,13 @@ package org.apache.qpid.pool; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.mina.common.IoSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A Job is a continuation that batches together other continuations, specifically {@link Event}s, into one continuation. @@ -66,6 +70,8 @@ public class Job implements ReadWriteRunnable private final boolean _readJob; + private final static Logger _logger = LoggerFactory.getLogger(Job.class); + /** * Creates a new job that aggregates many continuations together. * @@ -181,4 +187,38 @@ public class Job implements ReadWriteRunnable public void notCompleted(final Job job); } + + /** + * Adds an {@link Event} to a {@link Job}, triggering the execution of the job if it is not already running. + * + * @param job The job. + * @param event The event to hand off asynchronously. + */ + public static void fireAsynchEvent(ExecutorService pool, Job job, Event event) + { + + job.add(event); + + + if(pool == null) + { + return; + } + + // rather than perform additional checks on pool to check that it hasn't shutdown. + // catch the RejectedExecutionException that will result from executing on a shutdown pool + if (job.activate()) + { + try + { + pool.execute(job); + } + catch(RejectedExecutionException e) + { + _logger.warn("Thread pool shutdown while tasks still outstanding"); + } + } + + } + } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngine.java b/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngine.java index 8ab845454a..5bfc189b02 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngine.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolEngine.java @@ -37,6 +37,9 @@ public interface ProtocolEngine extends Receiver // Returns the remote address of the NetworkDriver SocketAddress getRemoteAddress(); + + // Returns the local address of the NetworkDriver + SocketAddress getLocalAddress(); // Returns number of bytes written long getWrittenBytes(); diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriver.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriver.java index 34b0ef65be..86af97bf7e 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriver.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/NetworkDriver.java @@ -24,8 +24,6 @@ import java.net.BindException; import java.net.InetAddress; import java.net.SocketAddress; -import javax.net.ssl.SSLEngine; - import org.apache.qpid.protocol.ProtocolEngine; import org.apache.qpid.protocol.ProtocolEngineFactory; import org.apache.qpid.ssl.SSLContextFactory; @@ -33,13 +31,14 @@ import org.apache.qpid.ssl.SSLContextFactory; public interface NetworkDriver extends Sender { // Creates a NetworkDriver which attempts to connect to destination on port and attaches the ProtocolEngine to - // it using the SSLEngine if provided + // it using the SSLContextFactory if provided void open(int port, InetAddress destination, ProtocolEngine engine, - NetworkDriverConfiguration config, SSLEngine sslEngine) + NetworkDriverConfiguration config, SSLContextFactory sslFactory) throws OpenException; // listens for incoming connections on the specified ports and address and creates a new NetworkDriver which - // processes incoming connections with ProtocolEngines created from factory using the SSLEngine if provided + // processes incoming connections with ProtocolEngines and SSLEngines created from the factories + // (in the case of an SSLContextFactory, if provided) void bind (int port, InetAddress[] addresses, ProtocolEngineFactory protocolFactory, NetworkDriverConfiguration config, SSLContextFactory sslFactory) throws BindException; diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/OpenException.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/OpenException.java index 8628b8c7aa..68fbb5e8ec 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/OpenException.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/OpenException.java @@ -21,7 +21,9 @@ package org.apache.qpid.transport; -public class OpenException extends Exception +import java.io.IOException; + +public class OpenException extends IOException { public OpenException(String string, Throwable lastException) diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java index e34103a944..7cc5f8e442 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java @@ -33,6 +33,7 @@ import javax.net.ssl.SSLEngine; import org.apache.mina.common.ConnectFuture; import org.apache.mina.common.IdleStatus; import org.apache.mina.common.IoAcceptor; +import org.apache.mina.common.IoConnector; import org.apache.mina.common.IoFilterChain; import org.apache.mina.common.IoHandlerAdapter; import org.apache.mina.common.IoSession; @@ -71,7 +72,7 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver private int _processors = 4; private boolean _executorPool = false; private SSLContextFactory _sslFactory = null; - private SocketConnector _socketConnector; + private IoConnector _socketConnector; private IoAcceptor _acceptor; private IoSession _ioSession; private ProtocolEngineFactory _factory; @@ -101,6 +102,7 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver _protectIO = protectIO; _protocolEngine = protocolEngine; _ioSession = session; + _ioSession.setAttachment(_protocolEngine); } public MINANetworkDriver() @@ -108,6 +110,17 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver } + public MINANetworkDriver(IoConnector ioConnector) + { + _socketConnector = ioConnector; + } + + public MINANetworkDriver(IoConnector ioConnector, ProtocolEngine engine) + { + _socketConnector = ioConnector; + _protocolEngine = engine; + } + public void bind(int port, InetAddress[] addresses, ProtocolEngineFactory factory, NetworkDriverConfiguration config, SSLContextFactory sslFactory) throws BindException { @@ -188,8 +201,13 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver public void open(int port, InetAddress destination, ProtocolEngine engine, NetworkDriverConfiguration config, - SSLEngine sslEngine) throws OpenException + SSLContextFactory sslFactory) throws OpenException { + if (sslFactory != null) + { + _sslFactory = sslFactory; + } + if (_useNIO) { _socketConnector = new MultiThreadSocketConnector(1, new QpidThreadExecutor()); @@ -207,7 +225,6 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver { org.apache.mina.common.ByteBuffer.setAllocator(new SimpleByteBufferAllocator()); } - SocketConnectorConfig cfg = (SocketConnectorConfig) _socketConnector.getDefaultConfig(); @@ -229,7 +246,11 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver // one SocketConnector per connection at the moment anyway). This allows // short-running // clients (like unit tests) to complete quickly. - _socketConnector.setWorkerTimeout(0); + if (_socketConnector instanceof SocketConnector) + { + ((SocketConnector) _socketConnector).setWorkerTimeout(0); + } + ConnectFuture future = _socketConnector.connect(new InetSocketAddress(destination, port), this, cfg); future.join(); if (!future.isConnected()) @@ -333,56 +354,54 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver public void sessionCreated(IoSession protocolSession) throws Exception { - if (_acceptingConnections) + // Configure the session with SSL if necessary + SessionUtil.initialize(protocolSession); + if (_executorPool) { - // Configure the session with SSL if necessary - SessionUtil.initialize(protocolSession); - if (_executorPool) + if (_sslFactory != null) { - if (_sslFactory != null) - { - protocolSession.getFilterChain().addAfter("AsynchronousReadFilter", "sslFilter", - new SSLFilter(_sslFactory.buildServerContext())); - } + protocolSession.getFilterChain().addAfter("AsynchronousReadFilter", "sslFilter", + new SSLFilter(_sslFactory.buildServerContext())); } - else + } + else + { + if (_sslFactory != null) { - if (_sslFactory != null) - { - protocolSession.getFilterChain().addBefore("protocolFilter", "sslFilter", - new SSLFilter(_sslFactory.buildServerContext())); - } + protocolSession.getFilterChain().addBefore("protocolFilter", "sslFilter", + new SSLFilter(_sslFactory.buildServerContext())); } + } + // Do we want to have read/write buffer limits? + if (_protectIO) + { + //Add IO Protection Filters + IoFilterChain chain = protocolSession.getFilterChain(); - // Do we want to have read/write buffer limits? - if (_protectIO) - { - //Add IO Protection Filters - IoFilterChain chain = protocolSession.getFilterChain(); + protocolSession.getFilterChain().addLast("tempExecutorFilterForFilterBuilder", new ExecutorFilter()); - protocolSession.getFilterChain().addLast("tempExecutorFilterForFilterBuilder", new ExecutorFilter()); + ReadThrottleFilterBuilder readfilter = new ReadThrottleFilterBuilder(); + readfilter.setMaximumConnectionBufferSize(_config.getReceiveBufferSize()); + readfilter.attach(chain); - ReadThrottleFilterBuilder readfilter = new ReadThrottleFilterBuilder(); - readfilter.setMaximumConnectionBufferSize(_config.getReceiveBufferSize()); - readfilter.attach(chain); + WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder(); + writefilter.setMaximumConnectionBufferSize(_config.getSendBufferSize()); + writefilter.attach(chain); - WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder(); - writefilter.setMaximumConnectionBufferSize(_config.getSendBufferSize()); - writefilter.attach(chain); + protocolSession.getFilterChain().remove("tempExecutorFilterForFilterBuilder"); + } - protocolSession.getFilterChain().remove("tempExecutorFilterForFilterBuilder"); - } - - if (_ioSession == null) - { - _ioSession = protocolSession; - } - + if (_ioSession == null) + { + _ioSession = protocolSession; + } + + if (_acceptingConnections) + { // Set up the protocol engine ProtocolEngine protocolEngine = _factory.newProtocolEngine(this); MINANetworkDriver newDriver = new MINANetworkDriver(_useNIO, _processors, _executorPool, _protectIO, protocolEngine, protocolSession); protocolEngine.setNetworkDriver(newDriver); - protocolSession.setAttachment(protocolEngine); } } @@ -409,4 +428,13 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver _acceptingConnections = acceptingConnections; } + public void setProtocolEngine(ProtocolEngine protocolEngine) + { + _protocolEngine = protocolEngine; + if (_ioSession != null) + { + _ioSession.setAttachment(protocolEngine); + } + } + } diff --git a/qpid/java/common/src/test/java/org/apache/qpid/transport/TestNetworkDriver.java b/qpid/java/common/src/test/java/org/apache/qpid/transport/TestNetworkDriver.java new file mode 100644 index 0000000000..a4c4b59cdd --- /dev/null +++ b/qpid/java/common/src/test/java/org/apache/qpid/transport/TestNetworkDriver.java @@ -0,0 +1,122 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.transport; + +import java.net.BindException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.apache.qpid.protocol.ProtocolEngine; +import org.apache.qpid.protocol.ProtocolEngineFactory; +import org.apache.qpid.ssl.SSLContextFactory; + +/** + * Test implementation of IoSession, which is required for some tests. Methods not being used are not implemented, + * so if this class is being used and some methods are to be used, then please update those. + */ +public class TestNetworkDriver implements NetworkDriver +{ + private final ConcurrentMap attributes = new ConcurrentHashMap(); + private String _remoteAddress = "127.0.0.1"; + private String _localAddress = "127.0.0.1"; + private int _port = 1; + + public TestNetworkDriver() + { + } + + public void setRemoteAddress(String string) + { + this._remoteAddress = string; + } + + public void setPort(int _port) + { + this._port = _port; + } + + public int getPort() + { + return _port; + } + + public void bind(int port, InetAddress[] addresses, ProtocolEngineFactory protocolFactory, + NetworkDriverConfiguration config, SSLContextFactory sslFactory) throws BindException + { + + } + + public SocketAddress getLocalAddress() + { + return new InetSocketAddress(_localAddress, _port); + } + + public SocketAddress getRemoteAddress() + { + return new InetSocketAddress(_remoteAddress, _port); + } + + public void open(int port, InetAddress destination, ProtocolEngine engine, NetworkDriverConfiguration config, + SSLContextFactory sslFactory) throws OpenException + { + + } + + public void setMaxReadIdle(int idleTime) + { + + } + + public void setMaxWriteIdle(int idleTime) + { + + } + + public void close() + { + + } + + public void flush() + { + + } + + public void send(ByteBuffer msg) + { + + } + + public void setIdleTimeout(long l) + { + + } + + public void setLocalAddress(String localAddress) + { + _localAddress = localAddress; + } + +} diff --git a/qpid/java/common/src/test/java/org/apache/qpid/transport/network/mina/MINANetworkDriverTest.java b/qpid/java/common/src/test/java/org/apache/qpid/transport/network/mina/MINANetworkDriverTest.java index 6024875cf5..5500ff9d4b 100644 --- a/qpid/java/common/src/test/java/org/apache/qpid/transport/network/mina/MINANetworkDriverTest.java +++ b/qpid/java/common/src/test/java/org/apache/qpid/transport/network/mina/MINANetworkDriverTest.java @@ -382,6 +382,18 @@ public class MINANetworkDriverTest extends TestCase return null; } } + + public SocketAddress getLocalAddress() + { + if (_driver != null) + { + return _driver.getLocalAddress(); + } + else + { + return null; + } + } public long getWrittenBytes() { @@ -459,6 +471,7 @@ public class MINANetworkDriverTest extends TestCase { return _closed; } + } private class EchoProtocolEngine extends CountingProtocolEngine diff --git a/qpid/java/systests/build.xml b/qpid/java/systests/build.xml index ac3c77e4a3..34a360ecad 100644 --- a/qpid/java/systests/build.xml +++ b/qpid/java/systests/build.xml @@ -20,7 +20,7 @@ nn - or more contributor license agreements. See the NOTICE file --> - + diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java index b5c0a87b0f..f402522a19 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java @@ -22,6 +22,9 @@ package org.apache.qpid.server.security.acl; import junit.framework.TestCase; + +import org.apache.log4j.BasicConfigurator; +import org.apache.log4j.Logger; import org.apache.qpid.client.transport.TransportConnection; import org.apache.qpid.client.*; import org.apache.qpid.framing.AMQShortString; @@ -34,11 +37,17 @@ import org.apache.qpid.url.URLSyntaxException; import javax.jms.*; import javax.jms.IllegalStateException; + import java.io.File; +import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; -public class SimpleACLTest extends QpidTestCase implements ConnectionListener +public class SimpleACLTest extends QpidTestCase implements ConnectionListener, ExceptionListener { private String BROKER = "vm://:"+ApplicationRegistry.DEFAULT_INSTANCE;//"tcp://localhost:5672"; + private ArrayList _thrownExceptions = new ArrayList(); + private CountDownLatch _awaitError = new CountDownLatch(51); public void setUp() throws Exception { @@ -268,7 +277,7 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener } } - public void testClientPublishInvalidQueueSuccess() throws AMQException, URLSyntaxException, JMSException + public void testClientPublishInvalidQueueSuccess() throws AMQException, URLSyntaxException, JMSException, InterruptedException { try { @@ -276,41 +285,38 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener ((AMQConnection) conn).setConnectionListener(this); - Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Session session = conn.createSession(true, Session.AUTO_ACKNOWLEDGE); conn.start(); - + conn.setExceptionListener(this); MessageProducer sender = ((AMQSession) session).createProducer(null); - Queue queue = session.createQueue("Invalid"); - + Queue queue = session.createQueue("NewQueueThatIDoNotHaveRightsToPublishToo"); + // Send a message that we will wait to be sent, this should give the broker time to close the connection // before we finish this test. Message is set !immed !mand as the queue is invalid so want to test ACLs not // queue existence. ((org.apache.qpid.jms.MessageProducer) sender).send(queue, session.createTextMessage("test"), DeliveryMode.NON_PERSISTENT, 0, 0L, false, false, true); - // Test the connection with a valid consumer - // This may fail as the session may be closed before the queue or the consumer created. - Queue temp = session.createTemporaryQueue(); - - session.createConsumer(temp).close(); - - //Connection should now be closed and will throw the exception caused by the above send - conn.close(); - - fail("Close is not expected to succeed."); + _awaitError.await(1, TimeUnit.SECONDS); } catch (JMSException e) { - Throwable cause = e.getLinkedException(); - if (!(cause instanceof AMQAuthenticationException)) + fail("Unknown exception thrown:" + e.getMessage()); + } + boolean foundCorrectException = false; + for (Exception cause : _thrownExceptions) + { + if (cause instanceof JMSException) { - e.printStackTrace(); + if (((JMSException) cause).getLinkedException() instanceof AMQAuthenticationException) + { + foundCorrectException = true; + } } - assertEquals("Incorrect exception", AMQAuthenticationException.class, cause.getClass()); - assertEquals("Incorrect error code thrown", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode()); } + assertTrue("Did not get AMQAuthenticationException thrown", foundCorrectException); } public void testServerConsumeFromNamedQueueValid() throws AMQException, URLSyntaxException @@ -657,4 +663,10 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener public void failoverComplete() { } + + public void onException(JMSException arg0) + { + _thrownExceptions.add(arg0); + _awaitError.countDown(); + } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java index 91cb37e455..b99cd239d3 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java @@ -20,27 +20,27 @@ */ package org.apache.qpid.test.unit.client.protocol; -import org.apache.mina.common.IoSession; import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.client.protocol.AMQProtocolSession; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.test.utils.QpidTestCase; -import org.apache.qpid.test.utils.protocol.TestIoSession; +import org.apache.qpid.transport.TestNetworkDriver; +import org.apache.qpid.transport.NetworkDriver; public class AMQProtocolSessionTest extends QpidTestCase { private static class AMQProtSession extends AMQProtocolSession { - public AMQProtSession(AMQProtocolHandler protocolHandler, IoSession protocolSession, AMQConnection connection) + public AMQProtSession(AMQProtocolHandler protocolHandler, AMQConnection connection) { - super(protocolHandler,protocolSession,connection); + super(protocolHandler,connection); } - public TestIoSession getMinaProtocolSession() + public TestNetworkDriver getNetworkDriver() { - return (TestIoSession) _minaProtocolSession; + return (TestNetworkDriver) _protocolHandler.getNetworkDriver(); } public AMQShortString genQueueName() @@ -63,8 +63,11 @@ public class AMQProtocolSessionTest extends QpidTestCase { super.setUp(); + AMQConnection con = (AMQConnection) getConnection("guest", "guest"); + AMQProtocolHandler protocolHandler = new AMQProtocolHandler(con); + protocolHandler.setNetworkDriver(new TestNetworkDriver()); //don't care about the values set here apart from the dummy IoSession - _testSession = new AMQProtSession(null,new TestIoSession(), (AMQConnection) getConnection("guest", "guest")); + _testSession = new AMQProtSession(protocolHandler , con); //initialise addresses for test and expected results _port = 123; @@ -75,32 +78,32 @@ public class AMQProtocolSessionTest extends QpidTestCase _validAddress = "abc"; _generatedAddress_3 = "tmp_abc123_3"; } - +/* public void testGenerateQueueName() { AMQShortString testAddress; //test address with / and ; chars which generateQueueName should removeKey - _testSession.getMinaProtocolSession().setStringLocalAddress(_brokenAddress); - _testSession.getMinaProtocolSession().setLocalPort(_port); + _testSession.getNetworkDriver().setLocalAddress(_brokenAddress); + _testSession.getNetworkDriver().setPort(_port); testAddress = _testSession.genQueueName(); assertEquals("Failure when generating a queue exchange from an address with special chars",_generatedAddress,testAddress.toString()); //test empty address - _testSession.getMinaProtocolSession().setStringLocalAddress(_emptyAddress); + _testSession.getNetworkDriver().setLocalAddress(_emptyAddress); testAddress = _testSession.genQueueName(); assertEquals("Failure when generating a queue exchange from an empty address",_generatedAddress_2,testAddress.toString()); //test address with no special chars - _testSession.getMinaProtocolSession().setStringLocalAddress(_validAddress); + _testSession.getNetworkDriver().setStringLocalAddress(_validAddress); testAddress = _testSession.genQueueName(); assertEquals("Failure when generating a queue exchange from an address with no special chars",_generatedAddress_3,testAddress.toString()); } - +*/ protected void tearDown() throws Exception { _testSession = null; -- cgit v1.2.1 From 23ed5eb6dbd7cf39cf18b308a0d4e339c4db1c83 Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Wed, 16 Sep 2009 10:07:44 +0000 Subject: QPID-2106: Don't close connections if the broker has asked it to close and there's still stuff to process. Let the cleanup thread do that so that publishes which are denied don't result in instant connection death. git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-network-refactor@815705 13f79535-47bb-0310-9956-ffa450edef68 --- .../qpid/server/connection/ConnectionRegistry.java | 8 +++++ .../qpid/server/protocol/AMQProtocolEngine.java | 34 ++++++++++++++-------- .../qpid/server/protocol/AMQProtocolSession.java | 2 ++ .../qpid/server/virtualhost/VirtualHost.java | 8 +++++ 4 files changed, 40 insertions(+), 12 deletions(-) (limited to 'qpid') diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java index 71e07172ed..7b50a2e3ad 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java @@ -44,6 +44,14 @@ public class ConnectionRegistry implements IConnectionRegistry { } + + public void expireClosedChannels() + { + for (AMQProtocolSession connection : _registry) + { + connection.closeIfLingeringClosedChannels(); + } + } /** Close all of the currently open connections. */ public void close() throws AMQException diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java index 16ebc76185..4f396015a2 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java @@ -29,6 +29,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicLong; @@ -135,7 +137,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol private FieldTable _clientProperties; private final List _taskList = new CopyOnWriteArrayList(); - private List _closingChannelsList = new CopyOnWriteArrayList(); + private Map _closingChannelsList = new ConcurrentHashMap(); private ProtocolOutputConverter _protocolOutputConverter; private Principal _authorizedID; private MethodDispatcher _dispatcher; @@ -293,12 +295,8 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol } else { - if (_logger.isInfoEnabled()) - { - _logger.info("Channel[" + channelId + "] awaiting closure. Should close socket as client did not close-ok :" + frame); - } - - closeProtocolSession(); + // The channel has been told to close, we don't process any more frames until + // it's closed. return; } } @@ -513,7 +511,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol public boolean channelAwaitingClosure(int channelId) { - return !_closingChannelsList.isEmpty() && _closingChannelsList.contains(channelId); + return !_closingChannelsList.isEmpty() && _closingChannelsList.containsKey(channelId); } public void addChannel(AMQChannel channel) throws AMQException @@ -525,7 +523,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol final int channelId = channel.getChannelId(); - if (_closingChannelsList.contains(channelId)) + if (_closingChannelsList.containsKey(channelId)) { throw new AMQException("Session is marked awaiting channel close"); } @@ -632,7 +630,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol private void markChannelAwaitingCloseOk(int channelId) { - _closingChannelsList.add(channelId); + _closingChannelsList.put(channelId, System.currentTimeMillis()); } /** @@ -1023,7 +1021,19 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol { return (_clientVersion == null) ? null : _clientVersion.toString(); } - - + + @Override + public void closeIfLingeringClosedChannels() + { + for (Entryid : _closingChannelsList.entrySet()) + { + if (id.getValue() + 30000 > System.currentTimeMillis()) + { + // We have a channel that we closed 30 seconds ago. Client's dead, kill the connection + _logger.error("Closing connection as channel was closed more than 30 seconds ago and no ChannelCloseOk has been processed"); + closeProtocolSession(); + } + } + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java index b0bef04354..b16ed01c79 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java @@ -225,5 +225,7 @@ public interface AMQProtocolSession extends AMQVersionAwareProtocolSession void commitTransactions(AMQChannel channel) throws AMQException; List getChannels(); + + void closeIfLingeringClosedChannels(); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java index fa6b2285eb..aec437b700 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java @@ -267,6 +267,14 @@ public class VirtualHost implements Accessable _houseKeepingTimer.scheduleAtFixedRate(new RemoveExpiredMessagesTask(), period / 2, period); + + class ForceChannelClosuresTask extends TimerTask + { + public void run() + { + _connectionRegistry.expireClosedChannels(); + } + } } } -- cgit v1.2.1 From e2318508673d16d54b544ca589357003b7cfecc1 Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Wed, 16 Sep 2009 10:17:20 +0000 Subject: QPID-2105: Merging the patch didn't uncomment this test, stoopid git. git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-network-refactor@815708 13f79535-47bb-0310-9956-ffa450edef68 --- .../qpid/test/unit/client/protocol/AMQProtocolSessionTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'qpid') diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java index b99cd239d3..7a27925a36 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java @@ -78,7 +78,7 @@ public class AMQProtocolSessionTest extends QpidTestCase _validAddress = "abc"; _generatedAddress_3 = "tmp_abc123_3"; } -/* + public void testGenerateQueueName() { AMQShortString testAddress; @@ -97,13 +97,13 @@ public class AMQProtocolSessionTest extends QpidTestCase assertEquals("Failure when generating a queue exchange from an empty address",_generatedAddress_2,testAddress.toString()); //test address with no special chars - _testSession.getNetworkDriver().setStringLocalAddress(_validAddress); + _testSession.getNetworkDriver().setLocalAddress(_validAddress); testAddress = _testSession.genQueueName(); assertEquals("Failure when generating a queue exchange from an address with no special chars",_generatedAddress_3,testAddress.toString()); } -*/ + protected void tearDown() throws Exception { _testSession = null; -- cgit v1.2.1 From 93fa7d17feecb3d27cead67e11b250af1fcc595e Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Wed, 16 Sep 2009 11:32:28 +0000 Subject: QPID-2015: Remove AMQIoTransportProtocolSession. Release the executor service in the same class as it's acquired git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-network-refactor@815729 13f79535-47bb-0310-9956-ffa450edef68 --- .../qpid/server/protocol/AMQProtocolEngine.java | 2 +- .../protocol/AMQIoTransportProtocolSession.java | 146 --------------------- .../qpid/client/protocol/AMQProtocolHandler.java | 1 + .../java/org/apache/qpid/pool/PoolingFilter.java | 3 - 4 files changed, 2 insertions(+), 150 deletions(-) delete mode 100644 qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQIoTransportProtocolSession.java (limited to 'qpid') diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java index 4f396015a2..b776c6ae82 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java @@ -706,7 +706,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol } _closed = true; - + _poolReference.releaseExecutorService(); CurrentActor.get().message(_logSubject, ConnectionMessages.CON_1002()); } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQIoTransportProtocolSession.java b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQIoTransportProtocolSession.java deleted file mode 100644 index 8782e00a12..0000000000 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQIoTransportProtocolSession.java +++ /dev/null @@ -1,146 +0,0 @@ -package org.apache.qpid.client.protocol; -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT 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 java.util.UUID; - -import javax.security.sasl.SaslClient; - -import org.apache.commons.lang.StringUtils; -import org.apache.mina.common.IdleStatus; -import org.apache.qpid.AMQException; -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.ConnectionTuneParameters; -import org.apache.qpid.client.handler.ClientMethodDispatcherImpl; -import org.apache.qpid.client.state.AMQState; -import org.apache.qpid.framing.AMQDataBlock; -import org.apache.qpid.framing.AMQMethodBody; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.ProtocolInitiation; -import org.apache.qpid.framing.ProtocolVersion; -import org.apache.qpid.transport.Sender; - -public class AMQIoTransportProtocolSession extends AMQProtocolSession -{ - - protected Sender _ioSender; - private SaslClient _saslClient; - private ConnectionTuneParameters _connectionTuneParameters; - - public AMQIoTransportProtocolSession(AMQProtocolHandler protocolHandler, AMQConnection connection) - { - super(protocolHandler, connection); - } - - @Override - public void closeProtocolSession(boolean waitLast) - { - _ioSender.close(); - _protocolHandler.getStateManager().changeState(AMQState.CONNECTION_CLOSED); - } - - @Override - public void init() - { - _ioSender.send(new ProtocolInitiation(_connection.getProtocolVersion()).toNioByteBuffer()); - _ioSender.flush(); - } - - @Override - protected AMQShortString generateQueueName() - { - int id; - synchronized (_queueIdLock) - { - id = _queueId++; - } - return new AMQShortString("tmp_" + UUID.randomUUID() + "_" + id); - } - - @Override - public AMQConnection getAMQConnection() - { - return _connection; - } - - @Override - public SaslClient getSaslClient() - { - return _saslClient; - } - - @Override - public void setSaslClient(SaslClient client) - { - _saslClient = client; - } - - /** @param delay delay in seconds (not ms) */ - @Override - void initHeartbeats(int delay) - { - if (delay > 0) - { - // FIXME: actually do something here - HeartbeatDiagnostics.init(delay, HeartbeatConfig.CONFIG.getTimeout(delay)); - } - } - - @Override - public void methodFrameReceived(final int channel, final AMQMethodBody amqMethodBody) throws AMQException - { - // FIXME? - _protocolHandler.methodBodyReceived(channel, amqMethodBody, null); - } - - @Override - public void writeFrame(AMQDataBlock frame, boolean wait) - { - _ioSender.send(frame.toNioByteBuffer()); - if (wait) - { - _ioSender.flush(); - } - } - - @Override - public void setSender(Sender sender) - { - _ioSender = sender; - } - - @Override - public ConnectionTuneParameters getConnectionTuneParameters() - { - return _connectionTuneParameters; - } - - @Override - public void setConnectionTuneParameters(ConnectionTuneParameters params) - { - _connectionTuneParameters = params; - AMQConnection con = getAMQConnection(); - con.setMaximumChannelCount(params.getChannelMax()); - con.setMaximumFrameSize(params.getFrameMax()); - initHeartbeats((int) params.getHeartbeat()); - } -} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java index c7e2493025..99366101d1 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java @@ -716,6 +716,7 @@ public class AMQProtocolHandler implements ProtocolEngine _logger.debug("FailoverException interrupted connection close, ignoring as connection close anyway."); } } + _poolReference.releaseExecutorService(); } /** @return the number of bytes read from this protocol session */ diff --git a/qpid/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java b/qpid/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java index 4863611c42..4e02ac3a55 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java @@ -136,9 +136,6 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo public void destroy() { _logger.debug("Destroy called on PoolingFilter " + toString()); - - // When the reference count gets to zero we release the executor service. - _poolReference.releaseExecutorService(); } /** -- cgit v1.2.1 From 31bbc100ac6b3a31eb25d29f407d60ff23334d1f Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Thu, 17 Sep 2009 15:19:54 +0000 Subject: QPID-2024 QPID-2105: Remove now unnecessary classes like Event, PoolingFilter, ReadWriteThreadModel. Move the couple of necessary methods to Job. Fix imports. git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-network-refactor@816232 13f79535-47bb-0310-9956-ffa450edef68 --- .../qpid/server/protocol/AMQProtocolEngine.java | 20 +- .../qpid/client/protocol/AMQProtocolHandler.java | 53 +-- .../transport/SocketTransportConnection.java | 23 +- .../qpid/client/transport/TransportConnection.java | 14 +- .../transport/VmPipeTransportConnection.java | 6 +- .../src/main/java/org/apache/qpid/pool/Event.java | 167 ------- .../src/main/java/org/apache/qpid/pool/Job.java | 111 +++-- .../java/org/apache/qpid/pool/PoolingFilter.java | 487 --------------------- .../org/apache/qpid/pool/ReadWriteThreadModel.java | 102 ----- .../transport/network/mina/MINANetworkDriver.java | 22 - .../org/apache/qpid/pool/PoolingFilterTest.java | 111 ----- 11 files changed, 113 insertions(+), 1003 deletions(-) delete mode 100644 qpid/java/common/src/main/java/org/apache/qpid/pool/Event.java delete mode 100644 qpid/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java delete mode 100644 qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java delete mode 100644 qpid/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java (limited to 'qpid') diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java index b776c6ae82..3bcd102858 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java @@ -62,10 +62,7 @@ import org.apache.qpid.framing.MethodDispatcher; import org.apache.qpid.framing.MethodRegistry; import org.apache.qpid.framing.ProtocolInitiation; import org.apache.qpid.framing.ProtocolVersion; -import org.apache.qpid.pool.Event; import org.apache.qpid.pool.Job; -import org.apache.qpid.pool.PoolingFilter; -import org.apache.qpid.pool.ReadWriteThreadModel; import org.apache.qpid.pool.ReferenceCountingExecutorService; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.AMQMethodEvent; @@ -172,14 +169,13 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol _networkDriver = driver; _codecFactory = new AMQCodecFactory(true, this); - - ReadWriteThreadModel threadModel = ReadWriteThreadModel.getInstance(); - _readJob = new Job(threadModel.getAsynchronousReadFilter(), PoolingFilter.MAX_JOB_EVENTS, true); - _writeJob = new Job(threadModel.getAsynchronousWriteFilter(), PoolingFilter.MAX_JOB_EVENTS, false); + _poolReference.acquireExecutorService(); + _readJob = new Job(_poolReference, Job.MAX_JOB_EVENTS, true); + _writeJob = new Job(_poolReference, Job.MAX_JOB_EVENTS, false); _actor = new AMQPConnectionActor(this, virtualHostRegistry.getApplicationRegistry().getRootMessageLogger()); _actor.message(ConnectionMessages.CON_1001(null, null, false, false)); - _poolReference.acquireExecutorService(); + } private AMQProtocolSessionMBean createMBean() throws AMQException @@ -212,7 +208,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol try { final ArrayList dataBlocks = _codecFactory.getDecoder().decodeBuffer(msg); - Job.fireAsynchEvent(_poolReference.getPool(), _readJob, new Event(new Runnable() + Job.fireAsynchEvent(_poolReference.getPool(), _readJob, new Runnable() { @Override public void run() @@ -232,7 +228,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol } } } - })); + }); } catch (Exception e) { @@ -459,14 +455,14 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol final ByteBuffer buf = frame.toNioByteBuffer(); _lastIoTime = System.currentTimeMillis(); _writtenBytes += buf.remaining(); - Job.fireAsynchEvent(_poolReference.getPool(), _writeJob, new Event(new Runnable() + Job.fireAsynchEvent(_poolReference.getPool(), _writeJob, new Runnable() { @Override public void run() { _networkDriver.send(buf); } - })); + }); } public AMQShortString getContextKey() diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java index 99366101d1..be75fc150f 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java @@ -20,20 +20,22 @@ */ package org.apache.qpid.client.protocol; -import org.apache.mina.filter.ReadThrottleFilterBuilder; -import org.apache.mina.filter.SSLFilter; -import org.apache.mina.filter.WriteBufferLimitFilterBuilder; +import java.io.IOException; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.CountDownLatch; + import org.apache.mina.filter.codec.ProtocolCodecException; -import org.apache.mina.filter.codec.ProtocolCodecFilter; -import org.apache.mina.filter.executor.ExecutorFilter; import org.apache.qpid.AMQConnectionClosedException; import org.apache.qpid.AMQDisconnectedException; import org.apache.qpid.AMQException; import org.apache.qpid.AMQTimeoutException; import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQSession; -import org.apache.qpid.client.SSLConfiguration; -import org.apache.qpid.client.configuration.ClientProperties; import org.apache.qpid.client.failover.FailoverException; import org.apache.qpid.client.failover.FailoverHandler; import org.apache.qpid.client.failover.FailoverState; @@ -42,32 +44,29 @@ import org.apache.qpid.client.state.AMQStateManager; import org.apache.qpid.client.state.StateWaiter; import org.apache.qpid.client.state.listener.SpecificMethodFrameListener; import org.apache.qpid.codec.AMQCodecFactory; -import org.apache.qpid.framing.*; +import org.apache.qpid.framing.AMQBody; +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ConnectionCloseBody; +import org.apache.qpid.framing.ConnectionCloseOkBody; +import org.apache.qpid.framing.HeartbeatBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.framing.ProtocolInitiation; +import org.apache.qpid.framing.ProtocolVersion; import org.apache.qpid.jms.BrokerDetails; -import org.apache.qpid.pool.Event; import org.apache.qpid.pool.Job; -import org.apache.qpid.pool.PoolingFilter; -import org.apache.qpid.pool.ReadWriteThreadModel; import org.apache.qpid.pool.ReferenceCountingExecutorService; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.AMQMethodEvent; import org.apache.qpid.protocol.AMQMethodListener; import org.apache.qpid.protocol.ProtocolEngine; -import org.apache.qpid.ssl.SSLContextFactory; import org.apache.qpid.transport.NetworkDriver; import org.apache.qpid.transport.network.io.IoTransport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.net.SocketAddress; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.CountDownLatch; - /** * AMQProtocolHandler is the client side protocol handler for AMQP, it handles all protocol events received from the * network by MINA. The primary purpose of AMQProtocolHandler is to translate the generic event model of MINA into the @@ -107,9 +106,6 @@ import java.util.concurrent.CountDownLatch; * *

*
CRC Card
Responsibilities Collaborations - *
Create the filter chain to filter this handlers events. - * {@link ProtocolCodecFilter}, {@link SSLContextFactory}, {@link SSLFilter}, {@link ReadWriteThreadModel}. - * *
Maintain fail-over state. *
*
@@ -191,9 +187,8 @@ public class AMQProtocolHandler implements ProtocolEngine _protocolSession = new AMQProtocolSession(this, _connection); _stateManager = new AMQStateManager(_protocolSession); _codecFactory = new AMQCodecFactory(false, _protocolSession); - ReadWriteThreadModel threadModel = ReadWriteThreadModel.getInstance(); - _readJob = new Job(threadModel.getAsynchronousReadFilter(), PoolingFilter.MAX_JOB_EVENTS, true); - _writeJob = new Job(threadModel.getAsynchronousWriteFilter(), PoolingFilter.MAX_JOB_EVENTS, false); + _readJob = new Job(_poolReference, Job.MAX_JOB_EVENTS, true); + _writeJob = new Job(_poolReference, Job.MAX_JOB_EVENTS, false); _poolReference.acquireExecutorService(); _failoverHandler = new FailoverHandler(this); } @@ -436,7 +431,7 @@ public class AMQProtocolHandler implements ProtocolEngine _readBytes += msg.remaining(); final ArrayList dataBlocks = _codecFactory.getDecoder().decodeBuffer(msg); - Job.fireAsynchEvent(_poolReference.getPool(), _readJob, new Event(new Runnable() + Job.fireAsynchEvent(_poolReference.getPool(), _readJob, new Runnable() { @Override public void run() @@ -495,7 +490,7 @@ public class AMQProtocolHandler implements ProtocolEngine } } } - })); + }); } catch (Exception e) { diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java index 77c9c40e82..1ac8f62e32 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java @@ -20,37 +20,20 @@ */ package org.apache.qpid.client.transport; +import java.io.IOException; +import java.net.InetSocketAddress; + import org.apache.mina.common.ByteBuffer; -import org.apache.mina.common.ConnectFuture; import org.apache.mina.common.IoConnector; import org.apache.mina.common.SimpleByteBufferAllocator; -import org.apache.mina.filter.SSLFilter; -import org.apache.mina.transport.socket.nio.ExistingSocketConnector; -import org.apache.mina.transport.socket.nio.SocketConnectorConfig; -import org.apache.mina.transport.socket.nio.SocketSessionConfig; - import org.apache.qpid.client.SSLConfiguration; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.jms.BrokerDetails; -import org.apache.qpid.pool.ReadWriteThreadModel; import org.apache.qpid.ssl.SSLContextFactory; import org.apache.qpid.transport.network.mina.MINANetworkDriver; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sun.net.InetAddressCachePolicy; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.security.GeneralSecurityException; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import javax.net.ssl.SSLEngine; - public class SocketTransportConnection implements ITransportConnection { private static final Logger _logger = LoggerFactory.getLogger(SocketTransportConnection.class); diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java index 45194750dc..a4f8bb0166 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java @@ -20,6 +20,12 @@ */ package org.apache.qpid.client.transport; +import java.io.IOException; +import java.net.Socket; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + import org.apache.mina.common.IoConnector; import org.apache.mina.common.IoHandlerAdapter; import org.apache.mina.common.IoServiceConfig; @@ -30,20 +36,12 @@ import org.apache.mina.transport.vmpipe.VmPipeAcceptor; import org.apache.mina.transport.vmpipe.VmPipeAddress; import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException; import org.apache.qpid.jms.BrokerDetails; -import org.apache.qpid.pool.ReadWriteThreadModel; -import org.apache.qpid.protocol.ProtocolEngine; import org.apache.qpid.protocol.ProtocolEngineFactory; import org.apache.qpid.thread.QpidThreadExecutor; import org.apache.qpid.transport.network.mina.MINANetworkDriver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.net.Socket; - /** * The TransportConnection is a helper class responsible for connecting to an AMQ server. It sets up the underlying * connector, which currently always uses TCP/IP sockets. It creates the "protocol handler" which deals with MINA diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java index 3de6f9b9ea..504d475740 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java @@ -20,20 +20,18 @@ */ package org.apache.qpid.client.transport; +import java.io.IOException; + import org.apache.mina.common.ConnectFuture; -import org.apache.mina.common.IoServiceConfig; import org.apache.mina.transport.vmpipe.QpidVmPipeConnector; import org.apache.mina.transport.vmpipe.VmPipeAddress; import org.apache.mina.transport.vmpipe.VmPipeConnector; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.jms.BrokerDetails; -import org.apache.qpid.pool.ReadWriteThreadModel; import org.apache.qpid.transport.network.mina.MINANetworkDriver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; - public class VmPipeTransportConnection implements ITransportConnection { private static final Logger _logger = LoggerFactory.getLogger(VmPipeTransportConnection.class); diff --git a/qpid/java/common/src/main/java/org/apache/qpid/pool/Event.java b/qpid/java/common/src/main/java/org/apache/qpid/pool/Event.java deleted file mode 100644 index 49bce9f2f9..0000000000 --- a/qpid/java/common/src/main/java/org/apache/qpid/pool/Event.java +++ /dev/null @@ -1,167 +0,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.pool; - -import org.apache.mina.common.IoFilter; -import org.apache.mina.common.IoSession; - -/** - * An Event is a continuation, which is used to break a Mina filter chain and save the current point in the chain - * for later processing. It is an abstract class, with different implementations for continuations of different kinds - * of Mina events. - * - *

These continuations are typically batched by {@link Job} for processing by a worker thread pool. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Process a continuation in the context of a Mina session. - *
- * - * @todo Pull up _nextFilter and getNextFilter into Event, as all events use it. Inner classes need to be non-static - * to use instance variables in the parent. Consequently they need to be non-inner to be instantiable outside of - * the context of the outer Event class. The inner class construction used here is preventing common code re-use - * (though not by a huge amount), but makes for an inelegent way of handling inheritance and doesn't seem like - * a justifiable use of inner classes. Move the inner classes out into their own files. - * - * @todo Could make Event implement Runnable, FutureTask, or a custom Continuation interface, to clarify its status as - * a continuation. Job is also a continuation, as is the job completion handler. Or, as Event is totally abstract, - * it is really an interface, so could just drop it and use the continuation interface instead. - */ -public class Event -{ - private Runnable _runner; - - public Event() - { - - } - - /** - * Creates a continuation. - */ - public Event(Runnable runner) - { - _runner = runner; - } - - /** - * Processes the continuation - */ - public void process() - { - _runner.run(); - } - - /** - * A continuation ({@link Event}) that takes a Mina messageReceived event, and passes it to a NextFilter. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Pass a Mina messageReceived event to a NextFilter. {@link IoFilter.NextFilter}, {@link IoSession} - *
- */ - public static final class MinaReceivedEvent extends Event - { - private final Object _data; - private final IoFilter.NextFilter _nextFilter; - private final IoSession _session; - - public MinaReceivedEvent(final IoFilter.NextFilter nextFilter, final Object data, final IoSession session) - { - _nextFilter = nextFilter; - _data = data; - _session = session; - } - - public void process() - { - _nextFilter.messageReceived(_session, _data); - } - - public IoFilter.NextFilter getNextFilter() - { - return _nextFilter; - } - } - - /** - * A continuation ({@link Event}) that takes a Mina filterWrite event, and passes it to a NextFilter. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Pass a Mina filterWrite event to a NextFilter. - * {@link IoFilter.NextFilter}, {@link IoFilter.WriteRequest}, {@link IoSession} - *
- */ - public static final class MinaWriteEvent extends Event - { - private final IoFilter.WriteRequest _data; - private final IoFilter.NextFilter _nextFilter; - private IoSession _session; - - public MinaWriteEvent(final IoFilter.NextFilter nextFilter, final IoFilter.WriteRequest data, final IoSession session) - { - _nextFilter = nextFilter; - _data = data; - _session = session; - } - - public void process() - { - _nextFilter.filterWrite(_session, _data); - } - - public IoFilter.NextFilter getNextFilter() - { - return _nextFilter; - } - } - - /** - * A continuation ({@link Event}) that takes a Mina sessionClosed event, and passes it to a NextFilter. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Pass a Mina sessionClosed event to a NextFilter. {@link IoFilter.NextFilter}, {@link IoSession} - *
- */ - public static final class CloseEvent extends Event - { - private final IoFilter.NextFilter _nextFilter; - private final IoSession _session; - - public CloseEvent(final IoFilter.NextFilter nextFilter, final IoSession session) - { - _nextFilter = nextFilter; - _session = session; - } - - public void process() - { - _nextFilter.sessionClosed(_session); - } - - public IoFilter.NextFilter getNextFilter() - { - return _nextFilter; - } - } -} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/pool/Job.java b/qpid/java/common/src/main/java/org/apache/qpid/pool/Job.java index 15d1c20ff1..82b600de88 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/pool/Job.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/pool/Job.java @@ -25,7 +25,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicBoolean; -import org.apache.mina.common.IoSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,40 +55,28 @@ import org.slf4j.LoggerFactory; */ public class Job implements ReadWriteRunnable { + + /** Defines the maximum number of events that will be batched into a single job. */ + public static final int MAX_JOB_EVENTS = Integer.getInteger("amqj.server.read_write_pool.max_events", 10); + /** The maximum number of events to process per run of the job. More events than this may be queued in the job. */ private final int _maxEvents; /** Holds the queue of events that make up the job. */ - private final java.util.Queue _eventQueue = new ConcurrentLinkedQueue(); + private final java.util.Queue _eventQueue = new ConcurrentLinkedQueue(); /** Holds a status flag, that indicates when the job is actively running. */ private final AtomicBoolean _active = new AtomicBoolean(); - /** Holds the completion continuation, called upon completion of a run of the job. */ - private final JobCompletionHandler _completionHandler; - private final boolean _readJob; + private ReferenceCountingExecutorService _poolReference; + private final static Logger _logger = LoggerFactory.getLogger(Job.class); - /** - * Creates a new job that aggregates many continuations together. - * - * @param session The Mina session. - * @param completionHandler The per job run, terminal continuation. - * @param maxEvents The maximum number of aggregated continuations to process per run of the job. - * @param readJob - */ - Job(IoSession session, JobCompletionHandler completionHandler, int maxEvents, final boolean readJob) - { - _completionHandler = completionHandler; - _maxEvents = maxEvents; - _readJob = readJob; - } - - public Job(JobCompletionHandler completionHandler, int maxEvents, boolean readJob) + public Job(ReferenceCountingExecutorService poolReference, int maxEvents, boolean readJob) { - _completionHandler = completionHandler; + _poolReference = poolReference; _maxEvents = maxEvents; _readJob = readJob; } @@ -99,7 +86,7 @@ public class Job implements ReadWriteRunnable * * @param evt The continuation to enqueue. */ - public void add(Event evt) + public void add(Runnable evt) { _eventQueue.add(evt); } @@ -113,14 +100,14 @@ public class Job implements ReadWriteRunnable int i = _maxEvents; while( --i != 0 ) { - Event e = _eventQueue.poll(); + Runnable e = _eventQueue.poll(); if (e == null) { return true; } else { - e.process(); + e.run(); } } return false; @@ -162,11 +149,11 @@ public class Job implements ReadWriteRunnable if(processAll()) { deactivate(); - _completionHandler.completed(this); + completed(); } else { - _completionHandler.notCompleted(this); + notCompleted(); } } @@ -174,19 +161,6 @@ public class Job implements ReadWriteRunnable { return _readJob; } - - /** - * Another interface for a continuation. - * - * @todo Get rid of this interface as there are other interfaces that could be used instead, such as FutureTask, - * Runnable or a custom Continuation interface. - */ - static interface JobCompletionHandler - { - public void completed(Job job); - - public void notCompleted(final Job job); - } /** * Adds an {@link Event} to a {@link Job}, triggering the execution of the job if it is not already running. @@ -194,7 +168,7 @@ public class Job implements ReadWriteRunnable * @param job The job. * @param event The event to hand off asynchronously. */ - public static void fireAsynchEvent(ExecutorService pool, Job job, Event event) + public static void fireAsynchEvent(ExecutorService pool, Job job, Runnable event) { job.add(event); @@ -221,4 +195,59 @@ public class Job implements ReadWriteRunnable } + /** + * Implements a terminal continuation for the {@link Job} for this filter. Whenever the Job completes its processing + * of a batch of events this is called. This method simply re-activates the job, if it has more events to process. + * + * @param session The Mina session to work in. + * @param job The job that completed. + */ + public void completed() + { + if (!isComplete()) + { + final ExecutorService pool = _poolReference.getPool(); + + if(pool == null) + { + return; + } + + + // ritchiem : 2006-12-13 Do we need to perform the additional checks here? + // Can the pool be shutdown at this point? + if (activate()) + { + try + { + pool.execute(this); + } + catch(RejectedExecutionException e) + { + _logger.warn("Thread pool shutdown while tasks still outstanding"); + } + + } + } + } + + public void notCompleted() + { + final ExecutorService pool = _poolReference.getPool(); + + if(pool == null) + { + return; + } + + try + { + pool.execute(this); + } + catch(RejectedExecutionException e) + { + _logger.warn("Thread pool shutdown while tasks still outstanding"); + } + } + } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java b/qpid/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java deleted file mode 100644 index 4e02ac3a55..0000000000 --- a/qpid/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java +++ /dev/null @@ -1,487 +0,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.pool; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.RejectedExecutionException; - -import org.apache.mina.common.IdleStatus; -import org.apache.mina.common.IoFilterAdapter; -import org.apache.mina.common.IoSession; -import org.apache.qpid.pool.Event.CloseEvent; -import org.apache.qpid.pool.Event.MinaReceivedEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * PoolingFilter, is a no-op pass through filter that hands all events down the Mina filter chain by default. As it - * adds no behaviour by default to the filter chain, it is abstract. - * - *

PoolingFilter provides a capability, available to sub-classes, to handle events in the chain asynchronously, by - * adding them to a job. If a job is not active, adding an event to it activates it. If it is active, the event is - * added to the job, which will run to completion and eventually process the event. The queue on the job itself acts as - * a buffer between stages of the pipeline. - * - *

There are two convenience methods, {@link #createAynschReadPoolingFilter} and - * {@link #createAynschWritePoolingFilter}, for obtaining pooling filters that handle 'messageReceived' and - * 'filterWrite' events, making it possible to process these event streams seperately. - * - *

Pooling filters have a name, in order to distinguish different filter types. They set up a {@link Job} on the - * Mina session they are working with, and store it in the session against their identifying name. This allows different - * filters with different names to be set up on the same filter chain, on the same Mina session, that batch their - * workloads in different jobs. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Implement default, pass through filter. - *
Create pooling filters and a specific thread pool. {@link ReferenceCountingExecutorService} - *
Provide the ability to batch Mina events for asynchronous processing. {@link Job}, {@link Event} - *
Provide a terminal continuation to keep jobs running till empty. - * {@link Job}, {@link Job.JobCompletionHandler} - *
- * - * @todo The static helper methods are pointless. Could just call new. - */ -public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCompletionHandler -{ - /** Used for debugging purposes. */ - private static final Logger _logger = LoggerFactory.getLogger(PoolingFilter.class); - - /** Holds the managed reference to obtain the executor for the batched jobs. */ - private final ReferenceCountingExecutorService _poolReference; - - /** Used to hold a name for identifying differeny pooling filter types. */ - private final String _name; - - /** Defines the maximum number of events that will be batched into a single job. */ - public static final int MAX_JOB_EVENTS = Integer.getInteger("amqj.server.read_write_pool.max_events", 10); - - private final int _maxEvents; - - private final boolean _readFilter; - - /** - * Creates a named pooling filter, on the specified shared thread pool. - * - * @param refCountingPool The thread pool reference. - * @param name The identifying name of the filter type. - */ - public PoolingFilter(ReferenceCountingExecutorService refCountingPool, String name, int maxEvents, boolean readFilter) - { - _poolReference = refCountingPool; - _name = name; - _maxEvents = maxEvents; - _readFilter = readFilter; - } - - /** - * Helper method to get an instance of a pooling filter that handles read events asynchronously. - * - * @param refCountingPool A managed reference to the thread pool. - * @param name The filter types identifying name. - * - * @return A pooling filter for asynchronous read events. - */ - public static PoolingFilter createAynschReadPoolingFilter(ReferenceCountingExecutorService refCountingPool, String name) - { - return new AsynchReadPoolingFilter(refCountingPool, name); - } - - /** - * Helper method to get an instance of a pooling filter that handles write events asynchronously. - * - * @param refCountingPool A managed reference to the thread pool. - * @param name The filter types identifying name. - * - * @return A pooling filter for asynchronous write events. - */ - public static PoolingFilter createAynschWritePoolingFilter(ReferenceCountingExecutorService refCountingPool, String name) - { - return new AsynchWritePoolingFilter(refCountingPool, name); - } - - /** - * Called by Mina to initialize this filter. Takes a reference to the thread pool. - */ - public void init() - { - _logger.debug("Init called on PoolingFilter " + toString()); - - // Called when the filter is initialised in the chain. If the reference count is - // zero this acquire will initialise the pool. - _poolReference.acquireExecutorService(); - } - - /** - * Called by Mina to clean up this filter. Releases the reference to the thread pool. - */ - public void destroy() - { - _logger.debug("Destroy called on PoolingFilter " + toString()); - } - - /** - * Adds an {@link Event} to a {@link Job}, triggering the execution of the job if it is not already running. - * - * @param job The job. - * @param event The event to hand off asynchronously. - */ - void fireAsynchEvent(Job job, Event event) - { - - job.add(event); - - final ExecutorService pool = _poolReference.getPool(); - - if(pool == null) - { - return; - } - - // rather than perform additional checks on pool to check that it hasn't shutdown. - // catch the RejectedExecutionException that will result from executing on a shutdown pool - if (job.activate()) - { - try - { - pool.execute(job); - } - catch(RejectedExecutionException e) - { - _logger.warn("Thread pool shutdown while tasks still outstanding"); - } - } - - } - - /** - * Creates a Job on the Mina session, identified by this filters name, in which this filter places asynchronously - * handled events. - * - * @param session The Mina session. - */ - public void createNewJobForSession(IoSession session) - { - Job job = new Job(session, this, MAX_JOB_EVENTS,_readFilter); - session.setAttribute(_name, job); - } - - /** - * Retrieves this filters Job, by this filters name, from the Mina session. - * - * @param session The Mina session. - * - * @return The Job for this filter to place asynchronous events into. - */ - public Job getJobForSession(IoSession session) - { - return (Job) session.getAttribute(_name); - } - - /** - * Implements a terminal continuation for the {@link Job} for this filter. Whenever the Job completes its processing - * of a batch of events this is called. This method simply re-activates the job, if it has more events to process. - * - * @param session The Mina session to work in. - * @param job The job that completed. - */ - public void completed(Job job) - { - - - if (!job.isComplete()) - { - final ExecutorService pool = _poolReference.getPool(); - - if(pool == null) - { - return; - } - - - // ritchiem : 2006-12-13 Do we need to perform the additional checks here? - // Can the pool be shutdown at this point? - if (job.activate()) - { - try - { - pool.execute(job); - } - catch(RejectedExecutionException e) - { - _logger.warn("Thread pool shutdown while tasks still outstanding"); - } - - } - } - } - - public void notCompleted(Job job) - { - final ExecutorService pool = _poolReference.getPool(); - - if(pool == null) - { - return; - } - - try - { - pool.execute(job); - } - catch(RejectedExecutionException e) - { - _logger.warn("Thread pool shutdown while tasks still outstanding"); - } - - } - - - - /** - * No-op pass through filter to the next filter in the chain. - * - * @param nextFilter The next filter in the chain. - * @param session The Mina session. - * - * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow - * overriding sub-classes the ability to. - */ - public void sessionOpened(final NextFilter nextFilter, final IoSession session) throws Exception - { - nextFilter.sessionOpened(session); - } - - /** - * No-op pass through filter to the next filter in the chain. - * - * @param nextFilter The next filter in the chain. - * @param session The Mina session. - * - * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow - * overriding sub-classes the ability to. - */ - public void sessionClosed(final NextFilter nextFilter, final IoSession session) throws Exception - { - nextFilter.sessionClosed(session); - } - - /** - * No-op pass through filter to the next filter in the chain. - * - * @param nextFilter The next filter in the chain. - * @param session The Mina session. - * @param status The session idle status. - * - * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow - * overriding sub-classes the ability to. - */ - public void sessionIdle(final NextFilter nextFilter, final IoSession session, final IdleStatus status) throws Exception - { - nextFilter.sessionIdle(session, status); - } - - /** - * No-op pass through filter to the next filter in the chain. - * - * @param nextFilter The next filter in the chain. - * @param session The Mina session. - * @param cause The underlying exception. - * - * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow - * overriding sub-classes the ability to. - */ - public void exceptionCaught(final NextFilter nextFilter, final IoSession session, final Throwable cause) throws Exception - { - nextFilter.exceptionCaught(session, cause); - } - - /** - * No-op pass through filter to the next filter in the chain. - * - * @param nextFilter The next filter in the chain. - * @param session The Mina session. - * @param message The message received. - * - * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow - * overriding sub-classes the ability to. - */ - public void messageReceived(final NextFilter nextFilter, final IoSession session, final Object message) throws Exception - { - nextFilter.messageReceived(session, message); - } - - /** - * No-op pass through filter to the next filter in the chain. - * - * @param nextFilter The next filter in the chain. - * @param session The Mina session. - * @param message The message sent. - * - * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow - * overriding sub-classes the ability to. - */ - public void messageSent(final NextFilter nextFilter, final IoSession session, final Object message) throws Exception - { - nextFilter.messageSent(session, message); - } - - /** - * No-op pass through filter to the next filter in the chain. - * - * @param nextFilter The next filter in the chain. - * @param session The Mina session. - * @param writeRequest The write request event. - * - * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow - * overriding sub-classes the ability to. - */ - public void filterWrite(final NextFilter nextFilter, final IoSession session, final WriteRequest writeRequest) - throws Exception - { - nextFilter.filterWrite(session, writeRequest); - } - - /** - * No-op pass through filter to the next filter in the chain. - * - * @param nextFilter The next filter in the chain. - * @param session The Mina session. - * - * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow - * overriding sub-classes the ability to. - */ - public void filterClose(NextFilter nextFilter, IoSession session) throws Exception - { - nextFilter.filterClose(session); - } - - /** - * No-op pass through filter to the next filter in the chain. - * - * @param nextFilter The next filter in the chain. - * @param session The Mina session. - * - * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow - * overriding sub-classes the ability to. - */ - public void sessionCreated(NextFilter nextFilter, IoSession session) throws Exception - { - nextFilter.sessionCreated(session); - } - - /** - * Prints the filter types identifying name to a string, mainly for debugging purposes. - * - * @return The filter types identifying name. - */ - public String toString() - { - return _name; - } - - /** - * AsynchReadPoolingFilter is a pooling filter that handles 'messageReceived' and 'sessionClosed' events - * asynchronously. - */ - public static class AsynchReadPoolingFilter extends PoolingFilter - { - /** - * Creates a pooling filter that handles read events asynchronously. - * - * @param refCountingPool A managed reference to the thread pool. - * @param name The filter types identifying name. - */ - public AsynchReadPoolingFilter(ReferenceCountingExecutorService refCountingPool, String name) - { - super(refCountingPool, name, Integer.getInteger("amqj.server.read_write_pool.max_read_events", MAX_JOB_EVENTS),true); - } - - /** - * Hands off this event for asynchronous execution. - * - * @param nextFilter The next filter in the chain. - * @param session The Mina session. - * @param message The message received. - */ - public void messageReceived(NextFilter nextFilter, final IoSession session, Object message) - { - Job job = getJobForSession(session); - fireAsynchEvent(job, new MinaReceivedEvent(nextFilter, message, session)); - } - - /** - * Hands off this event for asynchronous execution. - * - * @param nextFilter The next filter in the chain. - * @param session The Mina session. - */ - public void sessionClosed(final NextFilter nextFilter, final IoSession session) - { - Job job = getJobForSession(session); - fireAsynchEvent(job, new CloseEvent(nextFilter, session)); - } - } - - /** - * AsynchWritePoolingFilter is a pooling filter that handles 'filterWrite' and 'sessionClosed' events - * asynchronously. - */ - public static class AsynchWritePoolingFilter extends PoolingFilter - { - /** - * Creates a pooling filter that handles write events asynchronously. - * - * @param refCountingPool A managed reference to the thread pool. - * @param name The filter types identifying name. - */ - public AsynchWritePoolingFilter(ReferenceCountingExecutorService refCountingPool, String name) - { - super(refCountingPool, name, Integer.getInteger("amqj.server.read_write_pool.max_write_events", MAX_JOB_EVENTS),false); - } - - /** - * Hands off this event for asynchronous execution. - * - * @param nextFilter The next filter in the chain. - * @param session The Mina session. - * @param writeRequest The write request event. - */ - public void filterWrite(final NextFilter nextFilter, final IoSession session, final WriteRequest writeRequest) - { - Job job = getJobForSession(session); - fireAsynchEvent(job, new Event.MinaWriteEvent(nextFilter, writeRequest, session)); - } - - /** - * Hands off this event for asynchronous execution. - * - * @param nextFilter The next filter in the chain. - * @param session The Mina session. - */ - public void sessionClosed(final NextFilter nextFilter, final IoSession session) - { - Job job = getJobForSession(session); - fireAsynchEvent(job, new CloseEvent(nextFilter, session)); - } - } - -} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java b/qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java deleted file mode 100644 index 8cea70e597..0000000000 --- a/qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java +++ /dev/null @@ -1,102 +0,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.pool; - -import org.apache.mina.common.IoFilterChain; -import org.apache.mina.common.ThreadModel; -import org.apache.mina.filter.ReferenceCountingIoFilter; - -/** - * ReadWriteThreadModel is a Mina i/o filter chain factory, which creates a filter chain with seperate filters to - * handle read and write events. The seperate filters are {@link PoolingFilter}s, which have thread pools to handle - * these events. The effect of this is that reading and writing may happen concurrently. - * - *

Socket i/o will only happen with concurrent reads and writes if Mina has seperate selector threads for each. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Create a filter chain with seperate read and write thread pools for read/write Mina events. - * {@link PoolingFilter} - *
- */ -public class ReadWriteThreadModel implements ThreadModel -{ - /** Holds the singleton instance of this factory. */ - private static final ReadWriteThreadModel _instance = new ReadWriteThreadModel(); - - /** Holds the thread pooling filter for reads. */ - private final PoolingFilter _asynchronousReadFilter; - - /** Holds the thread pooloing filter for writes. */ - private final PoolingFilter _asynchronousWriteFilter; - - /** - * Creates a new factory for concurrent i/o, thread pooling filter chain construction. This is private, so that - * only a singleton instance of the factory is ever created. - */ - private ReadWriteThreadModel() - { - final ReferenceCountingExecutorService executor = ReferenceCountingExecutorService.getInstance(); - _asynchronousReadFilter = PoolingFilter.createAynschReadPoolingFilter(executor, "AsynchronousReadFilter"); - _asynchronousWriteFilter = PoolingFilter.createAynschWritePoolingFilter(executor, "AsynchronousWriteFilter"); - } - - /** - * Gets the singleton instance of this filter chain factory. - * - * @return The singleton instance of this filter chain factory. - */ - public static ReadWriteThreadModel getInstance() - { - return _instance; - } - - /** - * Gets the read filter. - * - * @return The read filter. - */ - public PoolingFilter getAsynchronousReadFilter() - { - return _asynchronousReadFilter; - } - - /** - * Gets the write filter. - * - * @return The write filter. - */ - public PoolingFilter getAsynchronousWriteFilter() - { - return _asynchronousWriteFilter; - } - - /** - * Adds the concurrent read and write filters to a filter chain. - * - * @param chain The Mina filter chain to add to. - */ - public void buildFilterChain(IoFilterChain chain) - { - chain.addFirst("AsynchronousReadFilter", new ReferenceCountingIoFilter(_asynchronousReadFilter)); - chain.addLast("AsynchronousWriteFilter", new ReferenceCountingIoFilter(_asynchronousWriteFilter)); - } -} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java index 7cc5f8e442..38ea9307b7 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java @@ -28,8 +28,6 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; -import javax.net.ssl.SSLEngine; - import org.apache.mina.common.ConnectFuture; import org.apache.mina.common.IdleStatus; import org.apache.mina.common.IoAcceptor; @@ -50,7 +48,6 @@ import org.apache.mina.transport.socket.nio.SocketConnectorConfig; import org.apache.mina.transport.socket.nio.SocketSessionConfig; import org.apache.mina.util.NewThreadExecutor; import org.apache.mina.util.SessionUtil; -import org.apache.qpid.pool.ReadWriteThreadModel; import org.apache.qpid.protocol.ProtocolEngine; import org.apache.qpid.protocol.ProtocolEngineFactory; import org.apache.qpid.ssl.SSLContextFactory; @@ -58,7 +55,6 @@ import org.apache.qpid.thread.QpidThreadExecutor; import org.apache.qpid.transport.NetworkDriver; import org.apache.qpid.transport.NetworkDriverConfiguration; import org.apache.qpid.transport.OpenException; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -148,14 +144,6 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver sc.setTcpNoDelay(config.getTcpNoDelay()); } - // if we do not use the executor pool threading model we get the default - // leader follower - // implementation provided by MINA - if (_executorPool) - { - sconfig.setThreadModel(ReadWriteThreadModel.getInstance()); - } - if (sslFactory != null) { _sslFactory = sslFactory; @@ -227,14 +215,6 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver } SocketConnectorConfig cfg = (SocketConnectorConfig) _socketConnector.getDefaultConfig(); - - // if we do not use our own thread model we get the MINA default which is to use - // its own leader-follower model - boolean readWriteThreading = Boolean.getBoolean("amqj.shared_read_write_pool"); - if (readWriteThreading) - { - cfg.setThreadModel(ReadWriteThreadModel.getInstance()); - } SocketSessionConfig scfg = (SocketSessionConfig) cfg.getSessionConfig(); scfg.setTcpNoDelay((config != null) ? config.getTcpNoDelay() : true); @@ -258,8 +238,6 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver throw new OpenException("Could not open connection", _lastException); } _ioSession = future.getSession(); - ReadWriteThreadModel.getInstance().getAsynchronousReadFilter().createNewJobForSession(_ioSession); - ReadWriteThreadModel.getInstance().getAsynchronousWriteFilter().createNewJobForSession(_ioSession); _ioSession.setAttachment(engine); engine.setNetworkDriver(this); _protocolEngine = engine; diff --git a/qpid/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java b/qpid/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java deleted file mode 100644 index 6383d52298..0000000000 --- a/qpid/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java +++ /dev/null @@ -1,111 +0,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.pool; - -import junit.framework.TestCase; -import junit.framework.Assert; -import org.apache.qpid.session.TestSession; -import org.apache.mina.common.IoFilter; -import org.apache.mina.common.IoSession; -import org.apache.mina.common.IdleStatus; - -import java.util.concurrent.RejectedExecutionException; - -public class PoolingFilterTest extends TestCase -{ - private PoolingFilter _pool; - ReferenceCountingExecutorService _executorService; - - public void setUp() - { - - //Create Pool - _executorService = ReferenceCountingExecutorService.getInstance(); - _executorService.acquireExecutorService(); - _pool = PoolingFilter.createAynschWritePoolingFilter(_executorService, - "AsynchronousWriteFilter"); - - } - - public void testRejectedExecution() throws Exception - { - - TestSession testSession = new TestSession(); - _pool.createNewJobForSession(testSession); - _pool.filterWrite(new NoOpFilter(), testSession, new IoFilter.WriteRequest("Message")); - - //Shutdown the pool - _executorService.getPool().shutdownNow(); - - try - { - - testSession = new TestSession(); - _pool.createNewJobForSession(testSession); - //prior to fix for QPID-172 this would throw RejectedExecutionException - _pool.filterWrite(null, testSession, null); - } - catch (RejectedExecutionException rje) - { - Assert.fail("RejectedExecutionException should not occur after pool has shutdown:" + rje); - } - } - - private static class NoOpFilter implements IoFilter.NextFilter - { - - public void sessionOpened(IoSession session) - { - } - - public void sessionClosed(IoSession session) - { - } - - public void sessionIdle(IoSession session, IdleStatus status) - { - } - - public void exceptionCaught(IoSession session, Throwable cause) - { - } - - public void messageReceived(IoSession session, Object message) - { - } - - public void messageSent(IoSession session, Object message) - { - } - - public void filterWrite(IoSession session, IoFilter.WriteRequest writeRequest) - { - } - - public void filterClose(IoSession session) - { - } - - public void sessionCreated(IoSession session) - { - } - } -} -- cgit v1.2.1 From 7d6a028be9f6c47418e98a6fa74a359864428150 Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Thu, 17 Sep 2009 16:21:13 +0000 Subject: Merge from trunk git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-network-refactor@816261 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/cpp/bindings/qmf/python/Makefile.am | 2 +- qpid/cpp/bindings/qmf/python/qmf.py | 14 +- qpid/cpp/bindings/qmf/ruby/qmf.rb | 295 +++++++++--- qpid/cpp/bindings/qmf/tests/agent_ruby.rb | 7 +- qpid/cpp/bindings/qmf/tests/python_agent.py | 4 +- qpid/cpp/bindings/qmf/tests/ruby_console.rb | 83 +++- qpid/cpp/include/qpid/messaging/Receiver.h | 33 +- qpid/cpp/include/qpid/messaging/Sender.h | 19 + qpid/cpp/include/qpid/messaging/Session.h | 14 +- qpid/cpp/src/CMakeLists.txt | 4 +- qpid/cpp/src/Makefile.am | 4 +- qpid/cpp/src/qmf/AgentEngine.cpp | 19 +- qpid/cpp/src/qmf/ConsoleEngine.cpp | 379 +++++++++++---- qpid/cpp/src/qmf/ConsoleEngine.h | 70 ++- qpid/cpp/src/qmf/Object.h | 3 +- qpid/cpp/src/qmf/ObjectId.h | 1 + qpid/cpp/src/qmf/ObjectIdImpl.cpp | 21 +- qpid/cpp/src/qmf/ObjectIdImpl.h | 3 +- qpid/cpp/src/qmf/ObjectImpl.cpp | 88 ++-- qpid/cpp/src/qmf/ObjectImpl.h | 13 +- qpid/cpp/src/qmf/Query.h | 70 ++- qpid/cpp/src/qmf/QueryImpl.cpp | 85 ++-- qpid/cpp/src/qmf/QueryImpl.h | 78 ++- qpid/cpp/src/qmf/SchemaImpl.cpp | 13 +- qpid/cpp/src/qmf/SchemaImpl.h | 7 + qpid/cpp/src/qmf/SequenceManager.cpp | 52 +- qpid/cpp/src/qmf/SequenceManager.h | 20 +- qpid/cpp/src/qpid/acl/AclData.cpp | 91 ++-- qpid/cpp/src/qpid/acl/AclData.h | 12 +- qpid/cpp/src/qpid/acl/AclReader.cpp | 227 +++++---- qpid/cpp/src/qpid/broker/AclModule.h | 17 +- qpid/cpp/src/qpid/broker/Exchange.cpp | 26 +- qpid/cpp/src/qpid/broker/Message.cpp | 2 +- qpid/cpp/src/qpid/broker/Message.h | 10 +- qpid/cpp/src/qpid/broker/MessageBuilder.cpp | 28 +- qpid/cpp/src/qpid/broker/PersistableMessage.cpp | 2 +- qpid/cpp/src/qpid/broker/PersistableMessage.h | 4 +- qpid/cpp/src/qpid/broker/Queue.cpp | 4 +- qpid/cpp/src/qpid/broker/QueueRegistry.h | 2 +- qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp | 2 +- qpid/cpp/src/qpid/broker/SessionAdapter.cpp | 18 +- qpid/cpp/src/qpid/client/Dispatcher.cpp | 11 +- .../cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp | 111 +++++ qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.h | 85 ++++ .../src/qpid/client/amqp0_10/CompletionTracker.cpp | 48 -- .../src/qpid/client/amqp0_10/CompletionTracker.h | 50 -- .../src/qpid/client/amqp0_10/IncomingMessages.cpp | 53 ++- .../src/qpid/client/amqp0_10/IncomingMessages.h | 9 +- qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp | 15 + qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h | 3 + qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp | 55 ++- qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h | 34 +- qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp | 55 +++ qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h | 17 + qpid/cpp/src/qpid/cluster/Connection.cpp | 10 +- qpid/cpp/src/qpid/cluster/Quorum_cman.cpp | 8 +- qpid/cpp/src/qpid/messaging/Address.cpp | 3 - qpid/cpp/src/qpid/messaging/Receiver.cpp | 3 + qpid/cpp/src/qpid/messaging/ReceiverImpl.h | 3 + qpid/cpp/src/qpid/messaging/Sender.cpp | 3 + qpid/cpp/src/qpid/messaging/SenderImpl.h | 3 + qpid/cpp/src/qpid/messaging/Session.cpp | 12 +- qpid/cpp/src/qpid/messaging/SessionImpl.h | 4 +- qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp | 11 +- qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp | 11 +- qpid/cpp/src/qpid/xml/XmlExchange.cpp | 46 +- qpid/cpp/src/tests/AccumulatedAckTest.cpp | 13 +- qpid/cpp/src/tests/Array.cpp | 11 +- qpid/cpp/src/tests/AsyncCompletion.cpp | 11 +- qpid/cpp/src/tests/AtomicValue.cpp | 5 + qpid/cpp/src/tests/BrokerFixture.h | 10 +- qpid/cpp/src/tests/ClientMessageTest.cpp | 7 +- qpid/cpp/src/tests/ClientSessionTest.cpp | 67 +-- qpid/cpp/src/tests/ClusterFailover.cpp | 5 + qpid/cpp/src/tests/ClusterFixture.cpp | 5 + qpid/cpp/src/tests/ClusterFixture.h | 5 + qpid/cpp/src/tests/ConnectionOptions.h | 14 +- qpid/cpp/src/tests/ConsoleTest.cpp | 9 +- qpid/cpp/src/tests/DeliveryRecordTest.cpp | 8 +- qpid/cpp/src/tests/DispatcherTest.cpp | 47 +- qpid/cpp/src/tests/DtxWorkRecordTest.cpp | 18 +- qpid/cpp/src/tests/ExchangeTest.cpp | 41 +- qpid/cpp/src/tests/FieldTable.cpp | 19 +- qpid/cpp/src/tests/FieldValue.cpp | 13 +- qpid/cpp/src/tests/ForkedBroker.cpp | 20 +- qpid/cpp/src/tests/ForkedBroker.h | 17 +- qpid/cpp/src/tests/Frame.cpp | 9 +- qpid/cpp/src/tests/FrameDecoder.cpp | 11 +- qpid/cpp/src/tests/FramingTest.cpp | 25 +- qpid/cpp/src/tests/HeaderTest.cpp | 18 +- qpid/cpp/src/tests/HeadersExchangeTest.cpp | 31 +- qpid/cpp/src/tests/IncompleteMessageList.cpp | 17 +- qpid/cpp/src/tests/InlineAllocator.cpp | 13 +- qpid/cpp/src/tests/InlineVector.cpp | 11 +- qpid/cpp/src/tests/ManagementTest.cpp | 9 +- qpid/cpp/src/tests/MessageBuilderTest.cpp | 46 +- qpid/cpp/src/tests/MessageReplayTracker.cpp | 21 +- qpid/cpp/src/tests/MessageTest.cpp | 12 +- qpid/cpp/src/tests/MessageUtils.h | 13 +- qpid/cpp/src/tests/MessagingSessionTests.cpp | 119 ++++- qpid/cpp/src/tests/PartialFailure.cpp | 5 + qpid/cpp/src/tests/PollableCondition.cpp | 18 +- qpid/cpp/src/tests/ProxyTest.cpp | 11 +- qpid/cpp/src/tests/QueueEvents.cpp | 17 +- qpid/cpp/src/tests/QueueOptionsTest.cpp | 27 +- qpid/cpp/src/tests/QueuePolicyTest.cpp | 45 +- qpid/cpp/src/tests/QueueRegistryTest.cpp | 17 +- qpid/cpp/src/tests/QueueTest.cpp | 165 ++++--- qpid/cpp/src/tests/RangeSet.cpp | 9 +- qpid/cpp/src/tests/RateFlowcontrolTest.cpp | 19 +- qpid/cpp/src/tests/RefCounted.cpp | 5 + qpid/cpp/src/tests/ReplicationTest.cpp | 13 +- qpid/cpp/src/tests/RetryList.cpp | 15 +- qpid/cpp/src/tests/SequenceNumberTest.cpp | 12 +- qpid/cpp/src/tests/SequenceSet.cpp | 9 +- qpid/cpp/src/tests/SessionState.cpp | 19 +- qpid/cpp/src/tests/Shlib.cpp | 9 +- qpid/cpp/src/tests/SocketProxy.h | 13 +- qpid/cpp/src/tests/TestMessageStore.h | 13 +- qpid/cpp/src/tests/TestOptions.h | 6 +- qpid/cpp/src/tests/TimerTest.cpp | 19 +- qpid/cpp/src/tests/TopicExchangeTest.cpp | 22 +- qpid/cpp/src/tests/TxBufferTest.cpp | 9 +- qpid/cpp/src/tests/TxMocks.h | 49 +- qpid/cpp/src/tests/TxPublishTest.cpp | 23 +- qpid/cpp/src/tests/Url.cpp | 5 + qpid/cpp/src/tests/Uuid.cpp | 5 + qpid/cpp/src/tests/Variant.cpp | 21 +- qpid/cpp/src/tests/XmlClientSessionTest.cpp | 18 +- qpid/cpp/src/tests/acl.py | 400 ++++++++++++++-- qpid/cpp/src/tests/client_test.cpp | 21 +- qpid/cpp/src/tests/cluster_test.cpp | 9 +- qpid/cpp/src/tests/consume.cpp | 21 +- qpid/cpp/src/tests/datagen.cpp | 19 +- qpid/cpp/src/tests/echotest.cpp | 21 +- qpid/cpp/src/tests/exception_test.cpp | 17 +- qpid/cpp/src/tests/failover_soak.cpp | 124 ++--- qpid/cpp/src/tests/latencytest.cpp | 55 ++- qpid/cpp/src/tests/logging.cpp | 31 +- qpid/cpp/src/tests/perftest.cpp | 77 +-- qpid/cpp/src/tests/publish.cpp | 21 +- qpid/cpp/src/tests/qpid_ping.cpp | 12 +- qpid/cpp/src/tests/qrsh.cpp | 37 +- qpid/cpp/src/tests/qrsh_server.cpp | 127 ++--- qpid/cpp/src/tests/receiver.cpp | 26 +- qpid/cpp/src/tests/replaying_sender.cpp | 39 +- qpid/cpp/src/tests/resuming_receiver.cpp | 45 +- qpid/cpp/src/tests/sender.cpp | 34 +- qpid/cpp/src/tests/shlibtest.cpp | 6 +- qpid/cpp/src/tests/test_store.cpp | 15 +- qpid/cpp/src/tests/test_tools.h | 6 +- qpid/cpp/src/tests/topic_listener.cpp | 105 ++-- qpid/cpp/src/tests/topic_publisher.cpp | 135 +++--- qpid/cpp/src/tests/txjob.cpp | 17 +- qpid/cpp/src/tests/txshift.cpp | 27 +- qpid/cpp/src/tests/txtest.cpp | 53 ++- qpid/java/broker/bin/qpid-passwd | 70 +-- qpid/java/broker/etc/acl.config.xml | 230 --------- qpid/java/broker/etc/config-systests-derby.xml | 141 ------ qpid/java/broker/etc/config-systests.xml | 143 ------ qpid/java/broker/etc/config.xml | 132 +++++- .../broker/etc/persistent_config-config-test.xml | 93 ---- qpid/java/broker/etc/sample-parsed-config.xml | 72 --- qpid/java/broker/etc/virtualhosts-config-test.xml | 40 -- qpid/java/broker/etc/virtualhosts-systests.xml | 124 ----- qpid/java/broker/etc/virtualhosts.xml | 123 ----- .../apache/qpid/server/AMQBrokerManagerMBean.java | 6 +- .../server/configuration/ServerConfiguration.java | 35 +- .../qpid/server/virtualhost/VirtualHost.java | 12 + .../configuration/ServerConfigurationTest.java | 55 ++- ...config-systests-ServerConfigurationTest-New.xml | 93 ++++ ...config-systests-ServerConfigurationTest-Old.xml | 72 +++ .../systests/etc/config-systests-acl-settings.xml | 139 ++++++ qpid/java/systests/etc/config-systests-acl.xml | 30 ++ .../etc/config-systests-derby-settings.xml | 64 +++ qpid/java/systests/etc/config-systests-derby.xml | 30 ++ .../java/systests/etc/config-systests-settings.xml | 29 ++ qpid/java/systests/etc/config-systests.xml | 29 ++ .../virtualhosts-ServerConfigurationTest-New.xml | 40 ++ .../management/jmx/ManagementActorLoggingTest.java | 527 +++++++-------------- .../configuration/ServerConfigurationFileTest.java | 2 + .../apache/qpid/server/logging/AlertingTest.java | 14 +- .../logging/DerbyMessageStoreLoggingTest.java | 43 +- .../qpid/server/logging/ManagementLoggingTest.java | 6 +- .../logging/MemoryMessageStoreLoggingTest.java | 12 +- .../server/logging/VirtualHostLoggingTest.java | 8 +- .../org/apache/qpid/server/queue/ModelTest.java | 299 ++++++++++++ .../qpid/server/security/acl/SimpleACLTest.java | 134 +++--- .../test/client/timeouts/SyncWaitDelayTest.java | 21 +- .../apache/qpid/test/utils/FailoverBaseCase.java | 4 +- .../org/apache/qpid/test/utils/JMXTestUtils.java | 208 ++++++++ .../org/apache/qpid/test/utils/QpidTestCase.java | 139 +++--- qpid/java/test-profiles/010Excludes | 3 + qpid/java/test-profiles/08Excludes | 5 + qpid/java/test-profiles/Excludes | 6 +- qpid/java/test-profiles/cpp.testprofile | 1 + qpid/java/test-profiles/default.testprofile | 5 +- qpid/python/qpid/datatypes.py | 18 + qpid/python/qpid/delegates.py | 8 +- qpid/python/qpid/ops.py | 5 +- qpid/python/tests/datatypes.py | 28 ++ qpid/specs/management-schema.xml | 2 +- 202 files changed, 5340 insertions(+), 3287 deletions(-) create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp create mode 100644 qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.h delete mode 100644 qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.cpp delete mode 100644 qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.h delete mode 100644 qpid/java/broker/etc/acl.config.xml delete mode 100644 qpid/java/broker/etc/config-systests-derby.xml delete mode 100644 qpid/java/broker/etc/config-systests.xml delete mode 100644 qpid/java/broker/etc/persistent_config-config-test.xml delete mode 100644 qpid/java/broker/etc/sample-parsed-config.xml delete mode 100644 qpid/java/broker/etc/virtualhosts-config-test.xml delete mode 100644 qpid/java/broker/etc/virtualhosts-systests.xml delete mode 100644 qpid/java/broker/etc/virtualhosts.xml create mode 100644 qpid/java/systests/etc/config-systests-ServerConfigurationTest-New.xml create mode 100644 qpid/java/systests/etc/config-systests-ServerConfigurationTest-Old.xml create mode 100644 qpid/java/systests/etc/config-systests-acl-settings.xml create mode 100644 qpid/java/systests/etc/config-systests-acl.xml create mode 100644 qpid/java/systests/etc/config-systests-derby-settings.xml create mode 100644 qpid/java/systests/etc/config-systests-derby.xml create mode 100644 qpid/java/systests/etc/config-systests-settings.xml create mode 100644 qpid/java/systests/etc/config-systests.xml create mode 100644 qpid/java/systests/etc/virtualhosts-ServerConfigurationTest-New.xml create mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ModelTest.java create mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java (limited to 'qpid') diff --git a/qpid/cpp/bindings/qmf/python/Makefile.am b/qpid/cpp/bindings/qmf/python/Makefile.am index f51d26bfad..55d9079fb7 100644 --- a/qpid/cpp/bindings/qmf/python/Makefile.am +++ b/qpid/cpp/bindings/qmf/python/Makefile.am @@ -29,7 +29,7 @@ EXTRA_DIST = python.i BUILT_SOURCES = $(generated_file_list) $(generated_file_list): $(srcdir)/python.i $(srcdir)/../qmfengine.i - swig -python -c++ -Wall -I/usr/include $(INCLUDES) $(QPID_CXXFLAGS) -I$(top_srcdir)/src/qmf -o qmfengine.cpp $(srcdir)/python.i + swig -c++ -python -Wall -I/usr/include $(INCLUDES) $(QPID_CXXFLAGS) -I$(top_srcdir)/src/qmf -o qmfengine.cpp $(srcdir)/python.i pylibdir = $(PYTHON_LIB) diff --git a/qpid/cpp/bindings/qmf/python/qmf.py b/qpid/cpp/bindings/qmf/python/qmf.py index 265f204852..4800b327f1 100644 --- a/qpid/cpp/bindings/qmf/python/qmf.py +++ b/qpid/cpp/bindings/qmf/python/qmf.py @@ -30,6 +30,8 @@ from qmfengine import (TYPE_ABSTIME, TYPE_ARRAY, TYPE_BOOL, TYPE_DELTATIME, TYPE_INT8, TYPE_LIST, TYPE_LSTR, TYPE_MAP, TYPE_OBJECT, TYPE_REF, TYPE_SSTR, TYPE_UINT16, TYPE_UINT32, TYPE_UINT64, TYPE_UINT8, TYPE_UUID) +from qmfengine import (O_EQ, O_NE, O_LT, O_LE, O_GT, O_GE, O_RE_MATCH, O_RE_NOMATCH, + E_NOT, E_AND, E_OR, E_XOR) ##============================================================================== @@ -404,11 +406,16 @@ class Arguments: class Query: - def __init__(self, i=None): + def __init__(self, i=None, package="", cls=None, oid=None): if i: self.impl = i else: - self.impl = qmfengine.Query() + if cls: + self.impl = qmfengine.Query(cls, package) + elif oid: + self.impl = qmfengine.Query(oid) + else: + raise "Argument error" def package_name(self): return self.impl.getPackage() @@ -419,9 +426,6 @@ class Query: return ObjectId(_objid) else: return None - OPER_AND = qmfengine.Query.OPER_AND - OPER_OR = qmfengine.Query.OPER_OR - ##============================================================================== diff --git a/qpid/cpp/bindings/qmf/ruby/qmf.rb b/qpid/cpp/bindings/qmf/ruby/qmf.rb index 21fbf6c157..16f1058f4a 100644 --- a/qpid/cpp/bindings/qmf/ruby/qmf.rb +++ b/qpid/cpp/bindings/qmf/ruby/qmf.rb @@ -67,6 +67,7 @@ module Qmf class ConnectionHandler def conn_event_connected(); end def conn_event_disconnected(error); end + def conn_event_visit(); end def sess_event_session_closed(context, error); end def sess_event_recv(context, message); end end @@ -82,6 +83,7 @@ module Qmf @sockEngine, @sock = Socket::socketpair(Socket::PF_UNIX, Socket::SOCK_STREAM, 0) @impl.setNotifyFd(@sockEngine.fileno) @new_conn_handlers = [] + @conn_handlers_to_delete = [] @conn_handlers = [] @thread = Thread.new do @@ -89,17 +91,30 @@ module Qmf end end + def kick + @sockEngine.write(".") + @sockEngine.flush + end + def add_conn_handler(handler) synchronize do @new_conn_handlers << handler end - @sockEngine.write("x") + kick + end + + def del_conn_handler(handler) + synchronize do + @conn_handlers_to_delete << handler + end + kick end def run() eventImpl = Qmfengine::ResilientConnectionEvent.new connected = nil new_handlers = nil + del_handlers = nil bt_count = 0 while :true @@ -107,7 +122,9 @@ module Qmf synchronize do new_handlers = @new_conn_handlers + del_handlers = @conn_handlers_to_delete @new_conn_handlers = [] + @conn_handlers_to_delete = [] end new_handlers.each do |nh| @@ -116,6 +133,11 @@ module Qmf end new_handlers = nil + del_handlers.each do |dh| + d = @conn_handlers.delete(dh) + end + del_handlers = nil + valid = @impl.getEvent(eventImpl) while valid begin @@ -141,6 +163,7 @@ module Qmf @impl.popEvent valid = @impl.getEvent(eventImpl) end + @conn_handlers.each { |h| h.conn_event_visit } end end end @@ -167,23 +190,20 @@ module Qmf class QmfObject attr_reader :impl, :object_class - def initialize(cls) - @object_class = cls - @impl = Qmfengine::Object.new(@object_class.impl) - end - - def destroy - @impl.destroy + def initialize(cls, kwargs={}) + if cls: + @object_class = cls + @impl = Qmfengine::Object.new(@object_class.impl) + elsif kwargs.include?(:impl) + @impl = Qmfengine::Object.new(kwargs[:impl]) + @object_class = SchemaObjectClass.new(nil, nil, :impl => @impl.getClass) + end end def object_id return ObjectId.new(@impl.getObjectId) end - def set_object_id(oid) - @impl.setObjectId(oid.impl) - end - def get_attr(name) val = value(name) case val.getType @@ -248,17 +268,31 @@ module Qmf def value(name) val = @impl.getValue(name.to_s) if val.nil? - raise ArgumentError, "Attribute '#{name}' not defined for class #{@object_class.impl.getName}" + raise ArgumentError, "Attribute '#{name}' not defined for class #{@object_class.impl.getClassKey.getPackageName}:#{@object_class.impl.getClassKey.getClassName}" end return val end end + class AgentObject < QmfObject + def initialize(cls, kwargs={}) + super(cls, kwargs) + end + + def destroy + @impl.destroy + end + + def set_object_id(oid) + @impl.setObjectId(oid.impl) + end + end + class ConsoleObject < QmfObject attr_reader :current_time, :create_time, :delete_time - def initialize(cls) - super(cls) + def initialize(cls, kwargs={}) + super(cls, kwargs) end def update() @@ -373,10 +407,30 @@ module Qmf end end + ##============================================================================== + ## QUERY + ##============================================================================== + class Query attr_reader :impl - def initialize(i) - @impl = i + def initialize(kwargs = {}) + if kwargs.include?(:impl) + @impl = kwargs[:impl] + else + package = '' + if kwargs.include?(:key) + @impl = Qmfengine::Query.new(kwargs[:key]) + elsif kwargs.include?(:object_id) + @impl = Qmfengine::Query.new(kwargs[:object_id]) + else + package = kwargs[:package] if kwargs.include?(:package) + if kwargs.include?(:class) + @impl = Qmfengine::Query.new(kwargs[:class], package) + else + raise ArgumentError, "Invalid arguments, use :key or :class[,:package]" + end + end + end end def package_name @@ -403,36 +457,60 @@ module Qmf class SchemaArgument attr_reader :impl def initialize(name, typecode, kwargs={}) - @impl = Qmfengine::SchemaArgument.new(name, typecode) - @impl.setDirection(kwargs[:dir]) if kwargs.include?(:dir) - @impl.setUnit(kwargs[:unit]) if kwargs.include?(:unit) - @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) + if kwargs.include?(:impl) + @impl = kwargs[:impl] + else + @impl = Qmfengine::SchemaArgument.new(name, typecode) + @impl.setDirection(kwargs[:dir]) if kwargs.include?(:dir) + @impl.setUnit(kwargs[:unit]) if kwargs.include?(:unit) + @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) + end + end + + def name + @impl.getName end end class SchemaMethod - attr_reader :impl + attr_reader :impl, :arguments def initialize(name, kwargs={}) - @impl = Qmfengine::SchemaMethod.new(name) - @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) @arguments = [] + if kwargs.include?(:impl) + @impl = kwargs[:impl] + arg_count = @impl.getArgumentCount + for i in 0...arg_count + @arguments << SchemaArgument.new(nil, nil, :impl => @impl.getArgument(i)) + end + else + @impl = Qmfengine::SchemaMethod.new(name) + @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) + end end def add_argument(arg) @arguments << arg @impl.addArgument(arg.impl) end + + def name + @impl.getName + end end class SchemaProperty attr_reader :impl def initialize(name, typecode, kwargs={}) - @impl = Qmfengine::SchemaProperty.new(name, typecode) - @impl.setAccess(kwargs[:access]) if kwargs.include?(:access) - @impl.setIndex(kwargs[:index]) if kwargs.include?(:index) - @impl.setOptional(kwargs[:optional]) if kwargs.include?(:optional) - @impl.setUnit(kwargs[:unit]) if kwargs.include?(:unit) - @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) + if kwargs.include?(:impl) + @impl = kwargs[:impl] + else + @impl = Qmfengine::SchemaProperty.new(name, typecode) + @impl.setAccess(kwargs[:access]) if kwargs.include?(:access) + @impl.setIndex(kwargs[:index]) if kwargs.include?(:index) + @impl.setOptional(kwargs[:optional]) if kwargs.include?(:optional) + @impl.setUnit(kwargs[:unit]) if kwargs.include?(:unit) + @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) + end end def name @@ -443,9 +521,17 @@ module Qmf class SchemaStatistic attr_reader :impl def initialize(name, typecode, kwargs={}) - @impl = Qmfengine::SchemaStatistic.new(name, typecode) - @impl.setUnit(kwargs[:unit]) if kwargs.include?(:unit) - @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) + if kwargs.include?(:impl) + @impl = kwargs[:impl] + else + @impl = Qmfengine::SchemaStatistic.new(name, typecode) + @impl.setUnit(kwargs[:unit]) if kwargs.include?(:unit) + @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) + end + end + + def name + @impl.getName end end @@ -465,13 +551,25 @@ module Qmf end class SchemaObjectClass - attr_reader :impl - def initialize(package='', name='', kwargs={}) + attr_reader :impl, :properties, :statistics, :methods + def initialize(package, name, kwargs={}) @properties = [] @statistics = [] @methods = [] if kwargs.include?(:impl) @impl = kwargs[:impl] + + @impl.getPropertyCount.times do |i| + @properties << SchemaProperty.new(nil, nil, :impl => @impl.getProperty(i)) + end + + @impl.getStatisticCount.times do |i| + @statistics << SchemaStatistic.new(nil, nil, :impl => @impl.getStatistic(i)) + end + + @impl.getMethodCount.times do |i| + @methods << SchemaMethod.new(nil, :impl => @impl.getMethod(i)) + end else @impl = Qmfengine::SchemaObjectClass.new(package, name) end @@ -495,24 +593,17 @@ module Qmf def name @impl.getClassKey.getClassName end - - def properties - unless @properties - @properties = [] - @impl.getPropertyCount.times do |i| - @properties << @impl.getProperty(i) - end - end - return @properties - end end class SchemaEventClass - attr_reader :impl - def initialize(package='', name='', kwargs={}) + attr_reader :impl, :arguments + def initialize(package, name, kwargs={}) @arguments = [] if kwargs.include?(:impl) @impl = kwargs[:impl] + @impl.getArgumentCount.times do |i| + @arguments << SchemaArgument.new(nil, nil, :impl => @impl.getArgument(i)) + end else @impl = Qmfengine::SchemaEventClass.new(package, name) @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) @@ -546,13 +637,18 @@ module Qmf end class Console + include MonitorMixin attr_reader :impl def initialize(handler = nil, kwargs={}) + super() @handler = handler @impl = Qmfengine::ConsoleEngine.new @event = Qmfengine::ConsoleEvent.new @broker_list = [] + @cv = new_cond + @sync_count = nil + @sync_result = nil end def add_connection(conn) @@ -562,6 +658,8 @@ module Qmf end def del_connection(broker) + broker.shutdown + @broker_list.delete(broker) end def get_packages() @@ -581,9 +679,9 @@ module Qmf class_kind = @impl.getClassKind(key) if class_kind == kind if kind == CLASS_OBJECT - clist << SchemaObjectClass.new('', '', :impl => @impl.getObjectClass(key)) + clist << SchemaObjectClass.new(nil, nil, :impl => @impl.getObjectClass(key)) elsif kind == CLASS_EVENT - clist << SchemaEventClass.new('', '', :impl => @impl.getEventClass(key)) + clist << SchemaEventClass.new(nil, nil, :impl => @impl.getEventClass(key)) end end end @@ -591,19 +689,70 @@ module Qmf return clist end - def get_schema(class_key) - end - def bind_package(package) + @impl.bindPackage(package) end def bind_class(kwargs = {}) + if kwargs.include?(:key) + @impl.bindClass(kwargs[:key]) + elsif kwargs.include?(:package) + package = kwargs[:package] + if kwargs.include?(:class) + @impl.bindClass(package, kwargs[:class]) + else + @impl.bindClass(package) + end + else + raise ArgumentError, "Invalid arguments, use :key or :package[,:class]" + end end def get_agents(broker = nil) + blist = [] + if broker + blist << broker + else + blist = @broker_list + end + + agents = [] + blist.each do |b| + count = b.impl.agentCount + for idx in 0...count + agents << AgentProxy.new(b.impl.getAgent(idx), b) + end + end + + return agents end def get_objects(query, kwargs = {}) + timeout = 30 + if kwargs.include?(:timeout) + timeout = kwargs[:timeout] + end + synchronize do + @sync_count = 1 + @sync_result = [] + broker = @broker_list[0] + broker.send_query(query.impl, nil) + unless @cv.wait(timeout) { @sync_count == 0 } + raise "Timed out waiting for response" + end + + return @sync_result + end + end + + def _get_result(list, context) + synchronize do + list.each do |item| + @sync_result << item + end + @sync_count -= 1 + @cv.signal + end end def start_sync(query) @@ -638,6 +787,19 @@ module Qmf end end + class AgentProxy + attr_reader :broker + + def initialize(impl, broker) + @impl = impl + @broker = broker + end + + def label + @impl.getLabel + end + end + class Broker < ConnectionHandler include MonitorMixin attr_reader :impl @@ -654,6 +816,13 @@ module Qmf @impl = Qmfengine::BrokerProxy.new(@console.impl) @console.impl.addConnection(@impl, self) @conn.add_conn_handler(self) + @operational = :true + end + + def shutdown() + @console.impl.delConnection(@impl) + @conn.del_conn_handler(self) + @operational = :false end def waitForStable(timeout = nil) @@ -671,6 +840,11 @@ module Qmf end end + def send_query(query, ctx) + @impl.sendQuery(query, ctx) + @conn.kick + end + def do_broker_events() count = 0 valid = @impl.getEvent(@event) @@ -694,6 +868,12 @@ module Qmf @stable = :true @cv.signal end + when Qmfengine::BrokerEvent::QUERY_COMPLETE + result = [] + for idx in 0...@event.queryResponse.getObjectCount + result << ConsoleObject.new(nil, :impl => @event.queryResponse.getObject(idx)) + end + @console._get_result(result, @event.context) end @impl.popEvent valid = @impl.getEvent(@event) @@ -732,12 +912,17 @@ module Qmf puts "Console Connection Lost" end + def conn_event_visit + do_events + end + def sess_event_session_closed(context, error) puts "Console Session Lost" @impl.sessionClosed() end def sess_event_recv(context, message) + puts "Unexpected RECV Event" if not @operational @impl.handleRcvMessage(message) do_events end @@ -798,7 +983,7 @@ module Qmf count += 1 case @event.kind when Qmfengine::AgentEvent::GET_QUERY - @handler.get_query(@event.sequence, Query.new(@event.query), @event.authUserId) + @handler.get_query(@event.sequence, Query.new(:impl => @event.query), @event.authUserId) when Qmfengine::AgentEvent::START_SYNC when Qmfengine::AgentEvent::END_SYNC when Qmfengine::AgentEvent::METHOD_CALL @@ -852,6 +1037,10 @@ module Qmf puts "Agent Connection Lost" end + def conn_event_visit + do_events + end + def sess_event_session_closed(context, error) puts "Agent Session Lost" end diff --git a/qpid/cpp/bindings/qmf/tests/agent_ruby.rb b/qpid/cpp/bindings/qmf/tests/agent_ruby.rb index 75de2b5fa1..67591319ee 100755 --- a/qpid/cpp/bindings/qmf/tests/agent_ruby.rb +++ b/qpid/cpp/bindings/qmf/tests/agent_ruby.rb @@ -72,8 +72,7 @@ end class App < Qmf::AgentHandler def get_query(context, query, userId) -# puts "Query: user=#{userId} context=#{context} class=#{query.class_name} object_num=#{query.object_id.object_num_low if query.object_id}" - #@parent.inc_attr("queryCount") +# puts "Query: user=#{userId} context=#{context} class=#{query.class_name} object_num=#{query.object_id.object_num_low if query.object_id}" if query.class_name == 'parent' @agent.query_response(context, @parent) elsif query.object_id == @parent_oid @@ -135,7 +134,7 @@ class App < Qmf::AgentHandler elsif name == "create_child" oid = @agent.alloc_object_id(2) args['child_ref'] = oid - @child = Qmf::QmfObject.new(@model.child_class) + @child = Qmf::AgentObject.new(@model.child_class) @child.set_attr("name", args.by_key("child_name")) @child.set_object_id(oid) @agent.method_response(context, 0, "OK", args) @@ -161,7 +160,7 @@ class App < Qmf::AgentHandler @agent.set_connection(@connection) - @parent = Qmf::QmfObject.new(@model.parent_class) + @parent = Qmf::AgentObject.new(@model.parent_class) @parent.set_attr("name", "Parent One") @parent.set_attr("state", "OPERATIONAL") diff --git a/qpid/cpp/bindings/qmf/tests/python_agent.py b/qpid/cpp/bindings/qmf/tests/python_agent.py index f6cb51cbf5..d4373d3bb8 100644 --- a/qpid/cpp/bindings/qmf/tests/python_agent.py +++ b/qpid/cpp/bindings/qmf/tests/python_agent.py @@ -72,7 +72,9 @@ class Model: class App(qmf.AgentHandler): def get_query(self, context, query, userId): - # puts "Query: user=#{userId} context=#{context} class=#{query.class_name} object_num=#{query.object_id.object_num_low if query.object_id}" + #print "Query: user=%s context=%d class=%s" % (userId, context, query.class_name()) + #if query.object_id(): + # print query.object_id().object_num_low() self._parent.inc_attr("queryCount") if query.class_name() == 'parent': self._agent.query_response(context, self._parent) diff --git a/qpid/cpp/bindings/qmf/tests/ruby_console.rb b/qpid/cpp/bindings/qmf/tests/ruby_console.rb index fb48c29566..c071829f09 100755 --- a/qpid/cpp/bindings/qmf/tests/ruby_console.rb +++ b/qpid/cpp/bindings/qmf/tests/ruby_console.rb @@ -24,33 +24,90 @@ require 'socket' class App < Qmf::ConsoleHandler - def main - @settings = Qmf::ConnectionSettings.new - @settings.set_attr("host", ARGV[0]) if ARGV.size > 0 - @settings.set_attr("port", ARGV[1].to_i) if ARGV.size > 1 - @connection = Qmf::Connection.new(@settings) - @qmf = Qmf::Console.new - - @broker = @qmf.add_connection(@connection) - @broker.waitForStable - - packages = @qmf.get_packages + def dump_schema + packages = @qmfc.get_packages puts "----- Packages -----" packages.each do |p| puts p puts " ----- Object Classes -----" - classes = @qmf.get_classes(p) + classes = @qmfc.get_classes(p) classes.each do |c| puts " #{c.name}" + + puts " ---- Properties ----" + props = c.properties + props.each do |prop| + puts " #{prop.name}" + end + + puts " ---- Statistics ----" + stats = c.statistics + stats.each do |stat| + puts " #{stat.name}" + end + + puts " ---- Methods ----" + methods = c.methods + methods.each do |method| + puts " #{method.name}" + puts " ---- Args ----" + args = method.arguments + args.each do |arg| + puts " #{arg.name}" + end + end end + puts " ----- Event Classes -----" - classes = @qmf.get_classes(p, Qmf::CLASS_EVENT) + classes = @qmfc.get_classes(p, Qmf::CLASS_EVENT) classes.each do |c| puts " #{c.name}" + puts " ---- Args ----" + args = c.arguments + args.each do |arg| + puts " #{arg.name}" + end end end puts "-----" + end + + def main + @settings = Qmf::ConnectionSettings.new + @settings.set_attr("host", ARGV[0]) if ARGV.size > 0 + @settings.set_attr("port", ARGV[1].to_i) if ARGV.size > 1 + @connection = Qmf::Connection.new(@settings) + @qmfc = Qmf::Console.new + + @broker = @qmfc.add_connection(@connection) + @broker.waitForStable + + dump_schema + + agents = @qmfc.get_agents() + puts "---- Agents ----" + agents.each do |a| + puts " => #{a.label}" + end + puts "----" + + for idx in 0...20 + blist = @qmfc.get_objects(Qmf::Query.new(:class => "broker")) + puts "---- Brokers ----" + blist.each do |b| + puts " ---- Broker ----" + puts " systemRef: #{b.get_attr('systemRef')}" + puts " port : #{b.get_attr('port')}" + puts " uptime : #{b.get_attr('uptime') / 1000000000}" + end + puts "----" + sleep(5) + end + sleep(5) + puts "Deleting connection..." + @qmfc.del_connection(@broker) + puts " done" sleep end end diff --git a/qpid/cpp/include/qpid/messaging/Receiver.h b/qpid/cpp/include/qpid/messaging/Receiver.h index e51e1093d1..a4fdd7a34b 100644 --- a/qpid/cpp/include/qpid/messaging/Receiver.h +++ b/qpid/cpp/include/qpid/messaging/Receiver.h @@ -40,7 +40,7 @@ class MessageListener; class ReceiverImpl; /** - * A pull style interface for message retrieval. + * Interface through which messages are received. */ class Receiver : public qpid::client::Handle { @@ -75,7 +75,7 @@ class Receiver : public qpid::client::Handle QPID_CLIENT_EXTERN bool fetch(Message& message, qpid::sys::Duration timeout=qpid::sys::TIME_INFINITE); /** * Retrieves a message for this receivers subscription or waits - * for upto the specified timeout for one to become + * for up to the specified timeout for one to become * available. Unlike get() this method will check with the server * that there is no message for the subscription this receiver is * serving before throwing an exception. @@ -87,8 +87,8 @@ class Receiver : public qpid::client::Handle */ QPID_CLIENT_EXTERN void start(); /** - * Stops the message flow for this receiver (without actually - * cancelling the subscription). + * Stops the message flow for this receiver (but does not cancel + * the subscription). */ QPID_CLIENT_EXTERN void stop(); /** @@ -97,14 +97,35 @@ class Receiver : public qpid::client::Handle * requested by a client via fetch() (or pushed to a listener). */ QPID_CLIENT_EXTERN void setCapacity(uint32_t); + /** + * Returns the capacity of the receiver. The capacity determines + * how many incoming messages can be held in the receiver before + * being requested by a client via fetch() (or pushed to a + * listener). + */ + QPID_CLIENT_EXTERN uint32_t getCapacity(); + /** + * Returns the number of messages received and waiting to be + * fetched. + */ + QPID_CLIENT_EXTERN uint32_t available(); + /** + * Returns a count of the number of messages received on this + * receiver that have been acknowledged, but for which that + * acknowledgement has not yet been confirmed as processed by the + * server. + */ + QPID_CLIENT_EXTERN uint32_t pendingAck(); /** - * Cancels this receiver + * Cancels this receiver. */ QPID_CLIENT_EXTERN void cancel(); /** - * Set a message listener for receiving messages asynchronously. + * Set a message listener for this receiver. + * + * @see Session::dispatch() */ QPID_CLIENT_EXTERN void setListener(MessageListener* listener); private: diff --git a/qpid/cpp/include/qpid/messaging/Sender.h b/qpid/cpp/include/qpid/messaging/Sender.h index 45ec659ecf..9b83a04d60 100644 --- a/qpid/cpp/include/qpid/messaging/Sender.h +++ b/qpid/cpp/include/qpid/messaging/Sender.h @@ -23,6 +23,7 @@ */ #include "qpid/client/ClientImportExport.h" #include "qpid/client/Handle.h" +#include "qpid/sys/IntegerTypes.h" namespace qpid { namespace client { @@ -49,6 +50,24 @@ class Sender : public qpid::client::Handle QPID_CLIENT_EXTERN void send(const Message& message); QPID_CLIENT_EXTERN void cancel(); + + /** + * Sets the capacity for the sender. The capacity determines how + * many outgoing messages can be held pending confirmation of + * receipt by the broker. + */ + QPID_CLIENT_EXTERN void setCapacity(uint32_t); + /** + * Returns the capacity of the sender. + * @see setCapacity + */ + QPID_CLIENT_EXTERN uint32_t getCapacity(); + /** + * Returns the number of sent messages pending confirmation of + * receipt by the broker. (These are the 'in-doubt' messages). + */ + QPID_CLIENT_EXTERN uint32_t pending(); + private: friend class qpid::client::PrivateImplRef; }; diff --git a/qpid/cpp/include/qpid/messaging/Session.h b/qpid/cpp/include/qpid/messaging/Session.h index 1d88882db6..979e27adae 100644 --- a/qpid/cpp/include/qpid/messaging/Session.h +++ b/qpid/cpp/include/qpid/messaging/Session.h @@ -75,6 +75,17 @@ class Session : public qpid::client::Handle QPID_CLIENT_EXTERN void sync(); QPID_CLIENT_EXTERN void flush(); + /** + * Returns the number of messages received and waiting to be + * fetched. + */ + QPID_CLIENT_EXTERN uint32_t available(); + /** + * Returns a count of the number of messages received this session + * that have been acknowledged, but for which that acknowledgement + * has not yet been confirmed as processed by the server. + */ + QPID_CLIENT_EXTERN uint32_t pendingAck(); QPID_CLIENT_EXTERN bool fetch(Message& message, qpid::sys::Duration timeout=qpid::sys::TIME_INFINITE); QPID_CLIENT_EXTERN Message fetch(qpid::sys::Duration timeout=qpid::sys::TIME_INFINITE); QPID_CLIENT_EXTERN bool dispatch(qpid::sys::Duration timeout=qpid::sys::TIME_INFINITE); @@ -88,9 +99,6 @@ class Session : public qpid::client::Handle QPID_CLIENT_EXTERN Receiver createReceiver(const std::string& address, const Filter& filter, const VariantMap& options = VariantMap()); QPID_CLIENT_EXTERN Address createTempQueue(const std::string& baseName = std::string()); - - QPID_CLIENT_EXTERN void* getLastConfirmedSent(); - QPID_CLIENT_EXTERN void* getLastConfirmedAcknowledged(); private: friend class qpid::client::PrivateImplRef; }; diff --git a/qpid/cpp/src/CMakeLists.txt b/qpid/cpp/src/CMakeLists.txt index ff0188890b..786facced9 100644 --- a/qpid/cpp/src/CMakeLists.txt +++ b/qpid/cpp/src/CMakeLists.txt @@ -537,12 +537,12 @@ set (libqpidclient_SOURCES qpid/messaging/Sender.cpp qpid/messaging/SenderImpl.h qpid/messaging/Variant.cpp + qpid/client/amqp0_10/AcceptTracker.h + qpid/client/amqp0_10/AcceptTracker.cpp qpid/client/amqp0_10/AddressResolution.h qpid/client/amqp0_10/AddressResolution.cpp qpid/client/amqp0_10/Codecs.cpp qpid/client/amqp0_10/CodecsInternal.h - qpid/client/amqp0_10/CompletionTracker.h - qpid/client/amqp0_10/CompletionTracker.cpp qpid/client/amqp0_10/ConnectionImpl.h qpid/client/amqp0_10/ConnectionImpl.cpp qpid/client/amqp0_10/IncomingMessages.h diff --git a/qpid/cpp/src/Makefile.am b/qpid/cpp/src/Makefile.am index 75cda31dbb..4988f3f031 100644 --- a/qpid/cpp/src/Makefile.am +++ b/qpid/cpp/src/Makefile.am @@ -697,14 +697,14 @@ libqpidclient_la_SOURCES = \ qpid/messaging/SenderImpl.h \ qpid/messaging/ReceiverImpl.h \ qpid/messaging/SessionImpl.h \ + qpid/client/amqp0_10/AcceptTracker.h \ + qpid/client/amqp0_10/AcceptTracker.cpp \ qpid/client/amqp0_10/AddressResolution.h \ qpid/client/amqp0_10/AddressResolution.cpp \ qpid/client/amqp0_10/Codecs.cpp \ qpid/client/amqp0_10/CodecsInternal.h \ qpid/client/amqp0_10/ConnectionImpl.h \ qpid/client/amqp0_10/ConnectionImpl.cpp \ - qpid/client/amqp0_10/CompletionTracker.h \ - qpid/client/amqp0_10/CompletionTracker.cpp \ qpid/client/amqp0_10/IncomingMessages.h \ qpid/client/amqp0_10/IncomingMessages.cpp \ qpid/client/amqp0_10/MessageSink.h \ diff --git a/qpid/cpp/src/qmf/AgentEngine.cpp b/qpid/cpp/src/qmf/AgentEngine.cpp index d3204042d5..9ea3be5907 100644 --- a/qpid/cpp/src/qmf/AgentEngine.cpp +++ b/qpid/cpp/src/qmf/AgentEngine.cpp @@ -57,7 +57,7 @@ namespace qmf { string name; Object* object; boost::shared_ptr objectId; - Query query; + boost::shared_ptr query; boost::shared_ptr arguments; string exchange; string bindingKey; @@ -214,7 +214,7 @@ AgentEvent AgentEventImpl::copy() item.sequence = sequence; item.object = object; item.objectId = objectId.get(); - item.query = &query; + item.query = query.get(); item.arguments = arguments.get(); item.objectClass = objectClass; @@ -381,7 +381,7 @@ void AgentEngineImpl::methodResponse(uint32_t sequence, uint32_t status, char* t } } sendBufferLH(buffer, context->exchange, context->key); - QPID_LOG(trace, "SENT MethodResponse"); + QPID_LOG(trace, "SENT MethodResponse seq=" << context->sequence << " status=" << status << " text=" << text); } void AgentEngineImpl::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat) @@ -403,7 +403,7 @@ void AgentEngineImpl::queryResponse(uint32_t sequence, Object& object, bool prop object.impl->encodeStatistics(buffer); sendBufferLH(buffer, context->exchange, context->key); - QPID_LOG(trace, "SENT ContentIndication"); + QPID_LOG(trace, "SENT ContentIndication seq=" << context->sequence); } void AgentEngineImpl::queryComplete(uint32_t sequence) @@ -511,9 +511,10 @@ AgentEventImpl::Ptr AgentEngineImpl::eventQuery(uint32_t num, const string& user AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::GET_QUERY)); event->sequence = num; event->authUserId = userId; - event->query.impl->packageName = package; - event->query.impl->className = cls; - event->query.impl->oid = oid; + if (oid.get()) + event->query.reset(new Query(oid.get())); + else + event->query.reset(new Query(cls.c_str(), package.c_str())); return event; } @@ -723,7 +724,7 @@ void AgentEngineImpl::handleGetQuery(Buffer& inBuffer, uint32_t sequence, const ft.decode(inBuffer); - QPID_LOG(trace, "RCVD GetQuery: map=" << ft); + QPID_LOG(trace, "RCVD GetQuery: seq=" << sequence << " map=" << ft); value = ft.get("_package"); if (value.get() && value->convertsTo()) { @@ -773,6 +774,8 @@ void AgentEngineImpl::handleMethodRequest(Buffer& buffer, uint32_t sequence, con AgentClassKey classKey(buffer); buffer.getShortString(method); + QPID_LOG(trace, "RCVD MethodRequest seq=" << sequence << " method=" << method); + map::const_iterator pIter = packages.find(pname); if (pIter == packages.end()) { sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_PACKAGE, pname); diff --git a/qpid/cpp/src/qmf/ConsoleEngine.cpp b/qpid/cpp/src/qmf/ConsoleEngine.cpp index 3d1b378b68..e7991328ee 100644 --- a/qpid/cpp/src/qmf/ConsoleEngine.cpp +++ b/qpid/cpp/src/qmf/ConsoleEngine.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -58,12 +59,27 @@ namespace qmf { auto_ptr arguments; MethodResponseImpl(Buffer& buf); - ~MethodResponseImpl() {} + ~MethodResponseImpl() { delete envelope; } uint32_t getStatus() const { return status; } const Value* getException() const { return exception.get(); } const Value* getArgs() const { return arguments.get(); } }; + struct QueryResponseImpl { + typedef boost::shared_ptr Ptr; + QueryResponse *envelope; + uint32_t status; + auto_ptr exception; + vector results; + + QueryResponseImpl() : envelope(new QueryResponse(this)), status(0) {} + ~QueryResponseImpl() { delete envelope; } + uint32_t getStatus() const { return status; } + const Value* getException() const { return exception.get(); } + uint32_t getObjectCount() const { return results.size(); } + const Object* getObject(uint32_t idx) const; + }; + struct ConsoleEventImpl { typedef boost::shared_ptr Ptr; ConsoleEvent::EventKind kind; @@ -89,13 +105,29 @@ namespace qmf { string name; string exchange; string bindingKey; + void* context; + QueryResponseImpl::Ptr queryResponse; BrokerEventImpl(BrokerEvent::EventKind k) : kind(k) {} ~BrokerEventImpl() {} BrokerEvent copy(); }; - class BrokerProxyImpl : public SequenceContext { + struct AgentProxyImpl { + typedef boost::shared_ptr Ptr; + AgentProxy* envelope; + ConsoleEngineImpl* console; + BrokerProxyImpl* broker; + uint32_t agentBank; + string label; + + AgentProxyImpl(ConsoleEngineImpl* c, BrokerProxyImpl* b, uint32_t ab, const string& l) : + envelope(new AgentProxy(this)), console(c), broker(b), agentBank(ab), label(l) {} + ~AgentProxyImpl() {} + const string& getLabel() const { return label; } + }; + + class BrokerProxyImpl { public: typedef boost::shared_ptr Ptr; @@ -114,12 +146,17 @@ namespace qmf { bool getEvent(BrokerEvent& event) const; void popEvent(); - // From SequenceContext - void complete(); + uint32_t agentCount() const; + const AgentProxy* getAgent(uint32_t idx) const; + void sendQuery(const Query& query, void* context, const AgentProxy* agent); + void sendGetRequestLH(SequenceContext::Ptr queryContext, const Query& query, const AgentProxyImpl* agent); void addBinding(const string& exchange, const string& key); + void staticRelease() { decOutstanding(); } private: + friend class StaticContext; + friend class QueryContext; mutable Mutex lock; BrokerProxy* envelope; ConsoleEngineImpl* console; @@ -128,6 +165,7 @@ namespace qmf { SequenceManager seqMgr; uint32_t requestsOutstanding; bool topicBound; + vector agentList; deque xmtQueue; deque eventQueue; @@ -138,6 +176,7 @@ namespace qmf { BrokerEventImpl::Ptr eventBind(const string& exchange, const string& queue, const string& key); BrokerEventImpl::Ptr eventSetupComplete(); BrokerEventImpl::Ptr eventStable(); + BrokerEventImpl::Ptr eventQueryComplete(void* context, QueryResponseImpl::Ptr response); void handleBrokerResponse(Buffer& inBuffer, uint32_t seq); void handlePackageIndication(Buffer& inBuffer, uint32_t seq); @@ -147,19 +186,33 @@ namespace qmf { void handleHeartbeatIndication(Buffer& inBuffer, uint32_t seq); void handleEventIndication(Buffer& inBuffer, uint32_t seq); void handleSchemaResponse(Buffer& inBuffer, uint32_t seq); - void handleObjectIndication(Buffer& inBuffer, uint32_t seq, bool prop, bool stat); + ObjectImpl::Ptr handleObjectIndication(Buffer& inBuffer, uint32_t seq, bool prop, bool stat); void incOutstandingLH(); void decOutstanding(); }; - struct AgentProxyImpl { - typedef boost::shared_ptr Ptr; - AgentProxy* envelope; - ConsoleEngineImpl* console; + struct StaticContext : public SequenceContext { + StaticContext(BrokerProxyImpl& b) : broker(b) {} + ~StaticContext() {} + void reserve() {} + void release() { broker.staticRelease(); } + bool handleMessage(uint8_t opcode, uint32_t sequence, Buffer& buffer); + BrokerProxyImpl& broker; + }; - AgentProxyImpl(AgentProxy* e, ConsoleEngine& _console) : - envelope(e), console(_console.impl) {} - ~AgentProxyImpl() {} + struct QueryContext : public SequenceContext { + QueryContext(BrokerProxyImpl& b, void* u) : + broker(b), userContext(u), requestsOutstanding(0), queryResponse(new QueryResponseImpl()) {} + ~QueryContext() {} + void reserve(); + void release(); + bool handleMessage(uint8_t opcode, uint32_t sequence, Buffer& buffer); + + mutable Mutex lock; + BrokerProxyImpl& broker; + void* userContext; + uint32_t requestsOutstanding; + QueryResponseImpl::Ptr queryResponse; }; class ConsoleEngineImpl { @@ -187,11 +240,6 @@ namespace qmf { void bindClass(const SchemaClassKey* key); void bindClass(const char* packageName, const char* className); - uint32_t agentCount() const; - const AgentProxy* getAgent(uint32_t idx) const; - - void sendQuery(const Query& query, void* context); - /* void startSync(const Query& query, void* context, SyncQuery& sync); void touchSync(SyncQuery& sync); @@ -226,13 +274,31 @@ namespace qmf { void learnClass(SchemaObjectClassImpl::Ptr cls); void learnClass(SchemaEventClassImpl::Ptr cls); bool haveClass(const SchemaClassKeyImpl& key) const; + SchemaObjectClassImpl::Ptr getSchema(const SchemaClassKeyImpl& key) const; }; } namespace { -const char* QMF_EXCHANGE = "qpid.management"; -const char* DIR_EXCHANGE = "amq.direct"; -const char* BROKER_KEY = "broker"; + const char* QMF_EXCHANGE = "qpid.management"; + const char* DIR_EXCHANGE = "amq.direct"; + const char* BROKER_KEY = "broker"; + const char* BROKER_PACKAGE = "org.apache.qpid.broker"; + const char* AGENT_CLASS = "agent"; + const char* BROKER_AGENT_KEY = "agent.1.0"; +} + +const Object* QueryResponseImpl::getObject(uint32_t idx) const +{ + vector::const_iterator iter = results.begin(); + + while (idx > 0) { + if (iter == results.end()) + return 0; + iter++; + idx--; + } + + return (*iter)->envelope; } #define STRING_REF(s) {if (!s.empty()) item.s = const_cast(s.c_str());} @@ -267,19 +333,29 @@ BrokerEvent BrokerEventImpl::copy() STRING_REF(name); STRING_REF(exchange); STRING_REF(bindingKey); + item.context = context; + item.queryResponse = queryResponse.get() ? queryResponse->envelope : 0; return item; } BrokerProxyImpl::BrokerProxyImpl(BrokerProxy* e, ConsoleEngine& _console) : - envelope(e), console(_console.impl), queueName("qmfc-") + envelope(e), console(_console.impl) { - // TODO: Give the queue name a unique suffix + stringstream qn; + qpid::TcpAddress addr; + + SystemInfo::getLocalHostname(addr); + qn << "qmfc-" << SystemInfo::getProcessName() << "-" << addr << "-" << SystemInfo::getProcessId(); + queueName = qn.str(); + + seqMgr.setUnsolicitedContext(SequenceContext::Ptr(new StaticContext(*this))); } void BrokerProxyImpl::sessionOpened(SessionHandle& /*sh*/) { Mutex::ScopedLock _lock(lock); + agentList.clear(); eventQueue.clear(); xmtQueue.clear(); eventQueue.push_back(eventDeclareQueue(queueName)); @@ -292,6 +368,7 @@ void BrokerProxyImpl::sessionOpened(SessionHandle& /*sh*/) void BrokerProxyImpl::sessionClosed() { Mutex::ScopedLock _lock(lock); + agentList.clear(); eventQueue.clear(); xmtQueue.clear(); } @@ -302,11 +379,14 @@ void BrokerProxyImpl::startProtocol() char rawbuffer[512]; Buffer buffer(rawbuffer, 512); + agentList.push_back(AgentProxyImpl::Ptr(new AgentProxyImpl(console, this, 0, "Agent embedded in broker"))); + requestsOutstanding = 1; topicBound = false; - Protocol::encodeHeader(buffer, Protocol::OP_BROKER_REQUEST); + uint32_t sequence(seqMgr.reserve()); + Protocol::encodeHeader(buffer, Protocol::OP_BROKER_REQUEST, sequence); sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); - QPID_LOG(trace, "SENT BrokerRequest"); + QPID_LOG(trace, "SENT BrokerRequest seq=" << sequence); } void BrokerProxyImpl::sendBufferLH(Buffer& buf, const string& destination, const string& routingKey) @@ -330,23 +410,8 @@ void BrokerProxyImpl::handleRcvMessage(Message& message) uint8_t opcode; uint32_t sequence; - while (Protocol::checkHeader(inBuffer, &opcode, &sequence)) { - if (opcode == Protocol::OP_BROKER_RESPONSE) handleBrokerResponse(inBuffer, sequence); - else if (opcode == Protocol::OP_PACKAGE_INDICATION) handlePackageIndication(inBuffer, sequence); - else if (opcode == Protocol::OP_COMMAND_COMPLETE) handleCommandComplete(inBuffer, sequence); - else if (opcode == Protocol::OP_CLASS_INDICATION) handleClassIndication(inBuffer, sequence); - else if (opcode == Protocol::OP_METHOD_RESPONSE) handleMethodResponse(inBuffer, sequence); - else if (opcode == Protocol::OP_HEARTBEAT_INDICATION) handleHeartbeatIndication(inBuffer, sequence); - else if (opcode == Protocol::OP_EVENT_INDICATION) handleEventIndication(inBuffer, sequence); - else if (opcode == Protocol::OP_SCHEMA_RESPONSE) handleSchemaResponse(inBuffer, sequence); - else if (opcode == Protocol::OP_PROPERTY_INDICATION) handleObjectIndication(inBuffer, sequence, true, false); - else if (opcode == Protocol::OP_STATISTIC_INDICATION) handleObjectIndication(inBuffer, sequence, false, true); - else if (opcode == Protocol::OP_OBJECT_INDICATION) handleObjectIndication(inBuffer, sequence, true, true); - else { - QPID_LOG(trace, "BrokerProxyImpl::handleRcvMessage invalid opcode: " << opcode); - break; - } - } + while (Protocol::checkHeader(inBuffer, &opcode, &sequence)) + seqMgr.dispatch(opcode, sequence, inBuffer); } bool BrokerProxyImpl::getXmtMessage(Message& item) const @@ -381,9 +446,48 @@ void BrokerProxyImpl::popEvent() eventQueue.pop_front(); } -void BrokerProxyImpl::complete() +uint32_t BrokerProxyImpl::agentCount() const { - decOutstanding(); + Mutex::ScopedLock _lock(lock); + return agentList.size(); +} + +const AgentProxy* BrokerProxyImpl::getAgent(uint32_t idx) const +{ + Mutex::ScopedLock _lock(lock); + for (vector::const_iterator iter = agentList.begin(); + iter != agentList.end(); iter++) + if (idx-- == 0) + return (*iter)->envelope; + return 0; +} + +void BrokerProxyImpl::sendQuery(const Query& query, void* context, const AgentProxy* agent) +{ + SequenceContext::Ptr queryContext(new QueryContext(*this, context)); + Mutex::ScopedLock _lock(lock); + if (agent != 0) { + sendGetRequestLH(queryContext, query, agent->impl); + } else { + // TODO (optimization) only send queries to agents that have the requested class+package + for (vector::const_iterator iter = agentList.begin(); + iter != agentList.end(); iter++) { + sendGetRequestLH(queryContext, query, (*iter).get()); + } + } +} + +void BrokerProxyImpl::sendGetRequestLH(SequenceContext::Ptr queryContext, const Query& query, const AgentProxyImpl* agent) +{ + stringstream key; + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve(queryContext)); + + Protocol::encodeHeader(outBuffer, Protocol::OP_GET_QUERY, sequence); + query.impl->encode(outBuffer); + key << "agent.1." << agent->agentBank; + sendBufferLH(outBuffer, QMF_EXCHANGE, key.str()); + QPID_LOG(trace, "SENT GetQuery seq=" << sequence << " key=" << key.str()); } void BrokerProxyImpl::addBinding(const string& exchange, const string& key) @@ -420,17 +524,22 @@ BrokerEventImpl::Ptr BrokerProxyImpl::eventStable() return event; } -void BrokerProxyImpl::handleBrokerResponse(Buffer& inBuffer, uint32_t seq) +BrokerEventImpl::Ptr BrokerProxyImpl::eventQueryComplete(void* context, QueryResponseImpl::Ptr response) { - // Note that this function doesn't touch requestsOutstanding. This is because - // it accounts for one request completed (the BrokerRequest) and one request - // started (the PackageRequest) which cancel each other out. + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::QUERY_COMPLETE)); + event->context = context; + event->queryResponse = response; + return event; +} +void BrokerProxyImpl::handleBrokerResponse(Buffer& inBuffer, uint32_t seq) +{ brokerId.decode(inBuffer); QPID_LOG(trace, "RCVD BrokerResponse seq=" << seq << " brokerId=" << brokerId); Mutex::ScopedLock _lock(lock); Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); - uint32_t sequence(seqMgr.reserve(this)); + uint32_t sequence(seqMgr.reserve()); + incOutstandingLH(); Protocol::encodeHeader(outBuffer, Protocol::OP_PACKAGE_REQUEST, sequence); sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY); QPID_LOG(trace, "SENT PackageRequest seq=" << sequence); @@ -446,7 +555,7 @@ void BrokerProxyImpl::handlePackageIndication(Buffer& inBuffer, uint32_t seq) Mutex::ScopedLock _lock(lock); Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); - uint32_t sequence(seqMgr.reserve(this)); + uint32_t sequence(seqMgr.reserve()); incOutstandingLH(); Protocol::encodeHeader(outBuffer, Protocol::OP_CLASS_QUERY, sequence); outBuffer.putShortString(package); @@ -460,20 +569,12 @@ void BrokerProxyImpl::handleCommandComplete(Buffer& inBuffer, uint32_t seq) uint32_t code = inBuffer.getLong(); inBuffer.getShortString(text); QPID_LOG(trace, "RCVD CommandComplete seq=" << seq << " code=" << code << " text=" << text); - seqMgr.release(seq); } void BrokerProxyImpl::handleClassIndication(Buffer& inBuffer, uint32_t seq) { - string package; - string clsName; - SchemaHash hash; uint8_t kind = inBuffer.getOctet(); - inBuffer.getShortString(package); - inBuffer.getShortString(clsName); - hash.decode(inBuffer); - Uuid printableHash(hash.get()); - SchemaClassKeyImpl classKey(package, clsName, hash); + SchemaClassKeyImpl classKey(inBuffer); QPID_LOG(trace, "RCVD ClassIndication seq=" << seq << " kind=" << (int) kind << " key=" << classKey.str()); @@ -481,7 +582,7 @@ void BrokerProxyImpl::handleClassIndication(Buffer& inBuffer, uint32_t seq) Mutex::ScopedLock _lock(lock); incOutstandingLH(); Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); - uint32_t sequence(seqMgr.reserve(this)); + uint32_t sequence(seqMgr.reserve()); Protocol::encodeHeader(outBuffer, Protocol::OP_SCHEMA_REQUEST, sequence); classKey.encode(outBuffer); sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY); @@ -515,6 +616,25 @@ void BrokerProxyImpl::handleSchemaResponse(Buffer& inBuffer, uint32_t seq) console->learnClass(oClassPtr); key = oClassPtr->getClassKey()->impl; QPID_LOG(trace, "RCVD SchemaResponse seq=" << seq << " kind=object key=" << key->str()); + + // + // If we have just learned about the org.apache.qpid.broker:agent class, send a get + // request for the current list of agents so we can have it on-hand before we declare + // this session "stable". + // + if (key->getClassName() == AGENT_CLASS && key->getPackageName() == BROKER_PACKAGE) { + Mutex::ScopedLock _lock(lock); + incOutstandingLH(); + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve()); + Protocol::encodeHeader(outBuffer, Protocol::OP_GET_QUERY, sequence); + FieldTable ft; + ft.setString("_class", AGENT_CLASS); + ft.setString("_package", BROKER_PACKAGE); + ft.encode(outBuffer); + sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_AGENT_KEY); + QPID_LOG(trace, "SENT GetQuery seq=" << sequence << " key=" << BROKER_AGENT_KEY); + } } else if (kind == CLASS_EVENT) { eClassPtr.reset(new SchemaEventClassImpl(inBuffer)); console->learnClass(eClassPtr); @@ -524,13 +644,20 @@ void BrokerProxyImpl::handleSchemaResponse(Buffer& inBuffer, uint32_t seq) else { QPID_LOG(error, "BrokerProxyImpl::handleSchemaResponse received unknown class kind: " << (int) kind); } - - decOutstanding(); } -void BrokerProxyImpl::handleObjectIndication(Buffer& /*inBuffer*/, uint32_t /*seq*/, bool /*prop*/, bool /*stat*/) +ObjectImpl::Ptr BrokerProxyImpl::handleObjectIndication(Buffer& inBuffer, uint32_t seq, bool prop, bool stat) { - // TODO + SchemaClassKeyImpl classKey(inBuffer); + QPID_LOG(trace, "RCVD ObjectIndication seq=" << seq << " key=" << classKey.str()); + + SchemaObjectClassImpl::Ptr schema = console->getSchema(classKey); + if (schema.get() == 0) { + QPID_LOG(trace, "No Schema Found for ObjectIndication. seq=" << seq << " key=" << classKey.str()); + return ObjectImpl::Ptr(); + } + + return ObjectImpl::Ptr(new ObjectImpl(schema->envelope, inBuffer, prop, stat, true)); } void BrokerProxyImpl::incOutstandingLH() @@ -567,6 +694,79 @@ MethodResponseImpl::MethodResponseImpl(Buffer& buf) : envelope(new MethodRespons arguments.reset(new Value(TYPE_MAP)); } +bool StaticContext::handleMessage(uint8_t opcode, uint32_t sequence, Buffer& buffer) +{ + bool completeContext = false; + if (opcode == Protocol::OP_BROKER_RESPONSE) { + broker.handleBrokerResponse(buffer, sequence); + completeContext = true; + } + else if (opcode == Protocol::OP_COMMAND_COMPLETE) { + broker.handleCommandComplete(buffer, sequence); + completeContext = true; + } + else if (opcode == Protocol::OP_SCHEMA_RESPONSE) { + broker.handleSchemaResponse(buffer, sequence); + completeContext = true; + } + else if (opcode == Protocol::OP_PACKAGE_INDICATION) + broker.handlePackageIndication(buffer, sequence); + else if (opcode == Protocol::OP_CLASS_INDICATION) + broker.handleClassIndication(buffer, sequence); + else if (opcode == Protocol::OP_HEARTBEAT_INDICATION) + broker.handleHeartbeatIndication(buffer, sequence); + else if (opcode == Protocol::OP_EVENT_INDICATION) + broker.handleEventIndication(buffer, sequence); + else if (opcode == Protocol::OP_PROPERTY_INDICATION) + broker.handleObjectIndication(buffer, sequence, true, false); + else if (opcode == Protocol::OP_STATISTIC_INDICATION) + broker.handleObjectIndication(buffer, sequence, false, true); + else if (opcode == Protocol::OP_OBJECT_INDICATION) + broker.handleObjectIndication(buffer, sequence, true, true); + else { + QPID_LOG(trace, "StaticContext::handleMessage invalid opcode: " << opcode); + completeContext = true; + } + + return completeContext; +} + +void QueryContext::reserve() +{ + Mutex::ScopedLock _lock(lock); + requestsOutstanding++; +} + +void QueryContext::release() +{ + Mutex::ScopedLock _lock(lock); + if (--requestsOutstanding == 0) { + broker.eventQueue.push_back(broker.eventQueryComplete(userContext, queryResponse)); + } +} + +bool QueryContext::handleMessage(uint8_t opcode, uint32_t sequence, Buffer& buffer) +{ + bool completeContext = false; + ObjectImpl::Ptr object; + + if (opcode == Protocol::OP_COMMAND_COMPLETE) { + broker.handleCommandComplete(buffer, sequence); + completeContext = true; + } + else if (opcode == Protocol::OP_OBJECT_INDICATION) { + object = broker.handleObjectIndication(buffer, sequence, true, true); + if (object.get() != 0) + queryResponse->results.push_back(object); + } + else { + QPID_LOG(trace, "QueryContext::handleMessage invalid opcode: " << opcode); + completeContext = true; + } + + return completeContext; +} + ConsoleEngineImpl::ConsoleEngineImpl(ConsoleEngine* e, const ConsoleSettings& s) : envelope(e), settings(s) { @@ -757,23 +957,6 @@ void ConsoleEngineImpl::bindClass(const char* packageName, const char* className (*iter)->addBinding(QMF_EXCHANGE, key.str()); } -uint32_t ConsoleEngineImpl::agentCount() const -{ - // TODO - return 0; -} - -const AgentProxy* ConsoleEngineImpl::getAgent(uint32_t /*idx*/) const -{ - // TODO - return 0; -} - -void ConsoleEngineImpl::sendQuery(const Query& /*query*/, void* /*context*/) -{ - // TODO -} - /* void ConsoleEngineImpl::startSync(const Query& query, void* context, SyncQuery& sync) { @@ -835,11 +1018,29 @@ bool ConsoleEngineImpl::haveClass(const SchemaClassKeyImpl& key) const return oList.find(&key) != oList.end() || eList.find(&key) != eList.end(); } +SchemaObjectClassImpl::Ptr ConsoleEngineImpl::getSchema(const SchemaClassKeyImpl& key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key.getPackageName()); + if (pIter == packages.end()) + return SchemaObjectClassImpl::Ptr(); + + const ObjectClassList& oList = pIter->second.first; + ObjectClassList::const_iterator iter = oList.find(&key); + if (iter == oList.end()) + return SchemaObjectClassImpl::Ptr(); + + return iter->second; +} //================================================================== // Wrappers //================================================================== +AgentProxy::AgentProxy(AgentProxyImpl* i) : impl(i) {} +AgentProxy::~AgentProxy() { delete impl; } +const char* AgentProxy::getLabel() const { return impl->getLabel().c_str(); } + BrokerProxy::BrokerProxy(ConsoleEngine& console) : impl(new BrokerProxyImpl(this, console)) {} BrokerProxy::~BrokerProxy() { delete impl; } void BrokerProxy::sessionOpened(SessionHandle& sh) { impl->sessionOpened(sh); } @@ -850,16 +1051,23 @@ bool BrokerProxy::getXmtMessage(Message& item) const { return impl->getXmtMessag void BrokerProxy::popXmt() { impl->popXmt(); } bool BrokerProxy::getEvent(BrokerEvent& event) const { return impl->getEvent(event); } void BrokerProxy::popEvent() { impl->popEvent(); } - -AgentProxy::AgentProxy(ConsoleEngine& console) : impl(new AgentProxyImpl(this, console)) {} -AgentProxy::~AgentProxy() { delete impl; } +uint32_t BrokerProxy::agentCount() const { return impl->agentCount(); } +const AgentProxy* BrokerProxy::getAgent(uint32_t idx) const { return impl->getAgent(idx); } +void BrokerProxy::sendQuery(const Query& query, void* context, const AgentProxy* agent) { impl->sendQuery(query, context, agent); } MethodResponse::MethodResponse(MethodResponseImpl* i) : impl(i) {} -MethodResponse::~MethodResponse() { delete impl; } // TODO: correct to delete here? +MethodResponse::~MethodResponse() {} uint32_t MethodResponse::getStatus() const { return impl->getStatus(); } const Value* MethodResponse::getException() const { return impl->getException(); } const Value* MethodResponse::getArgs() const { return impl->getArgs(); } +QueryResponse::QueryResponse(QueryResponseImpl* i) : impl(i) {} +QueryResponse::~QueryResponse() {} +uint32_t QueryResponse::getStatus() const { return impl->getStatus(); } +const Value* QueryResponse::getException() const { return impl->getException(); } +uint32_t QueryResponse::getObjectCount() const { return impl->getObjectCount(); } +const Object* QueryResponse::getObject(uint32_t idx) const { return impl->getObject(idx); } + ConsoleEngine::ConsoleEngine(const ConsoleSettings& settings) : impl(new ConsoleEngineImpl(this, settings)) {} ConsoleEngine::~ConsoleEngine() { delete impl; } bool ConsoleEngine::getEvent(ConsoleEvent& event) const { return impl->getEvent(event); } @@ -876,9 +1084,6 @@ const SchemaEventClass* ConsoleEngine::getEventClass(const SchemaClassKey* key) void ConsoleEngine::bindPackage(const char* packageName) { impl->bindPackage(packageName); } void ConsoleEngine::bindClass(const SchemaClassKey* key) { impl->bindClass(key); } void ConsoleEngine::bindClass(const char* packageName, const char* className) { impl->bindClass(packageName, className); } -uint32_t ConsoleEngine::agentCount() const { return impl->agentCount(); } -const AgentProxy* ConsoleEngine::getAgent(uint32_t idx) const { return impl->getAgent(idx); } -void ConsoleEngine::sendQuery(const Query& query, void* context) { impl->sendQuery(query, context); } //void ConsoleEngine::startSync(const Query& query, void* context, SyncQuery& sync) { impl->startSync(query, context, sync); } //void ConsoleEngine::touchSync(SyncQuery& sync) { impl->touchSync(sync); } //void ConsoleEngine::endSync(SyncQuery& sync) { impl->endSync(sync); } diff --git a/qpid/cpp/src/qmf/ConsoleEngine.h b/qpid/cpp/src/qmf/ConsoleEngine.h index 84ac78cd69..457e83ad58 100644 --- a/qpid/cpp/src/qmf/ConsoleEngine.h +++ b/qpid/cpp/src/qmf/ConsoleEngine.h @@ -37,6 +37,8 @@ namespace qmf { class AgentProxy; class AgentProxyImpl; class MethodResponseImpl; + class QueryResponseImpl; + class QueryContext; /** * @@ -54,6 +56,23 @@ namespace qmf { MethodResponseImpl* impl; }; + /** + * + */ + class QueryResponse { + public: + QueryResponse(QueryResponseImpl* impl); + ~QueryResponse(); + uint32_t getStatus() const; + const Value* getException() const; + uint32_t getObjectCount() const; + const Object* getObject(uint32_t idx) const; + + private: + friend class QueryContext; + QueryResponseImpl *impl; + }; + /** * */ @@ -64,7 +83,6 @@ namespace qmf { NEW_PACKAGE = 3, NEW_CLASS = 4, OBJECT_UPDATE = 5, - QUERY_COMPLETE = 6, EVENT_RECEIVED = 7, AGENT_HEARTBEAT = 8, METHOD_RESPONSE = 9 @@ -75,11 +93,12 @@ namespace qmf { char* name; // (NEW_PACKAGE) SchemaClassKey* classKey; // (NEW_CLASS) Object* object; // (OBJECT_UPDATE) - void* context; // (OBJECT_UPDATE, QUERY_COMPLETE) + void* context; // (OBJECT_UPDATE) Event* event; // (EVENT_RECEIVED) uint64_t timestamp; // (AGENT_HEARTBEAT) uint32_t methodHandle; // (METHOD_RESPONSE) MethodResponse* methodResponse; // (METHOD_RESPONSE) + QueryResponse* queryResponse; // (QUERY_COMPLETE) }; /** @@ -93,13 +112,30 @@ namespace qmf { BIND = 13, UNBIND = 14, SETUP_COMPLETE = 15, - STABLE = 16 + STABLE = 16, + QUERY_COMPLETE = 17 }; EventKind kind; - char* name; // ([DECLARE|DELETE]_QUEUE, [UN]BIND) - char* exchange; // ([UN]BIND) - char* bindingKey; // ([UN]BIND) + char* name; // ([DECLARE|DELETE]_QUEUE, [UN]BIND) + char* exchange; // ([UN]BIND) + char* bindingKey; // ([UN]BIND) + void* context; // (QUERY_COMPLETE) + QueryResponse* queryResponse; // (QUERY_COMPLETE) + }; + + /** + * + */ + class AgentProxy { + public: + AgentProxy(AgentProxyImpl* impl); + ~AgentProxy(); + const char* getLabel() const; + + private: + friend class BrokerProxyImpl; + AgentProxyImpl* impl; }; /** @@ -121,22 +157,13 @@ namespace qmf { bool getEvent(BrokerEvent& event) const; void popEvent(); - private: - friend class ConsoleEngineImpl; - BrokerProxyImpl* impl; - }; - - /** - * - */ - class AgentProxy { - public: - AgentProxy(ConsoleEngine& console); - ~AgentProxy(); + uint32_t agentCount() const; + const AgentProxy* getAgent(uint32_t idx) const; + void sendQuery(const Query& query, void* context, const AgentProxy* agent = 0); private: friend class ConsoleEngineImpl; - AgentProxyImpl* impl; + BrokerProxyImpl* impl; }; // TODO - move this to a public header @@ -178,11 +205,6 @@ namespace qmf { void bindClass(const SchemaClassKey* key); void bindClass(const char* packageName, const char* className); - uint32_t agentCount() const; - const AgentProxy* getAgent(uint32_t idx) const; - - void sendQuery(const Query& query, void* context); - /* void startSync(const Query& query, void* context, SyncQuery& sync); void touchSync(SyncQuery& sync); diff --git a/qpid/cpp/src/qmf/Object.h b/qpid/cpp/src/qmf/Object.h index eb92cbbe45..9cb3224d9b 100644 --- a/qpid/cpp/src/qmf/Object.h +++ b/qpid/cpp/src/qmf/Object.h @@ -31,13 +31,14 @@ namespace qmf { public: Object(const SchemaObjectClass* type); Object(ObjectImpl* impl); + Object(const Object& from); virtual ~Object(); void destroy(); const ObjectId* getObjectId() const; void setObjectId(ObjectId* oid); const SchemaObjectClass* getClass() const; - Value* getValue(char* key); + Value* getValue(char* key) const; ObjectImpl* impl; }; diff --git a/qpid/cpp/src/qmf/ObjectId.h b/qpid/cpp/src/qmf/ObjectId.h index ffd1b6978b..e894e0b39c 100644 --- a/qpid/cpp/src/qmf/ObjectId.h +++ b/qpid/cpp/src/qmf/ObjectId.h @@ -30,6 +30,7 @@ namespace qmf { class ObjectId { public: ObjectId(); + ObjectId(const ObjectId& from); ObjectId(ObjectIdImpl* impl); ~ObjectId(); diff --git a/qpid/cpp/src/qmf/ObjectIdImpl.cpp b/qpid/cpp/src/qmf/ObjectIdImpl.cpp index 75661fdb47..c0618ccc49 100644 --- a/qpid/cpp/src/qmf/ObjectIdImpl.cpp +++ b/qpid/cpp/src/qmf/ObjectIdImpl.cpp @@ -100,6 +100,15 @@ void ObjectIdImpl::fromString(const std::string& repr) agent = 0; } +std::string ObjectIdImpl::asString() const +{ + stringstream val; + + val << getFlags() << "-" << getSequence() << "-" << getBrokerBank() << "-" << + getAgentBank() << "-" << getObjectNum(); + return val.str(); +} + bool ObjectIdImpl::operator==(const ObjectIdImpl& other) const { uint64_t otherFirst = agent == 0 ? other.first : other.first & 0xffff000000000000LL; @@ -126,15 +135,11 @@ bool ObjectIdImpl::operator>(const ObjectIdImpl& other) const // Wrappers //================================================================== -ObjectId::ObjectId() -{ - impl = new ObjectIdImpl(this); -} +ObjectId::ObjectId() : impl(new ObjectIdImpl(this)) {} -ObjectId::ObjectId(ObjectIdImpl* i) -{ - impl = i; -} +ObjectId::ObjectId(const ObjectId& from) : impl(new ObjectIdImpl(*(from.impl))) {} + +ObjectId::ObjectId(ObjectIdImpl* i) : impl(i) {} ObjectId::~ObjectId() { diff --git a/qpid/cpp/src/qmf/ObjectIdImpl.h b/qpid/cpp/src/qmf/ObjectIdImpl.h index 5d8ee59aee..38d231237f 100644 --- a/qpid/cpp/src/qmf/ObjectIdImpl.h +++ b/qpid/cpp/src/qmf/ObjectIdImpl.h @@ -39,13 +39,14 @@ namespace qmf { uint64_t first; uint64_t second; - ObjectIdImpl(ObjectId* e) : envelope(e), agent(0) {} + ObjectIdImpl(ObjectId* e) : envelope(e), agent(0), first(0), second(0) {} ObjectIdImpl(qpid::framing::Buffer& buffer); ObjectIdImpl(AgentAttachment* agent, uint8_t flags, uint16_t seq, uint64_t object); void decode(qpid::framing::Buffer& buffer); void encode(qpid::framing::Buffer& buffer) const; void fromString(const std::string& repr); + std::string asString() const; uint8_t getFlags() const { return (first & 0xF000000000000000LL) >> 60; } uint16_t getSequence() const { return (first & 0x0FFF000000000000LL) >> 48; } uint32_t getBrokerBank() const { return (first & 0x0000FFFFF0000000LL) >> 28; } diff --git a/qpid/cpp/src/qmf/ObjectImpl.cpp b/qpid/cpp/src/qmf/ObjectImpl.cpp index 645ccd5c81..1ea2d54527 100644 --- a/qpid/cpp/src/qmf/ObjectImpl.cpp +++ b/qpid/cpp/src/qmf/ObjectImpl.cpp @@ -45,30 +45,40 @@ ObjectImpl::ObjectImpl(Object* e, const SchemaObjectClass* type) : } } -ObjectImpl::ObjectImpl(const SchemaObjectClass* type, Buffer& buffer) : - envelope(new Object(this)), objectClass(type), createTime(uint64_t(Duration(now()))), - destroyTime(0), lastUpdatedTime(createTime) +ObjectImpl::ObjectImpl(const SchemaObjectClass* type, Buffer& buffer, bool prop, bool stat, bool managed) : + envelope(new Object(this)), objectClass(type), createTime(0), destroyTime(0), lastUpdatedTime(0) { - int propCount = objectClass->getPropertyCount(); - int statCount = objectClass->getStatisticCount(); int idx; - set excludes; - parsePresenceMasks(buffer, excludes); - for (idx = 0; idx < propCount; idx++) { - const SchemaProperty* prop = objectClass->getProperty(idx); - if (excludes.count(prop->getName()) != 0) { - properties[prop->getName()] = ValuePtr(new Value(prop->getType())); - } else { - ValueImpl* pval = new ValueImpl(prop->getType(), buffer); - properties[prop->getName()] = ValuePtr(pval->envelope); + if (managed) { + lastUpdatedTime = buffer.getLongLong(); + createTime = buffer.getLongLong(); + destroyTime = buffer.getLongLong(); + objectId.reset(new ObjectIdImpl(buffer)); + } + + if (prop) { + int propCount = objectClass->getPropertyCount(); + set excludes; + parsePresenceMasks(buffer, excludes); + for (idx = 0; idx < propCount; idx++) { + const SchemaProperty* prop = objectClass->getProperty(idx); + if (excludes.count(prop->getName()) != 0) { + properties[prop->getName()] = ValuePtr(new Value(prop->getType())); + } else { + ValueImpl* pval = new ValueImpl(prop->getType(), buffer); + properties[prop->getName()] = ValuePtr(pval->envelope); + } } } - for (idx = 0; idx < statCount; idx++) { - const SchemaStatistic* stat = objectClass->getStatistic(idx); - ValueImpl* sval = new ValueImpl(stat->getType(), buffer); - statistics[stat->getName()] = ValuePtr(sval->envelope); + if (stat) { + int statCount = objectClass->getStatisticCount(); + for (idx = 0; idx < statCount; idx++) { + const SchemaStatistic* stat = objectClass->getStatistic(idx); + ValueImpl* sval = new ValueImpl(stat->getType(), buffer); + statistics[stat->getName()] = ValuePtr(sval->envelope); + } } } @@ -82,7 +92,7 @@ void ObjectImpl::destroy() // TODO - flag deletion } -Value* ObjectImpl::getValue(const string& key) +Value* ObjectImpl::getValue(const string& key) const { map::const_iterator iter; @@ -133,7 +143,7 @@ void ObjectImpl::encodeManagedObjectData(qpid::framing::Buffer& buffer) const buffer.putLongLong(lastUpdatedTime); buffer.putLongLong(createTime); buffer.putLongLong(destroyTime); - objectId->impl->encode(buffer); + objectId->encode(buffer); } void ObjectImpl::encodeProperties(qpid::framing::Buffer& buffer) const @@ -187,36 +197,12 @@ void ObjectImpl::encodeStatistics(qpid::framing::Buffer& buffer) const //================================================================== Object::Object(const SchemaObjectClass* type) : impl(new ObjectImpl(this, type)) {} - Object::Object(ObjectImpl* i) : impl(i) {} - -Object::~Object() -{ - delete impl; -} - -void Object::destroy() -{ - impl->destroy(); -} - -const ObjectId* Object::getObjectId() const -{ - return impl->getObjectId(); -} - -void Object::setObjectId(ObjectId* oid) -{ - impl->setObjectId(oid); -} - -const SchemaObjectClass* Object::getClass() const -{ - return impl->getClass(); -} - -Value* Object::getValue(char* key) -{ - return impl->getValue(key); -} +Object::Object(const Object& from) : impl(new ObjectImpl(*(from.impl))) {} +Object::~Object() { delete impl; } +void Object::destroy() { impl->destroy(); } +const ObjectId* Object::getObjectId() const { return impl->getObjectId(); } +void Object::setObjectId(ObjectId* oid) { impl->setObjectId(oid); } +const SchemaObjectClass* Object::getClass() const { return impl->getClass(); } +Value* Object::getValue(char* key) const { return impl->getValue(key); } diff --git a/qpid/cpp/src/qmf/ObjectImpl.h b/qpid/cpp/src/qmf/ObjectImpl.h index 4dc2170bfc..d69979e0da 100644 --- a/qpid/cpp/src/qmf/ObjectImpl.h +++ b/qpid/cpp/src/qmf/ObjectImpl.h @@ -21,19 +21,22 @@ */ #include +#include #include #include #include #include #include +#include namespace qmf { struct ObjectImpl { + typedef boost::shared_ptr Ptr; typedef boost::shared_ptr ValuePtr; Object* envelope; const SchemaObjectClass* objectClass; - boost::shared_ptr objectId; + boost::shared_ptr objectId; uint64_t createTime; uint64_t destroyTime; uint64_t lastUpdatedTime; @@ -41,14 +44,14 @@ namespace qmf { mutable std::map statistics; ObjectImpl(Object* e, const SchemaObjectClass* type); - ObjectImpl(const SchemaObjectClass* type, qpid::framing::Buffer& buffer); + ObjectImpl(const SchemaObjectClass* type, qpid::framing::Buffer& buffer, bool prop, bool stat, bool managed); ~ObjectImpl(); void destroy(); - const ObjectId* getObjectId() const { return objectId.get(); } - void setObjectId(ObjectId* oid) { objectId.reset(oid); } + const ObjectId* getObjectId() const { return objectId.get() ? objectId->envelope : 0; } + void setObjectId(ObjectId* oid) { objectId.reset(oid->impl); } const SchemaObjectClass* getClass() const { return objectClass; } - Value* getValue(const std::string& key); + Value* getValue(const std::string& key) const; void parsePresenceMasks(qpid::framing::Buffer& buffer, std::set& excludeList); void encodeSchemaKey(qpid::framing::Buffer& buffer) const; diff --git a/qpid/cpp/src/qmf/Query.h b/qpid/cpp/src/qmf/Query.h index 78bc6f4ae2..875749862e 100644 --- a/qpid/cpp/src/qmf/Query.h +++ b/qpid/cpp/src/qmf/Query.h @@ -25,26 +25,76 @@ namespace qmf { + struct Object; + struct QueryElementImpl; struct QueryImpl; + struct QueryExpressionImpl; + struct SchemaClassKey; + + enum ValueOper { + O_EQ = 1, + O_NE = 2, + O_LT = 3, + O_LE = 4, + O_GT = 5, + O_GE = 6, + O_RE_MATCH = 7, + O_RE_NOMATCH = 8 + }; + + struct QueryOperand { + virtual ~QueryOperand() {} + virtual bool evaluate(const Object* object) const = 0; + }; + + struct QueryElement : public QueryOperand { + QueryElement(const char* attrName, const Value* value, ValueOper oper); + QueryElement(QueryElementImpl* impl); + virtual ~QueryElement(); + bool evaluate(const Object* object) const; + + QueryElementImpl* impl; + }; + + enum ExprOper { + E_NOT = 1, + E_AND = 2, + E_OR = 3, + E_XOR = 4 + }; + + struct QueryExpression : public QueryOperand { + QueryExpression(ExprOper oper, const QueryOperand* operand1, const QueryOperand* operand2); + QueryExpression(QueryExpressionImpl* impl); + virtual ~QueryExpression(); + bool evaluate(const Object* object) const; + + QueryExpressionImpl* impl; + }; + class Query { public: - Query(); + Query(const char* className, const char* packageName); + Query(const SchemaClassKey* key); + Query(const ObjectId* oid); Query(QueryImpl* impl); ~Query(); + void setSelect(const QueryOperand* criterion); + void setLimit(uint32_t maxResults); + void setOrderBy(const char* attrName, bool decreasing); + const char* getPackage() const; const char* getClass() const; const ObjectId* getObjectId() const; - enum Oper { - OPER_AND = 1, - OPER_OR = 2 - }; - - int whereCount() const; - Oper whereOper() const; - const char* whereKey() const; - const Value* whereValue() const; + bool haveSelect() const; + bool haveLimit() const; + bool haveOrderBy() const; + const QueryOperand* getSelect() const; + uint32_t getLimit() const; + const char* getOrderBy() const; + bool getDecreasing() const; QueryImpl* impl; }; diff --git a/qpid/cpp/src/qmf/QueryImpl.cpp b/qpid/cpp/src/qmf/QueryImpl.cpp index 7e827796bb..f75a9aa5d5 100644 --- a/qpid/cpp/src/qmf/QueryImpl.cpp +++ b/qpid/cpp/src/qmf/QueryImpl.cpp @@ -18,54 +18,77 @@ */ #include "qmf/QueryImpl.h" +#include "qmf/ObjectIdImpl.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/FieldTable.h" using namespace std; using namespace qmf; +using namespace qpid::framing; -//================================================================== -// Wrappers -//================================================================== - -Query::Query() : impl(new QueryImpl(this)) {} -Query::Query(QueryImpl* i) : impl(i) {} - -Query::~Query() +bool QueryElementImpl::evaluate(const Object* /*object*/) const { - delete impl; + // TODO: Implement this + return false; } -const char* Query::getPackage() const +bool QueryExpressionImpl::evaluate(const Object* /*object*/) const { - return impl->getPackage(); + // TODO: Implement this + return false; } -const char* Query::getClass() const +QueryImpl::QueryImpl(Buffer& buffer) { - return impl->getClass(); + FieldTable ft; + ft.decode(buffer); + // TODO } -const ObjectId* Query::getObjectId() const +void QueryImpl::encode(Buffer& buffer) const { - return impl->getObjectId(); -} + FieldTable ft; -int Query::whereCount() const -{ - return impl->whereCount(); -} + if (oid.get() != 0) { + ft.setString("_objectid", oid->impl->asString()); + } else { + if (!packageName.empty()) + ft.setString("_package", packageName); + ft.setString("_class", className); + } -Query::Oper Query::whereOper() const -{ - return impl->whereOper(); + ft.encode(buffer); } -const char* Query::whereKey() const -{ - return impl->whereKey(); -} -const Value* Query::whereValue() const -{ - return impl->whereValue(); -} +//================================================================== +// Wrappers +//================================================================== + +QueryElement::QueryElement(const char* attrName, const Value* value, ValueOper oper) : impl(new QueryElementImpl(attrName, value, oper)) {} +QueryElement::QueryElement(QueryElementImpl* i) : impl(i) {} +QueryElement::~QueryElement() { delete impl; } +bool QueryElement::evaluate(const Object* object) const { return impl->evaluate(object); } +QueryExpression::QueryExpression(ExprOper oper, const QueryOperand* operand1, const QueryOperand* operand2) : impl(new QueryExpressionImpl(oper, operand1, operand2)) {} +QueryExpression::QueryExpression(QueryExpressionImpl* i) : impl(i) {} +QueryExpression::~QueryExpression() { delete impl; } +bool QueryExpression::evaluate(const Object* object) const { return impl->evaluate(object); } +Query::Query(const char* className, const char* packageName) : impl(new QueryImpl(className, packageName)) {} +Query::Query(const SchemaClassKey* key) : impl(new QueryImpl(key)) {} +Query::Query(const ObjectId* oid) : impl(new QueryImpl(oid)) {} +Query::Query(QueryImpl* i) : impl(i) {} +Query::~Query() { delete impl; } +void Query::setSelect(const QueryOperand* criterion) { impl->setSelect(criterion); } +void Query::setLimit(uint32_t maxResults) { impl->setLimit(maxResults); } +void Query::setOrderBy(const char* attrName, bool decreasing) { impl->setOrderBy(attrName, decreasing); } +const char* Query::getPackage() const { return impl->getPackage().c_str(); } +const char* Query::getClass() const { return impl->getClass().c_str(); } +const ObjectId* Query::getObjectId() const { return impl->getObjectId(); } +bool Query::haveSelect() const { return impl->haveSelect(); } +bool Query::haveLimit() const { return impl->haveLimit(); } +bool Query::haveOrderBy() const { return impl->haveOrderBy(); } +const QueryOperand* Query::getSelect() const { return impl->getSelect(); } +uint32_t Query::getLimit() const { return impl->getLimit(); } +const char* Query::getOrderBy() const { return impl->getOrderBy().c_str(); } +bool Query::getDecreasing() const { return impl->getDecreasing(); } diff --git a/qpid/cpp/src/qmf/QueryImpl.h b/qpid/cpp/src/qmf/QueryImpl.h index 1cb9bfe554..4a56a457c0 100644 --- a/qpid/cpp/src/qmf/QueryImpl.h +++ b/qpid/cpp/src/qmf/QueryImpl.h @@ -20,28 +20,82 @@ * under the License. */ -#include +#include "qmf/Query.h" +#include "qmf/Schema.h" #include #include +namespace qpid { + namespace framing { + class Buffer; + } +} + namespace qmf { + struct QueryElementImpl { + QueryElementImpl(const std::string& a, const Value* v, ValueOper o) : + envelope(new QueryElement(this)), attrName(a), value(v), oper(o) {} + ~QueryElementImpl() {} + bool evaluate(const Object* object) const; + + QueryElement* envelope; + std::string attrName; + const Value* value; + ValueOper oper; + }; + + struct QueryExpressionImpl { + QueryExpressionImpl(ExprOper o, const QueryOperand* operand1, const QueryOperand* operand2) : + envelope(new QueryExpression(this)), oper(o), left(operand1), right(operand2) {} + ~QueryExpressionImpl() {} + bool evaluate(const Object* object) const; + + QueryExpression* envelope; + ExprOper oper; + const QueryOperand* left; + const QueryOperand* right; + }; + struct QueryImpl { - Query* envelope; - std::string packageName; - std::string className; - boost::shared_ptr oid; + QueryImpl(Query* e) : envelope(e), select(0) {} + QueryImpl(const std::string& c, const std::string& p) : + envelope(new Query(this)), packageName(p), className(c) {} + QueryImpl(const SchemaClassKey* key) : + envelope(new Query(this)), packageName(key->getPackageName()), className(key->getClassName()) {} + QueryImpl(const ObjectId* oid) : + envelope(new Query(this)), oid(new ObjectId(*oid)) {} + QueryImpl(qpid::framing::Buffer& buffer); + ~QueryImpl() {}; - QueryImpl(Query* e) : envelope(e) {} + void setSelect(const QueryOperand* criterion) { select = criterion; } + void setLimit(uint32_t maxResults) { resultLimit = maxResults; } + void setOrderBy(const std::string& attrName, bool decreasing) { + orderBy = attrName; orderDecreasing = decreasing; + } - const char* getPackage() const { return packageName.empty() ? 0 : packageName.c_str(); } - const char* getClass() const { return className.empty() ? 0 : className.c_str(); } + const std::string& getPackage() const { return packageName; } + const std::string& getClass() const { return className; } const ObjectId* getObjectId() const { return oid.get(); } - int whereCount() const { return 0;} - Query::Oper whereOper() const { return Query::OPER_AND; } - const char* whereKey() const { return 0; } - const Value* whereValue() const { return 0; } + bool haveSelect() const { return select != 0; } + bool haveLimit() const { return resultLimit > 0; } + bool haveOrderBy() const { return !orderBy.empty(); } + const QueryOperand* getSelect() const { return select; } + uint32_t getLimit() const { return resultLimit; } + const std::string& getOrderBy() const { return orderBy; } + bool getDecreasing() const { return orderDecreasing; } + + void encode(qpid::framing::Buffer& buffer) const; + + Query* envelope; + std::string packageName; + std::string className; + boost::shared_ptr oid; + const QueryOperand* select; + uint32_t resultLimit; + std::string orderBy; + bool orderDecreasing; }; } diff --git a/qpid/cpp/src/qmf/SchemaImpl.cpp b/qpid/cpp/src/qmf/SchemaImpl.cpp index ae7d6ca689..3eb14c3952 100644 --- a/qpid/cpp/src/qmf/SchemaImpl.cpp +++ b/qpid/cpp/src/qmf/SchemaImpl.cpp @@ -261,7 +261,15 @@ void SchemaStatisticImpl::updateHash(SchemaHash& hash) const SchemaClassKeyImpl::SchemaClassKeyImpl(const string& p, const string& n, const SchemaHash& h) : envelope(new SchemaClassKey(this)), package(p), name(n), hash(h) {} -void SchemaClassKeyImpl::encode(qpid::framing::Buffer& buffer) const +SchemaClassKeyImpl::SchemaClassKeyImpl(Buffer& buffer) : + envelope(new SchemaClassKey(this)), package(packageContainer), name(nameContainer), hash(hashContainer) +{ + buffer.getShortString(packageContainer); + buffer.getShortString(nameContainer); + hashContainer.decode(buffer); +} + +void SchemaClassKeyImpl::encode(Buffer& buffer) const { buffer.putShortString(package); buffer.putShortString(name); @@ -413,8 +421,9 @@ SchemaEventClassImpl::SchemaEventClassImpl(Buffer& buffer) : buffer.getShortString(package); buffer.getShortString(name); hash.decode(buffer); + buffer.putOctet(0); // No parent class - uint16_t argCount = buffer.getShort(); + uint16_t argCount = buffer.getShort(); for (uint16_t idx = 0; idx < argCount; idx++) { SchemaArgumentImpl* argument = new SchemaArgumentImpl(buffer); diff --git a/qpid/cpp/src/qmf/SchemaImpl.h b/qpid/cpp/src/qmf/SchemaImpl.h index 3e9677d1fa..035d99aecd 100644 --- a/qpid/cpp/src/qmf/SchemaImpl.h +++ b/qpid/cpp/src/qmf/SchemaImpl.h @@ -148,7 +148,14 @@ namespace qmf { const std::string& name; const SchemaHash& hash; + // The *Container elements are only used if there isn't an external place to + // store these values. + std::string packageContainer; + std::string nameContainer; + SchemaHash hashContainer; + SchemaClassKeyImpl(const std::string& package, const std::string& name, const SchemaHash& hash); + SchemaClassKeyImpl(qpid::framing::Buffer& buffer); const std::string& getPackageName() const { return package; } const std::string& getClassName() const { return name; } diff --git a/qpid/cpp/src/qmf/SequenceManager.cpp b/qpid/cpp/src/qmf/SequenceManager.cpp index f51ce9d8b8..3171e66fac 100644 --- a/qpid/cpp/src/qmf/SequenceManager.cpp +++ b/qpid/cpp/src/qmf/SequenceManager.cpp @@ -25,26 +25,72 @@ using namespace qpid::sys; SequenceManager::SequenceManager() : nextSequence(1) {} -uint32_t SequenceManager::reserve(SequenceContext* ctx) +void SequenceManager::setUnsolicitedContext(SequenceContext::Ptr ctx) +{ + unsolicitedContext = ctx; +} + +uint32_t SequenceManager::reserve(SequenceContext::Ptr ctx) { Mutex::ScopedLock _lock(lock); + if (ctx.get() == 0) + ctx = unsolicitedContext; uint32_t seq = nextSequence; while (contextMap.find(seq) != contextMap.end()) seq = seq < 0xFFFFFFFF ? seq + 1 : 1; nextSequence = seq < 0xFFFFFFFF ? seq + 1 : 1; contextMap[seq] = ctx; + ctx->reserve(); return seq; } void SequenceManager::release(uint32_t sequence) { Mutex::ScopedLock _lock(lock); - map::iterator iter = contextMap.find(sequence); + + if (sequence == 0) { + if (unsolicitedContext.get() != 0) + unsolicitedContext->release(); + return; + } + + map::iterator iter = contextMap.find(sequence); if (iter != contextMap.end()) { if (iter->second != 0) - iter->second->complete(); + iter->second->release(); contextMap.erase(iter); } } +void SequenceManager::releaseAll() +{ + Mutex::ScopedLock _lock(lock); + contextMap.clear(); +} + +void SequenceManager::dispatch(uint8_t opcode, uint32_t sequence, qpid::framing::Buffer& buffer) +{ + Mutex::ScopedLock _lock(lock); + bool done; + + if (sequence == 0) { + if (unsolicitedContext.get() != 0) { + done = unsolicitedContext->handleMessage(opcode, sequence, buffer); + if (done) + unsolicitedContext->release(); + } + return; + } + + map::iterator iter = contextMap.find(sequence); + if (iter != contextMap.end()) { + if (iter->second != 0) { + done = iter->second->handleMessage(opcode, sequence, buffer); + if (done) { + iter->second->release(); + contextMap.erase(iter); + } + } + } +} diff --git a/qpid/cpp/src/qmf/SequenceManager.h b/qpid/cpp/src/qmf/SequenceManager.h index c027872313..bbfd0728a7 100644 --- a/qpid/cpp/src/qmf/SequenceManager.h +++ b/qpid/cpp/src/qmf/SequenceManager.h @@ -21,29 +21,43 @@ */ #include "qpid/sys/Mutex.h" +#include #include +namespace qpid { + namespace framing { + class Buffer; + } +} + namespace qmf { class SequenceContext { public: + typedef boost::shared_ptr Ptr; SequenceContext() {} virtual ~SequenceContext() {} - virtual void complete() = 0; + virtual void reserve() = 0; + virtual bool handleMessage(uint8_t opcode, uint32_t sequence, qpid::framing::Buffer& buffer) = 0; + virtual void release() = 0; }; class SequenceManager { public: SequenceManager(); - uint32_t reserve(SequenceContext* ctx); + void setUnsolicitedContext(SequenceContext::Ptr ctx); + uint32_t reserve(SequenceContext::Ptr ctx = SequenceContext::Ptr()); void release(uint32_t sequence); + void releaseAll(); + void dispatch(uint8_t opcode, uint32_t sequence, qpid::framing::Buffer& buffer); private: mutable qpid::sys::Mutex lock; uint32_t nextSequence; - std::map contextMap; + SequenceContext::Ptr unsolicitedContext; + std::map contextMap; }; } diff --git a/qpid/cpp/src/qpid/acl/AclData.cpp b/qpid/cpp/src/qpid/acl/AclData.cpp index d2a55c0027..81519c3311 100644 --- a/qpid/cpp/src/qpid/acl/AclData.cpp +++ b/qpid/cpp/src/qpid/acl/AclData.cpp @@ -53,42 +53,65 @@ bool AclData::matchProp(const std::string & src, const std::string& src1) } } -AclResult AclData::lookup(const std::string& id, const Action& action, const ObjectType& objType, const std::string& name, std::map* params) -{ - AclResult aclresult = decisionMode; - - if (actionList[action] && actionList[action][objType]){ - AclData::actObjItr itrRule = actionList[action][objType]->find(id); - if (itrRule == actionList[action][objType]->end()) - itrRule = actionList[action][objType]->find("*"); - if (itrRule != actionList[action][objType]->end() ) { - - //loop the vector - for (ruleSetItr i=itrRule->second.begin(); isecond.end(); i++) { - - // loop the names looking for match - bool match =true; - for (propertyMapItr pMItr = i->props.begin(); (pMItr != i->props.end()) && match; pMItr++) - { - //match name is exists first - if (pMItr->first == acl::PROP_NAME){ - if (!matchProp(pMItr->second, name)){ - match= false; - } - }else if (params){ //match pMItr against params - propertyMapItr paramItr = params->find (pMItr->first); - if (paramItr == params->end()){ - match = false; - }else if (!matchProp(paramItr->second, pMItr->second)){ - match = false; - } +AclResult AclData::lookup(const std::string& id, const Action& action, const ObjectType& objType, + const std::string& name, std::map* params) { + + QPID_LOG(debug, "ACL: Lookup for id:" << id << " action:" << AclHelper::getActionStr((Action) action) + << " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType) << " name:" << name + << " with params " << AclHelper::propertyMapToString(params)); + + AclResult aclresult = decisionMode; + if (actionList[action] && actionList[action][objType]) { + AclData::actObjItr itrRule = actionList[action][objType]->find(id); + if (itrRule == actionList[action][objType]->end()) + itrRule = actionList[action][objType]->find("*"); + if (itrRule != actionList[action][objType]->end()) { + + QPID_LOG(debug, "ACL: checking the following rules for : " << itrRule->first ); + + //loop the vector + for (ruleSetItr i = itrRule->second.begin(); i < itrRule->second.end(); i++) { + QPID_LOG(debug, "ACL: checking rule " << i->toString()); + // loop the names looking for match + bool match = true; + for (propertyMapItr pMItr = i->props.begin(); (pMItr != i->props.end()) && match; pMItr++) { + //match name is exists first + if (pMItr->first == acl::PROP_NAME) { + if (matchProp(pMItr->second, name)){ + QPID_LOG(debug, "ACL: name '" << name << "' matched with name '" + << pMItr->second << "' given in the rule"); + }else{ + match = false; + QPID_LOG(debug, "ACL: name '" << name << "' didn't match with name '" + << pMItr->second << "' given in the rule"); + } + } else if (params) { //match pMItr against params + propertyMapItr paramItr = params->find(pMItr->first); + if (paramItr == params->end()) { + match = false; + QPID_LOG(debug, "ACL: the given parameter map in lookup doesn't contain the property '" + << AclHelper::getPropertyStr(pMItr->first) << "'"); + } else if (!matchProp(pMItr->second, paramItr->second)) { + QPID_LOG(debug, "ACL: the pair(" + << AclHelper::getPropertyStr(paramItr->first) << "," << paramItr->second + << ") given in lookup doesn't match the pair(" + << AclHelper::getPropertyStr(pMItr->first) << "," << pMItr->second << ") given in the rule"); + match = false; } } - if (match) return getACLResult(i->logOnly, i->log); - } - } - } - return aclresult; + } + if (match) + { + aclresult = getACLResult(i->logOnly, i->log); + QPID_LOG(debug,"Successful match, the decision is:" << AclHelper::getAclResultStr(aclresult)); + return aclresult; + } + } + } + } + + QPID_LOG(debug,"No successful match, defaulting to the decision mode " << AclHelper::getAclResultStr(aclresult)); + return aclresult; } AclResult AclData::lookup(const std::string& id, const Action& action, const ObjectType& objType, const std::string& /*Exchange*/ name, const std::string& RoutingKey) diff --git a/qpid/cpp/src/qpid/acl/AclData.h b/qpid/cpp/src/qpid/acl/AclData.h index 249c3523eb..a63afab12b 100644 --- a/qpid/cpp/src/qpid/acl/AclData.h +++ b/qpid/cpp/src/qpid/acl/AclData.h @@ -22,7 +22,7 @@ #include "qpid/broker/AclModule.h" #include - +#include namespace qpid { namespace acl { @@ -45,6 +45,16 @@ public: rule (propertyMap& p):log(false),logOnly(false),props(p) {}; + + std::string toString () const { + std::ostringstream ruleStr; + ruleStr << "[log=" << log << ", logOnly=" << logOnly << " props{"; + for (propertyMapItr pMItr = props.begin(); pMItr != props.end(); pMItr++) { + ruleStr << " " << AclHelper::getPropertyStr((Property) pMItr-> first) << "=" << pMItr->second; + } + ruleStr << " }]"; + return ruleStr.str(); + } }; typedef std::vector ruleSet; typedef ruleSet::const_iterator ruleSetItr; diff --git a/qpid/cpp/src/qpid/acl/AclReader.cpp b/qpid/cpp/src/qpid/acl/AclReader.cpp index 8f5e4f5b57..8f419a6f50 100644 --- a/qpid/cpp/src/qpid/acl/AclReader.cpp +++ b/qpid/cpp/src/qpid/acl/AclReader.cpp @@ -83,115 +83,142 @@ std::string AclReader::aclRule::toString() { return oss.str(); } -void AclReader::loadDecisionData( boost::shared_ptr d ) -{ - d->clear(); - QPID_LOG(debug, "ACL Load Rules"); - int cnt = rules.size(); +void AclReader::loadDecisionData(boost::shared_ptr d) { + d->clear(); + QPID_LOG(debug, "ACL Load Rules"); + int cnt = rules.size(); bool foundmode = false; - for (rlCitr i=rules.end()-1; cnt; i--,cnt--) { - QPID_LOG(debug, "ACL Processing " << std::setfill(' ') << std::setw(2) << cnt << " " << (*i)->toString()); - - if (!foundmode && (*i)->actionAll && (*i)->names.size()==1 && (*((*i)->names.begin())).compare("*")==0 ){ - d->decisionMode = (*i)->res; - QPID_LOG(debug, "ACL FoundMode " << AclHelper::getAclResultStr(d->decisionMode)); - foundmode=true; - }else{ - AclData::rule rule((*i)->props); - bool addrule= true; - - switch ((*i)->res) - { - case qpid::acl::ALLOWLOG: - rule.log = true; - if (d->decisionMode == qpid::acl::ALLOW || d->decisionMode == qpid::acl::ALLOWLOG) - rule.logOnly = true; + + for (rlCitr i = rules.end() - 1; cnt; i--, cnt--) { + QPID_LOG(debug, "ACL Processing " << std::setfill(' ') << std::setw(2) + << cnt << " " << (*i)->toString()); + + if (!foundmode && (*i)->actionAll && (*i)->names.size() == 1 + && (*((*i)->names.begin())).compare("*") == 0) { + d->decisionMode = (*i)->res; + QPID_LOG(debug, "ACL FoundMode " << AclHelper::getAclResultStr( + d->decisionMode)); + foundmode = true; + } else { + AclData::rule rule((*i)->props); + bool addrule = true; + + switch ((*i)->res) { + case qpid::acl::ALLOWLOG: + rule.log = true; + if (d->decisionMode == qpid::acl::ALLOW || d->decisionMode + == qpid::acl::ALLOWLOG) + rule.logOnly = true; + break; + case qpid::acl::ALLOW: + if (d->decisionMode == qpid::acl::ALLOW || d->decisionMode + == qpid::acl::ALLOWLOG) + addrule = false; + break; + case qpid::acl::DENYLOG: + rule.log = true; + if (d->decisionMode == qpid::acl::DENY || d->decisionMode + == qpid::acl::DENYLOG) + rule.logOnly = true; + break; + case qpid::acl::DENY: + if (d->decisionMode == qpid::acl::DENY || d->decisionMode + == qpid::acl::DENYLOG) + addrule = false; break; - case qpid::acl::ALLOW: - if (d->decisionMode == qpid::acl::ALLOW || d->decisionMode == qpid::acl::ALLOWLOG) - addrule = false; - break; - case qpid::acl::DENYLOG: - rule.log = true; - if (d->decisionMode == qpid::acl::DENY || d->decisionMode == qpid::acl::DENYLOG) - rule.logOnly = true; - break; - case qpid::acl::DENY: - if (d->decisionMode == qpid::acl::DENY || d->decisionMode == qpid::acl::DENYLOG) - addrule = false; - break; - default: - throw Exception("Invalid ACL Result loading rules."); - } - - - // Action -> Object -> map set > - if (addrule){ - for (int acnt= ((*i)->actionAll?0:(*i)->action); - acnt< acl::ACTIONSIZE; (*i)->actionAll?acnt++:acnt=acl::ACTIONSIZE ) { - - if (acnt == acl::ACT_PUBLISH) d->transferAcl = true; // we have transfer ACL - - QPID_LOG(debug, "ACL Adding action:" << AclHelper::getActionStr((Action)acnt) ); - - //find the Action, create if not exist - if (d->actionList[acnt]==NULL) { - d->actionList[acnt] = new AclData::aclAction[qpid::acl::OBJECTSIZE]; - for (int j=0;jactionList[acnt][j] = NULL; - } + default: + throw Exception("Invalid ACL Result loading rules."); + } + + // Action -> Object -> map set > + if (addrule) { + std::ostringstream actionstr; + for (int acnt = ((*i)->actionAll ? 0 : (*i)->action); acnt + < acl::ACTIONSIZE; (*i)->actionAll ? acnt++ : acnt + = acl::ACTIONSIZE) { + + if (acnt == acl::ACT_PUBLISH) + d->transferAcl = true; // we have transfer ACL + + actionstr << AclHelper::getActionStr((Action) acnt) << ","; + + //find the Action, create if not exist + if (d->actionList[acnt] == NULL) { + d->actionList[acnt] + = new AclData::aclAction[qpid::acl::OBJECTSIZE]; + for (int j = 0; j < qpid::acl::OBJECTSIZE; j++) + d->actionList[acnt][j] = NULL; + } // optimize this loop to limit to valid options only!! - for (int ocnt= ((*i)->objStatus!=aclRule::VALUE ?0:(*i)->object); - ocnt< acl::OBJECTSIZE; - (*i)->objStatus!=aclRule::VALUE?ocnt++:ocnt=acl::OBJECTSIZE ) { - - QPID_LOG(debug, "ACL Adding object:" << AclHelper::getObjectTypeStr((ObjectType)ocnt) ); - - //find the Object, create if not exist - if (d->actionList[acnt][ocnt] == NULL) - d->actionList[acnt][ocnt] = new AclData::actionObject; - - // add users and Rule to object set - bool allNames=false; - // check to see if names.begin is '*' - if ( (*(*i)->names.begin()).compare("*")==0 ) allNames = true; - - for (nsCitr itr = (allNames?names.begin():(*i)->names.begin()); - itr != (allNames?names.end():(*i)->names.end()); itr++) { - AclData::actObjItr itrRule = d->actionList[acnt][ocnt]->find(*itr); - if (itrRule == d->actionList[acnt][ocnt]->end()) { - QPID_LOG(debug, "ACL Adding rule & user:" << *itr); - AclData::ruleSet rSet; - rSet.push_back(rule); - d->actionList[acnt][ocnt]->insert(make_pair( std::string(*itr) , rSet) ); - }else{ - - // TODO add code to check for dead rules - // allow peter create queue name=tmp <-- dead rule!! - // allow peter create queue - - itrRule->second.push_back(rule); - QPID_LOG(debug, "ACL Adding rule to user:" << *itr); - } - } - - } - - } - }else{ - QPID_LOG(debug, "ACL Skipping based on Mode:" << AclHelper::getAclResultStr(d->decisionMode) ); - } - } - - } + for (int ocnt = ((*i)->objStatus != aclRule::VALUE ? 0 + : (*i)->object); ocnt < acl::OBJECTSIZE; (*i)->objStatus + != aclRule::VALUE ? ocnt++ : ocnt = acl::OBJECTSIZE) { + + //find the Object, create if not exist + if (d->actionList[acnt][ocnt] == NULL) + d->actionList[acnt][ocnt] + = new AclData::actionObject; + + // add users and Rule to object set + bool allNames = false; + // check to see if names.begin is '*' + if ((*(*i)->names.begin()).compare("*") == 0) + allNames = true; + + for (nsCitr itr = (allNames ? names.begin() + : (*i)->names.begin()); itr + != (allNames ? names.end() : (*i)->names.end()); itr++) { + + AclData::actObjItr itrRule = + d->actionList[acnt][ocnt]->find(*itr); + + if (itrRule == d->actionList[acnt][ocnt]->end()) { + AclData::ruleSet rSet; + rSet.push_back(rule); + d->actionList[acnt][ocnt]->insert(make_pair( + std::string(*itr), rSet)); + } else { + + // TODO add code to check for dead rules + // allow peter create queue name=tmp <-- dead rule!! + // allow peter create queue + + itrRule->second.push_back(rule); + } + } + + } + } + + std::ostringstream objstr; + for (int ocnt = ((*i)->objStatus != aclRule::VALUE ? 0 : (*i)->object); ocnt < acl::OBJECTSIZE; + (*i)->objStatus != aclRule::VALUE ? ocnt++ : ocnt = acl::OBJECTSIZE) { + objstr << AclHelper::getObjectTypeStr((ObjectType) ocnt) << ","; + } + + bool allNames = ((*(*i)->names.begin()).compare("*") == 0); + std::ostringstream userstr; + for (nsCitr itr = (allNames ? names.begin() : (*i)->names.begin()); + itr != (allNames ? names.end() : (*i)->names.end()); itr++) { + userstr << *itr << ","; + } + + QPID_LOG(debug,"ACL: Adding actions {" << actionstr.str().substr(0,actionstr.str().length()-1) + << "} to objects {" << objstr.str().substr(0,objstr.str().length()-1) + << "} with props " << AclHelper::propertyMapToString(&rule.props) + << " for users {" << userstr.str().substr(0,userstr.str().length()-1) << "}" ); + } else { + QPID_LOG(debug, "ACL Skipping based on Mode:" + << AclHelper::getAclResultStr(d->decisionMode)); + } + } + } } - - void AclReader::aclRule::processName(const std::string& name, const groupMap& groups) { if (name.compare("all") == 0) { names.insert("*"); diff --git a/qpid/cpp/src/qpid/broker/AclModule.h b/qpid/cpp/src/qpid/broker/AclModule.h index a78b2d5b4a..536fa21b2b 100644 --- a/qpid/cpp/src/qpid/broker/AclModule.h +++ b/qpid/cpp/src/qpid/broker/AclModule.h @@ -26,7 +26,7 @@ #include #include #include - +#include namespace qpid { @@ -179,6 +179,8 @@ class AclHelper { typedef std::map objectMap; typedef objectMap::const_iterator omCitr; typedef boost::shared_ptr objectMapPtr; + typedef std::map propMap; + typedef propMap::const_iterator propMapItr; // This map contains the legal combinations of object/action/properties found in an ACL file static void loadValidationMap(objectMapPtr& map) { @@ -248,6 +250,19 @@ class AclHelper { map->insert(objectPair(OBJ_METHOD, a4)); } + + static std::string propertyMapToString(const std::map* params) { + std::ostringstream ss; + ss << "{"; + if (params) + { + for (propMapItr pMItr = params->begin(); pMItr != params->end(); pMItr++) { + ss << " " << getPropertyStr((Property) pMItr-> first) << "=" << pMItr->second; + } + } + ss << " }"; + return ss.str(); + } }; diff --git a/qpid/cpp/src/qpid/broker/Exchange.cpp b/qpid/cpp/src/qpid/broker/Exchange.cpp index b1fc1295f3..90d81b81c6 100644 --- a/qpid/cpp/src/qpid/broker/Exchange.cpp +++ b/qpid/cpp/src/qpid/broker/Exchange.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -38,7 +38,7 @@ using qpid::management::Manageable; using qpid::management::Args; namespace _qmf = qmf::org::apache::qpid::broker; -namespace +namespace { const std::string qpidMsgSequence("qpid.msg_sequence"); const std::string qpidSequenceCounter("qpid.sequence_counter"); @@ -51,17 +51,19 @@ const std::string fedOpBind("B"); const std::string fedOpUnbind("U"); const std::string fedOpReorigin("R"); const std::string fedOpHello("H"); + +const std::string QPID_MANAGEMENT("qpid.management"); } Exchange::PreRoute::PreRoute(Deliverable& msg, Exchange* _p):parent(_p) { if (parent){ if (parent->sequence || parent->ive) parent->sequenceLock.lock(); - + if (parent->sequence){ parent->sequenceNo++; - msg.getMessage().getProperties()->getApplicationHeaders().setInt64(qpidMsgSequence,parent->sequenceNo); - } + msg.getMessage().getProperties()->getApplicationHeaders().setInt64(qpidMsgSequence,parent->sequenceNo); + } if (parent->ive) { parent->lastMsg = &( msg.getMessage()); } @@ -99,11 +101,9 @@ Exchange::Exchange (const string& _name, Manageable* parent, Broker* b) : } } -static const std::string QPID_MANAGEMENT("qpid.management"); - Exchange::Exchange(const string& _name, bool _durable, const qpid::framing::FieldTable& _args, Manageable* parent, Broker* b) - : name(_name), durable(_durable), alternateUsers(0), persistenceId(0), + : name(_name), durable(_durable), alternateUsers(0), persistenceId(0), args(_args), sequence(false), sequenceNo(0), ive(false), mgmtExchange(0), broker(b) { if (parent != 0 && broker != 0) @@ -169,7 +169,7 @@ Exchange::shared_ptr Exchange::decode(ExchangeRegistry& exchanges, Buffer& buffe string name; string type; FieldTable args; - + buffer.getShortString(name); bool durable(buffer.getOctet()); buffer.getShortString(type); @@ -185,7 +185,7 @@ Exchange::shared_ptr Exchange::decode(ExchangeRegistry& exchanges, Buffer& buffe } } -void Exchange::encode(Buffer& buffer) const +void Exchange::encode(Buffer& buffer) const { buffer.putShortString(name); buffer.putOctet(durable); @@ -195,8 +195,8 @@ void Exchange::encode(Buffer& buffer) const buffer.put(args); } -uint32_t Exchange::encodedSize() const -{ +uint32_t Exchange::encodedSize() const +{ return name.size() + 1/*short string size*/ + 1 /*durable*/ + getType().size() + 1/*short string size*/ diff --git a/qpid/cpp/src/qpid/broker/Message.cpp b/qpid/cpp/src/qpid/broker/Message.cpp index 639f04faa2..7360010192 100644 --- a/qpid/cpp/src/qpid/broker/Message.cpp +++ b/qpid/cpp/src/qpid/broker/Message.cpp @@ -362,7 +362,7 @@ boost::intrusive_ptr& Message::getReplacementMessage(const Queue* qfor) Replacement::iterator i = replacement.find(qfor); if (i != replacement.end()){ return i->second; - } + } return empty; } diff --git a/qpid/cpp/src/qpid/broker/Message.h b/qpid/cpp/src/qpid/broker/Message.h index 0024509bc8..e4d09b1042 100644 --- a/qpid/cpp/src/qpid/broker/Message.h +++ b/qpid/cpp/src/qpid/broker/Message.h @@ -34,12 +34,12 @@ #include namespace qpid { - + namespace framing { class FieldTable; class SequenceNumber; } - + namespace broker { class ConnectionToken; class Exchange; @@ -145,9 +145,9 @@ public: bool isExcluded(const std::vector& excludes) const; void addTraceId(const std::string& id); - - void forcePersistent(); - bool isForcedPersistent(); + + void forcePersistent(); + bool isForcedPersistent(); boost::intrusive_ptr& getReplacementMessage(const Queue* qfor) const; void setReplacementMessage(boost::intrusive_ptr msg, const Queue* qfor); diff --git a/qpid/cpp/src/qpid/broker/MessageBuilder.cpp b/qpid/cpp/src/qpid/broker/MessageBuilder.cpp index e01fd81074..14b233fd6c 100644 --- a/qpid/cpp/src/qpid/broker/MessageBuilder.cpp +++ b/qpid/cpp/src/qpid/broker/MessageBuilder.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -30,14 +30,14 @@ using boost::intrusive_ptr; using namespace qpid::broker; using namespace qpid::framing; -namespace +namespace { std::string type_str(uint8_t type); + const std::string QPID_MANAGEMENT("qpid.management"); } -MessageBuilder::MessageBuilder(MessageStore* const _store, uint64_t _stagingThreshold) : - state(DORMANT), store(_store), stagingThreshold(_stagingThreshold), staging(false) {} -static const std::string QPID_MANAGEMENT("qpid.management"); +MessageBuilder::MessageBuilder(MessageStore* const _store, uint64_t _stagingThreshold) : + state(DORMANT), store(_store), stagingThreshold(_stagingThreshold), staging(false) {} void MessageBuilder::handle(AMQFrame& frame) { @@ -54,10 +54,10 @@ void MessageBuilder::handle(AMQFrame& frame) AMQFrame header((AMQHeaderBody())); header.setBof(false); header.setEof(false); - message->getFrames().append(header); + message->getFrames().append(header); } else if (type != HEADER_BODY) { throw CommandInvalidException( - QPID_MSG("Invalid frame sequence for message, expected header or content got " + QPID_MSG("Invalid frame sequence for message, expected header or content got " << type_str(type) << ")")); } state = CONTENT; @@ -74,13 +74,13 @@ void MessageBuilder::handle(AMQFrame& frame) } else { message->getFrames().append(frame); //have we reached the staging limit? if so stage message and release content - if (state == CONTENT - && stagingThreshold + if (state == CONTENT + && stagingThreshold && message->getFrames().getContentSize() >= stagingThreshold && !NullMessageStore::isNullStore(store) - && message->getExchangeName() != QPID_MANAGEMENT /* don't stage mgnt messages */) + && message->getExchangeName() != QPID_MANAGEMENT /* don't stage mgnt messages */) { - message->releaseContent(store); + message->releaseContent(store); staging = true; } } @@ -108,7 +108,7 @@ const std::string CONTENT_BODY_S = "CONTENT"; const std::string HEARTBEAT_BODY_S = "HEARTBEAT"; const std::string UNKNOWN = "unknown"; -std::string type_str(uint8_t type) +std::string type_str(uint8_t type) { switch(type) { case METHOD_BODY: return METHOD_BODY_S; @@ -124,7 +124,7 @@ std::string type_str(uint8_t type) void MessageBuilder::checkType(uint8_t expected, uint8_t actual) { if (expected != actual) { - throw CommandInvalidException(QPID_MSG("Invalid frame sequence for message (expected " + throw CommandInvalidException(QPID_MSG("Invalid frame sequence for message (expected " << type_str(expected) << " got " << type_str(actual) << ")")); } } diff --git a/qpid/cpp/src/qpid/broker/PersistableMessage.cpp b/qpid/cpp/src/qpid/broker/PersistableMessage.cpp index c1f86d4ca4..2ef223aa81 100644 --- a/qpid/cpp/src/qpid/broker/PersistableMessage.cpp +++ b/qpid/cpp/src/qpid/broker/PersistableMessage.cpp @@ -62,7 +62,7 @@ void PersistableMessage::flush() void PersistableMessage::setContentReleased() {contentReleased = true; } bool PersistableMessage::isContentReleased()const { return contentReleased; } - + bool PersistableMessage::isEnqueueComplete() { sys::ScopedLock l(asyncEnqueueLock); return asyncEnqueueCounter == 0; diff --git a/qpid/cpp/src/qpid/broker/PersistableMessage.h b/qpid/cpp/src/qpid/broker/PersistableMessage.h index 05de9ff4c3..0274b41375 100644 --- a/qpid/cpp/src/qpid/broker/PersistableMessage.h +++ b/qpid/cpp/src/qpid/broker/PersistableMessage.h @@ -46,7 +46,7 @@ class PersistableMessage : public Persistable sys::Mutex asyncEnqueueLock; sys::Mutex asyncDequeueLock; sys::Mutex storeLock; - + /** * Tracks the number of outstanding asynchronous enqueue * operations. When the message is enqueued asynchronously the @@ -97,7 +97,7 @@ class PersistableMessage : public Persistable void flush(); bool isContentReleased() const; - + QPID_BROKER_EXTERN bool isEnqueueComplete(); QPID_BROKER_EXTERN void enqueueComplete(); diff --git a/qpid/cpp/src/qpid/broker/Queue.cpp b/qpid/cpp/src/qpid/broker/Queue.cpp index 08ee133981..b2a8e223c5 100644 --- a/qpid/cpp/src/qpid/broker/Queue.cpp +++ b/qpid/cpp/src/qpid/broker/Queue.cpp @@ -597,7 +597,7 @@ void Queue::push(boost::intrusive_ptr& msg, bool isRecovery){ Mutex::ScopedUnlock u(messageLock); dequeue(0, QueuedMessage(qm.queue, old, qm.position)); } - } + } }else { messages.push_back(qm); listeners.populate(copy); @@ -702,7 +702,7 @@ bool Queue::enqueue(TransactionContext* ctxt, boost::intrusive_ptr msg) if (inLastNodeFailure && persistLastNode){ msg->forcePersistent(); } - + if (traceId.size()) { msg->addTraceId(traceId); } diff --git a/qpid/cpp/src/qpid/broker/QueueRegistry.h b/qpid/cpp/src/qpid/broker/QueueRegistry.h index c134f399c8..72a91dff24 100644 --- a/qpid/cpp/src/qpid/broker/QueueRegistry.h +++ b/qpid/cpp/src/qpid/broker/QueueRegistry.h @@ -111,7 +111,7 @@ class QueueRegistry { /** Call f for each queue in the registry. */ template void eachQueue(F f) const { - qpid::sys::RWlock::ScopedWlock l(lock); + qpid::sys::RWlock::ScopedRlock l(lock); for (QueueMap::const_iterator i = queues.begin(); i != queues.end(); ++i) f(i->second); } diff --git a/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp b/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp index 2a19115fd1..5bc4cdf960 100644 --- a/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp +++ b/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp @@ -195,7 +195,7 @@ void RecoverableQueueImpl::setPersistenceId(uint64_t id) { queue->setPersistenceId(id); } - + uint64_t RecoverableQueueImpl::getPersistenceId() const { return queue->getPersistenceId(); diff --git a/qpid/cpp/src/qpid/broker/SessionAdapter.cpp b/qpid/cpp/src/qpid/broker/SessionAdapter.cpp index af07605552..a1ad5a0a30 100644 --- a/qpid/cpp/src/qpid/broker/SessionAdapter.cpp +++ b/qpid/cpp/src/qpid/broker/SessionAdapter.cpp @@ -72,7 +72,7 @@ void SessionAdapter::ExchangeHandlerImpl::declare(const string& exchange, const params.insert(make_pair(acl::PROP_PASSIVE, std::string(passive ? _TRUE : _FALSE) )); params.insert(make_pair(acl::PROP_DURABLE, std::string(durable ? _TRUE : _FALSE))); if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_EXCHANGE,exchange,¶ms) ) - throw NotAllowedException(QPID_MSG("ACL denied exhange declare request from " << getConnection().getUserId())); + throw NotAllowedException(QPID_MSG("ACL denied exchange declare request from " << getConnection().getUserId())); } //TODO: implement autoDelete @@ -134,7 +134,7 @@ void SessionAdapter::ExchangeHandlerImpl::delete_(const string& name, bool /*ifU AclModule* acl = getBroker().getAcl(); if (acl) { if (!acl->authorise(getConnection().getUserId(),acl::ACT_DELETE,acl::OBJ_EXCHANGE,name,NULL) ) - throw NotAllowedException(QPID_MSG("ACL denied exhange delete request from " << getConnection().getUserId())); + throw NotAllowedException(QPID_MSG("ACL denied exchange delete request from " << getConnection().getUserId())); } //TODO: implement unused @@ -154,7 +154,7 @@ ExchangeQueryResult SessionAdapter::ExchangeHandlerImpl::query(const string& nam AclModule* acl = getBroker().getAcl(); if (acl) { if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_EXCHANGE,name,NULL) ) - throw NotAllowedException(QPID_MSG("ACL denied exhange query request from " << getConnection().getUserId())); + throw NotAllowedException(QPID_MSG("ACL denied exchange query request from " << getConnection().getUserId())); } try { @@ -171,8 +171,12 @@ void SessionAdapter::ExchangeHandlerImpl::bind(const string& queueName, { AclModule* acl = getBroker().getAcl(); if (acl) { - if (!acl->authorise(getConnection().getUserId(),acl::ACT_BIND,acl::OBJ_EXCHANGE,exchangeName,routingKey) ) - throw NotAllowedException(QPID_MSG("ACL denied exhange bind request from " << getConnection().getUserId())); + std::map params; + params.insert(make_pair(acl::PROP_QUEUENAME, queueName)); + params.insert(make_pair(acl::PROP_ROUTINGKEY, routingKey)); + + if (!acl->authorise(getConnection().getUserId(),acl::ACT_BIND,acl::OBJ_EXCHANGE,exchangeName,¶ms)) + throw NotAllowedException(QPID_MSG("ACL denied exchange bind request from " << getConnection().getUserId())); } Queue::shared_ptr queue = getQueue(queueName); @@ -234,8 +238,8 @@ ExchangeBoundResult SessionAdapter::ExchangeHandlerImpl::bound(const std::string std::map params; params.insert(make_pair(acl::PROP_QUEUENAME, queueName)); params.insert(make_pair(acl::PROP_ROUTINGKEY, key)); - if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_EXCHANGE,exchangeName,¶ms) ) - throw NotAllowedException(QPID_MSG("ACL denied exhange bound request from " << getConnection().getUserId())); + if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_EXCHANGE,exchangeName,¶ms) ) + throw NotAllowedException(QPID_MSG("ACL denied exchange bound request from " << getConnection().getUserId())); } Exchange::shared_ptr exchange; diff --git a/qpid/cpp/src/qpid/client/Dispatcher.cpp b/qpid/cpp/src/qpid/client/Dispatcher.cpp index 43cbf3aa4d..a715c623bf 100644 --- a/qpid/cpp/src/qpid/client/Dispatcher.cpp +++ b/qpid/cpp/src/qpid/client/Dispatcher.cpp @@ -29,7 +29,14 @@ #include "qpid/client/Message.h" #include "qpid/client/MessageImpl.h" -#include +#include +#if (BOOST_VERSION >= 104000) +# include + using boost::serialization::state_saver; +#else +# include + using boost::state_saver; +#endif /* BOOST_VERSION */ using qpid::framing::FrameSet; using qpid::framing::MessageTransferBody; @@ -65,7 +72,7 @@ void Dispatcher::run() Mutex::ScopedLock l(lock); if (running) throw Exception("Dispatcher is already running."); - boost::state_saver reset(running); // Reset to false on exit. + state_saver reset(running); // Reset to false on exit. running = true; try { while (!queue->isClosed()) { diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp b/qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp new file mode 100644 index 0000000000..80be5c56f3 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp @@ -0,0 +1,111 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "AcceptTracker.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + +void AcceptTracker::State::accept() +{ + unconfirmed.add(unaccepted); + unaccepted.clear(); +} + +void AcceptTracker::State::release() +{ + unaccepted.clear(); +} + +uint32_t AcceptTracker::State::acceptsPending() +{ + return unconfirmed.size(); +} + +void AcceptTracker::State::completed(qpid::framing::SequenceSet& set) +{ + unconfirmed.remove(set); +} + +void AcceptTracker::delivered(const std::string& destination, const qpid::framing::SequenceNumber& id) +{ + aggregateState.unaccepted.add(id); + destinationState[destination].unaccepted.add(id); +} + +void AcceptTracker::accept(qpid::client::AsyncSession& session) +{ + for (StateMap::iterator i = destinationState.begin(); i != destinationState.end(); ++i) { + i->second.accept(); + } + Record record; + record.status = session.messageAccept(aggregateState.unaccepted); + record.accepted = aggregateState.unaccepted; + pending.push_back(record); + aggregateState.accept(); +} + +void AcceptTracker::release(qpid::client::AsyncSession& session) +{ + session.messageRelease(aggregateState.unaccepted); + for (StateMap::iterator i = destinationState.begin(); i != destinationState.end(); ++i) { + i->second.release(); + } + aggregateState.release(); +} + +uint32_t AcceptTracker::acceptsPending() +{ + checkPending(); + return aggregateState.acceptsPending(); +} + +uint32_t AcceptTracker::acceptsPending(const std::string& destination) +{ + checkPending(); + return destinationState[destination].acceptsPending(); +} + +void AcceptTracker::reset() +{ + destinationState.clear(); + aggregateState.unaccepted.clear(); + aggregateState.unconfirmed.clear(); + pending.clear(); +} + +void AcceptTracker::checkPending() +{ + while (!pending.empty() && pending.front().status.isComplete()) { + completed(pending.front().accepted); + pending.pop_front(); + } +} + +void AcceptTracker::completed(qpid::framing::SequenceSet& set) +{ + for (StateMap::iterator i = destinationState.begin(); i != destinationState.end(); ++i) { + i->second.completed(set); + } + aggregateState.completed(set); +} + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.h b/qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.h new file mode 100644 index 0000000000..fb58a3a8c8 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.h @@ -0,0 +1,85 @@ +#ifndef QPID_CLIENT_AMQP0_10_ACCEPTTRACKER_H +#define QPID_CLIENT_AMQP0_10_ACCEPTTRACKER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/AsyncSession.h" +#include "qpid/client/Completion.h" +#include "qpid/framing/SequenceNumber.h" +#include "qpid/framing/SequenceSet.h" +#include +#include + +namespace qpid { +namespace client { +namespace amqp0_10 { + +/** + * Tracks the set of messages requiring acceptance, and those for + * which an accept has been issued but is yet to be confirmed + * complete. + */ +class AcceptTracker +{ + public: + void delivered(const std::string& destination, const qpid::framing::SequenceNumber& id); + void accept(qpid::client::AsyncSession&); + void release(qpid::client::AsyncSession&); + uint32_t acceptsPending(); + uint32_t acceptsPending(const std::string& destination); + void reset(); + private: + struct State + { + /** + * ids of messages that have been delivered but not yet + * accepted + */ + qpid::framing::SequenceSet unaccepted; + /** + * ids of messages for which an accpet has been issued but not + * yet confirmed as completed + */ + qpid::framing::SequenceSet unconfirmed; + + void accept(); + void release(); + uint32_t acceptsPending(); + void completed(qpid::framing::SequenceSet&); + }; + typedef std::map StateMap; + struct Record + { + qpid::client::Completion status; + qpid::framing::SequenceSet accepted; + }; + typedef std::deque Records; + + State aggregateState; + StateMap destinationState; + Records pending; + + void checkPending(); + void completed(qpid::framing::SequenceSet&); +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_ACCEPTTRACKER_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.cpp b/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.cpp deleted file mode 100644 index 52b623b65c..0000000000 --- a/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.cpp +++ /dev/null @@ -1,48 +0,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. - * - */ -#include "CompletionTracker.h" - -namespace qpid { -namespace client { -namespace amqp0_10 { - -using qpid::framing::SequenceNumber; - -void CompletionTracker::track(SequenceNumber command, void* token) -{ - tokens[command] = token; -} - -void CompletionTracker::completedTo(SequenceNumber command) -{ - Tokens::iterator i = tokens.lower_bound(command); - if (i != tokens.end()) { - lastCompleted = i->second; - tokens.erase(tokens.begin(), ++i); - } -} - -void* CompletionTracker::getLastCompletedToken() -{ - return lastCompleted; -} - -}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.h b/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.h deleted file mode 100644 index 6147c5682e..0000000000 --- a/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef QPID_CLIENT_AMQP0_10_COMPLETIONTRACKER_H -#define QPID_CLIENT_AMQP0_10_COMPLETIONTRACKER_H - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include "qpid/framing/SequenceNumber.h" -#include - -namespace qpid { -namespace client { -namespace amqp0_10 { - -/** - * Provides a mapping from command ids to application supplied - * 'tokens', and is used to determine when the sending or - * acknowledging of a specific message is complete. - */ -class CompletionTracker -{ - public: - void track(qpid::framing::SequenceNumber command, void* token); - void completedTo(qpid::framing::SequenceNumber command); - void* getLastCompletedToken(); - private: - typedef std::map Tokens; - Tokens tokens; - void* lastCompleted; -}; -}}} // namespace qpid::client::amqp0_10 - -#endif /*!QPID_CLIENT_AMQP0_10_COMPLETIONTRACKER_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp index b0a16674e1..d22208368b 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp @@ -81,12 +81,31 @@ struct MatchAndTrack } } }; + +struct Match +{ + const std::string destination; + uint32_t matched; + + Match(const std::string& d) : destination(d), matched(0) {} + + bool operator()(boost::shared_ptr command) + { + if (command->as()->getDestination() == destination) { + ++matched; + return true; + } else { + return false; + } + } +}; } void IncomingMessages::setSession(qpid::client::AsyncSession s) { session = s; incoming = SessionBase_0_10Access(session).get()->getDemux().getDefault(); + acceptTracker.reset(); } bool IncomingMessages::get(Handler& handler, Duration timeout) @@ -106,8 +125,7 @@ bool IncomingMessages::get(Handler& handler, Duration timeout) void IncomingMessages::accept() { - session.messageAccept(unaccepted); - unaccepted.clear(); + acceptTracker.accept(session); } void IncomingMessages::releaseAll() @@ -121,8 +139,7 @@ void IncomingMessages::releaseAll() GetAny handler; while (process(&handler, 0)) ; //now release all messages - session.messageRelease(unaccepted); - unaccepted.clear(); + acceptTracker.release(session); } void IncomingMessages::releasePending(const std::string& destination) @@ -166,6 +183,32 @@ bool IncomingMessages::process(Handler* handler, qpid::sys::Duration duration) return false; } +uint32_t IncomingMessages::pendingAccept() +{ + return acceptTracker.acceptsPending(); +} +uint32_t IncomingMessages::pendingAccept(const std::string& destination) +{ + return acceptTracker.acceptsPending(destination); +} + +uint32_t IncomingMessages::available() +{ + //first pump all available messages from incoming to received... + while (process(0, 0)) {} + //return the count of received messages + return received.size(); +} + +uint32_t IncomingMessages::available(const std::string& destination) +{ + //first pump all available messages from incoming to received... + while (process(0, 0)) {} + + //count all messages for this destination from received list + return std::for_each(received.begin(), received.end(), Match(destination)).matched; +} + void populate(qpid::messaging::Message& message, FrameSet& command); /** @@ -180,7 +223,7 @@ void IncomingMessages::retrieve(FrameSetPtr command, qpid::messaging::Message* m } const MessageTransferBody* transfer = command->as(); if (transfer->getAcquireMode() == ACQUIRE_MODE_PRE_ACQUIRED && transfer->getAcceptMode() == ACCEPT_MODE_EXPLICIT) { - unaccepted.add(command->getId()); + acceptTracker.delivered(transfer->getDestination(), command->getId()); } session.markCompleted(command->getId(), false, false); } diff --git a/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h index 5e28877305..e84cd18892 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h +++ b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h @@ -27,6 +27,7 @@ #include "qpid/framing/SequenceSet.h" #include "qpid/sys/BlockingQueue.h" #include "qpid/sys/Time.h" +#include "qpid/client/amqp0_10/AcceptTracker.h" namespace qpid { @@ -74,13 +75,19 @@ class IncomingMessages void accept(); void releaseAll(); void releasePending(const std::string& destination); + + uint32_t pendingAccept(); + uint32_t pendingAccept(const std::string& destination); + + uint32_t available(); + uint32_t available(const std::string& destination); private: typedef std::deque FrameSetQueue; qpid::client::AsyncSession session; - qpid::framing::SequenceSet unaccepted; boost::shared_ptr< sys::BlockingQueue > incoming; FrameSetQueue received; + AcceptTracker acceptTracker; bool process(Handler*, qpid::sys::Duration); void retrieve(FrameSetPtr, qpid::messaging::Message*); diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp index 31efff38a6..da91c4a160 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp @@ -120,6 +120,21 @@ qpid::messaging::MessageListener* ReceiverImpl::getListener() { return listener; const std::string& ReceiverImpl::getName() const { return destination; } +uint32_t ReceiverImpl::getCapacity() +{ + return capacity; +} + +uint32_t ReceiverImpl::available() +{ + return parent.available(destination); +} + +uint32_t ReceiverImpl::pendingAck() +{ + return parent.pendingAck(destination); +} + ReceiverImpl::ReceiverImpl(SessionImpl& p, const std::string& name, const qpid::messaging::Address& a, const qpid::messaging::Filter* f, diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h index 509c784513..b941348fc8 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h +++ b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h @@ -62,6 +62,9 @@ class ReceiverImpl : public qpid::messaging::ReceiverImpl void stop(); const std::string& getName() const; void setCapacity(uint32_t); + uint32_t getCapacity(); + uint32_t available(); + uint32_t pendingAck(); void setListener(qpid::messaging::MessageListener* listener); qpid::messaging::MessageListener* getListener(); void received(qpid::messaging::Message& message); diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp index c619d1226a..4cd2dc0521 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp @@ -32,11 +32,12 @@ SenderImpl::SenderImpl(SessionImpl& _parent, const std::string& _name, const qpid::messaging::Address& _address, const qpid::messaging::Variant::Map& _options) : parent(_parent), name(_name), address(_address), options(_options), state(UNRESOLVED), - capacity(50), window(0) {} + capacity(50), window(0), flushed(false) {} -void SenderImpl::send(const qpid::messaging::Message& m) +void SenderImpl::send(const qpid::messaging::Message& message) { - execute1(&m); + Send f(*this, &message); + while (f.repeat) parent.execute(f); } void SenderImpl::cancel() @@ -44,6 +45,20 @@ void SenderImpl::cancel() execute(); } +void SenderImpl::setCapacity(uint32_t c) +{ + bool flush = c < capacity; + capacity = c; + execute1(flush); +} +uint32_t SenderImpl::getCapacity() { return capacity; } +uint32_t SenderImpl::pending() +{ + CheckPendingSends f(*this, false); + parent.execute(f); + return f.pending; +} + void SenderImpl::init(qpid::client::AsyncSession s, AddressResolution& resolver) { session = s; @@ -60,18 +75,31 @@ void SenderImpl::init(qpid::client::AsyncSession s, AddressResolution& resolver) } } +void SenderImpl::waitForCapacity() +{ + //TODO: add option to throw exception rather than blocking? + if (capacity <= (flushed ? checkPendingSends(false) : outgoing.size())) { + //Initial implementation is very basic. As outgoing is + //currently only reduced on receiving completions and we are + //blocking anyway we may as well sync(). If successful that + //should clear all outstanding sends. + session.sync(); + checkPendingSends(false); + } + //flush periodically and check for conmpleted sends + if (++window > (capacity / 4)) {//TODO: make this configurable? + checkPendingSends(true); + window = 0; + } +} + void SenderImpl::sendImpl(const qpid::messaging::Message& m) { - //TODO: make recoding for replay optional + //TODO: make recording for replay optional (would still want to track completion however) std::auto_ptr msg(new OutgoingMessage()); msg->convert(m); outgoing.push_back(msg.release()); sink->send(session, name, outgoing.back()); - if (++window > (capacity / 2)) {//TODO: make this configurable? - session.flush(); - checkPendingSends(); - window = 0; - } } void SenderImpl::replay() @@ -81,11 +109,18 @@ void SenderImpl::replay() } } -void SenderImpl::checkPendingSends() +uint32_t SenderImpl::checkPendingSends(bool flush) { + if (flush) { + session.flush(); + flushed = true; + } else { + flushed = false; + } while (!outgoing.empty() && outgoing.front().status.isComplete()) { outgoing.pop_front(); } + return outgoing.size(); } void SenderImpl::cancelImpl() diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h index 4ba793d71c..028d26bda7 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h +++ b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h @@ -51,6 +51,9 @@ class SenderImpl : public qpid::messaging::SenderImpl const qpid::messaging::Variant::Map& options); void send(const qpid::messaging::Message&); void cancel(); + void setCapacity(uint32_t); + uint32_t getCapacity(); + uint32_t pending(); void init(qpid::client::AsyncSession, AddressResolution&); private: @@ -69,14 +72,17 @@ class SenderImpl : public qpid::messaging::SenderImpl OutgoingMessages outgoing; uint32_t capacity; uint32_t window; + bool flushed; - void checkPendingSends(); + uint32_t checkPendingSends(bool flush); void replay(); + void waitForCapacity(); //logic for application visible methods: void sendImpl(const qpid::messaging::Message&); void cancelImpl(); + //functors for application visible methods (allowing locking and //retry to be centralised): struct Command @@ -89,9 +95,17 @@ class SenderImpl : public qpid::messaging::SenderImpl struct Send : Command { const qpid::messaging::Message* message; - - Send(SenderImpl& i, const qpid::messaging::Message* m) : Command(i), message(m) {} - void operator()() { impl.sendImpl(*message); } + bool repeat; + + Send(SenderImpl& i, const qpid::messaging::Message* m) : Command(i), message(m), repeat(true) {} + void operator()() + { + impl.waitForCapacity(); + //from this point message will be recorded if there is any + //failure (and replayed) so need not repeat the call + repeat = false; + impl.sendImpl(*message); + } }; struct Cancel : Command @@ -100,6 +114,14 @@ class SenderImpl : public qpid::messaging::SenderImpl void operator()() { impl.cancelImpl(); } }; + struct CheckPendingSends : Command + { + bool flush; + uint32_t pending; + CheckPendingSends(SenderImpl& i, bool f) : Command(i), flush(f), pending(0) {} + void operator()() { pending = impl.checkPendingSends(flush); } + }; + //helper templates for some common patterns template void execute() { @@ -107,10 +129,10 @@ class SenderImpl : public qpid::messaging::SenderImpl parent.execute(f); } - template void execute1(P p) + template bool execute1(P p) { F f(*this, p); - parent.execute(f); + return parent.execute(f); } }; }}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp index 0e6c430d89..bc6289d84b 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp @@ -298,6 +298,61 @@ bool SessionImpl::fetch(qpid::messaging::Message& message, qpid::sys::Duration t } } +uint32_t SessionImpl::available() +{ + return get1((const std::string*) 0); +} +uint32_t SessionImpl::available(const std::string& destination) +{ + return get1(&destination); +} + +struct SessionImpl::Available : Command +{ + const std::string* destination; + uint32_t result; + + Available(SessionImpl& i, const std::string* d) : Command(i), destination(d), result(0) {} + void operator()() { result = impl.availableImpl(destination); } +}; + +uint32_t SessionImpl::availableImpl(const std::string* destination) +{ + if (destination) { + return incoming.available(*destination); + } else { + return incoming.available(); + } +} + +uint32_t SessionImpl::pendingAck() +{ + return get1((const std::string*) 0); +} + +uint32_t SessionImpl::pendingAck(const std::string& destination) +{ + return get1(&destination); +} + +struct SessionImpl::PendingAck : Command +{ + const std::string* destination; + uint32_t result; + + PendingAck(SessionImpl& i, const std::string* d) : Command(i), destination(d), result(0) {} + void operator()() { result = impl.pendingAckImpl(destination); } +}; + +uint32_t SessionImpl::pendingAckImpl(const std::string* destination) +{ + if (destination) { + return incoming.pendingAccept(*destination); + } else { + return incoming.pendingAccept(); + } +} + void SessionImpl::syncImpl() { session.sync(); diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h index 1c7db17bbb..b453f3f08f 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h +++ b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h @@ -83,6 +83,12 @@ class SessionImpl : public qpid::messaging::SessionImpl void receiverCancelled(const std::string& name); void senderCancelled(const std::string& name); + uint32_t available(); + uint32_t available(const std::string& destination); + + uint32_t pendingAck(); + uint32_t pendingAck(const std::string& destination); + void setSession(qpid::client::Session); template bool execute(T& f) @@ -128,6 +134,8 @@ class SessionImpl : public qpid::messaging::SessionImpl qpid::messaging::Receiver createReceiverImpl(const qpid::messaging::Address& address, const qpid::messaging::Filter* filter, const qpid::messaging::VariantMap& options); + uint32_t availableImpl(const std::string* destination); + uint32_t pendingAckImpl(const std::string* destination); //functors for public facing methods (allows locking and retry //logic to be centralised) @@ -178,6 +186,8 @@ class SessionImpl : public qpid::messaging::SessionImpl struct CreateSender; struct CreateReceiver; + struct PendingAck; + struct Available; //helper templates for some common patterns template bool execute() @@ -196,6 +206,13 @@ class SessionImpl : public qpid::messaging::SessionImpl F f(*this, p); return execute(f); } + + template R get1(P p) + { + F f(*this, p); + while (!execute(f)) {} + return f.result; + } }; }}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/cluster/Connection.cpp b/qpid/cpp/src/qpid/cluster/Connection.cpp index 4cc977d14a..6873827b81 100644 --- a/qpid/cpp/src/qpid/cluster/Connection.cpp +++ b/qpid/cpp/src/qpid/cluster/Connection.cpp @@ -156,8 +156,17 @@ bool Connection::checkUnsupported(const AMQBody& body) { return !message.empty(); } +struct GiveReadCreditOnExit { + Connection& connection; + int credit; + GiveReadCreditOnExit(Connection& connection_, int credit_) : + connection(connection_), credit(credit_) {} + ~GiveReadCreditOnExit() { connection.giveReadCredit(credit); } +}; + // Called in delivery thread, in cluster order. void Connection::deliveredFrame(const EventFrame& f) { + GiveReadCreditOnExit gc(*this, f.readCredit); assert(!catchUp); currentChannel = f.frame.getChannel(); if (f.frame.getBody() // frame can be emtpy with just readCredit @@ -171,7 +180,6 @@ void Connection::deliveredFrame(const EventFrame& f) { if (ss) ss->out(const_cast(f.frame)); } } - giveReadCredit(f.readCredit); } // A local connection is closed by the network layer. diff --git a/qpid/cpp/src/qpid/cluster/Quorum_cman.cpp b/qpid/cpp/src/qpid/cluster/Quorum_cman.cpp index 277adaf7b1..507d9649b9 100644 --- a/qpid/cpp/src/qpid/cluster/Quorum_cman.cpp +++ b/qpid/cpp/src/qpid/cluster/Quorum_cman.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -33,8 +33,8 @@ namespace { boost::function errorFn; -void cmanCallbackFn(cman_handle_t handle, void */*privdata*/, int reason, int arg) { - if (reason == CMAN_REASON_STATECHANGE && arg == 0) { +void cmanCallbackFn(cman_handle_t handle, void */*privdata*/, int reason, int /*arg*/) { + if (reason == CMAN_REASON_STATECHANGE && !cman_is_quorate(handle)) { QPID_LOG(critical, "Lost contact with cluster quorum."); if (errorFn) errorFn(); cman_stop_notification(handle); diff --git a/qpid/cpp/src/qpid/messaging/Address.cpp b/qpid/cpp/src/qpid/messaging/Address.cpp index ed35054a00..813a8e1377 100644 --- a/qpid/cpp/src/qpid/messaging/Address.cpp +++ b/qpid/cpp/src/qpid/messaging/Address.cpp @@ -21,9 +21,6 @@ #include "qpid/messaging/Address.h" namespace qpid { -namespace client { -} - namespace messaging { Address::Address() {} diff --git a/qpid/cpp/src/qpid/messaging/Receiver.cpp b/qpid/cpp/src/qpid/messaging/Receiver.cpp index 2e8b89d27f..3290ea98ac 100644 --- a/qpid/cpp/src/qpid/messaging/Receiver.cpp +++ b/qpid/cpp/src/qpid/messaging/Receiver.cpp @@ -45,6 +45,9 @@ Message Receiver::fetch(qpid::sys::Duration timeout) { return impl->fetch(timeou void Receiver::start() { impl->start(); } void Receiver::stop() { impl->stop(); } void Receiver::setCapacity(uint32_t c) { impl->setCapacity(c); } +uint32_t Receiver::getCapacity() { return impl->getCapacity(); } +uint32_t Receiver::available() { return impl->available(); } +uint32_t Receiver::pendingAck() { return impl->pendingAck(); } void Receiver::cancel() { impl->cancel(); } void Receiver::setListener(MessageListener* listener) { impl->setListener(listener); } diff --git a/qpid/cpp/src/qpid/messaging/ReceiverImpl.h b/qpid/cpp/src/qpid/messaging/ReceiverImpl.h index 77697b730c..7db20acc29 100644 --- a/qpid/cpp/src/qpid/messaging/ReceiverImpl.h +++ b/qpid/cpp/src/qpid/messaging/ReceiverImpl.h @@ -44,6 +44,9 @@ class ReceiverImpl : public virtual qpid::RefCounted virtual void start() = 0; virtual void stop() = 0; virtual void setCapacity(uint32_t) = 0; + virtual uint32_t getCapacity() = 0; + virtual uint32_t available() = 0; + virtual uint32_t pendingAck() = 0; virtual void cancel() = 0; virtual void setListener(MessageListener*) = 0; }; diff --git a/qpid/cpp/src/qpid/messaging/Sender.cpp b/qpid/cpp/src/qpid/messaging/Sender.cpp index 8db700b060..62b2944701 100644 --- a/qpid/cpp/src/qpid/messaging/Sender.cpp +++ b/qpid/cpp/src/qpid/messaging/Sender.cpp @@ -40,5 +40,8 @@ Sender::~Sender() { PI::dtor(*this); } Sender& Sender::operator=(const Sender& s) { return PI::assign(*this, s); } void Sender::send(const Message& message) { impl->send(message); } void Sender::cancel() { impl->cancel(); } +void Sender::setCapacity(uint32_t c) { impl->setCapacity(c); } +uint32_t Sender::getCapacity() { return impl->getCapacity(); } +uint32_t Sender::pending() { return impl->pending(); } }} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/SenderImpl.h b/qpid/cpp/src/qpid/messaging/SenderImpl.h index 77d2cfaeaf..fa3794ca4e 100644 --- a/qpid/cpp/src/qpid/messaging/SenderImpl.h +++ b/qpid/cpp/src/qpid/messaging/SenderImpl.h @@ -37,6 +37,9 @@ class SenderImpl : public virtual qpid::RefCounted virtual ~SenderImpl() {} virtual void send(const Message& message) = 0; virtual void cancel() = 0; + virtual void setCapacity(uint32_t) = 0; + virtual uint32_t getCapacity() = 0; + virtual uint32_t pending() = 0; private: }; }} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/Session.cpp b/qpid/cpp/src/qpid/messaging/Session.cpp index 284b20dacc..62b1ca0dcf 100644 --- a/qpid/cpp/src/qpid/messaging/Session.cpp +++ b/qpid/cpp/src/qpid/messaging/Session.cpp @@ -103,15 +103,7 @@ bool Session::dispatch(qpid::sys::Duration timeout) { return impl->dispatch(timeout); } - -void* Session::getLastConfirmedSent() -{ - return impl->getLastConfirmedSent(); -} - -void* Session::getLastConfirmedAcknowledged() -{ - return impl->getLastConfirmedAcknowledged(); -} +uint32_t Session::available() { return impl->available(); } +uint32_t Session::pendingAck() { return impl->pendingAck(); } }} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/SessionImpl.h b/qpid/cpp/src/qpid/messaging/SessionImpl.h index 9b122a24bc..0933cea9c8 100644 --- a/qpid/cpp/src/qpid/messaging/SessionImpl.h +++ b/qpid/cpp/src/qpid/messaging/SessionImpl.h @@ -56,8 +56,8 @@ class SessionImpl : public virtual qpid::RefCounted virtual Sender createSender(const Address& address, const VariantMap& options) = 0; virtual Receiver createReceiver(const Address& address, const VariantMap& options) = 0; virtual Receiver createReceiver(const Address& address, const Filter& filter, const VariantMap& options) = 0; - virtual void* getLastConfirmedSent() = 0; - virtual void* getLastConfirmedAcknowledged() = 0; + virtual uint32_t available() = 0; + virtual uint32_t pendingAck() = 0; private: }; }} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp b/qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp index 52208d0519..9da6c835ce 100644 --- a/qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp +++ b/qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -45,6 +45,9 @@ using qpid::sys::Duration; using qpid::sys::TIME_SEC; using qpid::sys::TIME_INFINITE; +namespace qpid { +namespace tests { + // count of messages int64_t smsgs = 0; int64_t sbytes = 0; @@ -144,6 +147,10 @@ void rejected(boost::shared_ptr p, Rdma::Connection::intrusive_ptr&, con p->shutdown(); } +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char* argv[]) { vector args(&argv[0], &argv[argc]); diff --git a/qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp b/qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp index 1ab5268596..07d6379362 100644 --- a/qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp +++ b/qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -39,6 +39,9 @@ using qpid::sys::Poller; using qpid::sys::Dispatcher; // All the accepted connections +namespace qpid { +namespace tests { + struct ConRec { Rdma::Connection::intrusive_ptr connection; Rdma::AsynchIO* data; @@ -134,6 +137,10 @@ void connected(Poller::shared_ptr poller, Rdma::Connection::intrusive_ptr& ci) { cr->data->start(poller); } +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char* argv[]) { vector args(&argv[0], &argv[argc]); diff --git a/qpid/cpp/src/qpid/xml/XmlExchange.cpp b/qpid/cpp/src/qpid/xml/XmlExchange.cpp index a41c8840ff..8a1ef6149e 100644 --- a/qpid/cpp/src/qpid/xml/XmlExchange.cpp +++ b/qpid/cpp/src/qpid/xml/XmlExchange.cpp @@ -156,7 +156,7 @@ bool XmlExchange::matches(Query& query, Deliverable& msg, const qpid::framing::F // This will parse the document using either Xerces or FastXDM, depending // on your XQilla configuration. FastXDM can be as much as 10x faster. - + Sequence seq(context->parseDocument(xml)); if(!seq.isEmpty() && seq.first()->isNode()) { @@ -206,11 +206,11 @@ void XmlExchange::route(Deliverable& msg, const string& routingKey, const FieldT PreRoute pr(msg, this); try { XmlBinding::vector::ConstPtr p; - { + { RWlock::ScopedRlock l(lock); - p = bindingsMap[routingKey].snapshot(); - if (!p) return; - } + p = bindingsMap[routingKey].snapshot(); + if (!p) return; + } int count(0); for (std::vector::const_iterator i = p->begin(); i != p->end(); i++) { @@ -222,24 +222,24 @@ void XmlExchange::route(Deliverable& msg, const string& routingKey, const FieldT if ((*i)->mgmtBinding != 0) (*i)->mgmtBinding->inc_msgMatched (); } - } - if (!count) { - QPID_LOG(warning, "XMLExchange " << getName() << ": could not route message with query " << routingKey); - if (mgmtExchange != 0) { - mgmtExchange->inc_msgDrops (); - mgmtExchange->inc_byteDrops (msg.contentSize ()); - } - } else { - if (mgmtExchange != 0) { - mgmtExchange->inc_msgRoutes (count); - mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); - } - } - - if (mgmtExchange != 0) { - mgmtExchange->inc_msgReceives (); - mgmtExchange->inc_byteReceives (msg.contentSize ()); - } + } + if (!count) { + QPID_LOG(warning, "XMLExchange " << getName() << ": could not route message with query " << routingKey); + if (mgmtExchange != 0) { + mgmtExchange->inc_msgDrops (); + mgmtExchange->inc_byteDrops (msg.contentSize ()); + } + } else { + if (mgmtExchange != 0) { + mgmtExchange->inc_msgRoutes (count); + mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); + } + } + + if (mgmtExchange != 0) { + mgmtExchange->inc_msgReceives (); + mgmtExchange->inc_byteReceives (msg.contentSize ()); + } } catch (...) { QPID_LOG(warning, "XMLExchange " << getName() << ": exception routing message with query " << routingKey); } diff --git a/qpid/cpp/src/tests/AccumulatedAckTest.cpp b/qpid/cpp/src/tests/AccumulatedAckTest.cpp index 028ce71907..c736a519d2 100644 --- a/qpid/cpp/src/tests/AccumulatedAckTest.cpp +++ b/qpid/cpp/src/tests/AccumulatedAckTest.cpp @@ -8,9 +8,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -28,6 +28,9 @@ using std::list; using namespace qpid::framing; +namespace qpid { +namespace tests { + bool covers(const AccumulatedAck& ack, int i) { return ack.covers(SequenceNumber(i)); @@ -97,7 +100,7 @@ QPID_AUTO_TEST_CASE(testUpdateFromCompletionData) ack.update(mark, ranges); - for(int i = 0; i <= 15; i++) { + for(int i = 0; i <= 15; i++) { BOOST_CHECK(covers(ack, i)); } BOOST_CHECK(!covers(ack, 16)); @@ -221,7 +224,7 @@ QPID_AUTO_TEST_CASE(testConsolidation4) ack.update(SequenceNumber(9), SequenceNumber(9)); ack.update(SequenceNumber(3), SequenceNumber(4)); - for(int i = 0; i <= 15; i++) { + for(int i = 0; i <= 15; i++) { BOOST_CHECK(covers(ack, i)); } BOOST_CHECK(!covers(ack, 16)); @@ -230,3 +233,5 @@ QPID_AUTO_TEST_CASE(testConsolidation4) QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/Array.cpp b/qpid/cpp/src/tests/Array.cpp index c779cbe901..7622b89d15 100644 --- a/qpid/cpp/src/tests/Array.cpp +++ b/qpid/cpp/src/tests/Array.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -25,6 +25,9 @@ #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ArrayTestSuite) using namespace qpid::framing; @@ -69,7 +72,7 @@ QPID_AUTO_TEST_CASE(testArrayAssignment) Array a(data); b = a; BOOST_CHECK_EQUAL(a, b); - } + } std::vector data2; b.collect(data2); //BOOST_CHECK_EQUAL(data, data2); @@ -77,3 +80,5 @@ QPID_AUTO_TEST_CASE(testArrayAssignment) } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/AsyncCompletion.cpp b/qpid/cpp/src/tests/AsyncCompletion.cpp index 41423d8245..4492e6b6bc 100644 --- a/qpid/cpp/src/tests/AsyncCompletion.cpp +++ b/qpid/cpp/src/tests/AsyncCompletion.cpp @@ -49,10 +49,13 @@ using boost::intrusive_ptr; * message enqueues at the correct time. */ +namespace qpid { +namespace tests { + class AsyncCompletionMessageStore : public NullMessageStore { public: sys::BlockingQueue > enqueued; - + AsyncCompletionMessageStore() : NullMessageStore() {} ~AsyncCompletionMessageStore(){} @@ -82,10 +85,10 @@ QPID_AUTO_TEST_CASE(testWaitTillComplete) { transfers[i] = s.messageTransfer(arg::content=msg); } - // Get hold of the broker-side messages. + // Get hold of the broker-side messages. typedef vector > BrokerMessages; BrokerMessages enqueued; - for (int j = 0; j < count; ++j) + for (int j = 0; j < count; ++j) enqueued.push_back(store->enqueued.pop(TIME_SEC)); // Send a sync, make sure it does not complete till all messages are complete. @@ -111,3 +114,5 @@ QPID_AUTO_TEST_CASE(testGetResult) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/AtomicValue.cpp b/qpid/cpp/src/tests/AtomicValue.cpp index 05083ad177..d855d993a7 100644 --- a/qpid/cpp/src/tests/AtomicValue.cpp +++ b/qpid/cpp/src/tests/AtomicValue.cpp @@ -21,6 +21,9 @@ #include "test_tools.h" #include "qpid/sys/AtomicValue.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(AtomicValueTestSuite) QPID_AUTO_TEST_CASE(test) { @@ -47,3 +50,5 @@ QPID_AUTO_TEST_CASE(test) { QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/BrokerFixture.h b/qpid/cpp/src/tests/BrokerFixture.h index 397045d00b..bb985cf7e1 100644 --- a/qpid/cpp/src/tests/BrokerFixture.h +++ b/qpid/cpp/src/tests/BrokerFixture.h @@ -10,9 +10,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -35,6 +35,9 @@ #include "qpid/sys/Thread.h" #include +namespace qpid { +namespace tests { + /** * A fixture with an in-process broker. */ @@ -55,7 +58,7 @@ struct BrokerFixture : private boost::noncopyable { } opts.port=0; // Management doesn't play well with multiple in-process brokers. - opts.enableMgmt=false; + opts.enableMgmt=false; opts.workerThreads=1; opts.dataDir=""; opts.auth=false; @@ -144,5 +147,6 @@ struct SessionFixtureT : BrokerFixture, ClientT { typedef SessionFixtureT SessionFixture; typedef SessionFixtureT ProxySessionFixture; +}} // namespace qpid::tests #endif /*!TESTS_BROKERFIXTURE_H*/ diff --git a/qpid/cpp/src/tests/ClientMessageTest.cpp b/qpid/cpp/src/tests/ClientMessageTest.cpp index bc0945674f..f925f1c234 100644 --- a/qpid/cpp/src/tests/ClientMessageTest.cpp +++ b/qpid/cpp/src/tests/ClientMessageTest.cpp @@ -24,6 +24,9 @@ using namespace qpid::client; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ClientMessageTestSuite) QPID_AUTO_TEST_CASE(MessageCopyAssign) { @@ -33,7 +36,7 @@ QPID_AUTO_TEST_CASE(MessageCopyAssign) { Message c(m); BOOST_CHECK_EQUAL("foo", c.getData()); Message a; - BOOST_CHECK_EQUAL("", a.getData()); + BOOST_CHECK_EQUAL("", a.getData()); a = m; BOOST_CHECK_EQUAL("foo", a.getData()); a.setData("a"); @@ -44,3 +47,5 @@ QPID_AUTO_TEST_CASE(MessageCopyAssign) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/ClientSessionTest.cpp b/qpid/cpp/src/tests/ClientSessionTest.cpp index 3ed7491f7d..6ca0aa6d44 100644 --- a/qpid/cpp/src/tests/ClientSessionTest.cpp +++ b/qpid/cpp/src/tests/ClientSessionTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -40,6 +40,9 @@ #include +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ClientSessionTest) using namespace qpid::client; @@ -122,12 +125,12 @@ QPID_AUTO_TEST_CASE(testDispatcher) ClientSessionFixture fix; fix.session =fix.connection.newSession(); size_t count = 100; - for (size_t i = 0; i < count; ++i) + for (size_t i = 0; i < count; ++i) fix.session.messageTransfer(arg::content=Message(boost::lexical_cast(i), "my-queue")); DummyListener listener(fix.session, "my-queue", count); listener.run(); - BOOST_CHECK_EQUAL(count, listener.messages.size()); - for (size_t i = 0; i < count; ++i) + BOOST_CHECK_EQUAL(count, listener.messages.size()); + for (size_t i = 0; i < count; ++i) BOOST_CHECK_EQUAL(boost::lexical_cast(i), listener.messages[i].getData()); } @@ -142,8 +145,8 @@ QPID_AUTO_TEST_CASE(testDispatcherThread) fix.session.messageTransfer(arg::content=Message(boost::lexical_cast(i), "my-queue")); } t.join(); - BOOST_CHECK_EQUAL(count, listener.messages.size()); - for (size_t i = 0; i < count; ++i) + BOOST_CHECK_EQUAL(count, listener.messages.size()); + for (size_t i = 0; i < count; ++i) BOOST_CHECK_EQUAL(boost::lexical_cast(i), listener.messages[i].getData()); } @@ -215,7 +218,7 @@ QPID_AUTO_TEST_CASE(testLocalQueue) { BOOST_CHECK_EQUAL("foo1", lq.pop().getData()); BOOST_CHECK(lq.empty()); // Credit exhausted. fix.subs.getSubscription("lq").setFlowControl(FlowControl::unlimited()); - BOOST_CHECK_EQUAL("foo2", lq.pop().getData()); + BOOST_CHECK_EQUAL("foo2", lq.pop().getData()); } struct DelayedTransfer : sys::Runnable @@ -246,7 +249,7 @@ QPID_AUTO_TEST_CASE(testGet) { Thread t(sender); //test timed get where message shows up after a short delay BOOST_CHECK(fix.subs.get(got, "getq", 5*TIME_SEC)); - BOOST_CHECK_EQUAL("foo2", got.getData()); + BOOST_CHECK_EQUAL("foo2", got.getData()); t.join(); } @@ -271,8 +274,8 @@ QPID_AUTO_TEST_CASE(testPeriodicExpiration) { ClientSessionFixture fix(opts); fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true); - for (uint i = 0; i < 10; i++) { - Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); + for (uint i = 0; i < 10; i++) { + Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); if (i % 2) m.getDeliveryProperties().setTtl(500); fix.session.messageTransfer(arg::content=m); } @@ -286,15 +289,15 @@ QPID_AUTO_TEST_CASE(testExpirationOnPop) { ClientSessionFixture fix; fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true); - for (uint i = 0; i < 10; i++) { - Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); + for (uint i = 0; i < 10; i++) { + Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); if (i % 2) m.getDeliveryProperties().setTtl(200); fix.session.messageTransfer(arg::content=m); } qpid::sys::usleep(300* 1000); - for (uint i = 0; i < 10; i++) { + for (uint i = 0; i < 10; i++) { if (i % 2) continue; Message m; BOOST_CHECK(fix.subs.get(m, "my-queue", TIME_SEC)); @@ -306,8 +309,8 @@ QPID_AUTO_TEST_CASE(testRelease) { ClientSessionFixture fix; const uint count=10; - for (uint i = 0; i < count; i++) { - Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); + for (uint i = 0; i < count; i++) { + Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); fix.session.messageTransfer(arg::content=m); } @@ -334,7 +337,7 @@ QPID_AUTO_TEST_CASE(testRelease) { for (uint i = 0; i < count; i++) { BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), l2.messages[i].getData()); } - + fix.subs.stop(); fix.subs.wait(); fix.session.close(); @@ -344,8 +347,8 @@ QPID_AUTO_TEST_CASE(testCompleteOnAccept) { ClientSessionFixture fix; const uint count = 8; const uint chunk = 4; - for (uint i = 0; i < count; i++) { - Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); + for (uint i = 0; i < count; i++) { + Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); fix.session.messageTransfer(arg::content=m); } @@ -358,25 +361,25 @@ QPID_AUTO_TEST_CASE(testCompleteOnAccept) { Subscription s = fix.subs.subscribe(q, "my-queue", settings); fix.session.messageFlush(arg::destination=s.getName()); SequenceSet accepted; - for (uint i = 0; i < chunk; i++) { + for (uint i = 0; i < chunk; i++) { Message m; BOOST_CHECK(q.get(m)); BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData()); accepted.add(m.getId()); - } + } Message m; BOOST_CHECK(!q.get(m)); - + s.accept(accepted); fix.session.messageFlush(arg::destination=s.getName()); accepted.clear(); - - for (uint i = chunk; i < count; i++) { + + for (uint i = chunk; i < count; i++) { Message m; BOOST_CHECK(q.get(m)); BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData()); accepted.add(m.getId()); - } + } fix.session.messageAccept(accepted); } @@ -424,7 +427,7 @@ QPID_AUTO_TEST_CASE(testConcurrentSenders) connection.open(settings); AsyncSession session = connection.newSession(); Message message(string(512, 'X')); - + boost::ptr_vector publishers; for (size_t i = 0; i < 5; i++) { publishers.push_back(new Publisher(connection, message, 100)); @@ -447,7 +450,7 @@ QPID_AUTO_TEST_CASE(testExclusiveSubscribe) ScopedSuppressLogging sl; BOOST_CHECK_THROW(fix.subs.subscribe(q, "myq", "second"), ResourceLockedException); ; - + } QPID_AUTO_TEST_CASE(testExclusiveBinding) { @@ -478,7 +481,7 @@ QPID_AUTO_TEST_CASE(testResubscribeWithLocalQueue) { fix.subs.subscribe(p, "some-queue"); fix.subs.cancel("some-queue"); fix.subs.subscribe(q, "some-queue"); - + fix.session.messageTransfer(arg::content=Message("some-data", "some-queue")); fix.session.messageFlush(arg::destination="some-queue"); @@ -542,10 +545,10 @@ QPID_AUTO_TEST_CASE(testLVQVariedSize) { std::ostringstream data; size_t size = 100 - ((i % 10) * 10); data << std::string(size, 'x'); - + Message m(data.str(), queue); m.getHeaders().setString(key, "abc"); - fix.session.messageTransfer(arg::content=m); + fix.session.messageTransfer(arg::content=m); } } @@ -594,7 +597,7 @@ QPID_AUTO_TEST_CASE(testExpirationNotAltered) { ClientSessionFixture fix; fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true); - Message m("my-message", "my-queue"); + Message m("my-message", "my-queue"); m.getDeliveryProperties().setTtl(60000); m.getDeliveryProperties().setExpiration(12345); fix.session.messageTransfer(arg::content=m); @@ -606,4 +609,4 @@ QPID_AUTO_TEST_CASE(testExpirationNotAltered) { QPID_AUTO_TEST_SUITE_END() - +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/ClusterFailover.cpp b/qpid/cpp/src/tests/ClusterFailover.cpp index 9ce9c4a36b..c2fb1282b1 100644 --- a/qpid/cpp/src/tests/ClusterFailover.cpp +++ b/qpid/cpp/src/tests/ClusterFailover.cpp @@ -32,6 +32,9 @@ #include #include +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ClusterFailoverTestSuite) using namespace std; @@ -60,3 +63,5 @@ QPID_AUTO_TEST_CASE(testReconnectSameSessionName) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/ClusterFixture.cpp b/qpid/cpp/src/tests/ClusterFixture.cpp index e12106c464..7c357c3cd1 100644 --- a/qpid/cpp/src/tests/ClusterFixture.cpp +++ b/qpid/cpp/src/tests/ClusterFixture.cpp @@ -61,6 +61,9 @@ using boost::assign::list_of; #include "ClusterFixture.h" +namespace qpid { +namespace tests { + ClusterFixture::ClusterFixture(size_t n, const Args& args_, int localIndex_) : name(Uuid(true).str()), localIndex(localIndex_), userArgs(args_) { @@ -152,3 +155,5 @@ std::set knownBrokerPorts(qpid::client::Connection& source, int n) { s.insert((*i)[0].get()->port); return s; } + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/ClusterFixture.h b/qpid/cpp/src/tests/ClusterFixture.h index 08b314499e..5952cc1736 100644 --- a/qpid/cpp/src/tests/ClusterFixture.h +++ b/qpid/cpp/src/tests/ClusterFixture.h @@ -60,6 +60,9 @@ using qpid::broker::Broker; using boost::shared_ptr; using qpid::cluster::Cluster; +namespace qpid { +namespace tests { + /** Cluster fixture is a vector of ports for the replicas. * * At most one replica (by default replica 0) is in the current @@ -107,4 +110,6 @@ class ClusterFixture : public vector { */ std::set knownBrokerPorts(qpid::client::Connection& source, int n=-1); +}} // namespace qpid::tests + #endif /*!CLUSTER_FIXTURE_H*/ diff --git a/qpid/cpp/src/tests/ConnectionOptions.h b/qpid/cpp/src/tests/ConnectionOptions.h index cf86894235..6fd6c2c63f 100644 --- a/qpid/cpp/src/tests/ConnectionOptions.h +++ b/qpid/cpp/src/tests/ConnectionOptions.h @@ -10,9 +10,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -25,6 +25,8 @@ #include "qpid/client/ConnectionSettings.h" #include "qpid/Options.h" +namespace qpid { + /** * Options parser for ConnectionOptions. */ @@ -35,9 +37,9 @@ struct ConnectionOptions : public qpid::Options, { using namespace qpid; addOptions() - ("broker,b", optValue(host, "HOST"), "Broker host to connect to") + ("broker,b", optValue(host, "HOST"), "Broker host to connect to") ("port,p", optValue(port, "PORT"), "Broker port to connect to") - ("protocol,P", optValue(protocol, "tcp|rdma"), "Protocol to use for broker connection") + ("protocol,P", optValue(protocol, "tcp|rdma"), "Protocol to use for broker connection") ("virtualhost,v", optValue(virtualhost, "VHOST"), "virtual host") ("username", optValue(username, "USER"), "user name for broker log in.") ("password", optValue(password, "PASSWORD"), "password for broker log in.") @@ -46,7 +48,7 @@ struct ConnectionOptions : public qpid::Options, ("max-channels", optValue(maxChannels, "N"), "the maximum number of channels the client requires.") ("heartbeat", optValue(heartbeat, "N"), "Desired heartbeat interval in seconds.") ("max-frame-size", optValue(maxFrameSize, "N"), "the maximum frame size to request.") - ("bounds-multiplier", optValue(bounds, "N"), + ("bounds-multiplier", optValue(bounds, "N"), "bound size of write queue (as a multiple of the max frame size).") ("tcp-nodelay", optValue(tcpNoDelay), "Turn on tcp-nodelay") ("service", optValue(service, "SERVICE-NAME"), "SASL service name.") @@ -55,4 +57,6 @@ struct ConnectionOptions : public qpid::Options, } }; +} // namespace qpid + #endif /*!QPID_CLIENT_CONNECTIONOPTIONS_H*/ diff --git a/qpid/cpp/src/tests/ConsoleTest.cpp b/qpid/cpp/src/tests/ConsoleTest.cpp index 1d55b13f3c..107472ed9e 100644 --- a/qpid/cpp/src/tests/ConsoleTest.cpp +++ b/qpid/cpp/src/tests/ConsoleTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -23,6 +23,9 @@ #include "qpid/console/ClassKey.h" #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ConsoleTestSuite) using namespace qpid::framing; @@ -40,4 +43,4 @@ QPID_AUTO_TEST_CASE(testClassKey) { QPID_AUTO_TEST_SUITE_END() - +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/DeliveryRecordTest.cpp b/qpid/cpp/src/tests/DeliveryRecordTest.cpp index 8ff7ad3584..17f9a0d148 100644 --- a/qpid/cpp/src/tests/DeliveryRecordTest.cpp +++ b/qpid/cpp/src/tests/DeliveryRecordTest.cpp @@ -8,9 +8,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -31,6 +31,9 @@ using namespace qpid::framing; using boost::dynamic_pointer_cast; using std::list; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(DeliveryRecordTestSuite) QPID_AUTO_TEST_CASE(testSort) @@ -60,3 +63,4 @@ QPID_AUTO_TEST_CASE(testSort) QPID_AUTO_TEST_SUITE_END() +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/DispatcherTest.cpp b/qpid/cpp/src/tests/DispatcherTest.cpp index c619a36438..17b3b4e3e6 100644 --- a/qpid/cpp/src/tests/DispatcherTest.cpp +++ b/qpid/cpp/src/tests/DispatcherTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -39,6 +39,9 @@ using namespace std; using namespace qpid::sys; +namespace qpid { +namespace tests { + int writeALot(int fd, const string& s) { int bytesWritten = 0; do { @@ -46,7 +49,7 @@ int writeALot(int fd, const string& s) { int lastWrite = ::write(fd, s.c_str(), s.size()); if ( lastWrite >= 0) { bytesWritten += lastWrite; - } + } } while (errno != EAGAIN); return bytesWritten; } @@ -54,13 +57,13 @@ int writeALot(int fd, const string& s) { int readALot(int fd) { int bytesRead = 0; char buf[10240]; - + do { errno = 0; int lastRead = ::read(fd, buf, sizeof(buf)); if ( lastRead >= 0) { bytesRead += lastRead; - } + } } while (errno != EAGAIN); return bytesRead; } @@ -83,7 +86,7 @@ void rInterrupt(DispatchHandle&) { } void wInterrupt(DispatchHandle&) { - cerr << "W"; + cerr << "W"; } DispatchHandle::Callback rcb = rInterrupt; @@ -108,15 +111,19 @@ void timer_handler(int /*signo*/, siginfo_t* /*info*/, void* /*context*/) { wh->call(wcb); } else { phase1finished = true; - assert(::timer_delete(timer) == 0); + assert(::timer_delete(timer) == 0); } } +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int /*argc*/, char** /*argv*/) { // Create poller Poller::shared_ptr poller(new Poller); - + // Create dispatcher thread Dispatcher d(poller); Dispatcher d1(poller); @@ -131,14 +138,14 @@ int main(int /*argc*/, char** /*argv*/) int sv[2]; int rc = ::socketpair(AF_UNIX, SOCK_STREAM, 0, sv); assert(rc >= 0); - + // Set non-blocking rc = ::fcntl(sv[0], F_SETFL, O_NONBLOCK); assert(rc >= 0); rc = ::fcntl(sv[1], F_SETFL, O_NONBLOCK); assert(rc >= 0); - + // Make up a large string string testString = "This is only a test ... 1,2,3,4,5,6,7,8,9,10;"; for (int i = 0; i < 8; i++) @@ -148,19 +155,19 @@ int main(int /*argc*/, char** /*argv*/) PosixIOHandle f1(sv[1]); rh = new DispatchHandleRef(f0, boost::bind(reader, _1, sv[0]), 0, 0); - wh = new DispatchHandleRef(f1, 0, boost::bind(writer, _1, sv[1], testString), 0); + wh = new DispatchHandleRef(f1, 0, boost::bind(writer, _1, sv[1], testString), 0); rh->startWatch(poller); wh->startWatch(poller); // Set up a regular itimer interupt - + // Ignore signal in this thread ::sigset_t sm; ::sigemptyset(&sm); ::sigaddset(&sm, SIGRTMIN); ::pthread_sigmask(SIG_BLOCK, &sm, 0); - + // Signal handling struct ::sigaction sa; sa.sa_sigaction = timer_handler; @@ -168,18 +175,18 @@ int main(int /*argc*/, char** /*argv*/) ::sigemptyset(&sa.sa_mask); rc = ::sigaction(SIGRTMIN, &sa,0); assert(rc == 0); - + ::sigevent se; ::memset(&se, 0, sizeof(se)); // Clear to make valgrind happy (this *is* the neatest way to do this portably - sigh) se.sigev_notify = SIGEV_SIGNAL; se.sigev_signo = SIGRTMIN; rc = ::timer_create(CLOCK_REALTIME, &se, &timer); assert(rc == 0); - + itimerspec ts = { /*.it_value = */ {2, 0}, // s, ns /*.it_interval = */ {2, 0}}; // s, ns - + rc = ::timer_settime(timer, 0, &ts, 0); assert(rc == 0); @@ -200,11 +207,11 @@ int main(int /*argc*/, char** /*argv*/) sa.sa_sigaction = stop_handler; rc = ::sigaction(SIGRTMIN, &sa,0); assert(rc == 0); - + itimerspec nts = { /*.it_value = */ {30, 0}, // s, ns /*.it_interval = */ {30, 0}}; // s, ns - + rc = ::timer_create(CLOCK_REALTIME, &se, &timer); assert(rc == 0); rc = ::timer_settime(timer, 0, &nts, 0); @@ -228,7 +235,7 @@ int main(int /*argc*/, char** /*argv*/) rc = ::timer_delete(timer); assert(rc == 0); - + poller->shutdown(); dt.join(); dt1.join(); @@ -237,6 +244,6 @@ int main(int /*argc*/, char** /*argv*/) cout << "\nWrote: " << writtenBytes << "\n"; cout << "Read: " << readBytes << "\n"; - + return 0; } diff --git a/qpid/cpp/src/tests/DtxWorkRecordTest.cpp b/qpid/cpp/src/tests/DtxWorkRecordTest.cpp index c7c1b460ff..9d7666dca4 100644 --- a/qpid/cpp/src/tests/DtxWorkRecordTest.cpp +++ b/qpid/cpp/src/tests/DtxWorkRecordTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -27,6 +27,9 @@ using namespace qpid::broker; using boost::static_pointer_cast; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(DtxWorkRecordTestSuite) QPID_AUTO_TEST_CASE(testOnePhaseCommit){ @@ -44,7 +47,7 @@ QPID_AUTO_TEST_CASE(testOnePhaseCommit){ DtxBuffer::shared_ptr bufferB(new DtxBuffer()); bufferB->enlist(static_pointer_cast(opB)); bufferB->markEnded(); - + DtxWorkRecord work("my-xid", &store); work.add(bufferA); work.add(bufferB); @@ -77,7 +80,7 @@ QPID_AUTO_TEST_CASE(testFailOnOnePhaseCommit){ DtxBuffer::shared_ptr bufferC(new DtxBuffer()); bufferC->enlist(static_pointer_cast(opC)); bufferC->markEnded(); - + DtxWorkRecord work("my-xid", &store); work.add(bufferA); work.add(bufferB); @@ -108,7 +111,7 @@ QPID_AUTO_TEST_CASE(testTwoPhaseCommit){ DtxBuffer::shared_ptr bufferB(new DtxBuffer()); bufferB->enlist(static_pointer_cast(opB)); bufferB->markEnded(); - + DtxWorkRecord work("my-xid", &store); work.add(bufferA); work.add(bufferB); @@ -142,7 +145,7 @@ QPID_AUTO_TEST_CASE(testFailOnTwoPhaseCommit){ DtxBuffer::shared_ptr bufferC(new DtxBuffer()); bufferC->enlist(static_pointer_cast(opC)); bufferC->markEnded(); - + DtxWorkRecord work("my-xid", &store); work.add(bufferA); work.add(bufferB); @@ -171,7 +174,7 @@ QPID_AUTO_TEST_CASE(testRollback){ DtxBuffer::shared_ptr bufferB(new DtxBuffer()); bufferB->enlist(static_pointer_cast(opB)); bufferB->markEnded(); - + DtxWorkRecord work("my-xid", &store); work.add(bufferA); work.add(bufferB); @@ -187,3 +190,4 @@ QPID_AUTO_TEST_CASE(testRollback){ QPID_AUTO_TEST_SUITE_END() +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/ExchangeTest.cpp b/qpid/cpp/src/tests/ExchangeTest.cpp index 2100fb5ed7..44835c6184 100644 --- a/qpid/cpp/src/tests/ExchangeTest.cpp +++ b/qpid/cpp/src/tests/ExchangeTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -39,9 +39,12 @@ using namespace qpid::framing; using namespace qpid::sys; using namespace qpid; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ExchangeTestSuite) -QPID_AUTO_TEST_CASE(testMe) +QPID_AUTO_TEST_CASE(testMe) { Queue::shared_ptr queue(new Queue("queue", true)); Queue::shared_ptr queue2(new Queue("queue2", true)); @@ -70,7 +73,7 @@ QPID_AUTO_TEST_CASE(testIsBound) Queue::shared_ptr b(new Queue("b", true)); Queue::shared_ptr c(new Queue("c", true)); Queue::shared_ptr d(new Queue("d", true)); - + string k1("abc"); string k2("def"); string k3("xyz"); @@ -139,7 +142,7 @@ QPID_AUTO_TEST_CASE(testIsBound) headers.bind(a, "", &args3); headers.bind(b, "", &args2); headers.bind(c, "", &args1); - + BOOST_CHECK(headers.isBound(a, 0, 0)); BOOST_CHECK(headers.isBound(a, 0, &args1)); BOOST_CHECK(headers.isBound(a, 0, &args3)); @@ -153,7 +156,7 @@ QPID_AUTO_TEST_CASE(testIsBound) BOOST_CHECK(!headers.isBound(d, 0, &args3)); } -QPID_AUTO_TEST_CASE(testDeleteGetAndRedeclare) +QPID_AUTO_TEST_CASE(testDeleteGetAndRedeclare) { ExchangeRegistry exchanges; exchanges.declare("my-exchange", "direct", false, FieldTable()); @@ -162,7 +165,7 @@ QPID_AUTO_TEST_CASE(testDeleteGetAndRedeclare) exchanges.get("my-exchange"); } catch (const NotFoundException&) {} std::pair response = exchanges.declare("my-exchange", "direct", false, FieldTable()); - BOOST_CHECK_EQUAL(string("direct"), response.first->getType()); + BOOST_CHECK_EQUAL(string("direct"), response.first->getType()); } intrusive_ptr cmessage(std::string exchange, std::string routingKey) { @@ -175,7 +178,7 @@ intrusive_ptr cmessage(std::string exchange, std::string routingKey) { return msg; } -QPID_AUTO_TEST_CASE(testSequenceOptions) +QPID_AUTO_TEST_CASE(testSequenceOptions) { FieldTable args; args.setInt("qpid.msg_sequence",1); @@ -225,22 +228,22 @@ QPID_AUTO_TEST_CASE(testSequenceOptions) direct.encode(buffer); } { - + ExchangeRegistry exchanges; buffer.reset(); DirectExchange::shared_ptr exch_dec = Exchange::decode(exchanges, buffer); - + intrusive_ptr msg1 = cmessage("e", "A"); DeliverableMessage dmsg1(msg1); exch_dec->route(dmsg1, "abc", 0); BOOST_CHECK_EQUAL(4, msg1->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); - + } delete [] buff; } -QPID_AUTO_TEST_CASE(testIVEOption) +QPID_AUTO_TEST_CASE(testIVEOption) { FieldTable args; args.setInt("qpid.ive",1); @@ -248,7 +251,7 @@ QPID_AUTO_TEST_CASE(testIVEOption) FanOutExchange fanout("fanout1", false, args); HeadersExchange header("headers1", false, args); TopicExchange topic ("topic1", false, args); - + intrusive_ptr msg1 = cmessage("direct1", "abc"); msg1->getProperties()->getApplicationHeaders().setString("a", "abc"); DeliverableMessage dmsg1(msg1); @@ -256,7 +259,7 @@ QPID_AUTO_TEST_CASE(testIVEOption) FieldTable args2; args2.setString("x-match", "any"); args2.setString("a", "abc"); - + direct.route(dmsg1, "abc", 0); fanout.route(dmsg1, "abc", 0); header.route(dmsg1, "abc", &args2); @@ -265,20 +268,22 @@ QPID_AUTO_TEST_CASE(testIVEOption) Queue::shared_ptr queue1(new Queue("queue1", true)); Queue::shared_ptr queue2(new Queue("queue2", true)); Queue::shared_ptr queue3(new Queue("queue3", true)); - + BOOST_CHECK(HeadersExchange::match(args2, msg1->getProperties()->getApplicationHeaders())); - + BOOST_CHECK(direct.bind(queue, "abc", 0)); BOOST_CHECK(fanout.bind(queue1, "abc", 0)); BOOST_CHECK(header.bind(queue2, "", &args2)); BOOST_CHECK(topic.bind(queue3, "abc", 0)); - + BOOST_CHECK_EQUAL(1u,queue->getMessageCount()); BOOST_CHECK_EQUAL(1u,queue1->getMessageCount()); BOOST_CHECK_EQUAL(1u,queue2->getMessageCount()); BOOST_CHECK_EQUAL(1u,queue3->getMessageCount()); - + } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/FieldTable.cpp b/qpid/cpp/src/tests/FieldTable.cpp index 5b43871f6d..fe2a14ec03 100644 --- a/qpid/cpp/src/tests/FieldTable.cpp +++ b/qpid/cpp/src/tests/FieldTable.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -29,6 +29,9 @@ using namespace qpid::framing; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(FieldTableTestSuite) QPID_AUTO_TEST_CASE(testMe) @@ -57,7 +60,7 @@ QPID_AUTO_TEST_CASE(testAssignment) a.setInt("B", 1234); b = a; a.setString("A", "CCCC"); - + BOOST_CHECK(string("CCCC") == a.getAsString("A")); BOOST_CHECK(string("BBBB") == b.getAsString("A")); BOOST_CHECK_EQUAL(1234, a.getAsInt("B")); @@ -69,7 +72,7 @@ QPID_AUTO_TEST_CASE(testAssignment) { FieldTable c; c = a; - + char* buff = static_cast(::alloca(c.encodedSize())); Buffer wbuffer(buff, c.encodedSize()); wbuffer.put(c); @@ -102,7 +105,7 @@ QPID_AUTO_TEST_CASE(testNestedValues) list.push_back(List::ValuePtr(new Unsigned32Value(u))); list.push_back(List::ValuePtr(new Str8Value("yellow"))); list.push_back(List::ValuePtr(new DoubleValue(d))); - + a.setString("id", "A"); b.setString("id", "B"); a.setTable("B", b); @@ -192,7 +195,7 @@ QPID_AUTO_TEST_CASE(test64GetAndSetConverts) FieldTable args; args.setInt64("a",100); args.setInt64("b",-(int64_t) ((int64_t) 1<<34)); - + args.setUInt64("c",1u); args.setUInt64("d",(uint64_t) ((uint64_t) 1<<34)); BOOST_CHECK_EQUAL(1u, args.getAsUInt64("c")); @@ -204,5 +207,7 @@ QPID_AUTO_TEST_CASE(test64GetAndSetConverts) BOOST_CHECK_EQUAL((int64_t) ((int64_t) 1<<34), args.getAsInt64("d")); } - + QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/FieldValue.cpp b/qpid/cpp/src/tests/FieldValue.cpp index 448f068107..0ebd0d7d44 100644 --- a/qpid/cpp/src/tests/FieldValue.cpp +++ b/qpid/cpp/src/tests/FieldValue.cpp @@ -6,9 +6,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -20,6 +20,9 @@ #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(FieldValueTestSuite) using namespace qpid::framing; @@ -32,7 +35,7 @@ IntegerValue i(42); QPID_AUTO_TEST_CASE(testStr16ValueEquals) { - + BOOST_CHECK(Str16Value("abc") == s); BOOST_CHECK(Str16Value("foo") != s); BOOST_CHECK(s != i); @@ -73,7 +76,7 @@ QPID_AUTO_TEST_CASE(testFieldTableValueEquals) BOOST_CHECK_EQUAL(std::string("FOO"), ft.getValue().getString("foo")); BOOST_CHECK_EQUAL(7, ft.getValue().getInt("magic")); - + FieldTableValue f2; BOOST_CHECK(ft != f2); f2.getValue().setString("foo", "FOO"); @@ -88,3 +91,5 @@ QPID_AUTO_TEST_CASE(testFieldTableValueEquals) #endif QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/ForkedBroker.cpp b/qpid/cpp/src/tests/ForkedBroker.cpp index 30c346850c..e1a96c8e90 100644 --- a/qpid/cpp/src/tests/ForkedBroker.cpp +++ b/qpid/cpp/src/tests/ForkedBroker.cpp @@ -30,6 +30,16 @@ using namespace std; using qpid::ErrnoException; +namespace std { +static ostream& operator<<(ostream& o, const qpid::tests::ForkedBroker::Args& a) { +copy(a.begin(), a.end(), ostream_iterator(o, " ")); +return o; +} +} + +namespace qpid { +namespace tests { + ForkedBroker::ForkedBroker(const Args& constArgs) { Args args(constArgs); Args::iterator i = find(args.begin(), args.end(), string("TMP_DATA_DIR")); @@ -71,12 +81,6 @@ void ForkedBroker::kill(int sig) { throw qpid::Exception(QPID_MSG("Forked broker exited with: " << WEXITSTATUS(status))); } -namespace std { -static ostream& operator<<(ostream& o, const ForkedBroker::Args& a) { - copy(a.begin(), a.end(), ostream_iterator(o, " ")); - return o; -} - bool isLogOption(const std::string& s) { const char * log_enable = "--log-enable", * trace = "--trace"; @@ -85,8 +89,6 @@ bool isLogOption(const std::string& s) { ); } -} - void ForkedBroker::init(const Args& userArgs) { using qpid::ErrnoException; port = 0; @@ -125,3 +127,5 @@ void ForkedBroker::init(const Args& userArgs) { ::exit(1); } } + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/ForkedBroker.h b/qpid/cpp/src/tests/ForkedBroker.h index 45b522068c..ddbad185d8 100644 --- a/qpid/cpp/src/tests/ForkedBroker.h +++ b/qpid/cpp/src/tests/ForkedBroker.h @@ -11,9 +11,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -31,9 +31,12 @@ #include #include +namespace qpid { +namespace tests { + /** * Class to fork a broker child process. - * + * * For most tests a BrokerFixture may be more convenient as it starts * a broker in the same process which allows you to easily debug into * the broker. @@ -42,17 +45,17 @@ * those brokers can't coexist in the same process (e.g. for cluster * tests where CPG doesn't allow multiple group members in a single * process.) - * + * */ class ForkedBroker { public: typedef std::vector Args; // argv args are passed to broker. - // + // // Special value "TMP_DATA_DIR" is substituted with a temporary // data directory for the broker. - // + // ForkedBroker(const Args& argv); ~ForkedBroker(); @@ -70,4 +73,6 @@ class ForkedBroker { std::string dataDir; }; +}} // namespace qpid::tests + #endif /*!TESTS_FORKEDBROKER_H*/ diff --git a/qpid/cpp/src/tests/Frame.cpp b/qpid/cpp/src/tests/Frame.cpp index 11905911fa..1270eabba3 100644 --- a/qpid/cpp/src/tests/Frame.cpp +++ b/qpid/cpp/src/tests/Frame.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -23,6 +23,9 @@ #include #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(FrameTestSuite) using namespace std; @@ -78,3 +81,5 @@ QPID_AUTO_TEST_CASE(testLoop) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/FrameDecoder.cpp b/qpid/cpp/src/tests/FrameDecoder.cpp index f5db66d5fe..9eeff2a41e 100644 --- a/qpid/cpp/src/tests/FrameDecoder.cpp +++ b/qpid/cpp/src/tests/FrameDecoder.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -27,6 +27,9 @@ #include +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(FrameDecoderTest) using namespace std; @@ -69,5 +72,7 @@ QPID_AUTO_TEST_CASE(testByteFragments) { } - + QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/FramingTest.cpp b/qpid/cpp/src/tests/FramingTest.cpp index e09ee19bc2..3d0fa0c0de 100644 --- a/qpid/cpp/src/tests/FramingTest.cpp +++ b/qpid/cpp/src/tests/FramingTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -39,8 +39,11 @@ using namespace qpid; using namespace qpid::framing; using namespace std; +namespace qpid { +namespace tests { + template -std::string tostring(const T& x) +std::string tostring(const T& x) { std::ostringstream out; out << x; @@ -49,7 +52,7 @@ std::string tostring(const T& x) QPID_AUTO_TEST_SUITE(FramingTestSuite) -QPID_AUTO_TEST_CASE(testMessageTransferBody) +QPID_AUTO_TEST_CASE(testMessageTransferBody) { char buffer[1024]; ProtocolVersion version(highestProtocolVersion); @@ -62,8 +65,8 @@ QPID_AUTO_TEST_CASE(testMessageTransferBody) out.decode(rbuff); BOOST_CHECK_EQUAL(tostring(in), tostring(out)); } - -QPID_AUTO_TEST_CASE(testConnectionSecureBody) + +QPID_AUTO_TEST_CASE(testConnectionSecureBody) { char buffer[1024]; ProtocolVersion version(highestProtocolVersion); @@ -88,10 +91,10 @@ QPID_AUTO_TEST_CASE(testConnectionRedirectBody) Array hosts(0x95); hosts.add(boost::shared_ptr(new Str16Value(a))); hosts.add(boost::shared_ptr(new Str16Value(b))); - + ConnectionRedirectBody in(version, a, hosts); in.encode(wbuff); - + Buffer rbuff(buffer, sizeof(buffer)); ConnectionRedirectBody out(version); out.decode(rbuff); @@ -111,7 +114,7 @@ QPID_AUTO_TEST_CASE(testQueueDeclareBody) out.decode(rbuff); BOOST_CHECK_EQUAL(tostring(in), tostring(out)); } - + QPID_AUTO_TEST_CASE(testConnectionRedirectBodyFrame) { char buffer[1024]; @@ -122,7 +125,7 @@ QPID_AUTO_TEST_CASE(testConnectionRedirectBodyFrame) Array hosts(0x95); hosts.add(boost::shared_ptr(new Str16Value(a))); hosts.add(boost::shared_ptr(new Str16Value(b))); - + AMQFrame in((ConnectionRedirectBody(version, a, hosts))); in.setChannel(999); in.encode(wbuff); @@ -149,3 +152,5 @@ QPID_AUTO_TEST_CASE(testMessageCancelBodyFrame) } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/HeaderTest.cpp b/qpid/cpp/src/tests/HeaderTest.cpp index 01e7c22ee6..4b16f3c793 100644 --- a/qpid/cpp/src/tests/HeaderTest.cpp +++ b/qpid/cpp/src/tests/HeaderTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -26,9 +26,12 @@ using namespace qpid::framing; using namespace std; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(HeaderTestSuite) -QPID_AUTO_TEST_CASE(testGenericProperties) +QPID_AUTO_TEST_CASE(testGenericProperties) { AMQHeaderBody body; body.get(true)->getApplicationHeaders().setString( @@ -47,10 +50,10 @@ QPID_AUTO_TEST_CASE(testGenericProperties) props->getApplicationHeaders().get("A")->get()); } -QPID_AUTO_TEST_CASE(testMessageProperties) +QPID_AUTO_TEST_CASE(testMessageProperties) { AMQFrame out((AMQHeaderBody())); - MessageProperties* props1 = + MessageProperties* props1 = out.castBody()->get(true); props1->setContentLength(42); @@ -82,10 +85,10 @@ QPID_AUTO_TEST_CASE(testMessageProperties) } -QPID_AUTO_TEST_CASE(testDeliveryProperies) +QPID_AUTO_TEST_CASE(testDeliveryProperies) { AMQFrame out((AMQHeaderBody())); - DeliveryProperties* props1 = + DeliveryProperties* props1 = out.castBody()->get(true); props1->setDiscardUnroutable(true); @@ -108,3 +111,4 @@ QPID_AUTO_TEST_CASE(testDeliveryProperies) QPID_AUTO_TEST_SUITE_END() +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/HeadersExchangeTest.cpp b/qpid/cpp/src/tests/HeadersExchangeTest.cpp index 46933f955a..40deb59c86 100644 --- a/qpid/cpp/src/tests/HeadersExchangeTest.cpp +++ b/qpid/cpp/src/tests/HeadersExchangeTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -28,9 +28,12 @@ using namespace qpid::broker; using namespace qpid::framing; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(HeadersExchangeTestSuite) - -QPID_AUTO_TEST_CASE(testMatchAll) + +QPID_AUTO_TEST_CASE(testMatchAll) { FieldTable b, m, n; b.setString("x-match", "all"); @@ -43,7 +46,7 @@ QPID_AUTO_TEST_CASE(testMatchAll) // Ignore extras. m.setString("extra", "x"); BOOST_CHECK(HeadersExchange::match(b, m)); - + // Fail mismatch, wrong value. m.setString("foo", "NotFoo"); BOOST_CHECK(!HeadersExchange::match(b, m)); @@ -54,7 +57,7 @@ QPID_AUTO_TEST_CASE(testMatchAll) BOOST_CHECK(!HeadersExchange::match(b, n)); } -QPID_AUTO_TEST_CASE(testMatchAny) +QPID_AUTO_TEST_CASE(testMatchAny) { FieldTable b, m, n; b.setString("x-match", "any"); @@ -67,7 +70,7 @@ QPID_AUTO_TEST_CASE(testMatchAny) BOOST_CHECK(HeadersExchange::match(b, m)); } -QPID_AUTO_TEST_CASE(testMatchEmptyValue) +QPID_AUTO_TEST_CASE(testMatchEmptyValue) { FieldTable b, m; b.setString("x-match", "all"); @@ -82,23 +85,23 @@ QPID_AUTO_TEST_CASE(testMatchEmptyArgs) { FieldTable b, m; m.setString("foo", "FOO"); - + b.setString("x-match", "all"); BOOST_CHECK(HeadersExchange::match(b, m)); b.setString("x-match", "any"); BOOST_CHECK(!HeadersExchange::match(b, m)); } - -QPID_AUTO_TEST_CASE(testMatchNoXMatch) + +QPID_AUTO_TEST_CASE(testMatchNoXMatch) { FieldTable b, m; b.setString("foo", "FOO"); m.setString("foo", "FOO"); BOOST_CHECK(!HeadersExchange::match(b, m)); } - -QPID_AUTO_TEST_CASE(testBindNoXMatch) + +QPID_AUTO_TEST_CASE(testBindNoXMatch) { HeadersExchange exchange("test"); Queue::shared_ptr queue; @@ -112,4 +115,6 @@ QPID_AUTO_TEST_CASE(testBindNoXMatch) } } -QPID_AUTO_TEST_SUITE_END() +QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/IncompleteMessageList.cpp b/qpid/cpp/src/tests/IncompleteMessageList.cpp index e830df0e8b..303d83cd66 100644 --- a/qpid/cpp/src/tests/IncompleteMessageList.cpp +++ b/qpid/cpp/src/tests/IncompleteMessageList.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -27,6 +27,9 @@ #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(IncompleteMessageListTestSuite) using namespace qpid::broker; @@ -41,7 +44,7 @@ struct Checker Checker(uint start, uint end) { for (uint i = start; i <= end; i++) { ids.push_back(i); - } + } } Checker& expect(const SequenceNumber& id) { @@ -49,11 +52,11 @@ struct Checker return *this; } - void operator()(boost::intrusive_ptr msg) { + void operator()(boost::intrusive_ptr msg) { BOOST_CHECK(!ids.empty()); BOOST_CHECK_EQUAL(msg->getCommandId(), ids.front()); ids.pop_front(); - } + } }; QPID_AUTO_TEST_CASE(testProcessSimple) @@ -91,7 +94,7 @@ QPID_AUTO_TEST_CASE(testProcessWithIncomplete) list.process(Checker(1, 2), false); //mark message complete and re-process to get remaining messages sent to listener middle->enqueueComplete(); - list.process(Checker(3, 5), false); + list.process(Checker(3, 5), false); } @@ -127,3 +130,5 @@ QPID_AUTO_TEST_CASE(testSyncProcessWithIncomplete) } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/InlineAllocator.cpp b/qpid/cpp/src/tests/InlineAllocator.cpp index fe6eaefaf4..a4c4d64cea 100644 --- a/qpid/cpp/src/tests/InlineAllocator.cpp +++ b/qpid/cpp/src/tests/InlineAllocator.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -22,6 +22,9 @@ #include "qpid/InlineAllocator.h" #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(InlineAllocatorTestSuite) using namespace qpid; @@ -48,16 +51,18 @@ QPID_AUTO_TEST_CASE(testAllocateFull) { char* p = alloc.allocate(1); BOOST_CHECK(p == (char*)&alloc); - + char* q = alloc.allocate(1); BOOST_CHECK(q != (char*)&alloc); alloc.deallocate(p,1); p = alloc.allocate(1); BOOST_CHECK(p == (char*)&alloc); - + alloc.deallocate(p,1); alloc.deallocate(q,1); } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/InlineVector.cpp b/qpid/cpp/src/tests/InlineVector.cpp index 009f10af9d..ba5165886d 100644 --- a/qpid/cpp/src/tests/InlineVector.cpp +++ b/qpid/cpp/src/tests/InlineVector.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -22,6 +22,9 @@ #include "qpid/InlineVector.h" #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(InlineVectorTestSuite) using namespace qpid; @@ -117,7 +120,9 @@ QPID_AUTO_TEST_CASE(testAssign) { QPID_AUTO_TEST_CASE(testResize) { Vec v; v.resize(5); - BOOST_CHECK(!isInline(v)); + BOOST_CHECK(!isInline(v)); } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/ManagementTest.cpp b/qpid/cpp/src/tests/ManagementTest.cpp index e6f2e2e3fd..d05b4676ba 100644 --- a/qpid/cpp/src/tests/ManagementTest.cpp +++ b/qpid/cpp/src/tests/ManagementTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -24,6 +24,9 @@ #include "qpid/console/ObjectId.h" #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ManagementTestSuite) using namespace qpid::framing; @@ -109,4 +112,4 @@ QPID_AUTO_TEST_CASE(testConsoleObjectId) { QPID_AUTO_TEST_SUITE_END() - +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/MessageBuilderTest.cpp b/qpid/cpp/src/tests/MessageBuilderTest.cpp index 1f3f830633..c2fb8ad32e 100644 --- a/qpid/cpp/src/tests/MessageBuilderTest.cpp +++ b/qpid/cpp/src/tests/MessageBuilderTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -31,15 +31,18 @@ using namespace qpid::broker; using namespace qpid::framing; using namespace qpid::sys; +namespace qpid { +namespace tests { + class MockMessageStore : public NullMessageStore { enum Op {STAGE=1, APPEND=2}; uint64_t id; - boost::intrusive_ptr expectedMsg; + boost::intrusive_ptr expectedMsg; string expectedData; std::list ops; - + void checkExpectation(Op actual) { BOOST_CHECK_EQUAL(ops.front(), actual); @@ -49,17 +52,17 @@ class MockMessageStore : public NullMessageStore public: MockMessageStore() : id(0), expectedMsg(0) {} - void expectStage(PersistableMessage& msg) - { + void expectStage(PersistableMessage& msg) + { expectedMsg = &msg; - ops.push_back(STAGE); + ops.push_back(STAGE); } - void expectAppendContent(PersistableMessage& msg, const string& data) - { + void expectAppendContent(PersistableMessage& msg, const string& data) + { expectedMsg = &msg; expectedData = data; - ops.push_back(APPEND); + ops.push_back(APPEND); } void stage(const boost::intrusive_ptr& msg) @@ -74,7 +77,7 @@ class MockMessageStore : public NullMessageStore { checkExpectation(APPEND); BOOST_CHECK_EQUAL(boost::static_pointer_cast(expectedMsg), msg); - BOOST_CHECK_EQUAL(expectedData, data); + BOOST_CHECK_EQUAL(expectedData, data); } bool expectationsMet() @@ -89,7 +92,7 @@ class MockMessageStore : public NullMessageStore } }; - + QPID_AUTO_TEST_SUITE(MessageBuilderTestSuite) QPID_AUTO_TEST_CASE(testHeaderOnly) @@ -103,7 +106,7 @@ QPID_AUTO_TEST_CASE(testHeaderOnly) AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0))); AMQFrame header((AMQHeaderBody())); - header.castBody()->get(true)->setContentLength(0); + header.castBody()->get(true)->setContentLength(0); header.castBody()->get(true)->setRoutingKey(key); builder.handle(method); @@ -132,7 +135,7 @@ QPID_AUTO_TEST_CASE(test1ContentFrame) header.setEof(false); content.setBof(false); - header.castBody()->get(true)->setContentLength(data.size()); + header.castBody()->get(true)->setContentLength(data.size()); header.castBody()->get(true)->setRoutingKey(key); builder.handle(method); @@ -143,7 +146,7 @@ QPID_AUTO_TEST_CASE(test1ContentFrame) BOOST_CHECK(builder.getMessage()); BOOST_CHECK(!builder.getMessage()->getFrames().isComplete()); - builder.handle(content); + builder.handle(content); BOOST_CHECK(builder.getMessage()); BOOST_CHECK(builder.getMessage()->getFrames().isComplete()); } @@ -169,7 +172,7 @@ QPID_AUTO_TEST_CASE(test2ContentFrames) content1.setEof(false); content2.setBof(false); - header.castBody()->get(true)->setContentLength(data1.size() + data2.size()); + header.castBody()->get(true)->setContentLength(data1.size() + data2.size()); header.castBody()->get(true)->setRoutingKey(key); builder.handle(method); @@ -188,7 +191,7 @@ QPID_AUTO_TEST_CASE(testStaging) MockMessageStore store; MessageBuilder builder(&store, 5); builder.start(SequenceNumber()); - + std::string data1("abcdefg"); std::string data2("hijklmn"); std::string exchange("builder-exchange"); @@ -199,7 +202,7 @@ QPID_AUTO_TEST_CASE(testStaging) AMQFrame content1((AMQContentBody(data1))); AMQFrame content2((AMQContentBody(data2))); - header.castBody()->get(true)->setContentLength(data1.size() + data2.size()); + header.castBody()->get(true)->setContentLength(data1.size() + data2.size()); header.castBody()->get(true)->setRoutingKey(key); builder.handle(method); @@ -223,7 +226,7 @@ QPID_AUTO_TEST_CASE(testNoManagementStaging) MockMessageStore store; MessageBuilder builder(&store, 5); builder.start(SequenceNumber()); - + std::string data1("abcdefg"); std::string exchange("qpid.management"); std::string key("builder-exchange"); @@ -232,7 +235,7 @@ QPID_AUTO_TEST_CASE(testNoManagementStaging) AMQFrame header((AMQHeaderBody())); AMQFrame content1((AMQContentBody(data1))); - header.castBody()->get(true)->setContentLength(data1.size()); + header.castBody()->get(true)->setContentLength(data1.size()); header.castBody()->get(true)->setRoutingKey(key); builder.handle(method); @@ -242,4 +245,7 @@ QPID_AUTO_TEST_CASE(testNoManagementStaging) BOOST_CHECK(store.expectationsMet()); BOOST_CHECK_EQUAL((uint64_t) 0, builder.getMessage()->getPersistenceId()); } + QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/MessageReplayTracker.cpp b/qpid/cpp/src/tests/MessageReplayTracker.cpp index a5121cdeb7..3d79ee53c2 100644 --- a/qpid/cpp/src/tests/MessageReplayTracker.cpp +++ b/qpid/cpp/src/tests/MessageReplayTracker.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -23,6 +23,9 @@ #include "qpid/client/MessageReplayTracker.h" #include "qpid/sys/Time.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(MessageReplayTrackerTests) using namespace qpid::client; @@ -53,8 +56,8 @@ QPID_AUTO_TEST_CASE(testReplay) MessageReplayTracker tracker(10); tracker.init(fix.session); - for (uint i = 0; i < 5; i++) { - Message message((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); + for (uint i = 0; i < 5; i++) { + Message message((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); tracker.send(message); } ReplayBufferChecker checker(1, 10); @@ -62,7 +65,7 @@ QPID_AUTO_TEST_CASE(testReplay) tracker.replay(fix.session); for (uint j = 0; j < 2; j++) {//each message should have been sent twice - for (uint i = 0; i < 5; i++) { + for (uint i = 0; i < 5; i++) { Message m; BOOST_CHECK(fix.subs.get(m, "my-queue", TIME_SEC)); BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData()); @@ -79,15 +82,15 @@ QPID_AUTO_TEST_CASE(testCheckCompletion) MessageReplayTracker tracker(10); tracker.init(fix.session); - for (uint i = 0; i < 5; i++) { - Message message((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); + for (uint i = 0; i < 5; i++) { + Message message((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); tracker.send(message); } fix.session.sync();//ensures all messages are complete tracker.checkCompletion(); tracker.replay(fix.session); Message received; - for (uint i = 0; i < 5; i++) { + for (uint i = 0; i < 5; i++) { BOOST_CHECK(fix.subs.get(received, "my-queue")); BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), received.getData()); } @@ -96,4 +99,4 @@ QPID_AUTO_TEST_CASE(testCheckCompletion) QPID_AUTO_TEST_SUITE_END() - +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/MessageTest.cpp b/qpid/cpp/src/tests/MessageTest.cpp index cd63f64a37..7d67c92b37 100644 --- a/qpid/cpp/src/tests/MessageTest.cpp +++ b/qpid/cpp/src/tests/MessageTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -33,6 +33,9 @@ using namespace qpid::broker; using namespace qpid::framing; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(MessageTestSuite) QPID_AUTO_TEST_CASE(testEncodeDecode) @@ -56,7 +59,7 @@ QPID_AUTO_TEST_CASE(testEncodeDecode) msg->getFrames().append(content2); MessageProperties* mProps = msg->getFrames().getHeaders()->get(true); - mProps->setContentLength(data1.size() + data2.size()); + mProps->setContentLength(data1.size() + data2.size()); mProps->setMessageId(messageId); FieldTable applicationHeaders; applicationHeaders.setString("abc", "xyz"); @@ -69,7 +72,7 @@ QPID_AUTO_TEST_CASE(testEncodeDecode) char* buff = static_cast(::alloca(msg->encodedSize())); Buffer wbuffer(buff, msg->encodedSize()); msg->encode(wbuffer); - + Buffer rbuffer(buff, msg->encodedSize()); msg = new Message(); msg->decodeHeader(rbuffer); @@ -86,3 +89,4 @@ QPID_AUTO_TEST_CASE(testEncodeDecode) QPID_AUTO_TEST_SUITE_END() +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/MessageUtils.h b/qpid/cpp/src/tests/MessageUtils.h index 6a12c72007..dae74cce7d 100644 --- a/qpid/cpp/src/tests/MessageUtils.h +++ b/qpid/cpp/src/tests/MessageUtils.h @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -28,9 +28,12 @@ using namespace qpid; using namespace broker; using namespace framing; +namespace qpid { +namespace tests { + struct MessageUtils { - static boost::intrusive_ptr createMessage(const string& exchange="", const string& routingKey="", + static boost::intrusive_ptr createMessage(const string& exchange="", const string& routingKey="", const Uuid& messageId=Uuid(true), uint64_t contentSize = 0) { boost::intrusive_ptr msg(new broker::Message()); @@ -41,7 +44,7 @@ struct MessageUtils msg->getFrames().append(method); msg->getFrames().append(header); MessageProperties* props = msg->getFrames().getHeaders()->get(true); - props->setContentLength(contentSize); + props->setContentLength(contentSize); props->setMessageId(messageId); msg->getFrames().getHeaders()->get(true)->setRoutingKey(routingKey); return msg; @@ -53,3 +56,5 @@ struct MessageUtils msg->getFrames().append(content); } }; + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/MessagingSessionTests.cpp b/qpid/cpp/src/tests/MessagingSessionTests.cpp index 4ee27f0764..f5a5420d3a 100644 --- a/qpid/cpp/src/tests/MessagingSessionTests.cpp +++ b/qpid/cpp/src/tests/MessagingSessionTests.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -36,6 +36,9 @@ #include #include +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(MessagingSessionTests) using namespace qpid::messaging; @@ -86,7 +89,7 @@ struct MessagingFixture : public BrokerFixture Session session; BrokerAdmin admin; - MessagingFixture(Broker::Options opts = Broker::Options()) : + MessagingFixture(Broker::Options opts = Broker::Options()) : BrokerFixture(opts), connection(Connection::open((boost::format("amqp:tcp:localhost:%1%") % (broker->getPort(Broker::TCP_TRANSPORT))).str())), session(connection.newSession()), @@ -133,7 +136,7 @@ struct TopicFixture : MessagingFixture struct MultiQueueFixture : MessagingFixture { - typedef std::vector::const_iterator const_iterator; + typedef std::vector::const_iterator const_iterator; std::vector queues; MultiQueueFixture(const std::vector& names = boost::assign::list_of("q1")("q2")("q3")) : queues(names) @@ -161,7 +164,7 @@ struct MessageDataCollector : MessageListener } }; -std::vector fetch(Receiver& receiver, int count, qpid::sys::Duration timeout=qpid::sys::TIME_SEC*5) +std::vector fetch(Receiver& receiver, int count, qpid::sys::Duration timeout=qpid::sys::TIME_SEC*5) { std::vector data; Message message; @@ -189,13 +192,12 @@ QPID_AUTO_TEST_CASE(testSendReceiveHeaders) Sender sender = fix.session.createSender(fix.queue); Message out("test-message"); for (uint i = 0; i < 10; ++i) { - out.getHeaders()["a"] = i; + out.getHeaders()["a"] = i; sender.send(out); } Receiver receiver = fix.session.createReceiver(fix.queue); Message in; for (uint i = 0; i < 10; ++i) { - //Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); BOOST_CHECK(receiver.fetch(in, 5 * qpid::sys::TIME_SEC)); BOOST_CHECK_EQUAL(in.getBytes(), out.getBytes()); BOOST_CHECK_EQUAL(in.getHeaders()["a"].asUint32(), i); @@ -249,14 +251,14 @@ QPID_AUTO_TEST_CASE(testSimpleTopic) Message in; BOOST_CHECK(!sub2.fetch(in, 0));//TODO: or should this raise an error? - + //TODO: check pending messages... } QPID_AUTO_TEST_CASE(testSessionFetch) { MultiQueueFixture fix; - + for (uint i = 0; i < fix.queues.size(); i++) { Receiver r = fix.session.createReceiver(fix.queues[i]); r.setCapacity(10u); @@ -267,8 +269,8 @@ QPID_AUTO_TEST_CASE(testSessionFetch) Sender s = fix.session.createSender(fix.queues[i]); Message msg((boost::format("Message_%1%") % (i+1)).str()); s.send(msg); - } - + } + for (uint i = 0; i < fix.queues.size(); i++) { Message msg; BOOST_CHECK(fix.session.fetch(msg, qpid::sys::TIME_SEC)); @@ -279,7 +281,7 @@ QPID_AUTO_TEST_CASE(testSessionFetch) QPID_AUTO_TEST_CASE(testSessionDispatch) { MultiQueueFixture fix; - + MessageDataCollector collector; for (uint i = 0; i < fix.queues.size(); i++) { Receiver r = fix.session.createReceiver(fix.queues[i]); @@ -292,10 +294,10 @@ QPID_AUTO_TEST_CASE(testSessionDispatch) Sender s = fix.session.createSender(fix.queues[i]); Message msg((boost::format("Message_%1%") % (i+1)).str()); s.send(msg); - } + } while (fix.session.dispatch(qpid::sys::TIME_SEC)) ; - + BOOST_CHECK_EQUAL(collector.messageData, boost::assign::list_of("Message_1")("Message_2")("Message_3")); } @@ -309,7 +311,7 @@ QPID_AUTO_TEST_CASE(testMapMessage) out.getContent().asMap()["pi"] = 3.14f; sender.send(out); Receiver receiver = fix.session.createReceiver(fix.queue); - Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); + Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); BOOST_CHECK_EQUAL(in.getContent().asMap()["abc"].asString(), "def"); BOOST_CHECK_EQUAL(in.getContent().asMap()["pi"].asFloat(), 3.14f); fix.session.acknowledge(); @@ -324,11 +326,11 @@ QPID_AUTO_TEST_CASE(testListMessage) out.getContent() << "abc"; out.getContent() << 1234; out.getContent() << "def"; - out.getContent() << 56.789; + out.getContent() << 56.789; sender.send(out); Receiver receiver = fix.session.createReceiver(fix.queue); - Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); - Variant::List& list = in.getContent().asList(); + Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); + Variant::List& list = in.getContent().asList(); BOOST_CHECK_EQUAL(list.size(), out.getContent().asList().size()); BOOST_CHECK_EQUAL(list.front().asString(), "abc"); list.pop_front(); @@ -354,7 +356,86 @@ QPID_AUTO_TEST_CASE(testReject) fix.session.reject(in); in = receiver.fetch(5 * qpid::sys::TIME_SEC); BOOST_CHECK_EQUAL(in.getBytes(), m2.getBytes()); - fix.session.acknowledge(); + fix.session.acknowledge(); +} + +QPID_AUTO_TEST_CASE(testAvailable) +{ + MultiQueueFixture fix; + + Receiver r1 = fix.session.createReceiver(fix.queues[0]); + r1.setCapacity(100); + r1.start(); + + Receiver r2 = fix.session.createReceiver(fix.queues[1]); + r2.setCapacity(100); + r2.start(); + + Sender s1 = fix.session.createSender(fix.queues[0]); + Sender s2 = fix.session.createSender(fix.queues[1]); + + for (uint i = 0; i < 10; ++i) { + s1.send(Message((boost::format("A_%1%") % (i+1)).str())); + } + for (uint i = 0; i < 5; ++i) { + s2.send(Message((boost::format("B_%1%") % (i+1)).str())); + } + qpid::sys::sleep(1);//is there any avoid an arbitrary sleep while waiting for messages to be dispatched? + for (uint i = 0; i < 5; ++i) { + BOOST_CHECK_EQUAL(fix.session.available(), 15u - 2*i); + BOOST_CHECK_EQUAL(r1.available(), 10u - i); + BOOST_CHECK_EQUAL(r1.fetch().getBytes(), (boost::format("A_%1%") % (i+1)).str()); + BOOST_CHECK_EQUAL(r2.available(), 5u - i); + BOOST_CHECK_EQUAL(r2.fetch().getBytes(), (boost::format("B_%1%") % (i+1)).str()); + fix.session.acknowledge(); + } + for (uint i = 5; i < 10; ++i) { + BOOST_CHECK_EQUAL(fix.session.available(), 10u - i); + BOOST_CHECK_EQUAL(r1.available(), 10u - i); + BOOST_CHECK_EQUAL(r1.fetch().getBytes(), (boost::format("A_%1%") % (i+1)).str()); + } +} + +QPID_AUTO_TEST_CASE(testPendingAck) +{ + QueueFixture fix; + Sender sender = fix.session.createSender(fix.queue); + for (uint i = 0; i < 10; ++i) { + sender.send(Message((boost::format("Message_%1%") % (i+1)).str())); + } + Receiver receiver = fix.session.createReceiver(fix.queue); + for (uint i = 0; i < 10; ++i) { + BOOST_CHECK_EQUAL(receiver.fetch().getBytes(), (boost::format("Message_%1%") % (i+1)).str()); + } + BOOST_CHECK_EQUAL(fix.session.pendingAck(), 0u); + fix.session.acknowledge(); + BOOST_CHECK_EQUAL(fix.session.pendingAck(), 10u); + fix.session.sync(); + BOOST_CHECK_EQUAL(fix.session.pendingAck(), 0u); +} + +QPID_AUTO_TEST_CASE(testPendingSend) +{ + QueueFixture fix; + Sender sender = fix.session.createSender(fix.queue); + for (uint i = 0; i < 10; ++i) { + sender.send(Message((boost::format("Message_%1%") % (i+1)).str())); + } + //Note: this test relies on 'inside knowledge' of the sender + //implementation and the fact that the simple test case makes it + //possible to predict when completion information will be sent to + //the client. TODO: is there a better way of testing this? + BOOST_CHECK_EQUAL(sender.pending(), 10u); + fix.session.sync(); + BOOST_CHECK_EQUAL(sender.pending(), 0u); + + Receiver receiver = fix.session.createReceiver(fix.queue); + for (uint i = 0; i < 10; ++i) { + BOOST_CHECK_EQUAL(receiver.fetch().getBytes(), (boost::format("Message_%1%") % (i+1)).str()); + } + fix.session.acknowledge(); } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/PartialFailure.cpp b/qpid/cpp/src/tests/PartialFailure.cpp index f77a1401f8..8d9970f909 100644 --- a/qpid/cpp/src/tests/PartialFailure.cpp +++ b/qpid/cpp/src/tests/PartialFailure.cpp @@ -31,6 +31,9 @@ #include #include +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(PartialFailureTestSuite) using namespace std; @@ -257,3 +260,5 @@ QPID_AUTO_TEST_CASE(testPartialFailureMemberLeaves) { #endif #endif // FIXME aconway 2009-07-30: QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/PollableCondition.cpp b/qpid/cpp/src/tests/PollableCondition.cpp index b5cf1b4cd2..f9b3c25c93 100644 --- a/qpid/cpp/src/tests/PollableCondition.cpp +++ b/qpid/cpp/src/tests/PollableCondition.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -28,6 +28,8 @@ #include "qpid/sys/Thread.h" #include +namespace qpid { +namespace tests { QPID_AUTO_TEST_SUITE(PollableConditionTest) @@ -37,7 +39,7 @@ const Duration SHORT = TIME_SEC/100; const Duration LONG = TIME_SEC/10; class Callback { - public: + public: enum Action { NONE, CLEAR }; Callback() : count(), action(NONE) {} @@ -46,7 +48,7 @@ class Callback { Mutex::ScopedLock l(lock); ++count; switch(action) { - case NONE: break; + case NONE: break; case CLEAR: pc.clear(); break; } action = NONE; @@ -62,9 +64,9 @@ class Callback { action = a; return wait(LONG); } - + private: - bool wait(Duration timeout) { + bool wait(Duration timeout) { int n = count; AbsTime deadline(now(), timeout); while (n == count && lock.wait(deadline)) @@ -83,7 +85,7 @@ QPID_AUTO_TEST_CASE(testPollableCondition) { PollableCondition pc(boost::bind(&Callback::call, &callback, _1), poller); Thread runner = Thread(*poller); - + BOOST_CHECK(callback.isNotCalling()); // condition is not set. pc.set(); @@ -104,4 +106,4 @@ QPID_AUTO_TEST_CASE(testPollableCondition) { QPID_AUTO_TEST_SUITE_END() - +}} //namespace qpid::tests diff --git a/qpid/cpp/src/tests/ProxyTest.cpp b/qpid/cpp/src/tests/ProxyTest.cpp index 4ea10f7be9..a926b28395 100644 --- a/qpid/cpp/src/tests/ProxyTest.cpp +++ b/qpid/cpp/src/tests/ProxyTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -28,6 +28,9 @@ using namespace qpid::framing; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ProxyTestSuite) @@ -47,5 +50,7 @@ QPID_AUTO_TEST_CASE(testScopedSync) Proxy::ScopedSync s(p); p.send(ExecutionSyncBody(p.getVersion())); } - + QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/QueueEvents.cpp b/qpid/cpp/src/tests/QueueEvents.cpp index cd9439355e..bd18fa45fb 100644 --- a/qpid/cpp/src/tests/QueueEvents.cpp +++ b/qpid/cpp/src/tests/QueueEvents.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -31,6 +31,9 @@ #include #include +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(QueueEventsSuite) using namespace qpid::client; @@ -156,7 +159,7 @@ QPID_AUTO_TEST_CASE(testSystemLevelEventProcessing) fixture.session.queueDeclare(arg::queue=q, arg::arguments=options); //send and consume some messages LocalQueue incoming; - Subscription sub = fixture.subs.subscribe(incoming, q); + Subscription sub = fixture.subs.subscribe(incoming, q); for (int i = 0; i < 5; i++) { fixture.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q)); } @@ -177,7 +180,7 @@ QPID_AUTO_TEST_CASE(testSystemLevelEventProcessing) SequenceNumber dequeueId(1); for (int i = 0; i < 5; i++) { listener.checkEnqueue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), enqueueId++); - } + } for (int i = 0; i < 3; i++) { listener.checkDequeue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), dequeueId++); } @@ -203,7 +206,7 @@ QPID_AUTO_TEST_CASE(testSystemLevelEventProcessing_enqueuesOnly) fixture.session.queueDeclare(arg::queue=q, arg::arguments=options); //send and consume some messages LocalQueue incoming; - Subscription sub = fixture.subs.subscribe(incoming, q); + Subscription sub = fixture.subs.subscribe(incoming, q); for (int i = 0; i < 5; i++) { fixture.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q)); } @@ -224,7 +227,7 @@ QPID_AUTO_TEST_CASE(testSystemLevelEventProcessing_enqueuesOnly) SequenceNumber dequeueId(1); for (int i = 0; i < 5; i++) { listener.checkEnqueue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), enqueueId++); - } + } for (int i = 5; i < 10; i++) { listener.checkEnqueue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), enqueueId++); } @@ -232,4 +235,4 @@ QPID_AUTO_TEST_CASE(testSystemLevelEventProcessing_enqueuesOnly) QPID_AUTO_TEST_SUITE_END() - +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/QueueOptionsTest.cpp b/qpid/cpp/src/tests/QueueOptionsTest.cpp index 93d1961caa..f2fbaba2c1 100644 --- a/qpid/cpp/src/tests/QueueOptionsTest.cpp +++ b/qpid/cpp/src/tests/QueueOptionsTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -27,14 +27,17 @@ using namespace qpid::client; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(QueueOptionsTestSuite) QPID_AUTO_TEST_CASE(testSizePolicy) { QueueOptions ft; - + ft.setSizePolicy(REJECT,1,2); - + BOOST_CHECK(QueueOptions::strREJECT == ft.getAsString(QueueOptions::strTypeKey)); BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strMaxSizeKey)); BOOST_CHECK(2 == ft.getAsInt(QueueOptions::strMaxCountKey)); @@ -49,7 +52,7 @@ QPID_AUTO_TEST_CASE(testSizePolicy) ft.setSizePolicy(RING_STRICT,1,0); BOOST_CHECK(QueueOptions::strRING_STRICT == ft.getAsString(QueueOptions::strTypeKey)); - + ft.clearSizePolicy(); BOOST_CHECK(!ft.isSet(QueueOptions::strTypeKey)); BOOST_CHECK(!ft.isSet(QueueOptions::strMaxSizeKey)); @@ -59,13 +62,13 @@ QPID_AUTO_TEST_CASE(testSizePolicy) QPID_AUTO_TEST_CASE(testFlags) { QueueOptions ft; - + ft.setPersistLastNode(); ft.setOrdering(LVQ); - + BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strPersistLastNode)); BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strLastValueQueue)); - + ft.clearPersistLastNode(); ft.setOrdering(FIFO); @@ -78,8 +81,8 @@ QPID_AUTO_TEST_CASE(testSetOrdering) { //ensure setOrdering(FIFO) works even if not preceded by a call to //setOrdering(LVQ) - QueueOptions ft; - ft.setOrdering(FIFO); + QueueOptions ft; + ft.setOrdering(FIFO); BOOST_CHECK(!ft.isSet(QueueOptions::strLastValueQueue)); } @@ -88,10 +91,12 @@ QPID_AUTO_TEST_CASE(testClearPersistLastNode) { //ensure clear works even if not preceded by the setting on the //option - QueueOptions ft; + QueueOptions ft; ft.clearPersistLastNode(); BOOST_CHECK(!ft.isSet(QueueOptions::strPersistLastNode)); } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/QueuePolicyTest.cpp b/qpid/cpp/src/tests/QueuePolicyTest.cpp index 7c7f8b7a10..f40d30b588 100644 --- a/qpid/cpp/src/tests/QueuePolicyTest.cpp +++ b/qpid/cpp/src/tests/QueuePolicyTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -32,6 +32,9 @@ using namespace qpid::broker; using namespace qpid::client; using namespace qpid::framing; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(QueuePolicyTestSuite) QueuedMessage createMessage(uint32_t size) @@ -50,11 +53,11 @@ QPID_AUTO_TEST_CASE(testCount) BOOST_CHECK_EQUAL((uint32_t) 5, policy->getMaxCount()); QueuedMessage msg = createMessage(10); - for (size_t i = 0; i < 5; i++) { + for (size_t i = 0; i < 5; i++) { policy->tryEnqueue(msg); } try { - policy->tryEnqueue(msg); + policy->tryEnqueue(msg); BOOST_FAIL("Policy did not fail on enqueuing sixth message"); } catch (const ResourceLimitExceededException&) {} @@ -62,7 +65,7 @@ QPID_AUTO_TEST_CASE(testCount) policy->tryEnqueue(msg); try { - policy->tryEnqueue(msg); + policy->tryEnqueue(msg); BOOST_FAIL("Policy did not fail on enqueuing sixth message (after dequeue)"); } catch (const ResourceLimitExceededException&) {} } @@ -71,12 +74,12 @@ QPID_AUTO_TEST_CASE(testSize) { std::auto_ptr policy(QueuePolicy::createQueuePolicy(0, 50)); QueuedMessage msg = createMessage(10); - - for (size_t i = 0; i < 5; i++) { + + for (size_t i = 0; i < 5; i++) { policy->tryEnqueue(msg); } try { - policy->tryEnqueue(msg); + policy->tryEnqueue(msg); BOOST_FAIL("Policy did not fail on aggregate size exceeding 50. " << *policy); } catch (const ResourceLimitExceededException&) {} @@ -84,7 +87,7 @@ QPID_AUTO_TEST_CASE(testSize) policy->tryEnqueue(msg); try { - policy->tryEnqueue(msg); + policy->tryEnqueue(msg); BOOST_FAIL("Policy did not fail on aggregate size exceeding 50 (after dequeue). " << *policy); } catch (const ResourceLimitExceededException&) {} } @@ -104,7 +107,7 @@ QPID_AUTO_TEST_CASE(testBoth) messages.push_back(createMessage(11)); messages.push_back(createMessage(2)); messages.push_back(createMessage(7)); - for (size_t i = 0; i < messages.size(); i++) { + for (size_t i = 0; i < messages.size(); i++) { policy->tryEnqueue(messages[i]); } //size = 45 at this point, count = 5 @@ -140,7 +143,7 @@ QPID_AUTO_TEST_CASE(testSettings) BOOST_CHECK_EQUAL(a->getMaxSize(), b->getMaxSize()); } -QPID_AUTO_TEST_CASE(testRingPolicy) +QPID_AUTO_TEST_CASE(testRingPolicy) { FieldTable args; std::auto_ptr policy = QueuePolicy::createQueuePolicy(5, 0, QueuePolicy::RING); @@ -169,7 +172,7 @@ QPID_AUTO_TEST_CASE(testRingPolicy) BOOST_CHECK(!f.subs.get(msg, q)); } -QPID_AUTO_TEST_CASE(testStrictRingPolicy) +QPID_AUTO_TEST_CASE(testStrictRingPolicy) { FieldTable args; std::auto_ptr policy = QueuePolicy::createQueuePolicy(5, 0, QueuePolicy::RING_STRICT); @@ -181,7 +184,7 @@ QPID_AUTO_TEST_CASE(testStrictRingPolicy) LocalQueue incoming; SubscriptionSettings settings(FlowControl::unlimited()); settings.autoAck = 0; // no auto ack. - Subscription sub = f.subs.subscribe(incoming, q, settings); + Subscription sub = f.subs.subscribe(incoming, q, settings); for (int i = 0; i < 5; i++) { f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q)); } @@ -192,10 +195,10 @@ QPID_AUTO_TEST_CASE(testStrictRingPolicy) ScopedSuppressLogging sl; // Suppress messages for expected errors. f.session.messageTransfer(arg::content=client::Message("Message_6", q)); BOOST_FAIL("expecting ResourceLimitExceededException."); - } catch (const ResourceLimitExceededException&) {} + } catch (const ResourceLimitExceededException&) {} } -QPID_AUTO_TEST_CASE(testPolicyWithDtx) +QPID_AUTO_TEST_CASE(testPolicyWithDtx) { FieldTable args; std::auto_ptr policy = QueuePolicy::createQueuePolicy(5, 0, QueuePolicy::REJECT); @@ -207,7 +210,7 @@ QPID_AUTO_TEST_CASE(testPolicyWithDtx) LocalQueue incoming; SubscriptionSettings settings(FlowControl::unlimited()); settings.autoAck = 0; // no auto ack. - Subscription sub = f.subs.subscribe(incoming, q, settings); + Subscription sub = f.subs.subscribe(incoming, q, settings); f.session.dtxSelect(); Xid tx1(1, "test-dtx-mgr", "tx1"); f.session.dtxStart(arg::xid=tx1); @@ -244,7 +247,7 @@ QPID_AUTO_TEST_CASE(testPolicyWithDtx) ScopedSuppressLogging sl; // Suppress messages for expected errors. other.messageTransfer(arg::content=client::Message("Message_6", q)); BOOST_FAIL("expecting ResourceLimitExceededException."); - } catch (const ResourceLimitExceededException&) {} + } catch (const ResourceLimitExceededException&) {} f.session.dtxCommit(arg::xid=tx3); //now retry and this time should succeed @@ -252,7 +255,7 @@ QPID_AUTO_TEST_CASE(testPolicyWithDtx) other.messageTransfer(arg::content=client::Message("Message_6", q)); } -QPID_AUTO_TEST_CASE(testFlowToDiskWithNoStore) +QPID_AUTO_TEST_CASE(testFlowToDiskWithNoStore) { //Ensure that with no store loaded, we don't flow to disk but //fallback to rejecting messages @@ -265,7 +268,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskWithNoStore) LocalQueue incoming; SubscriptionSettings settings(FlowControl::unlimited()); settings.autoAck = 0; // no auto ack. - Subscription sub = f.subs.subscribe(incoming, q, settings); + Subscription sub = f.subs.subscribe(incoming, q, settings); for (int i = 0; i < 5; i++) { f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q)); } @@ -276,8 +279,10 @@ QPID_AUTO_TEST_CASE(testFlowToDiskWithNoStore) ScopedSuppressLogging sl; // Suppress messages for expected errors. f.session.messageTransfer(arg::content=client::Message("Message_6", q)); BOOST_FAIL("expecting ResourceLimitExceededException."); - } catch (const ResourceLimitExceededException&) {} + } catch (const ResourceLimitExceededException&) {} } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/QueueRegistryTest.cpp b/qpid/cpp/src/tests/QueueRegistryTest.cpp index 7ad4e0b89d..712cb568c3 100644 --- a/qpid/cpp/src/tests/QueueRegistryTest.cpp +++ b/qpid/cpp/src/tests/QueueRegistryTest.cpp @@ -6,9 +6,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -23,6 +23,9 @@ using namespace qpid::broker; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(QueueRegistryTest) QPID_AUTO_TEST_CASE(testDeclare) @@ -49,7 +52,7 @@ QPID_AUTO_TEST_CASE(testDeclare) BOOST_CHECK_EQUAL(bar, q->getName()); } -QPID_AUTO_TEST_CASE(testDeclareTmp) +QPID_AUTO_TEST_CASE(testDeclareTmp) { QueueRegistry reg; std::pair qc; @@ -58,8 +61,8 @@ QPID_AUTO_TEST_CASE(testDeclareTmp) BOOST_CHECK(qc.second); BOOST_CHECK_EQUAL(std::string("tmp_1"), qc.first->getName()); } - -QPID_AUTO_TEST_CASE(testFind) + +QPID_AUTO_TEST_CASE(testFind) { std::string foo("foo"); std::string bar("bar"); @@ -75,7 +78,7 @@ QPID_AUTO_TEST_CASE(testFind) BOOST_CHECK_EQUAL(bar, q->getName()); } -QPID_AUTO_TEST_CASE(testDestroy) +QPID_AUTO_TEST_CASE(testDestroy) { std::string foo("foo"); QueueRegistry reg; @@ -92,3 +95,5 @@ QPID_AUTO_TEST_CASE(testDestroy) } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/QueueTest.cpp b/qpid/cpp/src/tests/QueueTest.cpp index b70afa52a7..841a19f7c1 100644 --- a/qpid/cpp/src/tests/QueueTest.cpp +++ b/qpid/cpp/src/tests/QueueTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -39,9 +39,12 @@ using namespace qpid::broker; using namespace qpid::framing; using namespace qpid::sys; +namespace qpid { +namespace tests { + class TestConsumer : public virtual Consumer{ public: - typedef boost::shared_ptr shared_ptr; + typedef boost::shared_ptr shared_ptr; intrusive_ptr last; bool received; @@ -82,68 +85,68 @@ QPID_AUTO_TEST_SUITE(QueueTestSuite) QPID_AUTO_TEST_CASE(testAsyncMessage) { Queue::shared_ptr queue(new Queue("my_test_queue", true)); intrusive_ptr received; - + TestConsumer::shared_ptr c1(new TestConsumer()); queue->consume(c1); - - + + //Test basic delivery: intrusive_ptr msg1 = create_message("e", "A"); msg1->enqueueAsync(queue, 0);//this is done on enqueue which is not called from process queue->process(msg1); sleep(2); - + BOOST_CHECK(!c1->received); msg1->enqueueComplete(); - + received = queue->get().payload; - BOOST_CHECK_EQUAL(msg1.get(), received.get()); + BOOST_CHECK_EQUAL(msg1.get(), received.get()); } - - + + QPID_AUTO_TEST_CASE(testAsyncMessageCount){ Queue::shared_ptr queue(new Queue("my_test_queue", true)); intrusive_ptr msg1 = create_message("e", "A"); msg1->enqueueAsync(queue, 0);//this is done on enqueue which is not called from process - + queue->process(msg1); sleep(2); uint32_t compval=0; BOOST_CHECK_EQUAL(compval, queue->getMessageCount()); msg1->enqueueComplete(); compval=1; - BOOST_CHECK_EQUAL(compval, queue->getMessageCount()); + BOOST_CHECK_EQUAL(compval, queue->getMessageCount()); } QPID_AUTO_TEST_CASE(testConsumers){ Queue::shared_ptr queue(new Queue("my_queue", true)); - + //Test adding consumers: TestConsumer::shared_ptr c1(new TestConsumer()); TestConsumer::shared_ptr c2(new TestConsumer()); queue->consume(c1); queue->consume(c2); - + BOOST_CHECK_EQUAL(uint32_t(2), queue->getConsumerCount()); - + //Test basic delivery: intrusive_ptr msg1 = create_message("e", "A"); intrusive_ptr msg2 = create_message("e", "B"); intrusive_ptr msg3 = create_message("e", "C"); - + queue->deliver(msg1); BOOST_CHECK(queue->dispatch(c1)); BOOST_CHECK_EQUAL(msg1.get(), c1->last.get()); - + queue->deliver(msg2); BOOST_CHECK(queue->dispatch(c2)); BOOST_CHECK_EQUAL(msg2.get(), c2->last.get()); - + c1->received = false; queue->deliver(msg3); BOOST_CHECK(queue->dispatch(c1)); - BOOST_CHECK_EQUAL(msg3.get(), c1->last.get()); - + BOOST_CHECK_EQUAL(msg3.get(), c1->last.get()); + //Test cancellation: queue->cancel(c1); BOOST_CHECK_EQUAL(uint32_t(1), queue->getConsumerCount()); @@ -157,15 +160,15 @@ QPID_AUTO_TEST_CASE(testRegistry){ registry.declare("queue1", true, true); registry.declare("queue2", true, true); registry.declare("queue3", true, true); - + BOOST_CHECK(registry.find("queue1")); BOOST_CHECK(registry.find("queue2")); BOOST_CHECK(registry.find("queue3")); - + registry.destroy("queue1"); registry.destroy("queue2"); registry.destroy("queue3"); - + BOOST_CHECK(!registry.find("queue1")); BOOST_CHECK(!registry.find("queue2")); BOOST_CHECK(!registry.find("queue3")); @@ -177,13 +180,13 @@ QPID_AUTO_TEST_CASE(testDequeue){ intrusive_ptr msg2 = create_message("e", "B"); intrusive_ptr msg3 = create_message("e", "C"); intrusive_ptr received; - + queue->deliver(msg1); queue->deliver(msg2); queue->deliver(msg3); - + BOOST_CHECK_EQUAL(uint32_t(3), queue->getMessageCount()); - + received = queue->get().payload; BOOST_CHECK_EQUAL(msg1.get(), received.get()); BOOST_CHECK_EQUAL(uint32_t(2), queue->getMessageCount()); @@ -204,7 +207,7 @@ QPID_AUTO_TEST_CASE(testDequeue){ received = queue->get().payload; BOOST_CHECK(!received); BOOST_CHECK_EQUAL(uint32_t(0), queue->getMessageCount()); - + } QPID_AUTO_TEST_CASE(testBound) @@ -236,7 +239,7 @@ QPID_AUTO_TEST_CASE(testBound) queue->unbind(exchanges, queue); //ensure the remaining exchanges don't still have the queue bound to them: - FailOnDeliver deliverable; + FailOnDeliver deliverable; exchange1->route(deliverable, key, &args); exchange3->route(deliverable, key, &args); } @@ -245,10 +248,10 @@ QPID_AUTO_TEST_CASE(testPersistLastNodeStanding){ client::QueueOptions args; args.setPersistLastNode(); - + Queue::shared_ptr queue(new Queue("my-queue", true)); queue->configure(args); - + intrusive_ptr msg1 = create_message("e", "A"); intrusive_ptr msg2 = create_message("e", "B"); intrusive_ptr msg3 = create_message("e", "C"); @@ -256,13 +259,13 @@ QPID_AUTO_TEST_CASE(testPersistLastNodeStanding){ //enqueue 2 messages queue->deliver(msg1); queue->deliver(msg2); - + //change mode queue->setLastNodeFailure(); - + //enqueue 1 message queue->deliver(msg3); - + //check all have persistent ids. BOOST_CHECK(msg1->isPersistent()); BOOST_CHECK(msg2->isPersistent()); @@ -277,7 +280,7 @@ class TestMessageStoreOC : public NullMessageStore uint enqCnt; uint deqCnt; bool error; - + virtual void dequeue(TransactionContext*, const boost::intrusive_ptr& /*msg*/, const PersistableQueue& /*queue*/) @@ -298,7 +301,7 @@ class TestMessageStoreOC : public NullMessageStore { error=true; } - + TestMessageStoreOC() : NullMessageStore(),enqCnt(0),deqCnt(0),error(false) {} ~TestMessageStoreOC(){} }; @@ -312,7 +315,7 @@ QPID_AUTO_TEST_CASE(testLVQOrdering){ Queue::shared_ptr queue(new Queue("my-queue", true )); queue->configure(args); - + intrusive_ptr msg1 = create_message("e", "A"); intrusive_ptr msg2 = create_message("e", "B"); intrusive_ptr msg3 = create_message("e", "C"); @@ -324,27 +327,27 @@ QPID_AUTO_TEST_CASE(testLVQOrdering){ string key; args.getLVQKey(key); BOOST_CHECK_EQUAL(key, "qpid.LVQ_key"); - + msg1->getProperties()->getApplicationHeaders().setString(key,"a"); msg2->getProperties()->getApplicationHeaders().setString(key,"b"); msg3->getProperties()->getApplicationHeaders().setString(key,"c"); msg4->getProperties()->getApplicationHeaders().setString(key,"a"); - + //enqueue 4 message queue->deliver(msg1); queue->deliver(msg2); queue->deliver(msg3); queue->deliver(msg4); - + BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); - + received = queue->get().payload; BOOST_CHECK_EQUAL(msg4.get(), received.get()); received = queue->get().payload; BOOST_CHECK_EQUAL(msg2.get(), received.get()); - + received = queue->get().payload; BOOST_CHECK_EQUAL(msg3.get(), received.get()); @@ -357,18 +360,18 @@ QPID_AUTO_TEST_CASE(testLVQOrdering){ queue->deliver(msg5); queue->deliver(msg6); queue->deliver(msg7); - + BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); - + received = queue->get().payload; BOOST_CHECK_EQUAL(msg5.get(), received.get()); received = queue->get().payload; BOOST_CHECK_EQUAL(msg6.get(), received.get()); - + received = queue->get().payload; BOOST_CHECK_EQUAL(msg7.get(), received.get()); - + } QPID_AUTO_TEST_CASE(testLVQEmptyKey){ @@ -379,20 +382,20 @@ QPID_AUTO_TEST_CASE(testLVQEmptyKey){ Queue::shared_ptr queue(new Queue("my-queue", true )); queue->configure(args); - + intrusive_ptr msg1 = create_message("e", "A"); intrusive_ptr msg2 = create_message("e", "B"); string key; args.getLVQKey(key); BOOST_CHECK_EQUAL(key, "qpid.LVQ_key"); - + msg1->getProperties()->getApplicationHeaders().setString(key,"a"); queue->deliver(msg1); queue->deliver(msg2); BOOST_CHECK_EQUAL(queue->getMessageCount(), 2u); - + } QPID_AUTO_TEST_CASE(testLVQAcquire){ @@ -403,7 +406,7 @@ QPID_AUTO_TEST_CASE(testLVQAcquire){ Queue::shared_ptr queue(new Queue("my-queue", true )); queue->configure(args); - + intrusive_ptr msg1 = create_message("e", "A"); intrusive_ptr msg2 = create_message("e", "B"); intrusive_ptr msg3 = create_message("e", "C"); @@ -416,7 +419,7 @@ QPID_AUTO_TEST_CASE(testLVQAcquire){ string key; args.getLVQKey(key); BOOST_CHECK_EQUAL(key, "qpid.LVQ_key"); - + msg1->getProperties()->getApplicationHeaders().setString(key,"a"); msg2->getProperties()->getApplicationHeaders().setString(key,"b"); @@ -424,13 +427,13 @@ QPID_AUTO_TEST_CASE(testLVQAcquire){ msg4->getProperties()->getApplicationHeaders().setString(key,"a"); msg5->getProperties()->getApplicationHeaders().setString(key,"b"); msg6->getProperties()->getApplicationHeaders().setString(key,"c"); - + //enqueue 4 message queue->deliver(msg1); queue->deliver(msg2); queue->deliver(msg3); queue->deliver(msg4); - + BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); framing::SequenceNumber sequence(1); @@ -439,9 +442,9 @@ QPID_AUTO_TEST_CASE(testLVQAcquire){ BOOST_CHECK(!queue->acquire(qmsg)); BOOST_CHECK(queue->acquire(qmsg2)); - + BOOST_CHECK_EQUAL(queue->getMessageCount(), 2u); - + queue->deliver(msg5); BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); @@ -449,11 +452,11 @@ QPID_AUTO_TEST_CASE(testLVQAcquire){ args.setOrdering(client::LVQ_NO_BROWSE); queue->configure(args); TestConsumer::shared_ptr c1(new TestConsumer(false)); - + queue->dispatch(c1); queue->dispatch(c1); queue->dispatch(c1); - + queue->deliver(msg6); BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); @@ -474,7 +477,7 @@ QPID_AUTO_TEST_CASE(testLVQMultiQueue){ intrusive_ptr received; queue1->configure(args); queue2->configure(args); - + intrusive_ptr msg1 = create_message("e", "A"); intrusive_ptr msg2 = create_message("e", "A"); @@ -484,17 +487,17 @@ QPID_AUTO_TEST_CASE(testLVQMultiQueue){ msg1->getProperties()->getApplicationHeaders().setString(key,"a"); msg2->getProperties()->getApplicationHeaders().setString(key,"a"); - + queue1->deliver(msg1); queue2->deliver(msg1); queue1->deliver(msg2); - + received = queue1->get().payload; BOOST_CHECK_EQUAL(msg2.get(), received.get()); received = queue2->get().payload; BOOST_CHECK_EQUAL(msg1.get(), received.get()); - + } QPID_AUTO_TEST_CASE(testLVQRecover){ @@ -518,7 +521,7 @@ QPID_AUTO_TEST_CASE(testLVQRecover){ Queue::shared_ptr queue1(new Queue("my-queue", true, &testStore)); intrusive_ptr received; queue1->configure(args); - + intrusive_ptr msg1 = create_message("e", "A"); intrusive_ptr msg2 = create_message("e", "A"); // 2 @@ -544,7 +547,7 @@ QPID_AUTO_TEST_CASE(testLVQRecover){ BOOST_CHECK_EQUAL(testStore.deqCnt, 1u); } -void addMessagesToQueue(uint count, Queue& queue, uint oddTtl = 200, uint evenTtl = 0) +void addMessagesToQueue(uint count, Queue& queue, uint oddTtl = 200, uint evenTtl = 0) { for (uint i = 0; i < count; i++) { intrusive_ptr m = create_message("exchange", "key"); @@ -592,7 +595,7 @@ QPID_AUTO_TEST_CASE(testMultiQueueLastNode){ queue1->configure(args); Queue::shared_ptr queue2(new Queue("queue2", true, &testStore )); queue2->configure(args); - + intrusive_ptr msg1 = create_message("e", "A"); queue1->deliver(msg1); @@ -623,7 +626,7 @@ QPID_AUTO_TEST_CASE(testMultiQueueLastNode){ // check no failure messages are stored queue1->clearLastNodeFailure(); queue2->clearLastNodeFailure(); - + intrusive_ptr msg3 = create_message("e", "B"); queue1->deliver(msg3); queue2->deliver(msg3); @@ -631,7 +634,7 @@ QPID_AUTO_TEST_CASE(testMultiQueueLastNode){ queue1->setLastNodeFailure(); queue2->setLastNodeFailure(); BOOST_CHECK_EQUAL(testStore.enqCnt, 6u); - + // check requeue 1 intrusive_ptr msg4 = create_message("e", "C"); intrusive_ptr msg5 = create_message("e", "D"); @@ -639,17 +642,17 @@ QPID_AUTO_TEST_CASE(testMultiQueueLastNode){ framing::SequenceNumber sequence(1); QueuedMessage qmsg1(queue1.get(), msg4, sequence); QueuedMessage qmsg2(queue2.get(), msg5, ++sequence); - + queue1->requeue(qmsg1); BOOST_CHECK_EQUAL(testStore.enqCnt, 7u); - + // check requeue 2 queue2->clearLastNodeFailure(); queue2->requeue(qmsg2); BOOST_CHECK_EQUAL(testStore.enqCnt, 7u); queue2->setLastNodeFailure(); BOOST_CHECK_EQUAL(testStore.enqCnt, 8u); - + queue2->clearLastNodeFailure(); queue2->setLastNodeFailure(); BOOST_CHECK_EQUAL(testStore.enqCnt, 8u); @@ -664,8 +667,8 @@ simulate this: 4. stop and recover remaining node 5. add another node 6. kill that new node again -make sure that an attempt to re-enqueue a message does not happen which will -result in the last man standing exiting with an error. +make sure that an attempt to re-enqueue a message does not happen which will +result in the last man standing exiting with an error. we need to make sure that recover is safe, i.e. messages are not requeued to the store. @@ -678,7 +681,7 @@ not requeued to the store. Queue::shared_ptr queue1(new Queue("my-queue", true, &testStore)); intrusive_ptr received; queue1->configure(args); - + // check requeue 1 intrusive_ptr msg1 = create_message("e", "C"); intrusive_ptr msg2 = create_message("e", "D"); @@ -711,17 +714,29 @@ simulate store excption going into last node standing Queue::shared_ptr queue1(new Queue("my-queue", true, &testStore)); intrusive_ptr received; queue1->configure(args); - + // check requeue 1 intrusive_ptr msg1 = create_message("e", "C"); queue1->deliver(msg1); testStore.createError(); - + ScopedSuppressLogging sl; // Suppress messages for expected errors. queue1->setLastNodeFailure(); BOOST_CHECK_EQUAL(testStore.enqCnt, 0u); -}QPID_AUTO_TEST_SUITE_END() +} + +intrusive_ptr mkMsg(std::string exchange, std::string routingKey) { + intrusive_ptr msg(new Message()); + AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0))); + AMQFrame header((AMQHeaderBody())); + msg->getFrames().append(method); + msg->getFrames().append(header); + msg->getFrames().getHeaders()->get(true)->setRoutingKey(routingKey); + return msg; +} +QPID_AUTO_TEST_SUITE_END() +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/RangeSet.cpp b/qpid/cpp/src/tests/RangeSet.cpp index 9c602de78d..db3a964086 100644 --- a/qpid/cpp/src/tests/RangeSet.cpp +++ b/qpid/cpp/src/tests/RangeSet.cpp @@ -24,6 +24,9 @@ using namespace std; using namespace qpid; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(RangeSetTestSuite) typedef qpid::Range TestRange; @@ -44,8 +47,8 @@ QPID_AUTO_TEST_CASE(testRangeSetAddPoint) { BOOST_CHECK_MESSAGE(r.contains(TestRange(3,4)), r); BOOST_CHECK(!r.empty()); r += 5; - BOOST_CHECK_MESSAGE(r.contains(5), r); - BOOST_CHECK_MESSAGE(r.contains(TestRange(5,6)), r); + BOOST_CHECK_MESSAGE(r.contains(5), r); + BOOST_CHECK_MESSAGE(r.contains(TestRange(5,6)), r); BOOST_CHECK_MESSAGE(!r.contains(TestRange(3,6)), r); r += 4; BOOST_CHECK_MESSAGE(r.contains(TestRange(3,6)), r); @@ -139,3 +142,5 @@ QPID_AUTO_TEST_CASE(testRangeContaining) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/RateFlowcontrolTest.cpp b/qpid/cpp/src/tests/RateFlowcontrolTest.cpp index b8fda09f61..80ad06af8c 100644 --- a/qpid/cpp/src/tests/RateFlowcontrolTest.cpp +++ b/qpid/cpp/src/tests/RateFlowcontrolTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -27,25 +27,28 @@ using namespace qpid::broker; using namespace qpid::sys; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(RateFlowcontrolTestSuite) QPID_AUTO_TEST_CASE(RateFlowcontrolTest) { // BOOST_CHECK(predicate); // BOOST_CHECK_EQUAL(a, b); - + RateFlowcontrol fc(100); AbsTime n=AbsTime::now(); - + BOOST_CHECK_EQUAL( fc.receivedMessage(n, 0), 0U ); - + fc.sentCredit(n, 0); - + BOOST_CHECK_EQUAL( fc.receivedMessage(n, 0), 0U ); fc.sentCredit(n, 50); Duration d=250*TIME_MSEC; - + n = AbsTime(n,d); BOOST_CHECK_EQUAL( fc.receivedMessage(n, 25), 0U ); BOOST_CHECK_EQUAL( fc.availableCredit(n), 25U ); @@ -64,3 +67,5 @@ QPID_AUTO_TEST_CASE(RateFlowcontrolTest) } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/RefCounted.cpp b/qpid/cpp/src/tests/RefCounted.cpp index 8c679a3d2e..e4c1da5696 100644 --- a/qpid/cpp/src/tests/RefCounted.cpp +++ b/qpid/cpp/src/tests/RefCounted.cpp @@ -27,6 +27,9 @@ using boost::intrusive_ptr; using namespace std; using namespace qpid; +namespace qpid { +namespace tests { + struct CountMe : public RefCounted { static int instances; CountMe() { ++instances; } @@ -48,3 +51,5 @@ QPID_AUTO_TEST_CASE(testRefCounted) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/ReplicationTest.cpp b/qpid/cpp/src/tests/ReplicationTest.cpp index 38dc1a9e52..ed768f1306 100644 --- a/qpid/cpp/src/tests/ReplicationTest.cpp +++ b/qpid/cpp/src/tests/ReplicationTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -42,6 +42,9 @@ using namespace qpid::framing; using namespace qpid::replication::constants; using boost::assign::list_of; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ReplicationTestSuite) // The CMake-based build passes in the module suffix; if it's not there, this @@ -63,7 +66,7 @@ qpid::broker::Broker::Options getBrokerOpts(const std::vector& args return opts; } -QPID_AUTO_TEST_CASE(testReplicationExchange) +QPID_AUTO_TEST_CASE(testReplicationExchange) { qpid::broker::Broker::Options brokerOpts(getBrokerOpts(list_of("qpidd") ("--replication-exchange-name=qpid.replication"))); @@ -79,7 +82,7 @@ QPID_AUTO_TEST_CASE(testReplicationExchange) f.session.queueDeclare(arg::queue=eventQ, arg::exclusive=true, arg::autoDelete=true, arg::arguments=eventQopts); f.session.exchangeBind(arg::exchange="qpid.replication", arg::queue=eventQ, arg::bindingKey=dataQ); - + f.session.queueDeclare(arg::queue=eventQ2, arg::exclusive=true, arg::autoDelete=true, arg::arguments=eventQopts); f.session.exchangeBind(arg::exchange="qpid.replication", arg::queue=eventQ2, arg::bindingKey=dataQ2); @@ -133,3 +136,5 @@ QPID_AUTO_TEST_CASE(testReplicationExchange) QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/RetryList.cpp b/qpid/cpp/src/tests/RetryList.cpp index 80f59bf15f..d1d22348a3 100644 --- a/qpid/cpp/src/tests/RetryList.cpp +++ b/qpid/cpp/src/tests/RetryList.cpp @@ -24,6 +24,9 @@ using namespace qpid; using namespace qpid::broker; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(RetryListTestSuite) struct RetryListFixture @@ -36,7 +39,7 @@ struct RetryListFixture { urls.push_back(Url(s)); } - + void addExpectation(const std::string& host, uint16_t port) { expected.push_back(TcpAddress(host, port)); @@ -57,7 +60,7 @@ struct RetryListFixture } }; -QPID_AUTO_TEST_CASE(testWithSingleAddress) +QPID_AUTO_TEST_CASE(testWithSingleAddress) { RetryListFixture test; test.addUrl("amqp:host:5673"); @@ -65,7 +68,7 @@ QPID_AUTO_TEST_CASE(testWithSingleAddress) test.check(); } -QPID_AUTO_TEST_CASE(testWithSingleUrlOfMultipleAddresses) +QPID_AUTO_TEST_CASE(testWithSingleUrlOfMultipleAddresses) { RetryListFixture test; test.addUrl("amqp:host1,host2:2222,tcp:host3:5673,host4:1"); @@ -78,7 +81,7 @@ QPID_AUTO_TEST_CASE(testWithSingleUrlOfMultipleAddresses) test.check(); } -QPID_AUTO_TEST_CASE(testWithMultipleUrlsOfMultipleAddresses) +QPID_AUTO_TEST_CASE(testWithMultipleUrlsOfMultipleAddresses) { RetryListFixture test; test.addUrl("amqp:my-host"); @@ -97,10 +100,12 @@ QPID_AUTO_TEST_CASE(testWithMultipleUrlsOfMultipleAddresses) test.check(); } -QPID_AUTO_TEST_CASE(testEmptyList) +QPID_AUTO_TEST_CASE(testEmptyList) { RetryListFixture test; test.check(); } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/SequenceNumberTest.cpp b/qpid/cpp/src/tests/SequenceNumberTest.cpp index e4c6d066ef..f3c934e3ca 100644 --- a/qpid/cpp/src/tests/SequenceNumberTest.cpp +++ b/qpid/cpp/src/tests/SequenceNumberTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -26,6 +26,8 @@ using namespace qpid::framing; +namespace qpid { +namespace tests { void checkDifference(SequenceNumber& a, SequenceNumber& b, int gap) { @@ -54,7 +56,7 @@ void checkComparison(SequenceNumber& a, SequenceNumber& b, int gap) BOOST_CHECK(++a < ++b);//test prefix } //keep incrementing until a also wraps around - for (int i = 0; i < (gap + 2); i++) { + for (int i = 0; i < (gap + 2); i++) { BOOST_CHECK(a++ < b++);//test postfix } //let a 'catch up' @@ -91,7 +93,7 @@ QPID_AUTO_TEST_CASE(testIncrementPostfix) BOOST_CHECK(b != c); } -QPID_AUTO_TEST_CASE(testIncrementPrefix) +QPID_AUTO_TEST_CASE(testIncrementPrefix) { SequenceNumber a; SequenceNumber b; @@ -203,3 +205,5 @@ QPID_AUTO_TEST_CASE(testDifferenceWithWrapAround2) } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/SequenceSet.cpp b/qpid/cpp/src/tests/SequenceSet.cpp index ba2f1391a1..aaeb68e3c5 100644 --- a/qpid/cpp/src/tests/SequenceSet.cpp +++ b/qpid/cpp/src/tests/SequenceSet.cpp @@ -20,6 +20,9 @@ #include "unit_test.h" #include +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(SequenceSetTestSuite) using namespace qpid::framing; @@ -72,7 +75,7 @@ QPID_AUTO_TEST_CASE(testAdd) { BOOST_CHECK(!s.contains(i)); RangeExpectations().expect(2, 5).expect(8, 8).check(s); - + SequenceSet t; t.add(6, 10); t.add(s); @@ -90,7 +93,7 @@ QPID_AUTO_TEST_CASE(testAdd2) { SequenceSet s; s.add(7,6); s.add(4,4); - s.add(3,10); + s.add(3,10); s.add(2); RangeExpectations().expect(2, 10).check(s); } @@ -137,4 +140,4 @@ QPID_AUTO_TEST_CASE(testRemove) { QPID_AUTO_TEST_SUITE_END() - +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/SessionState.cpp b/qpid/cpp/src/tests/SessionState.cpp index 5e21ff2b70..157cabfb63 100644 --- a/qpid/cpp/src/tests/SessionState.cpp +++ b/qpid/cpp/src/tests/SessionState.cpp @@ -28,6 +28,9 @@ #include #include +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(SessionStateTestSuite) using namespace std; @@ -94,7 +97,7 @@ size_t transfer1(qpid::SessionState& s, string content) { size_t transfer1Char(qpid::SessionState& s, char content) { return transfer1(s, string(1,content)); } - + // Send transfer frame with multiple single-byte content frames. size_t transferN(qpid::SessionState& s, string content) { size_t size=send(s, transferFrame(!content.empty())); @@ -134,7 +137,7 @@ QPID_AUTO_TEST_CASE(testSendGetReplyList) { BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(0,0))),"CabcCdCeCfCxyz"); // Ignore controls. s.senderRecord(AMQFrame(new SessionFlushBody())); - BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(2,0))),"CeCfCxyz"); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(2,0))),"CeCfCxyz"); } QPID_AUTO_TEST_CASE(testNeedFlush) { @@ -185,7 +188,7 @@ QPID_AUTO_TEST_CASE(testPeerConfirmed) { s.senderConfirmed(SessionPoint(5)); BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(5,0))), "CxCy"); BOOST_CHECK(s.senderNeedFlush()); - + s.senderConfirmed(SessionPoint(6)); BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(6,0))), "Cy"); BOOST_CHECK(!s.senderNeedFlush()); @@ -195,7 +198,7 @@ QPID_AUTO_TEST_CASE(testPeerCompleted) { qpid::SessionState s; s.setTimeout(1); s.senderGetCommandPoint(); - // Completion implies confirmation + // Completion implies confirmation transfers(s, "abc"); BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(0,0))), "CaCbCc"); SequenceSet set(SequenceSet() + 0 + 1); @@ -205,7 +208,7 @@ QPID_AUTO_TEST_CASE(testPeerCompleted) { transfers(s, "def"); // We dont do out-of-order confirmation, so this will only confirm up to 3: set = SequenceSet(SequenceSet() + 2 + 3 + 5); - s.senderCompleted(set); + s.senderCompleted(set); BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(4,0))), "CeCf"); } @@ -215,11 +218,11 @@ QPID_AUTO_TEST_CASE(testReceive) { s.receiverSetCommandPoint(SessionPoint()); BOOST_CHECK_EQUAL(s.receiverGetExpected(), SessionPoint(0)); BOOST_CHECK_EQUAL(s.receiverGetReceived(), SessionPoint(0)); - + BOOST_CHECK(s.receiverRecord(transferFrame(false))); BOOST_CHECK_EQUAL(s.receiverGetExpected(), SessionPoint(1)); BOOST_CHECK_EQUAL(s.receiverGetReceived(), SessionPoint(1)); - + BOOST_CHECK(s.receiverRecord(transferFrame(true))); SessionPoint point = SessionPoint(1, transferFrameSize()); BOOST_CHECK_EQUAL(s.receiverGetExpected(), point); @@ -297,3 +300,5 @@ QPID_AUTO_TEST_CASE(testNeedKnownCompleted) { QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/Shlib.cpp b/qpid/cpp/src/tests/Shlib.cpp index 7d2f2456c7..692cfcdff9 100644 --- a/qpid/cpp/src/tests/Shlib.cpp +++ b/qpid/cpp/src/tests/Shlib.cpp @@ -24,6 +24,9 @@ #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ShlibTestSuite) using namespace qpid::sys; @@ -51,7 +54,7 @@ QPID_AUTO_TEST_CASE(testShlib) { } catch (const qpid::Exception&) {} } - + QPID_AUTO_TEST_CASE(testAutoShlib) { int unloaded = 0; { @@ -66,6 +69,8 @@ QPID_AUTO_TEST_CASE(testAutoShlib) { } BOOST_CHECK_EQUAL(42, unloaded); } - + QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/SocketProxy.h b/qpid/cpp/src/tests/SocketProxy.h index ccce3c8842..9df32a1336 100644 --- a/qpid/cpp/src/tests/SocketProxy.h +++ b/qpid/cpp/src/tests/SocketProxy.h @@ -35,8 +35,11 @@ #include "qpid/sys/Mutex.h" #include "qpid/log/Statement.h" +namespace qpid { +namespace tests { + /** - * A simple socket proxy that forwards to another socket. + * A simple socket proxy that forwards to another socket. * Used between client & local broker to simulate network failures. */ class SocketProxy : private qpid::sys::Runnable @@ -59,7 +62,7 @@ class SocketProxy : private qpid::sys::Runnable joined = false; thread = qpid::sys::Thread(static_cast(this)); } - + ~SocketProxy() { close(); if (!joined) thread.join(); } /** Simulate a network disconnect. */ @@ -88,7 +91,7 @@ class SocketProxy : private qpid::sys::Runnable } uint16_t getPort() const { return port; } - + private: static void throwErrno(const std::string& msg) { throw qpid::Exception(msg+":"+qpid::sys::strError(errno)); @@ -153,7 +156,7 @@ class SocketProxy : private qpid::sys::Runnable } try { if (server.get()) server->close(); - close(); + close(); } catch (const std::exception& e) { QPID_LOG(debug, "SocketProxy::run exception in client/server close()" << e.what()); @@ -169,4 +172,6 @@ class SocketProxy : private qpid::sys::Runnable bool dropClient, dropServer; }; +}} // namespace qpid::tests + #endif diff --git a/qpid/cpp/src/tests/TestMessageStore.h b/qpid/cpp/src/tests/TestMessageStore.h index be1ed57349..20e0b755b2 100644 --- a/qpid/cpp/src/tests/TestMessageStore.h +++ b/qpid/cpp/src/tests/TestMessageStore.h @@ -10,9 +10,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -28,6 +28,9 @@ using namespace qpid; using namespace qpid::broker; using namespace qpid::framing; +namespace qpid { +namespace tests { + typedef std::pair > msg_queue_pair; class TestMessageStore : public NullMessageStore @@ -35,7 +38,7 @@ class TestMessageStore : public NullMessageStore public: std::vector > dequeued; std::vector enqueued; - + void dequeue(TransactionContext*, const boost::intrusive_ptr& msg, const PersistableQueue& /*queue*/) @@ -47,7 +50,7 @@ class TestMessageStore : public NullMessageStore const boost::intrusive_ptr& msg, const PersistableQueue& queue) { - msg->enqueueComplete(); + msg->enqueueComplete(); enqueued.push_back(msg_queue_pair(queue.getName(), msg)); } @@ -55,4 +58,6 @@ class TestMessageStore : public NullMessageStore ~TestMessageStore(){} }; +}} // namespace qpid::tests + #endif diff --git a/qpid/cpp/src/tests/TestOptions.h b/qpid/cpp/src/tests/TestOptions.h index a400fe5ecb..f8da0f59cf 100644 --- a/qpid/cpp/src/tests/TestOptions.h +++ b/qpid/cpp/src/tests/TestOptions.h @@ -9,9 +9,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -67,7 +67,7 @@ struct TestOptions : public qpid::Options connection.open(con); } - + bool help; ConnectionOptions con; qpid::log::Options log; diff --git a/qpid/cpp/src/tests/TimerTest.cpp b/qpid/cpp/src/tests/TimerTest.cpp index 2642c980ba..1552421ba0 100644 --- a/qpid/cpp/src/tests/TimerTest.cpp +++ b/qpid/cpp/src/tests/TimerTest.cpp @@ -8,9 +8,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -32,6 +32,9 @@ using namespace qpid::sys; using boost::intrusive_ptr; using boost::dynamic_pointer_cast; +namespace qpid { +namespace tests { + class Counter { Mutex lock; @@ -44,7 +47,7 @@ class Counter return ++counter; } }; - + class TestTask : public TimerTask { const AbsTime start; @@ -56,7 +59,7 @@ class TestTask : public TimerTask Counter& counter; public: - TestTask(Duration timeout, Counter& _counter) + TestTask(Duration timeout, Counter& _counter) : TimerTask(timeout), start(now()), expected(timeout), end(start), fired(false), counter(_counter) {} void fire() @@ -106,14 +109,14 @@ QPID_AUTO_TEST_CASE(testGeneral) intrusive_ptr task2(new TestTask(Duration(1 * TIME_SEC), counter)); intrusive_ptr task3(new TestTask(Duration(4 * TIME_SEC), counter)); intrusive_ptr task4(new TestTask(Duration(2 * TIME_SEC), counter)); - + timer.add(task1); timer.add(task2); timer.add(task3); timer.add(task4); - + dynamic_pointer_cast(task3)->wait(Duration(6 * TIME_SEC)); - + dynamic_pointer_cast(task1)->check(3); dynamic_pointer_cast(task2)->check(1); dynamic_pointer_cast(task3)->check(4); @@ -121,3 +124,5 @@ QPID_AUTO_TEST_CASE(testGeneral) } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/TopicExchangeTest.cpp b/qpid/cpp/src/tests/TopicExchangeTest.cpp index d707066534..c103620dbf 100644 --- a/qpid/cpp/src/tests/TopicExchangeTest.cpp +++ b/qpid/cpp/src/tests/TopicExchangeTest.cpp @@ -6,9 +6,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -22,11 +22,15 @@ using namespace qpid::broker; using namespace std; + +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(TopicExchangeTestSuite) #define CHECK_NORMALIZED(expect, pattern) BOOST_CHECK_EQUAL(expect, TopicExchange::normalize(pattern)); -QPID_AUTO_TEST_CASE(testNormalize) +QPID_AUTO_TEST_CASE(testNormalize) { CHECK_NORMALIZED("", ""); CHECK_NORMALIZED("a.b.c", "a.b.c"); @@ -38,8 +42,8 @@ QPID_AUTO_TEST_CASE(testNormalize) CHECK_NORMALIZED("a.*.*.*.#", "a.*.#.*.#.*"); CHECK_NORMALIZED("*.*.*.#", "*.#.#.*.*.#"); } - -QPID_AUTO_TEST_CASE(testPlain) + +QPID_AUTO_TEST_CASE(testPlain) { string pattern("ab.cd.e"); BOOST_CHECK(TopicExchange::match(pattern, "ab.cd.e")); @@ -57,7 +61,7 @@ QPID_AUTO_TEST_CASE(testPlain) } -QPID_AUTO_TEST_CASE(testStar) +QPID_AUTO_TEST_CASE(testStar) { string pattern("a.*.b"); BOOST_CHECK(TopicExchange::match(pattern, "a.xx.b")); @@ -75,7 +79,7 @@ QPID_AUTO_TEST_CASE(testStar) BOOST_CHECK(!TopicExchange::match(pattern, "q.x.y")); } -QPID_AUTO_TEST_CASE(testHash) +QPID_AUTO_TEST_CASE(testHash) { string pattern("a.#.b"); BOOST_CHECK(TopicExchange::match(pattern, "a.b")); @@ -99,7 +103,7 @@ QPID_AUTO_TEST_CASE(testHash) BOOST_CHECK(TopicExchange::match(pattern, "a.x.x.b.y.y.c")); } -QPID_AUTO_TEST_CASE(testMixed) +QPID_AUTO_TEST_CASE(testMixed) { string pattern("*.x.#.y"); BOOST_CHECK(TopicExchange::match(pattern, "a.x.y")); @@ -119,3 +123,5 @@ QPID_AUTO_TEST_CASE(testMixed) } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/TxBufferTest.cpp b/qpid/cpp/src/tests/TxBufferTest.cpp index 3d6a12cacc..4807026ab7 100644 --- a/qpid/cpp/src/tests/TxBufferTest.cpp +++ b/qpid/cpp/src/tests/TxBufferTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -27,6 +27,9 @@ using namespace qpid::broker; using boost::static_pointer_cast; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(TxBufferTestSuite) QPID_AUTO_TEST_CASE(testCommitLocal) @@ -174,3 +177,5 @@ QPID_AUTO_TEST_CASE(testBufferIsClearedAfterCommit) } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/TxMocks.h b/qpid/cpp/src/tests/TxMocks.h index fe103c5fe5..a34d864bae 100644 --- a/qpid/cpp/src/tests/TxMocks.h +++ b/qpid/cpp/src/tests/TxMocks.h @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -32,6 +32,9 @@ using namespace qpid::broker; using boost::static_pointer_cast; using std::string; +namespace qpid { +namespace tests { + template void assertEqualVector(std::vector& expected, std::vector& actual){ unsigned int i = 0; while(i < expected.size() && i < actual.size()){ @@ -62,15 +65,15 @@ class MockTxOp : public TxOp, public TxOpConstants{ string debugName; public: typedef boost::shared_ptr shared_ptr; - + MockTxOp() : failOnPrepare(false) {} MockTxOp(bool _failOnPrepare) : failOnPrepare(_failOnPrepare) {} - + void setDebugName(string name){ debugName = name; } - void printExpected(){ + void printExpected(){ std::cout << std::endl << "MockTxOp[" << debugName << "] expects: "; for (std::vector::iterator i = expected.begin(); i < expected.end(); i++) { if(i != expected.begin()) std::cout << ", "; @@ -79,7 +82,7 @@ public: std::cout << std::endl; } - void printActual(){ + void printActual(){ std::cout << std::endl << "MockTxOp[" << debugName << "] actual: "; for (std::vector::iterator i = actual.begin(); i < actual.end(); i++) { if(i != actual.begin()) std::cout << ", "; @@ -87,7 +90,7 @@ public: } std::cout << std::endl; } - + bool prepare(TransactionContext*) throw(){ actual.push_back(PREPARE); return !failOnPrepare; @@ -116,8 +119,8 @@ public: } void accept(TxOpConstVisitor&) const {} - - ~MockTxOp(){} + + ~MockTxOp(){} }; class MockTransactionalStore : public TransactionalStore{ @@ -128,10 +131,10 @@ class MockTransactionalStore : public TransactionalStore{ const string ABORT; std::vector expected; std::vector actual; - + enum states {OPEN = 1, PREPARED = 2, COMMITTED = 3, ABORTED = 4}; int state; - + class TestTransactionContext : public TPCTransactionContext{ MockTransactionalStore* store; public: @@ -145,29 +148,29 @@ class MockTransactionalStore : public TransactionalStore{ if(!store->isOpen() && !store->isPrepared()) throw "txn already completed"; store->state = COMMITTED; } - + void abort(){ if(!store->isOpen() && !store->isPrepared()) throw "txn already completed"; store->state = ABORTED; } ~TestTransactionContext(){} }; - + public: MockTransactionalStore() : BEGIN("BEGIN"), BEGIN2PC("BEGIN2PC"), PREPARE("PREPARE"), COMMIT("COMMIT"), ABORT("ABORT"), state(OPEN){} void collectPreparedXids(std::set&) { - throw "Operation not supported"; + throw "Operation not supported"; } - - std::auto_ptr begin(const std::string&){ + + std::auto_ptr begin(const std::string&){ actual.push_back(BEGIN2PC); std::auto_ptr txn(new TestTransactionContext(this)); return txn; } - std::auto_ptr begin(){ + std::auto_ptr begin(){ actual.push_back(BEGIN); std::auto_ptr txn(new TestTransactionContext(this)); return txn; @@ -183,7 +186,7 @@ public: void abort(TransactionContext& ctxt){ actual.push_back(ABORT); dynamic_cast(ctxt).abort(); - } + } MockTransactionalStore& expectBegin(){ expected.push_back(BEGIN); return *this; @@ -207,23 +210,25 @@ public: void check(){ assertEqualVector(expected, actual); } - + bool isPrepared(){ return state == PREPARED; } - + bool isCommitted(){ return state == COMMITTED; } - + bool isAborted(){ return state == ABORTED; } - + bool isOpen() const{ return state == OPEN; } ~MockTransactionalStore(){} }; +}} // namespace qpid::tests + #endif diff --git a/qpid/cpp/src/tests/TxPublishTest.cpp b/qpid/cpp/src/tests/TxPublishTest.cpp index 63dbf99266..fabb01b864 100644 --- a/qpid/cpp/src/tests/TxPublishTest.cpp +++ b/qpid/cpp/src/tests/TxPublishTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -35,30 +35,33 @@ using boost::intrusive_ptr; using namespace qpid::broker; using namespace qpid::framing; +namespace qpid { +namespace tests { + struct TxPublishTest { - + TestMessageStore store; Queue::shared_ptr queue1; Queue::shared_ptr queue2; intrusive_ptr msg; TxPublish op; - + TxPublishTest() : - queue1(new Queue("queue1", false, &store, 0)), - queue2(new Queue("queue2", false, &store, 0)), + queue1(new Queue("queue1", false, &store, 0)), + queue2(new Queue("queue2", false, &store, 0)), msg(MessageUtils::createMessage("exchange", "routing_key", "id")), op(msg) { msg->getProperties()->setDeliveryMode(PERSISTENT); op.deliverTo(queue1); op.deliverTo(queue2); - } + } }; QPID_AUTO_TEST_SUITE(TxPublishTestSuite) - + QPID_AUTO_TEST_CASE(testPrepare) { TxPublishTest t; @@ -88,7 +91,9 @@ QPID_AUTO_TEST_CASE(testCommit) BOOST_CHECK_EQUAL(t.msg, msg_dequeue); BOOST_CHECK_EQUAL((uint32_t) 1, t.queue2->getMessageCount()); - BOOST_CHECK_EQUAL(t.msg, t.queue2->get().payload); + BOOST_CHECK_EQUAL(t.msg, t.queue2->get().payload); } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/Url.cpp b/qpid/cpp/src/tests/Url.cpp index f3b42a7208..343186eb1f 100644 --- a/qpid/cpp/src/tests/Url.cpp +++ b/qpid/cpp/src/tests/Url.cpp @@ -26,6 +26,9 @@ using namespace std; using namespace qpid; using namespace boost::assign; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(UrlTestSuite) #define URL_CHECK_STR(STR) BOOST_CHECK_EQUAL(Url(STR).str(), STR) @@ -65,3 +68,5 @@ QPID_AUTO_TEST_CASE(TestInvalidAddress) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/Uuid.cpp b/qpid/cpp/src/tests/Uuid.cpp index ea2e80b63b..a6ddb9b5a5 100644 --- a/qpid/cpp/src/tests/Uuid.cpp +++ b/qpid/cpp/src/tests/Uuid.cpp @@ -24,6 +24,9 @@ #include +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(UuidTestSuite) using namespace std; @@ -77,3 +80,5 @@ QPID_AUTO_TEST_CASE(testUuidEncodeDecode) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/Variant.cpp b/qpid/cpp/src/tests/Variant.cpp index b7ce776827..2d68bb842c 100644 --- a/qpid/cpp/src/tests/Variant.cpp +++ b/qpid/cpp/src/tests/Variant.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -25,6 +25,9 @@ using namespace qpid::messaging; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(VariantSuite) QPID_AUTO_TEST_CASE(testConversions) @@ -94,11 +97,11 @@ QPID_AUTO_TEST_CASE(testAssignment) const uint32_t i(1000); value = i; BOOST_CHECK_EQUAL(VAR_UINT32, value.getType()); - BOOST_CHECK_EQUAL(VAR_STRING, other.getType()); + BOOST_CHECK_EQUAL(VAR_STRING, other.getType()); } QPID_AUTO_TEST_CASE(testList) -{ +{ const std::string s("abc"); const float f(9.876f); const int16_t x(1000); @@ -108,7 +111,7 @@ QPID_AUTO_TEST_CASE(testList) value.asList().push_back(Variant(f)); value.asList().push_back(Variant(x)); BOOST_CHECK_EQUAL(3u, value.asList().size()); - Variant::List::const_iterator i = value.asList().begin(); + Variant::List::const_iterator i = value.asList().begin(); BOOST_CHECK(i != value.asList().end()); BOOST_CHECK_EQUAL(VAR_STRING, i->getType()); @@ -129,7 +132,7 @@ QPID_AUTO_TEST_CASE(testList) } QPID_AUTO_TEST_CASE(testMap) -{ +{ const std::string red("red"); const float pi(3.14f); const int16_t x(1000); @@ -145,7 +148,7 @@ QPID_AUTO_TEST_CASE(testMap) BOOST_CHECK_EQUAL(VAR_FLOAT, value.asMap()["pi"].getType()); BOOST_CHECK_EQUAL(pi, value.asMap()["pi"].asFloat()); - + BOOST_CHECK_EQUAL(VAR_INT16, value.asMap()["my-key"].getType()); BOOST_CHECK_EQUAL(x, value.asMap()["my-key"].asInt16()); @@ -153,5 +156,7 @@ QPID_AUTO_TEST_CASE(testMap) BOOST_CHECK_EQUAL(VAR_STRING, value.asMap()["my-key"].getType()); BOOST_CHECK_EQUAL(std::string("now it's a string"), value.asMap()["my-key"].asString()); } - + QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/XmlClientSessionTest.cpp b/qpid/cpp/src/tests/XmlClientSessionTest.cpp index b6b8520bd8..46a4c826a3 100644 --- a/qpid/cpp/src/tests/XmlClientSessionTest.cpp +++ b/qpid/cpp/src/tests/XmlClientSessionTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -39,6 +39,9 @@ #include +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(XmlClientSessionTest) using namespace qpid::client; @@ -118,10 +121,10 @@ QPID_AUTO_TEST_CASE(testXmlBinding) { FieldTable binding; binding.setString("xquery", "declare variable $color external;" "(./message/id mod 2 = 1) and ($color = 'blue')"); - f.session.exchangeBind(qpid::client::arg::exchange="xml", qpid::client::arg::queue="odd_blue", qpid::client::arg::bindingKey="query_name", qpid::client::arg::arguments=binding); + f.session.exchangeBind(qpid::client::arg::exchange="xml", qpid::client::arg::queue="odd_blue", qpid::client::arg::bindingKey="query_name", qpid::client::arg::arguments=binding); Message message; - message.getDeliveryProperties().setRoutingKey("query_name"); + message.getDeliveryProperties().setRoutingKey("query_name"); message.getHeaders().setString("color", "blue"); string m = "1"; @@ -130,7 +133,7 @@ QPID_AUTO_TEST_CASE(testXmlBinding) { f.session.messageTransfer(qpid::client::arg::content=message, qpid::client::arg::destination="xml"); Message m2 = localQueue.get(); - BOOST_CHECK_EQUAL(m, m2.getData()); + BOOST_CHECK_EQUAL(m, m2.getData()); } /** @@ -146,10 +149,10 @@ QPID_AUTO_TEST_CASE(testXMLBindMultipleQueues) { FieldTable blue; blue.setString("xquery", "./colour = 'blue'"); - f.session.exchangeBind(arg::exchange="xml", arg::queue="blue", arg::bindingKey="by-colour", arg::arguments=blue); + f.session.exchangeBind(arg::exchange="xml", arg::queue="blue", arg::bindingKey="by-colour", arg::arguments=blue); FieldTable red; red.setString("xquery", "./colour = 'red'"); - f.session.exchangeBind(arg::exchange="xml", arg::queue="red", arg::bindingKey="by-colour", arg::arguments=red); + f.session.exchangeBind(arg::exchange="xml", arg::queue="red", arg::bindingKey="by-colour", arg::arguments=red); Message sent1("blue", "by-colour"); f.session.messageTransfer(arg::content=sent1, arg::destination="xml"); @@ -223,3 +226,4 @@ olour", arg::arguments=blue); QPID_AUTO_TEST_SUITE_END() +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/acl.py b/qpid/cpp/src/tests/acl.py index b62288a769..fc53d2ce8b 100755 --- a/qpid/cpp/src/tests/acl.py +++ b/qpid/cpp/src/tests/acl.py @@ -61,7 +61,7 @@ class ACLTests(TestBase010): # ACL general tests #===================================== - def test_deny_all(self): + def test_deny_mode(self): """ Test the deny all mode """ @@ -71,7 +71,9 @@ class ACLTests(TestBase010): aclf.write('acl deny all all') aclf.close() - self.reload_acl() + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) session = self.get_session('bob','bob') try: @@ -87,7 +89,7 @@ class ACLTests(TestBase010): except qpid.session.SessionException, e: self.assertEqual(530,e.args[0].error_code) - def test_allow_all(self): + def test_allow_mode(self): """ Test the allow all mode """ @@ -96,7 +98,9 @@ class ACLTests(TestBase010): aclf.write('acl allow all all') aclf.close() - self.reload_acl() + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) session = self.get_session('bob','bob') try: @@ -124,7 +128,9 @@ class ACLTests(TestBase010): aclf.write('acl allow all all') aclf.close() - self.reload_acl() + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) session = self.get_session('bob','bob') try: @@ -208,9 +214,9 @@ class ACLTests(TestBase010): # ACL queue tests #===================================== - def test_queue_acl(self): + def test_queue_allow_mode(self): """ - Test various modes for queue acl + Test cases for queue acl in allow mode """ aclf = ACLFile() aclf.write('acl deny bob@QPID create queue name=q1 durable=true passive=true\n') @@ -221,27 +227,35 @@ class ACLTests(TestBase010): aclf.write('acl allow all all') aclf.close() - self.reload_acl() + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) session = self.get_session('bob','bob') try: - session.queue_declare(queue="q1", durable='true', passive='true') + session.queue_declare(queue="q1", durable=True, passive=True) self.fail("ACL should deny queue create request with name=q1 durable=true passive=true"); except qpid.session.SessionException, e: self.assertEqual(530,e.args[0].error_code) session = self.get_session('bob','bob') try: - session.queue_declare(queue="q2", exclusive='true') + session.queue_declare(queue="q2", exclusive=True) self.fail("ACL should deny queue create request with name=q2 exclusive=true"); except qpid.session.SessionException, e: self.assertEqual(530,e.args[0].error_code) session = self.get_session('bob','bob') try: - session.queue_declare(queue="q3", exclusive='true') - session.queue_declare(queue="q4", durable='true') + session.queue_declare(queue="q2", durable=True) + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue create request for q2 with any parameter other than exclusive=true"); + + try: + session.queue_declare(queue="q3", exclusive=True) + session.queue_declare(queue="q4", durable=True) except qpid.session.SessionException, e: if (530 == e.args[0].error_code): self.fail("ACL should allow queue create request for q3 and q4 with any parameter"); @@ -279,56 +293,184 @@ class ACLTests(TestBase010): if (530 == e.args[0].error_code): self.fail("ACL should allow queue delete request for q3"); + + def test_queue_deny_mode(self): + """ + Test cases for queue acl in deny mode + """ + aclf = ACLFile() + aclf.write('acl allow bob@QPID create queue name=q1 durable=true passive=true\n') + aclf.write('acl allow bob@QPID create queue name=q2 exclusive=true\n') + aclf.write('acl allow bob@QPID access queue name=q3\n') + aclf.write('acl allow bob@QPID purge queue name=q3\n') + aclf.write('acl allow bob@QPID create queue name=q3\n') + aclf.write('acl allow bob@QPID create queue name=q4\n') + aclf.write('acl allow bob@QPID delete queue name=q4\n') + aclf.write('acl allow guest@QPID all all\n') + aclf.write('acl deny all all') + aclf.close() + + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) + + session = self.get_session('bob','bob') + + try: + session.queue_declare(queue="q1", durable=True, passive=True) + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue create request with name=q1 durable=true passive=true"); + + try: + session.queue_declare(queue="q1", durable=False, passive=False) + self.fail("ACL should deny queue create request with name=q1 durable=true passive=false"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.queue_declare(queue="q2", exclusive=False) + self.fail("ACL should deny queue create request with name=q2 exclusive=false"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.queue_declare(queue="q2", exclusive=True) + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue create request for q2 with exclusive=true"); + + try: + session.queue_declare(queue="q3") + session.queue_declare(queue="q4") + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue create request for q3 and q4"); + + try: + session.queue_query(queue="q4") + self.fail("ACL should deny queue query request for q4"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.queue_purge(queue="q4") + self.fail("ACL should deny queue purge request for q4"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.queue_purge(queue="q3") + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue purge request for q3"); + + try: + session.queue_query(queue="q3") + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue query request for q3"); + + try: + session.queue_delete(queue="q3") + self.fail("ACL should deny queue delete request for q3"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.queue_delete(queue="q4") + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue delete request for q4"); + #===================================== # ACL exchange tests #===================================== - def test_exchange_acl(self): + def test_exchange_acl_allow_mode(self): + session = self.get_session('bob','bob') + session.queue_declare(queue="baz") + """ - Test various modes for exchange acl + Test cases for exchange acl in allow mode """ aclf = ACLFile() aclf.write('acl deny bob@QPID create exchange name=testEx durable=true passive=true\n') aclf.write('acl deny bob@QPID create exchange name=ex1 type=direct\n') - aclf.write('acl deny bob@QPID access exchange name=myEx\n') + aclf.write('acl deny bob@QPID access exchange name=myEx queuename=q1 routingkey=rk1.*\n') aclf.write('acl deny bob@QPID bind exchange name=myEx queuename=q1 routingkey=rk1\n') aclf.write('acl deny bob@QPID unbind exchange name=myEx queuename=q1 routingkey=rk1\n') - aclf.write('acl deny bob@QPID delete exchange name=myEx\n') + aclf.write('acl deny bob@QPID delete exchange name=myEx\n') aclf.write('acl allow all all') aclf.close() - self.reload_acl() + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) session = self.get_session('bob','bob') - + session.queue_declare(queue='q1') + session.queue_declare(queue='q2') + session.exchange_declare(exchange='myEx', type='direct') + try: - session.exchange_declare(exchange='testEx', durable='true', passive='true') + session.exchange_declare(exchange='testEx', durable=True, passive=True) self.fail("ACL should deny exchange create request with name=testEx durable=true passive=true"); except qpid.session.SessionException, e: self.assertEqual(530,e.args[0].error_code) session = self.get_session('bob','bob') + try: + session.exchange_declare(exchange='testEx', type='direct', durable=True, passive=False) + except qpid.session.SessionException, e: + print e + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange create request for testEx with any parameter other than durable=true and passive=true"); + try: session.exchange_declare(exchange='ex1', type='direct') self.fail("ACL should deny exchange create request with name=ex1 type=direct"); - except qpid.session.SessionException, e: + except qpid.session.SessionException, e: self.assertEqual(530,e.args[0].error_code) session = self.get_session('bob','bob') try: session.exchange_declare(exchange='myXml', type='direct') - session.queue_declare(queue='q1') except qpid.session.SessionException, e: if (530 == e.args[0].error_code): self.fail("ACL should allow exchange create request for myXml with any parameter"); try: session.exchange_query(name='myEx') - self.fail("ACL should deny queue query request for q3"); + self.fail("ACL should deny exchange query request for myEx"); except qpid.session.SessionException, e: self.assertEqual(530,e.args[0].error_code) session = self.get_session('bob','bob') + + try: + session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk1.*') + self.fail("ACL should deny exchange bound request for myEx with queuename=q1 and routing_key='rk1.*' "); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.exchange_query(name='amq.topic') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange query request for exchange='amq.topic'"); + try: + session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk2.*') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange bound request for myEx with queuename=q1 and binding_key='rk2.*'"); + try: session.exchange_bind(exchange='myEx', queue='q1', binding_key='rk1') self.fail("ACL should deny exchange bind request with exchange='myEx' queuename='q1' bindingkey='rk1'"); @@ -337,10 +479,17 @@ class ACLTests(TestBase010): session = self.get_session('bob','bob') try: - session.exchange_bind(exchange='myXml', queue='q1', binding_key='x') + session.exchange_bind(exchange='myEx', queue='q1', binding_key='x') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange bind request for exchange='myEx', queue='q1', binding_key='x'"); + + try: + session.exchange_bind(exchange='myEx', queue='q2', binding_key='rk1') except qpid.session.SessionException, e: if (530 == e.args[0].error_code): - self.fail("ACL should allow exchange bind request for exchange='myXml', queue='q1', binding_key='x'"); + self.fail("ACL should allow exchange bind request for exchange='myEx', queue='q2', binding_key='rk1'"); + try: session.exchange_unbind(exchange='myEx', queue='q1', binding_key='rk1') self.fail("ACL should deny exchange unbind request with exchange='myEx' queuename='q1' bindingkey='rk1'"); @@ -349,10 +498,16 @@ class ACLTests(TestBase010): session = self.get_session('bob','bob') try: - session.exchange_unbind(exchange='myXml', queue='q1', binding_key='x') + session.exchange_unbind(exchange='myEx', queue='q1', binding_key='x') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange unbind request for exchange='myEx', queue='q1', binding_key='x'"); + + try: + session.exchange_unbind(exchange='myEx', queue='q2', binding_key='rk1') except qpid.session.SessionException, e: if (530 == e.args[0].error_code): - self.fail("ACL should allow exchange unbind request for exchange='myXml', queue='q1', binding_key='x'"); + self.fail("ACL should allow exchange unbind request for exchange='myEx', queue='q2', binding_key='rk1'"); try: session.exchange_delete(exchange='myEx') @@ -366,45 +521,161 @@ class ACLTests(TestBase010): except qpid.session.SessionException, e: if (530 == e.args[0].error_code): self.fail("ACL should allow exchange delete request for myXml"); - + + + def test_exchange_acl_deny_mode(self): + session = self.get_session('bob','bob') + session.queue_declare(queue='bar') + + """ + Test cases for exchange acl in deny mode + """ + aclf = ACLFile() + aclf.write('acl allow bob@QPID create exchange name=myEx durable=true passive=false\n') + aclf.write('acl allow bob@QPID bind exchange name=amq.topic queuename=bar routingkey=foo.*\n') + aclf.write('acl allow bob@QPID unbind exchange name=amq.topic queuename=bar routingkey=foo.*\n') + aclf.write('acl allow bob@QPID access exchange name=myEx queuename=q1 routingkey=rk1.*\n') + aclf.write('acl allow bob@QPID delete exchange name=myEx\n') + aclf.write('acl allow guest@QPID all all\n') + aclf.write('acl deny all all') + aclf.close() + + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) + + session = self.get_session('bob','bob') + + try: + session.exchange_declare(exchange='myEx', type='direct', durable=True, passive=False) + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange create request for myEx with durable=true and passive=false"); + try: + session.exchange_declare(exchange='myEx', type='direct', durable=False) + self.fail("ACL should deny exchange create request with name=myEx durable=false"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.exchange_bind(exchange='amq.topic', queue='bar', binding_key='foo.bar') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange bind request for exchange='amq.topic', queue='bar', binding_key='foor.bar'"); + + try: + session.exchange_bind(exchange='amq.topic', queue='baz', binding_key='foo.bar') + self.fail("ACL should deny exchange bind request for exchange='amq.topic', queue='baz', binding_key='foo.bar'"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.exchange_bind(exchange='amq.topic', queue='bar', binding_key='fooz.bar') + self.fail("ACL should deny exchange bind request for exchange='amq.topic', queue='bar', binding_key='fooz.bar'"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.exchange_unbind(exchange='amq.topic', queue='bar', binding_key='foo.bar') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange unbind request for exchange='amq.topic', queue='bar', binding_key='foor.bar'"); + try: + session.exchange_unbind(exchange='amq.topic', queue='baz', binding_key='foo.bar') + self.fail("ACL should deny exchange unbind request for exchange='amq.topic', queue='baz', binding_key='foo.bar'"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.exchange_unbind(exchange='amq.topic', queue='bar', binding_key='fooz.bar') + self.fail("ACL should deny exchange unbind request for exchange='amq.topic', queue='bar', binding_key='fooz.bar'"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.exchange_query(name='amq.topic') + self.fail("ACL should deny exchange query request for amq.topic"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk2.*') + self.fail("ACL should deny exchange bound request for amq.topic with queuename=q1 and routing_key='rk2.*' "); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.exchange_query(name='myEx') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange query request for exchange='myEx'"); + + try: + session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk1.*') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange bound request for myEx with queuename=q1 and binding_key='rk1.*'"); + + try: + session.exchange_delete(exchange='myXml') + self.fail("ACL should deny exchange delete request for myXml"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.exchange_delete(exchange='myEx') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange delete request for myEx"); + #===================================== # ACL consume tests #===================================== - def test_consume_acl(self): + def test_consume_allow_mode(self): """ - Test various consume acl + Test cases for consume in allow mode """ aclf = ACLFile() - aclf.write('acl deny bob@QPID consume queue name=q1 durable=true\n') - aclf.write('acl deny bob@QPID consume queue name=q2 exclusive=true\n') + aclf.write('acl deny bob@QPID consume queue name=q1\n') + aclf.write('acl deny bob@QPID consume queue name=q2\n') aclf.write('acl allow all all') aclf.close() - self.reload_acl() + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) session = self.get_session('bob','bob') try: - session.queue_declare(queue='q1', durable='true') - session.queue_declare(queue='q2', exclusive='true') - session.queue_declare(queue='q3', durable='true') + session.queue_declare(queue='q1') + session.queue_declare(queue='q2') + session.queue_declare(queue='q3') except qpid.session.SessionException, e: if (530 == e.args[0].error_code): self.fail("ACL should allow create queue request"); try: session.message_subscribe(queue='q1', destination='myq1') - self.fail("ACL should deny message subscriber request for queue='q1'"); + self.fail("ACL should deny subscription for queue='q1'"); except qpid.session.SessionException, e: self.assertEqual(530,e.args[0].error_code) session = self.get_session('bob','bob') try: session.message_subscribe(queue='q2', destination='myq1') - self.fail("ACL should deny message subscriber request for queue='q2'"); + self.fail("ACL should deny subscription for queue='q2'"); except qpid.session.SessionException, e: self.assertEqual(530,e.args[0].error_code) session = self.get_session('bob','bob') @@ -413,9 +684,51 @@ class ACLTests(TestBase010): session.message_subscribe(queue='q3', destination='myq1') except qpid.session.SessionException, e: if (530 == e.args[0].error_code): - self.fail("ACL should allow create message subscribe"); + self.fail("ACL should allow subscription for q3"); + def test_consume_deny_mode(self): + """ + Test cases for consume in allow mode + """ + aclf = ACLFile() + aclf.write('acl allow bob@QPID consume queue name=q1\n') + aclf.write('acl allow bob@QPID consume queue name=q2\n') + aclf.write('acl allow bob@QPID create queue\n') + aclf.write('acl allow guest@QPID all\n') + aclf.write('acl deny all all') + aclf.close() + + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) + + session = self.get_session('bob','bob') + + + try: + session.queue_declare(queue='q1') + session.queue_declare(queue='q2') + session.queue_declare(queue='q3') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow create queue request"); + + try: + session.message_subscribe(queue='q1', destination='myq1') + session.message_subscribe(queue='q2', destination='myq2') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow subscription for q1 and q2"); + + try: + session.message_subscribe(queue='q3', destination='myq3') + self.fail("ACL should deny subscription for queue='q3'"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + #===================================== # ACL publish tests #===================================== @@ -431,15 +744,11 @@ class ACLTests(TestBase010): aclf.write('acl allow all all') aclf.close() - self.reload_acl() + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) session = self.get_session('bob','bob') - - try: - session.exchange_declare(exchange='myEx', type='topic') - except qpid.session.SessionException, e: - if (530 == e.args[0].error_code): - self.fail("ACL should allow exchange create request for myEx with any parameter"); props = session.delivery_properties(routing_key="rk1") @@ -458,6 +767,7 @@ class ACLTests(TestBase010): session = self.get_session('bob','bob') try: + session.exchange_declare(exchange='myEx', type='direct', durable=False) session.message_transfer(destination="myEx", message=Message(props,"Test")) except qpid.session.SessionException, e: if (530 == e.args[0].error_code): diff --git a/qpid/cpp/src/tests/client_test.cpp b/qpid/cpp/src/tests/client_test.cpp index 05b42f620c..2f5e8e5afe 100644 --- a/qpid/cpp/src/tests/client_test.cpp +++ b/qpid/cpp/src/tests/client_test.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -40,13 +40,16 @@ using namespace qpid::client; using namespace qpid::framing; using std::string; +namespace qpid { +namespace tests { + struct Args : public TestOptions { uint msgSize; bool verbose; Args() : TestOptions("Simple test of Qpid c++ client; sends and receives a single message."), msgSize(26) { - addOptions() + addOptions() ("size", optValue(msgSize, "N"), "message size") ("verbose", optValue(verbose), "print out some status messages"); } @@ -58,7 +61,7 @@ std::string generateData(uint size) { if (size < chars.length()) { return chars.substr(0, size); - } + } std::string data; for (uint i = 0; i < (size / chars.length()); i++) { data += chars; @@ -78,6 +81,10 @@ void print(const std::string& text, const Message& msg) std::cout << std::endl; } +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char** argv) { try { @@ -92,7 +99,7 @@ int main(int argc, char** argv) //Create and open a session on the connection through which //most functionality is exposed: Session session = connection.newSession(); - if (opts.verbose) std::cout << "Opened session." << std::endl; + if (opts.verbose) std::cout << "Opened session." << std::endl; //'declare' the exchange and the queue, which will create them @@ -116,13 +123,13 @@ int main(int argc, char** argv) // Using the SubscriptionManager, get the message from the queue. SubscriptionManager subs(session); Message msgIn = subs.get("MyQueue"); - if (msgIn.getData() == msgOut.getData()) + if (msgIn.getData() == msgOut.getData()) if (opts.verbose) std::cout << "Received the exepected message." << std::endl; //close the session & connection session.close(); if (opts.verbose) std::cout << "Closed session." << std::endl; - connection.close(); + connection.close(); if (opts.verbose) std::cout << "Closed connection." << std::endl; return 0; } catch(const std::exception& e) { diff --git a/qpid/cpp/src/tests/cluster_test.cpp b/qpid/cpp/src/tests/cluster_test.cpp index 50ca241b5d..28fcdd13ad 100644 --- a/qpid/cpp/src/tests/cluster_test.cpp +++ b/qpid/cpp/src/tests/cluster_test.cpp @@ -59,8 +59,6 @@ template ostream& operator<<(ostream& o, const std::set& s) { return seqPrint(o, s); } } -QPID_AUTO_TEST_SUITE(cluster_test) - using namespace std; using namespace qpid; using namespace qpid::cluster; @@ -70,6 +68,11 @@ using namespace boost::assign; using broker::Broker; using boost::shared_ptr; +namespace qpid { +namespace tests { + +QPID_AUTO_TEST_SUITE(cluster_test) + bool durableFlag = std::getenv("STORE_LIB") != 0; void prepareArgs(ClusterFixture::Args& args, const bool durableFlag = false) { @@ -1098,3 +1101,5 @@ QPID_AUTO_TEST_CASE(testRelease) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/consume.cpp b/qpid/cpp/src/tests/consume.cpp index 3aacf8b3da..69110d151f 100644 --- a/qpid/cpp/src/tests/consume.cpp +++ b/qpid/cpp/src/tests/consume.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -36,6 +36,9 @@ using namespace qpid::client; using namespace qpid::sys; using namespace std; +namespace qpid { +namespace tests { + typedef vector StringSet; struct Args : public qpid::TestOptions { @@ -46,7 +49,7 @@ struct Args : public qpid::TestOptions { bool summary; bool print; bool durable; - + Args() : count(1000), ack(0), queue("publish-consume"), declare(false), summary(false), print(false) { @@ -63,12 +66,12 @@ struct Args : public qpid::TestOptions { Args opts; -struct Client +struct Client { Connection connection; Session session; - Client() + Client() { opts.open(connection); session = connection.newSession(); @@ -85,7 +88,7 @@ struct Client settings.flowControl = FlowControl(opts.count, SubscriptionManager::UNLIMITED,false); Subscription sub = subs.subscribe(lq, opts.queue, settings); Message msg; - AbsTime begin=now(); + AbsTime begin=now(); for (size_t i = 0; i < opts.count; ++i) { msg=lq.pop(); QPID_LOG(info, "Received: " << msg.getMessageProperties().getCorrelationId()); @@ -99,7 +102,7 @@ struct Client else cout << "Time: " << secs << "s Rate: " << opts.count/secs << endl; } - ~Client() + ~Client() { try{ session.close(); @@ -110,6 +113,10 @@ struct Client } }; +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char** argv) { try { diff --git a/qpid/cpp/src/tests/datagen.cpp b/qpid/cpp/src/tests/datagen.cpp index 175f14cc57..acbc07d63c 100644 --- a/qpid/cpp/src/tests/datagen.cpp +++ b/qpid/cpp/src/tests/datagen.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -25,7 +25,10 @@ #include #include "qpid/Options.h" -struct Args : public qpid::Options +namespace qpid { +namespace tests { + +struct Args : public qpid::Options { uint count; uint minSize; @@ -34,12 +37,12 @@ struct Args : public qpid::Options uint maxChar; bool help; - Args() : qpid::Options("Random data generator"), - count(1), minSize(8), maxSize(4096), + Args() : qpid::Options("Random data generator"), + count(1), minSize(8), maxSize(4096), minChar(32), maxChar(126),//safely printable ascii chars help(false) { - addOptions() + addOptions() ("count", qpid::optValue(count, "N"), "number of data strings to generate") ("min-size", qpid::optValue(minSize, "N"), "minimum size of data string") ("max-size", qpid::optValue(maxSize, "N"), "maximum size of data string") @@ -81,6 +84,10 @@ std::string generateData(uint size, uint min, uint max) return data; } +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char** argv) { Args opts; diff --git a/qpid/cpp/src/tests/echotest.cpp b/qpid/cpp/src/tests/echotest.cpp index 98590e35ff..ab26dcf3fd 100644 --- a/qpid/cpp/src/tests/echotest.cpp +++ b/qpid/cpp/src/tests/echotest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -33,6 +33,9 @@ using namespace qpid::framing; using namespace qpid::sys; using namespace std; +namespace qpid { +namespace tests { + struct Args : public qpid::Options, public qpid::client::ConnectionSettings { @@ -48,7 +51,7 @@ struct Args : public qpid::Options, ("help", optValue(help), "Print this usage statement") ("count", optValue(count, "N"), "Number of messages to send") ("size", optValue(count, "N"), "Size of messages") - ("broker,b", optValue(host, "HOST"), "Broker host to connect to") + ("broker,b", optValue(host, "HOST"), "Broker host to connect to") ("port,p", optValue(port, "PORT"), "Broker port to connect to") ("username", optValue(username, "USER"), "user name for broker log in.") ("password", optValue(password, "PASSWORD"), "password for broker log in.") @@ -75,7 +78,7 @@ class Listener : public MessageListener Message request; double total, min, max; bool summary; - + public: Listener(Session& session, uint limit, bool summary); void start(uint size); @@ -92,7 +95,7 @@ void Listener::start(uint size) { session.queueDeclare(arg::queue=queue, arg::exclusive=true, arg::autoDelete=true); request.getDeliveryProperties().setRoutingKey(queue); - subscriptions.subscribe(*this, queue, SubscriptionSettings(FlowControl::unlimited(), ACCEPT_MODE_NONE)); + subscriptions.subscribe(*this, queue, SubscriptionSettings(FlowControl::unlimited(), ACCEPT_MODE_NONE)); request.getDeliveryProperties().setTimestamp(current_time()); if (size) request.setData(std::string(size, 'X')); @@ -100,7 +103,7 @@ void Listener::start(uint size) subscriptions.run(); } -void Listener::received(Message& response) +void Listener::received(Message& response) { //extract timestamp and compute latency: uint64_t sentAt = response.getDeliveryProperties().getTimestamp(); @@ -122,7 +125,11 @@ void Listener::received(Message& response) } } -int main(int argc, char** argv) +}} // namespace qpid::tests + +using namespace qpid::tests; + +int main(int argc, char** argv) { Args opts; opts.parse(argc, argv); diff --git a/qpid/cpp/src/tests/exception_test.cpp b/qpid/cpp/src/tests/exception_test.cpp index 379e957ef1..0e9a948f00 100644 --- a/qpid/cpp/src/tests/exception_test.cpp +++ b/qpid/cpp/src/tests/exception_test.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -28,6 +28,9 @@ #include "qpid/sys/Thread.h" #include "qpid/framing/reply_exceptions.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(exception_test) // FIXME aconway 2008-06-12: need to update our exception handling to @@ -49,12 +52,12 @@ struct Catcher : public Runnable { function f; bool caught; Thread thread; - + Catcher(function f_) : f(f_), caught(false), thread(this) {} ~Catcher() { join(); } - + void run() { - try { + try { ScopedSuppressLogging sl; // Suppress messages for expected errors. f(); } @@ -110,7 +113,7 @@ QPID_AUTO_TEST_CASE(DisconnectedListen) { Catcher runner(bind(&SubscriptionManager::run, boost::ref(fix.subs))); fix.connection.proxy.close(); runner.join(); - BOOST_CHECK_THROW(fix.session.close(), TransportFailure); + BOOST_CHECK_THROW(fix.session.close(), TransportFailure); } QPID_AUTO_TEST_CASE(NoSuchQueueTest) { @@ -120,3 +123,5 @@ QPID_AUTO_TEST_CASE(NoSuchQueueTest) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/failover_soak.cpp b/qpid/cpp/src/tests/failover_soak.cpp index 08691912aa..39d72e258a 100644 --- a/qpid/cpp/src/tests/failover_soak.cpp +++ b/qpid/cpp/src/tests/failover_soak.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -51,7 +51,8 @@ using namespace qpid::framing; using namespace qpid::client; - +namespace qpid { +namespace tests { typedef vector brokerVector; @@ -90,9 +91,9 @@ ostream& operator<< ( ostream& os, const childType& ct ) { struct child { - child ( string & name, pid_t pid, childType type ) + child ( string & name, pid_t pid, childType type ) : name(name), pid(pid), retval(-999), status(RUNNING), type(type) - { + { gettimeofday ( & startTime, 0 ); } @@ -107,7 +108,7 @@ struct child void - setType ( childType t ) + setType ( childType t ) { type = t; } @@ -126,7 +127,7 @@ struct child struct children : public vector -{ +{ void add ( string & name, pid_t pid, childType type ) @@ -135,7 +136,7 @@ struct children : public vector } - child * + child * get ( pid_t pid ) { vector::iterator i; @@ -155,7 +156,7 @@ struct children : public vector { if ( verbosity > 1 ) { - cerr << "children::exited warning: Can't find child with pid " + cerr << "children::exited warning: Can't find child with pid " << pid << endl; } @@ -192,7 +193,7 @@ struct children : public vector << endl; return (*i)->retval; } - + return 0; } @@ -226,11 +227,11 @@ struct children : public vector children allMyChildren; -void -childExit ( int ) +void +childExit ( int ) { - int childReturnCode; - pid_t pid = waitpid ( 0, & childReturnCode, WNOHANG); + int childReturnCode; + pid_t pid = waitpid ( 0, & childReturnCode, WNOHANG); if ( pid > 0 ) allMyChildren.exited ( pid, childReturnCode ); @@ -270,10 +271,10 @@ printBrokers ( brokerVector & brokers ) { cout << "Broker List ------------ size: " << brokers.size() << "\n"; for ( brokerVector::iterator i = brokers.begin(); i != brokers.end(); ++ i) { - cout << "pid: " - << (*i)->getPID() - << " port: " - << (*i)->getPort() + cout << "pid: " + << (*i)->getPID() + << " port: " + << (*i)->getPort() << endl; } cout << "end Broker List ------------\n"; @@ -293,7 +294,7 @@ wait_for_newbie ( ) if ( ! newbie ) return true; - try + try { Connection connection; connection.open ( "127.0.0.1", newbie_port ); @@ -303,8 +304,8 @@ wait_for_newbie ( ) } catch ( const std::exception& error ) { - std::cerr << "wait_for_newbie error: " - << error.what() + std::cerr << "wait_for_newbie error: " + << error.what() << endl; return false; } @@ -320,7 +321,7 @@ startNewBroker ( brokerVector & brokers, char const * moduleOrDir, string const clusterName, int verbosity, - int durable ) + int durable ) { static int brokerId = 0; stringstream path, prefix; @@ -353,8 +354,8 @@ startNewBroker ( brokerVector & brokers, ForkedBroker * broker = newbie; if ( verbosity > 0 ) - std::cerr << "new broker created: pid == " - << broker->getPID() + std::cerr << "new broker created: pid == " + << broker->getPID() << " log-prefix == " << "soak-" << brokerId << endl; @@ -381,8 +382,8 @@ killFrontBroker ( brokerVector & brokers, int verbosity ) catch ( const exception& error ) { if ( verbosity > 0 ) { - cout << "error killing broker: " - << error.what() + cout << "error killing broker: " + << error.what() << endl; } @@ -398,14 +399,14 @@ killFrontBroker ( brokerVector & brokers, int verbosity ) /* - * The optional delay is to avoid killing newbie brokers that have just + * The optional delay is to avoid killing newbie brokers that have just * been added and are still in the process of updating. This causes * spurious, test-generated errors that scare everybody. */ void killAllBrokers ( brokerVector & brokers, int delay ) { - if ( delay > 0 ) + if ( delay > 0 ) { std::cerr << "Killing all brokers after delay of " << delay << endl; sleep ( delay ); @@ -413,8 +414,8 @@ killAllBrokers ( brokerVector & brokers, int delay ) for ( uint i = 0; i < brokers.size(); ++ i ) try { brokers[i]->kill(9); } - catch ( const exception& error ) - { + catch ( const exception& error ) + { std::cerr << "killAllBrokers Warning: exception during kill on broker " << i << " " @@ -428,21 +429,21 @@ killAllBrokers ( brokerVector & brokers, int delay ) pid_t -runDeclareQueuesClient ( brokerVector brokers, +runDeclareQueuesClient ( brokerVector brokers, char const * host, char const * path, int verbosity, int durable - ) + ) { string name("declareQueues"); int port = brokers[0]->getPort ( ); if ( verbosity > 1 ) - cout << "startDeclareQueuesClient: host: " - << host - << " port: " - << port + cout << "startDeclareQueuesClient: host: " + << host + << " port: " + << port << endl; stringstream portSs; portSs << port; @@ -473,12 +474,12 @@ runDeclareQueuesClient ( brokerVector brokers, pid_t -startReceivingClient ( brokerVector brokers, +startReceivingClient ( brokerVector brokers, char const * host, char const * receiverPath, char const * reportFrequency, int verbosity - ) + ) { string name("receiver"); int port = brokers[0]->getPort ( ); @@ -520,14 +521,14 @@ startReceivingClient ( brokerVector brokers, pid_t -startSendingClient ( brokerVector brokers, +startSendingClient ( brokerVector brokers, char const * host, char const * senderPath, char const * nMessages, char const * reportFrequency, int verbosity, int durability - ) + ) { string name("sender"); int port = brokers[0]->getPort ( ); @@ -580,13 +581,14 @@ startSendingClient ( brokerVector brokers, #define HANGING 7 #define ERROR_KILLING_BROKER 8 +}} // namespace qpid::tests -// If you want durability, use the "dir" option of "moduleOrDir" . - +using namespace qpid::tests; +// If you want durability, use the "dir" option of "moduleOrDir" . int -main ( int argc, char const ** argv ) -{ +main ( int argc, char const ** argv ) +{ if ( argc != 9 ) { cerr << "Usage: " << argv[0] @@ -626,10 +628,10 @@ main ( int argc, char const ** argv ) int nBrokers = 3; for ( int i = 0; i < nBrokers; ++ i ) { startNewBroker ( brokers, - moduleOrDir, + moduleOrDir, clusterName, verbosity, - durable ); + durable ); } @@ -638,7 +640,7 @@ main ( int argc, char const ** argv ) // Run the declareQueues child. int childStatus; - pid_t dqClientPid = + pid_t dqClientPid = runDeclareQueuesClient ( brokers, host, declareQueuesPath, verbosity, durable ); if ( -1 == dqClientPid ) { cerr << "END_OF_TEST ERROR_START_DECLARE_1\n"; @@ -657,8 +659,8 @@ main ( int argc, char const ** argv ) // Start the receiving client. pid_t receivingClientPid = - startReceivingClient ( brokers, - host, + startReceivingClient ( brokers, + host, receiverPath, reportFrequency, verbosity ); @@ -669,10 +671,10 @@ main ( int argc, char const ** argv ) // Start the sending client. - pid_t sendingClientPid = - startSendingClient ( brokers, - host, - senderPath, + pid_t sendingClientPid = + startSendingClient ( brokers, + host, + senderPath, nMessages, reportFrequency, verbosity, @@ -687,10 +689,10 @@ main ( int argc, char const ** argv ) maxSleep = 4; - for ( int totalBrokers = 3; - totalBrokers < maxBrokers; - ++ totalBrokers - ) + for ( int totalBrokers = 3; + totalBrokers < maxBrokers; + ++ totalBrokers + ) { if ( verbosity > 0 ) cout << totalBrokers << " brokers have been added to the cluster.\n\n\n"; @@ -721,14 +723,14 @@ main ( int argc, char const ** argv ) cout << "Starting new broker.\n\n"; startNewBroker ( brokers, - moduleOrDir, + moduleOrDir, clusterName, verbosity, - durable ); - + durable ); + if ( verbosity > 1 ) printBrokers ( brokers ); - + // If all children have exited, quit. int unfinished = allMyChildren.unfinished(); if ( ! unfinished ) { diff --git a/qpid/cpp/src/tests/latencytest.cpp b/qpid/cpp/src/tests/latencytest.cpp index e1c47eab05..a205ef6c7c 100644 --- a/qpid/cpp/src/tests/latencytest.cpp +++ b/qpid/cpp/src/tests/latencytest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -40,6 +40,9 @@ using namespace qpid::client; using namespace qpid::sys; using std::string; +namespace qpid { +namespace tests { + typedef std::vector StringSet; struct Args : public qpid::TestOptions { @@ -64,7 +67,7 @@ struct Args : public qpid::TestOptions { durable(false), base("latency-test"), singleConnect(false) { - addOptions() + addOptions() ("size", optValue(size, "N"), "message size") ("concurrentTests", optValue(concurrentConnections, "N"), "number of concurrent test setups, will create another publisher,\ @@ -73,9 +76,9 @@ struct Args : public qpid::TestOptions { ("count", optValue(count, "N"), "number of messages to send") ("rate", optValue(rate, "N"), "target message rate (causes count to be ignored)") ("sync", optValue(sync), "send messages synchronously") - ("report-frequency", optValue(reportFrequency, "N"), + ("report-frequency", optValue(reportFrequency, "N"), "number of milliseconds to wait between reports (ignored unless rate specified)") - ("time-limit", optValue(timeLimit, "N"), + ("time-limit", optValue(timeLimit, "N"), "test duration, in seconds") ("prefetch", optValue(prefetch, "N"), "prefetch count (0 implies no flow control, and no acking)") ("ack", optValue(ack, "N"), "Ack frequency in messages (defaults to half the prefetch value)") @@ -98,7 +101,7 @@ uint64_t current_time() return t; } -struct Stats +struct Stats { Mutex lock; uint count; @@ -132,7 +135,7 @@ public: }; class Receiver : public Client, public MessageListener -{ +{ SubscriptionManager mgr; uint count; Stats& stats; @@ -168,7 +171,7 @@ class Test Receiver receiver; Sender sender; AbsTime begin; - + public: Test(const string& q) : queue(q), receiver(queue, stats), sender(queue, receiver), begin(now()) {} void start(); @@ -186,7 +189,7 @@ Client::Client(const string& q) : queue(q) connection = &localConnection; opts.open(localConnection); } - session = connection->newSession(); + session = connection->newSession(); } void Client::start() @@ -235,7 +238,7 @@ Receiver::Receiver(const string& q, Stats& s) : Client(q), mgr(session), count(0 settings.acceptMode = ACCEPT_MODE_NONE; settings.flowControl = FlowControl::unlimited(); } - mgr.subscribe(*this, queue, settings); + mgr.subscribe(*this, queue, settings); } void Receiver::test() @@ -283,7 +286,7 @@ void Stats::print() if (!opts.csv) { if (count) { std::cout << "Latency(ms): min=" << minLatency << ", max=" << - maxLatency << ", avg=" << aux_avg; + maxLatency << ", avg=" << aux_avg; } else { std::cout << "Stalled: no samples for interval"; } @@ -368,7 +371,7 @@ void Sender::sendByRate() Duration delay(sentAt, waitTill); if (delay < 0) ++missedRate; - else + else sys::usleep(delay / TIME_USEC); if (timeLimit != 0 && Duration(start, now()) > timeLimit) { session.sync(); @@ -382,7 +385,7 @@ string Sender::generateData(uint size) { if (size < chars.length()) { return chars.substr(0, size); - } + } std::string data; for (uint i = 0; i < (size / chars.length()); i++) { data += chars; @@ -392,35 +395,39 @@ string Sender::generateData(uint size) } -void Test::start() -{ - receiver.start(); +void Test::start() +{ + receiver.start(); begin = AbsTime(now()); - sender.start(); + sender.start(); } -void Test::join() -{ - sender.join(); - receiver.join(); +void Test::join() +{ + sender.join(); + receiver.join(); AbsTime end = now(); Duration time(begin, end); double msecs(time / TIME_MSEC); if (!opts.csv) { - std::cout << "Sent " << receiver.getCount() << " msgs through " << queue + std::cout << "Sent " << receiver.getCount() << " msgs through " << queue << " in " << msecs << "ms (" << (receiver.getCount() * 1000 / msecs) << " msgs/s) "; } stats.print(); std::cout << std::endl; } -void Test::report() -{ +void Test::report() +{ stats.print(); std::cout << std::endl; stats.reset(); } +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char** argv) { try { diff --git a/qpid/cpp/src/tests/logging.cpp b/qpid/cpp/src/tests/logging.cpp index 00e1d7de85..5cb563c7d3 100644 --- a/qpid/cpp/src/tests/logging.cpp +++ b/qpid/cpp/src/tests/logging.cpp @@ -37,6 +37,9 @@ #include +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(loggingTestSuite) using namespace std; @@ -106,7 +109,7 @@ struct TestOutput : public Logger::Output { TestOutput(Logger& l) { l.output(std::auto_ptr(this)); } - + void log(const Statement& s, const string& m) { msg.push_back(m); stmt.push_back(s); @@ -117,7 +120,7 @@ struct TestOutput : public Logger::Output { using boost::assign::list_of; QPID_AUTO_TEST_CASE(testLoggerOutput) { - Logger l; + Logger l; l.clear(); l.select(Selector(debug)); Statement s=QPID_LOG_STATEMENT_INIT(debug); @@ -174,7 +177,7 @@ QPID_AUTO_TEST_CASE(testLoggerFormat) { l.format(Logger::FUNCTION); QPID_LOG(critical, "foo"); BOOST_CHECK_EQUAL(string(BOOST_CURRENT_FUNCTION) + ": foo\n", out->last()); - + l.format(Logger::LEVEL); QPID_LOG(critical, "foo"); BOOST_CHECK_EQUAL("critical foo\n", out->last()); @@ -228,12 +231,12 @@ clock_t timeLoop(int times, int (*fp)()) { // Overhead test disabled because it consumes a ton of CPU and takes // forever under valgrind. Not friendly for regular test runs. -// +// #if 0 QPID_AUTO_TEST_CASE(testOverhead) { // Ensure that the ratio of CPU time for an incrementing loop // with and without disabled log statements is in acceptable limits. - // + // int times=100000000; clock_t noLog=timeLoop(times, count); clock_t withLog=timeLoop(times, loggedCount); @@ -242,9 +245,9 @@ QPID_AUTO_TEST_CASE(testOverhead) { // NB: in initial tests the ratio was consistently below 1.5, // 2.5 is reasonable and should avoid spurios failures // due to machine load. - // - BOOST_CHECK_SMALL(ratio, 2.5); -} + // + BOOST_CHECK_SMALL(ratio, 2.5); +} #endif // 0 Statement statement( @@ -290,7 +293,7 @@ QPID_AUTO_TEST_CASE(testOptionsParse) { } QPID_AUTO_TEST_CASE(testOptionsDefault) { - Options opts(""); + qpid::log::Options opts(""); #ifdef _WIN32 qpid::log::windows::SinkOptions sinks("test"); #else @@ -328,10 +331,10 @@ QPID_AUTO_TEST_CASE(testSelectorFromOptions) { QPID_AUTO_TEST_CASE(testLoggerStateure) { Logger& l=Logger::instance(); ScopedSuppressLogging ls(l); - Options opts("test"); + qpid::log::Options opts("test"); const char* argv[]={ 0, - "--log-time", "no", + "--log-time", "no", "--log-source", "yes", "--log-to-stderr", "no", "--log-to-file", "logging.tmp", @@ -352,7 +355,7 @@ QPID_AUTO_TEST_CASE(testLoggerStateure) { QPID_AUTO_TEST_CASE(testQuoteNonPrintable) { Logger& l=Logger::instance(); ScopedSuppressLogging ls(l); - Options opts("test"); + qpid::log::Options opts("test"); opts.time=false; #ifdef _WIN32 qpid::log::windows::SinkOptions *sinks = @@ -367,7 +370,7 @@ QPID_AUTO_TEST_CASE(testQuoteNonPrintable) { char s[] = "null\0tab\tspace newline\nret\r\x80\x99\xff"; string str(s, sizeof(s)); - QPID_LOG(critical, str); + QPID_LOG(critical, str); ifstream log("logging.tmp"); string line; getline(log, line, '\0'); @@ -378,3 +381,5 @@ QPID_AUTO_TEST_CASE(testQuoteNonPrintable) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/perftest.cpp b/qpid/cpp/src/tests/perftest.cpp index d383e0eb80..88d9fd15cb 100644 --- a/qpid/cpp/src/tests/perftest.cpp +++ b/qpid/cpp/src/tests/perftest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -49,6 +49,9 @@ using namespace sys; using boost::lexical_cast; using boost::bind; +namespace qpid { +namespace tests { + enum Mode { SHARED, FANOUT, TOPIC }; const char* modeNames[] = { "shared", "fanout", "topic" }; @@ -105,9 +108,9 @@ struct Opts : public TestOptions { bool commitAsync; static const std::string helpText; - + Opts() : - TestOptions(helpText), + TestOptions(helpText), setup(false), control(false), publish(false), subscribe(false), baseName("perftest"), pubs(1), count(500000), size(1024), confirm(true), durable(false), uniqueData(false), syncPub(false), subs(1), ack(0), @@ -136,16 +139,16 @@ struct Opts : public TestOptions { ("nsubs", optValue(subs, "N"), "Create N subscribers.") ("sub-ack", optValue(ack, "N"), "N>0: Subscriber acks batches of N.\n" "N==0: Subscriber uses unconfirmed mode") - + ("qt", optValue(qt, "N"), "Create N queues or topics.") ("single-connection", optValue(singleConnect, "yes|no"), "Use one connection for multiple sessions.") - + ("iterations", optValue(iterations, "N"), "Desired number of iterations of the test.") ("summary,s", optValue(summary), "Summary output: pubs/sec subs/sec transfers/sec Mbytes/sec") ("queue-max-count", optValue(queueMaxCount, "N"), "queue policy: count to trigger 'flow to disk'") ("queue-max-size", optValue(queueMaxSize, "N"), "queue policy: accumulated size to trigger 'flow to disk'") - ("base-name", optValue(baseName, "NAME"), "base name used for queues or topics") + ("base-name", optValue(baseName, "NAME"), "base name used for queues or topics") ("queue-durable", optValue(queueDurable, "N"), "Make queue durable (implied if durable set)") ("interval_sub", optValue(intervalSub, "ms"), ">=0 delay between msg consume") @@ -171,7 +174,7 @@ struct Opts : public TestOptions { count += subs - (count % subs); cout << "WARNING: Adjusted --count to " << count << " the nearest multiple of --nsubs" << endl; - } + } totalPubs = pubs*qt; totalSubs = subs*qt; subQuota = (pubs*count)/subs; @@ -258,7 +261,7 @@ struct Client : public Runnable { }; struct Setup : public Client { - + void queueInit(string name, bool durable=false, const framing::FieldTable& settings=framing::FieldTable()) { session.queueDeclare(arg::queue=name, arg::durable=durable, arg::arguments=settings); session.queuePurge(arg::queue=name); @@ -278,7 +281,7 @@ struct Setup : public Client { for (size_t i = 0; i < opts.qt; ++i) { ostringstream qname; qname << opts.baseName << i; - queueInit(qname.str(), opts.durable || opts.queueDurable, settings); + queueInit(qname.str(), opts.durable || opts.queueDurable, settings); } } } @@ -303,7 +306,7 @@ class Stats { public: Stats() : sum(0) {} - + // Functor to collect rates. void operator()(const string& data) { try { @@ -314,7 +317,7 @@ class Stats { throw Exception("Bad report: "+data); } } - + double mean() const { return sum/values.size(); } @@ -331,7 +334,7 @@ class Stats { } return sqrt(ssq/(values.size()-1)); } - + ostream& print(ostream& out) { ostream_iterator o(out, "\n"); copy(values.begin(), values.end(), o); @@ -341,11 +344,11 @@ class Stats { return out << endl; } }; - + // Manage control queues, collect and print reports. struct Controller : public Client { - + SubscriptionManager subs; Controller() : subs(session) {} @@ -354,7 +357,7 @@ struct Controller : public Client { void process(size_t n, string queue, boost::function msgFn) { - if (!opts.summary) + if (!opts.summary) cout << "Processing " << n << " messages from " << queue << " " << flush; LocalQueue lq; @@ -370,8 +373,8 @@ struct Controller : public Client { void process(size_t n, LocalQueue lq, string queue, boost::function msgFn) { - session.messageFlow(queue, 0, n); - if (!opts.summary) + session.messageFlow(queue, 0, n); + if (!opts.summary) cout << "Processing " << n << " messages from " << queue << " " << flush; for (size_t i = 0; i < n; ++i) { @@ -386,7 +389,7 @@ struct Controller : public Client { cout << "Sending " << data << " " << n << " times to " << queue << endl; Message msg(data, queue); - for (size_t i = 0; i < n; ++i) + for (size_t i = 0; i < n; ++i) session.messageTransfer(arg::content=msg, arg::acceptMode=1); } @@ -419,7 +422,7 @@ struct Controller : public Client { process(opts.totalPubs, pubDone, fqn("pub_done"), boost::ref(pubRates)); process(opts.totalSubs, subDone, fqn("sub_done"), boost::ref(subRates)); - AbsTime end=now(); + AbsTime end=now(); double time=secs(start, end); double txrate=opts.transfers/time; @@ -469,12 +472,12 @@ struct PublishThread : public Client { string routingKey; PublishThread() {}; - + PublishThread(string key, string dest=string()) { destination=dest; routingKey=key; } - + void run() { // Publisher try { string data; @@ -492,7 +495,7 @@ struct PublishThread : public Client { } } else { size_t msgSize=max(opts.size, sizeof(size_t)); - data = string(msgSize, 'X'); + data = string(msgSize, 'X'); } Message msg(data, routingKey); @@ -500,21 +503,21 @@ struct PublishThread : public Client { msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); - if (opts.txPub){ + if (opts.txPub){ session.txSelect(); } SubscriptionManager subs(session); LocalQueue lq; - subs.setFlowControl(1, SubscriptionManager::UNLIMITED, true); - subs.subscribe(lq, fqn("pub_start")); - + subs.setFlowControl(1, SubscriptionManager::UNLIMITED, true); + subs.subscribe(lq, fqn("pub_start")); + for (size_t j = 0; j < opts.iterations; ++j) { expect(lq.pop().getData(), "start"); AbsTime start=now(); for (size_t i=0; i(msg.getData()).replace(offset, sizeof(size_t), + const_cast(msg.getData()).replace(offset, sizeof(size_t), reinterpret_cast(&i), sizeof(size_t)); if (opts.syncPub) { sync(session).messageTransfer( @@ -540,7 +543,7 @@ struct PublishThread : public Client { if (opts.confirm) session.sync(); AbsTime end=now(); double time=secs(start,end); - + // Send result to controller. Message report(lexical_cast(opts.count/time), fqn("pub_done")); session.messageTransfer(arg::content=report, arg::acceptMode=1); @@ -561,7 +564,7 @@ struct SubscribeThread : public Client { string queue; SubscribeThread() {} - + SubscribeThread(string q) { queue = q; } SubscribeThread(string key, string ex) { @@ -586,7 +589,7 @@ struct SubscribeThread : public Client { } void run() { // Subscribe - try { + try { if (opts.txSub) sync(session).txSelect(); SubscriptionManager subs(session); SubscriptionSettings settings; @@ -606,15 +609,15 @@ struct SubscribeThread : public Client { if (opts.iterations > 1) { subs.subscribe(iterationControl, fqn("sub_iteration"), SubscriptionSettings(FlowControl::messageCredit(0))); } - + for (size_t j = 0; j < opts.iterations; ++j) { if (j > 0) { //need to wait here until all subs are done - session.messageFlow(fqn("sub_iteration"), 0, 1); + session.messageFlow(fqn("sub_iteration"), 0, 1); iterationControl.pop(); //need to allocate some more credit for subscription - session.messageFlow(queue, 0, opts.subQuota); + session.messageFlow(queue, 0, opts.subQuota); } Message msg; AbsTime start=now(); @@ -627,7 +630,7 @@ struct SubscribeThread : public Client { } if (opts.intervalSub) qpid::sys::usleep(opts.intervalSub*1000); - // TODO aconway 2007-11-23: check message order for. + // TODO aconway 2007-11-23: check message order for. // multiple publishers. Need an array of counters, // one per publisher and a publisher ID in the // message. Careful not to introduce a lot of overhead @@ -664,6 +667,10 @@ struct SubscribeThread : public Client { } }; +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char** argv) { int exitCode = 0; boost::ptr_vector subs(opts.subs); diff --git a/qpid/cpp/src/tests/publish.cpp b/qpid/cpp/src/tests/publish.cpp index 34c2b8fefc..3f456e7588 100644 --- a/qpid/cpp/src/tests/publish.cpp +++ b/qpid/cpp/src/tests/publish.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -36,6 +36,9 @@ using namespace qpid::client; using namespace qpid::sys; using namespace std; +namespace qpid { +namespace tests { + typedef vector StringSet; struct Args : public qpid::TestOptions { @@ -61,12 +64,12 @@ struct Args : public qpid::TestOptions { Args opts; -struct Client +struct Client { Connection connection; AsyncSession session; - Client() + Client() { opts.open(connection); session = connection.newSession(); @@ -75,7 +78,7 @@ struct Client // Cheap hex calculation, avoid expensive ostrstream and string // creation to generate correlation ids in message loop. char hex(char i) { return i<10 ? '0'+i : 'A'+i-10; } - void hex(char i, string& s) { + void hex(char i, string& s) { s[0]=hex(i>>24); s[1]=hex(i>>16); s[2]=hex(i>>8); s[3]=i; } @@ -86,7 +89,7 @@ struct Client string correlationId = "0000"; if (opts.durable) msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); - + for (uint i = 0; i < opts.count; i++) { if (opts.id) { hex(i+1, correlationId); @@ -103,7 +106,7 @@ struct Client else cout << "Time: " << secs << "s Rate: " << opts.count/secs << endl; } - ~Client() + ~Client() { try{ session.close(); @@ -114,6 +117,10 @@ struct Client } }; +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char** argv) { try { diff --git a/qpid/cpp/src/tests/qpid_ping.cpp b/qpid/cpp/src/tests/qpid_ping.cpp index cc07ade7bb..b046fdf54b 100644 --- a/qpid/cpp/src/tests/qpid_ping.cpp +++ b/qpid/cpp/src/tests/qpid_ping.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -38,6 +38,9 @@ using namespace qpid::framing; using namespace qpid::client; using namespace qpid; +namespace qpid { +namespace tests { + struct PingOptions : public qpid::TestOptions { int timeout; // Timeout in seconds. bool quiet; // No output @@ -58,7 +61,7 @@ class Ping : public Runnable { public: Ping() : status(WAITING) {} - + void run() { try { opts.open(connection); @@ -100,6 +103,9 @@ class Ping : public Runnable { } }; +}} // namespace qpid::tests + +using namespace qpid::tests; int main(int argc, char** argv) { try { diff --git a/qpid/cpp/src/tests/qrsh.cpp b/qpid/cpp/src/tests/qrsh.cpp index 2d71b600d5..0cb52b6b05 100644 --- a/qpid/cpp/src/tests/qrsh.cpp +++ b/qpid/cpp/src/tests/qrsh.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -37,11 +37,13 @@ using namespace qpid::framing; using namespace std; +namespace qpid { +namespace tests { class ResponseListener : public MessageListener { public : - + int exitCode; ResponseListener ( SubscriptionManager & subscriptions ) @@ -50,7 +52,7 @@ class ResponseListener : public MessageListener { } - virtual void + virtual void received ( Message & message ) { char first_word[1000]; @@ -66,9 +68,9 @@ class ResponseListener : public MessageListener if ( ! strcmp ( first_word, "get_response" ) ) { // The remainder of the message is the file we requested. - fprintf ( stdout, - "%s", - message.getData().c_str() + strlen("get_response" ) + fprintf ( stdout, + "%s", + message.getData().c_str() + strlen("get_response" ) ); subscriptions.cancel(message.getDestination()); } @@ -76,12 +78,13 @@ class ResponseListener : public MessageListener private : - + SubscriptionManager & subscriptions; }; +}} // namespace qpid::tests - +using namespace qpid::tests; /* * argv[1] host @@ -90,8 +93,8 @@ class ResponseListener : public MessageListener * argv[4] command name * argv[5..N] args to the command */ -int -main ( int argc, char ** argv ) +int +main ( int argc, char ** argv ) { const char* host = argv[1]; int port = atoi(argv[2]); @@ -99,14 +102,14 @@ main ( int argc, char ** argv ) Connection connection; - try + try { connection.open ( host, port ); Session session = connection.newSession ( ); // Make a queue and bind it to fanout. string myQueue = session.getId().getName(); - + session.queueDeclare ( arg::queue=myQueue, arg::exclusive=true, arg::autoDelete=true @@ -136,7 +139,7 @@ main ( int argc, char ** argv ) response_command = true; // Send the payload message. - // Skip "qrsh host_name port" + // Skip "qrsh host_name port" Message message; stringstream ss; for ( int i = 3; i < argc; ++ i ) @@ -144,7 +147,7 @@ main ( int argc, char ** argv ) message.setData ( ss.str() ); - session.messageTransfer(arg::content=message, + session.messageTransfer(arg::content=message, arg::destination="amq.fanout"); if ( response_command ) @@ -153,8 +156,8 @@ main ( int argc, char ** argv ) session.close(); connection.close(); return responseListener.exitCode; - } - catch ( exception const & e) + } + catch ( exception const & e) { cerr << e.what() << endl; } diff --git a/qpid/cpp/src/tests/qrsh_server.cpp b/qpid/cpp/src/tests/qrsh_server.cpp index 4b80212eae..f1163ba479 100644 --- a/qpid/cpp/src/tests/qrsh_server.cpp +++ b/qpid/cpp/src/tests/qrsh_server.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -43,7 +43,8 @@ using namespace qpid::framing; using namespace std; - +namespace qpid { +namespace tests { int mrand ( int max_desired_val ) @@ -54,7 +55,7 @@ mrand ( int max_desired_val ) -char * +char * file2str ( char const * file_name ) { FILE * fp = fopen ( file_name, "r" ); @@ -71,9 +72,9 @@ file2str ( char const * file_name ) if ( ! content ) { - fprintf ( stderr, - "file2str error: can't malloc %d bytes.\n", - (int)file_len + fprintf ( stderr, + "file2str error: can't malloc %d bytes.\n", + (int)file_len ); return 0; } @@ -123,9 +124,9 @@ class QrshServer : public MessageListener bool myMessage ( Message const & message ); /* ---------------------------------------------- - * Special Commands + * Special Commands * These are commands that the qrsh_server executes - * directly, rather than through a child process + * directly, rather than through a child process * instance of qrsh_run. */ void runCommand ( Message const & message ); @@ -157,9 +158,9 @@ class QrshServer : public MessageListener char const * skipWord ( char const * s ); - void string_replaceAll ( string & str, - string & target, - string & replacement + void string_replaceAll ( string & str, + string & target, + string & replacement ); @@ -186,12 +187,12 @@ class QrshServer : public MessageListener -QrshServer::QrshServer ( SubscriptionManager & subs, +QrshServer::QrshServer ( SubscriptionManager & subs, char const * name, char const * qrsh_run_path, char const * host, int port - ) + ) : subscriptions ( subs ), name ( name ), qrsh_run_path ( qrsh_run_path ), @@ -202,11 +203,11 @@ QrshServer::QrshServer ( SubscriptionManager & subs, { data_dir << "/tmp/qrsh_" << getpid(); - + if(mkdir ( data_dir.str().c_str(), 0777 ) ) { - fprintf ( stderr, - "QrshServer::QrshServer error: can't mkdir |%s|\n", + fprintf ( stderr, + "QrshServer::QrshServer error: can't mkdir |%s|\n", data_dir.str().c_str() ); exit ( 1 ); @@ -239,21 +240,21 @@ QrshServer::start ( ) << name; send ( announcement_data.str() ); - + saidHello = true; } -void +void QrshServer::send ( string const & content ) { try { Message message; message.setData ( content ); - + Connection connection; connection.open ( host, port ); Session session = connection.newSession ( ); @@ -289,7 +290,7 @@ QrshServer::sayHello ( ) -void +void QrshServer::sayName ( ) { fprintf ( stderr, "My name is: |%s|\n", name.c_str() ); @@ -343,7 +344,7 @@ QrshServer::getStraw ( Message const & message ) break; } } - + if ( i_win && (ties <= 0) ) { myStraw = 0; @@ -364,10 +365,10 @@ QrshServer::getStraw ( Message const & message ) /* * "APB" command (all-points-bullitens (commands that are not addressed * specifically to any server)) are handled directly, here. - * Because if I return simply "true", the normal command processing code + * Because if I return simply "true", the normal command processing code * will misinterpret the command. */ -bool +bool QrshServer::myMessage ( Message const & message ) { int const maxlen = 100; @@ -414,7 +415,7 @@ QrshServer::myMessage ( Message const & message ) { return true; } - else + else if ( ! strcmp ( first_word, "any" ) ) { straws.clear(); @@ -443,7 +444,7 @@ QrshServer::rememberIntroduction ( Message const & message ) -void +void QrshServer::addAlias ( Message const & message ) { char alias[1000]; @@ -463,8 +464,8 @@ QrshServer::getNames ( ) if ( ! dir ) { - fprintf ( stderr, - "QrshServer::getNames error: could not open dir |%s|.\n", + fprintf ( stderr, + "QrshServer::getNames error: could not open dir |%s|.\n", data_dir.str().c_str() ); return; @@ -491,8 +492,8 @@ QrshServer::getNames ( ) } else { - /* - * Fail silently. The non-existence of this file + /* + * Fail silently. The non-existence of this file * is not necessarily an error. */ } @@ -504,9 +505,9 @@ QrshServer::getNames ( ) void -QrshServer::string_replaceAll ( string & str, - string & target, - string & replacement +QrshServer::string_replaceAll ( string & str, + string & target, + string & replacement ) { int target_size = target.size(); @@ -519,7 +520,7 @@ QrshServer::string_replaceAll ( string & str, -bool +bool QrshServer::isProcessName ( char const * str ) { getNames(); @@ -537,12 +538,12 @@ QrshServer::isProcessName ( char const * str ) -int +int QrshServer::string_countWords ( char const * s1 ) { int count = 0; char const * s2 = s1 + 1; - + if ( ! isspace(* s1) ) { ++ count; @@ -603,7 +604,7 @@ QrshServer::get ( Message const & request_message ) */ char file_or_process_name[1000]; sscanf ( request_message.getData().c_str(), "%*s%*s%s", file_or_process_name ); - + if ( isProcessName ( file_or_process_name ) ) { stringstream desired_file_name; @@ -612,13 +613,13 @@ QrshServer::get ( Message const & request_message ) << file_or_process_name << '/'; char requested_output_stream[1000]; - if(1 != sscanf ( request_message.getData().c_str(), - "%*s%*s%*s%s", - requested_output_stream + if(1 != sscanf ( request_message.getData().c_str(), + "%*s%*s%*s%s", + requested_output_stream ) ) { - fprintf ( stderr, + fprintf ( stderr, "QrshServer::get error: Can't read requested data file name from this message: |%s|\n", request_message.getData().c_str() ); @@ -674,7 +675,7 @@ QrshServer::exited ( Message const & message ) if ( truncated_command ) { stringstream ss; - ss << qrsh_run_path + ss << qrsh_run_path << ' ' << data_dir.str() << ' ' @@ -706,9 +707,9 @@ QrshServer::exited ( Message const & message ) fprintf ( stderr, "qrsh_server error awaiting child!\n" ); exit ( 1 ); } - + exit_code >>= 8; - + stringstream data; data << "wait_response " << exit_code; @@ -731,7 +732,7 @@ QrshServer::wait ( Message const & message ) // The second word is "exec_wait". // The third word is the symbolic name of the command to wait for. // The fact that there are exactly three words means that this - // must be a command that has already been named and started -- + // must be a command that has already been named and started -- // we just need to find its pid and wait on it. pre_existing = true; } @@ -762,7 +763,7 @@ QrshServer::wait ( Message const & message ) if ( truncated_command ) { stringstream ss; - ss << qrsh_run_path + ss << qrsh_run_path << ' ' << data_dir.str() << ' ' @@ -795,7 +796,7 @@ QrshServer::wait ( Message const & message ) exit ( 1 ); } } - + exit_code >>= 8; stringstream data; @@ -810,7 +811,7 @@ QrshServer::wait ( Message const & message ) -char const * +char const * QrshServer::skipWord ( char const * s ) { if(! (s && *s) ) @@ -884,7 +885,7 @@ QrshServer::getArgs ( char const * str ) arg_len = 0; } - done: + done: if ( arg_len > 0 ) lengths.push_back ( arg_len ); @@ -896,8 +897,8 @@ QrshServer::getArgs ( char const * str ) for ( int i = 0; i < n_args; ++ i ) { argv[i] = ( char *) malloc ( lengths[i] + 1 ); - strncpy ( argv[i], - str + start_positions[i], + strncpy ( argv[i], + str + start_positions[i], lengths[i] ); argv[i][lengths[i]] = 0; @@ -971,12 +972,12 @@ QrshServer::runCommand ( Message const & message ) * qrsh_run, which will save all its data in the qrsh dir. */ stringstream ss; - ss << qrsh_run_path + ss << qrsh_run_path << ' ' << data_dir.str() << ' ' << s; - + if ( ! fork() ) { char ** argv = getArgs ( ss.str().c_str() ); @@ -988,8 +989,8 @@ QrshServer::runCommand ( Message const & message ) -void -QrshServer::received ( Message & message ) +void +QrshServer::received ( Message & message ) { if ( myMessage ( message ) ) runCommand ( message ); @@ -997,7 +998,9 @@ QrshServer::received ( Message & message ) +}} // namespace qpid::tests +using namespace qpid::tests; /* * fixme mick Mon Aug 3 10:29:26 EDT 2009 @@ -1024,23 +1027,23 @@ main ( int /*argc*/, char** argv ) // Declare queues. string myQueue = session.getId().getName(); - session.queueDeclare ( arg::queue=myQueue, + session.queueDeclare ( arg::queue=myQueue, arg::exclusive=true, arg::autoDelete=true); - session.exchangeBind ( arg::exchange="amq.fanout", - arg::queue=myQueue, + session.exchangeBind ( arg::exchange="amq.fanout", + arg::queue=myQueue, arg::bindingKey="my-key"); - + // Create a server and subscribe it to my queue. SubscriptionManager subscriptions ( session ); - QrshServer server ( subscriptions, + QrshServer server ( subscriptions, argv[1], // server name argv[2], // qrsh exe path host, port ); - subscriptions.subscribe ( server, myQueue ); + subscriptions.subscribe ( server, myQueue ); // Receive messages until the subscription is cancelled // by QrshServer::received() @@ -1048,7 +1051,7 @@ main ( int /*argc*/, char** argv ) connection.close(); } - catch(const exception& error) + catch(const exception& error) { cout << error.what() << endl; return 1; diff --git a/qpid/cpp/src/tests/receiver.cpp b/qpid/cpp/src/tests/receiver.cpp index 49f7ff0338..e01954e31a 100644 --- a/qpid/cpp/src/tests/receiver.cpp +++ b/qpid/cpp/src/tests/receiver.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -36,7 +36,10 @@ using namespace qpid::framing; using namespace std; -struct Args : public qpid::TestOptions +namespace qpid { +namespace tests { + +struct Args : public qpid::TestOptions { string queue; uint messages; @@ -47,7 +50,7 @@ struct Args : public qpid::TestOptions Args() : queue("test-queue"), messages(0), ignoreDuplicates(false), creditWindow(0), ackFrequency(1), browse(false) { - addOptions() + addOptions() ("queue", qpid::optValue(queue, "QUEUE NAME"), "Queue from which to request messages") ("messages", qpid::optValue(messages, "N"), "Number of messages to receive; 0 means receive indefinitely") ("ignore-duplicates", qpid::optValue(ignoreDuplicates), "Detect and ignore duplicates (by checking 'sn' header)") @@ -77,15 +80,15 @@ class Receiver : public MessageListener, public FailoverManager::Command bool isDuplicate(Message& message); }; -Receiver::Receiver(const string& q, uint messages, bool ignoreDuplicates, uint creditWindow, uint ackFrequency, bool browse) : - queue(q), count(messages), skipDups(ignoreDuplicates), processed(0), lastSn(0) +Receiver::Receiver(const string& q, uint messages, bool ignoreDuplicates, uint creditWindow, uint ackFrequency, bool browse) : + queue(q), count(messages), skipDups(ignoreDuplicates), processed(0), lastSn(0) { if (browse) settings.acquireMode = ACQUIRE_MODE_NOT_ACQUIRED; if (creditWindow) settings.flowControl = FlowControl::messageWindow(creditWindow); settings.autoAck = ackFrequency; } -void Receiver::received(Message& message) +void Receiver::received(Message& message) { if (!(skipDups && isDuplicate(message))) { bool eos = message.getData() == EOS; @@ -94,7 +97,7 @@ void Receiver::received(Message& message) } } -bool Receiver::isDuplicate(Message& message) +bool Receiver::isDuplicate(Message& message) { uint sn = message.getHeaders().getAsInt("sn"); if (lastSn < sn) { @@ -115,6 +118,10 @@ void Receiver::execute(AsyncSession& session, bool /*isRetry*/) } } +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char ** argv) { Args opts; @@ -130,6 +137,3 @@ int main(int argc, char ** argv) } return 1; } - - - diff --git a/qpid/cpp/src/tests/replaying_sender.cpp b/qpid/cpp/src/tests/replaying_sender.cpp index 3ee69eec14..bfb4b042b6 100644 --- a/qpid/cpp/src/tests/replaying_sender.cpp +++ b/qpid/cpp/src/tests/replaying_sender.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -35,12 +35,15 @@ using namespace qpid::framing; using namespace std; +namespace qpid { +namespace tests { + class Sender : public FailoverManager::Command { public: Sender(const std::string& queue, uint count, uint reportFreq); void execute(AsyncSession& session, bool isRetry); - uint getSent(); + uint getSent(); void setVerbosity ( int v ) { verbosity = v; } void setPersistence ( int p ) { persistence = p; } @@ -51,7 +54,7 @@ class Sender : public FailoverManager::Command uint sent; const uint reportFrequency; Message message; - + int verbosity; int persistence; }; @@ -93,7 +96,11 @@ uint Sender::getSent() return sent; } -int main(int argc, char ** argv) +}} // namespace qpid::tests + +using namespace qpid::tests; + +int main(int argc, char ** argv) { ConnectionSettings settings; @@ -118,23 +125,23 @@ int main(int argc, char ** argv) connection.execute ( sender ); if ( verbosity > 0 ) { - std::cout << "Sender finished. Sent " - << sender.getSent() - << " messages." + std::cout << "Sender finished. Sent " + << sender.getSent() + << " messages." << endl; } connection.close(); - return 0; - } - catch(const std::exception& error) + return 0; + } + catch(const std::exception& error) { - cerr << "Sender (host: " - << settings.host - << " port: " + cerr << "Sender (host: " + << settings.host + << " port: " << settings.port << " ) " - << " Failed: " - << error.what() + << " Failed: " + << error.what() << std::endl; } return 1; diff --git a/qpid/cpp/src/tests/resuming_receiver.cpp b/qpid/cpp/src/tests/resuming_receiver.cpp index ef559a009d..807bd83bee 100644 --- a/qpid/cpp/src/tests/resuming_receiver.cpp +++ b/qpid/cpp/src/tests/resuming_receiver.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -35,8 +35,11 @@ using namespace qpid::framing; using namespace std; -class Listener : public MessageListener, - public FailoverManager::Command, +namespace qpid { +namespace tests { + +class Listener : public MessageListener, + public FailoverManager::Command, public FailoverManager::ReconnectionStrategy { public: @@ -57,32 +60,32 @@ class Listener : public MessageListener, }; -Listener::Listener(int freq, int verbosity) - : count(0), - received_twice(0), - lastSn(0), - gaps(false), +Listener::Listener(int freq, int verbosity) + : count(0), + received_twice(0), + lastSn(0), + gaps(false), reportFrequency(freq), verbosity(verbosity), done(false) {} -void Listener::received(Message & message) +void Listener::received(Message & message) { - if (message.getData() == "That's all, folks!") + if (message.getData() == "That's all, folks!") { done = true; if(verbosity > 0 ) { - std::cout << "Shutting down listener for " + std::cout << "Shutting down listener for " << message.getDestination() << std::endl; - std::cout << "Listener received " - << count - << " messages (" - << received_twice - << " received_twice)" + std::cout << "Listener received " + << count + << " messages (" + << received_twice + << " received_twice)" << endl; } subscription.cancel(); @@ -99,8 +102,8 @@ void Listener::received(Message & message) ++count; if ( ! ( count % reportFrequency ) ) { if ( verbosity > 0 ) - std::cout << "Listener has received " - << count + std::cout << "Listener has received " + << count << " messages.\n"; } } else { @@ -133,6 +136,10 @@ void Listener::editUrlList(std::vector& urls) if (urls.size() > 1) std::rotate(urls.begin(), urls.begin() + 1, urls.end()); } +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char ** argv) { ConnectionSettings settings; diff --git a/qpid/cpp/src/tests/sender.cpp b/qpid/cpp/src/tests/sender.cpp index 311de2e5f8..4e845c42b4 100644 --- a/qpid/cpp/src/tests/sender.cpp +++ b/qpid/cpp/src/tests/sender.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -37,22 +37,27 @@ using namespace qpid::framing; using namespace std; -struct Args : public qpid::TestOptions +namespace qpid { +namespace tests { + +struct Args : public qpid::TestOptions { string destination; string key; uint sendEos; bool durable; + uint ttl; string lvqMatchValue; string lvqMatchFile; - Args() : key("test-queue"), sendEos(0), durable(false) + Args() : key("test-queue"), sendEos(0), durable(false), ttl(0) { addOptions() ("exchange", qpid::optValue(destination, "EXCHANGE"), "Exchange to send messages to") ("routing-key", qpid::optValue(key, "KEY"), "Routing key to add to messages") ("send-eos", qpid::optValue(sendEos, "N"), "Send N EOS messages to mark end of input") ("durable", qpid::optValue(durable, "true|false"), "Mark messages as durable.") + ("ttl", qpid::optValue(ttl, "msecs"), "Time-to-live for messages, in milliseconds") ("lvq-match-value", qpid::optValue(lvqMatchValue, "KEY"), "The value to set for the LVQ match key property") ("lvq-match-file", qpid::optValue(lvqMatchFile, "FILE"), "A file containing values to set for the LVQ match key property"); } @@ -63,26 +68,29 @@ const string EOS("eos"); class Sender : public FailoverManager::Command { public: - Sender(const std::string& destination, const std::string& key, uint sendEos, bool durable, + Sender(const std::string& destination, const std::string& key, uint sendEos, bool durable, uint ttl, const std::string& lvqMatchValue, const std::string& lvqMatchFile); void execute(AsyncSession& session, bool isRetry); private: const std::string destination; MessageReplayTracker sender; - Message message; + Message message; const uint sendEos; uint sent; std::ifstream lvqMatchValues; }; -Sender::Sender(const std::string& dest, const std::string& key, uint eos, bool durable, - const std::string& lvqMatchValue, const std::string& lvqMatchFile) : +Sender::Sender(const std::string& dest, const std::string& key, uint eos, bool durable, uint ttl, const std::string& lvqMatchValue, const std::string& lvqMatchFile) : destination(dest), sender(10), message("", key), sendEos(eos), sent(0) , lvqMatchValues(lvqMatchFile.c_str()) { if (durable){ message.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); } + if (ttl) { + message.getDeliveryProperties().setTtl(ttl); + } + if (!lvqMatchValue.empty()) { message.getHeaders().setString(QueueOptions::strLVQMatchProperty, lvqMatchValue); } @@ -108,16 +116,20 @@ void Sender::execute(AsyncSession& session, bool isRetry) } } -int main(int argc, char ** argv) +}} // namespace qpid::tests + +using namespace qpid::tests; + +int main(int argc, char ** argv) { Args opts; try { opts.parse(argc, argv); FailoverManager connection(opts.con); - Sender sender(opts.destination, opts.key, opts.sendEos, opts.durable, opts.lvqMatchValue, opts.lvqMatchFile); + Sender sender(opts.destination, opts.key, opts.sendEos, opts.durable, opts.ttl, opts.lvqMatchValue, opts.lvqMatchFile); connection.execute(sender); connection.close(); - return 0; + return 0; } catch(const std::exception& error) { std::cout << "Failed: " << error.what() << std::endl; } diff --git a/qpid/cpp/src/tests/shlibtest.cpp b/qpid/cpp/src/tests/shlibtest.cpp index e485cc9397..5655eb7e64 100644 --- a/qpid/cpp/src/tests/shlibtest.cpp +++ b/qpid/cpp/src/tests/shlibtest.cpp @@ -18,6 +18,9 @@ * */ +namespace qpid { +namespace tests { + int* loaderData = 0; extern "C" #ifdef WIN32 @@ -28,5 +31,4 @@ void callMe(int *i) { loaderData=i; } struct OnUnload { ~OnUnload() { *loaderData=42; } }; OnUnload unloader; // For destructor. - - +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/test_store.cpp b/qpid/cpp/src/tests/test_store.cpp index 90503818ed..64a96bf71a 100644 --- a/qpid/cpp/src/tests/test_store.cpp +++ b/qpid/cpp/src/tests/test_store.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -22,7 +22,7 @@ /**@file * Plug-in message store for tests. - * + * * Add functionality as required, build up a comprehensive set of * features to support persistent behavior tests. * @@ -46,6 +46,9 @@ using namespace std; using namespace boost; using namespace qpid::sys; +namespace qpid { +namespace tests { + struct TestStoreOptions : public Options { string name; @@ -66,7 +69,7 @@ struct Completer : public Runnable { delete this; } }; - + class TestStore : public NullMessageStore { public: TestStore(const string& name_, Broker& broker_) : name(name_), broker(broker_) {} @@ -83,7 +86,7 @@ class TestStore : public NullMessageStore { // Check the message for special instructions. size_t i = string::npos; - size_t j = string::npos; + size_t j = string::npos; if (strncmp(data.c_str(), TEST_STORE_DO.c_str(), strlen(TEST_STORE_DO.c_str())) == 0 && (i = data.find(name+"[")) != string::npos && (j = data.find("]", i)) != string::npos) @@ -144,3 +147,5 @@ struct TestStorePlugin : public Plugin { }; static TestStorePlugin pluginInstance; + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/test_tools.h b/qpid/cpp/src/tests/test_tools.h index 54837d3e5b..832c04af04 100644 --- a/qpid/cpp/src/tests/test_tools.h +++ b/qpid/cpp/src/tests/test_tools.h @@ -34,7 +34,7 @@ template std::ostream& seqPrint(std::ostream& o, const T& seq) { return o; } -// Compare sequences +// Compare sequences template bool seqEqual(const T& a, const U& b) { typename T::const_iterator i = a.begin(); @@ -60,6 +60,9 @@ template bool operator == (const boost::assign_detail::generic_list& b, const vector& a) { return seqEqual(a, b); } } +namespace qpid { +namespace tests { + /** NB: order of parameters is regex first, in line with * CHECK(expected, actual) convention. */ @@ -98,6 +101,7 @@ inline std::string getLibPath(const char* envName, const char* defaultPath = 0) return defaultPath; } +}} // namespace qpid::tests #endif /*!TEST_TOOLS_H*/ diff --git a/qpid/cpp/src/tests/topic_listener.cpp b/qpid/cpp/src/tests/topic_listener.cpp index 44070cd4c9..aa8c19df99 100644 --- a/qpid/cpp/src/tests/topic_listener.cpp +++ b/qpid/cpp/src/tests/topic_listener.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -23,7 +23,7 @@ * This file provides one half of a test and example of a pub-sub * style of interaction. See topic_publisher.cpp for the other half, * in which the logic for publishing is defined. - * + * * This file contains the listener logic. A listener will subscribe to * a logical 'topic'. It will count the number of messages it receives * and the time elapsed between the first one and the last one. It @@ -50,11 +50,14 @@ using namespace qpid::sys; using namespace qpid::framing; using namespace std; +namespace qpid { +namespace tests { + /** * A message listener implementation in which the runtime logic is * defined. */ -class Listener : public MessageListener{ +class Listener : public MessageListener{ Session session; SubscriptionManager& mgr; const string responseQueue; @@ -62,7 +65,7 @@ class Listener : public MessageListener{ bool init; int count; AbsTime start; - + void shutdown(); void report(); public: @@ -91,6 +94,52 @@ struct Args : public qpid::TestOptions { } }; +Listener::Listener(const Session& s, SubscriptionManager& m, const string& _responseq, bool tx) : + session(s), mgr(m), responseQueue(_responseq), transactional(tx), init(false), count(0){} + +void Listener::received(Message& message){ + if(!init){ + start = now(); + count = 0; + init = true; + cout << "Batch started." << endl; + } + string type = message.getHeaders().getAsString("TYPE"); + + if(string("TERMINATION_REQUEST") == type){ + shutdown(); + }else if(string("REPORT_REQUEST") == type){ + subscription.accept(subscription.getUnaccepted()); // Accept everything upto this point + cout <<"Batch ended, sending report." << endl; + //send a report: + report(); + init = false; + }else if (++count % 1000 == 0){ + cout <<"Received " << count << " messages." << endl; + } +} + +void Listener::shutdown(){ + mgr.stop(); +} + +void Listener::report(){ + AbsTime finish = now(); + Duration time(start, finish); + stringstream reportstr; + reportstr << "Received " << count << " messages in " + << time/TIME_MSEC << " ms."; + Message msg(reportstr.str(), responseQueue); + msg.getHeaders().setString("TYPE", "REPORT"); + session.messageTransfer(arg::destination="amq.direct", arg::content=msg, arg::acceptMode=1); + if(transactional){ + sync(session).txCommit(); + } +} + +}} // namespace qpid::tests + +using namespace qpid::tests; /** * The main routine creates a Listener instance and sets it up to @@ -142,7 +191,7 @@ int main(int argc, char** argv){ if (args.transactional) { session.txSelect(); } - + cout << "topic_listener: listening..." << endl; mgr.run(); if (args.durable) { @@ -158,47 +207,3 @@ int main(int argc, char** argv){ } return 1; } - -Listener::Listener(const Session& s, SubscriptionManager& m, const string& _responseq, bool tx) : - session(s), mgr(m), responseQueue(_responseq), transactional(tx), init(false), count(0){} - -void Listener::received(Message& message){ - if(!init){ - start = now(); - count = 0; - init = true; - cout << "Batch started." << endl; - } - string type = message.getHeaders().getAsString("TYPE"); - - if(string("TERMINATION_REQUEST") == type){ - shutdown(); - }else if(string("REPORT_REQUEST") == type){ - subscription.accept(subscription.getUnaccepted()); // Accept everything upto this point - cout <<"Batch ended, sending report." << endl; - //send a report: - report(); - init = false; - }else if (++count % 1000 == 0){ - cout <<"Received " << count << " messages." << endl; - } -} - -void Listener::shutdown(){ - mgr.stop(); -} - -void Listener::report(){ - AbsTime finish = now(); - Duration time(start, finish); - stringstream reportstr; - reportstr << "Received " << count << " messages in " - << time/TIME_MSEC << " ms."; - Message msg(reportstr.str(), responseQueue); - msg.getHeaders().setString("TYPE", "REPORT"); - session.messageTransfer(arg::destination="amq.direct", arg::content=msg, arg::acceptMode=1); - if(transactional){ - sync(session).txCommit(); - } -} - diff --git a/qpid/cpp/src/tests/topic_publisher.cpp b/qpid/cpp/src/tests/topic_publisher.cpp index 40055bbfa0..3381132b1a 100644 --- a/qpid/cpp/src/tests/topic_publisher.cpp +++ b/qpid/cpp/src/tests/topic_publisher.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -23,7 +23,7 @@ * This file provides one half of a test and example of a pub-sub * style of interaction. See topic_listener.cpp for the other half, in * which the logic for subscribers is defined. - * + * * This file contains the publisher logic. The publisher will send a * number of messages to the exchange with the appropriate routing key * for the logical 'topic'. Once it has done this it will then send a @@ -49,19 +49,22 @@ using namespace qpid::client; using namespace qpid::sys; using namespace std; +namespace qpid { +namespace tests { + /** * The publishing logic is defined in this class. It implements * message listener and can therfore be used to receive messages sent * back by the subscribers. */ -class Publisher { +class Publisher { AsyncSession session; SubscriptionManager mgr; LocalQueue queue; const string controlTopic; const bool transactional; const bool durable; - + string generateData(int size); public: @@ -99,6 +102,64 @@ struct Args : public TestOptions { } }; +Publisher::Publisher(const AsyncSession& _session, const string& _controlTopic, bool tx, bool d) : + session(_session), mgr(session), controlTopic(_controlTopic), transactional(tx), durable(d) +{ + mgr.subscribe(queue, "response"); +} + +int64_t Publisher::publish(int msgs, int listeners, int size){ + Message msg(generateData(size), controlTopic); + if (durable) { + msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); + } + AbsTime start = now(); + + for(int i = 0; i < msgs; i++){ + session.messageTransfer(arg::content=msg, arg::destination="amq.topic", arg::acceptMode=1); + } + //send report request + Message reportRequest("", controlTopic); + reportRequest.getHeaders().setString("TYPE", "REPORT_REQUEST"); + session.messageTransfer(arg::content=reportRequest, arg::destination="amq.topic", arg::acceptMode=1); + if(transactional){ + sync(session).txCommit(); + } + //wait for a response from each listener (TODO, could log these) + for (int i = 0; i < listeners; i++) { + Message report = queue.pop(); + } + + if(transactional){ + sync(session).txCommit(); + } + + AbsTime finish = now(); + return Duration(start, finish); +} + +string Publisher::generateData(int size){ + string data; + for(int i = 0; i < size; i++){ + data += ('A' + (i / 26)); + } + return data; +} + +void Publisher::terminate(){ + //send termination request + Message terminationRequest("", controlTopic); + terminationRequest.getHeaders().setString("TYPE", "TERMINATION_REQUEST"); + session.messageTransfer(arg::content=terminationRequest, arg::destination="amq.topic", arg::acceptMode=1); + if(transactional){ + session.txCommit(); + } +} + +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char** argv) { try{ Args args; @@ -120,11 +181,11 @@ int main(int argc, char** argv) { Message m = statusQ.get(); if( m.getData().find("topic_listener: ", 0) == 0 ) { cout << "Listener " << (i+1) << " of " << args.subscribers - << " is ready (pid " << m.getData().substr(16, m.getData().length() - 16) - << ")" << endl; + << " is ready (pid " << m.getData().substr(16, m.getData().length() - 16) + << ")" << endl; } else { throw Exception(QPID_MSG("Unexpected message received on status queue: " << m.getData())); - } + } } } @@ -150,12 +211,12 @@ int main(int argc, char** argv) { if(!min || msecs < min) min = msecs; sum += msecs; cout << "Completed " << (i+1) << " of " << batchSize - << " in " << msecs << "ms" << endl; + << " in " << msecs << "ms" << endl; } publisher.terminate(); int64_t avg = sum / batchSize; if(batchSize > 1){ - cout << batchSize << " batches completed. avg=" << avg << + cout << batchSize << " batches completed. avg=" << avg << ", max=" << max << ", min=" << min << endl; } session.close(); @@ -167,57 +228,3 @@ int main(int argc, char** argv) { } return 1; } - -Publisher::Publisher(const AsyncSession& _session, const string& _controlTopic, bool tx, bool d) : - session(_session), mgr(session), controlTopic(_controlTopic), transactional(tx), durable(d) -{ - mgr.subscribe(queue, "response"); -} - -int64_t Publisher::publish(int msgs, int listeners, int size){ - Message msg(generateData(size), controlTopic); - if (durable) { - msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); - } - AbsTime start = now(); - - for(int i = 0; i < msgs; i++){ - session.messageTransfer(arg::content=msg, arg::destination="amq.topic", arg::acceptMode=1); - } - //send report request - Message reportRequest("", controlTopic); - reportRequest.getHeaders().setString("TYPE", "REPORT_REQUEST"); - session.messageTransfer(arg::content=reportRequest, arg::destination="amq.topic", arg::acceptMode=1); - if(transactional){ - sync(session).txCommit(); - } - //wait for a response from each listener (TODO, could log these) - for (int i = 0; i < listeners; i++) { - Message report = queue.pop(); - } - - if(transactional){ - sync(session).txCommit(); - } - - AbsTime finish = now(); - return Duration(start, finish); -} - -string Publisher::generateData(int size){ - string data; - for(int i = 0; i < size; i++){ - data += ('A' + (i / 26)); - } - return data; -} - -void Publisher::terminate(){ - //send termination request - Message terminationRequest("", controlTopic); - terminationRequest.getHeaders().setString("TYPE", "TERMINATION_REQUEST"); - session.messageTransfer(arg::content=terminationRequest, arg::destination="amq.topic", arg::acceptMode=1); - if(transactional){ - session.txCommit(); - } -} diff --git a/qpid/cpp/src/tests/txjob.cpp b/qpid/cpp/src/tests/txjob.cpp index 94db96a666..a7a905c1b7 100644 --- a/qpid/cpp/src/tests/txjob.cpp +++ b/qpid/cpp/src/tests/txjob.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -33,7 +33,10 @@ using namespace qpid::client; using namespace qpid::sys; -struct Args : public qpid::TestOptions +namespace qpid { +namespace tests { + +struct Args : public qpid::TestOptions { string workQueue; string source; @@ -43,10 +46,10 @@ struct Args : public qpid::TestOptions bool quit; bool declareQueues; - Args() : workQueue("txshift-control"), source("txshift-1"), dest("txshift-2"), messages(0), jobs(0), + Args() : workQueue("txshift-control"), source("txshift-1"), dest("txshift-2"), messages(0), jobs(0), quit(false), declareQueues(false) { - addOptions() + addOptions() ("messages", qpid::optValue(messages, "N"), "Number of messages to shift") ("jobs", qpid::optValue(jobs, "N"), "Number of shift jobs to request") ("source", qpid::optValue(source, "QUEUE NAME"), "source queue from which messages will be shifted") @@ -57,6 +60,10 @@ struct Args : public qpid::TestOptions } }; +}} // namespace qpid::tests + +using namespace qpid::tests; + //TODO: might be nice to make this capable of failover as well at some //point; for now its just for the setup phase. int main(int argc, char** argv) diff --git a/qpid/cpp/src/tests/txshift.cpp b/qpid/cpp/src/tests/txshift.cpp index 97135c9829..882d3716d8 100644 --- a/qpid/cpp/src/tests/txshift.cpp +++ b/qpid/cpp/src/tests/txshift.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -34,14 +34,17 @@ using namespace qpid::client; using namespace qpid::sys; -struct Args : public qpid::TestOptions +namespace qpid { +namespace tests { + +struct Args : public qpid::TestOptions { string workQueue; size_t workers; Args() : workQueue("txshift-control"), workers(1) { - addOptions() + addOptions() ("workers", qpid::optValue(workers, "N"), "Number of separate worker sessions to start") ("work-queue", qpid::optValue(workQueue, "NAME"), "work queue from which to take instructions"); } @@ -61,7 +64,7 @@ struct Transfer : MessageListener Transfer(const std::string control_) : control(control_), expected(0), transfered(0) {} - void subscribeToSource(SubscriptionManager manager) + void subscribeToSource(SubscriptionManager manager) { sourceSettings.autoAck = 0;//will accept once at the end of the batch sourceSettings.flowControl = FlowControl::messageCredit(expected); @@ -69,7 +72,7 @@ struct Transfer : MessageListener QPID_LOG(info, "Subscribed to source: " << source << " expecting: " << expected); } - void subscribeToControl(SubscriptionManager manager) + void subscribeToControl(SubscriptionManager manager) { controlSettings.flowControl = FlowControl::messageCredit(1); controlSubscription = manager.subscribe(*this, control, controlSettings); @@ -94,7 +97,7 @@ struct Transfer : MessageListener message.getDeliveryProperties().setRoutingKey(destination); async(sourceSubscription.getSession()).messageTransfer(arg::content=message); if (++transfered == expected) { - QPID_LOG(info, "completed job: " << transfered << " messages shifted from " << + QPID_LOG(info, "completed job: " << transfered << " messages shifted from " << source << " to " << destination); sourceSubscription.accept(sourceSubscription.getUnaccepted()); sourceSubscription.getSession().txCommit(); @@ -111,7 +114,7 @@ struct Transfer : MessageListener destination = message.getHeaders().getAsString("dest"); expected = message.getHeaders().getAsInt("count"); transfered = 0; - QPID_LOG(info, "received transfer request: " << expected << " messages to be shifted from " << + QPID_LOG(info, "received transfer request: " << expected << " messages to be shifted from " << source << " to " << destination); subscribeToSource(controlSubscription.getSubscriptionManager()); } else if (message.getData() == "quit") { @@ -133,7 +136,7 @@ struct Worker : FailoverManager::Command, Runnable Worker(FailoverManager& c, const std::string& controlQueue) : connection(c), transfer(controlQueue) {} - void run() + void run() { connection.execute(*this); } @@ -148,7 +151,7 @@ struct Worker : FailoverManager::Command, Runnable runner.join(); } - void execute(AsyncSession& session, bool isRetry) + void execute(AsyncSession& session, bool isRetry) { if (isRetry) QPID_LOG(info, "Retrying..."); session.txSelect(); @@ -159,6 +162,10 @@ struct Worker : FailoverManager::Command, Runnable } }; +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char** argv) { Args opts; diff --git a/qpid/cpp/src/tests/txtest.cpp b/qpid/cpp/src/tests/txtest.cpp index f604df7e21..d0ba2f1245 100644 --- a/qpid/cpp/src/tests/txtest.cpp +++ b/qpid/cpp/src/tests/txtest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -41,6 +41,9 @@ using namespace qpid::client; using namespace qpid::sys; using std::string; +namespace qpid { +namespace tests { + typedef std::vector StringSet; struct Args : public qpid::TestOptions { @@ -55,12 +58,12 @@ struct Args : public qpid::TestOptions { bool dtx; bool quiet; - Args() : init(true), transfer(true), check(true), - size(256), durable(true), queues(2), + Args() : init(true), transfer(true), check(true), + size(256), durable(true), queues(2), base("tx-test"), msgsPerTx(1), txCount(1), totalMsgCount(10), dtx(false), quiet(false) { - addOptions() + addOptions() ("init", optValue(init, "yes|no"), "Declare queues and populate one with the initial set of messages.") ("transfer", optValue(transfer, "yes|no"), "'Move' messages from one queue to another using transactions to ensure no message loss.") @@ -83,7 +86,7 @@ std::string generateData(uint size) { if (size < chars.length()) { return chars.substr(0, size); - } + } std::string data; for (uint i = 0; i < (size / chars.length()); i++) { data += chars; @@ -103,18 +106,18 @@ void generateSet(const std::string& base, uint count, StringSet& collection) Args opts; -struct Client +struct Client { Connection connection; AsyncSession session; - Client() + Client() { opts.open(connection); session = connection.newSession(); } - ~Client() + ~Client() { try{ session.close(); @@ -134,19 +137,19 @@ struct Transfer : public Client, public Runnable Transfer(const std::string& to, const std::string& from) : src(to), dest(from), xid(0x4c414e47, "", from) {} - void run() + void run() { try { - + if (opts.dtx) session.dtxSelect(); else session.txSelect(); SubscriptionManager subs(session); - + LocalQueue lq; SubscriptionSettings settings(FlowControl::messageWindow(opts.msgsPerTx)); settings.autoAck = 0; // Disabled Subscription sub = subs.subscribe(lq, src, settings); - + for (uint t = 0; t < opts.txCount; t++) { Message in; Message out("", dest); @@ -187,7 +190,7 @@ struct Transfer : public Client, public Runnable } }; -struct Controller : public Client +struct Controller : public Client { StringSet ids; StringSet queues; @@ -198,7 +201,7 @@ struct Controller : public Client generateSet("msg", opts.totalMsgCount, ids); } - void init() + void init() { //declare queues for (StringSet::iterator i = queues.begin(); i != queues.end(); i++) { @@ -236,7 +239,7 @@ struct Controller : public Client } } - int check() + int check() { SubscriptionManager subs(session); @@ -291,10 +294,10 @@ struct Controller : public Client //check that drained == ids StringSet missing; - set_difference(ids.begin(), ids.end(), drained.begin(), drained.end(), back_inserter(missing)); + set_difference(ids.begin(), ids.end(), drained.begin(), drained.end(), back_inserter(missing)); StringSet extra; - set_difference(drained.begin(), drained.end(), ids.begin(), ids.end(), back_inserter(extra)); + set_difference(drained.begin(), drained.end(), ids.begin(), ids.end(), back_inserter(extra)); if (missing.empty() && extra.empty()) { std::cout << "All expected messages were retrieved." << std::endl; @@ -303,26 +306,30 @@ struct Controller : public Client if (!missing.empty()) { std::cout << "The following ids were missing:" << std::endl; for (StringSet::iterator i = missing.begin(); i != missing.end(); i++) { - std::cout << " '" << *i << "'" << std::endl; - } + std::cout << " '" << *i << "'" << std::endl; + } } if (!extra.empty()) { std::cout << "The following extra ids were encountered:" << std::endl; for (StringSet::iterator i = extra.begin(); i != extra.end(); i++) { - std::cout << " '" << *i << "'" << std::endl; - } + std::cout << " '" << *i << "'" << std::endl; + } } return 1; } } }; +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char** argv) { try { opts.parse(argc, argv); Controller controller; - if (opts.init) controller.init(); + if (opts.init) controller.init(); if (opts.transfer) controller.transfer(); if (opts.check) return controller.check(); return 0; diff --git a/qpid/java/broker/bin/qpid-passwd b/qpid/java/broker/bin/qpid-passwd index 63b30b5e71..b84580da60 100755 --- a/qpid/java/broker/bin/qpid-passwd +++ b/qpid/java/broker/bin/qpid-passwd @@ -1,35 +1,35 @@ -#!/bin/bash -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -if [ -z "$QPID_HOME" ]; then - export QPID_HOME=$(dirname $(dirname $(readlink -f $0))) - export PATH=${PATH}:${QPID_HOME}/bin -fi - -# Set classpath to include Qpid jar with all required jars in manifest -QPID_LIBS=$QPID_HOME/lib/qpid-all.jar - -# Set other variables used by the qpid-run script before calling -export JAVA=java \ - JAVA_VM=-server \ - JAVA_MEM=-Xmx1024m \ - QPID_CLASSPATH=$QPID_LIBS - -. qpid-run org.apache.qpid.tools.security.Passwd "$@" +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +if [ -z "$QPID_HOME" ]; then + export QPID_HOME=$(dirname $(dirname $(readlink -f $0))) + export PATH=${PATH}:${QPID_HOME}/bin +fi + +# Set classpath to include Qpid jar with all required jars in manifest +QPID_LIBS=$QPID_HOME/lib/qpid-all.jar + +# Set other variables used by the qpid-run script before calling +export JAVA=java \ + JAVA_VM=-server \ + JAVA_MEM=-Xmx1024m \ + QPID_CLASSPATH=$QPID_LIBS + +. qpid-run org.apache.qpid.tools.security.Passwd "$@" diff --git a/qpid/java/broker/etc/acl.config.xml b/qpid/java/broker/etc/acl.config.xml deleted file mode 100644 index a2b723fc63..0000000000 --- a/qpid/java/broker/etc/acl.config.xml +++ /dev/null @@ -1,230 +0,0 @@ - - - - ${QPID_HOME} - ${QPID_WORK} - ${prefix}/etc - - - false - - false - nio - 5672 - 8672 - 32768 - 32768 - - - false - 8999 - false - - - - false - false - 65535 - false - false - - - - - - - passwordfile - org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase - - - passwordFile - ${conf}/passwd - - - - - - - org.apache.qpid.server.security.access.plugins.DenyAll - - - - ${conf}/jmxremote.access - passwordfile - - - - - ${conf}/virtualhosts - - - test - - - org.apache.qpid.server.store.MemoryMessageStore - - - - amq.direct - - 4235264 - - 2117632 - - 600000 - - - - - - org.apache.qpid.server.security.access.plugins.SimpleXML - - - - - - - amq.direct - - - - - example.RequestQueue - - client - - - - - - tmp_* - - server - - - - TempQueue* - - server - - - - - - - - - - - - - - - - - client - - - - - - - example.RequestQueue - - server - - - - - - - - - - - - - - - - amq.direct - - client - - - - - - - example.RequestQueue - - server - - - - - - - - - - - - - - - - development - - - org.apache.qpid.server.store.MemoryMessageStore - - - - - - localhost - - - org.apache.qpid.server.store.MemoryMessageStore - - - - - - - - 0 - 2.0 - - - ${conf}/virtualhosts.xml - - - diff --git a/qpid/java/broker/etc/config-systests-derby.xml b/qpid/java/broker/etc/config-systests-derby.xml deleted file mode 100644 index e9cfa04ab5..0000000000 --- a/qpid/java/broker/etc/config-systests-derby.xml +++ /dev/null @@ -1,141 +0,0 @@ - - - - ${QPID_HOME} - ${QPID_WORK} - ${prefix}/etc - - - false - - false - - nio - 5672 - 8672 - 32768 - 32768 - - - false - 8999 - - false - - ${prefix}/../test-profiles/test_resources/ssl/keystore.jks - password - - - - - false - false - 65535 - false - false - - - - - - - passwordfile - org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase - - - passwordFile - ${conf}/passwd - - - - - - - org.apache.qpid.server.security.access.plugins.AllowAll - - - false - - - ${conf}/jmxremote.access - passwordfile - - - - - ${conf}/virtualhosts - - - localhost - - - org.apache.qpid.server.store.DerbyMessageStore - ${work}/derbyDB/localhost-store - - - - 20000 - - - - - - - development - - - org.apache.qpid.server.store.DerbyMessageStore - ${work}/derbyDB/development-store - - - - - - test - - - org.apache.qpid.server.store.DerbyMessageStore - ${work}/derbyDB/test-store - - - - - - - 0 - 2.0 - - - true - - - ${conf}/virtualhosts-systests.xml - - - diff --git a/qpid/java/broker/etc/config-systests.xml b/qpid/java/broker/etc/config-systests.xml deleted file mode 100644 index 42e8c9dbba..0000000000 --- a/qpid/java/broker/etc/config-systests.xml +++ /dev/null @@ -1,143 +0,0 @@ - - - - ${QPID_HOME} - ${QPID_WORK} - ${prefix}/etc - - - false - - false - 262144 - 262144 - - nio - 5672 - 8672 - 32768 - 32768 - - - false - 8999 - - false - - ${prefix}/../test-profiles/test_resources/ssl/keystore.jks - password - - - - - false - false - 65535 - false - false - en_US - - - - - - - passwordfile - org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase - - - passwordFile - ${conf}/passwd - - - - - - - org.apache.qpid.server.security.access.plugins.AllowAll - - - false - - - ${conf}/jmxremote.access - passwordfile - - - - - ${conf}/virtualhosts - - - localhost - - - org.apache.qpid.server.store.MemoryMessageStore - - - - 20000 - - - - - - - development - - - org.apache.qpid.server.store.MemoryMessageStore - - - - - - test - - - org.apache.qpid.server.store.MemoryMessageStore - - - - - - - 0 - 2.0 - - - true - - - ON - - ${conf}/virtualhosts-systests.xml - - - diff --git a/qpid/java/broker/etc/config.xml b/qpid/java/broker/etc/config.xml index 928b773606..8fb3a8cf5a 100644 --- a/qpid/java/broker/etc/config.xml +++ b/qpid/java/broker/etc/config.xml @@ -24,17 +24,20 @@ ${QPID_WORK} ${prefix}/etc - - true - true + false + false /path/to/keystore.ks keystorepass - --> + false false + 262144 + 262144 nio 5672 @@ -59,6 +62,7 @@ 65535 false false + en_US @@ -89,40 +93,143 @@ - ${conf}/virtualhosts + test localhost - org.apache.qpid.server.store.MemoryMessageStore + org.apache.qpid.server.store.MemoryMessageStore + 20000 + + + direct + test.direct + true + + + topic + test.topic + + + + amq.direct + 4235264 + + 2117632 + + 600000 + + 50 + + + + queue + + + ping + + + test-queue + + test.direct + true + + + + test-ping + + test.direct + + + + + development - org.apache.qpid.server.store.MemoryMessageStore + org.apache.qpid.server.store.MemoryMessageStore + + + + 30000 + 50 + + queue + + amq.direct + 4235264 + + 2117632 + + 600000 + + + + + ping + + amq.direct + 4235264 + + 2117632 + + 600000 + + + + - test - org.apache.qpid.server.store.MemoryMessageStore + org.apache.qpid.server.store.MemoryMessageStore + + + + 30000 + 50 + + queue + + amq.direct + 4235264 + + 2117632 + + 600000 + + + + + ping + + amq.direct + 4235264 + + 2117632 + + 600000 + + + + - 0 @@ -132,7 +239,8 @@ true - ${conf}/virtualhosts.xml + ON + diff --git a/qpid/java/broker/etc/persistent_config-config-test.xml b/qpid/java/broker/etc/persistent_config-config-test.xml deleted file mode 100644 index 258f54397d..0000000000 --- a/qpid/java/broker/etc/persistent_config-config-test.xml +++ /dev/null @@ -1,93 +0,0 @@ - - - - - ${QPID_WORK} - ${QPID_HOME}/etc - ${conf} - - nio - 5672 - 32768 - 32768 - - - false - 8999 - - - - false - false - 65535 - false - - - - - - passwordfile - org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase - - - passwordFile - ${passwordDir}/passwd - - - - - - - org.apache.qpid.server.security.access.plugins.AllowAll - - - ${passwordDir}/jmxremote.access - passwordfile - - - - - - dev-only - - - org.apache.qpid.server.store.MemoryMessageStore - ${work}/bdbstore/dev-only-store - - - - - - 0 - 2.0 - - - true - - - ${conf}/virtualhosts-config-test.xml - - - diff --git a/qpid/java/broker/etc/sample-parsed-config.xml b/qpid/java/broker/etc/sample-parsed-config.xml deleted file mode 100644 index 37dfae4d2e..0000000000 --- a/qpid/java/broker/etc/sample-parsed-config.xml +++ /dev/null @@ -1,72 +0,0 @@ - - -${QPID_WORK} -${QPID_HOME}/etc -${conf} - -nio -5672 -32768 -32768 - - -false -8999 - - - -false -false -65535 -false - - - - -passwordfile -org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase - - -passwordFile -${passwordDir}/passwd - - - - - -org.apache.qpid.server.security.access.plugins.AllowAll - - -${passwordDir}/jmxremote.access -passwordfile - - -${conf}/virtualhosts-config-test.xml -dev-only - -dev-only - - -org.apache.qpid.server.store.MemoryMessageStore -${work}/bdbstore/dev-only-store - - -amq.direct -102400 -20480 -60000 - -dev-queue - - - - - - -0 -2.0 - - -true - - diff --git a/qpid/java/broker/etc/virtualhosts-config-test.xml b/qpid/java/broker/etc/virtualhosts-config-test.xml deleted file mode 100644 index 168aa074da..0000000000 --- a/qpid/java/broker/etc/virtualhosts-config-test.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - dev-only - - dev-only - - - amq.direct - - 102400 - 20480 - 60000 - - - dev-queue - - - - - diff --git a/qpid/java/broker/etc/virtualhosts-systests.xml b/qpid/java/broker/etc/virtualhosts-systests.xml deleted file mode 100644 index 4f23f55579..0000000000 --- a/qpid/java/broker/etc/virtualhosts-systests.xml +++ /dev/null @@ -1,124 +0,0 @@ - - - - test - - localhost - - - - direct - test.direct - true - - - topic - test.topic - - - - amq.direct - 4235264 - 2117632 - 600000 - 50 - - - queue - - - ping - - - test-queue - - test.direct - true - - - - test-ping - - test.direct - - - - - - - - - - development - - - 30000 - 50 - - queue - - amq.direct - 4235264 - 2117632 - 600000 - - - - ping - - amq.direct - 4235264 - 2117632 - 600000 - - - - - - - test - - - 30000 - 50 - - queue - - amq.direct - 4235264 - 2117632 - 600000 - - - - ping - - amq.direct - 4235264 - 2117632 - 600000 - - - - - - diff --git a/qpid/java/broker/etc/virtualhosts.xml b/qpid/java/broker/etc/virtualhosts.xml deleted file mode 100644 index f62ec3f5d7..0000000000 --- a/qpid/java/broker/etc/virtualhosts.xml +++ /dev/null @@ -1,123 +0,0 @@ - - - - test - - localhost - - - - direct - test.direct - true - - - topic - test.topic - - - - amq.direct - 4235264 - 2117632 - 600000 - - - queue - - - ping - - - test-queue - - test.direct - true - - - - test-ping - - test.direct - - - - - - - - - - development - - - 30000 - 5000 - - queue - - amq.direct - 4235264 - 2117632 - 600000 - - - - ping - - amq.direct - 4235264 - 2117632 - 600000 - - - - - - - test - - - 30000 - 5000 - - queue - - amq.direct - 4235264 - 2117632 - 600000 - - - - ping - - amq.direct - 4235264 - 2117632 - 600000 - - - - - - diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java index 2afd3c1dc3..5cfa8066e5 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java @@ -317,8 +317,10 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr try { queue.delete(); - _messageStore.removeQueue(queue); - + if (queue.isDurable()) + { + _messageStore.removeQueue(queue); + } } catch (AMQException ex) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java index b3c8975c7c..a72c2889d1 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java @@ -148,32 +148,27 @@ public class ServerConfiguration implements SignalHandler Object thing = i.next(); if (thing instanceof String) { + //Open the Virtualhost.xml file and copy values in to main config XMLConfiguration vhostConfiguration = new XMLConfiguration((String) thing); - List hosts = vhostConfiguration.getList("virtualhost.name"); - for (int j = 0; j < hosts.size(); j++) - { - String name = (String) hosts.get(j); - // Add the keys of the virtual host to the main config then bail out - - Configuration myConf = vhostConfiguration.subset("virtualhost." + name); - Iterator k = myConf.getKeys(); - while (k.hasNext()) - { - String key = (String) k.next(); - conf.setProperty("virtualhosts.virtualhost."+name+"."+key, myConf.getProperty(key)); - } - VirtualHostConfiguration vhostConfig = new VirtualHostConfiguration(name, conf.subset("virtualhosts.virtualhost."+name)); - _virtualHosts.put(vhostConfig.getName(), vhostConfig); - } - // Grab things other than the virtualhosts themselves Iterator keys = vhostConfiguration.getKeys(); while (keys.hasNext()) { String key = (String) keys.next(); - conf.setProperty("virtualhosts."+key, vhostConfiguration.getProperty(key)); + conf.setProperty("virtualhosts." + key, vhostConfiguration.getProperty(key)); } } } + + List hosts = conf.getList("virtualhosts.virtualhost.name"); + for (int j = 0; j < hosts.size(); j++) + { + String name = (String) hosts.get(j); + // Add the keys of the virtual host to the main config then bail out + + VirtualHostConfiguration vhostConfig = new VirtualHostConfiguration(name, conf.subset("virtualhosts.virtualhost." + name)); + _virtualHosts.put(vhostConfig.getName(), vhostConfig); + } + } private void substituteEnvironmentVariables() @@ -203,7 +198,7 @@ public class ServerConfiguration implements SignalHandler } /** - * Check the configuration file to see if status updates are enabled. + * Check the configuration file to see if status updates are enabled. * @return true if status updates are enabled */ public boolean getStatusUpdatesEnabled() @@ -467,7 +462,7 @@ public class ServerConfiguration implements SignalHandler { return getConfig().getBoolean("management.enabled", true); } - + public void setManagementEnabled(boolean enabled) { getConfig().setProperty("management.enabled", enabled); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java index aec437b700..3b776a62b4 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java @@ -195,10 +195,22 @@ public class VirtualHost implements Accessable // perform a createExchange twice with the same details in the // MessageStore(RoutingTable) as some instances may not like that. // Derby being one. + // todo this can be removed with the resolution fo QPID-2096 configFileRT.exchange.clear(); initialiseModel(hostConfig); + //todo REMOVE Work Around for QPID-2096 + // This means that all durable exchanges declared in the configuration + // will not be stored in the MessageStore. + // They will still be created/registered/available on startup for as + // long as they are contained in the configuration. However, when they + // are removed from the configuration they will no longer exist. + // This differs from durable queues as they will be writen to to the + // store. After QPID-2096 has been resolved exchanges will mirror that + // functionality. + configFileRT.exchange.clear(); + if (store != null) { _messageStore = store; diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java index 2285f5256e..93e7e756e6 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java @@ -54,7 +54,7 @@ public class ServerConfigurationTest extends TestCase _config = new XMLConfiguration(); } - + @Override public void tearDown() throws Exception { @@ -727,7 +727,7 @@ public class ServerConfigurationTest extends TestCase assertEquals(true, config.getQpidNIO()); // From the second file, not // present in the first } - + public void testVariableInterpolation() throws Exception { File mainFile = File.createTempFile(getClass().getName(), null); @@ -742,7 +742,7 @@ public class ServerConfigurationTest extends TestCase out.close(); ServerConfiguration config = new ServerConfiguration(mainFile.getAbsoluteFile()); - assertEquals("Did not get correct interpolated value", + assertEquals("Did not get correct interpolated value", "foo", config.getManagementKeyStorePath()); } @@ -783,7 +783,7 @@ public class ServerConfigurationTest extends TestCase out.write("\t\n"); out.write("\n"); out.close(); - + // Load config ApplicationRegistry reg = new ConfigurationFileApplicationRegistry(mainFile); ApplicationRegistry.initialise(reg, 1); @@ -791,18 +791,18 @@ public class ServerConfigurationTest extends TestCase // Test config VirtualHostRegistry virtualHostRegistry = reg.getVirtualHostRegistry(); VirtualHost virtualHost = virtualHostRegistry.getVirtualHost("test"); - + TestNetworkDriver testDriver = new TestNetworkDriver(); testDriver.setRemoteAddress("127.0.0.1"); AMQProtocolEngine session = new AMQProtocolEngine(virtualHostRegistry, testDriver); assertFalse(reg.getAccessManager().authoriseConnect(session, virtualHost)); - + testDriver.setRemoteAddress("127.1.2.3"); session = new AMQProtocolEngine(virtualHostRegistry, testDriver); assertTrue(reg.getAccessManager().authoriseConnect(session, virtualHost)); } - + public void testCombinedConfigurationFirewall() throws Exception { // Write out config @@ -868,7 +868,7 @@ public class ServerConfigurationTest extends TestCase TestNetworkDriver testDriver = new TestNetworkDriver(); testDriver.setRemoteAddress("127.0.0.1"); - + AMQProtocolEngine session = new AMQProtocolEngine(virtualHostRegistry, testDriver); session.setNetworkDriver(testDriver); assertFalse(reg.getAccessManager().authoriseConnect(session, virtualHost)); @@ -945,22 +945,22 @@ public class ServerConfigurationTest extends TestCase fileBRandom.setLength(0); fileBRandom.seek(0); fileBRandom.close(); - + out = new FileWriter(fileB); out.write("\n"); out.write("\t"); out.write("\n"); out.close(); - + reg.getConfiguration().reparseConfigFile(); - + assertTrue(reg.getAccessManager().authoriseConnect(session, virtualHost)); - + fileBRandom = new RandomAccessFile(fileB, "rw"); fileBRandom.setLength(0); fileBRandom.seek(0); fileBRandom.close(); - + out = new FileWriter(fileB); out.write("\n"); out.write("\t"); @@ -968,17 +968,17 @@ public class ServerConfigurationTest extends TestCase out.close(); reg.getConfiguration().reparseConfigFile(); - + assertFalse(reg.getAccessManager().authoriseConnect(session, virtualHost)); } public void testnewParserOutputVsOldParserOutput() throws ConfigurationException { String configDir = System.getProperty("QPID_HOME")+"/etc"; - - XMLConfiguration oldConfig = new XMLConfiguration(configDir +"/sample-parsed-config.xml"); - Configuration newConfig = new ServerConfiguration(new File(configDir+"/persistent_config-config-test.xml")).getConfig(); - + + XMLConfiguration oldConfig = new XMLConfiguration(configDir +"/config-systests-ServerConfigurationTest-Old.xml"); + Configuration newConfig = new ServerConfiguration(new File(configDir+"/config-systests-ServerConfigurationTest-New.xml")).getConfig(); + Iterator xmlKeys = oldConfig.getKeys(); while (xmlKeys.hasNext()) { @@ -986,6 +986,21 @@ public class ServerConfigurationTest extends TestCase assertEquals("Incorrect value for "+key, oldConfig.getProperty(key), newConfig.getProperty(key)); } } - - + + + public void testNoVirtualhostXMLFile() throws Exception + { + int REGISTRY=1; + + File configFile = new File(System.getProperty("QPID_HOME")+"/etc/config.xml"); + assertTrue(configFile.exists()); + + ApplicationRegistry.initialise(new ConfigurationFileApplicationRegistry(configFile), REGISTRY); + + VirtualHostRegistry virtualHostRegistry = ApplicationRegistry.getInstance(REGISTRY).getVirtualHostRegistry(); + + assertEquals("Incorrect virtualhost count", 3 , virtualHostRegistry.getVirtualHosts().size()); + } + + } diff --git a/qpid/java/systests/etc/config-systests-ServerConfigurationTest-New.xml b/qpid/java/systests/etc/config-systests-ServerConfigurationTest-New.xml new file mode 100644 index 0000000000..33cc90b895 --- /dev/null +++ b/qpid/java/systests/etc/config-systests-ServerConfigurationTest-New.xml @@ -0,0 +1,93 @@ + + + + + ${QPID_WORK} + ${QPID_HOME}/etc + ${conf} + + nio + 5672 + 32768 + 32768 + + + false + 8999 + + + + false + false + 65535 + false + + + + + + passwordfile + org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase + + + passwordFile + ${passwordDir}/passwd + + + + + + + org.apache.qpid.server.security.access.plugins.AllowAll + + + ${passwordDir}/jmxremote.access + passwordfile + + + + + + dev-only + + + org.apache.qpid.server.store.MemoryMessageStore + ${work}/bdbstore/dev-only-store + + + + + + 0 + 2.0 + + + true + + + ${conf}/virtualhosts-ServerConfigurationTest-New.xml + + + diff --git a/qpid/java/systests/etc/config-systests-ServerConfigurationTest-Old.xml b/qpid/java/systests/etc/config-systests-ServerConfigurationTest-Old.xml new file mode 100644 index 0000000000..67e0702c41 --- /dev/null +++ b/qpid/java/systests/etc/config-systests-ServerConfigurationTest-Old.xml @@ -0,0 +1,72 @@ + + +${QPID_WORK} +${QPID_HOME}/etc +${conf} + +nio +5672 +32768 +32768 + + +false +8999 + + + +false +false +65535 +false + + + + +passwordfile +org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase + + +passwordFile +${passwordDir}/passwd + + + + + +org.apache.qpid.server.security.access.plugins.AllowAll + + +${passwordDir}/jmxremote.access +passwordfile + + +${conf}/virtualhosts-ServerConfigurationTest-New.xml +dev-only + +dev-only + + +org.apache.qpid.server.store.MemoryMessageStore +${work}/bdbstore/dev-only-store + + +amq.direct +102400 +20480 +60000 + +dev-queue + + + + + + +0 +2.0 + + +true + + diff --git a/qpid/java/systests/etc/config-systests-acl-settings.xml b/qpid/java/systests/etc/config-systests-acl-settings.xml new file mode 100644 index 0000000000..c5374a5c5e --- /dev/null +++ b/qpid/java/systests/etc/config-systests-acl-settings.xml @@ -0,0 +1,139 @@ + + + + + + + + test + + + amq.direct + + 4235264 + + 2117632 + + 600000 + + + + + + org.apache.qpid.server.security.access.plugins.SimpleXML + + + + + + + amq.direct + + + + + example.RequestQueue + + client + + + + + + tmp_* + + server + + + + TempQueue* + + server + + + + + + + + + + + + + + + + + client + + + + + + + example.RequestQueue + + server + + + + + + + + + + + + + + + + amq.direct + + client + + + + + + + example.RequestQueue + + server + + + + + + + + + + + + + + + + diff --git a/qpid/java/systests/etc/config-systests-acl.xml b/qpid/java/systests/etc/config-systests-acl.xml new file mode 100644 index 0000000000..34104dbe6b --- /dev/null +++ b/qpid/java/systests/etc/config-systests-acl.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + diff --git a/qpid/java/systests/etc/config-systests-derby-settings.xml b/qpid/java/systests/etc/config-systests-derby-settings.xml new file mode 100644 index 0000000000..9c25b5682e --- /dev/null +++ b/qpid/java/systests/etc/config-systests-derby-settings.xml @@ -0,0 +1,64 @@ + + + + + ${conf}/virtualhosts + + + localhost + + + org.apache.qpid.server.store.DerbyMessageStore + ${work}/derbyDB/localhost-store + + + + 20000 + + + + + + + development + + + org.apache.qpid.server.store.DerbyMessageStore + ${work}/derbyDB/development-store + + + + + + test + + + org.apache.qpid.server.store.DerbyMessageStore + ${work}/derbyDB/test-store + + + + + + + + diff --git a/qpid/java/systests/etc/config-systests-derby.xml b/qpid/java/systests/etc/config-systests-derby.xml new file mode 100644 index 0000000000..18ba0c4ad9 --- /dev/null +++ b/qpid/java/systests/etc/config-systests-derby.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + diff --git a/qpid/java/systests/etc/config-systests-settings.xml b/qpid/java/systests/etc/config-systests-settings.xml new file mode 100644 index 0000000000..4e9c863fda --- /dev/null +++ b/qpid/java/systests/etc/config-systests-settings.xml @@ -0,0 +1,29 @@ + + + + + false + + false + + + diff --git a/qpid/java/systests/etc/config-systests.xml b/qpid/java/systests/etc/config-systests.xml new file mode 100644 index 0000000000..290c082a4f --- /dev/null +++ b/qpid/java/systests/etc/config-systests.xml @@ -0,0 +1,29 @@ + + + + + + + + + + diff --git a/qpid/java/systests/etc/virtualhosts-ServerConfigurationTest-New.xml b/qpid/java/systests/etc/virtualhosts-ServerConfigurationTest-New.xml new file mode 100644 index 0000000000..168aa074da --- /dev/null +++ b/qpid/java/systests/etc/virtualhosts-ServerConfigurationTest-New.xml @@ -0,0 +1,40 @@ + + + + dev-only + + dev-only + + + amq.direct + + 102400 + 20480 + 60000 + + + dev-queue + + + + + diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java index b4ba6e8156..2e107ada34 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java @@ -20,26 +20,19 @@ */ package org.apache.qpid.management.jmx; -import org.apache.qpid.commands.objects.AllObjects; -import org.apache.qpid.management.common.JMXConnnectionFactory; import org.apache.qpid.management.common.mbeans.ManagedBroker; import org.apache.qpid.management.common.mbeans.ManagedConnection; import org.apache.qpid.management.common.mbeans.ManagedExchange; import org.apache.qpid.server.logging.AbstractTestLogging; import org.apache.qpid.server.logging.subjects.AbstractTestLogSubject; +import org.apache.qpid.test.utils.JMXTestUtils; import javax.jms.Connection; import javax.jms.ExceptionListener; import javax.jms.JMSException; import javax.management.JMException; -import javax.management.MBeanException; -import javax.management.MBeanServerConnection; -import javax.management.MBeanServerInvocationHandler; -import javax.management.ObjectName; -import javax.management.remote.JMXConnector; import java.io.IOException; import java.util.List; -import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -50,33 +43,21 @@ import java.util.concurrent.TimeUnit; */ public class ManagementActorLoggingTest extends AbstractTestLogging { - MBeanServerConnection _mbsc; - JMXConnector _jmxc; + private JMXTestUtils _jmxUtils; private static final String USER = "admin"; @Override public void setUp() throws Exception { - setConfigurationProperty("management.enabled", "true"); + _jmxUtils = new JMXTestUtils(this, USER, USER); + _jmxUtils.setUp(); super.setUp(); - - if (isExternalBroker()) - { - _jmxc = JMXConnnectionFactory.getJMXConnection( - 5000, "127.0.0.1", - getManagementPort(getPort()), USER, USER); - - _mbsc = _jmxc.getMBeanServerConnection(); - } } @Override public void tearDown() throws Exception { - if (isExternalBroker()) - { - _jmxc.close(); - } + _jmxUtils.close(); super.tearDown(); } @@ -107,34 +88,31 @@ public class ManagementActorLoggingTest extends AbstractTestLogging */ public void testJMXManagementConsoleConnection() throws IOException { - if (isExternalBroker()) - { - List results = _monitor.findMatches("MNG-1007"); + List results = _monitor.findMatches("MNG-1007"); - assertEquals("Unexpected Management Connection count", 1, results.size()); + assertEquals("Unexpected Management Connection count", 1, results.size()); - String log = getLog(results.get(0)); + String log = getLog(results.get(0)); - validateMessageID("MNG-1007", log); + validateMessageID("MNG-1007", log); - assertTrue("User not in log message:" + log, log.endsWith(USER)); - // Extract the id from the log string - // MESSAGE [mng:1(rmi://169.24.29.116)] MNG-1007 : Open : User admin - int connectionID = Integer.parseInt(fromActor(getLog(results.get(0))).charAt(4) + ""); + assertTrue("User not in log message:" + log, log.endsWith(USER)); + // Extract the id from the log string + // MESSAGE [mng:1(rmi://169.24.29.116)] MNG-1007 : Open : User admin + int connectionID = Integer.parseInt(fromActor(getLog(results.get(0))).charAt(4) + ""); - results = _monitor.findMatches("MNG-1008"); + results = _monitor.findMatches("MNG-1008"); - assertEquals("Unexpected Management Connection close count", 0, results.size()); + assertEquals("Unexpected Management Connection close count", 0, results.size()); - _jmxc.close(); + _jmxUtils.close(); - results = _monitor.findMatches("MNG-1008"); + results = _monitor.findMatches("MNG-1008"); - assertEquals("Unexpected Management Connection count", 1, results.size()); + assertEquals("Unexpected Management Connection count", 1, results.size()); - assertEquals("Close does not have same id as open,", connectionID, - Integer.parseInt(fromActor(getLog(results.get(0))).charAt(4) + "")); - } + assertEquals("Close does not have same id as open,", connectionID, + Integer.parseInt(fromActor(getLog(results.get(0))).charAt(4) + "")); } /** @@ -159,56 +137,40 @@ public class ManagementActorLoggingTest extends AbstractTestLogging */ public void testConnectionCloseViaManagement() throws IOException, Exception { - if (isExternalBroker()) + //Create a connection to the broker + Connection connection = getConnection(); + + // Monitor the connection for an exception being thrown + // this should be a DisconnectionException but it is not this tests + // job to valiate that. Only use the exception as a synchronisation + // to check the log file for the Close message + final CountDownLatch exceptionReceived = new CountDownLatch(1); + connection.setExceptionListener(new ExceptionListener() { - - //Create a connection to the broker - Connection connection = getConnection(); - - // Monitor the connection for an exception being thrown - // this should be a DisconnectionException but it is not this tests - // job to valiate that. Only use the exception as a synchronisation - // to check the log file for the Close message - final CountDownLatch exceptionReceived = new CountDownLatch(1); - connection.setExceptionListener(new ExceptionListener() + public void onException(JMSException e) { - public void onException(JMSException e) - { - //Failover being attempted. - exceptionReceived.countDown(); - } - }); - - //Remove the connection close from any 0-10 connections - _monitor.reset(); + //Failover being attempted. + exceptionReceived.countDown(); + } + }); - // Get all active AMQP connections - AllObjects allObject = new AllObjects(_mbsc); - allObject.querystring = "org.apache.qpid:type=VirtualHost.Connection,*"; + //Remove the connection close from any 0-10 connections + _monitor.reset(); - Set objectNames = allObject.returnObjects(); + // Get a managedConnection + ManagedConnection mangedConnection = _jmxUtils.getManagedObject(ManagedConnection.class, "org.apache.qpid:type=VirtualHost.Connection,*"); - assertEquals("More than one test connection returned", 1, objectNames.size()); + //Close the connection + mangedConnection.closeConnection(); - ObjectName connectionName = objectNames.iterator().next(); + //Wait for the connection to close + assertTrue("Timed out waiting for conneciton to report close", + exceptionReceived.await(2, TimeUnit.SECONDS)); - ManagedConnection mangedConnection = MBeanServerInvocationHandler. - newProxyInstance(_mbsc, connectionName, - ManagedConnection.class, false); + //Validate results + List results = _monitor.findMatches("CON-1002"); - - //Close the connection - mangedConnection.closeConnection(); - - //Wait for the connection to close - assertTrue("Timed out waiting for conneciton to report close", - exceptionReceived.await(2, TimeUnit.SECONDS)); - - //Validate results - List results = _monitor.findMatches("CON-1002"); - - assertEquals("Unexpected Connection Close count", 1, results.size()); - } + assertEquals("Unexpected Connection Close count", 1, results.size()); } /** @@ -234,114 +196,100 @@ public class ManagementActorLoggingTest extends AbstractTestLogging */ public void testCreateExchangeDirectTransientViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { - //Remove any previous exchange declares - _monitor.reset(); - - createExchange("direct"); + _monitor.reset(); - // Validate + _jmxUtils.createExchange("test", "direct", null, false); - //1 - ID is correct - List results = _monitor.findMatches("EXH-1001"); + // Validate - assertEquals("More than one exchange creation found", 1, results.size()); + //1 - ID is correct + List results = _monitor.findMatches("EXH-1001"); - String log = getLog(results.get(0)); + assertEquals("More than one exchange creation found", 1, results.size()); - // Validate correct exchange name - assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); + String log = getLog(results.get(0)); - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + // Validate correct exchange name + assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); - } + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); } public void testCreateExchangeTopicTransientViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { - //Remove any previous exchange declares - _monitor.reset(); + //Remove any previous exchange declares + _monitor.reset(); - createExchange("topic"); + _jmxUtils.createExchange("test", "topic", null, false); - // Validate + // Validate - //1 - ID is correct - List results = _monitor.findMatches("EXH-1001"); + //1 - ID is correct + List results = _monitor.findMatches("EXH-1001"); - assertEquals("More than one exchange creation found", 1, results.size()); + assertEquals("More than one exchange creation found", 1, results.size()); - String log = getLog(results.get(0)); + String log = getLog(results.get(0)); - // Validate correct exchange name - assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); + // Validate correct exchange name + assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); - } } public void testCreateExchangeFanoutTransientViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { - //Remove any previous exchange declares - _monitor.reset(); + //Remove any previous exchange declares + _monitor.reset(); - createExchange("fanout"); + _jmxUtils.createExchange("test", "fanout", null, false); - // Validate + // Validate - //1 - ID is correct - List results = _monitor.findMatches("EXH-1001"); + //1 - ID is correct + List results = _monitor.findMatches("EXH-1001"); - assertEquals("More than one exchange creation found", 1, results.size()); + assertEquals("More than one exchange creation found", 1, results.size()); - String log = getLog(results.get(0)); + String log = getLog(results.get(0)); - // Validate correct exchange name - assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); + // Validate correct exchange name + assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); - } } public void testCreateExchangeHeadersTransientViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { - //Remove any previous exchange declares - _monitor.reset(); + //Remove any previous exchange declares + _monitor.reset(); - createExchange("headers"); + _jmxUtils.createExchange("test", "headers", null, false); - // Validate + // Validate - //1 - ID is correct - List results = _monitor.findMatches("EXH-1001"); + //1 - ID is correct + List results = _monitor.findMatches("EXH-1001"); - assertEquals("More than one exchange creation found", 1, results.size()); + assertEquals("More than one exchange creation found", 1, results.size()); - String log = getLog(results.get(0)); + String log = getLog(results.get(0)); - // Validate correct exchange name - assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); + // Validate correct exchange name + assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); - } } /** @@ -365,29 +313,26 @@ public class ManagementActorLoggingTest extends AbstractTestLogging */ public void testCreateQueueTransientViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { - //Remove any previous queue declares - _monitor.reset(); + //Remove any previous queue declares + _monitor.reset(); - createQueue(); + _jmxUtils.createQueue("test", getName(), null, false); - // Validate + // Validate - List results = _monitor.findMatches("QUE-1001"); + List results = _monitor.findMatches("QUE-1001"); - assertEquals("More than one queue creation found", 1, results.size()); + assertEquals("More than one queue creation found", 1, results.size()); - String log = getLog(results.get(0)); + String log = getLog(results.get(0)); - // Validate correct queue name - String subject = fromSubject(log); - assertEquals("Incorrect queue name created", getName(), AbstractTestLogSubject.getSlice("qu", subject)); + // Validate correct queue name + String subject = fromSubject(log); + assertEquals("Incorrect queue name created", getName(), AbstractTestLogSubject.getSlice("qu", subject)); - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); - } + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); } /** @@ -411,34 +356,29 @@ public class ManagementActorLoggingTest extends AbstractTestLogging */ public void testQueueDeleteViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { - //Remove any previous queue declares - _monitor.reset(); + //Remove any previous queue declares + _monitor.reset(); - createQueue(); + _jmxUtils.createQueue("test", getName(), null, false); - ManagedBroker managedBroker = MBeanServerInvocationHandler. - newProxyInstance(_mbsc, getVirtualHostManagerObjectName(), - ManagedBroker.class, false); + ManagedBroker managedBroker = _jmxUtils.getManagedBroker("test"); - managedBroker.deleteQueue(getName()); + managedBroker.deleteQueue(getName()); - List results = _monitor.findMatches("QUE-1002"); + List results = _monitor.findMatches("QUE-1002"); - assertEquals("More than one queue deletion found", 1, results.size()); + assertEquals("More than one queue deletion found", 1, results.size()); - String log = getLog(results.get(0)); + String log = getLog(results.get(0)); - // Validate correct binding - String subject = fromSubject(log); - assertEquals("Incorrect queue named in delete", getName(), AbstractTestLogSubject.getSlice("qu", subject)); + // Validate correct binding + String subject = fromSubject(log); + assertEquals("Incorrect queue named in delete", getName(), AbstractTestLogSubject.getSlice("qu", subject)); - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); - } } /** @@ -462,98 +402,83 @@ public class ManagementActorLoggingTest extends AbstractTestLogging */ public void testBindingCreateOnDirectViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { - //Remove any previous queue declares - _monitor.reset(); + //Remove any previous queue declares + _monitor.reset(); - createQueue(); + _jmxUtils.createQueue("test", getName(), null, false); - ManagedExchange managedExchange = MBeanServerInvocationHandler. - newProxyInstance(_mbsc, getExchange("amq.direct"), - ManagedExchange.class, false); + ManagedExchange managedExchange = _jmxUtils.getManagedExchange("amq.direct"); - managedExchange.createNewBinding(getName(), getName()); + managedExchange.createNewBinding(getName(), getName()); - List results = _monitor.findMatches("BND-1001"); + List results = _monitor.findMatches("BND-1001"); - assertEquals("More than one bind creation found", 1, results.size()); + assertEquals("More than one bind creation found", 1, results.size()); - String log = getLog(results.get(0)); + String log = getLog(results.get(0)); - // Validate correct binding - String subject = fromSubject(log); - assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject)); - assertEquals("Incorrect routing key in create", getName(), AbstractTestLogSubject.getSlice("rk", subject)); + // Validate correct binding + String subject = fromSubject(log); + assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject)); + assertEquals("Incorrect routing key in create", getName(), AbstractTestLogSubject.getSlice("rk", subject)); - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); - } + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); } public void testBindingCreateOnTopicViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { - //Remove any previous queue declares - _monitor.reset(); + //Remove any previous queue declares + _monitor.reset(); - createQueue(); + _jmxUtils.createQueue("test", getName(), null, false); - ManagedExchange managedExchange = MBeanServerInvocationHandler. - newProxyInstance(_mbsc, getExchange("amq.topic"), - ManagedExchange.class, false); + ManagedExchange managedExchange = _jmxUtils.getManagedExchange("amq.topic"); - managedExchange.createNewBinding(getName(), getName()); + managedExchange.createNewBinding(getName(), getName()); - List results = _monitor.findMatches("BND-1001"); + List results = _monitor.findMatches("BND-1001"); - assertEquals("More than one bind creation found", 1, results.size()); + assertEquals("More than one bind creation found", 1, results.size()); - String log = getLog(results.get(0)); + String log = getLog(results.get(0)); - // Validate correct binding - String subject = fromSubject(log); - assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject)); - assertEquals("Incorrect routing key in create", getName(), AbstractTestLogSubject.getSlice("rk", subject)); + // Validate correct binding + String subject = fromSubject(log); + assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject)); + assertEquals("Incorrect routing key in create", getName(), AbstractTestLogSubject.getSlice("rk", subject)); - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); - } + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); } public void testBindingCreateOnFanoutViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { - //Remove any previous queue declares - _monitor.reset(); + //Remove any previous queue declares + _monitor.reset(); - createQueue(); + _jmxUtils.createQueue("test", getName(), null, false); - ManagedExchange managedExchange = MBeanServerInvocationHandler. - newProxyInstance(_mbsc, getExchange("amq.fanout"), - ManagedExchange.class, false); + ManagedExchange managedExchange = _jmxUtils.getManagedExchange("amq.fanout"); - managedExchange.createNewBinding(getName(), getName()); + managedExchange.createNewBinding(getName(), getName()); - List results = _monitor.findMatches("BND-1001"); + List results = _monitor.findMatches("BND-1001"); - assertEquals("More than one bind creation found", 1, results.size()); + assertEquals("More than one bind creation found", 1, results.size()); - String log = getLog(results.get(0)); + String log = getLog(results.get(0)); - // Validate correct binding - String subject = fromSubject(log); - assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject)); - assertEquals("Incorrect routing key in create", "*", AbstractTestLogSubject.getSlice("rk", subject)); + // Validate correct binding + String subject = fromSubject(log); + assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject)); + assertEquals("Incorrect routing key in create", "*", AbstractTestLogSubject.getSlice("rk", subject)); - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); - } + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); } /** @@ -578,114 +503,28 @@ public class ManagementActorLoggingTest extends AbstractTestLogging */ public void testUnRegisterExchangeViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { + //Remove any previous queue declares + _monitor.reset(); - //Remove any previous queue declares - _monitor.reset(); + _jmxUtils.createExchange("test", "direct", null, false); - createExchange("direct"); + ManagedBroker managedBroker = _jmxUtils.getManagedBroker("test"); - ManagedBroker managedBroker = MBeanServerInvocationHandler. - newProxyInstance(_mbsc, getVirtualHostManagerObjectName(), - ManagedBroker.class, false); + managedBroker.unregisterExchange(getName()); - managedBroker.unregisterExchange(getName()); + List results = _monitor.findMatches("EXH-1002"); - List results = _monitor.findMatches("EXH-1002"); - - assertEquals("More than one exchange deletion found", 1, results.size()); - - String log = getLog(results.get(0)); - - // Validate correct binding - String subject = fromSubject(log); - assertEquals("Incorrect exchange named in delete", "direct/" + getName(), AbstractTestLogSubject.getSlice("ex", subject)); - - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); - } - } - - /** - * Create a non-durable test exchange with the current test name - * - * @throws JMException - is thrown if a exchange with this testName already exists - * @throws IOException - if there is a problem with the JMX Connection - * @throws javax.management.MBeanException - * - if there is another problem creating the exchange - */ - private void createExchange(String type) - throws JMException, IOException, MBeanException - { - ManagedBroker managedBroker = MBeanServerInvocationHandler. - newProxyInstance(_mbsc, getVirtualHostManagerObjectName(), - ManagedBroker.class, false); - - managedBroker.createNewExchange(getName(), type, false); - } - - /** - * Create a non-durable queue (with no owner) that is named after the - * creating test. - * - * @throws JMException - is thrown if a queue with this testName already exists - * @throws IOException - if there is a problem with the JMX Connection - */ - private void createQueue() - throws JMException, IOException - { - ManagedBroker managedBroker = MBeanServerInvocationHandler. - newProxyInstance(_mbsc, getVirtualHostManagerObjectName(), - ManagedBroker.class, false); - - managedBroker.createNewQueue(getName(), null, false); - } - - /** - * Retrive the ObjectName for the test Virtualhost. - * - * This is then use to create aproxy to the ManagedBroker MBean. - * - * @return the ObjectName for the 'test' VirtualHost. - */ - private ObjectName getVirtualHostManagerObjectName() - { - // Get the name of the test manager - AllObjects allObject = new AllObjects(_mbsc); - allObject.querystring = "org.apache.qpid:type=VirtualHost.VirtualHostManager,VirtualHost=test,*"; - - Set objectNames = allObject.returnObjects(); - - assertEquals("Incorrect number test vhosts returned", 1, objectNames.size()); - - // We have verified we have only one value in objectNames so return it - return objectNames.iterator().next(); - } - - /** - * Retrive the ObjectName for the given Exchange on the test Virtualhost. - * - * This is then use to create aproxy to the ManagedExchange MBean. - * - * @param exchange The exchange to retireve e.g. 'direct' - * - * @return the ObjectName for the given exchange on the test VirtualHost. - */ - private ObjectName getExchange(String exchange) - { - // Get the name of the test manager - AllObjects allObject = new AllObjects(_mbsc); - allObject.querystring = "org.apache.qpid:type=VirtualHost.Exchange,VirtualHost=test,name=" + exchange + ",*"; + assertEquals("More than one exchange deletion found", 1, results.size()); - Set objectNames = allObject.returnObjects(); + String log = getLog(results.get(0)); - assertEquals("Incorrect number of exchange with name '" + exchange + - "' returned", 1, objectNames.size()); + // Validate correct binding + String subject = fromSubject(log); + assertEquals("Incorrect exchange named in delete", "direct/" + getName(), AbstractTestLogSubject.getSlice("ex", subject)); - // We have verified we have only one value in objectNames so return it - return objectNames.iterator().next(); + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/configuration/ServerConfigurationFileTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/configuration/ServerConfigurationFileTest.java index c4803e121e..0a88ef391c 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/configuration/ServerConfigurationFileTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/configuration/ServerConfigurationFileTest.java @@ -40,6 +40,8 @@ public class ServerConfigurationFileTest extends QpidTestCase { fail("Unable to test without config file:" + _configFile); } + + saveTestConfiguration(); _serverConfig = new ServerConfiguration(_configFile); } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AlertingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AlertingTest.java index 620b2a5161..683abee4da 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AlertingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AlertingTest.java @@ -94,7 +94,7 @@ public class AlertingTest extends AbstractTestLogging { _connection = getConnection(); _session = _connection.createSession(true, Session.SESSION_TRANSACTED); - _destination = _session.createQueue("testQueue"); + _destination = _session.createQueue(getTestQueueName()); // Consumer is only used to actually create the destination _session.createConsumer(_destination).close(); @@ -116,14 +116,12 @@ public class AlertingTest extends AbstractTestLogging // Add the current contents of the log file to test output message.append(_monitor.readFile()); - // Write the server config file to test output - message.append("Server configuration file in use:\n"); - message.append(FileUtils.readFileAsString(_configFile)); + // Write the test config file to test output + message.append("Server configuration overrides in use:\n"); + message.append(FileUtils.readFileAsString(getTestConfigFile())); - // Write the virtualhost config file to test output - message.append("\nVirtualhost configuration file in use:\n"); - message.append(FileUtils.readFileAsString(ServerConfiguration. - flatConfig(_configFile).getString("virtualhosts"))); + message.append("\nVirtualhost maxMessageCount:\n"); + message.append((new ServerConfiguration(_configFile)).getConfig().getString("virtualhosts.virtualhost." + VIRTUALHOST + ".queues.maximumMessageCount")); fail(message.toString()); } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DerbyMessageStoreLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DerbyMessageStoreLoggingTest.java index 254ec9693d..cc3993249c 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DerbyMessageStoreLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DerbyMessageStoreLoggingTest.java @@ -28,6 +28,7 @@ import javax.jms.Connection; import javax.jms.Queue; import javax.jms.Session; import java.util.List; +import java.io.File; /** * The MessageStore test suite validates that the follow log messages as @@ -56,9 +57,9 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest //We call super.setUp but this will not start the broker as that is //part of the test case. - // Load current configuration file to get the list of defined vhosts - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List vhosts = configuration.getList("virtualhosts.virtualhost.name"); + // Load the default configuration file to get the list of defined vhosts + ServerConfiguration configuration = new ServerConfiguration(new File(_configFile.getParent() + "/config.xml")); + List vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); // Make them all persistent i.e. Use DerbyMessageStore and // test that it logs correctly. @@ -97,8 +98,8 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest assertTrue("MST messages not logged", results.size() > 0); // Load VirtualHost list from file. - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("MST-1002"); @@ -117,7 +118,7 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest // the virtualhost name, found above. AND // the index that the virtualhost is within the configuration. // we can retrive that from the vhosts list previously extracted. - String fullStoreName = configuration.getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); + String fullStoreName = configuration.getConfig().getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); // Get the Simple class name from the expected class name of o.a.q.s.s.MMS String storeName = fullStoreName.substring(fullStoreName.lastIndexOf(".") + 1); @@ -160,8 +161,8 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest assertTrue("MST messages not logged", results.size() > 0); // Load VirtualHost list from file. - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("MST-1004"); @@ -186,7 +187,7 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest // the virtualhost name, found above. AND // the index that the virtualhost is within the configuration. // we can retrive that from the vhosts list previously extracted. - String fullStoreName = configuration.getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); + String fullStoreName = configuration.getConfig().getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); // Get the Simple class name from the expected class name of o.a.q.s.s.MMS String storeName = fullStoreName.substring(fullStoreName.lastIndexOf(".") + 1); @@ -227,8 +228,8 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest assertTrue("MST messages not logged", results.size() > 0); // Load VirtualHost list from file. - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("MST-1006"); @@ -253,7 +254,7 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest // the virtualhost name, found above. AND // the index that the virtualhost is within the configuration. // we can retrive that from the vhosts list previously extracted. - String fullStoreName = configuration.getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); + String fullStoreName = configuration.getConfig().getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); // Get the Simple class name from the expected class name of o.a.q.s.s.MMS String storeName = fullStoreName.substring(fullStoreName.lastIndexOf(".") + 1); @@ -293,8 +294,8 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest assertTrue("MST messages not logged", results.size() > 0); // Load VirtualHost list from file. - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("MST-1004 : Recovery Start :"); @@ -316,7 +317,7 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest // the virtualhost name, found above. AND // the index that the virtualhost is within the configuration. // we can retrive that from the vhosts list previously extracted. - String fullStoreName = configuration.getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); + String fullStoreName = configuration.getConfig().getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); // Get the Simple class name from the expected class name of o.a.q.s.s.MMS String storeName = fullStoreName.substring(fullStoreName.lastIndexOf(".") + 1); @@ -358,8 +359,8 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest assertTrue("MST messages not logged", results.size() > 0); // Load VirtualHost list from file. - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("MST-1006 : Recovery Complete :"); @@ -381,7 +382,7 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest // the virtualhost name, found above. AND // the index that the virtualhost is within the configuration. // we can retrive that from the vhosts list previously extracted. - String fullStoreName = configuration.getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); + String fullStoreName = configuration.getConfig().getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); // Get the Simple class name from the expected class name of o.a.q.s.s.MMS String storeName = fullStoreName.substring(fullStoreName.lastIndexOf(".") + 1); @@ -500,8 +501,8 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest assertTrue("MST messages not logged", results.size() > 0); // Load VirtualHost list from file. - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("MST-1004 : Recovery Start : " + queueName); @@ -542,7 +543,7 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest // the virtualhost name, found above. AND // the index that the virtualhost is within the configuration. // we can retrive that from the vhosts list previously extracted. - String fullStoreName = configuration.getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); + String fullStoreName = configuration.getConfig().getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); // Get the Simple class name from the expected class name of o.a.q.s.s.MMS String storeName = fullStoreName.substring(fullStoreName.lastIndexOf(".") + 1); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ManagementLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ManagementLoggingTest.java index 11c003a2a7..8b7c881a32 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ManagementLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ManagementLoggingTest.java @@ -24,6 +24,7 @@ import junit.framework.AssertionFailedError; import org.apache.qpid.util.LogMonitor; import java.util.List; +import java.io.File; /** * Management Console Test Suite @@ -308,9 +309,8 @@ public class ManagementLoggingTest extends AbstractTestLogging // We expect the RMIConnector Server port to be 100 higher than // the RMI Server Port - int mPort = getPort() + (DEFAULT_MANAGEMENT_PORT - DEFAULT_PORT) + 100; - assertTrue("SSL Keystore entry expected(" + mPort + ").:" + getMessageString(log), - getMessageString(log).endsWith(getConfigurationStringProperty("management.ssl.keyStorePath"))); + assertTrue("SSL Keystore entry expected.:" + getMessageString(log), + getMessageString(log).endsWith(new File(getConfigurationStringProperty("management.ssl.keyStorePath")).getName())); } catch (AssertionFailedError afe) { diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/MemoryMessageStoreLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/MemoryMessageStoreLoggingTest.java index a1cbeca6de..2298ba4da0 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/MemoryMessageStoreLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/MemoryMessageStoreLoggingTest.java @@ -89,8 +89,8 @@ public class MemoryMessageStoreLoggingTest extends AbstractTestLogging assertEquals("MST-1001 is not the first MST message", "MST-1001", getMessageID(fromMessage(log))); // Load VirtualHost list from file. - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("MST-1001"); @@ -109,7 +109,7 @@ public class MemoryMessageStoreLoggingTest extends AbstractTestLogging // the virtualhost name, found above. AND // the index that the virtualhost is within the configuration. // we can retrive that from the vhosts list previously extracted. - String fullStoreName = configuration.getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); + String fullStoreName = configuration.getConfig().getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); // Get the Simple class name from the expected class name of o.a.q.s.s.MMS String storeName = fullStoreName.substring(fullStoreName.lastIndexOf(".") + 1); @@ -156,8 +156,8 @@ public class MemoryMessageStoreLoggingTest extends AbstractTestLogging assertTrue("MST messages not logged", results.size() > 0); // Load VirtualHost list from file. - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("MST-1003"); @@ -176,7 +176,7 @@ public class MemoryMessageStoreLoggingTest extends AbstractTestLogging // the virtualhost name, found above. AND // the index that the virtualhost is within the configuration. // we can retrive that from the vhosts list previously extracted. - String fullStoreName = configuration.getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); + String fullStoreName = configuration.getConfig().getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); // Get the Simple class name from the expected class name of o.a.q.s.s.MMS String storeName = fullStoreName.substring(fullStoreName.lastIndexOf(".") + 1); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/VirtualHostLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/VirtualHostLoggingTest.java index 7bf644508e..f4a0c8b27d 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/VirtualHostLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/VirtualHostLoggingTest.java @@ -64,8 +64,8 @@ public class VirtualHostLoggingTest extends AbstractTestLogging try { // Validation - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("VHT-1001"); @@ -117,8 +117,8 @@ public class VirtualHostLoggingTest extends AbstractTestLogging { // Validation - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("VHT-1002"); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ModelTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ModelTest.java new file mode 100644 index 0000000000..078b8f43ce --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ModelTest.java @@ -0,0 +1,299 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.management.common.mbeans.ManagedBroker; +import org.apache.qpid.management.common.mbeans.ManagedQueue; +import org.apache.qpid.test.utils.JMXTestUtils; +import org.apache.qpid.test.utils.QpidTestCase; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Session; +import javax.management.JMException; +import javax.management.MBeanException; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.UndeclaredThrowableException; + +/** + * This Test validates the Queue Model on the broker. + * Currently it has some basic queue creation / deletion tests. + * However, it should be expanded to include other tests that relate to the + * model. i.e. + * + * The Create and Delete tests should ensure that the requisite logging is + * performed. + * + * Additions to this suite would be to complete testing of creations, validating + * fields such as owner/exclusive, autodelete and priority are correctly set. + * + * Currently this test uses the JMX interface to validate that the queue has + * been declared as expected so these tests cannot run against a CPP broker. + * + * + * Tests should ensure that they clean up after themselves. + * e,g. Durable queue creation test should perform a queue delete. + */ +public class ModelTest extends QpidTestCase +{ + + private static final String USER = "admin"; + private JMXTestUtils _jmxUtils; + private static final String VIRTUALHOST_NAME = "test"; + + @Override + public void setUp() throws Exception + { + // Create a JMX Helper + _jmxUtils = new JMXTestUtils(this, USER, USER); + _jmxUtils.setUp(); + super.setUp(); + + // Open the JMX Connection + _jmxUtils.open(); + } + + @Override + public void tearDown() throws Exception + { + // Close the JMX Connection + _jmxUtils.close(); + super.tearDown(); + } + + /** + * Test that a transient queue can be created via AMQP. + * + * @throws Exception On unexpected error + */ + public void testQueueCreationTransientViaAMQP() throws Exception + { + Connection connection = getConnection(); + + String queueName = getTestQueueName(); + boolean durable = false; + boolean autoDelete = false; + boolean exclusive = false; + + createViaAMQPandValidateViaJMX(connection, queueName, durable, + autoDelete, exclusive); + } + + /** + * Test that a durable queue can be created via AMQP. + * + * @throws Exception On unexpected error + */ + + public void testQueueCreationDurableViaAMQP() throws Exception + { + Connection connection = getConnection(); + + String queueName = getTestQueueName(); + boolean durable = true; + boolean autoDelete = false; + boolean exclusive = false; + + createViaAMQPandValidateViaJMX(connection, queueName, durable, + autoDelete, exclusive); + + // Clean up + ManagedBroker managedBroker = + _jmxUtils.getManagedBroker(VIRTUALHOST_NAME); + managedBroker.deleteQueue(queueName); + } + + /** + * Test that a transient queue can be created via JMX. + * + * @throws IOException if there is a problem via the JMX connection + * @throws javax.management.JMException if there is a problem with the JMX command + */ + public void testCreationTransientViaJMX() throws IOException, JMException + { + String name = getName(); + String owner = null; + boolean durable = false; + + createViaJMXandValidateViaJMX(name, owner, durable, durable); + } + + /** + * Test that a durable queue can be created via JMX. + * + * @throws IOException if there is a problem via the JMX connection + * @throws javax.management.JMException if there is a problem with the JMX command + */ + public void testCreationDurableViaJMX() throws IOException, JMException + { + String name = getName(); + String owner = null; + boolean durable = true; + + createViaJMXandValidateViaJMX(name, owner, durable, durable); + + // Clean up + ManagedBroker managedBroker = + _jmxUtils.getManagedBroker(VIRTUALHOST_NAME); + managedBroker.deleteQueue(name); + } + + /** + * Test that a transient queue can be deleted via JMX. + * + * @throws IOException if there is a problem via the JMX connection + * @throws javax.management.JMException if there is a problem with the JMX command + */ + public void testDeletionTransientViaJMX() throws IOException, JMException + { + String name = getName(); + + _jmxUtils.createQueue(VIRTUALHOST_NAME, name, null, false); + + ManagedBroker managedBroker = _jmxUtils. + getManagedBroker(VIRTUALHOST_NAME); + + try + { + managedBroker.deleteQueue(name); + } + catch (UndeclaredThrowableException e) + { + fail(((MBeanException) ((InvocationTargetException) + e.getUndeclaredThrowable()).getTargetException()).getTargetException().getMessage()); + } + } + + /** + * Test that a durable queue can be created via JMX. + * + * @throws IOException if there is a problem via the JMX connection + * @throws javax.management.JMException if there is a problem with the JMX command + */ + public void testDeletionDurableViaJMX() throws IOException, JMException + { + String name = getName(); + + _jmxUtils.createQueue(VIRTUALHOST_NAME, name, null, true); + + ManagedBroker managedBroker = _jmxUtils. + getManagedBroker(VIRTUALHOST_NAME); + + try + { + managedBroker.deleteQueue(name); + } + catch (UndeclaredThrowableException e) + { + fail(((MBeanException) ((InvocationTargetException) + e.getUndeclaredThrowable()).getTargetException()).getTargetException().getMessage()); + } + } + + /* + * Helper Methods + */ + + /** + * Using the provided JMS Connection create a queue using the AMQP extension + * with the given properties and then validate it was created correctly via + * the JMX Connection + * + * @param connection Qpid JMS Connection + * @param queueName String the desired QueueName + * @param durable boolean if the queue should be durable + * @param autoDelete boolean if the queue is an autoDelete queue + * @param exclusive boolean if the queue is exclusive + * + * @throws AMQException if there is a problem with the createQueue call + * @throws JMException if there is a problem with the JMX validatation + * @throws IOException if there is a problem with the JMX connection + * @throws JMSException if there is a problem creating the JMS Session + */ + private void createViaAMQPandValidateViaJMX(Connection connection, + String queueName, + boolean durable, + boolean autoDelete, + boolean exclusive) + throws AMQException, JMException, IOException, JMSException + { + AMQSession session = (AMQSession) connection.createSession(false, + Session.AUTO_ACKNOWLEDGE); + + session.createQueue(new AMQShortString(queueName), + autoDelete, durable, exclusive); + + validateQueueViaJMX(queueName, exclusive ? ((AMQConnection) connection). + getUsername() : null, durable, autoDelete); + } + + /** + * Use the JMX Helper to create a queue with the given properties and then + * validate it was created correctly via the JMX Connection + * + * @param queueName String the desired QueueName + * @param owner String the owner value that should be set + * @param durable boolean if the queue should be durable + * @param autoDelete boolean if the queue is an autoDelete queue + * + * @throws JMException if there is a problem with the JMX validatation + * @throws IOException if there is a problem with the JMX connection + */ + private void createViaJMXandValidateViaJMX(String queueName, String owner, + boolean durable, boolean autoDelete) + throws JMException, IOException + { + _jmxUtils.createQueue(VIRTUALHOST_NAME, queueName, owner, durable); + + validateQueueViaJMX(queueName, owner, durable, autoDelete); + } + + /** + * Validate that a queue with the given properties exists on the broker + * + * @param queueName String the desired QueueName + * @param owner String the owner value that should be set + * @param durable boolean if the queue should be durable + * @param autoDelete boolean if the queue is an autoDelete queue + * + * @throws JMException if there is a problem with the JMX validatation + * @throws IOException if there is a problem with the JMX connection + */ + private void validateQueueViaJMX(String queueName, String owner, boolean durable, boolean autoDelete) + throws JMException, IOException + { + ManagedQueue managedQueue = _jmxUtils. + getManagedObject(ManagedQueue.class, + _jmxUtils.getQueueObjectName(VIRTUALHOST_NAME, + queueName)); + + assertEquals(queueName, managedQueue.getName()); + assertEquals(String.valueOf(owner), managedQueue.getOwner()); + assertEquals(durable, managedQueue.isDurable()); + assertEquals(autoDelete, managedQueue.isAutoDelete()); + } + +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java index f402522a19..bb7b5efc75 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java @@ -21,6 +21,7 @@ package org.apache.qpid.server.security.acl; + import junit.framework.TestCase; import org.apache.log4j.BasicConfigurator; @@ -30,6 +31,7 @@ import org.apache.qpid.client.*; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; +import org.apache.qpid.AMQConnectionFailureException; import org.apache.qpid.AMQException; import org.apache.qpid.test.utils.QpidTestCase; import org.apache.qpid.jms.ConnectionListener; @@ -37,6 +39,7 @@ import org.apache.qpid.url.URLSyntaxException; import javax.jms.*; import javax.jms.IllegalStateException; +import javax.naming.NamingException; import java.io.File; import java.util.ArrayList; @@ -51,46 +54,30 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E public void setUp() throws Exception { - //Shutdown the QTC broker - stopBroker(); - - // Initialise ACLs. - final String QpidExampleHome = System.getProperty("QPID_EXAMPLE_HOME"); - final File defaultaclConfigFile = new File(QpidExampleHome, "etc/acl.config.xml"); - - if (!defaultaclConfigFile.exists()) - { - System.err.println("Configuration file not found:" + defaultaclConfigFile); - fail("Configuration file not found:" + defaultaclConfigFile); - } + final String QPID_HOME = System.getProperty("QPID_HOME"); - if (System.getProperty("QPID_HOME") == null) + if (QPID_HOME == null) { fail("QPID_HOME not set"); } - ConfigurationFileApplicationRegistry config = new ConfigurationFileApplicationRegistry(defaultaclConfigFile); - ApplicationRegistry.initialise(config, ApplicationRegistry.DEFAULT_INSTANCE); - TransportConnection.createVMBroker(ApplicationRegistry.DEFAULT_INSTANCE); - } + // Initialise ACLs. + _configFile = new File(QPID_HOME, "etc/config-systests-acl.xml"); - public void tearDown() - { - TransportConnection.killVMBroker(ApplicationRegistry.DEFAULT_INSTANCE); - ApplicationRegistry.remove(ApplicationRegistry.DEFAULT_INSTANCE); + super.setUp(); } - public String createConnectionString(String username, String password, String broker) + public String createConnectionString(String username, String password) { - return "amqp://" + username + ":" + password + "@clientid/test?brokerlist='" + broker + "?retries='0''"; + return "amqp://" + username + ":" + password + "@clientid/test?brokerlist='" + getBroker() + "?retries='0''"; } public void testAccessAuthorized() throws AMQException, URLSyntaxException { try { - Connection conn = createConnection("client", "guest"); + Connection conn = getConnection("client", "guest"); Session sesh = conn.createSession(true, Session.SESSION_TRANSACTED); @@ -103,28 +90,32 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E } catch (Exception e) { - fail("Connection was not created due to:" + e.getMessage()); + fail("Connection was not created due to:" + e); } } - public void testAccessNoRights() throws URLSyntaxException, JMSException + public void testAccessNoRights() throws Exception { try { - Connection conn = createConnection("guest", "guest"); + Connection conn = getConnection("guest", "guest"); //Attempt to do do things to test connection. Session sesh = conn.createSession(true, Session.SESSION_TRANSACTED); conn.start(); sesh.rollback(); - conn.close(); fail("Connection was created."); } - catch (AMQException amqe) + catch (JMSException jmse) { - Throwable cause = amqe.getCause(); - assertEquals("Exception was wrong type", AMQAuthenticationException.class, cause.getClass()); + Throwable linkedException = jmse.getLinkedException(); + assertNotNull("Cause was null", linkedException); + + assertEquals("Linked Exception was wrong type", AMQConnectionFailureException.class, linkedException.getClass()); + + Throwable cause = linkedException.getCause(); + assertEquals("Cause was wrong type", AMQAuthenticationException.class, cause.getClass()); assertEquals("Incorrect error code thrown", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode()); } } @@ -133,7 +124,7 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E { try { - Connection conn = createConnection("client", "guest"); + Connection conn = getConnection("client", "guest"); Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -149,11 +140,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E } } - public void testClientConsumeFromNamedQueueInvalid() throws AMQException, URLSyntaxException + public void testClientConsumeFromNamedQueueInvalid() throws NamingException, Exception { try { - Connection conn = createConnection("client", "guest"); + Connection conn = getConnection("client", "guest"); //Prevent Failover ((AMQConnection) conn).setConnectionListener(this); @@ -180,7 +171,7 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E { try { - Connection conn = createConnection("client", "guest"); + Connection conn = getConnection("client", "guest"); Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -198,11 +189,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E } } - public void testClientCreateNamedQueue() throws JMSException, URLSyntaxException, AMQException + public void testClientCreateNamedQueue() throws NamingException, JMSException, AMQException, Exception { try { - Connection conn = createConnection("client", "guest"); + Connection conn = getConnection("client", "guest"); Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -216,6 +207,7 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E } catch (AMQAuthenticationException amqe) { + amqe.printStackTrace(); assertEquals("Incorrect error code thrown", 403, ((AMQAuthenticationException) amqe).getErrorCode().getCode()); } } @@ -224,7 +216,7 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E { try { - Connection conn = createConnection("client", "guest"); + Connection conn = getConnection("client", "guest"); ((AMQConnection) conn).setConnectionListener(this); @@ -251,7 +243,7 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E { try { - Connection conn = createConnection("client", "guest"); + Connection conn = getConnection("client", "guest"); ((AMQConnection) conn).setConnectionListener(this); @@ -277,11 +269,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E } } - public void testClientPublishInvalidQueueSuccess() throws AMQException, URLSyntaxException, JMSException, InterruptedException + public void testClientPublishInvalidQueueSuccess() throws AMQException, URLSyntaxException, JMSException, NamingException, Exception { try { - Connection conn = createConnection("client", "guest"); + Connection conn = getConnection("client", "guest"); ((AMQConnection) conn).setConnectionListener(this); @@ -323,7 +315,7 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E { try { - Connection conn = createConnection("server", "guest"); + Connection conn = getConnection("server", "guest"); Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -339,11 +331,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E } } - public void testServerConsumeFromNamedQueueInvalid() throws AMQException, URLSyntaxException + public void testServerConsumeFromNamedQueueInvalid() throws AMQException, URLSyntaxException, NamingException, Exception { try - { - Connection conn = createConnection("client", "guest"); + { + Connection conn = getConnection("client", "guest"); Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -364,11 +356,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E } } - public void testServerConsumeFromTemporaryQueue() throws AMQException, URLSyntaxException + public void testServerConsumeFromTemporaryQueue() throws AMQException, URLSyntaxException, NamingException, Exception { try { - Connection conn = createConnection("server","guest"); + Connection conn = getConnection("server", "guest"); Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -388,30 +380,22 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E } } - private Connection createConnection(String username, String password) throws AMQException + @Override + public Connection getConnection(String username, String password) throws NamingException, JMSException { - AMQConnection connection = null; - try - { - connection = new AMQConnection(createConnectionString(username, password, BROKER)); - } - catch (URLSyntaxException e) - { - // This should never happen as we generate the URLs. - fail(e.getMessage()); - } + AMQConnection connection = (AMQConnection) super.getConnection(username, password); //Prevent Failover connection.setConnectionListener(this); - return (Connection)connection; + return (Connection) connection; } public void testServerCreateNamedQueueValid() throws JMSException, URLSyntaxException { try { - Connection conn = createConnection("server", "guest"); + Connection conn = getConnection("server", "guest"); Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -428,11 +412,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E } } - public void testServerCreateNamedQueueInvalid() throws JMSException, URLSyntaxException, AMQException + public void testServerCreateNamedQueueInvalid() throws JMSException, URLSyntaxException, AMQException, NamingException, Exception { try { - Connection conn = createConnection("server", "guest"); + Connection conn = getConnection("server", "guest"); Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -450,18 +434,18 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E } } - public void testServerCreateTemporaryQueueInvalid() throws JMSException, URLSyntaxException, AMQException + public void testServerCreateTemporaryQueueInvalid() throws NamingException, Exception { try { - Connection conn = createConnection("server", "guest"); + Connection conn = getConnection("server", "guest"); Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); session.createTemporaryQueue(); - + fail("Test failed as creation succeded."); //conn will be automatically closed } @@ -475,19 +459,19 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E } } - public void testServerCreateAutoDeleteQueueInvalid() throws JMSException, URLSyntaxException, AMQException + public void testServerCreateAutoDeleteQueueInvalid() throws NamingException, JMSException, AMQException, Exception { Connection connection = null; try { - connection = createConnection("server", "guest"); + connection = getConnection("server", "guest"); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); connection.start(); ((AMQSession) session).createQueue(new AMQShortString("again_ensure_auto_delete_queue_for_temporary"), - true, false, false); + true, false, false); fail("Test failed as creation succeded."); //connection will be automatically closed @@ -495,7 +479,7 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E catch (AMQAuthenticationException amqe) { assertEquals("Incorrect error code thrown", 403, amqe.getErrorCode().getCode()); - } + } } /** @@ -506,10 +490,10 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E * @throws URLSyntaxException * @throws JMSException */ - public void testServerPublishUsingTransactionSuccess() throws AMQException, URLSyntaxException, JMSException + public void testServerPublishUsingTransactionSuccess() throws AMQException, URLSyntaxException, JMSException, NamingException, Exception { //Set up the Server - Connection serverConnection = createConnection("server", "guest"); + Connection serverConnection = getConnection("server", "guest"); ((AMQConnection) serverConnection).setConnectionListener(this); @@ -522,7 +506,7 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E serverConnection.start(); //Set up the consumer - Connection clientConnection = createConnection("client", "guest"); + Connection clientConnection = getConnection("client", "guest"); //Send a test mesage Session clientSession = clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -563,8 +547,6 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E //Send the message using a transaction as this will allow us to retrieve any errors that occur on the broker. serverSession.commit(); - - //Ensure Response is received. Message clientResponseMsg = clientResponse.receive(2000); assertNotNull("Client did not receive response message,", clientResponseMsg); @@ -588,11 +570,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E } } - public void testServerPublishInvalidQueueSuccess() throws AMQException, URLSyntaxException, JMSException + public void testServerPublishInvalidQueueSuccess() throws AMQException, URLSyntaxException, JMSException, NamingException, Exception { try { - Connection conn = createConnection("server", "guest"); + Connection conn = getConnection("server", "guest"); ((AMQConnection) conn).setConnectionListener(this); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/timeouts/SyncWaitDelayTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/timeouts/SyncWaitDelayTest.java index b13170efc9..a123fb290c 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/timeouts/SyncWaitDelayTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/timeouts/SyncWaitDelayTest.java @@ -57,26 +57,13 @@ public class SyncWaitDelayTest extends QpidTestCase public void setUp() throws Exception { - super.setUp(); - stopBroker(); - if (!_configFile.exists()) - { - fail("Unable to test without config file:" + _configFile); - } - XMLConfiguration configuration = new XMLConfiguration(_configFile); - configuration.setProperty("virtualhosts.virtualhost." + VIRTUALHOST+".store.class", "org.apache.qpid.server.store.SlowMessageStore"); - configuration.setProperty("virtualhosts.virtualhost." + VIRTUALHOST+".store.delays.commitTran.post", POST_COMMIT_DELAY); - configuration.setProperty("management.enabled", "false"); + setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST+".store.class", "org.apache.qpid.server.store.SlowMessageStore"); + setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST+".store.delays.commitTran.post", String.valueOf(POST_COMMIT_DELAY)); + setConfigurationProperty("management.enabled", "false"); - File tmpFile = File.createTempFile("configFile", "test"); - tmpFile.deleteOnExit(); - configuration.save(tmpFile); - - _configFile = tmpFile; - - startBroker(1); + super.setUp(); //Set the syncWrite timeout to be just larger than the delay on the commitTran. setSystemProperty("amqj.default_syncwrite_timeout", String.valueOf(SYNC_WRITE_TIMEOUT)); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/FailoverBaseCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/FailoverBaseCase.java index 1bef07fcd5..64bd1503ba 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/FailoverBaseCase.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/FailoverBaseCase.java @@ -21,6 +21,8 @@ package org.apache.qpid.test.utils; import javax.jms.Connection; +import javax.jms.JMSException; +import javax.naming.NamingException; import org.apache.qpid.util.FileUtils; @@ -64,7 +66,7 @@ public class FailoverBaseCase extends QpidTestCase * @return a connection * @throws Exception */ - public Connection getConnection() throws Exception + public Connection getConnection() throws JMSException, NamingException { Connection conn = (Boolean.getBoolean("profile.use_ssl"))? diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java new file mode 100644 index 0000000000..3f8cdb9c25 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java @@ -0,0 +1,208 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.utils; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.commands.objects.AllObjects; +import org.apache.qpid.management.common.JMXConnnectionFactory; +import org.apache.qpid.management.common.mbeans.ManagedBroker; +import org.apache.qpid.management.common.mbeans.ManagedExchange; + +import javax.management.JMException; +import javax.management.MBeanException; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerInvocationHandler; +import javax.management.ObjectName; +import javax.management.remote.JMXConnector; +import java.io.IOException; +import java.util.Set; + +/** + * + */ +public class JMXTestUtils +{ + QpidTestCase _test; + MBeanServerConnection _mbsc; + JMXConnector _jmxc; + + private String USER; + private String PASSWORD; + + public JMXTestUtils(QpidTestCase test, String user, String password) + { + _test = test; + USER = user; + PASSWORD = password; + } + + public void setUp() throws IOException, ConfigurationException, Exception + { + _test.setConfigurationProperty("management.enabled", "true"); + } + + public void open() throws Exception + { + _jmxc = JMXConnnectionFactory.getJMXConnection( + 5000, "127.0.0.1", + _test.getManagementPort(_test.getPort()), USER, PASSWORD); + + _mbsc = _jmxc.getMBeanServerConnection(); + } + + public void close() throws IOException + { + _jmxc.close(); + } + + /** + * Create a non-durable test exchange with the current test name + * + * @throws javax.management.JMException - is thrown if a exchange with this testName already exists + * @throws java.io.IOException - if there is a problem with the JMX Connection + * @throws javax.management.MBeanException + * - if there is another problem creating the exchange + */ + public void createExchange(String virtualHostName, String name, String type, boolean durable) + throws JMException, IOException, MBeanException + { + ManagedBroker managedBroker = getManagedBroker(virtualHostName); + + managedBroker.createNewExchange(name, type, durable); + } + + /** + * Create a non-durable queue (with no owner) that is named after the + * creating test. + * + * @throws JMException - is thrown if a queue with this testName already exists + * @throws IOException - if there is a problem with the JMX Connection + */ + public void createQueue(String virtualHostName, String name, String owner, boolean durable) + throws JMException, IOException + { + ManagedBroker managedBroker = getManagedBroker(virtualHostName); + + managedBroker.createNewQueue(name, owner, durable); + } + + /** + * Retrive the ObjectName for the test Virtualhost. + * + * This is then use to create aproxy to the ManagedBroker MBean. + * + * @return the ObjectName for the 'test' VirtualHost. + */ + public ObjectName getVirtualHostManagerObjectName(String vhostName) + { + // Get the name of the test manager + AllObjects allObject = new AllObjects(_mbsc); + allObject.querystring = "org.apache.qpid:type=VirtualHost.VirtualHostManager,VirtualHost=" + vhostName + ",*"; + + Set objectNames = allObject.returnObjects(); + + _test.assertEquals("Incorrect number test vhosts returned", 1, objectNames.size()); + + // We have verified we have only one value in objectNames so return it + return objectNames.iterator().next(); + } + + /** + * Retrive the ObjectName for the given Exchange on the test Virtualhost. + * + * This is then use to create aproxy to the ManagedExchange MBean. + * + * @param queue The exchange to retireve e.g. 'direct' + * + * @return the ObjectName for the given exchange on the test VirtualHost. + */ + public ObjectName getQueueObjectName(String virtualHostName, String queue) + { + // Get the name of the test manager + AllObjects allObject = new AllObjects(_mbsc); + allObject.querystring = "org.apache.qpid:type=VirtualHost.Queue,VirtualHost=" + virtualHostName + ",name=" + queue + ",*"; + + Set objectNames = allObject.returnObjects(); + + _test.assertEquals("Incorrect number of exchange with name '" + queue + + "' returned", 1, objectNames.size()); + + // We have verified we have only one value in objectNames so return it + return objectNames.iterator().next(); + } + + /** + * Retrive the ObjectName for the given Exchange on the test Virtualhost. + * + * This is then use to create aproxy to the ManagedExchange MBean. + * + * @param virtualHostName + * @param exchange The exchange to retireve e.g. 'direct' + * + * @return the ObjectName for the given exchange on the test VirtualHost. + */ + public ObjectName getExchangeObjectName(String virtualHostName, String exchange) + { + // Get the name of the test manager + AllObjects allObject = new AllObjects(_mbsc); + allObject.querystring = "org.apache.qpid:type=VirtualHost.Exchange,VirtualHost=" + virtualHostName + ",name=" + exchange + ",*"; + + Set objectNames = allObject.returnObjects(); + + _test.assertEquals("Incorrect number of exchange with name '" + exchange + + "' returned", 1, objectNames.size()); + + // We have verified we have only one value in objectNames so return it + return objectNames.iterator().next(); + } + + public T getManagedObject(Class managedClass, String queryString) + { + AllObjects allObject = new AllObjects(_mbsc); + allObject.querystring = queryString; + + Set objectNames = allObject.returnObjects(); + + _test.assertEquals("More than one " + managedClass + " returned", 1, objectNames.size()); + + ObjectName objectName = objectNames.iterator().next(); + + return getManagedObject(managedClass, objectName); + } + + public T getManagedObject(Class managedClass, ObjectName objectName) + { + return MBeanServerInvocationHandler. + newProxyInstance(_mbsc, objectName, managedClass, false); + } + + public ManagedBroker getManagedBroker(String virtualHost) + { + return getManagedObject(ManagedBroker.class, getVirtualHostManagerObjectName(virtualHost).toString()); + } + + public ManagedExchange getManagedExchange(String exchangeName) + { + return MBeanServerInvocationHandler. + newProxyInstance(_mbsc, getExchangeObjectName("test", exchangeName), + ManagedExchange.class, false); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java index db096710dc..76e4118c96 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java @@ -21,6 +21,7 @@ import junit.framework.TestCase; import junit.framework.TestResult; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.XMLConfiguration; +import org.apache.qpid.AMQException; import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQConnectionFactory; import org.apache.qpid.client.transport.TransportConnection; @@ -30,6 +31,7 @@ import org.apache.qpid.server.configuration.ServerConfiguration; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; import org.apache.qpid.server.store.DerbyMessageStore; +import org.apache.qpid.url.URLSyntaxException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -73,6 +75,7 @@ public class QpidTestCase extends TestCase protected long RECEIVE_TIMEOUT = 1000l; private Map _setProperties = new HashMap(); + private XMLConfiguration _testConfiguration = new XMLConfiguration(); /** * Some tests are excluded when the property test.excludes is set to true. @@ -183,8 +186,7 @@ public class QpidTestCase extends TestCase public static final String QUEUE = "queue"; public static final String TOPIC = "topic"; /** Map to hold test defined environment properties */ - private Map _env; - + private Map _env; public QpidTestCase(String name) { @@ -335,7 +337,7 @@ public class QpidTestCase extends TestCase latch.countDown(); } - if (latch != null && line.contains(stopped)) + if (!seenReady && line.contains(stopped)) { stopLine = line; } @@ -368,7 +370,9 @@ public class QpidTestCase extends TestCase /** * Return the management portin use by the broker on this main port + * * @param mainPort the broker's main port. + * * @return the management port that corresponds to the broker on the given port */ protected int getManagementPort(int mainPort) @@ -415,9 +419,14 @@ public class QpidTestCase extends TestCase { port = getPort(port); + // Save any configuratio changes that have been made + saveTestConfiguration(); + Process process = null; if (_broker.equals(VM)) { + setConfigurationProperty("management.jmxport", String.valueOf(getManagementPort(port))); + saveTestConfiguration(); // create an in_VM broker ApplicationRegistry.initialise(new ConfigurationFileApplicationRegistry(_configFile), port); TransportConnection.createVMBroker(port); @@ -438,15 +447,35 @@ public class QpidTestCase extends TestCase env.put("PATH", env.get("PATH").concat(File.pathSeparator + qpidHome + "/bin")); //Add the test name to the broker run. - env.put("QPID_PNAME", "-DPNAME=\"" + _testName + "\""); + // DON'T change PNAME, qpid.stop needs this value. + env.put("QPID_PNAME", "-DPNAME=QPBRKR -DTNAME=\"" + _testName + "\""); env.put("QPID_WORK", System.getProperty("QPID_WORK")); // Add all the environment settings the test requested if (!_env.isEmpty()) { - for(Map.Entry entry : _env.entrySet()) + for (Map.Entry entry : _env.entrySet()) + { + env.put(entry.getKey(), entry.getValue()); + } + } + + String QPID_OPTS = " "; + // Add all the specified system properties to QPID_OPTS + if (!_setProperties.isEmpty()) + { + for (String key : _setProperties.keySet()) + { + QPID_OPTS += "-D" + key + "=" + System.getProperty(key) + " "; + } + + if (env.containsKey("QPID_OPTS")) { - env.put(entry.getKey() ,entry.getValue()); + env.put("QPID_OPTS", env.get("QPID_OPTS") + QPID_OPTS); + } + else + { + env.put("QPID_OPTS", QPID_OPTS); } } @@ -484,6 +513,27 @@ public class QpidTestCase extends TestCase _brokers.put(port, process); } + public String getTestConfigFile() + { + String path = _output == null ? System.getProperty("java.io.tmpdir") : _output; + return path + "/" + getTestQueueName() + ".xml"; + } + + protected void saveTestConfiguration() throws ConfigurationException + { + String testConfig = getTestConfigFile(); + //Specifiy the test configuration + setSystemProperty("test.config", testConfig); + + // This is a work + if (_testConfiguration.isEmpty()) + { + _testConfiguration.addProperty("test", getTestQueueName()); + } + + _testConfiguration.save(getTestConfigFile()); + } + public void cleanBroker() { if (_brokerClean != null) @@ -565,18 +615,12 @@ public class QpidTestCase extends TestCase storeClass = bdb; } - // First we munge the config file and, if we're in a VM, set up an additional logfile - XMLConfiguration configuration = new XMLConfiguration(_configFile); - configuration.setProperty("virtualhosts.virtualhost." + virtualhost + + + _testConfiguration.setProperty("virtualhosts.virtualhost." + virtualhost + ".store.class", storeClass.getName()); - configuration.setProperty("virtualhosts.virtualhost." + virtualhost + + _testConfiguration.setProperty("virtualhosts.virtualhost." + virtualhost + ".store." + DerbyMessageStore.ENVIRONMENT_PATH_PROPERTY, - "${work}/" + virtualhost); - - File tmpFile = File.createTempFile("configFile", "test"); - tmpFile.deleteOnExit(); - configuration.save(tmpFile); - _configFile = tmpFile; + "${QPID_WORK}/" + virtualhost); } /** @@ -591,6 +635,10 @@ public class QpidTestCase extends TestCase */ protected String getConfigurationStringProperty(String property) throws ConfigurationException { + // Call save Configuration to be sure we have saved the test specific + // file. As the optional status + saveTestConfiguration(); + ServerConfiguration configuration = new ServerConfiguration(_configFile); return configuration.getConfig().getString(property); } @@ -613,48 +661,9 @@ public class QpidTestCase extends TestCase protected void setConfigurationProperty(String property, String value) throws ConfigurationException, IOException { - XMLConfiguration configuration = new XMLConfiguration(_configFile); - - // If we are modifying a virtualhost value then we need to do so in - // the virtualhost.xml file as these values overwrite the values in - // the main config.xml file - if (property.startsWith("virtualhosts")) - { - // So locate the virtualhost.xml file and use the ServerConfiguration - // flatConfig method to get the interpolated value. - String vhostConfigFile = ServerConfiguration. - flatConfig(_configFile).getString("virtualhosts"); - - // Load the vhostConfigFile - XMLConfiguration vhostConfiguration = new XMLConfiguration(vhostConfigFile); - - // Set the value specified in to the vhostConfig. - // Remembering that property will be 'virtualhosts.virtulhost....' - // so we need to take off the 'virtualhosts.' from the start. - vhostConfiguration.setProperty(property.substring(property.indexOf(".") + 1), value); - - // Write out the new virtualhost config file - File tmpFile = File.createTempFile("virtualhost-configFile", ".xml"); - tmpFile.deleteOnExit(); - vhostConfiguration.save(tmpFile); - - // Change the property and value to be the new virtualhosts file - // so that then update the value in the main config file. - property = "virtualhosts"; - value = tmpFile.getAbsolutePath(); - } - - configuration.setProperty(property, value); - - // Write the new server config file - File tmpFile = File.createTempFile("configFile", ".xml"); - tmpFile.deleteOnExit(); - configuration.save(tmpFile); - - _logger.info("Qpid Test Case now using configuration File:" - + tmpFile.getAbsolutePath()); - - _configFile = tmpFile; + //Write the value in to this configuration file which will override the + // defaults. + _testConfiguration.setProperty(property, value); } /** @@ -695,14 +704,13 @@ public class QpidTestCase extends TestCase * Add an environtmen variable for the external broker environment * * @param property the property to set - * @param value the value to set it to + * @param value the value to set it to */ protected void setBrokerEnvironment(String property, String value) { - _env.put(property,value); + _env.put(property, value); } - /** * Check whether the broker is an 0.8 * @@ -720,7 +728,7 @@ public class QpidTestCase extends TestCase protected boolean isJavaBroker() { - return _brokerLanguage.equals("java"); + return _brokerLanguage.equals("java") || _broker.equals("vm"); } protected boolean isCppBroker() @@ -807,7 +815,7 @@ public class QpidTestCase extends TestCase return (AMQConnectionFactory) getInitialContext().lookup(factoryName); } - public Connection getConnection() throws Exception + public Connection getConnection() throws JMSException, NamingException { return getConnection("guest", "guest"); } @@ -831,7 +839,7 @@ public class QpidTestCase extends TestCase * * @throws Exception if there is an error getting the connection */ - public Connection getConnection(String username, String password) throws Exception + public Connection getConnection(String username, String password) throws JMSException, NamingException { _logger.info("get Connection"); Connection con = getConnectionFactory().createConnection(username, password); @@ -840,7 +848,7 @@ public class QpidTestCase extends TestCase return con; } - public Connection getConnection(String username, String password, String id) throws Exception + public Connection getConnection(String username, String password, String id) throws JMSException, URLSyntaxException, AMQException, NamingException { _logger.info("get Connection"); Connection con; @@ -860,6 +868,7 @@ public class QpidTestCase extends TestCase /** * Return a uniqueName for this test. * In this case it returns a queue Named by the TestCase and TestName + * * @return String name for a queue */ protected String getTestQueueName() diff --git a/qpid/java/test-profiles/010Excludes b/qpid/java/test-profiles/010Excludes index 36e7317e31..f03d62667d 100644 --- a/qpid/java/test-profiles/010Excludes +++ b/qpid/java/test-profiles/010Excludes @@ -83,6 +83,9 @@ org.apache.qpid.server.logging.* // CPP Broker does not have a JMX interface to test org.apache.qpid.management.jmx.* +// JMX is used in this test for validation +org.apache.qpid.server.queue.ModelTest#* + // 0-10 is not supported by the MethodRegistry org.apache.qpid.test.unit.close.JavaServerCloseRaceConditionTest#* diff --git a/qpid/java/test-profiles/08Excludes b/qpid/java/test-profiles/08Excludes index 0866694854..b277c6d929 100644 --- a/qpid/java/test-profiles/08Excludes +++ b/qpid/java/test-profiles/08Excludes @@ -15,3 +15,8 @@ org.apache.qpid.server.persistent.NoLocalAfterRecoveryTest#* org.apache.qpid.client.SessionCreateTest#* org.apache.qpid.test.client.RollbackOrderTest#* + +// QPID-2097 exclude it from the InVM test runs until InVM JMX Interface is reliable +org.apache.qpid.management.jmx.ManagementActorLoggingTest#* +org.apache.qpid.server.queue.ModelTest#* + diff --git a/qpid/java/test-profiles/Excludes b/qpid/java/test-profiles/Excludes index 7ef2a15e51..a72d3bc86c 100644 --- a/qpid/java/test-profiles/Excludes +++ b/qpid/java/test-profiles/Excludes @@ -13,5 +13,9 @@ org.apache.qpid.server.logging.BrokerLoggingTest#testBrokerShutdownListeningTCPS org.apache.qpid.server.logging.BrokerLoggingTest#testBrokerShutdownStopped org.apache.qpid.server.logging.VirtualHostLoggingTest#testVirtualhostClosure org.apache.qpid.server.logging.MemoryMessageStoreLoggingTest#testMessageStoreClose -org.apache.qpid.server.logging.DerbyMessageStoreLoggingTest#testMessageStoreClose +// QPID-XXX : Test fails to start external broker due to Derby Exception. +org.apache.qpid.server.logging.DerbyMessageStoreLoggingTest#* + +// QPID-2081 :The configuration changes are now highlighting the close race condition +org.apache.qpid.server.security.acl.SimpleACLTest#* diff --git a/qpid/java/test-profiles/cpp.testprofile b/qpid/java/test-profiles/cpp.testprofile index 1d5416fe19..b3b979c786 100644 --- a/qpid/java/test-profiles/cpp.testprofile +++ b/qpid/java/test-profiles/cpp.testprofile @@ -8,6 +8,7 @@ broker.executable=${broker.dir}/qpidd broker.module.ssl=${module.dir}/ssl.so broker.module.cluster=${module.dir}/cluster.so broker.module.store=${store.module.dir}/msgstore.so +broker.stopped=Exception constructed broker.modules= broker.args= diff --git a/qpid/java/test-profiles/default.testprofile b/qpid/java/test-profiles/default.testprofile index 49d4a25b82..86a5b2efb3 100644 --- a/qpid/java/test-profiles/default.testprofile +++ b/qpid/java/test-profiles/default.testprofile @@ -18,9 +18,10 @@ log4j.debug=false test.port=15672 test.mport=18999 +#Note : Management will start open second port on: mport + 100 : 19099 test.port.ssl=15671 -test.port.alt=15772 -test.port.alt.ssl=15771 +test.port.alt=25672 +test.port.alt.ssl=25671 test.exclude=true profile.excludes=08TransientExcludes diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index f832ddae34..61643715e4 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -234,6 +234,24 @@ class RangedSet: def add(self, lower, upper = None): self.add_range(Range(lower, upper)) + def empty(self): + for r in self.ranges: + if r.lower <= r.upper: + return False + return True + + def max(self): + if self.ranges: + return self.ranges[-1].upper + else: + return None + + def min(self): + if self.ranges: + return self.ranges[0].lower + else: + return None + def __iter__(self): return iter(self.ranges) diff --git a/qpid/python/qpid/delegates.py b/qpid/python/qpid/delegates.py index c74cc5a945..14111a88df 100644 --- a/qpid/python/qpid/delegates.py +++ b/qpid/python/qpid/delegates.py @@ -139,12 +139,18 @@ class Server(Delegate): class Client(Delegate): + ppid = 0 + try: + ppid = os.getppid() + except: + pass + PROPERTIES = {"product": "qpid python client", "version": "development", "platform": os.name, "qpid.client_process": os.path.basename(sys.argv[0]), "qpid.client_pid": os.getpid(), - "qpid.client_ppid": os.getppid()} + "qpid.client_ppid": ppid} def __init__(self, connection, username="guest", password="guest", mechanism="PLAIN", heartbeat=None): diff --git a/qpid/python/qpid/ops.py b/qpid/python/qpid/ops.py index 447f9953df..11e7d11fe9 100644 --- a/qpid/python/qpid/ops.py +++ b/qpid/python/qpid/ops.py @@ -74,10 +74,7 @@ class Compound(object): def dispatch(self, target, *args): handler = "do_%s" % self.NAME - if hasattr(target, handler): - getattr(target, handler)(self, *args) - else: - print "UNHANDLED:", target, args + getattr(target, handler)(self, *args) def __repr__(self, extras=()): return "%s(%s)" % (self.__class__.__name__, diff --git a/qpid/python/tests/datatypes.py b/qpid/python/tests/datatypes.py index b00e5e78f8..00e649d6cf 100644 --- a/qpid/python/tests/datatypes.py +++ b/qpid/python/tests/datatypes.py @@ -148,6 +148,34 @@ class RangedSetTest(TestCase): assert range.lower == 0 assert range.upper == 8 + def testEmpty(self): + s = RangedSet() + assert s.empty() + s.add(0, -1) + assert s.empty() + s.add(0, 0) + assert not s.empty() + + def testMinMax(self): + s = RangedSet() + assert s.max() is None + assert s.min() is None + s.add(0, 10) + assert s.max() == 10 + assert s.min() == 0 + s.add(0, 5) + assert s.max() == 10 + assert s.min() == 0 + s.add(0, 11) + assert s.max() == 11 + assert s.min() == 0 + s.add(15, 20) + assert s.max() == 20 + assert s.min() == 0 + s.add(-10, -5) + assert s.max() == 20 + assert s.min() == -10 + class RangeTest(TestCase): def testIntersect1(self): diff --git a/qpid/specs/management-schema.xml b/qpid/specs/management-schema.xml index e72ba1cdd7..c25aca67ed 100644 --- a/qpid/specs/management-schema.xml +++ b/qpid/specs/management-schema.xml @@ -169,7 +169,7 @@ - + -- cgit v1.2.1 From 2296769193754d1bc09e1dc3b998709a5808ecbb Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Fri, 18 Sep 2009 12:54:23 +0000 Subject: QPID-2104 AMQProtocolHandler: hand the actual write off to a seperate thread git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-network-refactor@816612 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/qpid/client/protocol/AMQProtocolHandler.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'qpid') diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java index be75fc150f..06a1fe2696 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java @@ -564,9 +564,16 @@ public class AMQProtocolHandler implements ProtocolEngine public void writeFrame(AMQDataBlock frame, boolean wait) { - ByteBuffer buf = frame.toNioByteBuffer(); + final ByteBuffer buf = frame.toNioByteBuffer(); _writtenBytes += buf.remaining(); - _networkDriver.send(buf); + Job.fireAsynchEvent(_poolReference.getPool(), _writeJob, new Runnable() + { + @Override + public void run() + { + _networkDriver.send(buf); + } + }); if (PROTOCOL_DEBUG) { _protocolLogger.debug(String.format("SEND: [%s] %s", this, frame)); -- cgit v1.2.1 From 555d38fcb330e544a01a2c0a2d9e0d94d4560b2d Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Fri, 18 Sep 2009 12:55:10 +0000 Subject: MINANetworkDriverTest: Make sure we sleep for at least the alloted time. git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-network-refactor@816613 13f79535-47bb-0310-9956-ffa450edef68 --- .../network/mina/MINANetworkDriverTest.java | 36 ++++++++++++---------- 1 file changed, 20 insertions(+), 16 deletions(-) (limited to 'qpid') diff --git a/qpid/java/common/src/test/java/org/apache/qpid/transport/network/mina/MINANetworkDriverTest.java b/qpid/java/common/src/test/java/org/apache/qpid/transport/network/mina/MINANetworkDriverTest.java index 5500ff9d4b..5af07d9735 100644 --- a/qpid/java/common/src/test/java/org/apache/qpid/transport/network/mina/MINANetworkDriverTest.java +++ b/qpid/java/common/src/test/java/org/apache/qpid/transport/network/mina/MINANetworkDriverTest.java @@ -186,14 +186,7 @@ public class MINANetworkDriverTest extends TestCase _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); assertFalse("Reader should not have been idle", _countingEngine.getReaderHasBeenIdle()); _client.setMaxReadIdle(1); - try - { - Thread.sleep(1000); - } - catch (InterruptedException e) - { - // Eat it - } + sleepForAtLeast(1500); assertTrue("Reader should have been idle", _countingEngine.getReaderHasBeenIdle()); } @@ -211,14 +204,7 @@ public class MINANetworkDriverTest extends TestCase _client.open(TEST_PORT, InetAddress.getLocalHost(), _countingEngine, null, null); assertFalse("Reader should not have been idle", _countingEngine.getWriterHasBeenIdle()); _client.setMaxWriteIdle(1); - try - { - Thread.sleep(1000); - } - catch (InterruptedException e) - { - // Eat it - } + sleepForAtLeast(1500); assertTrue("Reader should have been idle", _countingEngine.getWriterHasBeenIdle()); } @@ -484,4 +470,22 @@ public class MINANetworkDriverTest extends TestCase _driver.send(msg); } } + + public static void sleepForAtLeast(long period) + { + long start = System.currentTimeMillis(); + long timeLeft = period; + while (timeLeft > 0) + { + try + { + Thread.sleep(timeLeft); + } + catch (InterruptedException e) + { + // Ignore it + } + timeLeft = period - (System.currentTimeMillis() - start); + } + } } \ No newline at end of file -- cgit v1.2.1 From 788f96fd8af146cba5bff57300b1a513988c34b9 Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Fri, 18 Sep 2009 12:55:40 +0000 Subject: Fix bind error message git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-network-refactor@816614 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/qpid/transport/network/mina/MINANetworkDriver.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid') diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java index 38ea9307b7..b0d1c46572 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MINANetworkDriver.java @@ -159,7 +159,7 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver } catch (IOException e) { - throw new BindException(String.format("Could not bind to {0}:{2}", addr, port)); + throw new BindException(String.format("Could not bind to %1s:%2s", addr, port)); } } } @@ -171,7 +171,7 @@ public class MINANetworkDriver extends IoHandlerAdapter implements NetworkDriver } catch (IOException e) { - throw new BindException(String.format("Could not bind to *:{1}", port)); + throw new BindException(String.format("Could not bind to *:%1s", port)); } } _acceptingConnections = true; -- cgit v1.2.1 From 98cc985dbd81a84cd0b0a969c57cb941680ec81f Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Sun, 11 Oct 2009 23:22:08 +0000 Subject: Merge from trunk git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-network-refactor@824198 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/cpp/CMakeLists.txt | 146 ++- qpid/cpp/bindings/qmf/python/Makefile.am | 2 +- qpid/cpp/bindings/qmf/python/qmf.py | 4 +- qpid/cpp/bindings/qmf/qmfengine.i | 35 +- qpid/cpp/bindings/qmf/ruby/Makefile.am | 7 +- qpid/cpp/bindings/qmf/ruby/qmf.rb | 321 +++++- qpid/cpp/bindings/qmf/tests/agent_ruby.rb | 103 +- qpid/cpp/bindings/qmf/tests/ruby_console.rb | 76 +- qpid/cpp/bindings/qmf/tests/ruby_console_test.rb | 194 ++++ qpid/cpp/bindings/qmf/tests/run_interop_tests | 10 +- qpid/cpp/bindings/qmf/tests/test_base.rb | 73 ++ qpid/cpp/boost-1.32-support/supressions | 76 ++ qpid/cpp/etc/CMakeLists.txt | 20 + qpid/cpp/examples/messaging/client.cpp | 2 +- qpid/cpp/examples/messaging/map_receiver.cpp | 4 +- qpid/cpp/examples/messaging/map_sender.cpp | 11 +- qpid/cpp/examples/messaging/queue_listener.cpp | 4 +- qpid/cpp/examples/messaging/queue_receiver.cpp | 8 +- qpid/cpp/examples/messaging/queue_sender.cpp | 15 +- qpid/cpp/examples/messaging/server.cpp | 8 +- qpid/cpp/examples/messaging/topic_listener.cpp | 4 +- qpid/cpp/examples/messaging/topic_receiver.cpp | 4 +- qpid/cpp/examples/qmf-agent/Makefile | 2 +- qpid/cpp/include/qmf/ConnectionSettings.h | 121 +-- qpid/cpp/include/qmf/ConsoleObject.h | 94 ++ qpid/cpp/include/qmf/engine/Agent.h | 209 ++++ qpid/cpp/include/qmf/engine/ConnectionSettings.h | 150 +++ qpid/cpp/include/qmf/engine/Console.h | 235 +++++ qpid/cpp/include/qmf/engine/Event.h | 32 + qpid/cpp/include/qmf/engine/Message.h | 41 + qpid/cpp/include/qmf/engine/Object.h | 56 + qpid/cpp/include/qmf/engine/ObjectId.h | 67 ++ .../cpp/include/qmf/engine/QmfEngineImportExport.h | 33 + qpid/cpp/include/qmf/engine/Query.h | 110 ++ qpid/cpp/include/qmf/engine/ResilientConnection.h | 165 +++ qpid/cpp/include/qmf/engine/Schema.h | 210 ++++ qpid/cpp/include/qmf/engine/Typecode.h | 53 + qpid/cpp/include/qmf/engine/Value.h | 121 +++ qpid/cpp/include/qpid/client/QueueOptions.h | 15 + qpid/cpp/include/qpid/messaging/ListContent.h | 90 ++ qpid/cpp/include/qpid/messaging/ListView.h | 67 ++ qpid/cpp/include/qpid/messaging/MapContent.h | 90 ++ qpid/cpp/include/qpid/messaging/MapView.h | 70 ++ qpid/cpp/include/qpid/messaging/Message.h | 22 +- qpid/cpp/include/qpid/messaging/MessageContent.h | 90 -- qpid/cpp/include/qpid/sys/posix/PrivatePosix.h | 5 + qpid/cpp/include/qpid/sys/windows/IntegerTypes.h | 4 +- qpid/cpp/managementgen/CMakeLists.txt | 19 +- qpid/cpp/packaging/NSIS/qpid-icon.ico | Bin 0 -> 52972 bytes qpid/cpp/packaging/NSIS/qpid-icon.png | Bin 0 -> 97992 bytes qpid/cpp/packaging/NSIS/qpid-install-banner.bmp | Bin 0 -> 9742 bytes qpid/cpp/packaging/NSIS/qpid-install-banner.png | Bin 0 -> 57218 bytes qpid/cpp/rubygen/framing.0-10/structs.rb | 3 +- qpid/cpp/src/CMakeLists.txt | 277 +++-- qpid/cpp/src/Makefile.am | 11 +- qpid/cpp/src/cluster.mk | 3 +- qpid/cpp/src/qmf.mk | 120 ++- qpid/cpp/src/qmf/AgentEngine.cpp | 857 --------------- qpid/cpp/src/qmf/AgentEngine.h | 207 ---- qpid/cpp/src/qmf/ConnectionSettingsImpl.cpp | 323 ------ qpid/cpp/src/qmf/ConnectionSettingsImpl.h | 60 -- qpid/cpp/src/qmf/ConsoleEngine.cpp | 1091 -------------------- qpid/cpp/src/qmf/ConsoleEngine.h | 222 ---- qpid/cpp/src/qmf/Event.h | 30 - qpid/cpp/src/qmf/Message.h | 39 - qpid/cpp/src/qmf/MessageImpl.cpp | 43 - qpid/cpp/src/qmf/MessageImpl.h | 42 - qpid/cpp/src/qmf/Object.h | 48 - qpid/cpp/src/qmf/ObjectId.h | 53 - qpid/cpp/src/qmf/ObjectIdImpl.cpp | 192 ---- qpid/cpp/src/qmf/ObjectIdImpl.h | 67 -- qpid/cpp/src/qmf/ObjectImpl.cpp | 208 ---- qpid/cpp/src/qmf/ObjectImpl.h | 65 -- qpid/cpp/src/qmf/Protocol.cpp | 52 - qpid/cpp/src/qmf/Protocol.h | 67 -- qpid/cpp/src/qmf/Query.h | 104 -- qpid/cpp/src/qmf/QueryImpl.cpp | 94 -- qpid/cpp/src/qmf/QueryImpl.h | 102 -- qpid/cpp/src/qmf/ResilientConnection.cpp | 472 --------- qpid/cpp/src/qmf/ResilientConnection.h | 163 --- qpid/cpp/src/qmf/Schema.h | 171 --- qpid/cpp/src/qmf/SchemaImpl.cpp | 550 ---------- qpid/cpp/src/qmf/SchemaImpl.h | 224 ---- qpid/cpp/src/qmf/SequenceManager.cpp | 96 -- qpid/cpp/src/qmf/SequenceManager.h | 66 -- qpid/cpp/src/qmf/Typecode.h | 51 - qpid/cpp/src/qmf/Value.h | 113 -- qpid/cpp/src/qmf/ValueImpl.cpp | 478 --------- qpid/cpp/src/qmf/ValueImpl.h | 144 --- qpid/cpp/src/qmf/engine/Agent.cpp | 857 +++++++++++++++ qpid/cpp/src/qmf/engine/BrokerProxyImpl.cpp | 763 ++++++++++++++ qpid/cpp/src/qmf/engine/BrokerProxyImpl.h | 239 +++++ qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.cpp | 273 +++++ qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.h | 63 ++ qpid/cpp/src/qmf/engine/ConsoleImpl.cpp | 419 ++++++++ qpid/cpp/src/qmf/engine/ConsoleImpl.h | 145 +++ qpid/cpp/src/qmf/engine/MessageImpl.cpp | 43 + qpid/cpp/src/qmf/engine/MessageImpl.h | 44 + qpid/cpp/src/qmf/engine/ObjectIdImpl.cpp | 168 +++ qpid/cpp/src/qmf/engine/ObjectIdImpl.h | 72 ++ qpid/cpp/src/qmf/engine/ObjectImpl.cpp | 232 +++++ qpid/cpp/src/qmf/engine/ObjectImpl.h | 76 ++ qpid/cpp/src/qmf/engine/Protocol.cpp | 52 + qpid/cpp/src/qmf/engine/Protocol.h | 69 ++ qpid/cpp/src/qmf/engine/QueryImpl.cpp | 103 ++ qpid/cpp/src/qmf/engine/QueryImpl.h | 100 ++ qpid/cpp/src/qmf/engine/ResilientConnection.cpp | 489 +++++++++ qpid/cpp/src/qmf/engine/SchemaImpl.cpp | 611 +++++++++++ qpid/cpp/src/qmf/engine/SchemaImpl.h | 223 ++++ qpid/cpp/src/qmf/engine/SequenceManager.cpp | 96 ++ qpid/cpp/src/qmf/engine/SequenceManager.h | 68 ++ qpid/cpp/src/qmf/engine/ValueImpl.cpp | 266 +++++ qpid/cpp/src/qmf/engine/ValueImpl.h | 150 +++ qpid/cpp/src/qpid/acl/AclData.cpp | 109 +- qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp | 53 +- qpid/cpp/src/qpid/agent/ManagementAgentImpl.h | 11 +- qpid/cpp/src/qpid/amqp_0_10/Connection.cpp | 21 +- qpid/cpp/src/qpid/amqp_0_10/Connection.h | 2 +- qpid/cpp/src/qpid/broker/AclModule.h | 22 +- qpid/cpp/src/qpid/broker/Broker.cpp | 16 +- qpid/cpp/src/qpid/broker/Broker.h | 3 +- qpid/cpp/src/qpid/broker/DirectExchange.cpp | 33 +- qpid/cpp/src/qpid/broker/DtxAck.cpp | 22 +- qpid/cpp/src/qpid/broker/Exchange.cpp | 34 + qpid/cpp/src/qpid/broker/Exchange.h | 3 + qpid/cpp/src/qpid/broker/FanOutExchange.cpp | 32 +- qpid/cpp/src/qpid/broker/HeadersExchange.cpp | 29 +- qpid/cpp/src/qpid/broker/Message.cpp | 110 +- qpid/cpp/src/qpid/broker/Message.h | 17 +- qpid/cpp/src/qpid/broker/MessageBuilder.cpp | 3 +- qpid/cpp/src/qpid/broker/MessageStoreModule.cpp | 6 +- qpid/cpp/src/qpid/broker/MessageStoreModule.h | 5 +- qpid/cpp/src/qpid/broker/PersistableMessage.cpp | 31 +- qpid/cpp/src/qpid/broker/PersistableMessage.h | 21 +- qpid/cpp/src/qpid/broker/Queue.cpp | 102 +- qpid/cpp/src/qpid/broker/Queue.h | 17 +- qpid/cpp/src/qpid/broker/QueueEvents.cpp | 41 +- qpid/cpp/src/qpid/broker/QueueEvents.h | 4 +- qpid/cpp/src/qpid/broker/QueuePolicy.cpp | 144 ++- qpid/cpp/src/qpid/broker/QueuePolicy.h | 34 +- qpid/cpp/src/qpid/broker/SemanticState.cpp | 35 +- qpid/cpp/src/qpid/broker/SemanticState.h | 2 + qpid/cpp/src/qpid/broker/SessionAdapter.cpp | 7 +- qpid/cpp/src/qpid/broker/SessionContext.h | 3 +- qpid/cpp/src/qpid/broker/SessionState.h | 2 + qpid/cpp/src/qpid/broker/SignalHandler.cpp | 2 + qpid/cpp/src/qpid/broker/SignalHandler.h | 3 + qpid/cpp/src/qpid/broker/TopicExchange.cpp | 43 +- qpid/cpp/src/qpid/broker/TxAccept.cpp | 8 +- qpid/cpp/src/qpid/broker/TxPublish.cpp | 54 +- qpid/cpp/src/qpid/broker/TxPublish.h | 14 +- qpid/cpp/src/qpid/client/ConnectionHandler.cpp | 1 + qpid/cpp/src/qpid/client/ConnectionHandler.h | 2 + qpid/cpp/src/qpid/client/ConnectionImpl.cpp | 6 + qpid/cpp/src/qpid/client/Connector.cpp | 82 +- qpid/cpp/src/qpid/client/RdmaConnector.cpp | 15 +- qpid/cpp/src/qpid/client/Sasl.h | 1 + qpid/cpp/src/qpid/client/SaslFactory.cpp | 13 + qpid/cpp/src/qpid/client/SessionImpl.cpp | 18 +- qpid/cpp/src/qpid/client/SessionImpl.h | 4 + .../src/qpid/client/amqp0_10/AddressResolution.cpp | 11 +- .../src/qpid/client/amqp0_10/IncomingMessages.cpp | 11 +- .../src/qpid/client/amqp0_10/OutgoingMessage.cpp | 17 +- qpid/cpp/src/qpid/client/windows/SaslFactory.cpp | 6 + qpid/cpp/src/qpid/cluster/Cluster.cpp | 30 +- qpid/cpp/src/qpid/cluster/ErrorCheck.cpp | 30 +- qpid/cpp/src/qpid/cluster/ErrorCheck.h | 1 + qpid/cpp/src/qpid/cluster/Event.cpp | 9 - qpid/cpp/src/qpid/cluster/Multicaster.cpp | 4 - qpid/cpp/src/qpid/cluster/Multicaster.h | 3 - qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp | 12 +- qpid/cpp/src/qpid/cluster/UpdateClient.cpp | 9 +- qpid/cpp/src/qpid/cluster/UpdateExchange.h | 2 +- qpid/cpp/src/qpid/messaging/ListContent.cpp | 98 ++ qpid/cpp/src/qpid/messaging/ListView.cpp | 63 ++ qpid/cpp/src/qpid/messaging/MapContent.cpp | 87 ++ qpid/cpp/src/qpid/messaging/MapView.cpp | 63 ++ qpid/cpp/src/qpid/messaging/Message.cpp | 28 +- qpid/cpp/src/qpid/messaging/MessageImpl.cpp | 149 +-- qpid/cpp/src/qpid/messaging/MessageImpl.h | 54 +- qpid/cpp/src/qpid/sys/AsynchIO.h | 6 +- qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp | 39 +- qpid/cpp/src/qpid/sys/AsynchIOHandler.h | 2 +- qpid/cpp/src/qpid/sys/LatencyTracker.h | 157 --- qpid/cpp/src/qpid/sys/RdmaIOPlugin.cpp | 20 +- qpid/cpp/src/qpid/sys/Socket.h | 2 + qpid/cpp/src/qpid/sys/SocketAddress.h | 51 + qpid/cpp/src/qpid/sys/TCPIOPlugin.cpp | 27 +- qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp | 31 +- qpid/cpp/src/qpid/sys/posix/Socket.cpp | 55 +- qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp | 71 ++ qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp | 17 +- qpid/cpp/src/qpid/sys/rdma/RdmaIO.cpp | 5 +- qpid/cpp/src/qpid/sys/rdma/RdmaIO.h | 9 +- qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp | 12 +- qpid/cpp/src/qpid/sys/rdma/rdma_wrap.h | 9 +- qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp | 2 +- qpid/cpp/src/qpid/xml/XmlExchange.cpp | 39 +- qpid/cpp/src/qpidd.cpp | 34 +- qpid/cpp/src/tests/AsyncCompletion.cpp | 6 +- qpid/cpp/src/tests/ClusterFixture.h | 4 +- qpid/cpp/src/tests/ExchangeTest.cpp | 2 +- qpid/cpp/src/tests/Makefile.am | 64 +- qpid/cpp/src/tests/MessageUtils.h | 5 +- qpid/cpp/src/tests/MessagingSessionTests.cpp | 83 +- qpid/cpp/src/tests/PartialFailure.cpp | 5 +- qpid/cpp/src/tests/QueuePolicyTest.cpp | 58 +- qpid/cpp/src/tests/QueueTest.cpp | 341 +++++- qpid/cpp/src/tests/TxPublishTest.cpp | 2 +- qpid/cpp/src/tests/acl.py | 126 ++- qpid/cpp/src/tests/ais_check | 35 +- qpid/cpp/src/tests/background.ps1 | 21 +- qpid/cpp/src/tests/cluster.mk | 5 +- qpid/cpp/src/tests/cluster_read_credit | 27 + qpid/cpp/src/tests/cluster_test.cpp | 59 +- qpid/cpp/src/tests/clustered_replication_test | 41 +- qpid/cpp/src/tests/federated_cluster_test | 39 +- qpid/cpp/src/tests/federated_topic_test | 17 +- qpid/cpp/src/tests/python_env.sh | 26 + qpid/cpp/src/tests/python_tests | 10 +- qpid/cpp/src/tests/python_tests.ps1 | 42 + qpid/cpp/src/tests/qpid_stream.cpp | 9 +- qpid/cpp/src/tests/quick_topictest.ps1 | 11 +- qpid/cpp/src/tests/reliable_replication_test | 14 +- qpid/cpp/src/tests/replication_test | 43 +- qpid/cpp/src/tests/ring_queue_test | 7 +- qpid/cpp/src/tests/run_acl_tests | 4 +- qpid/cpp/src/tests/run_cli_tests | 6 +- qpid/cpp/src/tests/run_cluster_test | 26 + qpid/cpp/src/tests/run_cluster_tests | 38 +- qpid/cpp/src/tests/run_failover_soak | 24 +- qpid/cpp/src/tests/run_federation_tests | 4 +- qpid/cpp/src/tests/run_federation_tests.ps1 | 65 +- qpid/cpp/src/tests/run_header_test | 2 +- qpid/cpp/src/tests/run_header_test.ps1 | 33 +- qpid/cpp/src/tests/run_long_cluster_tests | 3 +- qpid/cpp/src/tests/run_ring_queue_test | 5 +- qpid/cpp/src/tests/run_test.ps1 | 3 +- qpid/cpp/src/tests/start_broker.ps1 | 21 +- qpid/cpp/src/tests/start_cluster | 9 +- qpid/cpp/src/tests/stop_broker.ps1 | 17 +- qpid/cpp/src/tests/test_store.cpp | 3 +- qpid/cpp/src/tests/topictest.ps1 | 32 +- .../src/tests/windows/DisableWin32ErrorWindows.cpp | 17 + qpid/cpp/src/windows/QpiddBroker.cpp | 9 + qpid/java/broker/etc/log4j.xml | 4 + .../java/org/apache/qpid/server/AMQChannel.java | 109 +- .../server/configuration/QueueConfiguration.java | 10 + .../server/configuration/ServerConfiguration.java | 13 +- .../configuration/VirtualHostConfiguration.java | 11 + .../management/ConfigurationManagementMBean.java | 2 - .../server/exchange/DefaultExchangeFactory.java | 1 - .../server/handler/BasicRejectMethodHandler.java | 2 +- .../qpid/server/handler/ChannelOpenHandler.java | 7 + .../qpid/server/logging/actors/AbstractActor.java | 5 + .../qpid/server/logging/actors/BrokerActor.java | 8 + .../logging/messages/LogMessages_en_US.properties | 6 +- .../org/apache/qpid/server/queue/AMQQueue.java | 13 + .../apache/qpid/server/queue/AMQQueueFactory.java | 106 ++ .../apache/qpid/server/queue/QueueEntryImpl.java | 16 +- .../apache/qpid/server/queue/SimpleAMQQueue.java | 278 +++-- .../qpid/server/registry/ApplicationRegistry.java | 2 +- .../ConfigurationFileApplicationRegistry.java | 10 +- .../qpid/server/registry/IApplicationRegistry.java | 3 +- .../security/access/PrincipalPermissions.java | 1002 +++++++++--------- .../security/access/plugins/BasicACLPlugin.java | 12 - .../access/plugins/network/FirewallPlugin.java | 1 - .../PropertiesPrincipalDatabaseManager.java | 1 - .../qpid/server/subscription/SubscriptionImpl.java | 10 +- .../qpid/server/txn/LocalTransactionalContext.java | 1 + .../qpid/server/txn/NonTransactionalContext.java | 2 + .../org/apache/qpid/server/queue/MockAMQQueue.java | 37 +- .../registry/ApplicationRegistryShutdownTest.java | 3 +- .../qpid/server/util/NullApplicationRegistry.java | 5 +- .../qpid/server/util/TestApplicationRegistry.java | 5 +- qpid/java/client/src/main/java/log4j.xml | 36 + .../java/org/apache/qpid/client/AMQConnection.java | 12 +- .../apache/qpid/client/AMQConnectionDelegate.java | 9 + .../qpid/client/AMQConnectionDelegate_0_10.java | 13 +- .../qpid/client/AMQConnectionDelegate_8_0.java | 13 +- .../java/org/apache/qpid/client/AMQSession.java | 152 ++- .../org/apache/qpid/client/AMQSession_0_10.java | 3 - .../org/apache/qpid/client/AMQSession_0_8.java | 11 +- .../apache/qpid/client/BasicMessageConsumer.java | 1 + .../apache/qpid/client/BasicMessageProducer.java | 2 +- .../org/apache/qpid/client/XAConnectionImpl.java | 2 +- .../client/configuration/ClientProperties.java | 2 +- .../qpid/client/failover/FailoverHandler.java | 2 + .../client/handler/ClientMethodDispatcherImpl.java | 4 +- .../qpid/client/protocol/AMQProtocolHandler.java | 1 - .../apache/qpid/client/util/BlockingWaiter.java | 2 +- .../qpid/jms/failover/FailoverExchangeMethod.java | 13 +- qpid/java/common/bin/qpid-run | 1 + .../main/java/org/apache/qpid/ConsoleOutput.java | 1 - .../common/mbeans/LoggingManagement.java | 2 +- .../common/mbeans/ServerInformation.java | 2 +- .../qpid/management/ui/ApplicationRegistry.java | 2 +- .../views/logging/ConfigurationFileTabControl.java | 2 +- qpid/java/module.xml | 2 + .../org/apache/qpid/server/BrokerStartupTest.java | 22 +- .../MessageDisappearWithIOExceptionTest.java | 330 ++++++ .../qpid/server/logging/BrokerLoggingTest.java | 2 +- .../server/queue/DeepQueueConsumeWithSelector.java | 5 +- .../qpid/server/queue/ProducerFlowControlTest.java | 393 +++++++ .../qpid/server/security/acl/SimpleACLTest.java | 123 ++- .../qpid/test/client/QueueBrowserAutoAckTest.java | 6 +- .../apache/qpid/test/client/RollbackOrderTest.java | 170 ++- .../qpid/test/client/failover/FailoverTest.java | 22 +- .../qpid/test/client/message/SelectorTest.java | 271 ++++- .../test/unit/ack/Acknowledge2ConsumersTest.java | 193 ++++ .../ack/AcknowledgeAfterFailoverOnMessageTest.java | 356 +++++++ .../unit/ack/AcknowledgeAfterFailoverTest.java | 306 ++++++ .../test/unit/ack/AcknowledgeOnMessageTest.java | 170 +++ .../apache/qpid/test/unit/ack/AcknowledgeTest.java | 179 ++-- .../ack/FailoverBeforeConsumingRecoverTest.java | 40 + .../org/apache/qpid/test/unit/ack/QuickAcking.java | 148 +++ .../org/apache/qpid/test/unit/ack/RecoverTest.java | 253 ++--- .../apache/qpid/test/unit/basic/SelectorTest.java | 306 ------ .../qpid/test/unit/message/JMSPropertiesTest.java | 4 +- .../unit/publish/DirtyTrasactedPubilshTest.java | 403 ++++++++ .../apache/qpid/test/utils/FailoverBaseCase.java | 83 +- .../org/apache/qpid/test/utils/QpidTestCase.java | 221 +++- qpid/java/test-profiles/010Excludes | 9 + qpid/java/test-profiles/08Excludes | 2 - qpid/java/test-profiles/08StandaloneExcludes | 2 - qpid/java/test-profiles/Excludes | 12 + qpid/java/test-profiles/default.testprofile | 3 + qpid/java/test-profiles/log4j-test.xml | 6 +- qpid/java/test-profiles/test-provider.properties | 17 +- qpid/python/Makefile | 2 +- qpid/python/examples/api/drain | 62 ++ qpid/python/examples/api/ping | 76 ++ qpid/python/qpid-python-test | 42 +- qpid/python/qpid/address.py | 171 +++ qpid/python/qpid/compat.py | 11 +- qpid/python/qpid/driver.py | 864 +++++++++++----- qpid/python/qpid/messaging.py | 148 ++- qpid/python/qpid/ops.py | 6 +- qpid/python/qpid/selector.py | 156 +++ qpid/python/qpid/tests/messaging.py | 174 +++- qpid/review/changeLogToWiki.py | 85 ++ qpid/wcf/ReadMe.txt | 8 +- qpid/wcf/src/Apache/Qpid/Channel/AmqpBinding.cs | 6 + .../Channel/AmqpBindingConfigurationElement.cs | 11 + .../src/Apache/Qpid/Channel/AmqpChannelFactory.cs | 4 +- .../src/Apache/Qpid/Channel/AmqpChannelHelpers.cs | 2 +- .../src/Apache/Qpid/Channel/AmqpChannelListener.cs | 4 +- .../Qpid/Channel/AmqpTransportBindingElement.cs | 8 + .../Apache/Qpid/Channel/AmqpTransportChannel.cs | 11 +- qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.cpp | 32 +- qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.h | 3 +- .../wcf/src/Apache/Qpid/Interop/CompletionWaiter.h | 1 - qpid/wcf/src/Apache/Qpid/Interop/InputLink.cpp | 150 ++- qpid/wcf/src/Apache/Qpid/Interop/InputLink.h | 15 + qpid/wcf/src/Apache/Qpid/Interop/Interop.vcproj | 4 +- qpid/wcf/src/Apache/Qpid/Interop/MessageWaiter.h | 4 +- .../Qpid/Test/Channel/Functional/RunTests.bat | 10 +- qpid/wcf/tools/QCreate/QCreate.vcproj | 6 +- 358 files changed, 17558 insertions(+), 10315 deletions(-) create mode 100755 qpid/cpp/bindings/qmf/tests/ruby_console_test.rb create mode 100644 qpid/cpp/bindings/qmf/tests/test_base.rb create mode 100644 qpid/cpp/etc/CMakeLists.txt create mode 100644 qpid/cpp/include/qmf/ConsoleObject.h create mode 100644 qpid/cpp/include/qmf/engine/Agent.h create mode 100644 qpid/cpp/include/qmf/engine/ConnectionSettings.h create mode 100644 qpid/cpp/include/qmf/engine/Console.h create mode 100644 qpid/cpp/include/qmf/engine/Event.h create mode 100644 qpid/cpp/include/qmf/engine/Message.h create mode 100644 qpid/cpp/include/qmf/engine/Object.h create mode 100644 qpid/cpp/include/qmf/engine/ObjectId.h create mode 100644 qpid/cpp/include/qmf/engine/QmfEngineImportExport.h create mode 100644 qpid/cpp/include/qmf/engine/Query.h create mode 100644 qpid/cpp/include/qmf/engine/ResilientConnection.h create mode 100644 qpid/cpp/include/qmf/engine/Schema.h create mode 100644 qpid/cpp/include/qmf/engine/Typecode.h create mode 100644 qpid/cpp/include/qmf/engine/Value.h create mode 100644 qpid/cpp/include/qpid/messaging/ListContent.h create mode 100644 qpid/cpp/include/qpid/messaging/ListView.h create mode 100644 qpid/cpp/include/qpid/messaging/MapContent.h create mode 100644 qpid/cpp/include/qpid/messaging/MapView.h delete mode 100644 qpid/cpp/include/qpid/messaging/MessageContent.h create mode 100644 qpid/cpp/packaging/NSIS/qpid-icon.ico create mode 100644 qpid/cpp/packaging/NSIS/qpid-icon.png create mode 100644 qpid/cpp/packaging/NSIS/qpid-install-banner.bmp create mode 100644 qpid/cpp/packaging/NSIS/qpid-install-banner.png delete mode 100644 qpid/cpp/src/qmf/AgentEngine.cpp delete mode 100644 qpid/cpp/src/qmf/AgentEngine.h delete mode 100644 qpid/cpp/src/qmf/ConnectionSettingsImpl.cpp delete mode 100644 qpid/cpp/src/qmf/ConnectionSettingsImpl.h delete mode 100644 qpid/cpp/src/qmf/ConsoleEngine.cpp delete mode 100644 qpid/cpp/src/qmf/ConsoleEngine.h delete mode 100644 qpid/cpp/src/qmf/Event.h delete mode 100644 qpid/cpp/src/qmf/Message.h delete mode 100644 qpid/cpp/src/qmf/MessageImpl.cpp delete mode 100644 qpid/cpp/src/qmf/MessageImpl.h delete mode 100644 qpid/cpp/src/qmf/Object.h delete mode 100644 qpid/cpp/src/qmf/ObjectId.h delete mode 100644 qpid/cpp/src/qmf/ObjectIdImpl.cpp delete mode 100644 qpid/cpp/src/qmf/ObjectIdImpl.h delete mode 100644 qpid/cpp/src/qmf/ObjectImpl.cpp delete mode 100644 qpid/cpp/src/qmf/ObjectImpl.h delete mode 100644 qpid/cpp/src/qmf/Protocol.cpp delete mode 100644 qpid/cpp/src/qmf/Protocol.h delete mode 100644 qpid/cpp/src/qmf/Query.h delete mode 100644 qpid/cpp/src/qmf/QueryImpl.cpp delete mode 100644 qpid/cpp/src/qmf/QueryImpl.h delete mode 100644 qpid/cpp/src/qmf/ResilientConnection.cpp delete mode 100644 qpid/cpp/src/qmf/ResilientConnection.h delete mode 100644 qpid/cpp/src/qmf/Schema.h delete mode 100644 qpid/cpp/src/qmf/SchemaImpl.cpp delete mode 100644 qpid/cpp/src/qmf/SchemaImpl.h delete mode 100644 qpid/cpp/src/qmf/SequenceManager.cpp delete mode 100644 qpid/cpp/src/qmf/SequenceManager.h delete mode 100644 qpid/cpp/src/qmf/Typecode.h delete mode 100644 qpid/cpp/src/qmf/Value.h delete mode 100644 qpid/cpp/src/qmf/ValueImpl.cpp delete mode 100644 qpid/cpp/src/qmf/ValueImpl.h create mode 100644 qpid/cpp/src/qmf/engine/Agent.cpp create mode 100644 qpid/cpp/src/qmf/engine/BrokerProxyImpl.cpp create mode 100644 qpid/cpp/src/qmf/engine/BrokerProxyImpl.h create mode 100644 qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.cpp create mode 100644 qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.h create mode 100644 qpid/cpp/src/qmf/engine/ConsoleImpl.cpp create mode 100644 qpid/cpp/src/qmf/engine/ConsoleImpl.h create mode 100644 qpid/cpp/src/qmf/engine/MessageImpl.cpp create mode 100644 qpid/cpp/src/qmf/engine/MessageImpl.h create mode 100644 qpid/cpp/src/qmf/engine/ObjectIdImpl.cpp create mode 100644 qpid/cpp/src/qmf/engine/ObjectIdImpl.h create mode 100644 qpid/cpp/src/qmf/engine/ObjectImpl.cpp create mode 100644 qpid/cpp/src/qmf/engine/ObjectImpl.h create mode 100644 qpid/cpp/src/qmf/engine/Protocol.cpp create mode 100644 qpid/cpp/src/qmf/engine/Protocol.h create mode 100644 qpid/cpp/src/qmf/engine/QueryImpl.cpp create mode 100644 qpid/cpp/src/qmf/engine/QueryImpl.h create mode 100644 qpid/cpp/src/qmf/engine/ResilientConnection.cpp create mode 100644 qpid/cpp/src/qmf/engine/SchemaImpl.cpp create mode 100644 qpid/cpp/src/qmf/engine/SchemaImpl.h create mode 100644 qpid/cpp/src/qmf/engine/SequenceManager.cpp create mode 100644 qpid/cpp/src/qmf/engine/SequenceManager.h create mode 100644 qpid/cpp/src/qmf/engine/ValueImpl.cpp create mode 100644 qpid/cpp/src/qmf/engine/ValueImpl.h create mode 100644 qpid/cpp/src/qpid/messaging/ListContent.cpp create mode 100644 qpid/cpp/src/qpid/messaging/ListView.cpp create mode 100644 qpid/cpp/src/qpid/messaging/MapContent.cpp create mode 100644 qpid/cpp/src/qpid/messaging/MapView.cpp delete mode 100644 qpid/cpp/src/qpid/sys/LatencyTracker.h create mode 100644 qpid/cpp/src/qpid/sys/SocketAddress.h create mode 100644 qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp create mode 100755 qpid/cpp/src/tests/cluster_read_credit create mode 100644 qpid/cpp/src/tests/python_env.sh create mode 100644 qpid/cpp/src/tests/python_tests.ps1 create mode 100755 qpid/cpp/src/tests/run_cluster_test create mode 100644 qpid/java/client/src/main/java/log4j.xml create mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/server/failover/MessageDisappearWithIOExceptionTest.java create mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ProducerFlowControlTest.java create mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/Acknowledge2ConsumersTest.java create mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverOnMessageTest.java create mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverTest.java create mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeOnMessageTest.java create mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/FailoverBeforeConsumingRecoverTest.java create mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/QuickAcking.java delete mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/SelectorTest.java create mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/test/unit/publish/DirtyTrasactedPubilshTest.java mode change 100644 => 100755 qpid/java/test-profiles/010Excludes create mode 100755 qpid/python/examples/api/drain create mode 100755 qpid/python/examples/api/ping create mode 100644 qpid/python/qpid/address.py create mode 100644 qpid/python/qpid/selector.py create mode 100755 qpid/review/changeLogToWiki.py (limited to 'qpid') diff --git a/qpid/cpp/CMakeLists.txt b/qpid/cpp/CMakeLists.txt index de1353bd11..18a7616d99 100644 --- a/qpid/cpp/CMakeLists.txt +++ b/qpid/cpp/CMakeLists.txt @@ -23,26 +23,150 @@ if(COMMAND cmake_policy) cmake_policy(SET CMP0003 NEW) endif(COMMAND cmake_policy) +set (QPID_VERSION_MAJOR 0) +set (QPID_VERSION_MINOR 6) +set (qpidc_version ${QPID_VERSION_MAJOR}.${QPID_VERSION_MINOR}) + enable_testing() include (CTest) -set (qpidc_version 0.5) +# When doing installs, there are a number of components that the item can +# be associated with. Since there may be different sets of components desired +# for the various platforms, the component names are defined here. When +# setting the COMPONENT in an install directive, use these to ensure that +# the item is installed correctly. +# +# This section also has all the setup for various packaging-specific options. +if (WIN32) + set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") + set (CPACK_NSIS_MUI_ICON "${CMAKE_CURRENT_SOURCE_DIR}/packaging/NSIS\\\\qpid-icon.ico") + set (CPACK_NSIS_MUI_UNIICON "${CMAKE_CURRENT_SOURCE_DIR}/packaging/NSIS\\\\qpid-icon.ico") + set (CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/packaging/NSIS\\\\qpid-install-banner.bmp") -# set(CMAKE_INCLUDE_CURRENT_DIR ON) + # Install types; these defines the component sets that are installed. + # Each component (below) indicates which of these install type(s) it is + # included in. The user can refine the components at install time. + set (CPACK_ALL_INSTALL_TYPES Broker Development Full) -add_subdirectory(managementgen) -# add_subdirectory(etc) -add_subdirectory(src) -# add_subdirectory(docs/api) -# add_subdirectory(docs/man) + set (QPID_COMPONENT_COMMON Common) + set (CPACK_COMPONENT_COMMON_INSTALL_TYPES Broker Development Full) + set (CPACK_COMPONENT_COMMON_DISPLAY_NAME "Required common runtime items") + set (CPACK_COMPONENT_COMMON_DESCRIPTION + "Run-time library common to all runtime components in Qpid.\n + This item is required by both broker and client components.") + + set (QPID_COMPONENT_BROKER Broker) + set (CPACK_COMPONENT_BROKER_DEPENDS Common) + set (CPACK_COMPONENT_BROKER_INSTALL_TYPES Broker Full) + set (CPACK_COMPONENT_BROKER_DISPLAY_NAME "Broker") + set (CPACK_COMPONENT_BROKER_DESCRIPTION + "Messaging broker; controls message flow within the system.\n + At least one broker is required to run any messaging application.") + + set (QPID_COMPONENT_CLIENT Client) + set (CPACK_COMPONENT_CLIENT_DEPENDS Common) + set (CPACK_COMPONENT_CLIENT_INSTALL_TYPES Development Full) + set (CPACK_COMPONENT_CLIENT_DISPLAY_NAME "Client runtime libraries") + set (CPACK_COMPONENT_CLIENT_DESCRIPTION + "Runtime library components required to build and execute a client application.") + + set (QPID_COMPONENT_CLIENT_INCLUDE ClientInclude) + set (CPACK_COMPONENT_CLIENTINCLUDE_INSTALL_TYPES Development Full) + set (CPACK_COMPONENT_CLIENTINCLUDE_DISPLAY_NAME + "Client programming header files") + set (CPACK_COMPONENT_CLIENTINCLUDE_DESCRIPTION + "C++ header files required to build any Qpid messaging application.") + + set (QPID_COMPONENT_QMF QMF) + set (CPACK_COMPONENT_QMF_INSTALL_TYPES Development Full) + set (CPACK_COMPONENT_QMF_DISPLAY_NAME + "Qpid Management Framework (QMF)") + set (CPACK_COMPONENT_QMF_DESCRIPTION + "QMF Agent allows you to embed QMF management in your program.\n + QMF Console allows you to build management programs using QMF.") + + set (QPID_INSTALL_BINDIR bin CACHE STRING + "Directory to install user executables") + set (QPID_INSTALL_CONFDIR conf CACHE STRING + "Directory to install configuration files") + set (QPID_INSTALL_DATADIR conf CACHE STRING + "Directory to install read-only arch.-independent data root") + set (QPID_INSTALL_INCLUDEDIR include CACHE STRING + "Directory to install programming header files") + set (QPID_INSTALL_LIBDIR bin CACHE STRING + "Directory to install library files") + set (QPID_INSTALL_SBINDIR bin CACHE STRING + "Directory to install system admin executables") + set (QPIDC_MODULE_DIR plugins/client CACHE STRING + "Directory to load client plug-in modules from") + set (QPIDD_MODULE_DIR plugins/broker CACHE STRING + "Directory to load broker plug-in modules from") +endif (WIN32) +if (CMAKE_SYSTEM_NAME STREQUAL Linux) + # Set up install locations. Since the Linux install puts some files in + # /etc and most in the install location, we need to use a DESTDIR build + # rather than the usual simple use of CPACK_INSTALL_PREFIX. + set (CPACK_SET_DESTDIR ON) -# if (WIN32) -# do something Microsoft specific -# endif (WIN32) + set (QPID_COMPONENT_BROKER runtime) + set (QPID_COMPONENT_CLIENT runtime) + set (QPID_COMPONENT_COMMON runtime) + set (CPACK_COMPONENT_RUNTIME_DISPLAY_NAME + "Items required to run broker and/or client programs") + set (QPID_COMPONENT_CLIENT_INCLUDE development) + set (QPID_COMPONENT_QMF development) + set (CPACK_COMPONENT_DEVELOPMENT_DISPLAY_NAME + "Items required to build new C++ Qpid client programs") + + + set (QPID_INSTALL_BINDIR bin CACHE STRING + "Directory to install user executables") + set (QPID_INSTALL_CONFDIR /etc/qpid CACHE STRING + "Directory to install configuration files") + set (QPID_INSTALL_DATADIR share/qpid CACHE STRING + "Directory to install read-only arch.-independent data root") + set (QPID_INSTALL_INCLUDEDIR include CACHE STRING + "Directory to install programming header files") + set (QPID_INSTALL_LIBDIR lib CACHE STRING + "Directory to install library files") + set (QPID_INSTALL_SBINDIR sbin CACHE STRING + "Directory to install system admin executables") + set (QPIDC_MODULE_DIR ${QPID_INSTALL_LIBDIR}/qpid/client CACHE STRING + "Directory to load client plug-in modules from") + set (QPIDD_MODULE_DIR ${QPID_INSTALL_LIBDIR}/qpid/daemon CACHE STRING + "Directory to load broker plug-in modules from") +endif (CMAKE_SYSTEM_NAME STREQUAL Linux) + +set (QPIDC_CONF_FILE ${QPID_INSTALL_CONFDIR}/qpidc.conf CACHE STRING + "Name of the Qpid client configuration file") +set (QPIDD_CONF_FILE ${QPID_INSTALL_CONFDIR}/qpidd.conf CACHE STRING + "Name of the Qpid broker configuration file") install(FILES LICENSE NOTICE README SSL RELEASE_NOTES DESIGN xml/cluster.xml INSTALL-WINDOWS - DESTINATION .) + DESTINATION ${QPID_INSTALL_DATADIR}) + +if (WIN32) + set (CMAKE_DEBUG_POSTFIX "d") +endif (WIN32) + +# set(CMAKE_INCLUDE_CURRENT_DIR ON) +add_subdirectory(managementgen) +add_subdirectory(etc) +add_subdirectory(src) +# add_subdirectory(docs/api) +# add_subdirectory(docs/man) add_subdirectory(examples) + +set(CPACK_PACKAGE_NAME "qpid-cpp") +set(CPACK_PACKAGE_VENDOR "Apache Software Foundation") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Apache Qpid C++") +set(CPACK_PACKAGE_VERSION "${qpidc_version}") +set(CPACK_PACKAGE_VERSION_MAJOR "${QPID_VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${QPID_VERSION_MINOR}") +set(CPACK_PACKAGE_VERSION_PATCH "0") +set(CPACK_PACKAGE_INSTALL_DIRECTORY "qpidc-${qpidc_version}") + +include (CPack) diff --git a/qpid/cpp/bindings/qmf/python/Makefile.am b/qpid/cpp/bindings/qmf/python/Makefile.am index 55d9079fb7..53303c7be9 100644 --- a/qpid/cpp/bindings/qmf/python/Makefile.am +++ b/qpid/cpp/bindings/qmf/python/Makefile.am @@ -38,7 +38,7 @@ lib_LTLIBRARIES = _qmfengine.la #_qmfengine_la_LDFLAGS = -avoid-version -module -shrext "$(PYTHON_SO)" #_qmfengine_la_LDFLAGS = -avoid-version -module -shrext ".so" _qmfengine_la_LDFLAGS = -avoid-version -module -shared -_qmfengine_la_LIBADD = $(PYTHON_LIBS) -L$(top_builddir)/src/.libs -lqpidclient $(top_builddir)/src/libqmfagent.la +_qmfengine_la_LIBADD = $(PYTHON_LIBS) -L$(top_builddir)/src/.libs -lqpidclient $(top_builddir)/src/libqmf.la _qmfengine_la_CXXFLAGS = $(INCLUDES) -I$(srcdir)/qmf -I$(PYTHON_INC) nodist__qmfengine_la_SOURCES = qmfengine.cpp diff --git a/qpid/cpp/bindings/qmf/python/qmf.py b/qpid/cpp/bindings/qmf/python/qmf.py index 4800b327f1..383baad0e3 100644 --- a/qpid/cpp/bindings/qmf/python/qmf.py +++ b/qpid/cpp/bindings/qmf/python/qmf.py @@ -566,7 +566,7 @@ class Console: # attr_reader :impl def initialize(handler=None, kwargs={}): self._handler = handler - self.impl = qmfengine.ConsoleEngine() + self.impl = qmfengine.Console() self._event = qmfengine.ConsoleEvent() self._broker_list = [] @@ -741,7 +741,7 @@ class Agent(ConnectionHandler): self._agentLabel = label self._conn = None self._handler = handler - self.impl = qmfengine.AgentEngine(self._agentLabel) + self.impl = qmfengine.Agent(self._agentLabel) self._event = qmfengine.AgentEvent() self._xmtMessage = qmfengine.Message() diff --git a/qpid/cpp/bindings/qmf/qmfengine.i b/qpid/cpp/bindings/qmf/qmfengine.i index d3500c9b8f..3477215254 100644 --- a/qpid/cpp/bindings/qmf/qmfengine.i +++ b/qpid/cpp/bindings/qmf/qmfengine.i @@ -19,34 +19,35 @@ %{ -#include "qmf/AgentEngine.h" -#include "qmf/ConsoleEngine.h" -#include "qmf/ResilientConnection.h" +#include "qmf/engine/Agent.h" +#include "qmf/engine/Console.h" +#include "qmf/engine/ResilientConnection.h" %} -%include -%include -%include -%include -%include -%include -%include -%include -%include -%include -%include -%include +%include +%include +%include +%include +%include +%include +%include +%include +%include +%include +%include +%include %inline { using namespace std; -using namespace qmf; +using namespace qmf::engine; namespace qmf { +namespace engine { - +} } } diff --git a/qpid/cpp/bindings/qmf/ruby/Makefile.am b/qpid/cpp/bindings/qmf/ruby/Makefile.am index 0537dd1cd8..34096da9ee 100644 --- a/qpid/cpp/bindings/qmf/ruby/Makefile.am +++ b/qpid/cpp/bindings/qmf/ruby/Makefile.am @@ -23,23 +23,22 @@ INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src EXTRA_DIST = ruby.i BUILT_SOURCES = qmfengine.cpp -generated_file_list = qmfengine.cpp rubylibdir = $(RUBY_LIB) dist_rubylib_DATA = qmf.rb -$(generated_file_list): $(srcdir)/ruby.i $(srcdir)/../qmfengine.i +qmfengine.cpp: $(srcdir)/ruby.i $(srcdir)/../qmfengine.i $(SWIG) -ruby -c++ -Wall -I/usr/include $(INCLUDES) $(QPID_CXXFLAGS) -o qmfengine.cpp $(srcdir)/ruby.i rubylibarchdir = $(RUBY_LIB_ARCH) rubylibarch_LTLIBRARIES = qmfengine.la qmfengine_la_LDFLAGS = -avoid-version -module -shrext ".$(RUBY_DLEXT)" -qmfengine_la_LIBADD = $(RUBY_LIBS) -L$(top_builddir)/src/.libs -lqpidclient $(top_builddir)/src/libqmfagent.la +qmfengine_la_LIBADD = $(RUBY_LIBS) -L$(top_builddir)/src/.libs -lqpidclient $(top_builddir)/src/libqmfengine.la qmfengine_la_CXXFLAGS = $(INCLUDES) -I$(RUBY_INC) -I$(RUBY_INC_ARCH) nodist_qmfengine_la_SOURCES = qmfengine.cpp -CLEANFILES = $(generated_file_list) +CLEANFILES = qmfengine.cpp endif # HAVE_RUBY_DEVEL diff --git a/qpid/cpp/bindings/qmf/ruby/qmf.rb b/qpid/cpp/bindings/qmf/ruby/qmf.rb index 16f1058f4a..fbf95215fd 100644 --- a/qpid/cpp/bindings/qmf/ruby/qmf.rb +++ b/qpid/cpp/bindings/qmf/ruby/qmf.rb @@ -60,7 +60,19 @@ module Qmf raise ArgumentError, "Value for attribute '#{key}' has unsupported type: #{val.class}" end - @impl.setAttr(key, v) + good = @impl.setAttr(key, v) + raise "Invalid attribute '#{key}'" unless good + end + + def method_missing(name_in, *args) + name = name_in.to_s + if name[name.length - 1] == 61 + attr = name[0..name.length - 2] + set_attr(attr, args[0]) + return + end + + super.method_missing(name_in, args) end end @@ -85,12 +97,17 @@ module Qmf @new_conn_handlers = [] @conn_handlers_to_delete = [] @conn_handlers = [] + @connected = nil @thread = Thread.new do run end end + def connected? + @connected + end + def kick @sockEngine.write(".") @sockEngine.flush @@ -112,7 +129,6 @@ module Qmf def run() eventImpl = Qmfengine::ResilientConnectionEvent.new - connected = nil new_handlers = nil del_handlers = nil bt_count = 0 @@ -129,7 +145,7 @@ module Qmf new_handlers.each do |nh| @conn_handlers << nh - nh.conn_event_connected() if connected + nh.conn_event_connected() if @connected end new_handlers = nil @@ -143,10 +159,10 @@ module Qmf begin case eventImpl.kind when Qmfengine::ResilientConnectionEvent::CONNECTED - connected = :true + @connected = :true @conn_handlers.each { |h| h.conn_event_connected() } when Qmfengine::ResilientConnectionEvent::DISCONNECTED - connected = nil + @connected = nil @conn_handlers.each { |h| h.conn_event_disconnected(eventImpl.errorText) } when Qmfengine::ResilientConnectionEvent::SESSION_CLOSED eventImpl.sessionContext.handler.sess_event_session_closed(eventImpl.sessionContext, eventImpl.errorText) @@ -189,8 +205,16 @@ module Qmf ##============================================================================== class QmfObject + include MonitorMixin attr_reader :impl, :object_class def initialize(cls, kwargs={}) + super() + @cv = new_cond + @sync_count = 0 + @sync_result = nil + @allow_sets = :false + @broker = kwargs[:broker] if kwargs.include?(:broker) + if cls: @object_class = cls @impl = Qmfengine::Object.new(@object_class.impl) @@ -204,6 +228,22 @@ module Qmf return ObjectId.new(@impl.getObjectId) end + def properties + list = [] + @object_class.properties.each do |prop| + list << [prop, get_attr(prop.name)] + end + return list + end + + def statistics + list = [] + @object_class.statistics.each do |stat| + list << [stat, get_attr(stat.name)] + end + return list + end + def get_attr(name) val = value(name) case val.getType @@ -212,7 +252,7 @@ module Qmf when TYPE_SSTR, TYPE_LSTR then val.asString when TYPE_ABSTIME then val.asInt64 when TYPE_DELTATIME then val.asUint64 - when TYPE_REF then val.asObjectId + when TYPE_REF then ObjectId.new(val.asObjectId) when TYPE_BOOL then val.asBool when TYPE_FLOAT then val.asFloat when TYPE_DOUBLE then val.asDouble @@ -264,6 +304,103 @@ module Qmf set_attr(name, get_attr(name) - by) end + def method_missing(name_in, *args) + # + # Convert the name to a string and determine if it represents an + # attribute assignment (i.e. "attr=") + # + name = name_in.to_s + attr_set = (name[name.length - 1] == 61) + name = name[0..name.length - 2] if attr_set + raise "Sets not permitted on this object" if attr_set && !@allow_sets + + # + # If the name matches a property name, set or return the value of the property. + # + @object_class.properties.each do |prop| + if prop.name == name + if attr_set + return set_attr(name, args[0]) + else + return get_attr(name) + end + end + end + + # + # Do the same for statistics + # + @object_class.statistics.each do |stat| + if stat.name == name + if attr_set + return set_attr(name, args[0]) + else + return get_attr(name) + end + end + end + + # + # If we still haven't found a match for the name, check to see if + # it matches a method name. If so, marshall the arguments and invoke + # the method. + # + @object_class.methods.each do |method| + if method.name == name + raise "Sets not permitted on methods" if attr_set + timeout = 30 + synchronize do + @sync_count = 1 + @impl.invokeMethod(name, _marshall(method, args), self) + @broker.conn.kick if @broker + unless @cv.wait(timeout) { @sync_count == 0 } + raise "Timed out waiting for response" + end + end + + return @sync_result + end + end + + # + # This name means nothing to us, pass it up the line to the parent + # class's handler. + # + super.method_missing(name_in, args) + end + + def _method_result(result) + synchronize do + @sync_result = result + @sync_count -= 1 + @cv.signal + end + end + + # + # Convert a Ruby array of arguments (positional) into a Value object of type "map". + # + private + def _marshall(schema, args) + map = Qmfengine::Value.new(TYPE_MAP) + schema.arguments.each do |arg| + if arg.direction == DIR_IN || arg.direction == DIR_IN_OUT + map.insert(arg.name, Qmfengine::Value.new(arg.typecode)) + end + end + + marshalled = Arguments.new(map) + idx = 0 + schema.arguments.each do |arg| + if arg.direction == DIR_IN || arg.direction == DIR_IN_OUT + marshalled[arg.name] = args[idx] unless args[idx] == nil + idx += 1 + end + end + + return marshalled.map + end + private def value(name) val = @impl.getValue(name.to_s) @@ -277,6 +414,7 @@ module Qmf class AgentObject < QmfObject def initialize(cls, kwargs={}) super(cls, kwargs) + @allow_sets = :true end def destroy @@ -296,20 +434,22 @@ module Qmf end def update() + raise "No linkage to broker" unless @broker + newer = @broker.console.objects(Query.new(:object_id => object_id)) + raise "Expected exactly one update for this object" unless newer.size == 1 + merge_update(newer[0]) end - def mergeUpdate(newObject) + def merge_update(new_object) + @impl.merge(new_object.impl) end def deleted?() - @delete_time > 0 + @impl.isDeleted end def index() end - - def method_missing(name, *args) - end end class ObjectId @@ -323,17 +463,29 @@ module Qmf end def object_num_high - return @impl.getObjectNumHi + @impl.getObjectNumHi end def object_num_low - return @impl.getObjectNumLo + @impl.getObjectNumLo + end + + def broker_bank + @impl.getBrokerBank + end + + def agent_bank + @impl.getAgentBank end def ==(other) return (@impl.getObjectNumHi == other.impl.getObjectNumHi) && (@impl.getObjectNumLo == other.impl.getObjectNumLo) end + + def to_s + @impl.str + end end class Arguments @@ -362,6 +514,14 @@ module Qmf @by_hash.each { |k, v| yield(k, v) } end + def method_missing(name, *args) + if @by_hash.include?(name.to_s) + return @by_hash[name.to_s] + end + + super.method_missing(name, args) + end + def by_key(key) val = @map.byKey(key) case val.getType @@ -370,7 +530,7 @@ module Qmf when TYPE_SSTR, TYPE_LSTR then val.asString when TYPE_ABSTIME then val.asInt64 when TYPE_DELTATIME then val.asUint64 - when TYPE_REF then val.asObjectId + when TYPE_REF then ObjectId.new(val.asObjectId) when TYPE_BOOL then val.asBool when TYPE_FLOAT then val.asFloat when TYPE_DOUBLE then val.asDouble @@ -407,6 +567,32 @@ module Qmf end end + class MethodResponse + def initialize(impl) + @impl = Qmfengine::MethodResponse.new(impl) + end + + def status + @impl.getStatus + end + + def exception + @impl.getException + end + + def text + exception.asString + end + + def args + Arguments.new(@impl.getArgs) + end + + def method_missing(name, *extra_args) + args.__send__(name, extra_args) + end + end + ##============================================================================== ## QUERY ##============================================================================== @@ -421,13 +607,13 @@ module Qmf if kwargs.include?(:key) @impl = Qmfengine::Query.new(kwargs[:key]) elsif kwargs.include?(:object_id) - @impl = Qmfengine::Query.new(kwargs[:object_id]) + @impl = Qmfengine::Query.new(kwargs[:object_id].impl) else package = kwargs[:package] if kwargs.include?(:package) if kwargs.include?(:class) @impl = Qmfengine::Query.new(kwargs[:class], package) else - raise ArgumentError, "Invalid arguments, use :key or :class[,:package]" + raise ArgumentError, "Invalid arguments, use :key, :object_id or :class[,:package]" end end end @@ -470,6 +656,18 @@ module Qmf def name @impl.getName end + + def direction + @impl.getDirection + end + + def typecode + @impl.getType + end + + def to_s + name + end end class SchemaMethod @@ -496,6 +694,10 @@ module Qmf def name @impl.getName end + + def to_s + name + end end class SchemaProperty @@ -516,6 +718,10 @@ module Qmf def name @impl.getName end + + def to_s + name + end end class SchemaStatistic @@ -533,6 +739,10 @@ module Qmf def name @impl.getName end + + def to_s + name + end end class SchemaClassKey @@ -541,12 +751,16 @@ module Qmf @impl = i end - def get_package() - @impl.getPackageName() + def package_name + @impl.getPackageName + end + + def class_name + @impl.getClassName end - def get_class() - @impl.getClassName() + def to_s + @impl.asString end end @@ -590,7 +804,15 @@ module Qmf @impl.addMethod(meth.impl) end - def name + def class_key + SchemaClassKey.new(@impl.getClassKey) + end + + def package_name + @impl.getClassKey.getPackageName + end + + def class_name @impl.getClassKey.getClassName end end @@ -643,7 +865,7 @@ module Qmf def initialize(handler = nil, kwargs={}) super() @handler = handler - @impl = Qmfengine::ConsoleEngine.new + @impl = Qmfengine::Console.new @event = Qmfengine::ConsoleEvent.new @broker_list = [] @cv = new_cond @@ -662,7 +884,7 @@ module Qmf @broker_list.delete(broker) end - def get_packages() + def packages() plist = [] count = @impl.packageCount for i in 0...count @@ -671,7 +893,7 @@ module Qmf return plist end - def get_classes(package, kind=CLASS_OBJECT) + def classes(package, kind=CLASS_OBJECT) clist = [] count = @impl.classCount(package) for i in 0...count @@ -708,7 +930,7 @@ module Qmf end end - def get_agents(broker = nil) + def agents(broker = nil) blist = [] if broker blist << broker @@ -727,11 +949,17 @@ module Qmf return agents end - def get_objects(query, kwargs = {}) + def objects(query, kwargs = {}) timeout = 30 + kwargs.merge!(query) if query.class == Hash + if kwargs.include?(:timeout) timeout = kwargs[:timeout] + kwargs.delete(:timeout) end + + query = Query.new(kwargs) if query.class == Hash + synchronize do @sync_count = 1 @sync_result = [] @@ -745,6 +973,18 @@ module Qmf end end + # Return one and only one object or nil. + def object(query, kwargs = {}) + objs = objects(query, kwargs) + return objs.length == 1 ? objs[0] : nil + end + + # Return the first of potentially many objects. + def first_object(query, kwargs = {}) + objs = objects(query, kwargs) + return objs.length > 0 ? objs[0] : nil + end + def _get_result(list, context) synchronize do list.each do |item| @@ -769,15 +1009,20 @@ module Qmf valid = @impl.getEvent(@event) while valid count += 1 - puts "Console Event: #{@event.kind}" case @event.kind when Qmfengine::ConsoleEvent::AGENT_ADDED + @handler.agent_added(AgentProxy.new(@event.agent, nil)) if @handler when Qmfengine::ConsoleEvent::AGENT_DELETED + @handler.agent_deleted(AgentProxy.new(@event.agent, nil)) if @handler when Qmfengine::ConsoleEvent::NEW_PACKAGE + @handler.new_package(@event.name) if @handler when Qmfengine::ConsoleEvent::NEW_CLASS + @handler.new_class(SchemaClassKey.new(@event.classKey)) if @handler when Qmfengine::ConsoleEvent::OBJECT_UPDATE + @handler.object_update(ConsoleObject.new(nil, :impl => @event.object), @event.hasProps, @event.hasStats) if @handler when Qmfengine::ConsoleEvent::EVENT_RECEIVED when Qmfengine::ConsoleEvent::AGENT_HEARTBEAT + @handler.agent_heartbeat(AgentProxy.new(@event.agent, nil), @event.timestamp) if @handler when Qmfengine::ConsoleEvent::METHOD_RESPONSE end @impl.popEvent @@ -798,14 +1043,23 @@ module Qmf def label @impl.getLabel end + + def broker_bank + @impl.getBrokerBank + end + + def agent_bank + @impl.getAgentBank + end end class Broker < ConnectionHandler include MonitorMixin - attr_reader :impl + attr_reader :impl, :conn, :console, :broker_bank def initialize(console, conn) super() + @broker_bank = 1 @console = console @conn = conn @session = nil @@ -825,7 +1079,7 @@ module Qmf @operational = :false end - def waitForStable(timeout = nil) + def wait_for_stable(timeout = nil) synchronize do return if @stable if timeout @@ -850,7 +1104,6 @@ module Qmf valid = @impl.getEvent(@event) while valid count += 1 - puts "Broker Event: #{@event.kind}" case @event.kind when Qmfengine::BrokerEvent::BROKER_INFO when Qmfengine::BrokerEvent::DECLARE_QUEUE @@ -871,9 +1124,12 @@ module Qmf when Qmfengine::BrokerEvent::QUERY_COMPLETE result = [] for idx in 0...@event.queryResponse.getObjectCount - result << ConsoleObject.new(nil, :impl => @event.queryResponse.getObject(idx)) + result << ConsoleObject.new(nil, :impl => @event.queryResponse.getObject(idx), :broker => self) end @console._get_result(result, @event.context) + when Qmfengine::BrokerEvent::METHOD_RESPONSE + obj = @event.context + obj._method_result(MethodResponse.new(@event.methodResponse)) end @impl.popEvent valid = @impl.getEvent(@event) @@ -946,7 +1202,7 @@ module Qmf end @conn = nil @handler = handler - @impl = Qmfengine::AgentEngine.new(@agentLabel) + @impl = Qmfengine::Agent.new(@agentLabel) @event = Qmfengine::AgentEvent.new @xmtMessage = Qmfengine::Message.new end @@ -1050,5 +1306,4 @@ module Qmf do_events end end - end diff --git a/qpid/cpp/bindings/qmf/tests/agent_ruby.rb b/qpid/cpp/bindings/qmf/tests/agent_ruby.rb index 67591319ee..426a284e7d 100755 --- a/qpid/cpp/bindings/qmf/tests/agent_ruby.rb +++ b/qpid/cpp/bindings/qmf/tests/agent_ruby.rb @@ -40,6 +40,9 @@ class Model @parent_class.add_property(Qmf::SchemaProperty.new("int16val", Qmf::TYPE_INT16)) @parent_class.add_property(Qmf::SchemaProperty.new("int8val", Qmf::TYPE_INT8)) + @parent_class.add_property(Qmf::SchemaProperty.new("sstrval", Qmf::TYPE_SSTR)) + @parent_class.add_property(Qmf::SchemaProperty.new("lstrval", Qmf::TYPE_LSTR)) + @parent_class.add_statistic(Qmf::SchemaStatistic.new("queryCount", Qmf::TYPE_UINT32, :unit => "query", :desc => "Query count")) method = Qmf::SchemaMethod.new("echo", :desc => "Check responsiveness of the agent object") @@ -50,6 +53,14 @@ class Model method.add_argument(Qmf::SchemaArgument.new("test", Qmf::TYPE_SSTR, :dir => Qmf::DIR_IN)) @parent_class.add_method(method) + method = Qmf::SchemaMethod.new("set_short_string", :desc => "Set the short string value in the object") + method.add_argument(Qmf::SchemaArgument.new("value", Qmf::TYPE_SSTR, :dir => Qmf::DIR_IN_OUT)) + @parent_class.add_method(method) + + method = Qmf::SchemaMethod.new("set_long_string", :desc => "Set the long string value in the object") + method.add_argument(Qmf::SchemaArgument.new("value", Qmf::TYPE_LSTR, :dir => Qmf::DIR_IN_OUT)) + @parent_class.add_method(method) + method = Qmf::SchemaMethod.new("create_child", :desc => "Create a new child object") method.add_argument(Qmf::SchemaArgument.new("child_name", Qmf::TYPE_LSTR, :dir => Qmf::DIR_IN)) method.add_argument(Qmf::SchemaArgument.new("child_ref", Qmf::TYPE_REF, :dir => Qmf::DIR_OUT)) @@ -84,68 +95,74 @@ class App < Qmf::AgentHandler def method_call(context, name, object_id, args, userId) # puts "Method: user=#{userId} context=#{context} method=#{name} object_num=#{object_id.object_num_low if object_id} args=#{args}" + retCode = 0 + retText = "OK" + if name == "echo" @agent.method_response(context, 0, "OK", args) elsif name == "set_numerics" - retCode = 0 - retText = "OK" if args['test'] == "big" - @parent.set_attr("uint64val", 0x9494949449494949) - @parent.set_attr("uint32val", 0xa5a55a5a) - @parent.set_attr("uint16val", 0xb66b) - @parent.set_attr("uint8val", 0xc7) + @parent.uint64val = 0x9494949449494949 + @parent.uint32val = 0xa5a55a5a + @parent.uint16val = 0xb66b + @parent.uint8val = 0xc7 - @parent.set_attr("int64val", 1000000000000000000) - @parent.set_attr("int32val", 1000000000) - @parent.set_attr("int16val", 10000) - @parent.set_attr("int8val", 100) + @parent.int64val = 1000000000000000000 + @parent.int32val = 1000000000 + @parent.int16val = 10000 + @parent.int8val = 100 elsif args['test'] == "small" - @parent.set_attr("uint64val", 4) - @parent.set_attr("uint32val", 5) - @parent.set_attr("uint16val", 6) - @parent.set_attr("uint8val", 7) + @parent.uint64val = 4 + @parent.uint32val = 5 + @parent.uint16val = 6 + @parent.uint8val = 7 - @parent.set_attr("int64val", 8) - @parent.set_attr("int32val", 9) - @parent.set_attr("int16val", 10) - @parent.set_attr("int8val", 11) + @parent.int64val = 8 + @parent.int32val = 9 + @parent.int16val = 10 + @parent.int8val = 11 elsif args['test'] == "negative" - @parent.set_attr("uint64val", 0) - @parent.set_attr("uint32val", 0) - @parent.set_attr("uint16val", 0) - @parent.set_attr("uint8val", 0) + @parent.uint64val = 0 + @parent.uint32val = 0 + @parent.uint16val = 0 + @parent.uint8val = 0 - @parent.set_attr("int64val", -10000000000) - @parent.set_attr("int32val", -100000) - @parent.set_attr("int16val", -1000) - @parent.set_attr("int8val", -100) + @parent.int64val = -10000000000 + @parent.int32val = -100000 + @parent.int16val = -1000 + @parent.int8val = -100 else retCode = 1 retText = "Invalid argument value for test" end - @agent.method_response(context, retCode, retText, args) + elsif name == "set_short_string" + @parent.sstrval = args['value'] + + elsif name == "set_long_string" + @parent.lstrval = args['value'] elsif name == "create_child" oid = @agent.alloc_object_id(2) args['child_ref'] = oid @child = Qmf::AgentObject.new(@model.child_class) - @child.set_attr("name", args.by_key("child_name")) + @child.name = args.by_key("child_name") @child.set_object_id(oid) - @agent.method_response(context, 0, "OK", args) elsif name == "probe_userid" args['userid'] = userId - @agent.method_response(context, 0, "OK", args) else - @agent.method_response(context, 1, "Unimplemented Method: #{name}", args) + retCode = 1 + retText = "Unimplemented Method: #{name}" end + + @agent.method_response(context, retCode, retText, args) end def main @@ -161,18 +178,18 @@ class App < Qmf::AgentHandler @agent.set_connection(@connection) @parent = Qmf::AgentObject.new(@model.parent_class) - @parent.set_attr("name", "Parent One") - @parent.set_attr("state", "OPERATIONAL") - - @parent.set_attr("uint64val", 0) - @parent.set_attr("uint32val", 0) - @parent.set_attr("uint16val", 0) - @parent.set_attr("uint8val", 0) - - @parent.set_attr("int64val", 0) - @parent.set_attr("int32val", 0) - @parent.set_attr("int16val", 0) - @parent.set_attr("int8val", 0) + @parent.name = "Parent One" + @parent.state = "OPERATIONAL" + + @parent.uint64val = 0 + @parent.uint32val = 0 + @parent.uint16val = 0 + @parent.uint8val = 0 + + @parent.int64val = 0 + @parent.int32val = 0 + @parent.int16val = 0 + @parent.int8val = 0 @parent_oid = @agent.alloc_object_id(1) @parent.set_object_id(@parent_oid) diff --git a/qpid/cpp/bindings/qmf/tests/ruby_console.rb b/qpid/cpp/bindings/qmf/tests/ruby_console.rb index c071829f09..0fa856c724 100755 --- a/qpid/cpp/bindings/qmf/tests/ruby_console.rb +++ b/qpid/cpp/bindings/qmf/tests/ruby_console.rb @@ -24,13 +24,46 @@ require 'socket' class App < Qmf::ConsoleHandler + def agent_added(agent) + puts "AgentAdded: #{agent.label} broker=#{agent.broker_bank} agent=#{agent.agent_bank}" + end + + def agent_deleted(agent) + puts "AgentDeleted: #{agent.label}" + end + + def new_package(package) + puts "NewPackage: #{package}" + end + + def new_class(class_key) + puts "NewClass: #{class_key}" + end + + def object_update(object, hasProps, hasStats) + puts "ObjectUpdate: #{object.object_class.class_name} props=#{hasProps} stats=#{hasStats}" + puts " broker-bank=#{object.object_id.broker_bank}" + puts " agent-bank=#{object.object_id.agent_bank}" + puts " package=#{object.object_class.package_name}" + end + + def event_received(event); end + + def agent_heartbeat(agent, timestamp) + puts "AgentHeartbeat: #{agent.label} time=#{timestamp/1000000000}" + end + + def method_response(resp); end + def broker_info(broker); end + + def dump_schema - packages = @qmfc.get_packages + packages = @qmfc.packages puts "----- Packages -----" packages.each do |p| puts p puts " ----- Object Classes -----" - classes = @qmfc.get_classes(p) + classes = @qmfc.classes(p) classes.each do |c| puts " #{c.name}" @@ -59,7 +92,7 @@ class App < Qmf::ConsoleHandler end puts " ----- Event Classes -----" - classes = @qmfc.get_classes(p, Qmf::CLASS_EVENT) + classes = @qmfc.classes(p, Qmf::CLASS_EVENT) classes.each do |c| puts " #{c.name}" puts " ---- Args ----" @@ -74,17 +107,17 @@ class App < Qmf::ConsoleHandler def main @settings = Qmf::ConnectionSettings.new - @settings.set_attr("host", ARGV[0]) if ARGV.size > 0 - @settings.set_attr("port", ARGV[1].to_i) if ARGV.size > 1 + @settings.host = ARGV[0] if ARGV.size > 0 + @settings.port = ARGV[1].to_i if ARGV.size > 1 @connection = Qmf::Connection.new(@settings) - @qmfc = Qmf::Console.new + @qmfc = Qmf::Console.new(self) @broker = @qmfc.add_connection(@connection) - @broker.waitForStable + @broker.wait_for_stable - dump_schema + ##dump_schema - agents = @qmfc.get_agents() + agents = @qmfc.agents() puts "---- Agents ----" agents.each do |a| puts " => #{a.label}" @@ -92,13 +125,30 @@ class App < Qmf::ConsoleHandler puts "----" for idx in 0...20 - blist = @qmfc.get_objects(Qmf::Query.new(:class => "broker")) + blist = @qmfc.objects(Qmf::Query.new(:class => "broker")) puts "---- Brokers ----" blist.each do |b| puts " ---- Broker ----" - puts " systemRef: #{b.get_attr('systemRef')}" - puts " port : #{b.get_attr('port')}" - puts " uptime : #{b.get_attr('uptime') / 1000000000}" + puts " systemRef: #{b.systemRef}" + puts " port : #{b.port}" + puts " uptime : #{b.uptime / 1000000000}" + puts " properties : #{b.properties}" + puts " statistics : #{b.statistics}" + + for rep in 0...1 + puts " Pinging..." + ret = b.echo(45, 'text string') + puts " status=#{ret.status} text=#{ret.exception.asString} seq=#{ret.args.sequence} body=#{ret.args.body}" + end + end + puts "----" + + qlist = @qmfc.objects(Qmf::Query.new(:package => "org.apache.qpid.broker", + :class => "queue")) + puts "---- Queues ----" + qlist.each do |q| + puts " ---- Queue ----" + puts " name : #{q.name}" end puts "----" sleep(5) diff --git a/qpid/cpp/bindings/qmf/tests/ruby_console_test.rb b/qpid/cpp/bindings/qmf/tests/ruby_console_test.rb new file mode 100755 index 0000000000..b72c8e3806 --- /dev/null +++ b/qpid/cpp/bindings/qmf/tests/ruby_console_test.rb @@ -0,0 +1,194 @@ +#!/usr/bin/ruby + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'test_base' + +class ConsoleTest < ConsoleTestBase + + def test_A_agent_presence + assert(@connection.connected?, "Connection not connected") + + agents = [] + count = 0 + while agents.size == 0 + agents = @qmfc.objects(Qmf::Query.new(:class => "agent")) + sleep(1) + count += 1 + fail("Timed out waiting for remote agent") if count > 10 + end + + agentList = @qmfc.agents + assert_equal(agentList.size, 2, "Number of agents reported by Console") + end + + def test_A_connection_settings + begin + @settings.bogusAttribute = 25 + fail("Connection settings accepted bogus attribute") + rescue + end + end + + def test_B_basic_method_invocation + parent = @qmfc.object(:class => "parent") + assert(parent, "Number of 'parent' objects") + for seq in 0...10 + result = parent.echo(seq) + assert_equal(result.status, 0, "Method Response Status") + assert_equal(result.text, "OK", "Method Response Text") + assert_equal(result.args.sequence, seq, "Echo Response Sequence") + end + + result = parent.set_numerics("bogus") + assert_equal(result.status, 1) + assert_equal(result.text, "Invalid argument value for test") + end + + def test_C_basic_types_numeric_big + parent = @qmfc.object(:class =>"parent") + assert(parent, "Number of parent objects") + + result = parent.set_numerics("big") + assert_equal(result.status, 0, "Method Response Status") + assert_equal(result.text, "OK", "Method Response Text") + + parent.update + + assert_equal(parent.uint64val, 0x9494949449494949) + assert_equal(parent.uint32val, 0xA5A55A5A) + assert_equal(parent.uint16val, 0xB66B) + assert_equal(parent.uint8val, 0xC7) + + assert_equal(parent.int64val, 1000000000000000000) + assert_equal(parent.int32val, 1000000000) + assert_equal(parent.int16val, 10000) + assert_equal(parent.int8val, 100) + end + + def test_C_basic_types_numeric_small + parent = @qmfc.object(:class =>"parent") + assert(parent, "Number of parent objects") + + result = parent.set_numerics("small") + assert_equal(result.status, 0, "Method Response Status") + assert_equal(result.text, "OK", "Method Response Text") + + parent.update + + assert_equal(parent.uint64val, 4) + assert_equal(parent.uint32val, 5) + assert_equal(parent.uint16val, 6) + assert_equal(parent.uint8val, 7) + + assert_equal(parent.int64val, 8) + assert_equal(parent.int32val, 9) + assert_equal(parent.int16val, 10) + assert_equal(parent.int8val, 11) + end + + def test_C_basic_types_numeric_negative + parent = @qmfc.object(:class =>"parent") + assert(parent, "Number of parent objects") + + result = parent.set_numerics("negative") + assert_equal(result.status, 0, "Method Response Status") + assert_equal(result.text, "OK", "Method Response Text") + + parent.update + + assert_equal(parent.uint64val, 0) + assert_equal(parent.uint32val, 0) + assert_equal(parent.uint16val, 0) + assert_equal(parent.uint8val, 0) + + assert_equal(parent.int64val, -10000000000) + assert_equal(parent.int32val, -100000) + assert_equal(parent.int16val, -1000) + assert_equal(parent.int8val, -100) + end + + def test_C_basic_types_string_short + parent = @qmfc.object(:class =>"parent") + assert(parent, "Number of parent objects") + + strings = [] + strings << "" + strings << "A" + strings << "BC" + strings << "DEF" + strings << "GHIJKLMNOPQRSTUVWXYZ" + big = "a" + for i in 0...270 + big << "X" + end + strings << big + + strings.each do |str| + result = parent.set_short_string(str) + assert_equal(result.status, 0, "Method Response Status") + compare = str + compare = compare[0..254] if compare.size > 255 + assert_equal(result.args.value, compare, "Value returned by method") + parent.update + assert_equal(parent.sstrval, compare, "Value stored in the object") + end + end + + def test_C_basic_types_string_long + parent = @qmfc.object(:class =>"parent") + assert(parent, "Number of parent objects") + + strings = [] + strings << "" + strings << "A" + strings << "BC" + strings << "DEF" + strings << "GHIJKLMNOPQRSTUVWXYZ" + big = "a" + for i in 0...270 + big << "X" + end + strings << big + + strings.each do |str| + result = parent.set_long_string(str) + assert_equal(result.status, 0, "Method Response Status") + assert_equal(result.args.value, str, "Value returned by method") + parent.update + assert_equal(parent.lstrval, str, "Value stored in the object") + end + end + + def test_D_userid_for_method + parent = @qmfc.object(:class => "parent") + assert(parent, "Number of parent objects") + + result = parent.probe_userid + assert_equal(result.status, 0, "Method Response Status") + assert_equal(result.args.userid, "anonymous") + end + +end + +app = ConsoleTest.new + + + diff --git a/qpid/cpp/bindings/qmf/tests/run_interop_tests b/qpid/cpp/bindings/qmf/tests/run_interop_tests index 01d7221ac6..b5545d736d 100755 --- a/qpid/cpp/bindings/qmf/tests/run_interop_tests +++ b/qpid/cpp/bindings/qmf/tests/run_interop_tests @@ -88,11 +88,19 @@ if test -d ${PYTHON_DIR} ; then echo " Ruby agent started at pid $AGENT_PID" ${PYTHON_DIR}/qpid-python-test -m python_console -b localhost:$BROKER_PORT $@ RETCODE=$? - stop_ruby_agent if test x$RETCODE != x0; then echo "FAIL qmf interop tests (Ruby Agent)"; TESTS_FAILED=1 fi + + echo " Ruby Agent (external storage) vs. Ruby Console" + ruby -I${MY_DIR} -I${MY_DIR}/../ruby -I${RUBY_LIB_DIR} ${MY_DIR}/ruby_console_test.rb localhost $BROKER_PORT $@ + RETCODE=$? + stop_ruby_agent + if test x$RETCODE != x0; then + echo "FAIL qmf interop tests (Ruby Console/Ruby Agent)"; + TESTS_FAILED=1 + fi fi # Also against the Pure-Python console: diff --git a/qpid/cpp/bindings/qmf/tests/test_base.rb b/qpid/cpp/bindings/qmf/tests/test_base.rb new file mode 100644 index 0000000000..cb7fd9d4f9 --- /dev/null +++ b/qpid/cpp/bindings/qmf/tests/test_base.rb @@ -0,0 +1,73 @@ +#!/usr/bin/ruby + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'qmf' +require 'socket' + +class ConsoleTestBase < Qmf::ConsoleHandler + def initialize + @settings = Qmf::ConnectionSettings.new + @settings.host = ARGV[0] if ARGV.size > 0 + @settings.port = ARGV[1].to_i if ARGV.size > 1 + @connection = Qmf::Connection.new(@settings) + @qmfc = Qmf::Console.new + + @broker = @qmfc.add_connection(@connection) + @broker.wait_for_stable + + tests = [] + methods.each do |m| + name = m.to_s + tests << name if name[0..4] == "test_" + end + + failures = 0 + + tests.sort.each do |t| + begin + print "#{t}..." + $stdout.flush + send(t) + puts " Pass" + rescue + puts " Fail: #{$!}" + failures += 1 + end + end + + @qmfc.del_connection(@broker) + exit(1) if failures > 0 + end + + def assert_equal(left, right, in_text=nil) + text = " (#{in_text})" if in_text + raise "Assertion failed: #{left} != #{right}#{text}" unless left == right + end + + def assert(condition, in_text=nil) + text = " (#{in_text})" if in_text + raise "Assertion failed: #{left} != #{right}#{text}" unless condition + end + + def fail(text) + raise text + end +end diff --git a/qpid/cpp/boost-1.32-support/supressions b/qpid/cpp/boost-1.32-support/supressions index 0747e32cc7..c2b4392566 100644 --- a/qpid/cpp/boost-1.32-support/supressions +++ b/qpid/cpp/boost-1.32-support/supressions @@ -789,3 +789,79 @@ fun:getaddrinfo fun:_ZNK4qpid3sys6Socket7connectERKSst } +{ + dl 0 + Memcheck:Leak + fun:*dl* +} + +{ + dl 1 + Memcheck:Leak + fun:* + fun:*dl* +} + +{ + dl 2 + fun:* + fun:* + Memcheck:Leak + fun:*dl* +} + +{ + Znwm + Memcheck:Leak + fun:_Znwm +} + +{ + Znwj + Memcheck:Leak + fun:_Znwj +} + +{ + Znaj + Memcheck:Leak + fun:_Znaj +} + +{ + libc res nsend + Memcheck:Leak + fun:malloc + fun:__libc_res_nsend +} + +{ + new exitfn + Memcheck:Leak + fun:malloc + fun:__new_exitfn +} + +{ + pthread mutex unlock + Memcheck:Param + futex(uaddr2) + fun:__lll_mutex_unlock_wake + fun:pthread_mutex_unlock +} + +{ + sasl 1 + Memcheck:Leak + fun:* + fun:sasl* +} + +{ + sasl 2 + Memcheck:Leak + fun:* + obj:* + fun:sasl* +} + diff --git a/qpid/cpp/etc/CMakeLists.txt b/qpid/cpp/etc/CMakeLists.txt new file mode 100644 index 0000000000..03121b364a --- /dev/null +++ b/qpid/cpp/etc/CMakeLists.txt @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +install(FILES qpidd.conf qpidc.conf DESTINATION ${QPID_INSTALL_CONFDIR}) diff --git a/qpid/cpp/examples/messaging/client.cpp b/qpid/cpp/examples/messaging/client.cpp index 45c065880b..de6d7768df 100644 --- a/qpid/cpp/examples/messaging/client.cpp +++ b/qpid/cpp/examples/messaging/client.cpp @@ -63,7 +63,7 @@ int main(int argc, char** argv) { request.setContent(s[i]); sender.send(request); Message response = receiver.fetch(); - std::cout << request.getContent().asString() << " -> " << response.getContent().asString() << std::endl; + std::cout << request.getContent() << " -> " << response.getContent() << std::endl; } connection.close(); return 0; diff --git a/qpid/cpp/examples/messaging/map_receiver.cpp b/qpid/cpp/examples/messaging/map_receiver.cpp index e6557b1560..f97c44eebd 100644 --- a/qpid/cpp/examples/messaging/map_receiver.cpp +++ b/qpid/cpp/examples/messaging/map_receiver.cpp @@ -20,6 +20,7 @@ */ #include +#include #include #include #include @@ -42,7 +43,8 @@ int main(int argc, char** argv) { Session session = connection.newSession(); Receiver receiver = session.createReceiver("message_queue"); Message message = receiver.fetch(); - std::cout << message.getContent().asMap() << std::endl; + MapView content(message); + std::cout << content << std::endl; session.acknowledge(); receiver.cancel(); connection.close(); diff --git a/qpid/cpp/examples/messaging/map_sender.cpp b/qpid/cpp/examples/messaging/map_sender.cpp index 9301c1fe1f..02c6433836 100644 --- a/qpid/cpp/examples/messaging/map_sender.cpp +++ b/qpid/cpp/examples/messaging/map_sender.cpp @@ -20,6 +20,7 @@ */ #include +#include #include #include #include @@ -43,14 +44,16 @@ int main(int argc, char** argv) { Sender sender = session.createSender("message_queue"); Message message; - message.getContent()["id"] = 987654321; - message.getContent()["name"] = "Widget"; - message.getContent()["price"] = 0.99;//bad use of floating point number, just an example! + MapContent content(message); + content["id"] = 987654321; + content["name"] = "Widget"; + content["price"] = 0.99;//bad use of floating point number, just an example! Variant::List colours; colours.push_back(Variant("red")); colours.push_back(Variant("green")); colours.push_back(Variant("white")); - message.getContent()["colours"] = colours; + content["colours"] = colours; + content.encode(); sender.send(message); session.sync(); diff --git a/qpid/cpp/examples/messaging/queue_listener.cpp b/qpid/cpp/examples/messaging/queue_listener.cpp index 099e8e145a..92a0eed5ed 100644 --- a/qpid/cpp/examples/messaging/queue_listener.cpp +++ b/qpid/cpp/examples/messaging/queue_listener.cpp @@ -47,8 +47,8 @@ bool Listener::isFinished() { return finished; } void Listener::received(Message& message) { - std::cout << "Message: " << message.getContent().asString() << std::endl; - if (message.getContent().asString() == "That's all, folks!") { + std::cout << "Message: " << message.getContent() << std::endl; + if (message.getContent() == "That's all, folks!") { std::cout << "Shutting down listener" << std::endl; receiver.cancel(); finished = true; diff --git a/qpid/cpp/examples/messaging/queue_receiver.cpp b/qpid/cpp/examples/messaging/queue_receiver.cpp index 83a44b2ca9..40f863eb30 100644 --- a/qpid/cpp/examples/messaging/queue_receiver.cpp +++ b/qpid/cpp/examples/messaging/queue_receiver.cpp @@ -24,16 +24,10 @@ #include #include -#include #include -#include - using namespace qpid::messaging; -using std::stringstream; -using std::string; - int main(int argc, char** argv) { const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; @@ -47,7 +41,7 @@ int main(int argc, char** argv) { Message message = receiver.fetch(); std::cout << "Message: " << message.getContent() << std::endl; session.acknowledge(); - if (message.getContent().asString() == "That's all, folks!") { + if (message.getContent() == "That's all, folks!") { std::cout << "Cancelling receiver" << std::endl; receiver.cancel(); break; diff --git a/qpid/cpp/examples/messaging/queue_sender.cpp b/qpid/cpp/examples/messaging/queue_sender.cpp index 637e7eb8e4..1396e26d5c 100644 --- a/qpid/cpp/examples/messaging/queue_sender.cpp +++ b/qpid/cpp/examples/messaging/queue_sender.cpp @@ -26,14 +26,10 @@ #include #include - #include using namespace qpid::messaging; -using std::stringstream; -using std::string; - int main(int argc, char** argv) { const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; int count = argc>2 ? atoi(argv[2]) : 10; @@ -45,14 +41,13 @@ int main(int argc, char** argv) { // Now send some messages ... for (int i=0; i " - << response.getContent().asString() << std::endl; + << response.getContent() << std::endl; session.acknowledge(); } else { - std::cerr << "Error: no reply address specified for request: " << request.getContent().asString() << std::endl; + std::cerr << "Error: no reply address specified for request: " << request.getContent() << std::endl; session.reject(request); } } diff --git a/qpid/cpp/examples/messaging/topic_listener.cpp b/qpid/cpp/examples/messaging/topic_listener.cpp index 700e03cdf9..ba999c03a7 100644 --- a/qpid/cpp/examples/messaging/topic_listener.cpp +++ b/qpid/cpp/examples/messaging/topic_listener.cpp @@ -48,8 +48,8 @@ bool Listener::isFinished() { return finished; } void Listener::received(Message& message) { - std::cout << "Message: " << message.getContent().asString() << std::endl; - if (message.getContent().asString() == "That's all, folks!") { + std::cout << "Message: " << message.getContent() << std::endl; + if (message.getContent() == "That's all, folks!") { std::cout << "Shutting down listener" << std::endl; receiver.cancel(); finished = true; diff --git a/qpid/cpp/examples/messaging/topic_receiver.cpp b/qpid/cpp/examples/messaging/topic_receiver.cpp index 063f0d9cb0..7352a91b30 100644 --- a/qpid/cpp/examples/messaging/topic_receiver.cpp +++ b/qpid/cpp/examples/messaging/topic_receiver.cpp @@ -47,8 +47,8 @@ int main(int argc, char** argv) { Receiver receiver = session.createReceiver(Address("news_service", "topic"), filter); while (true) { Message message = receiver.fetch(); - std::cout << "Message: " << message.getContent().asString() << std::endl; - if (message.getContent().asString() == "That's all, folks!") { + std::cout << "Message: " << message.getContent() << std::endl; + if (message.getContent() == "That's all, folks!") { std::cout << "Cancelling receiver" << std::endl; receiver.cancel(); break; diff --git a/qpid/cpp/examples/qmf-agent/Makefile b/qpid/cpp/examples/qmf-agent/Makefile index e652edb1a2..5b1afc4b01 100644 --- a/qpid/cpp/examples/qmf-agent/Makefile +++ b/qpid/cpp/examples/qmf-agent/Makefile @@ -27,7 +27,7 @@ CC = gcc LIB_DIR = $(QPID_DIR)/cpp/src/.libs CC_INCLUDES = -I$(SRC_DIR) -I$(QPID_DIR)/cpp/include -I$(GEN_DIR) CC_FLAGS = -g -O3 -LD_FLAGS = -lqmfagent -lqmfcommon -L$(LIB_DIR) +LD_FLAGS = -lqmf -L$(LIB_DIR) SPEC_DIR = $(QPID_DIR)/specs MGEN_DIR = $(QPID_DIR)/cpp/managementgen MGEN = $(MGEN_DIR)/qmf-gen diff --git a/qpid/cpp/include/qmf/ConnectionSettings.h b/qpid/cpp/include/qmf/ConnectionSettings.h index 9bd6922a56..11af73d797 100644 --- a/qpid/cpp/include/qmf/ConnectionSettings.h +++ b/qpid/cpp/include/qmf/ConnectionSettings.h @@ -20,124 +20,13 @@ * under the License. */ -#include "qmf/QmfImportExport.h" -#include "qpid/sys/IntegerTypes.h" - namespace qmf { + namespace engine { + class ConnectionSettings; + } - class ConnectionSettingsImpl; - class Value; - - /** - * Settings for AMQP connections to the broker. - * - * \ingroup qmfapi - */ - class ConnectionSettings { - public: - - ConnectionSettings(const ConnectionSettings& copy); - - /** - * Create a set of default connection settings. - * - * If no further attributes are set, the settings will cause a connection to be made to - * the default broker (on localhost or at a host/port supplied by service discovery) and - * authentication will be the best-available (GSSAPI/Kerberos, Anonymous, Plain with prompts - * for username and password). - */ - QMF_EXTERN ConnectionSettings(); - - /** - * Create a set of connection settings by URL. - * - * @param url Universal resource locator describing the broker address and additional attributes. - * - * The URL is of the form: - * amqp[s]://host[:port][?key=value[&key=value]*] - * - * For example: - * amqp://localhost - * amqp://broker?transport=rdma&authmech=GSSAPI&authservice=qpidd - * amqps://broker?authmech=PLAIN&authuser=guest&authpass=guest - */ - QMF_EXTERN ConnectionSettings(const char* url); - - /** - * Destroy the connection settings object. - */ - QMF_EXTERN ~ConnectionSettings(); - - /** - * Set an attribute to control connection setup. - * - * @param key A null-terminated string that is an attribute name. - * - * @param value Reference to a value to be stored as the attribute. The type of the value - * is specific to the key. - */ - QMF_EXTERN void setAttr(const char* key, const Value& value); - - /** - * Get the value of an attribute. - * - * @param key A null-terminated attribute name. - * - * @return The value associated with the attribute name. - */ - QMF_EXTERN Value getAttr(const char* key) const; - - /** - * Get the attribute string (the portion of the URL following the '?') for the settings. - * - * @return A pointer to the attribute string. If the content of this string needs to be - * available beyond the scope of the calling function, it should be copied. The - * returned pointer may become invalid if the set of attributes is changed. - */ - QMF_EXTERN const char* getAttrString() const; - - /** - * Shortcuts for setting the transport for the connection. - * - * @param port The port value for the connection address. - */ - QMF_EXTERN void transportTcp(uint16_t port = 5672); - QMF_EXTERN void transportSsl(uint16_t port = 5671); - QMF_EXTERN void transportRdma(uint16_t port = 5672); - - /** - * Shortcuts for setting authentication mechanisms. - * - * @param username Null-terminated authentication user name. - * - * @param password Null-terminated authentication password. - * - * @param serviceName Null-terminated GSSAPI service name (Kerberos service principal) - * - * @param minSsf Minimum security factor for connections. 0 = encryption not required. - * - * @param maxSsf Maximum security factor for connections. 0 = encryption not permitted. - */ - QMF_EXTERN void authAnonymous(const char* username = 0); - QMF_EXTERN void authPlain(const char* username = 0, const char* password = 0); - QMF_EXTERN void authGssapi(const char* serviceName, uint32_t minSsf = 0, uint32_t maxSsf = 256); - - /** - * Shortcut for setting connection retry attributes. - * - * @param delayMin Minimum delay (in seconds) between connection attempts. - * - * @param delaxMax Maximum delay (in seconds) between connection attempts. - * - * @param delayFactor Factor to multiply the delay by between failed connection attempts. - */ - QMF_EXTERN void setRetry(int delayMin = 1, int delayMax = 128, int delayFactor = 2); - - private: - friend class ResilientConnectionImpl; - ConnectionSettingsImpl* impl; - }; - + typedef class engine::ConnectionSettings ConnectionSettings; } #endif + diff --git a/qpid/cpp/include/qmf/ConsoleObject.h b/qpid/cpp/include/qmf/ConsoleObject.h new file mode 100644 index 0000000000..acbc285e05 --- /dev/null +++ b/qpid/cpp/include/qmf/ConsoleObject.h @@ -0,0 +1,94 @@ +#ifndef _QmfConsoleObject_ +#define _QmfConsoleObject_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/QmfImportExport.h" + +namespace qmf { + + class ConsoleObjectImpl; + class SchemaObjectClass; + class ObjectId; + class Value; + + /** + * ConsoleObject is an extension of Object with console-specific methods added. + * + * \ingroup qmfapi + */ + class ConsoleObject : public Object { + public: + /** + * Create a new Object of a specific type. + * + * @param type Pointer to the schema class to use as a type for this object. + */ + QMF_EXTERN ConsoleObject(const SchemaObjectClass* type); + + /** + * Destroy the object. + */ + QMF_EXTERN virtual ~ConsoleObject(); + + /** + * + */ + QMF_EXTERN const SchemaObjectClass* getSchema() const; + + /** + * + */ + QMF_EXTERN ObjectId* getObjectId() const; + + /** + * + */ + QMF_EXTERN uint64_t getCurrentTime() const; + QMF_EXTERN uint64_t getCreateTime() const; + QMF_EXTERN uint64_t getDeleteTime() const; + + /** + * + */ + QMF_EXTERN bool isDeleted() const; + + /** + * + */ + QMF_EXTERN void update(); + + /** + * + */ + QMF_EXTERN void invokeMethod(const char* name, const Value* arguments, MethodResult& result); + + /** + * + */ + QMF_EXTERN uint32_t invokeMethodAsync(const char* name, const Value* arguments = 0); + + private: + ConsoleObjectImpl* impl; + }; + +} + +#endif diff --git a/qpid/cpp/include/qmf/engine/Agent.h b/qpid/cpp/include/qmf/engine/Agent.h new file mode 100644 index 0000000000..71abf82254 --- /dev/null +++ b/qpid/cpp/include/qmf/engine/Agent.h @@ -0,0 +1,209 @@ +#ifndef _QmfEngineAgent_ +#define _QmfEngineAgent_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace qmf { +namespace engine { + + /** + * AgentEvent + * + * This structure represents a QMF event coming from the agent to + * the application. + */ + struct AgentEvent { + enum EventKind { + GET_QUERY = 1, + START_SYNC = 2, + END_SYNC = 3, + METHOD_CALL = 4, + DECLARE_QUEUE = 5, + DELETE_QUEUE = 6, + BIND = 7, + UNBIND = 8, + SETUP_COMPLETE = 9 + }; + + EventKind kind; + uint32_t sequence; // Protocol sequence (for all kinds) + char* authUserId; // Authenticated user ID (for all kinds) + char* authToken; // Authentication token if issued (for all kinds) + char* name; // Name of the method/sync query + // (METHOD_CALL, START_SYNC, END_SYNC, DECLARE_QUEUE, BIND, UNBIND) + Object* object; // Object involved in method call (METHOD_CALL) + ObjectId* objectId; // ObjectId for method call (METHOD_CALL) + Query* query; // Query parameters (GET_QUERY, START_SYNC) + Value* arguments; // Method parameters (METHOD_CALL) + char* exchange; // Exchange for bind (BIND, UNBIND) + char* bindingKey; // Key for bind (BIND, UNBIND) + const SchemaObjectClass* objectClass; // (METHOD_CALL) + }; + + class AgentImpl; + + /** + * Agent - Protocol engine for the QMF agent + */ + class Agent { + public: + Agent(char* label, bool internalStore=true); + ~Agent(); + + /** + * Configure the directory path for storing persistent data. + *@param path Null-terminated string containing a directory path where files can be + * created, written, and read. If NULL, no persistent storage will be + * attempted. + */ + void setStoreDir(const char* path); + + /** + * Configure the directory path for files transferred over QMF. + *@param path Null-terminated string containing a directory path where files can be + * created, deleted, written, and read. If NULL, file transfers shall not + * be permitted. + */ + void setTransferDir(const char* path); + + /** + * Pass messages received from the AMQP session to the Agent engine. + *@param message AMQP messages received on the agent session. + */ + void handleRcvMessage(Message& message); + + /** + * Get the next message to be sent to the AMQP network. + *@param item The Message structure describing the message to be produced. + *@return true if the Message is valid, false if there are no messages to send. + */ + bool getXmtMessage(Message& item) const; + + /** + * Remove and discard one message from the head of the transmit queue. + */ + void popXmt(); + + /** + * Get the next application event from the agent engine. + *@param event The event iff the return value is true + *@return true if event is valid, false if there are no events to process + */ + bool getEvent(AgentEvent& event) const; + + /** + * Remove and discard one event from the head of the event queue. + */ + void popEvent(); + + /** + * A new AMQP session has been established for Agent communication. + */ + void newSession(); + + /** + * Start the QMF Agent protocol. This should be invoked after a SETUP_COMPLETE event + * is received from the Agent engine. + */ + void startProtocol(); + + /** + * This method is called periodically so the agent can supply a heartbeat. + */ + void heartbeat(); + + /** + * Respond to a method request. + *@param sequence The sequence number from the method request event. + *@param status The method's completion status. + *@param text Status text ("OK" or an error message) + *@param arguments The list of output arguments from the method call. + */ + void methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments); + + /** + * Send a content indication to the QMF bus. This is only needed for objects that are + * managed by the application. This is *NOT* needed for objects managed by the Agent + * (inserted using addObject). + *@param sequence The sequence number of the GET request or the SYNC_START request. + *@param object The object (annotated with "changed" flags) for publication. + *@param prop If true, changed object properties are transmitted. + *@param stat If true, changed object statistics are transmitted. + */ + void queryResponse(uint32_t sequence, Object& object, bool prop = true, bool stat = true); + + /** + * Indicate the completion of a query. This is not used for SYNC_START requests. + *@param sequence The sequence number of the GET request. + */ + void queryComplete(uint32_t sequence); + + /** + * Register a schema class with the Agent. + *@param cls A SchemaObejctClass object that defines data managed by the agent. + */ + void registerClass(SchemaObjectClass* cls); + + /** + * Register a schema class with the Agent. + *@param cls A SchemaEventClass object that defines events sent by the agent. + */ + void registerClass(SchemaEventClass* cls); + + /** + * Give an object to the Agent for storage and management. Once added, the agent takes + * responsibility for the life cycle of the object. + *@param obj The object to be managed by the Agent. + *@param persistId A unique non-zero value if the object-id is to be persistent. + *@return The objectId of the managed object. + */ + const ObjectId* addObject(Object& obj, uint64_t persistId); + // const ObjectId* addObject(Object& obj, uint32_t persistIdLo, uint32_t persistIdHi); + + /** + * Allocate an object-id for an object that will be managed by the application. + *@param persistId A unique non-zero value if the object-id is to be persistent. + *@return The objectId structure for the allocated ID. + */ + const ObjectId* allocObjectId(uint64_t persistId); + const ObjectId* allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi); + + /** + * Raise an event into the QMF network.. + *@param event The event object for the event to be raised. + */ + void raiseEvent(Event& event); + + private: + AgentImpl* impl; + }; +} +} + +#endif + diff --git a/qpid/cpp/include/qmf/engine/ConnectionSettings.h b/qpid/cpp/include/qmf/engine/ConnectionSettings.h new file mode 100644 index 0000000000..36312400b1 --- /dev/null +++ b/qpid/cpp/include/qmf/engine/ConnectionSettings.h @@ -0,0 +1,150 @@ +#ifndef _QmfEngineConnectionSettings_ +#define _QmfEngineConnectionSettings_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/engine/QmfEngineImportExport.h" +#include "qpid/sys/IntegerTypes.h" + +namespace qmf { +namespace engine { + + class ConnectionSettingsImpl; + class Value; + + /** + * Settings for AMQP connections to the broker. + * + * \ingroup qmfapi + */ + class ConnectionSettings { + public: + + /** + * Create a set of default connection settings. + * + * If no further attributes are set, the settings will cause a connection to be made to + * the default broker (on localhost or at a host/port supplied by service discovery) and + * authentication will be the best-available (GSSAPI/Kerberos, Anonymous, Plain with prompts + * for username and password). + */ + QMFE_EXTERN ConnectionSettings(); + + /** + * Create a set of connection settings by URL. + * + * @param url Universal resource locator describing the broker address and additional attributes. + * + * The URL is of the form: + * amqp[s]://host[:port][?key=value[&key=value]*] + * + * For example: + * amqp://localhost + * amqp://broker?transport=rdma&authmech=GSSAPI&authservice=qpidd + * amqps://broker?authmech=PLAIN&authuser=guest&authpass=guest + */ + QMFE_EXTERN ConnectionSettings(const char* url); + + /** + * Copy Constructor. + */ + ConnectionSettings(const ConnectionSettings& from); + + /** + * Destroy the connection settings object. + */ + QMFE_EXTERN ~ConnectionSettings(); + + /** + * Set an attribute to control connection setup. + * + * @param key A null-terminated string that is an attribute name. + * + * @param value Reference to a value to be stored as the attribute. The type of the value + * is specific to the key. + * + * @return True if success, False if invalid attribute + */ + QMFE_EXTERN bool setAttr(const char* key, const Value& value); + + /** + * Get the value of an attribute. + * + * @param key A null-terminated attribute name. + * + * @return The value associated with the attribute name. + */ + QMFE_EXTERN Value getAttr(const char* key) const; + + /** + * Get the attribute string (the portion of the URL following the '?') for the settings. + * + * @return A pointer to the attribute string. If the content of this string needs to be + * available beyond the scope of the calling function, it should be copied. The + * returned pointer may become invalid if the set of attributes is changed. + */ + QMFE_EXTERN const char* getAttrString() const; + + /** + * Shortcuts for setting the transport for the connection. + * + * @param port The port value for the connection address. + */ + QMFE_EXTERN void transportTcp(uint16_t port = 5672); + QMFE_EXTERN void transportSsl(uint16_t port = 5671); + QMFE_EXTERN void transportRdma(uint16_t port = 5672); + + /** + * Shortcuts for setting authentication mechanisms. + * + * @param username Null-terminated authentication user name. + * + * @param password Null-terminated authentication password. + * + * @param serviceName Null-terminated GSSAPI service name (Kerberos service principal) + * + * @param minSsf Minimum security factor for connections. 0 = encryption not required. + * + * @param maxSsf Maximum security factor for connections. 0 = encryption not permitted. + */ + QMFE_EXTERN void authAnonymous(const char* username = 0); + QMFE_EXTERN void authPlain(const char* username = 0, const char* password = 0); + QMFE_EXTERN void authGssapi(const char* serviceName, uint32_t minSsf = 0, uint32_t maxSsf = 256); + + /** + * Shortcut for setting connection retry attributes. + * + * @param delayMin Minimum delay (in seconds) between connection attempts. + * + * @param delaxMax Maximum delay (in seconds) between connection attempts. + * + * @param delayFactor Factor to multiply the delay by between failed connection attempts. + */ + QMFE_EXTERN void setRetry(int delayMin = 1, int delayMax = 128, int delayFactor = 2); + + private: + friend class ResilientConnectionImpl; + ConnectionSettingsImpl* impl; + }; + +} +} + +#endif diff --git a/qpid/cpp/include/qmf/engine/Console.h b/qpid/cpp/include/qmf/engine/Console.h new file mode 100644 index 0000000000..ce72360da7 --- /dev/null +++ b/qpid/cpp/include/qmf/engine/Console.h @@ -0,0 +1,235 @@ +#ifndef _QmfEngineConsole_ +#define _QmfEngineConsole_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace qmf { +namespace engine { + + class Console; + struct ConsoleImpl; + class BrokerProxyImpl; + class AgentProxy; + struct AgentProxyImpl; + struct MethodResponseImpl; + struct QueryResponseImpl; + struct QueryContext; + + /** + * + */ + class MethodResponse { + public: + MethodResponse(const MethodResponse& from); + ~MethodResponse(); + uint32_t getStatus() const; + const Value* getException() const; + const Value* getArgs() const; + + private: + friend struct MethodResponseImpl; + friend struct ConsoleImpl; + MethodResponse(MethodResponseImpl* impl); + MethodResponseImpl* impl; + }; + + /** + * + */ + class QueryResponse { + public: + ~QueryResponse(); + uint32_t getStatus() const; + const Value* getException() const; + uint32_t getObjectCount() const; + const Object* getObject(uint32_t idx) const; + + private: + friend struct QueryResponseImpl; + friend struct QueryContext; + QueryResponse(QueryResponseImpl* impl); + QueryResponseImpl *impl; + }; + + /** + * + */ + struct ConsoleEvent { + enum EventKind { + AGENT_ADDED = 1, + AGENT_DELETED = 2, + NEW_PACKAGE = 3, + NEW_CLASS = 4, + OBJECT_UPDATE = 5, + EVENT_RECEIVED = 7, + AGENT_HEARTBEAT = 8 + }; + + EventKind kind; + AgentProxy* agent; // (AGENT_[ADDED|DELETED|HEARTBEAT]) + char* name; // (NEW_PACKAGE) + const SchemaClassKey* classKey; // (NEW_CLASS) + Object* object; // (OBJECT_UPDATE) + void* context; // (OBJECT_UPDATE) + Event* event; // (EVENT_RECEIVED) + uint64_t timestamp; // (AGENT_HEARTBEAT) + QueryResponse* queryResponse; // (QUERY_COMPLETE) + bool hasProps; + bool hasStats; + }; + + /** + * + */ + struct BrokerEvent { + enum EventKind { + BROKER_INFO = 10, + DECLARE_QUEUE = 11, + DELETE_QUEUE = 12, + BIND = 13, + UNBIND = 14, + SETUP_COMPLETE = 15, + STABLE = 16, + QUERY_COMPLETE = 17, + METHOD_RESPONSE = 18 + }; + + EventKind kind; + char* name; // ([DECLARE|DELETE]_QUEUE, [UN]BIND) + char* exchange; // ([UN]BIND) + char* bindingKey; // ([UN]BIND) + void* context; // (QUERY_COMPLETE, METHOD_RESPONSE) + QueryResponse* queryResponse; // (QUERY_COMPLETE) + MethodResponse* methodResponse; // (METHOD_RESPONSE) + }; + + /** + * + */ + class AgentProxy { + public: + ~AgentProxy(); + const char* getLabel() const; + uint32_t getBrokerBank() const; + uint32_t getAgentBank() const; + + private: + friend struct StaticContext; + friend struct QueryContext; + friend struct AgentProxyImpl; + friend class BrokerProxyImpl; + AgentProxy(AgentProxyImpl* impl); + AgentProxyImpl* impl; + }; + + /** + * + */ + class BrokerProxy { + public: + BrokerProxy(Console& console); + ~BrokerProxy(); + + void sessionOpened(SessionHandle& sh); + void sessionClosed(); + void startProtocol(); + + void handleRcvMessage(Message& message); + bool getXmtMessage(Message& item) const; + void popXmt(); + + bool getEvent(BrokerEvent& event) const; + void popEvent(); + + uint32_t agentCount() const; + const AgentProxy* getAgent(uint32_t idx) const; + void sendQuery(const Query& query, void* context, const AgentProxy* agent = 0); + + private: + friend struct ConsoleImpl; + friend struct StaticContext; + BrokerProxyImpl* impl; + }; + + // TODO - move this to a public header + struct ConsoleSettings { + bool rcvObjects; + bool rcvEvents; + bool rcvHeartbeats; + bool userBindings; + + ConsoleSettings() : + rcvObjects(true), + rcvEvents(true), + rcvHeartbeats(true), + userBindings(false) {} + }; + + class Console { + public: + Console(const ConsoleSettings& settings = ConsoleSettings()); + ~Console(); + + bool getEvent(ConsoleEvent& event) const; + void popEvent(); + + void addConnection(BrokerProxy& broker, void* context); + void delConnection(BrokerProxy& broker); + + uint32_t packageCount() const; + const char* getPackageName(uint32_t idx) const; + + uint32_t classCount(const char* packageName) const; + const SchemaClassKey* getClass(const char* packageName, uint32_t idx) const; + + ClassKind getClassKind(const SchemaClassKey* key) const; + const SchemaObjectClass* getObjectClass(const SchemaClassKey* key) const; + const SchemaEventClass* getEventClass(const SchemaClassKey* key) const; + + void bindPackage(const char* packageName); + void bindClass(const SchemaClassKey* key); + void bindClass(const char* packageName, const char* className); + + /* + void startSync(const Query& query, void* context, SyncQuery& sync); + void touchSync(SyncQuery& sync); + void endSync(SyncQuery& sync); + */ + + private: + friend class BrokerProxyImpl; + friend struct AgentProxyImpl; + friend struct StaticContext; + ConsoleImpl* impl; + }; +} +} + +#endif + diff --git a/qpid/cpp/include/qmf/engine/Event.h b/qpid/cpp/include/qmf/engine/Event.h new file mode 100644 index 0000000000..50ab5c1200 --- /dev/null +++ b/qpid/cpp/include/qmf/engine/Event.h @@ -0,0 +1,32 @@ +#ifndef _QmfEngineEvent_ +#define _QmfEngineEvent_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +namespace qmf { +namespace engine { + + class Event { + }; +} +} + +#endif + diff --git a/qpid/cpp/include/qmf/engine/Message.h b/qpid/cpp/include/qmf/engine/Message.h new file mode 100644 index 0000000000..1e95cc6afe --- /dev/null +++ b/qpid/cpp/include/qmf/engine/Message.h @@ -0,0 +1,41 @@ +#ifndef _QmfEngineMessage_ +#define _QmfEngineMessage_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qpid/sys/IntegerTypes.h" + +namespace qmf { +namespace engine { + + struct Message { + char* body; + uint32_t length; + char* destination; + char* routingKey; + char* replyExchange; + char* replyKey; + char* userId; + }; + +} +} + +#endif diff --git a/qpid/cpp/include/qmf/engine/Object.h b/qpid/cpp/include/qmf/engine/Object.h new file mode 100644 index 0000000000..ad67cfdb95 --- /dev/null +++ b/qpid/cpp/include/qmf/engine/Object.h @@ -0,0 +1,56 @@ +#ifndef _QmfEngineObject_ +#define _QmfEngineObject_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include + +namespace qmf { +namespace engine { + + struct ObjectImpl; + class Object { + public: + Object(const SchemaObjectClass* type); + Object(const Object& from); + virtual ~Object(); + + void destroy(); + const ObjectId* getObjectId() const; + void setObjectId(ObjectId* oid); + const SchemaObjectClass* getClass() const; + Value* getValue(const char* key) const; + void invokeMethod(const char* methodName, const Value* inArgs, void* context) const; + bool isDeleted() const; + void merge(const Object& from); + + private: + friend struct ObjectImpl; + friend class AgentImpl; + Object(ObjectImpl* impl); + ObjectImpl* impl; + }; +} +} + +#endif + diff --git a/qpid/cpp/include/qmf/engine/ObjectId.h b/qpid/cpp/include/qmf/engine/ObjectId.h new file mode 100644 index 0000000000..2055972c00 --- /dev/null +++ b/qpid/cpp/include/qmf/engine/ObjectId.h @@ -0,0 +1,67 @@ +#ifndef _QmfEngineObjectId_ +#define _QmfEngineObjectId_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +namespace qmf { +namespace engine { + + // TODO: Add to/from string and << operator + + struct ObjectIdImpl; + class ObjectId { + public: + ObjectId(); + ObjectId(const ObjectId& from); + ~ObjectId(); + + uint64_t getObjectNum() const; + uint32_t getObjectNumHi() const; + uint32_t getObjectNumLo() const; + bool isDurable() const; + const char* str() const; + uint8_t getFlags() const; + uint16_t getSequence() const; + uint32_t getBrokerBank() const; + uint32_t getAgentBank() const; + + bool operator==(const ObjectId& other) const; + bool operator<(const ObjectId& other) const; + bool operator>(const ObjectId& other) const; + bool operator<=(const ObjectId& other) const; + bool operator>=(const ObjectId& other) const; + + private: + friend struct ObjectIdImpl; + friend struct ObjectImpl; + friend class BrokerProxyImpl; + friend struct QueryImpl; + friend struct ValueImpl; + friend class AgentImpl; + ObjectId(ObjectIdImpl* impl); + ObjectIdImpl* impl; + }; +} +} + +#endif + diff --git a/qpid/cpp/include/qmf/engine/QmfEngineImportExport.h b/qpid/cpp/include/qmf/engine/QmfEngineImportExport.h new file mode 100644 index 0000000000..70164c749f --- /dev/null +++ b/qpid/cpp/include/qmf/engine/QmfEngineImportExport.h @@ -0,0 +1,33 @@ +#ifndef QMF_ENGINE_IMPORT_EXPORT_H +#define QMF_ENGINE_IMPORT_EXPORT_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#if defined(WIN32) && !defined(QPID_DECLARE_STATIC) +# if defined(QMF_EXPORT) || defined (qmfcommon_EXPORTS) +# define QMFE_EXTERN __declspec(dllexport) +# else +# define QMFE_EXTERN __declspec(dllimport) +# endif +#else +# define QMFE_EXTERN +#endif + +#endif diff --git a/qpid/cpp/include/qmf/engine/Query.h b/qpid/cpp/include/qmf/engine/Query.h new file mode 100644 index 0000000000..1b11ea83f2 --- /dev/null +++ b/qpid/cpp/include/qmf/engine/Query.h @@ -0,0 +1,110 @@ +#ifndef _QmfEngineQuery_ +#define _QmfEngineQuery_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +namespace qmf { +namespace engine { + + class Object; + struct QueryElementImpl; + struct QueryImpl; + struct QueryExpressionImpl; + class SchemaClassKey; + + enum ValueOper { + O_EQ = 1, + O_NE = 2, + O_LT = 3, + O_LE = 4, + O_GT = 5, + O_GE = 6, + O_RE_MATCH = 7, + O_RE_NOMATCH = 8 + }; + + struct QueryOperand { + virtual ~QueryOperand() {} + virtual bool evaluate(const Object* object) const = 0; + }; + + struct QueryElement : public QueryOperand { + QueryElement(const char* attrName, const Value* value, ValueOper oper); + QueryElement(QueryElementImpl* impl); + virtual ~QueryElement(); + bool evaluate(const Object* object) const; + + QueryElementImpl* impl; + }; + + enum ExprOper { + E_NOT = 1, + E_AND = 2, + E_OR = 3, + E_XOR = 4 + }; + + struct QueryExpression : public QueryOperand { + QueryExpression(ExprOper oper, const QueryOperand* operand1, const QueryOperand* operand2); + QueryExpression(QueryExpressionImpl* impl); + virtual ~QueryExpression(); + bool evaluate(const Object* object) const; + + QueryExpressionImpl* impl; + }; + + class Query { + public: + Query(const char* className, const char* packageName); + Query(const SchemaClassKey* key); + Query(const ObjectId* oid); + Query(const Query& from); + ~Query(); + + void setSelect(const QueryOperand* criterion); + void setLimit(uint32_t maxResults); + void setOrderBy(const char* attrName, bool decreasing); + + const char* getPackage() const; + const char* getClass() const; + const ObjectId* getObjectId() const; + + bool haveSelect() const; + bool haveLimit() const; + bool haveOrderBy() const; + const QueryOperand* getSelect() const; + uint32_t getLimit() const; + const char* getOrderBy() const; + bool getDecreasing() const; + + private: + friend struct QueryImpl; + friend class BrokerProxyImpl; + Query(QueryImpl* impl); + QueryImpl* impl; + }; +} +} + +#endif + diff --git a/qpid/cpp/include/qmf/engine/ResilientConnection.h b/qpid/cpp/include/qmf/engine/ResilientConnection.h new file mode 100644 index 0000000000..359c8ea6ff --- /dev/null +++ b/qpid/cpp/include/qmf/engine/ResilientConnection.h @@ -0,0 +1,165 @@ +#ifndef _QmfEngineResilientConnection_ +#define _QmfEngineResilientConnection_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include + +namespace qmf { +namespace engine { + + class ResilientConnectionImpl; + + /** + * Represents events that occur, unsolicited, from ResilientConnection. + */ + struct ResilientConnectionEvent { + enum EventKind { + CONNECTED = 1, + DISCONNECTED = 2, + SESSION_CLOSED = 3, + RECV = 4 + }; + + EventKind kind; + void* sessionContext; // SESSION_CLOSED, RECV + char* errorText; // DISCONNECTED, SESSION_CLOSED + Message message; // RECV + }; + + class SessionHandle { + friend class ResilientConnectionImpl; + void* impl; + }; + + /** + * ResilientConnection represents a Qpid connection that is resilient. + * + * Upon creation, ResilientConnection attempts to establish a connection to the + * messaging broker. If it fails, it will continue to retry at an interval that + * increases over time (to a maximum interval). If an extablished connection is + * dropped, a reconnect will be attempted. + */ + class ResilientConnection { + public: + + /** + * Create a new resilient connection. + *@param settings Settings that define how the connection is to be made. + *@param delayMin Minimum delay (in seconds) between retries. + *@param delayMax Maximum delay (in seconds) between retries. + *@param delayFactor Factor to multiply retry delay by after each failure. + */ + ResilientConnection(const ConnectionSettings& settings); + ~ResilientConnection(); + + /** + * Get the connected status of the resilient connection. + *@return true iff the connection is established. + */ + bool isConnected() const; + + /** + * Get the next event (if present) from the connection. + *@param event Returned event if one is available. + *@return true if event is valid, false if there are no more events to handle. + */ + bool getEvent(ResilientConnectionEvent& event); + + /** + * Discard the event on the front of the queue. This should be invoked after processing + * the event from getEvent. + */ + void popEvent(); + + /** + * Create a new AMQP session. + *@param name Unique name for the session. + *@param sessionContext Optional user-context value that will be provided in events + * pertaining to this session. + *@param handle Output handle to be stored and used in subsequent calls pertaining to + * this session. + *@return true iff the session was successfully created. + */ + bool createSession(const char* name, void* sessionContext, SessionHandle& handle); + + /** + * Destroy a created session. + *@param handle SessionHandle returned by createSession. + */ + void destroySession(SessionHandle handle); + + /** + * Send a message into the AMQP broker via a session. + *@param handle The session handle of the session to transmit through. + *@param message The QMF message to transmit. + */ + void sendMessage(SessionHandle handle, Message& message); + + /** + * Declare an exclusive, auto-delete queue for a session. + *@param handle The session handle for the owner of the queue. + *@param queue The name of the queue. + */ + void declareQueue(SessionHandle handle, char* queue); + + /** + * Delete a queue. + *@param handle The session handle for the owner of the queue. + *@param queue The name of the queue. + */ + void deleteQueue(SessionHandle handle, char* queue); + + /** + * Bind a queue to an exchange. + *@param handle The session handle of the session to use for binding. + *@param exchange The name of the exchange for binding. + *@param queue The name of the queue for binding. + *@param key The binding key. + */ + void bind(SessionHandle handle, char* exchange, char* queue, char* key); + + /** + * Remove a binding. + *@param handle The session handle of the session to use for un-binding. + *@param exchange The name of the exchange. + *@param queue The name of the queue. + *@param key The binding key. + */ + void unbind(SessionHandle handle, char* exchange, char* queue, char* key); + + /** + * Establish a file descriptor for event notification. + *@param fd A file descriptor into which the connection shall write a character each + * time an event is enqueued. This fd may be in a pair, the other fd of which + * is used in a select loop to control execution. + */ + void setNotifyFd(int fd); + + private: + ResilientConnectionImpl* impl; + }; +} +} + +#endif + diff --git a/qpid/cpp/include/qmf/engine/Schema.h b/qpid/cpp/include/qmf/engine/Schema.h new file mode 100644 index 0000000000..16f11a83f9 --- /dev/null +++ b/qpid/cpp/include/qmf/engine/Schema.h @@ -0,0 +1,210 @@ +#ifndef _QmfEngineSchema_ +#define _QmfEngineSchema_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +namespace qmf { +namespace engine { + + enum Access { ACCESS_READ_CREATE = 1, ACCESS_READ_WRITE = 2, ACCESS_READ_ONLY = 3 }; + enum Direction { DIR_IN = 1, DIR_OUT = 2, DIR_IN_OUT = 3 }; + enum ClassKind { CLASS_OBJECT = 1, CLASS_EVENT = 2 }; + + struct SchemaArgumentImpl; + struct SchemaMethodImpl; + struct SchemaPropertyImpl; + struct SchemaStatisticImpl; + struct SchemaObjectClassImpl; + struct SchemaEventClassImpl; + struct SchemaClassKeyImpl; + + /** + */ + class SchemaArgument { + public: + SchemaArgument(const char* name, Typecode typecode); + SchemaArgument(const SchemaArgument& from); + ~SchemaArgument(); + void setDirection(Direction dir); + void setUnit(const char* val); + void setDesc(const char* desc); + const char* getName() const; + Typecode getType() const; + Direction getDirection() const; + const char* getUnit() const; + const char* getDesc() const; + + private: + friend struct SchemaArgumentImpl; + friend struct SchemaMethodImpl; + friend struct SchemaEventClassImpl; + SchemaArgument(SchemaArgumentImpl* impl); + SchemaArgumentImpl* impl; + }; + + /** + */ + class SchemaMethod { + public: + SchemaMethod(const char* name); + SchemaMethod(const SchemaMethod& from); + ~SchemaMethod(); + void addArgument(const SchemaArgument* argument); + void setDesc(const char* desc); + const char* getName() const; + const char* getDesc() const; + int getArgumentCount() const; + const SchemaArgument* getArgument(int idx) const; + + private: + friend struct SchemaMethodImpl; + friend struct SchemaObjectClassImpl; + friend class AgentImpl; + SchemaMethod(SchemaMethodImpl* impl); + SchemaMethodImpl* impl; + }; + + /** + */ + class SchemaProperty { + public: + SchemaProperty(const char* name, Typecode typecode); + SchemaProperty(const SchemaProperty& from); + ~SchemaProperty(); + void setAccess(Access access); + void setIndex(bool val); + void setOptional(bool val); + void setUnit(const char* val); + void setDesc(const char* desc); + const char* getName() const; + Typecode getType() const; + Access getAccess() const; + bool isIndex() const; + bool isOptional() const; + const char* getUnit() const; + const char* getDesc() const; + + private: + friend struct SchemaPropertyImpl; + friend struct SchemaObjectClassImpl; + SchemaProperty(SchemaPropertyImpl* impl); + SchemaPropertyImpl* impl; + }; + + /** + */ + class SchemaStatistic { + public: + SchemaStatistic(const char* name, Typecode typecode); + SchemaStatistic(const SchemaStatistic& from); + ~SchemaStatistic(); + void setUnit(const char* val); + void setDesc(const char* desc); + const char* getName() const; + Typecode getType() const; + const char* getUnit() const; + const char* getDesc() const; + + private: + friend struct SchemaStatisticImpl; + friend struct SchemaObjectClassImpl; + SchemaStatistic(SchemaStatisticImpl* impl); + SchemaStatisticImpl* impl; + }; + + /** + */ + class SchemaClassKey { + public: + SchemaClassKey(const SchemaClassKey& from); + ~SchemaClassKey(); + + const char* getPackageName() const; + const char* getClassName() const; + const uint8_t* getHash() const; + const char* asString() const; + + bool operator==(const SchemaClassKey& other) const; + bool operator<(const SchemaClassKey& other) const; + + private: + friend struct SchemaClassKeyImpl; + friend class BrokerProxyImpl; + friend struct ConsoleImpl; + SchemaClassKey(SchemaClassKeyImpl* impl); + SchemaClassKeyImpl* impl; + }; + + /** + */ + class SchemaObjectClass { + public: + SchemaObjectClass(const char* package, const char* name); + SchemaObjectClass(const SchemaObjectClass& from); + ~SchemaObjectClass(); + void addProperty(const SchemaProperty* property); + void addStatistic(const SchemaStatistic* statistic); + void addMethod(const SchemaMethod* method); + + const SchemaClassKey* getClassKey() const; + int getPropertyCount() const; + int getStatisticCount() const; + int getMethodCount() const; + const SchemaProperty* getProperty(int idx) const; + const SchemaStatistic* getStatistic(int idx) const; + const SchemaMethod* getMethod(int idx) const; + + private: + friend struct SchemaObjectClassImpl; + friend class BrokerProxyImpl; + friend class AgentImpl; + SchemaObjectClass(SchemaObjectClassImpl* impl); + SchemaObjectClassImpl* impl; + }; + + /** + */ + class SchemaEventClass { + public: + SchemaEventClass(const char* package, const char* name); + SchemaEventClass(const SchemaEventClass& from); + ~SchemaEventClass(); + void addArgument(const SchemaArgument* argument); + void setDesc(const char* desc); + + const SchemaClassKey* getClassKey() const; + int getArgumentCount() const; + const SchemaArgument* getArgument(int idx) const; + + private: + friend struct SchemaEventClassImpl; + friend class BrokerProxyImpl; + friend class AgentImpl; + SchemaEventClass(SchemaEventClassImpl* impl); + SchemaEventClassImpl* impl; + }; +} +} + +#endif + diff --git a/qpid/cpp/include/qmf/engine/Typecode.h b/qpid/cpp/include/qmf/engine/Typecode.h new file mode 100644 index 0000000000..613f96a483 --- /dev/null +++ b/qpid/cpp/include/qmf/engine/Typecode.h @@ -0,0 +1,53 @@ +#ifndef _QmfEngineTypecode_ +#define _QmfEngineTypecode_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +namespace qmf { +namespace engine { + + enum Typecode { + TYPE_UINT8 = 1, + TYPE_UINT16 = 2, + TYPE_UINT32 = 3, + TYPE_UINT64 = 4, + TYPE_SSTR = 6, + TYPE_LSTR = 7, + TYPE_ABSTIME = 8, + TYPE_DELTATIME = 9, + TYPE_REF = 10, + TYPE_BOOL = 11, + TYPE_FLOAT = 12, + TYPE_DOUBLE = 13, + TYPE_UUID = 14, + TYPE_MAP = 15, + TYPE_INT8 = 16, + TYPE_INT16 = 17, + TYPE_INT32 = 18, + TYPE_INT64 = 19, + TYPE_OBJECT = 20, + TYPE_LIST = 21, + TYPE_ARRAY = 22 + }; +} +} + +#endif + diff --git a/qpid/cpp/include/qmf/engine/Value.h b/qpid/cpp/include/qmf/engine/Value.h new file mode 100644 index 0000000000..8eae382caf --- /dev/null +++ b/qpid/cpp/include/qmf/engine/Value.h @@ -0,0 +1,121 @@ +#ifndef _QmfEngineValue_ +#define _QmfEngineValue_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +namespace qmf { +namespace engine { + + class Object; + struct ValueImpl; + + class Value { + public: + // Value(); + Value(const Value& from); + Value(Typecode t, Typecode arrayType = TYPE_UINT8); + ~Value(); + + Typecode getType() const; + bool isNull() const; + void setNull(); + + bool isObjectId() const; + const ObjectId& asObjectId() const; + void setObjectId(const ObjectId& oid); + + bool isUint() const; + uint32_t asUint() const; + void setUint(uint32_t val); + + bool isInt() const; + int32_t asInt() const; + void setInt(int32_t val); + + bool isUint64() const; + uint64_t asUint64() const; + void setUint64(uint64_t val); + + bool isInt64() const; + int64_t asInt64() const; + void setInt64(int64_t val); + + bool isString() const; + const char* asString() const; + void setString(const char* val); + + bool isBool() const; + bool asBool() const; + void setBool(bool val); + + bool isFloat() const; + float asFloat() const; + void setFloat(float val); + + bool isDouble() const; + double asDouble() const; + void setDouble(double val); + + bool isUuid() const; + const uint8_t* asUuid() const; + void setUuid(const uint8_t* val); + + bool isObject() const; + const Object* asObject() const; + void setObject(Object* val); + + bool isMap() const; + bool keyInMap(const char* key) const; + Value* byKey(const char* key); + const Value* byKey(const char* key) const; + void deleteKey(const char* key); + void insert(const char* key, Value* val); + uint32_t keyCount() const; + const char* key(uint32_t idx) const; + + bool isList() const; + uint32_t listItemCount() const; + Value* listItem(uint32_t idx); + void appendToList(Value* val); + void deleteListItem(uint32_t idx); + + bool isArray() const; + Typecode arrayType() const; + uint32_t arrayItemCount() const; + Value* arrayItem(uint32_t idx); + void appendToArray(Value* val); + void deleteArrayItem(uint32_t idx); + + private: + friend struct ValueImpl; + friend class BrokerProxyImpl; + friend struct ObjectImpl; + friend class AgentImpl; + Value(ValueImpl* impl); + ValueImpl* impl; + }; +} +} + +#endif + diff --git a/qpid/cpp/include/qpid/client/QueueOptions.h b/qpid/cpp/include/qpid/client/QueueOptions.h index 9418cb092d..f8a4963f06 100644 --- a/qpid/cpp/include/qpid/client/QueueOptions.h +++ b/qpid/cpp/include/qpid/client/QueueOptions.h @@ -90,7 +90,22 @@ class QueueOptions: public framing::FieldTable * Turns on event generation for this queue (either enqueue only * or for enqueue and dequeue events); the events can then be * processed by a regsitered broker plugin. + * + * DEPRECATED + * + * This is confusing to anyone who sees only the function call + * and not the variable name / doxygen. Consider the following call: + * + * options.enableQueueEvents(false); + * + * It looks like it disables queue events, but what it really does is + * enable both enqueue and dequeue events. + * + * Use setInt() instead: + * + * options.setInt("qpid.queue_event_generation", 2); */ + QPID_CLIENT_EXTERN void enableQueueEvents(bool enqueueOnly); static QPID_CLIENT_EXTERN const std::string strMaxCountKey; diff --git a/qpid/cpp/include/qpid/messaging/ListContent.h b/qpid/cpp/include/qpid/messaging/ListContent.h new file mode 100644 index 0000000000..1c4e13716d --- /dev/null +++ b/qpid/cpp/include/qpid/messaging/ListContent.h @@ -0,0 +1,90 @@ +#ifndef QPID_MESSAGING_LISTCONTENT_H +#define QPID_MESSAGING_LISTCONTENT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/ClientImportExport.h" +#include "Variant.h" + +namespace qpid { +namespace messaging { + +class ListContentImpl; +class Message; + +/** + * Allows message content to be manipulated as a list. + */ +class ListContent +{ + public: + typedef Variant::List::iterator iterator; + typedef Variant::List::reverse_iterator reverse_iterator; + typedef Variant::List::const_iterator const_iterator; + typedef Variant::List::const_reverse_iterator const_reverse_iterator; + + QPID_CLIENT_EXTERN ListContent(Message&); + QPID_CLIENT_EXTERN ~ListContent(); + + QPID_CLIENT_EXTERN const_iterator begin() const; + QPID_CLIENT_EXTERN iterator begin(); + QPID_CLIENT_EXTERN const_iterator end() const; + QPID_CLIENT_EXTERN iterator end(); + QPID_CLIENT_EXTERN const_reverse_iterator rbegin() const; + QPID_CLIENT_EXTERN reverse_iterator rbegin(); + QPID_CLIENT_EXTERN const_reverse_iterator rend() const; + QPID_CLIENT_EXTERN reverse_iterator rend(); + + QPID_CLIENT_EXTERN bool empty() const; + QPID_CLIENT_EXTERN size_t size() const; + + QPID_CLIENT_EXTERN const Variant& front() const; + QPID_CLIENT_EXTERN Variant& front(); + QPID_CLIENT_EXTERN const Variant& back() const; + QPID_CLIENT_EXTERN Variant& back(); + + QPID_CLIENT_EXTERN void push_front(const Variant&); + QPID_CLIENT_EXTERN void push_back(const Variant&); + + QPID_CLIENT_EXTERN void pop_front(); + QPID_CLIENT_EXTERN void pop_back(); + + QPID_CLIENT_EXTERN iterator insert(iterator position, const Variant&); + QPID_CLIENT_EXTERN void insert(iterator position, size_t n, const Variant&); + QPID_CLIENT_EXTERN iterator erase(iterator position); + QPID_CLIENT_EXTERN iterator erase(iterator first, iterator last); + QPID_CLIENT_EXTERN void clear(); + + QPID_CLIENT_EXTERN void encode(); + + QPID_CLIENT_EXTERN const Variant::List& asList() const; + QPID_CLIENT_EXTERN Variant::List& asList(); + private: + ListContentImpl* impl; + + QPID_CLIENT_EXTERN ListContent& operator=(const ListContent&); +}; + +QPID_CLIENT_EXTERN std::ostream& operator<<(std::ostream& out, const ListContent& m); + +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_LISTCONTENT_H*/ diff --git a/qpid/cpp/include/qpid/messaging/ListView.h b/qpid/cpp/include/qpid/messaging/ListView.h new file mode 100644 index 0000000000..4970a20072 --- /dev/null +++ b/qpid/cpp/include/qpid/messaging/ListView.h @@ -0,0 +1,67 @@ +#ifndef QPID_MESSAGING_LISTVIEW_H +#define QPID_MESSAGING_LISTVIEW_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/client/ClientImportExport.h" +#include "Variant.h" + +namespace qpid { +namespace messaging { + +class ListViewImpl; +class Message; + +/** + * Provides a view of message content as a list + */ +class ListView +{ + public: + typedef Variant::List::const_iterator const_iterator; + typedef Variant::List::const_reverse_iterator const_reverse_iterator; + + QPID_CLIENT_EXTERN ListView(const Message&); + QPID_CLIENT_EXTERN ~ListView(); + QPID_CLIENT_EXTERN ListView& operator=(const ListView&); + + QPID_CLIENT_EXTERN const_iterator begin() const; + QPID_CLIENT_EXTERN const_iterator end() const; + QPID_CLIENT_EXTERN const_reverse_iterator rbegin() const; + QPID_CLIENT_EXTERN const_reverse_iterator rend() const; + + QPID_CLIENT_EXTERN bool empty() const; + QPID_CLIENT_EXTERN size_t size() const; + + QPID_CLIENT_EXTERN const Variant& front() const; + QPID_CLIENT_EXTERN const Variant& back() const; + + QPID_CLIENT_EXTERN const Variant::List& asList() const; + private: + ListViewImpl* impl; +}; + +QPID_CLIENT_EXTERN std::ostream& operator<<(std::ostream& out, const ListView& m); + +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_LISTVIEW_H*/ diff --git a/qpid/cpp/include/qpid/messaging/MapContent.h b/qpid/cpp/include/qpid/messaging/MapContent.h new file mode 100644 index 0000000000..b05cb31295 --- /dev/null +++ b/qpid/cpp/include/qpid/messaging/MapContent.h @@ -0,0 +1,90 @@ +#ifndef QPID_MESSAGING_MAPCONTENT_H +#define QPID_MESSAGING_MAPCONTENT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/client/ClientImportExport.h" +#include "Variant.h" +#include +#include + +namespace qpid { +namespace messaging { + +class MapContentImpl; +class Message; + +/** + * Allows message content to be manipulated as a map + */ +class MapContent +{ + public: + typedef std::string key_type; + typedef std::pair value_type; + typedef std::map::const_iterator const_iterator; + typedef std::map::iterator iterator; + typedef std::map::const_reverse_iterator const_reverse_iterator; + typedef std::map::reverse_iterator reverse_iterator; + + QPID_CLIENT_EXTERN MapContent(Message&); + QPID_CLIENT_EXTERN ~MapContent(); + + QPID_CLIENT_EXTERN const_iterator begin() const; + QPID_CLIENT_EXTERN const_iterator end() const; + QPID_CLIENT_EXTERN const_reverse_iterator rbegin() const; + QPID_CLIENT_EXTERN const_reverse_iterator rend() const; + QPID_CLIENT_EXTERN iterator begin(); + QPID_CLIENT_EXTERN iterator end(); + QPID_CLIENT_EXTERN reverse_iterator rbegin(); + QPID_CLIENT_EXTERN reverse_iterator rend(); + + QPID_CLIENT_EXTERN bool empty() const; + QPID_CLIENT_EXTERN size_t size() const; + + QPID_CLIENT_EXTERN const_iterator find(const key_type&) const; + QPID_CLIENT_EXTERN iterator find(const key_type&); + QPID_CLIENT_EXTERN const Variant& operator[](const key_type&) const; + QPID_CLIENT_EXTERN Variant& operator[](const key_type&); + + QPID_CLIENT_EXTERN std::pair insert(const value_type&); + QPID_CLIENT_EXTERN iterator insert(iterator position, const value_type&); + QPID_CLIENT_EXTERN void erase(iterator position); + QPID_CLIENT_EXTERN void erase(iterator first, iterator last); + QPID_CLIENT_EXTERN size_t erase(const key_type&); + QPID_CLIENT_EXTERN void clear(); + + QPID_CLIENT_EXTERN void encode(); + + QPID_CLIENT_EXTERN const std::map& asMap() const; + QPID_CLIENT_EXTERN std::map& asMap(); + private: + MapContentImpl* impl; + + QPID_CLIENT_EXTERN MapContent& operator=(const MapContent&); +}; + +QPID_CLIENT_EXTERN std::ostream& operator<<(std::ostream& out, const MapContent& m); + +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_MAPCONTENT_H*/ diff --git a/qpid/cpp/include/qpid/messaging/MapView.h b/qpid/cpp/include/qpid/messaging/MapView.h new file mode 100644 index 0000000000..910dfca5c2 --- /dev/null +++ b/qpid/cpp/include/qpid/messaging/MapView.h @@ -0,0 +1,70 @@ +#ifndef QPID_MESSAGING_MAPVIEW_H +#define QPID_MESSAGING_MAPVIEW_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/ClientImportExport.h" +#include "Variant.h" +#include +#include + +namespace qpid { +namespace messaging { + +class MapViewImpl; +class Message; + +/** + * Provides a view of message content as a list + */ +class MapView +{ + public: + typedef std::string key_type; + typedef std::pair value_type; + typedef std::map::const_iterator const_iterator; + typedef std::map::const_reverse_iterator const_reverse_iterator; + + QPID_CLIENT_EXTERN MapView(const Message&); + QPID_CLIENT_EXTERN ~MapView(); + QPID_CLIENT_EXTERN MapView& operator=(const MapView&); + + QPID_CLIENT_EXTERN const_iterator begin() const; + QPID_CLIENT_EXTERN const_iterator end() const; + QPID_CLIENT_EXTERN const_reverse_iterator rbegin() const; + QPID_CLIENT_EXTERN const_reverse_iterator rend() const; + + QPID_CLIENT_EXTERN bool empty() const; + QPID_CLIENT_EXTERN size_t size() const; + + QPID_CLIENT_EXTERN const_iterator find(const key_type&) const; + QPID_CLIENT_EXTERN const Variant& operator[](const key_type&) const; + + QPID_CLIENT_EXTERN const std::map& asMap() const; + private: + MapViewImpl* impl; +}; + +QPID_CLIENT_EXTERN std::ostream& operator<<(std::ostream& out, const MapView& m); + +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_MAPVIEW_H*/ diff --git a/qpid/cpp/include/qpid/messaging/Message.h b/qpid/cpp/include/qpid/messaging/Message.h index e68d8a1141..4477d5a2e9 100644 --- a/qpid/cpp/include/qpid/messaging/Message.h +++ b/qpid/cpp/include/qpid/messaging/Message.h @@ -24,7 +24,6 @@ #include #include "qpid/messaging/Variant.h" -#include "qpid/messaging/MessageContent.h" #include "qpid/client/ClientImportExport.h" namespace qpid { @@ -62,22 +61,11 @@ class Message QPID_CLIENT_EXTERN const VariantMap& getHeaders() const; QPID_CLIENT_EXTERN VariantMap& getHeaders(); - QPID_CLIENT_EXTERN const std::string& getBytes() const; - QPID_CLIENT_EXTERN std::string& getBytes(); - QPID_CLIENT_EXTERN void setBytes(const std::string&); - QPID_CLIENT_EXTERN void setBytes(const char* chars, size_t count); - QPID_CLIENT_EXTERN const char* getRawContent() const; - QPID_CLIENT_EXTERN size_t getContentSize() const; - - - QPID_CLIENT_EXTERN MessageContent& getContent(); - QPID_CLIENT_EXTERN const MessageContent& getContent() const; - QPID_CLIENT_EXTERN void setContent(const std::string& s); - QPID_CLIENT_EXTERN void setContent(const Variant::Map&); - QPID_CLIENT_EXTERN void setContent(const Variant::List&); - - QPID_CLIENT_EXTERN void encode(Codec&); - QPID_CLIENT_EXTERN void decode(Codec&); + QPID_CLIENT_EXTERN const std::string& getContent() const; + QPID_CLIENT_EXTERN std::string& getContent(); + QPID_CLIENT_EXTERN void setContent(const std::string&); + QPID_CLIENT_EXTERN void setContent(const char* chars, size_t count); + QPID_CLIENT_EXTERN void getContent(std::pair& content) const; private: MessageImpl* impl; diff --git a/qpid/cpp/include/qpid/messaging/MessageContent.h b/qpid/cpp/include/qpid/messaging/MessageContent.h deleted file mode 100644 index 7c3a636c07..0000000000 --- a/qpid/cpp/include/qpid/messaging/MessageContent.h +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef QPID_MESSAGING_MESSAGECONTENT_H -#define QPID_MESSAGING_MESSAGECONTENT_H - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include "qpid/messaging/Variant.h" -#include -#include "qpid/client/ClientImportExport.h" - -namespace qpid { -namespace messaging { - -/** - * - */ -class MessageContent -{ - public: - QPID_CLIENT_EXTERN virtual ~MessageContent() {} - - virtual const std::string& asString() const = 0; - virtual std::string& asString() = 0; - - virtual const char* asChars() const = 0; - virtual size_t size() const = 0; - - virtual const Variant::Map& asMap() const = 0; - virtual Variant::Map& asMap() = 0; - virtual bool isMap() const = 0; - - virtual const Variant::List& asList() const = 0; - virtual Variant::List& asList() = 0; - virtual bool isList() const = 0; - - virtual void clear() = 0; - - virtual Variant& operator[](const std::string&) = 0; - - - virtual std::ostream& print(std::ostream& out) const = 0; - - - //operator<< for variety of types... (is this a good idea?) - virtual MessageContent& operator<<(const std::string&) = 0; - virtual MessageContent& operator<<(const char*) = 0; - virtual MessageContent& operator<<(bool) = 0; - virtual MessageContent& operator<<(int8_t) = 0; - virtual MessageContent& operator<<(int16_t) = 0; - virtual MessageContent& operator<<(int32_t) = 0; - virtual MessageContent& operator<<(int64_t) = 0; - virtual MessageContent& operator<<(uint8_t) = 0; - virtual MessageContent& operator<<(uint16_t) = 0; - virtual MessageContent& operator<<(uint32_t) = 0; - virtual MessageContent& operator<<(uint64_t) = 0; - virtual MessageContent& operator<<(double) = 0; - virtual MessageContent& operator<<(float) = 0; - - //assignment from string, map and list - virtual MessageContent& operator=(const std::string&) = 0; - virtual MessageContent& operator=(const char*) = 0; - virtual MessageContent& operator=(const Variant::Map&) = 0; - virtual MessageContent& operator=(const Variant::List&) = 0; - - private: -}; - -QPID_CLIENT_EXTERN std::ostream& operator<<(std::ostream& out, const MessageContent& content); - -}} // namespace qpid::messaging - -#endif /*!QPID_MESSAGING_MESSAGECONTENT_H*/ diff --git a/qpid/cpp/include/qpid/sys/posix/PrivatePosix.h b/qpid/cpp/include/qpid/sys/posix/PrivatePosix.h index 6ffd3d8383..79cb950275 100644 --- a/qpid/cpp/include/qpid/sys/posix/PrivatePosix.h +++ b/qpid/cpp/include/qpid/sys/posix/PrivatePosix.h @@ -27,6 +27,7 @@ struct timespec; struct timeval; +struct addrinfo; namespace qpid { namespace sys { @@ -36,6 +37,10 @@ struct timespec& toTimespec(struct timespec& ts, const Duration& t); struct timeval& toTimeval(struct timeval& tv, const Duration& t); Duration toTime(const struct timespec& ts); +// Private SocketAddress details +class SocketAddress; +const struct addrinfo& getAddrInfo(const SocketAddress&); + // Private fd related implementation details class IOHandlePrivate { public: diff --git a/qpid/cpp/include/qpid/sys/windows/IntegerTypes.h b/qpid/cpp/include/qpid/sys/windows/IntegerTypes.h index 47b1d16a76..7b2c57ad8e 100755 --- a/qpid/cpp/include/qpid/sys/windows/IntegerTypes.h +++ b/qpid/cpp/include/qpid/sys/windows/IntegerTypes.h @@ -21,6 +21,8 @@ * */ +#include /* Windows system types */ + typedef unsigned char uint8_t; typedef char int8_t; typedef unsigned short uint16_t; @@ -32,7 +34,7 @@ typedef __int64 int64_t; // Visual Studio doesn't define other common types, so set them up here too. typedef int pid_t; -typedef int ssize_t; +typedef SSIZE_T ssize_t; typedef unsigned int uint; #endif /*!QPID_SYS_WINDOWS_INTEGERTYPES_H*/ diff --git a/qpid/cpp/managementgen/CMakeLists.txt b/qpid/cpp/managementgen/CMakeLists.txt index 8d053e3e08..2511b745a3 100644 --- a/qpid/cpp/managementgen/CMakeLists.txt +++ b/qpid/cpp/managementgen/CMakeLists.txt @@ -20,18 +20,7 @@ project(qpidc-qmfgen) cmake_minimum_required(VERSION 2.4.0 FATAL_ERROR) install(PROGRAMS qmf-gen DESTINATION managementgen - COMPONENT all-source) -install(FILES qmfgen/__init__.py - qmfgen/generate.py - qmfgen/schema.py - qmfgen/templates/Args.h - qmfgen/templates/Class.cpp - qmfgen/templates/Class.h - qmfgen/templates/Event.cpp - qmfgen/templates/Event.h - qmfgen/templates/Makefile.mk - qmfgen/templates/Package.cpp - qmfgen/templates/Package.h - qmfgen/management-types.xml - DESTINATION managementgen - COMPONENT all-source) + COMPONENT ${QPID_COMPONENT_QMF}) +install(DIRECTORY qmfgen DESTINATION managementgen + COMPONENT ${QPID_COMPONENT_QMF} + PATTERN ".svn" EXCLUDE PATTERN "*.pyc" EXCLUDE) diff --git a/qpid/cpp/packaging/NSIS/qpid-icon.ico b/qpid/cpp/packaging/NSIS/qpid-icon.ico new file mode 100644 index 0000000000..112f5d8f1f Binary files /dev/null and b/qpid/cpp/packaging/NSIS/qpid-icon.ico differ diff --git a/qpid/cpp/packaging/NSIS/qpid-icon.png b/qpid/cpp/packaging/NSIS/qpid-icon.png new file mode 100644 index 0000000000..d9bcc5657f Binary files /dev/null and b/qpid/cpp/packaging/NSIS/qpid-icon.png differ diff --git a/qpid/cpp/packaging/NSIS/qpid-install-banner.bmp b/qpid/cpp/packaging/NSIS/qpid-install-banner.bmp new file mode 100644 index 0000000000..1dac04c685 Binary files /dev/null and b/qpid/cpp/packaging/NSIS/qpid-install-banner.bmp differ diff --git a/qpid/cpp/packaging/NSIS/qpid-install-banner.png b/qpid/cpp/packaging/NSIS/qpid-install-banner.png new file mode 100644 index 0000000000..be70d02ee6 Binary files /dev/null and b/qpid/cpp/packaging/NSIS/qpid-install-banner.png differ diff --git a/qpid/cpp/rubygen/framing.0-10/structs.rb b/qpid/cpp/rubygen/framing.0-10/structs.rb index 809e453381..c3684aea66 100755 --- a/qpid/cpp/rubygen/framing.0-10/structs.rb +++ b/qpid/cpp/rubygen/framing.0-10/structs.rb @@ -381,9 +381,10 @@ EOS else inheritance = ": public AMQMethodBody" end + else + public_api("qpid/framing/#{classname}.h") # Non-method structs are public end - public_api("qpid/framing/#{classname}.h") h_file("qpid/framing/#{classname}.h") { if (s.kind_of? AmqpMethod) gen < API/ABI version. Bump this if the interface changes +# REVISION => Version of underlying implementation. +# Bump if implementation changes but API/ABI doesn't +# AGE => Number of API/ABI versions this is backward compatible with +set (qmf_version 1.0.0) +set (qmfengine_version 1.0.0) + +set (qmf_SOURCES qpid/agent/ManagementAgentImpl.cpp qpid/agent/ManagementAgentImpl.h ) -add_library (qmfagent SHARED ${qmfagent_SOURCES}) -target_link_libraries (qmfagent qmfcommon) -set_target_properties (qmfagent PROPERTIES - VERSION ${qpidc_version}) - -set (qmfcommon_SOURCES - qmf/ConnectionSettingsImpl.cpp - qmf/ConnectionSettingsImpl.h - qmf/ConsoleEngine.h - qmf/Event.h - qmf/Message.h - qmf/MessageImpl.cpp - qmf/MessageImpl.h - qmf/Object.h - qmf/ObjectId.h - qmf/ObjectIdImpl.cpp - qmf/ObjectIdImpl.h - qmf/ObjectImpl.cpp - qmf/ObjectImpl.h - qmf/Query.h - qmf/QueryImpl.cpp - qmf/QueryImpl.h - qmf/ResilientConnection.cpp - qmf/ResilientConnection.h - qmf/Schema.h - qmf/SchemaImpl.cpp - qmf/SchemaImpl.h - qmf/Typecode.h - qmf/Value.h - qmf/ValueImpl.cpp - qmf/ValueImpl.h +add_library (qmf SHARED ${qmf_SOURCES}) +target_link_libraries (qmf qmfengine) +set_target_properties (qmf PROPERTIES + VERSION ${qmf_version}) +install (TARGETS qmf LIBRARY + DESTINATION ${QPID_INSTALL_LIBDIR} + COMPONENT ${QPID_COMPONENT_QMF}) + +set (qmfengine_SOURCES + qmf/engine/Agent.cpp + qmf/engine/BrokerProxyImpl.cpp + qmf/engine/BrokerProxyImpl.h + qmf/engine/ConnectionSettingsImpl.cpp + qmf/engine/ConnectionSettingsImpl.h + qmf/engine/ConsoleImpl.cpp + qmf/engine/ConsoleImpl.h + qmf/engine/MessageImpl.cpp + qmf/engine/MessageImpl.h + qmf/engine/ObjectIdImpl.cpp + qmf/engine/ObjectIdImpl.h + qmf/engine/ObjectImpl.cpp + qmf/engine/ObjectImpl.h + qmf/engine/Protocol.cpp + qmf/engine/Protocol.h + qmf/engine/QueryImpl.cpp + qmf/engine/QueryImpl.h + qmf/engine/ResilientConnection.cpp + qmf/engine/SequenceManager.cpp + qmf/engine/SequenceManager.h + qmf/engine/SchemaImpl.cpp + qmf/engine/SchemaImpl.h + qmf/engine/ValueImpl.cpp + qmf/engine/ValueImpl.h ) -add_library (qmfcommon SHARED ${qmfcommon_SOURCES}) -target_link_libraries (qmfcommon qpidclient) -set_target_properties (qmfcommon PROPERTIES - VERSION ${qpidc_version}) +add_library (qmfengine SHARED ${qmfengine_SOURCES}) +target_link_libraries (qmfengine qpidclient) +set_target_properties (qmfengine PROPERTIES + VERSION ${qmfengine_version}) +install (TARGETS qmfengine + DESTINATION ${QPID_INSTALL_LIBDIR} + COMPONENT ${QPID_COMPONENT_QMF}) # QMF console library #module_hdr += \ @@ -737,6 +855,9 @@ add_library (qmfconsole SHARED ${qmfconsole_SOURCES}) target_link_libraries (qmfconsole qpidclient) set_target_properties (qmfconsole PROPERTIES VERSION ${qpidc_version}) +install (TARGETS qmfconsole + DESTINATION ${QPID_INSTALL_LIBDIR} + COMPONENT ${QPID_COMPONENT_QMF}) # A queue event listener plugin that creates messages on a replication # queue corresponding to enqueue and dequeue events: @@ -752,6 +873,9 @@ if (CMAKE_COMPILER_IS_GNUCXX) set_target_properties(replicating_listener PROPERTIES LINK_FLAGS -Wl,--no-undefined) endif (CMAKE_COMPILER_IS_GNUCXX) +install (TARGETS replicating_listener + DESTINATION ${QPIDD_MODULE_DIR} + COMPONENT ${QPID_COMPONENT_BROKER}) # A custom exchange plugin that allows an exchange to be created that # can process the messages from a replication queue (populated on the @@ -769,6 +893,9 @@ if (CMAKE_COMPILER_IS_GNUCXX) set_target_properties(replicating_exchange PROPERTIES LINK_FLAGS -Wl,--no-undefined) endif (CMAKE_COMPILER_IS_GNUCXX) +install (TARGETS replicating_exchange + DESTINATION ${QPIDD_MODULE_DIR} + COMPONENT ${QPID_COMPONENT_BROKER}) # This is only really needed until all the trunk builds (Linux, UNIX, Windows) # are all on cmake only. This is because cmake builds always have a config.h diff --git a/qpid/cpp/src/Makefile.am b/qpid/cpp/src/Makefile.am index 4988f3f031..a6b90d7bde 100644 --- a/qpid/cpp/src/Makefile.am +++ b/qpid/cpp/src/Makefile.am @@ -144,6 +144,7 @@ libqpidcommon_la_SOURCES += \ qpid/log/posix/SinkOptions.cpp \ qpid/sys/posix/IOHandle.cpp \ qpid/sys/posix/Socket.cpp \ + qpid/sys/posix/SocketAddress.cpp \ qpid/sys/posix/AsynchIO.cpp \ qpid/sys/posix/FileSysDir.cpp \ qpid/sys/posix/LockFile.cpp \ @@ -456,6 +457,7 @@ libqpidcommon_la_SOURCES += \ qpid/sys/Shlib.h \ qpid/sys/ShutdownHandler.h \ qpid/sys/Socket.h \ + qpid/sys/SocketAddress.h \ qpid/sys/StateMonitor.h \ qpid/sys/TimeoutHandler.h \ qpid/sys/Timer.cpp \ @@ -686,6 +688,10 @@ libqpidclient_la_SOURCES = \ qpid/messaging/Address.cpp \ qpid/messaging/Connection.cpp \ qpid/messaging/Filter.cpp \ + qpid/messaging/ListContent.cpp \ + qpid/messaging/ListView.cpp \ + qpid/messaging/MapContent.cpp \ + qpid/messaging/MapView.cpp \ qpid/messaging/Message.cpp \ qpid/messaging/MessageImpl.h \ qpid/messaging/MessageImpl.cpp \ @@ -790,8 +796,11 @@ nobase_include_HEADERS += \ ../include/qpid/messaging/Connection.h \ ../include/qpid/messaging/Codec.h \ ../include/qpid/messaging/Filter.h \ + ../include/qpid/messaging/ListContent.h \ + ../include/qpid/messaging/ListView.h \ + ../include/qpid/messaging/MapContent.h \ + ../include/qpid/messaging/MapView.h \ ../include/qpid/messaging/Message.h \ - ../include/qpid/messaging/MessageContent.h \ ../include/qpid/messaging/MessageListener.h \ ../include/qpid/messaging/Sender.h \ ../include/qpid/messaging/Receiver.h \ diff --git a/qpid/cpp/src/cluster.mk b/qpid/cpp/src/cluster.mk index d90a06e1e2..1a8812d169 100644 --- a/qpid/cpp/src/cluster.mk +++ b/qpid/cpp/src/cluster.mk @@ -82,8 +82,7 @@ cluster_la_SOURCES = \ qpid/cluster/PollerDispatch.h \ qpid/cluster/ProxyInputHandler.h \ qpid/cluster/Quorum.h \ - qpid/cluster/types.h \ - qpid/sys/LatencyTracker.h + qpid/cluster/types.h cluster_la_LIBADD= -lcpg $(libcman) libqpidbroker.la libqpidclient.la cluster_la_CXXFLAGS = $(AM_CXXFLAGS) -fno-strict-aliasing diff --git a/qpid/cpp/src/qmf.mk b/qpid/cpp/src/qmf.mk index 54110ebaf7..2d034cf7c4 100644 --- a/qpid/cpp/src/qmf.mk +++ b/qpid/cpp/src/qmf.mk @@ -20,12 +20,14 @@ # # qmf library makefile fragment, to be included in Makefile.am # -lib_LTLIBRARIES += \ - libqmfcommon.la \ - libqmfagent.la +lib_LTLIBRARIES += \ + libqmf.la \ + libqmfengine.la -# Public header files -nobase_include_HEADERS += \ +# +# Public headers for the QMF API +# +QMF_API = \ ../include/qpid/agent/ManagementAgent.h \ ../include/qpid/agent/QmfAgentImportExport.h \ ../include/qmf/Agent.h \ @@ -34,42 +36,78 @@ nobase_include_HEADERS += \ ../include/qmf/ConnectionSettings.h \ ../include/qmf/AgentObject.h -libqmfcommon_la_SOURCES = \ - qmf/ConnectionSettingsImpl.cpp \ - qmf/ConnectionSettingsImpl.h \ - qmf/ConsoleEngine.cpp \ - qmf/ConsoleEngine.h \ - qmf/Event.h \ - qmf/Message.h \ - qmf/MessageImpl.cpp \ - qmf/MessageImpl.h \ - qmf/Object.h \ - qmf/ObjectId.h \ - qmf/ObjectIdImpl.cpp \ - qmf/ObjectIdImpl.h \ - qmf/ObjectImpl.cpp \ - qmf/ObjectImpl.h \ - qmf/Protocol.cpp \ - qmf/Protocol.h \ - qmf/Query.h \ - qmf/QueryImpl.cpp \ - qmf/QueryImpl.h \ - qmf/ResilientConnection.cpp \ - qmf/ResilientConnection.h \ - qmf/SequenceManager.cpp \ - qmf/SequenceManager.h \ - qmf/Schema.h \ - qmf/SchemaImpl.cpp \ - qmf/SchemaImpl.h \ - qmf/Typecode.h \ - qmf/Value.h \ - qmf/ValueImpl.cpp \ - qmf/ValueImpl.h +# +# Public headers for the QMF Engine API +# +QMF_ENGINE_API = \ + ../include/qmf/engine/Agent.h \ + ../include/qmf/engine/ConnectionSettings.h \ + ../include/qmf/engine/Console.h \ + ../include/qmf/engine/Event.h \ + ../include/qmf/engine/Message.h \ + ../include/qmf/engine/Object.h \ + ../include/qmf/engine/ObjectId.h \ + ../include/qmf/engine/QmfEngineImportExport.h \ + ../include/qmf/engine/Query.h \ + ../include/qmf/engine/ResilientConnection.h \ + ../include/qmf/engine/Schema.h \ + ../include/qmf/engine/Typecode.h \ + ../include/qmf/engine/Value.h + +# Public header files +nobase_include_HEADERS += \ + $(QMF_API) \ + $(QMF_ENGINE_API) -libqmfagent_la_SOURCES = \ - qmf/AgentEngine.cpp \ - qmf/AgentEngine.h \ - qpid/agent/ManagementAgentImpl.cpp \ +libqmf_la_SOURCES = \ + $(QMF_API) \ + qpid/agent/ManagementAgentImpl.cpp \ qpid/agent/ManagementAgentImpl.h -libqmfagent_la_LIBADD = libqpidclient.la libqmfcommon.la +libqmfengine_la_SOURCES = \ + $(QMF_ENGINE_API) \ + qmf/engine/Agent.cpp \ + qmf/engine/BrokerProxyImpl.cpp \ + qmf/engine/BrokerProxyImpl.h \ + qmf/engine/ConnectionSettingsImpl.cpp \ + qmf/engine/ConnectionSettingsImpl.h \ + qmf/engine/ConsoleImpl.cpp \ + qmf/engine/ConsoleImpl.h \ + qmf/engine/MessageImpl.cpp \ + qmf/engine/MessageImpl.h \ + qmf/engine/ObjectIdImpl.cpp \ + qmf/engine/ObjectIdImpl.h \ + qmf/engine/ObjectImpl.cpp \ + qmf/engine/ObjectImpl.h \ + qmf/engine/Protocol.cpp \ + qmf/engine/Protocol.h \ + qmf/engine/QueryImpl.cpp \ + qmf/engine/QueryImpl.h \ + qmf/engine/ResilientConnection.cpp \ + qmf/engine/SequenceManager.cpp \ + qmf/engine/SequenceManager.h \ + qmf/engine/SchemaImpl.cpp \ + qmf/engine/SchemaImpl.h \ + qmf/engine/ValueImpl.cpp \ + qmf/engine/ValueImpl.h + +libqmf_la_LIBADD = libqmfengine.la +libqmfengine_la_LIBADD = libqpidclient.la + +# Library Version Information: +# +# CURRENT => API/ABI version. Bump this if the interface changes +# REVISION => Version of underlying implementation. +# Bump if implementation changes but API/ABI doesn't +# AGE => Number of API/ABI versions this is backward compatible with +# +QMF_CURRENT = 1 +QMF_REVISION = 0 +QMF_AGE = 0 + +QMF_ENGINE_CURRENT = 1 +QMF_ENGINE_REVISION = 0 +QMF_ENGINE_AGE = 0 + +libqmf_la_LDFLAGS = -version-info $(QMF_CURRENT):$(QMF_REVISION):$(QMF_AGE) +libqmfengine_la_LDFLAGS = -version-info $(QMF_ENGINE_CURRENT):$(QMF_ENGINE_REVISION):$(QMF_ENGINE_AGE) diff --git a/qpid/cpp/src/qmf/AgentEngine.cpp b/qpid/cpp/src/qmf/AgentEngine.cpp deleted file mode 100644 index 9ea3be5907..0000000000 --- a/qpid/cpp/src/qmf/AgentEngine.cpp +++ /dev/null @@ -1,857 +0,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. - */ - -#include "qmf/AgentEngine.h" -#include "qmf/MessageImpl.h" -#include "qmf/SchemaImpl.h" -#include "qmf/Typecode.h" -#include "qmf/ObjectImpl.h" -#include "qmf/ObjectIdImpl.h" -#include "qmf/QueryImpl.h" -#include "qmf/ValueImpl.h" -#include "qmf/Protocol.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace qmf; -using namespace qpid::framing; -using namespace qpid::sys; - -namespace qmf { - - struct AgentEventImpl { - typedef boost::shared_ptr Ptr; - AgentEvent::EventKind kind; - uint32_t sequence; - string authUserId; - string authToken; - string name; - Object* object; - boost::shared_ptr objectId; - boost::shared_ptr query; - boost::shared_ptr arguments; - string exchange; - string bindingKey; - SchemaObjectClass* objectClass; - - AgentEventImpl(AgentEvent::EventKind k) : - kind(k), sequence(0), object(0), objectClass(0) {} - ~AgentEventImpl() {} - AgentEvent copy(); - }; - - struct AgentQueryContext { - typedef boost::shared_ptr Ptr; - uint32_t sequence; - string exchange; - string key; - SchemaMethodImpl* schemaMethod; - AgentQueryContext() : schemaMethod(0) {} - }; - - class AgentEngineImpl { - public: - AgentEngineImpl(char* label, bool internalStore); - ~AgentEngineImpl(); - - void setStoreDir(const char* path); - void setTransferDir(const char* path); - void handleRcvMessage(Message& message); - bool getXmtMessage(Message& item) const; - void popXmt(); - bool getEvent(AgentEvent& event) const; - void popEvent(); - void newSession(); - void startProtocol(); - void heartbeat(); - void methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments); - void queryResponse(uint32_t sequence, Object& object, bool prop, bool stat); - void queryComplete(uint32_t sequence); - void registerClass(SchemaObjectClass* cls); - void registerClass(SchemaEventClass* cls); - const ObjectId* addObject(Object& obj, uint64_t persistId); - const ObjectId* allocObjectId(uint64_t persistId); - const ObjectId* allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi); - void raiseEvent(Event& event); - - private: - mutable Mutex lock; - Mutex addLock; - string label; - string queueName; - string storeDir; - string transferDir; - bool internalStore; - uint64_t nextTransientId; - Uuid systemId; - uint32_t requestedBrokerBank; - uint32_t requestedAgentBank; - uint32_t assignedBrokerBank; - uint32_t assignedAgentBank; - AgentAttachment attachment; - uint16_t bootSequence; - uint64_t nextObjectId; - uint32_t nextContextNum; - deque eventQueue; - deque xmtQueue; - map contextMap; - - static const char* QMF_EXCHANGE; - static const char* DIR_EXCHANGE; - static const char* BROKER_KEY; - static const uint32_t MERR_UNKNOWN_METHOD = 2; - static const uint32_t MERR_UNKNOWN_PACKAGE = 8; - static const uint32_t MERR_UNKNOWN_CLASS = 9; - static const uint32_t MERR_INTERNAL_ERROR = 10; -# define MA_BUFFER_SIZE 65536 - char outputBuffer[MA_BUFFER_SIZE]; - - struct AgentClassKey { - string name; - uint8_t hash[16]; - AgentClassKey(const string& n, const uint8_t* h) : name(n) { - memcpy(hash, h, 16); - } - AgentClassKey(Buffer& buffer) { - buffer.getShortString(name); - buffer.getBin128(hash); - } - string repr() { - return name; - } - }; - - struct AgentClassKeyComp { - bool operator() (const AgentClassKey& lhs, const AgentClassKey& rhs) const - { - if (lhs.name != rhs.name) - return lhs.name < rhs.name; - else - for (int i = 0; i < 16; i++) - if (lhs.hash[i] != rhs.hash[i]) - return lhs.hash[i] < rhs.hash[i]; - return false; - } - }; - - typedef map ObjectClassMap; - typedef map EventClassMap; - - struct ClassMaps { - ObjectClassMap objectClasses; - EventClassMap eventClasses; - }; - - map packages; - - AgentEventImpl::Ptr eventDeclareQueue(const string& queueName); - AgentEventImpl::Ptr eventBind(const string& exchange, const string& queue, const string& key); - AgentEventImpl::Ptr eventSetupComplete(); - AgentEventImpl::Ptr eventQuery(uint32_t num, const string& userId, const string& package, const string& cls, - boost::shared_ptr oid); - AgentEventImpl::Ptr eventMethod(uint32_t num, const string& userId, const string& method, - boost::shared_ptr oid, boost::shared_ptr argMap, - SchemaObjectClass* objectClass); - void sendBufferLH(Buffer& buf, const string& destination, const string& routingKey); - - void sendPackageIndicationLH(const string& packageName); - void sendClassIndicationLH(ClassKind kind, const string& packageName, const AgentClassKey& key); - void sendCommandCompleteLH(const string& exchange, const string& key, uint32_t seq, - uint32_t code = 0, const string& text = "OK"); - void sendMethodErrorLH(uint32_t sequence, const string& key, uint32_t code, const string& text=""); - void handleAttachResponse(Buffer& inBuffer); - void handlePackageRequest(Buffer& inBuffer); - void handleClassQuery(Buffer& inBuffer); - void handleSchemaRequest(Buffer& inBuffer, uint32_t sequence, - const string& replyToExchange, const string& replyToKey); - void handleGetQuery(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId); - void handleMethodRequest(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId); - void handleConsoleAddedIndication(); - }; -} - -const char* AgentEngineImpl::QMF_EXCHANGE = "qpid.management"; -const char* AgentEngineImpl::DIR_EXCHANGE = "amq.direct"; -const char* AgentEngineImpl::BROKER_KEY = "broker"; - -#define STRING_REF(s) {if (!s.empty()) item.s = const_cast(s.c_str());} - -AgentEvent AgentEventImpl::copy() -{ - AgentEvent item; - - ::memset(&item, 0, sizeof(AgentEvent)); - item.kind = kind; - item.sequence = sequence; - item.object = object; - item.objectId = objectId.get(); - item.query = query.get(); - item.arguments = arguments.get(); - item.objectClass = objectClass; - - STRING_REF(authUserId); - STRING_REF(authToken); - STRING_REF(name); - STRING_REF(exchange); - STRING_REF(bindingKey); - - return item; -} - -AgentEngineImpl::AgentEngineImpl(char* _label, bool i) : - label(_label), queueName("qmfa-"), internalStore(i), nextTransientId(1), - requestedBrokerBank(0), requestedAgentBank(0), - assignedBrokerBank(0), assignedAgentBank(0), - bootSequence(1), nextObjectId(1), nextContextNum(1) -{ - queueName += label; -} - -AgentEngineImpl::~AgentEngineImpl() -{ -} - -void AgentEngineImpl::setStoreDir(const char* path) -{ - Mutex::ScopedLock _lock(lock); - if (path) - storeDir = path; - else - storeDir.clear(); -} - -void AgentEngineImpl::setTransferDir(const char* path) -{ - Mutex::ScopedLock _lock(lock); - if (path) - transferDir = path; - else - transferDir.clear(); -} - -void AgentEngineImpl::handleRcvMessage(Message& message) -{ - Buffer inBuffer(message.body, message.length); - uint8_t opcode; - uint32_t sequence; - string replyToExchange(message.replyExchange ? message.replyExchange : ""); - string replyToKey(message.replyKey ? message.replyKey : ""); - string userId(message.userId ? message.userId : ""); - - while (Protocol::checkHeader(inBuffer, &opcode, &sequence)) { - if (opcode == Protocol::OP_ATTACH_RESPONSE) handleAttachResponse(inBuffer); - else if (opcode == Protocol::OP_SCHEMA_REQUEST) handleSchemaRequest(inBuffer, sequence, replyToExchange, replyToKey); - else if (opcode == Protocol::OP_CONSOLE_ADDED_INDICATION) handleConsoleAddedIndication(); - else if (opcode == Protocol::OP_GET_QUERY) handleGetQuery(inBuffer, sequence, replyToKey, userId); - else if (opcode == Protocol::OP_METHOD_REQUEST) handleMethodRequest(inBuffer, sequence, replyToKey, userId); - else { - QPID_LOG(error, "AgentEngineImpl::handleRcvMessage invalid opcode=" << opcode); - break; - } - } -} - -bool AgentEngineImpl::getXmtMessage(Message& item) const -{ - Mutex::ScopedLock _lock(lock); - if (xmtQueue.empty()) - return false; - item = xmtQueue.front()->copy(); - return true; -} - -void AgentEngineImpl::popXmt() -{ - Mutex::ScopedLock _lock(lock); - if (!xmtQueue.empty()) - xmtQueue.pop_front(); -} - -bool AgentEngineImpl::getEvent(AgentEvent& event) const -{ - Mutex::ScopedLock _lock(lock); - if (eventQueue.empty()) - return false; - event = eventQueue.front()->copy(); - return true; -} - -void AgentEngineImpl::popEvent() -{ - Mutex::ScopedLock _lock(lock); - if (!eventQueue.empty()) - eventQueue.pop_front(); -} - -void AgentEngineImpl::newSession() -{ - Mutex::ScopedLock _lock(lock); - eventQueue.clear(); - xmtQueue.clear(); - eventQueue.push_back(eventDeclareQueue(queueName)); - eventQueue.push_back(eventBind("amq.direct", queueName, queueName)); - eventQueue.push_back(eventSetupComplete()); -} - -void AgentEngineImpl::startProtocol() -{ - Mutex::ScopedLock _lock(lock); - char rawbuffer[512]; - Buffer buffer(rawbuffer, 512); - - Protocol::encodeHeader(buffer, Protocol::OP_ATTACH_REQUEST); - buffer.putShortString("qmfa"); - systemId.encode(buffer); - buffer.putLong(requestedBrokerBank); - buffer.putLong(requestedAgentBank); - sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); - QPID_LOG(trace, "SENT AttachRequest: reqBroker=" << requestedBrokerBank << - " reqAgent=" << requestedAgentBank); -} - -void AgentEngineImpl::heartbeat() -{ - Mutex::ScopedLock _lock(lock); - Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - - Protocol::encodeHeader(buffer, Protocol::OP_HEARTBEAT_INDICATION); - buffer.putLongLong(uint64_t(Duration(now()))); - stringstream key; - key << "console.heartbeat." << assignedBrokerBank << "." << assignedAgentBank; - sendBufferLH(buffer, QMF_EXCHANGE, key.str()); - QPID_LOG(trace, "SENT HeartbeatIndication"); -} - -void AgentEngineImpl::methodResponse(uint32_t sequence, uint32_t status, char* text, - const Value& argMap) -{ - Mutex::ScopedLock _lock(lock); - map::iterator iter = contextMap.find(sequence); - if (iter == contextMap.end()) - return; - AgentQueryContext::Ptr context = iter->second; - contextMap.erase(iter); - - Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - Protocol::encodeHeader(buffer, Protocol::OP_METHOD_RESPONSE, context->sequence); - buffer.putLong(status); - buffer.putMediumString(text); - if (status == 0) { - for (vector::const_iterator aIter = context->schemaMethod->arguments.begin(); - aIter != context->schemaMethod->arguments.end(); aIter++) { - const SchemaArgumentImpl* schemaArg = *aIter; - if (schemaArg->dir == DIR_OUT || schemaArg->dir == DIR_IN_OUT) { - if (argMap.keyInMap(schemaArg->name.c_str())) { - const Value* val = argMap.byKey(schemaArg->name.c_str()); - val->impl->encode(buffer); - } else { - Value val(schemaArg->typecode); - val.impl->encode(buffer); - } - } - } - } - sendBufferLH(buffer, context->exchange, context->key); - QPID_LOG(trace, "SENT MethodResponse seq=" << context->sequence << " status=" << status << " text=" << text); -} - -void AgentEngineImpl::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat) -{ - Mutex::ScopedLock _lock(lock); - map::iterator iter = contextMap.find(sequence); - if (iter == contextMap.end()) - return; - AgentQueryContext::Ptr context = iter->second; - - Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - Protocol::encodeHeader(buffer, Protocol::OP_OBJECT_INDICATION, context->sequence); - - object.impl->encodeSchemaKey(buffer); - object.impl->encodeManagedObjectData(buffer); - if (prop) - object.impl->encodeProperties(buffer); - if (stat) - object.impl->encodeStatistics(buffer); - - sendBufferLH(buffer, context->exchange, context->key); - QPID_LOG(trace, "SENT ContentIndication seq=" << context->sequence); -} - -void AgentEngineImpl::queryComplete(uint32_t sequence) -{ - Mutex::ScopedLock _lock(lock); - map::iterator iter = contextMap.find(sequence); - if (iter == contextMap.end()) - return; - - AgentQueryContext::Ptr context = iter->second; - contextMap.erase(iter); - sendCommandCompleteLH(context->exchange, context->key, context->sequence, 0, "OK"); -} - -void AgentEngineImpl::registerClass(SchemaObjectClass* cls) -{ - Mutex::ScopedLock _lock(lock); - SchemaObjectClassImpl* impl = cls->impl; - - map::iterator iter = packages.find(impl->package); - if (iter == packages.end()) { - packages[impl->package] = ClassMaps(); - iter = packages.find(impl->getClassKey()->getPackageName()); - // TODO: Indicate this package if connected - } - - AgentClassKey key(impl->getClassKey()->getClassName(), impl->getClassKey()->getHash()); - iter->second.objectClasses[key] = impl; - - // TODO: Indicate this schema if connected. -} - -void AgentEngineImpl::registerClass(SchemaEventClass* cls) -{ - Mutex::ScopedLock _lock(lock); - SchemaEventClassImpl* impl = cls->impl; - - map::iterator iter = packages.find(impl->package); - if (iter == packages.end()) { - packages[impl->package] = ClassMaps(); - iter = packages.find(impl->getClassKey()->getPackageName()); - // TODO: Indicate this package if connected - } - - AgentClassKey key(impl->getClassKey()->getClassName(), impl->getClassKey()->getHash()); - iter->second.eventClasses[key] = impl; - - // TODO: Indicate this schema if connected. -} - -const ObjectId* AgentEngineImpl::addObject(Object&, uint64_t) -{ - Mutex::ScopedLock _lock(lock); - return 0; -} - -const ObjectId* AgentEngineImpl::allocObjectId(uint64_t persistId) -{ - Mutex::ScopedLock _lock(lock); - uint16_t sequence = persistId ? 0 : bootSequence; - uint64_t objectNum = persistId ? persistId : nextObjectId++; - - ObjectIdImpl* oid = new ObjectIdImpl(&attachment, 0, sequence, objectNum); - return oid->envelope; -} - -const ObjectId* AgentEngineImpl::allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi) -{ - return allocObjectId(((uint64_t) persistIdHi) << 32 | (uint64_t) persistIdLo); -} - -void AgentEngineImpl::raiseEvent(Event&) -{ - Mutex::ScopedLock _lock(lock); -} - -AgentEventImpl::Ptr AgentEngineImpl::eventDeclareQueue(const string& name) -{ - AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::DECLARE_QUEUE)); - event->name = name; - - return event; -} - -AgentEventImpl::Ptr AgentEngineImpl::eventBind(const string& exchange, const string& queue, - const string& key) -{ - AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::BIND)); - event->name = queue; - event->exchange = exchange; - event->bindingKey = key; - - return event; -} - -AgentEventImpl::Ptr AgentEngineImpl::eventSetupComplete() -{ - AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::SETUP_COMPLETE)); - return event; -} - -AgentEventImpl::Ptr AgentEngineImpl::eventQuery(uint32_t num, const string& userId, const string& package, - const string& cls, boost::shared_ptr oid) -{ - AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::GET_QUERY)); - event->sequence = num; - event->authUserId = userId; - if (oid.get()) - event->query.reset(new Query(oid.get())); - else - event->query.reset(new Query(cls.c_str(), package.c_str())); - return event; -} - -AgentEventImpl::Ptr AgentEngineImpl::eventMethod(uint32_t num, const string& userId, const string& method, - boost::shared_ptr oid, boost::shared_ptr argMap, - SchemaObjectClass* objectClass) -{ - AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::METHOD_CALL)); - event->sequence = num; - event->authUserId = userId; - event->name = method; - event->objectId = oid; - event->arguments = argMap; - event->objectClass = objectClass; - return event; -} - -void AgentEngineImpl::sendBufferLH(Buffer& buf, const string& destination, const string& routingKey) -{ - uint32_t length = buf.getPosition(); - MessageImpl::Ptr message(new MessageImpl); - - buf.reset(); - buf.getRawData(message->body, length); - message->destination = destination; - message->routingKey = routingKey; - message->replyExchange = "amq.direct"; - message->replyKey = queueName; - - xmtQueue.push_back(message); -} - -void AgentEngineImpl::sendPackageIndicationLH(const string& packageName) -{ - Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - Protocol::encodeHeader(buffer, Protocol::OP_PACKAGE_INDICATION); - buffer.putShortString(packageName); - sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); - QPID_LOG(trace, "SENT PackageIndication: package_name=" << packageName); -} - -void AgentEngineImpl::sendClassIndicationLH(ClassKind kind, const string& packageName, const AgentClassKey& key) -{ - Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - Protocol::encodeHeader(buffer, Protocol::OP_CLASS_INDICATION); - buffer.putOctet((int) kind); - buffer.putShortString(packageName); - buffer.putShortString(key.name); - buffer.putBin128(const_cast(key.hash)); // const_cast needed for older Qpid libraries - sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); - QPID_LOG(trace, "SENT ClassIndication: package_name=" << packageName << " class_name=" << key.name); -} - -void AgentEngineImpl::sendCommandCompleteLH(const string& exchange, const string& replyToKey, - uint32_t sequence, uint32_t code, const string& text) -{ - Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - Protocol::encodeHeader(buffer, Protocol::OP_COMMAND_COMPLETE, sequence); - buffer.putLong(code); - buffer.putShortString(text); - sendBufferLH(buffer, exchange, replyToKey); - QPID_LOG(trace, "SENT CommandComplete: seq=" << sequence << " code=" << code << " text=" << text); -} - -void AgentEngineImpl::sendMethodErrorLH(uint32_t sequence, const string& key, uint32_t code, const string& text) -{ - Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - Protocol::encodeHeader(buffer, Protocol::OP_METHOD_RESPONSE, sequence); - buffer.putLong(code); - - string fulltext; - switch (code) { - case MERR_UNKNOWN_PACKAGE: fulltext = "Unknown Package"; break; - case MERR_UNKNOWN_CLASS: fulltext = "Unknown Class"; break; - case MERR_UNKNOWN_METHOD: fulltext = "Unknown Method"; break; - case MERR_INTERNAL_ERROR: fulltext = "Internal Error"; break; - default: fulltext = "Unspecified Error"; break; - } - - if (!text.empty()) { - fulltext += " ("; - fulltext += text; - fulltext += ")"; - } - - buffer.putMediumString(fulltext); - sendBufferLH(buffer, DIR_EXCHANGE, key); - QPID_LOG(trace, "SENT MethodResponse: errorCode=" << code << " text=" << fulltext); -} - -void AgentEngineImpl::handleAttachResponse(Buffer& inBuffer) -{ - Mutex::ScopedLock _lock(lock); - - assignedBrokerBank = inBuffer.getLong(); - assignedAgentBank = inBuffer.getLong(); - - QPID_LOG(trace, "RCVD AttachResponse: broker=" << assignedBrokerBank << " agent=" << assignedAgentBank); - - if ((assignedBrokerBank != requestedBrokerBank) || - (assignedAgentBank != requestedAgentBank)) { - if (requestedAgentBank == 0) { - QPID_LOG(notice, "Initial object-id bank assigned: " << assignedBrokerBank << "." << - assignedAgentBank); - } else { - QPID_LOG(warning, "Collision in object-id! New bank assigned: " << assignedBrokerBank << - "." << assignedAgentBank); - } - //storeData(); // TODO - requestedBrokerBank = assignedBrokerBank; - requestedAgentBank = assignedAgentBank; - } - - attachment.setBanks(assignedBrokerBank, assignedAgentBank); - - // Bind to qpid.management to receive commands - stringstream key; - key << "agent." << assignedBrokerBank << "." << assignedAgentBank; - eventQueue.push_back(eventBind(QMF_EXCHANGE, queueName, key.str())); - - // Send package indications for all local packages - for (map::iterator pIter = packages.begin(); - pIter != packages.end(); - pIter++) { - sendPackageIndicationLH(pIter->first); - - // Send class indications for all local classes - ClassMaps cMap = pIter->second; - for (ObjectClassMap::iterator cIter = cMap.objectClasses.begin(); - cIter != cMap.objectClasses.end(); cIter++) - sendClassIndicationLH(CLASS_OBJECT, pIter->first, cIter->first); - for (EventClassMap::iterator cIter = cMap.eventClasses.begin(); - cIter != cMap.eventClasses.end(); cIter++) - sendClassIndicationLH(CLASS_EVENT, pIter->first, cIter->first); - } -} - -void AgentEngineImpl::handlePackageRequest(Buffer&) -{ - Mutex::ScopedLock _lock(lock); -} - -void AgentEngineImpl::handleClassQuery(Buffer&) -{ - Mutex::ScopedLock _lock(lock); -} - -void AgentEngineImpl::handleSchemaRequest(Buffer& inBuffer, uint32_t sequence, - const string& replyExchange, const string& replyKey) -{ - Mutex::ScopedLock _lock(lock); - string rExchange(replyExchange); - string rKey(replyKey); - string packageName; - inBuffer.getShortString(packageName); - AgentClassKey key(inBuffer); - - if (rExchange.empty()) - rExchange = QMF_EXCHANGE; - if (rKey.empty()) - rKey = BROKER_KEY; - - QPID_LOG(trace, "RCVD SchemaRequest: package=" << packageName << " class=" << key.name); - - map::iterator pIter = packages.find(packageName); - if (pIter == packages.end()) { - sendCommandCompleteLH(rExchange, rKey, sequence, 1, "package not found"); - return; - } - - ClassMaps cMap = pIter->second; - ObjectClassMap::iterator ocIter = cMap.objectClasses.find(key); - if (ocIter != cMap.objectClasses.end()) { - SchemaObjectClassImpl* oImpl = ocIter->second; - Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - Protocol::encodeHeader(buffer, Protocol::OP_SCHEMA_RESPONSE, sequence); - oImpl->encode(buffer); - sendBufferLH(buffer, rExchange, rKey); - QPID_LOG(trace, "SENT SchemaResponse: (object) package=" << packageName << " class=" << key.name); - return; - } - - EventClassMap::iterator ecIter = cMap.eventClasses.find(key); - if (ecIter != cMap.eventClasses.end()) { - SchemaEventClassImpl* eImpl = ecIter->second; - Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - Protocol::encodeHeader(buffer, Protocol::OP_SCHEMA_RESPONSE, sequence); - eImpl->encode(buffer); - sendBufferLH(buffer, rExchange, rKey); - QPID_LOG(trace, "SENT SchemaResponse: (event) package=" << packageName << " class=" << key.name); - return; - } - - sendCommandCompleteLH(rExchange, rKey, sequence, 1, "class not found"); -} - -void AgentEngineImpl::handleGetQuery(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId) -{ - Mutex::ScopedLock _lock(lock); - FieldTable ft; - FieldTable::ValuePtr value; - map::const_iterator pIter = packages.end(); - string pname; - string cname; - string oidRepr; - boost::shared_ptr oid; - - ft.decode(inBuffer); - - QPID_LOG(trace, "RCVD GetQuery: seq=" << sequence << " map=" << ft); - - value = ft.get("_package"); - if (value.get() && value->convertsTo()) { - pname = value->get(); - pIter = packages.find(pname); - if (pIter == packages.end()) { - sendCommandCompleteLH(DIR_EXCHANGE, replyTo, sequence); - return; - } - } - - value = ft.get("_class"); - if (value.get() && value->convertsTo()) { - cname = value->get(); - // TODO - check for validity of class (in package or any package) - if (pIter == packages.end()) { - } else { - - } - } - - value = ft.get("_objectid"); - if (value.get() && value->convertsTo()) { - oidRepr = value->get(); - oid.reset(new ObjectId()); - oid->impl->fromString(oidRepr); - } - - AgentQueryContext::Ptr context(new AgentQueryContext); - uint32_t contextNum = nextContextNum++; - context->sequence = sequence; - context->exchange = DIR_EXCHANGE; - context->key = replyTo; - contextMap[contextNum] = context; - - eventQueue.push_back(eventQuery(contextNum, userId, pname, cname, oid)); -} - -void AgentEngineImpl::handleMethodRequest(Buffer& buffer, uint32_t sequence, const string& replyTo, const string& userId) -{ - Mutex::ScopedLock _lock(lock); - string pname; - string method; - ObjectIdImpl* oidImpl = new ObjectIdImpl(buffer); - boost::shared_ptr oid(oidImpl->envelope); - buffer.getShortString(pname); - AgentClassKey classKey(buffer); - buffer.getShortString(method); - - QPID_LOG(trace, "RCVD MethodRequest seq=" << sequence << " method=" << method); - - map::const_iterator pIter = packages.find(pname); - if (pIter == packages.end()) { - sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_PACKAGE, pname); - return; - } - - ObjectClassMap::const_iterator cIter = pIter->second.objectClasses.find(classKey); - if (cIter == pIter->second.objectClasses.end()) { - sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_CLASS, classKey.repr()); - return; - } - - const SchemaObjectClassImpl* schema = cIter->second; - vector::const_iterator mIter = schema->methods.begin(); - for (; mIter != schema->methods.end(); mIter++) { - if ((*mIter)->name == method) - break; - } - - if (mIter == schema->methods.end()) { - sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_METHOD, method); - return; - } - - SchemaMethodImpl* schemaMethod = *mIter; - boost::shared_ptr argMap(new Value(TYPE_MAP)); - ValueImpl* value; - for (vector::const_iterator aIter = schemaMethod->arguments.begin(); - aIter != schemaMethod->arguments.end(); aIter++) { - const SchemaArgumentImpl* schemaArg = *aIter; - if (schemaArg->dir == DIR_IN || schemaArg->dir == DIR_IN_OUT) - value = new ValueImpl(schemaArg->typecode, buffer); - else - value = new ValueImpl(schemaArg->typecode); - argMap->insert(schemaArg->name.c_str(), value->envelope); - } - - AgentQueryContext::Ptr context(new AgentQueryContext); - uint32_t contextNum = nextContextNum++; - context->sequence = sequence; - context->exchange = DIR_EXCHANGE; - context->key = replyTo; - context->schemaMethod = schemaMethod; - contextMap[contextNum] = context; - - eventQueue.push_back(eventMethod(contextNum, userId, method, oid, argMap, schema->envelope)); -} - -void AgentEngineImpl::handleConsoleAddedIndication() -{ - Mutex::ScopedLock _lock(lock); -} - -//================================================================== -// Wrappers -//================================================================== - -AgentEngine::AgentEngine(char* label, bool internalStore) { impl = new AgentEngineImpl(label, internalStore); } -AgentEngine::~AgentEngine() { delete impl; } -void AgentEngine::setStoreDir(const char* path) { impl->setStoreDir(path); } -void AgentEngine::setTransferDir(const char* path) { impl->setTransferDir(path); } -void AgentEngine::handleRcvMessage(Message& message) { impl->handleRcvMessage(message); } -bool AgentEngine::getXmtMessage(Message& item) const { return impl->getXmtMessage(item); } -void AgentEngine::popXmt() { impl->popXmt(); } -bool AgentEngine::getEvent(AgentEvent& event) const { return impl->getEvent(event); } -void AgentEngine::popEvent() { impl->popEvent(); } -void AgentEngine::newSession() { impl->newSession(); } -void AgentEngine::startProtocol() { impl->startProtocol(); } -void AgentEngine::heartbeat() { impl->heartbeat(); } -void AgentEngine::methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments) { impl->methodResponse(sequence, status, text, arguments); } -void AgentEngine::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat) { impl->queryResponse(sequence, object, prop, stat); } -void AgentEngine::queryComplete(uint32_t sequence) { impl->queryComplete(sequence); } -void AgentEngine::registerClass(SchemaObjectClass* cls) { impl->registerClass(cls); } -void AgentEngine::registerClass(SchemaEventClass* cls) { impl->registerClass(cls); } -const ObjectId* AgentEngine::addObject(Object& obj, uint64_t persistId) { return impl->addObject(obj, persistId); } -const ObjectId* AgentEngine::allocObjectId(uint64_t persistId) { return impl->allocObjectId(persistId); } -const ObjectId* AgentEngine::allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi) { return impl->allocObjectId(persistIdLo, persistIdHi); } -void AgentEngine::raiseEvent(Event& event) { impl->raiseEvent(event); } - diff --git a/qpid/cpp/src/qmf/AgentEngine.h b/qpid/cpp/src/qmf/AgentEngine.h deleted file mode 100644 index c88ef33657..0000000000 --- a/qpid/cpp/src/qmf/AgentEngine.h +++ /dev/null @@ -1,207 +0,0 @@ -#ifndef _QmfAgentEngine_ -#define _QmfAgentEngine_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace qmf { - - /** - * AgentEvent - * - * This structure represents a QMF event coming from the agent to - * the application. - */ - struct AgentEvent { - enum EventKind { - GET_QUERY = 1, - START_SYNC = 2, - END_SYNC = 3, - METHOD_CALL = 4, - DECLARE_QUEUE = 5, - DELETE_QUEUE = 6, - BIND = 7, - UNBIND = 8, - SETUP_COMPLETE = 9 - }; - - EventKind kind; - uint32_t sequence; // Protocol sequence (for all kinds) - char* authUserId; // Authenticated user ID (for all kinds) - char* authToken; // Authentication token if issued (for all kinds) - char* name; // Name of the method/sync query - // (METHOD_CALL, START_SYNC, END_SYNC, DECLARE_QUEUE, BIND, UNBIND) - Object* object; // Object involved in method call (METHOD_CALL) - ObjectId* objectId; // ObjectId for method call (METHOD_CALL) - Query* query; // Query parameters (GET_QUERY, START_SYNC) - Value* arguments; // Method parameters (METHOD_CALL) - char* exchange; // Exchange for bind (BIND, UNBIND) - char* bindingKey; // Key for bind (BIND, UNBIND) - SchemaObjectClass* objectClass; // (METHOD_CALL) - }; - - class AgentEngineImpl; - - /** - * AgentEngine - Protocol engine for the QMF agent - */ - class AgentEngine { - public: - AgentEngine(char* label, bool internalStore=true); - ~AgentEngine(); - - /** - * Configure the directory path for storing persistent data. - *@param path Null-terminated string containing a directory path where files can be - * created, written, and read. If NULL, no persistent storage will be - * attempted. - */ - void setStoreDir(const char* path); - - /** - * Configure the directory path for files transferred over QMF. - *@param path Null-terminated string containing a directory path where files can be - * created, deleted, written, and read. If NULL, file transfers shall not - * be permitted. - */ - void setTransferDir(const char* path); - - /** - * Pass messages received from the AMQP session to the Agent engine. - *@param message AMQP messages received on the agent session. - */ - void handleRcvMessage(Message& message); - - /** - * Get the next message to be sent to the AMQP network. - *@param item The Message structure describing the message to be produced. - *@return true if the Message is valid, false if there are no messages to send. - */ - bool getXmtMessage(Message& item) const; - - /** - * Remove and discard one message from the head of the transmit queue. - */ - void popXmt(); - - /** - * Get the next application event from the agent engine. - *@param event The event iff the return value is true - *@return true if event is valid, false if there are no events to process - */ - bool getEvent(AgentEvent& event) const; - - /** - * Remove and discard one event from the head of the event queue. - */ - void popEvent(); - - /** - * A new AMQP session has been established for Agent communication. - */ - void newSession(); - - /** - * Start the QMF Agent protocol. This should be invoked after a SETUP_COMPLETE event - * is received from the Agent engine. - */ - void startProtocol(); - - /** - * This method is called periodically so the agent can supply a heartbeat. - */ - void heartbeat(); - - /** - * Respond to a method request. - *@param sequence The sequence number from the method request event. - *@param status The method's completion status. - *@param text Status text ("OK" or an error message) - *@param arguments The list of output arguments from the method call. - */ - void methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments); - - /** - * Send a content indication to the QMF bus. This is only needed for objects that are - * managed by the application. This is *NOT* needed for objects managed by the Agent - * (inserted using addObject). - *@param sequence The sequence number of the GET request or the SYNC_START request. - *@param object The object (annotated with "changed" flags) for publication. - *@param prop If true, changed object properties are transmitted. - *@param stat If true, changed object statistics are transmitted. - */ - void queryResponse(uint32_t sequence, Object& object, bool prop = true, bool stat = true); - - /** - * Indicate the completion of a query. This is not used for SYNC_START requests. - *@param sequence The sequence number of the GET request. - */ - void queryComplete(uint32_t sequence); - - /** - * Register a schema class with the Agent. - *@param cls A SchemaObejctClass object that defines data managed by the agent. - */ - void registerClass(SchemaObjectClass* cls); - - /** - * Register a schema class with the Agent. - *@param cls A SchemaEventClass object that defines events sent by the agent. - */ - void registerClass(SchemaEventClass* cls); - - /** - * Give an object to the Agent for storage and management. Once added, the agent takes - * responsibility for the life cycle of the object. - *@param obj The object to be managed by the Agent. - *@param persistId A unique non-zero value if the object-id is to be persistent. - *@return The objectId of the managed object. - */ - const ObjectId* addObject(Object& obj, uint64_t persistId); - // const ObjectId* addObject(Object& obj, uint32_t persistIdLo, uint32_t persistIdHi); - - /** - * Allocate an object-id for an object that will be managed by the application. - *@param persistId A unique non-zero value if the object-id is to be persistent. - *@return The objectId structure for the allocated ID. - */ - const ObjectId* allocObjectId(uint64_t persistId); - const ObjectId* allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi); - - /** - * Raise an event into the QMF network.. - *@param event The event object for the event to be raised. - */ - void raiseEvent(Event& event); - - private: - AgentEngineImpl* impl; - }; -} - -#endif - diff --git a/qpid/cpp/src/qmf/ConnectionSettingsImpl.cpp b/qpid/cpp/src/qmf/ConnectionSettingsImpl.cpp deleted file mode 100644 index 034ab18395..0000000000 --- a/qpid/cpp/src/qmf/ConnectionSettingsImpl.cpp +++ /dev/null @@ -1,323 +0,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. - */ - -#include "qmf/ConnectionSettingsImpl.h" -#include "qmf/Typecode.h" - -using namespace std; -using namespace qmf; -using namespace qpid; - -const string attrProtocol("protocol"); -const string attrHost("host"); -const string attrPort("port"); -const string attrVirtualhost("virtualhost"); -const string attrUsername("username"); -const string attrPassword("password"); -const string attrMechanism("mechanism"); -const string attrLocale("locale"); -const string attrHeartbeat("heartbeat"); -const string attrMaxChannels("maxChannels"); -const string attrMaxFrameSize("maxFrameSize"); -const string attrBounds("bounds"); -const string attrTcpNoDelay("tcpNoDelay"); -const string attrService("service"); -const string attrMinSsf("minSsf"); -const string attrMaxSsf("maxSsf"); -const string attrRetryDelayMin("retryDelayMin"); -const string attrRetryDelayMax("retryDelayMax"); -const string attrRetryDelayFactor("retryDelayFactor"); - -ConnectionSettingsImpl::ConnectionSettingsImpl(ConnectionSettings* e) : - envelope(e), retryDelayMin(1), retryDelayMax(64), retryDelayFactor(2) -{ -} - -ConnectionSettingsImpl::ConnectionSettingsImpl(ConnectionSettings* e, const string& /*url*/) : - envelope(e), retryDelayMin(1), retryDelayMax(64), retryDelayFactor(2) -{ - // TODO: Parse the URL -} - -void ConnectionSettingsImpl::setAttr(const string& key, const Value& value) -{ - if (key == attrProtocol) clientSettings.protocol = value.asString(); - else if (key == attrHost) clientSettings.host = value.asString(); - else if (key == attrPort) clientSettings.port = value.asUint(); - else if (key == attrVirtualhost) clientSettings.virtualhost = value.asString(); - else if (key == attrUsername) clientSettings.username = value.asString(); - else if (key == attrPassword) clientSettings.password = value.asString(); - else if (key == attrMechanism) clientSettings.mechanism = value.asString(); - else if (key == attrLocale) clientSettings.locale = value.asString(); - else if (key == attrHeartbeat) clientSettings.heartbeat = value.asUint(); - else if (key == attrMaxChannels) clientSettings.maxChannels = value.asUint(); - else if (key == attrMaxFrameSize) clientSettings.maxFrameSize = value.asUint(); - else if (key == attrBounds) clientSettings.bounds = value.asUint(); - else if (key == attrTcpNoDelay) clientSettings.tcpNoDelay = value.asBool(); - else if (key == attrService) clientSettings.service = value.asString(); - else if (key == attrMinSsf) clientSettings.minSsf = value.asUint(); - else if (key == attrMaxSsf) clientSettings.maxSsf = value.asUint(); - - else if (key == attrRetryDelayMin) retryDelayMin = value.asUint(); - else if (key == attrRetryDelayMax) retryDelayMax = value.asUint(); - else if (key == attrRetryDelayFactor) retryDelayFactor = value.asUint(); -} - -Value ConnectionSettingsImpl::getAttr(const string& key) const -{ - Value strval(TYPE_LSTR); - Value intval(TYPE_UINT32); - Value boolval(TYPE_BOOL); - - if (key == attrProtocol) { - strval.setString(clientSettings.protocol.c_str()); - return strval; - } - - if (key == attrHost) { - strval.setString(clientSettings.host.c_str()); - return strval; - } - - if (key == attrPort) { - intval.setUint(clientSettings.port); - return intval; - } - - if (key == attrVirtualhost) { - strval.setString(clientSettings.virtualhost.c_str()); - return strval; - } - - if (key == attrUsername) { - strval.setString(clientSettings.username.c_str()); - return strval; - } - - if (key == attrPassword) { - strval.setString(clientSettings.password.c_str()); - return strval; - } - - if (key == attrMechanism) { - strval.setString(clientSettings.mechanism.c_str()); - return strval; - } - - if (key == attrLocale) { - strval.setString(clientSettings.locale.c_str()); - return strval; - } - - if (key == attrHeartbeat) { - intval.setUint(clientSettings.heartbeat); - return intval; - } - - if (key == attrMaxChannels) { - intval.setUint(clientSettings.maxChannels); - return intval; - } - - if (key == attrMaxFrameSize) { - intval.setUint(clientSettings.maxFrameSize); - return intval; - } - - if (key == attrBounds) { - intval.setUint(clientSettings.bounds); - return intval; - } - - if (key == attrTcpNoDelay) { - boolval.setBool(clientSettings.tcpNoDelay); - return boolval; - } - - if (key == attrService) { - strval.setString(clientSettings.service.c_str()); - return strval; - } - - if (key == attrMinSsf) { - intval.setUint(clientSettings.minSsf); - return intval; - } - - if (key == attrMaxSsf) { - intval.setUint(clientSettings.maxSsf); - return intval; - } - - if (key == attrRetryDelayMin) { - intval.setUint(retryDelayMin); - return intval; - } - - if (key == attrRetryDelayMax) { - intval.setUint(retryDelayMax); - return intval; - } - - if (key == attrRetryDelayFactor) { - intval.setUint(retryDelayFactor); - return intval; - } - - return strval; -} - -const string& ConnectionSettingsImpl::getAttrString() const -{ - // TODO: build and return attribute string - return attrString; -} - -void ConnectionSettingsImpl::transportTcp(uint16_t port) -{ - clientSettings.protocol = "tcp"; - clientSettings.port = port; -} - -void ConnectionSettingsImpl::transportSsl(uint16_t port) -{ - clientSettings.protocol = "ssl"; - clientSettings.port = port; -} - -void ConnectionSettingsImpl::transportRdma(uint16_t port) -{ - clientSettings.protocol = "rdma"; - clientSettings.port = port; -} - -void ConnectionSettingsImpl::authAnonymous(const string& username) -{ - clientSettings.mechanism = "ANONYMOUS"; - clientSettings.username = username; -} - -void ConnectionSettingsImpl::authPlain(const string& username, const string& password) -{ - clientSettings.mechanism = "PLAIN"; - clientSettings.username = username; - clientSettings.password = password; -} - -void ConnectionSettingsImpl::authGssapi(const string& serviceName, uint32_t minSsf, uint32_t maxSsf) -{ - clientSettings.mechanism = "GSSAPI"; - clientSettings.service = serviceName; - clientSettings.minSsf = minSsf; - clientSettings.maxSsf = maxSsf; -} - -void ConnectionSettingsImpl::setRetry(int delayMin, int delayMax, int delayFactor) -{ - retryDelayMin = delayMin; - retryDelayMax = delayMax; - retryDelayFactor = delayFactor; -} - -const client::ConnectionSettings& ConnectionSettingsImpl::getClientSettings() const -{ - return clientSettings; -} - -void ConnectionSettingsImpl::getRetrySettings(int* min, int* max, int* factor) const -{ - *min = retryDelayMin; - *max = retryDelayMax; - *factor = retryDelayFactor; -} - -//================================================================== -// Wrappers -//================================================================== - -ConnectionSettings::ConnectionSettings(const ConnectionSettings& from) -{ - impl = new ConnectionSettingsImpl(*from.impl); -} - -ConnectionSettings::ConnectionSettings() -{ - impl = new ConnectionSettingsImpl(this); -} - -ConnectionSettings::ConnectionSettings(const char* url) -{ - impl = new ConnectionSettingsImpl(this, url); -} - -ConnectionSettings::~ConnectionSettings() -{ - delete impl; -} - -void ConnectionSettings::setAttr(const char* key, const Value& value) -{ - impl->setAttr(key, value); -} - -Value ConnectionSettings::getAttr(const char* key) const -{ - return impl->getAttr(key); -} - -const char* ConnectionSettings::getAttrString() const -{ - return impl->getAttrString().c_str(); -} - -void ConnectionSettings::transportTcp(uint16_t port) -{ - impl->transportTcp(port); -} - -void ConnectionSettings::transportSsl(uint16_t port) -{ - impl->transportSsl(port); -} - -void ConnectionSettings::transportRdma(uint16_t port) -{ - impl->transportRdma(port); -} - -void ConnectionSettings::authAnonymous(const char* username) -{ - impl->authAnonymous(username); -} - -void ConnectionSettings::authPlain(const char* username, const char* password) -{ - impl->authPlain(username, password); -} - -void ConnectionSettings::authGssapi(const char* serviceName, uint32_t minSsf, uint32_t maxSsf) -{ - impl->authGssapi(serviceName, minSsf, maxSsf); -} - -void ConnectionSettings::setRetry(int delayMin, int delayMax, int delayFactor) -{ - impl->setRetry(delayMin, delayMax, delayFactor); -} - diff --git a/qpid/cpp/src/qmf/ConnectionSettingsImpl.h b/qpid/cpp/src/qmf/ConnectionSettingsImpl.h deleted file mode 100644 index a177233cf3..0000000000 --- a/qpid/cpp/src/qmf/ConnectionSettingsImpl.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef _QmfConnectionSettingsImpl_ -#define _QmfConnectionSettingsImpl_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include "qmf/ConnectionSettings.h" -#include "qmf/Value.h" -#include "qpid/client/ConnectionSettings.h" -#include -#include - -namespace qmf { - - class ConnectionSettingsImpl { - ConnectionSettings* envelope; - qpid::client::ConnectionSettings clientSettings; - mutable std::string attrString; - int retryDelayMin; - int retryDelayMax; - int retryDelayFactor; - - public: - ConnectionSettingsImpl(ConnectionSettings* e); - ConnectionSettingsImpl(ConnectionSettings* e, const std::string& url); - ~ConnectionSettingsImpl() {} - void setAttr(const std::string& key, const Value& value); - Value getAttr(const std::string& key) const; - const std::string& getAttrString() const; - void transportTcp(uint16_t port); - void transportSsl(uint16_t port); - void transportRdma(uint16_t port); - void authAnonymous(const std::string& username); - void authPlain(const std::string& username, const std::string& password); - void authGssapi(const std::string& serviceName, uint32_t minSsf, uint32_t maxSsf); - void setRetry(int delayMin, int delayMax, int delayFactor); - - const qpid::client::ConnectionSettings& getClientSettings() const; - void getRetrySettings(int* delayMin, int* delayMax, int* delayFactor) const; - }; - -} - -#endif diff --git a/qpid/cpp/src/qmf/ConsoleEngine.cpp b/qpid/cpp/src/qmf/ConsoleEngine.cpp deleted file mode 100644 index e7991328ee..0000000000 --- a/qpid/cpp/src/qmf/ConsoleEngine.cpp +++ /dev/null @@ -1,1091 +0,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. - */ - -#include "qmf/ConsoleEngine.h" -#include "qmf/MessageImpl.h" -#include "qmf/SchemaImpl.h" -#include "qmf/Typecode.h" -#include "qmf/ObjectImpl.h" -#include "qmf/ObjectIdImpl.h" -#include "qmf/QueryImpl.h" -#include "qmf/ValueImpl.h" -#include "qmf/Protocol.h" -#include "qmf/SequenceManager.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace qmf; -using namespace qpid::framing; -using namespace qpid::sys; - -namespace qmf { - - struct MethodResponseImpl { - typedef boost::shared_ptr Ptr; - MethodResponse* envelope; - uint32_t status; - auto_ptr exception; - auto_ptr arguments; - - MethodResponseImpl(Buffer& buf); - ~MethodResponseImpl() { delete envelope; } - uint32_t getStatus() const { return status; } - const Value* getException() const { return exception.get(); } - const Value* getArgs() const { return arguments.get(); } - }; - - struct QueryResponseImpl { - typedef boost::shared_ptr Ptr; - QueryResponse *envelope; - uint32_t status; - auto_ptr exception; - vector results; - - QueryResponseImpl() : envelope(new QueryResponse(this)), status(0) {} - ~QueryResponseImpl() { delete envelope; } - uint32_t getStatus() const { return status; } - const Value* getException() const { return exception.get(); } - uint32_t getObjectCount() const { return results.size(); } - const Object* getObject(uint32_t idx) const; - }; - - struct ConsoleEventImpl { - typedef boost::shared_ptr Ptr; - ConsoleEvent::EventKind kind; - boost::shared_ptr agent; - string name; - boost::shared_ptr classKey; - Object* object; - void* context; - Event* event; - uint64_t timestamp; - uint32_t methodHandle; - MethodResponseImpl::Ptr methodResponse; - - ConsoleEventImpl(ConsoleEvent::EventKind k) : - kind(k), object(0), context(0), event(0), timestamp(0), methodHandle(0) {} - ~ConsoleEventImpl() {} - ConsoleEvent copy(); - }; - - struct BrokerEventImpl { - typedef boost::shared_ptr Ptr; - BrokerEvent::EventKind kind; - string name; - string exchange; - string bindingKey; - void* context; - QueryResponseImpl::Ptr queryResponse; - - BrokerEventImpl(BrokerEvent::EventKind k) : kind(k) {} - ~BrokerEventImpl() {} - BrokerEvent copy(); - }; - - struct AgentProxyImpl { - typedef boost::shared_ptr Ptr; - AgentProxy* envelope; - ConsoleEngineImpl* console; - BrokerProxyImpl* broker; - uint32_t agentBank; - string label; - - AgentProxyImpl(ConsoleEngineImpl* c, BrokerProxyImpl* b, uint32_t ab, const string& l) : - envelope(new AgentProxy(this)), console(c), broker(b), agentBank(ab), label(l) {} - ~AgentProxyImpl() {} - const string& getLabel() const { return label; } - }; - - class BrokerProxyImpl { - public: - typedef boost::shared_ptr Ptr; - - BrokerProxyImpl(BrokerProxy* e, ConsoleEngine& _console); - ~BrokerProxyImpl() {} - - void sessionOpened(SessionHandle& sh); - void sessionClosed(); - void startProtocol(); - - void sendBufferLH(Buffer& buf, const string& destination, const string& routingKey); - void handleRcvMessage(Message& message); - bool getXmtMessage(Message& item) const; - void popXmt(); - - bool getEvent(BrokerEvent& event) const; - void popEvent(); - - uint32_t agentCount() const; - const AgentProxy* getAgent(uint32_t idx) const; - void sendQuery(const Query& query, void* context, const AgentProxy* agent); - void sendGetRequestLH(SequenceContext::Ptr queryContext, const Query& query, const AgentProxyImpl* agent); - - void addBinding(const string& exchange, const string& key); - void staticRelease() { decOutstanding(); } - - private: - friend class StaticContext; - friend class QueryContext; - mutable Mutex lock; - BrokerProxy* envelope; - ConsoleEngineImpl* console; - string queueName; - Uuid brokerId; - SequenceManager seqMgr; - uint32_t requestsOutstanding; - bool topicBound; - vector agentList; - deque xmtQueue; - deque eventQueue; - -# define MA_BUFFER_SIZE 65536 - char outputBuffer[MA_BUFFER_SIZE]; - - BrokerEventImpl::Ptr eventDeclareQueue(const string& queueName); - BrokerEventImpl::Ptr eventBind(const string& exchange, const string& queue, const string& key); - BrokerEventImpl::Ptr eventSetupComplete(); - BrokerEventImpl::Ptr eventStable(); - BrokerEventImpl::Ptr eventQueryComplete(void* context, QueryResponseImpl::Ptr response); - - void handleBrokerResponse(Buffer& inBuffer, uint32_t seq); - void handlePackageIndication(Buffer& inBuffer, uint32_t seq); - void handleCommandComplete(Buffer& inBuffer, uint32_t seq); - void handleClassIndication(Buffer& inBuffer, uint32_t seq); - void handleMethodResponse(Buffer& inBuffer, uint32_t seq); - void handleHeartbeatIndication(Buffer& inBuffer, uint32_t seq); - void handleEventIndication(Buffer& inBuffer, uint32_t seq); - void handleSchemaResponse(Buffer& inBuffer, uint32_t seq); - ObjectImpl::Ptr handleObjectIndication(Buffer& inBuffer, uint32_t seq, bool prop, bool stat); - void incOutstandingLH(); - void decOutstanding(); - }; - - struct StaticContext : public SequenceContext { - StaticContext(BrokerProxyImpl& b) : broker(b) {} - ~StaticContext() {} - void reserve() {} - void release() { broker.staticRelease(); } - bool handleMessage(uint8_t opcode, uint32_t sequence, Buffer& buffer); - BrokerProxyImpl& broker; - }; - - struct QueryContext : public SequenceContext { - QueryContext(BrokerProxyImpl& b, void* u) : - broker(b), userContext(u), requestsOutstanding(0), queryResponse(new QueryResponseImpl()) {} - ~QueryContext() {} - void reserve(); - void release(); - bool handleMessage(uint8_t opcode, uint32_t sequence, Buffer& buffer); - - mutable Mutex lock; - BrokerProxyImpl& broker; - void* userContext; - uint32_t requestsOutstanding; - QueryResponseImpl::Ptr queryResponse; - }; - - class ConsoleEngineImpl { - public: - ConsoleEngineImpl(ConsoleEngine* e, const ConsoleSettings& settings = ConsoleSettings()); - ~ConsoleEngineImpl(); - - bool getEvent(ConsoleEvent& event) const; - void popEvent(); - - void addConnection(BrokerProxy& broker, void* context); - void delConnection(BrokerProxy& broker); - - uint32_t packageCount() const; - const string& getPackageName(uint32_t idx) const; - - uint32_t classCount(const char* packageName) const; - const SchemaClassKey* getClass(const char* packageName, uint32_t idx) const; - - ClassKind getClassKind(const SchemaClassKey* key) const; - const SchemaObjectClass* getObjectClass(const SchemaClassKey* key) const; - const SchemaEventClass* getEventClass(const SchemaClassKey* key) const; - - void bindPackage(const char* packageName); - void bindClass(const SchemaClassKey* key); - void bindClass(const char* packageName, const char* className); - - /* - void startSync(const Query& query, void* context, SyncQuery& sync); - void touchSync(SyncQuery& sync); - void endSync(SyncQuery& sync); - */ - - private: - friend class BrokerProxyImpl; - ConsoleEngine* envelope; - const ConsoleSettings& settings; - mutable Mutex lock; - deque eventQueue; - vector brokerList; - vector > bindingList; // exchange/key (empty exchange => QMF_EXCHANGE) - - // Declare a compare class for the class maps that compares the dereferenced - // class key pointers. The default behavior would be to compare the pointer - // addresses themselves. - struct KeyCompare { - bool operator()(const SchemaClassKeyImpl* left, const SchemaClassKeyImpl* right) const { - return *left < *right; - } - }; - - typedef map ObjectClassList; - typedef map EventClassList; - typedef map > PackageList; - - PackageList packages; - - void learnPackage(const string& packageName); - void learnClass(SchemaObjectClassImpl::Ptr cls); - void learnClass(SchemaEventClassImpl::Ptr cls); - bool haveClass(const SchemaClassKeyImpl& key) const; - SchemaObjectClassImpl::Ptr getSchema(const SchemaClassKeyImpl& key) const; - }; -} - -namespace { - const char* QMF_EXCHANGE = "qpid.management"; - const char* DIR_EXCHANGE = "amq.direct"; - const char* BROKER_KEY = "broker"; - const char* BROKER_PACKAGE = "org.apache.qpid.broker"; - const char* AGENT_CLASS = "agent"; - const char* BROKER_AGENT_KEY = "agent.1.0"; -} - -const Object* QueryResponseImpl::getObject(uint32_t idx) const -{ - vector::const_iterator iter = results.begin(); - - while (idx > 0) { - if (iter == results.end()) - return 0; - iter++; - idx--; - } - - return (*iter)->envelope; -} - -#define STRING_REF(s) {if (!s.empty()) item.s = const_cast(s.c_str());} - -ConsoleEvent ConsoleEventImpl::copy() -{ - ConsoleEvent item; - - ::memset(&item, 0, sizeof(ConsoleEvent)); - item.kind = kind; - item.agent = agent.get() ? agent->envelope : 0; - item.classKey = classKey.get(); - item.object = object; - item.context = context; - item.event = event; - item.timestamp = timestamp; - item.methodHandle = methodHandle; - item.methodResponse = methodResponse.get() ? methodResponse->envelope : 0; - - STRING_REF(name); - - return item; -} - -BrokerEvent BrokerEventImpl::copy() -{ - BrokerEvent item; - - ::memset(&item, 0, sizeof(BrokerEvent)); - item.kind = kind; - - STRING_REF(name); - STRING_REF(exchange); - STRING_REF(bindingKey); - item.context = context; - item.queryResponse = queryResponse.get() ? queryResponse->envelope : 0; - - return item; -} - -BrokerProxyImpl::BrokerProxyImpl(BrokerProxy* e, ConsoleEngine& _console) : - envelope(e), console(_console.impl) -{ - stringstream qn; - qpid::TcpAddress addr; - - SystemInfo::getLocalHostname(addr); - qn << "qmfc-" << SystemInfo::getProcessName() << "-" << addr << "-" << SystemInfo::getProcessId(); - queueName = qn.str(); - - seqMgr.setUnsolicitedContext(SequenceContext::Ptr(new StaticContext(*this))); -} - -void BrokerProxyImpl::sessionOpened(SessionHandle& /*sh*/) -{ - Mutex::ScopedLock _lock(lock); - agentList.clear(); - eventQueue.clear(); - xmtQueue.clear(); - eventQueue.push_back(eventDeclareQueue(queueName)); - eventQueue.push_back(eventBind(DIR_EXCHANGE, queueName, queueName)); - eventQueue.push_back(eventSetupComplete()); - - // TODO: Store session handle -} - -void BrokerProxyImpl::sessionClosed() -{ - Mutex::ScopedLock _lock(lock); - agentList.clear(); - eventQueue.clear(); - xmtQueue.clear(); -} - -void BrokerProxyImpl::startProtocol() -{ - Mutex::ScopedLock _lock(lock); - char rawbuffer[512]; - Buffer buffer(rawbuffer, 512); - - agentList.push_back(AgentProxyImpl::Ptr(new AgentProxyImpl(console, this, 0, "Agent embedded in broker"))); - - requestsOutstanding = 1; - topicBound = false; - uint32_t sequence(seqMgr.reserve()); - Protocol::encodeHeader(buffer, Protocol::OP_BROKER_REQUEST, sequence); - sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); - QPID_LOG(trace, "SENT BrokerRequest seq=" << sequence); -} - -void BrokerProxyImpl::sendBufferLH(Buffer& buf, const string& destination, const string& routingKey) -{ - uint32_t length = buf.getPosition(); - MessageImpl::Ptr message(new MessageImpl); - - buf.reset(); - buf.getRawData(message->body, length); - message->destination = destination; - message->routingKey = routingKey; - message->replyExchange = DIR_EXCHANGE; - message->replyKey = queueName; - - xmtQueue.push_back(message); -} - -void BrokerProxyImpl::handleRcvMessage(Message& message) -{ - Buffer inBuffer(message.body, message.length); - uint8_t opcode; - uint32_t sequence; - - while (Protocol::checkHeader(inBuffer, &opcode, &sequence)) - seqMgr.dispatch(opcode, sequence, inBuffer); -} - -bool BrokerProxyImpl::getXmtMessage(Message& item) const -{ - Mutex::ScopedLock _lock(lock); - if (xmtQueue.empty()) - return false; - item = xmtQueue.front()->copy(); - return true; -} - -void BrokerProxyImpl::popXmt() -{ - Mutex::ScopedLock _lock(lock); - if (!xmtQueue.empty()) - xmtQueue.pop_front(); -} - -bool BrokerProxyImpl::getEvent(BrokerEvent& event) const -{ - Mutex::ScopedLock _lock(lock); - if (eventQueue.empty()) - return false; - event = eventQueue.front()->copy(); - return true; -} - -void BrokerProxyImpl::popEvent() -{ - Mutex::ScopedLock _lock(lock); - if (!eventQueue.empty()) - eventQueue.pop_front(); -} - -uint32_t BrokerProxyImpl::agentCount() const -{ - Mutex::ScopedLock _lock(lock); - return agentList.size(); -} - -const AgentProxy* BrokerProxyImpl::getAgent(uint32_t idx) const -{ - Mutex::ScopedLock _lock(lock); - for (vector::const_iterator iter = agentList.begin(); - iter != agentList.end(); iter++) - if (idx-- == 0) - return (*iter)->envelope; - return 0; -} - -void BrokerProxyImpl::sendQuery(const Query& query, void* context, const AgentProxy* agent) -{ - SequenceContext::Ptr queryContext(new QueryContext(*this, context)); - Mutex::ScopedLock _lock(lock); - if (agent != 0) { - sendGetRequestLH(queryContext, query, agent->impl); - } else { - // TODO (optimization) only send queries to agents that have the requested class+package - for (vector::const_iterator iter = agentList.begin(); - iter != agentList.end(); iter++) { - sendGetRequestLH(queryContext, query, (*iter).get()); - } - } -} - -void BrokerProxyImpl::sendGetRequestLH(SequenceContext::Ptr queryContext, const Query& query, const AgentProxyImpl* agent) -{ - stringstream key; - Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); - uint32_t sequence(seqMgr.reserve(queryContext)); - - Protocol::encodeHeader(outBuffer, Protocol::OP_GET_QUERY, sequence); - query.impl->encode(outBuffer); - key << "agent.1." << agent->agentBank; - sendBufferLH(outBuffer, QMF_EXCHANGE, key.str()); - QPID_LOG(trace, "SENT GetQuery seq=" << sequence << " key=" << key.str()); -} - -void BrokerProxyImpl::addBinding(const string& exchange, const string& key) -{ - eventQueue.push_back(eventBind(exchange, queueName, key)); -} - -BrokerEventImpl::Ptr BrokerProxyImpl::eventDeclareQueue(const string& queueName) -{ - BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::DECLARE_QUEUE)); - event->name = queueName; - return event; -} - -BrokerEventImpl::Ptr BrokerProxyImpl::eventBind(const string& exchange, const string& queue, const string& key) -{ - BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::BIND)); - event->name = queue; - event->exchange = exchange; - event->bindingKey = key; - - return event; -} - -BrokerEventImpl::Ptr BrokerProxyImpl::eventSetupComplete() -{ - BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::SETUP_COMPLETE)); - return event; -} - -BrokerEventImpl::Ptr BrokerProxyImpl::eventStable() -{ - BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::STABLE)); - return event; -} - -BrokerEventImpl::Ptr BrokerProxyImpl::eventQueryComplete(void* context, QueryResponseImpl::Ptr response) -{ - BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::QUERY_COMPLETE)); - event->context = context; - event->queryResponse = response; - return event; -} - -void BrokerProxyImpl::handleBrokerResponse(Buffer& inBuffer, uint32_t seq) -{ - brokerId.decode(inBuffer); - QPID_LOG(trace, "RCVD BrokerResponse seq=" << seq << " brokerId=" << brokerId); - Mutex::ScopedLock _lock(lock); - Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); - uint32_t sequence(seqMgr.reserve()); - incOutstandingLH(); - Protocol::encodeHeader(outBuffer, Protocol::OP_PACKAGE_REQUEST, sequence); - sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY); - QPID_LOG(trace, "SENT PackageRequest seq=" << sequence); -} - -void BrokerProxyImpl::handlePackageIndication(Buffer& inBuffer, uint32_t seq) -{ - string package; - - inBuffer.getShortString(package); - QPID_LOG(trace, "RCVD PackageIndication seq=" << seq << " package=" << package); - console->learnPackage(package); - - Mutex::ScopedLock _lock(lock); - Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); - uint32_t sequence(seqMgr.reserve()); - incOutstandingLH(); - Protocol::encodeHeader(outBuffer, Protocol::OP_CLASS_QUERY, sequence); - outBuffer.putShortString(package); - sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY); - QPID_LOG(trace, "SENT ClassQuery seq=" << sequence << " package=" << package); -} - -void BrokerProxyImpl::handleCommandComplete(Buffer& inBuffer, uint32_t seq) -{ - string text; - uint32_t code = inBuffer.getLong(); - inBuffer.getShortString(text); - QPID_LOG(trace, "RCVD CommandComplete seq=" << seq << " code=" << code << " text=" << text); -} - -void BrokerProxyImpl::handleClassIndication(Buffer& inBuffer, uint32_t seq) -{ - uint8_t kind = inBuffer.getOctet(); - SchemaClassKeyImpl classKey(inBuffer); - - QPID_LOG(trace, "RCVD ClassIndication seq=" << seq << " kind=" << (int) kind << " key=" << classKey.str()); - - if (!console->haveClass(classKey)) { - Mutex::ScopedLock _lock(lock); - incOutstandingLH(); - Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); - uint32_t sequence(seqMgr.reserve()); - Protocol::encodeHeader(outBuffer, Protocol::OP_SCHEMA_REQUEST, sequence); - classKey.encode(outBuffer); - sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY); - QPID_LOG(trace, "SENT SchemaRequest seq=" << sequence <<" key=" << classKey.str()); - } -} - -void BrokerProxyImpl::handleMethodResponse(Buffer& /*inBuffer*/, uint32_t /*seq*/) -{ - // TODO -} - -void BrokerProxyImpl::handleHeartbeatIndication(Buffer& /*inBuffer*/, uint32_t /*seq*/) -{ - // TODO -} - -void BrokerProxyImpl::handleEventIndication(Buffer& /*inBuffer*/, uint32_t /*seq*/) -{ - // TODO -} - -void BrokerProxyImpl::handleSchemaResponse(Buffer& inBuffer, uint32_t seq) -{ - SchemaObjectClassImpl::Ptr oClassPtr; - SchemaEventClassImpl::Ptr eClassPtr; - uint8_t kind = inBuffer.getOctet(); - const SchemaClassKeyImpl* key; - if (kind == CLASS_OBJECT) { - oClassPtr.reset(new SchemaObjectClassImpl(inBuffer)); - console->learnClass(oClassPtr); - key = oClassPtr->getClassKey()->impl; - QPID_LOG(trace, "RCVD SchemaResponse seq=" << seq << " kind=object key=" << key->str()); - - // - // If we have just learned about the org.apache.qpid.broker:agent class, send a get - // request for the current list of agents so we can have it on-hand before we declare - // this session "stable". - // - if (key->getClassName() == AGENT_CLASS && key->getPackageName() == BROKER_PACKAGE) { - Mutex::ScopedLock _lock(lock); - incOutstandingLH(); - Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); - uint32_t sequence(seqMgr.reserve()); - Protocol::encodeHeader(outBuffer, Protocol::OP_GET_QUERY, sequence); - FieldTable ft; - ft.setString("_class", AGENT_CLASS); - ft.setString("_package", BROKER_PACKAGE); - ft.encode(outBuffer); - sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_AGENT_KEY); - QPID_LOG(trace, "SENT GetQuery seq=" << sequence << " key=" << BROKER_AGENT_KEY); - } - } else if (kind == CLASS_EVENT) { - eClassPtr.reset(new SchemaEventClassImpl(inBuffer)); - console->learnClass(eClassPtr); - key = eClassPtr->getClassKey()->impl; - QPID_LOG(trace, "RCVD SchemaResponse seq=" << seq << " kind=event key=" << key->str()); - } - else { - QPID_LOG(error, "BrokerProxyImpl::handleSchemaResponse received unknown class kind: " << (int) kind); - } -} - -ObjectImpl::Ptr BrokerProxyImpl::handleObjectIndication(Buffer& inBuffer, uint32_t seq, bool prop, bool stat) -{ - SchemaClassKeyImpl classKey(inBuffer); - QPID_LOG(trace, "RCVD ObjectIndication seq=" << seq << " key=" << classKey.str()); - - SchemaObjectClassImpl::Ptr schema = console->getSchema(classKey); - if (schema.get() == 0) { - QPID_LOG(trace, "No Schema Found for ObjectIndication. seq=" << seq << " key=" << classKey.str()); - return ObjectImpl::Ptr(); - } - - return ObjectImpl::Ptr(new ObjectImpl(schema->envelope, inBuffer, prop, stat, true)); -} - -void BrokerProxyImpl::incOutstandingLH() -{ - requestsOutstanding++; -} - -void BrokerProxyImpl::decOutstanding() -{ - Mutex::ScopedLock _lock(lock); - requestsOutstanding--; - if (requestsOutstanding == 0 && !topicBound) { - topicBound = true; - for (vector >::const_iterator iter = console->bindingList.begin(); - iter != console->bindingList.end(); iter++) { - string exchange(iter->first.empty() ? QMF_EXCHANGE : iter->first); - string key(iter->second); - eventQueue.push_back(eventBind(exchange, queueName, key)); - } - eventQueue.push_back(eventStable()); - } -} - -MethodResponseImpl::MethodResponseImpl(Buffer& buf) : envelope(new MethodResponse(this)) -{ - string text; - - status = buf.getLong(); - buf.getMediumString(text); - exception.reset(new Value(TYPE_LSTR)); - exception->setString(text.c_str()); - - // TODO: Parse schema-specific output arguments. - arguments.reset(new Value(TYPE_MAP)); -} - -bool StaticContext::handleMessage(uint8_t opcode, uint32_t sequence, Buffer& buffer) -{ - bool completeContext = false; - if (opcode == Protocol::OP_BROKER_RESPONSE) { - broker.handleBrokerResponse(buffer, sequence); - completeContext = true; - } - else if (opcode == Protocol::OP_COMMAND_COMPLETE) { - broker.handleCommandComplete(buffer, sequence); - completeContext = true; - } - else if (opcode == Protocol::OP_SCHEMA_RESPONSE) { - broker.handleSchemaResponse(buffer, sequence); - completeContext = true; - } - else if (opcode == Protocol::OP_PACKAGE_INDICATION) - broker.handlePackageIndication(buffer, sequence); - else if (opcode == Protocol::OP_CLASS_INDICATION) - broker.handleClassIndication(buffer, sequence); - else if (opcode == Protocol::OP_HEARTBEAT_INDICATION) - broker.handleHeartbeatIndication(buffer, sequence); - else if (opcode == Protocol::OP_EVENT_INDICATION) - broker.handleEventIndication(buffer, sequence); - else if (opcode == Protocol::OP_PROPERTY_INDICATION) - broker.handleObjectIndication(buffer, sequence, true, false); - else if (opcode == Protocol::OP_STATISTIC_INDICATION) - broker.handleObjectIndication(buffer, sequence, false, true); - else if (opcode == Protocol::OP_OBJECT_INDICATION) - broker.handleObjectIndication(buffer, sequence, true, true); - else { - QPID_LOG(trace, "StaticContext::handleMessage invalid opcode: " << opcode); - completeContext = true; - } - - return completeContext; -} - -void QueryContext::reserve() -{ - Mutex::ScopedLock _lock(lock); - requestsOutstanding++; -} - -void QueryContext::release() -{ - Mutex::ScopedLock _lock(lock); - if (--requestsOutstanding == 0) { - broker.eventQueue.push_back(broker.eventQueryComplete(userContext, queryResponse)); - } -} - -bool QueryContext::handleMessage(uint8_t opcode, uint32_t sequence, Buffer& buffer) -{ - bool completeContext = false; - ObjectImpl::Ptr object; - - if (opcode == Protocol::OP_COMMAND_COMPLETE) { - broker.handleCommandComplete(buffer, sequence); - completeContext = true; - } - else if (opcode == Protocol::OP_OBJECT_INDICATION) { - object = broker.handleObjectIndication(buffer, sequence, true, true); - if (object.get() != 0) - queryResponse->results.push_back(object); - } - else { - QPID_LOG(trace, "QueryContext::handleMessage invalid opcode: " << opcode); - completeContext = true; - } - - return completeContext; -} - -ConsoleEngineImpl::ConsoleEngineImpl(ConsoleEngine* e, const ConsoleSettings& s) : - envelope(e), settings(s) -{ - bindingList.push_back(pair(string(), "schema.#")); - if (settings.rcvObjects && settings.rcvEvents && settings.rcvHeartbeats && !settings.userBindings) { - bindingList.push_back(pair(string(), "console.#")); - } else { - if (settings.rcvObjects && !settings.userBindings) - bindingList.push_back(pair(string(), "console.obj.#")); - else - bindingList.push_back(pair(string(), "console.obj.*.*.org.apache.qpid.broker.agent")); - if (settings.rcvEvents) - bindingList.push_back(pair(string(), "console.event.#")); - if (settings.rcvHeartbeats) - bindingList.push_back(pair(string(), "console.heartbeat.#")); - } -} - -ConsoleEngineImpl::~ConsoleEngineImpl() -{ - // This function intentionally left blank. -} - -bool ConsoleEngineImpl::getEvent(ConsoleEvent& event) const -{ - Mutex::ScopedLock _lock(lock); - if (eventQueue.empty()) - return false; - event = eventQueue.front()->copy(); - return true; -} - -void ConsoleEngineImpl::popEvent() -{ - Mutex::ScopedLock _lock(lock); - if (!eventQueue.empty()) - eventQueue.pop_front(); -} - -void ConsoleEngineImpl::addConnection(BrokerProxy& broker, void* /*context*/) -{ - Mutex::ScopedLock _lock(lock); - brokerList.push_back(broker.impl); -} - -void ConsoleEngineImpl::delConnection(BrokerProxy& broker) -{ - Mutex::ScopedLock _lock(lock); - for (vector::iterator iter = brokerList.begin(); - iter != brokerList.end(); iter++) - if (*iter == broker.impl) { - brokerList.erase(iter); - break; - } -} - -uint32_t ConsoleEngineImpl::packageCount() const -{ - Mutex::ScopedLock _lock(lock); - return packages.size(); -} - -const string& ConsoleEngineImpl::getPackageName(uint32_t idx) const -{ - const static string empty; - - Mutex::ScopedLock _lock(lock); - if (idx >= packages.size()) - return empty; - - PackageList::const_iterator iter = packages.begin(); - for (uint32_t i = 0; i < idx; i++) iter++; - return iter->first; -} - -uint32_t ConsoleEngineImpl::classCount(const char* packageName) const -{ - Mutex::ScopedLock _lock(lock); - PackageList::const_iterator pIter = packages.find(packageName); - if (pIter == packages.end()) - return 0; - - const ObjectClassList& oList = pIter->second.first; - const EventClassList& eList = pIter->second.second; - - return oList.size() + eList.size(); -} - -const SchemaClassKey* ConsoleEngineImpl::getClass(const char* packageName, uint32_t idx) const -{ - Mutex::ScopedLock _lock(lock); - PackageList::const_iterator pIter = packages.find(packageName); - if (pIter == packages.end()) - return 0; - - const ObjectClassList& oList = pIter->second.first; - const EventClassList& eList = pIter->second.second; - uint32_t count = 0; - - for (ObjectClassList::const_iterator oIter = oList.begin(); - oIter != oList.end(); oIter++) { - if (count == idx) - return oIter->second->getClassKey(); - count++; - } - - for (EventClassList::const_iterator eIter = eList.begin(); - eIter != eList.end(); eIter++) { - if (count == idx) - return eIter->second->getClassKey(); - count++; - } - - return 0; -} - -ClassKind ConsoleEngineImpl::getClassKind(const SchemaClassKey* key) const -{ - Mutex::ScopedLock _lock(lock); - PackageList::const_iterator pIter = packages.find(key->getPackageName()); - if (pIter == packages.end()) - return CLASS_OBJECT; - - const EventClassList& eList = pIter->second.second; - if (eList.find(key->impl) != eList.end()) - return CLASS_EVENT; - return CLASS_OBJECT; -} - -const SchemaObjectClass* ConsoleEngineImpl::getObjectClass(const SchemaClassKey* key) const -{ - Mutex::ScopedLock _lock(lock); - PackageList::const_iterator pIter = packages.find(key->getPackageName()); - if (pIter == packages.end()) - return 0; - - const ObjectClassList& oList = pIter->second.first; - ObjectClassList::const_iterator iter = oList.find(key->impl); - if (iter == oList.end()) - return 0; - return iter->second->envelope; -} - -const SchemaEventClass* ConsoleEngineImpl::getEventClass(const SchemaClassKey* key) const -{ - Mutex::ScopedLock _lock(lock); - PackageList::const_iterator pIter = packages.find(key->getPackageName()); - if (pIter == packages.end()) - return 0; - - const EventClassList& eList = pIter->second.second; - EventClassList::const_iterator iter = eList.find(key->impl); - if (iter == eList.end()) - return 0; - return iter->second->envelope; -} - -void ConsoleEngineImpl::bindPackage(const char* packageName) -{ - stringstream key; - key << "console.obj.*.*." << packageName << ".#"; - Mutex::ScopedLock _lock(lock); - bindingList.push_back(pair(string(), key.str())); - for (vector::iterator iter = brokerList.begin(); - iter != brokerList.end(); iter++) - (*iter)->addBinding(QMF_EXCHANGE, key.str()); -} - -void ConsoleEngineImpl::bindClass(const SchemaClassKey* classKey) -{ - stringstream key; - key << "console.obj.*.*." << classKey->getPackageName() << "." << classKey->getClassName() << ".#"; - Mutex::ScopedLock _lock(lock); - bindingList.push_back(pair(string(), key.str())); - for (vector::iterator iter = brokerList.begin(); - iter != brokerList.end(); iter++) - (*iter)->addBinding(QMF_EXCHANGE, key.str()); -} - -void ConsoleEngineImpl::bindClass(const char* packageName, const char* className) -{ - stringstream key; - key << "console.obj.*.*." << packageName << "." << className << ".#"; - Mutex::ScopedLock _lock(lock); - bindingList.push_back(pair(string(), key.str())); - for (vector::iterator iter = brokerList.begin(); - iter != brokerList.end(); iter++) - (*iter)->addBinding(QMF_EXCHANGE, key.str()); -} - -/* -void ConsoleEngineImpl::startSync(const Query& query, void* context, SyncQuery& sync) -{ -} - -void ConsoleEngineImpl::touchSync(SyncQuery& sync) -{ -} - -void ConsoleEngineImpl::endSync(SyncQuery& sync) -{ -} -*/ - -void ConsoleEngineImpl::learnPackage(const string& packageName) -{ - Mutex::ScopedLock _lock(lock); - if (packages.find(packageName) == packages.end()) - packages.insert(pair > - (packageName, pair(ObjectClassList(), EventClassList()))); -} - -void ConsoleEngineImpl::learnClass(SchemaObjectClassImpl::Ptr cls) -{ - Mutex::ScopedLock _lock(lock); - const SchemaClassKey* key = cls->getClassKey(); - PackageList::iterator pIter = packages.find(key->getPackageName()); - if (pIter == packages.end()) - return; - - ObjectClassList& list = pIter->second.first; - if (list.find(key->impl) == list.end()) - list[key->impl] = cls; -} - -void ConsoleEngineImpl::learnClass(SchemaEventClassImpl::Ptr cls) -{ - Mutex::ScopedLock _lock(lock); - const SchemaClassKey* key = cls->getClassKey(); - PackageList::iterator pIter = packages.find(key->getPackageName()); - if (pIter == packages.end()) - return; - - EventClassList& list = pIter->second.second; - if (list.find(key->impl) == list.end()) - list[key->impl] = cls; -} - -bool ConsoleEngineImpl::haveClass(const SchemaClassKeyImpl& key) const -{ - Mutex::ScopedLock _lock(lock); - PackageList::const_iterator pIter = packages.find(key.getPackageName()); - if (pIter == packages.end()) - return false; - - const ObjectClassList& oList = pIter->second.first; - const EventClassList& eList = pIter->second.second; - - return oList.find(&key) != oList.end() || eList.find(&key) != eList.end(); -} - -SchemaObjectClassImpl::Ptr ConsoleEngineImpl::getSchema(const SchemaClassKeyImpl& key) const -{ - Mutex::ScopedLock _lock(lock); - PackageList::const_iterator pIter = packages.find(key.getPackageName()); - if (pIter == packages.end()) - return SchemaObjectClassImpl::Ptr(); - - const ObjectClassList& oList = pIter->second.first; - ObjectClassList::const_iterator iter = oList.find(&key); - if (iter == oList.end()) - return SchemaObjectClassImpl::Ptr(); - - return iter->second; -} - -//================================================================== -// Wrappers -//================================================================== - -AgentProxy::AgentProxy(AgentProxyImpl* i) : impl(i) {} -AgentProxy::~AgentProxy() { delete impl; } -const char* AgentProxy::getLabel() const { return impl->getLabel().c_str(); } - -BrokerProxy::BrokerProxy(ConsoleEngine& console) : impl(new BrokerProxyImpl(this, console)) {} -BrokerProxy::~BrokerProxy() { delete impl; } -void BrokerProxy::sessionOpened(SessionHandle& sh) { impl->sessionOpened(sh); } -void BrokerProxy::sessionClosed() { impl->sessionClosed(); } -void BrokerProxy::startProtocol() { impl->startProtocol(); } -void BrokerProxy::handleRcvMessage(Message& message) { impl->handleRcvMessage(message); } -bool BrokerProxy::getXmtMessage(Message& item) const { return impl->getXmtMessage(item); } -void BrokerProxy::popXmt() { impl->popXmt(); } -bool BrokerProxy::getEvent(BrokerEvent& event) const { return impl->getEvent(event); } -void BrokerProxy::popEvent() { impl->popEvent(); } -uint32_t BrokerProxy::agentCount() const { return impl->agentCount(); } -const AgentProxy* BrokerProxy::getAgent(uint32_t idx) const { return impl->getAgent(idx); } -void BrokerProxy::sendQuery(const Query& query, void* context, const AgentProxy* agent) { impl->sendQuery(query, context, agent); } - -MethodResponse::MethodResponse(MethodResponseImpl* i) : impl(i) {} -MethodResponse::~MethodResponse() {} -uint32_t MethodResponse::getStatus() const { return impl->getStatus(); } -const Value* MethodResponse::getException() const { return impl->getException(); } -const Value* MethodResponse::getArgs() const { return impl->getArgs(); } - -QueryResponse::QueryResponse(QueryResponseImpl* i) : impl(i) {} -QueryResponse::~QueryResponse() {} -uint32_t QueryResponse::getStatus() const { return impl->getStatus(); } -const Value* QueryResponse::getException() const { return impl->getException(); } -uint32_t QueryResponse::getObjectCount() const { return impl->getObjectCount(); } -const Object* QueryResponse::getObject(uint32_t idx) const { return impl->getObject(idx); } - -ConsoleEngine::ConsoleEngine(const ConsoleSettings& settings) : impl(new ConsoleEngineImpl(this, settings)) {} -ConsoleEngine::~ConsoleEngine() { delete impl; } -bool ConsoleEngine::getEvent(ConsoleEvent& event) const { return impl->getEvent(event); } -void ConsoleEngine::popEvent() { impl->popEvent(); } -void ConsoleEngine::addConnection(BrokerProxy& broker, void* context) { impl->addConnection(broker, context); } -void ConsoleEngine::delConnection(BrokerProxy& broker) { impl->delConnection(broker); } -uint32_t ConsoleEngine::packageCount() const { return impl->packageCount(); } -const char* ConsoleEngine::getPackageName(uint32_t idx) const { return impl->getPackageName(idx).c_str(); } -uint32_t ConsoleEngine::classCount(const char* packageName) const { return impl->classCount(packageName); } -const SchemaClassKey* ConsoleEngine::getClass(const char* packageName, uint32_t idx) const { return impl->getClass(packageName, idx); } -ClassKind ConsoleEngine::getClassKind(const SchemaClassKey* key) const { return impl->getClassKind(key); } -const SchemaObjectClass* ConsoleEngine::getObjectClass(const SchemaClassKey* key) const { return impl->getObjectClass(key); } -const SchemaEventClass* ConsoleEngine::getEventClass(const SchemaClassKey* key) const { return impl->getEventClass(key); } -void ConsoleEngine::bindPackage(const char* packageName) { impl->bindPackage(packageName); } -void ConsoleEngine::bindClass(const SchemaClassKey* key) { impl->bindClass(key); } -void ConsoleEngine::bindClass(const char* packageName, const char* className) { impl->bindClass(packageName, className); } -//void ConsoleEngine::startSync(const Query& query, void* context, SyncQuery& sync) { impl->startSync(query, context, sync); } -//void ConsoleEngine::touchSync(SyncQuery& sync) { impl->touchSync(sync); } -//void ConsoleEngine::endSync(SyncQuery& sync) { impl->endSync(sync); } - - diff --git a/qpid/cpp/src/qmf/ConsoleEngine.h b/qpid/cpp/src/qmf/ConsoleEngine.h deleted file mode 100644 index 457e83ad58..0000000000 --- a/qpid/cpp/src/qmf/ConsoleEngine.h +++ /dev/null @@ -1,222 +0,0 @@ -#ifndef _QmfConsoleEngine_ -#define _QmfConsoleEngine_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace qmf { - - class ConsoleEngine; - class ConsoleEngineImpl; - class BrokerProxyImpl; - class AgentProxy; - class AgentProxyImpl; - class MethodResponseImpl; - class QueryResponseImpl; - class QueryContext; - - /** - * - */ - class MethodResponse { - public: - MethodResponse(MethodResponseImpl* impl); - ~MethodResponse(); - uint32_t getStatus() const; - const Value* getException() const; - const Value* getArgs() const; - - private: - friend class ConsoleEngineImpl; - MethodResponseImpl* impl; - }; - - /** - * - */ - class QueryResponse { - public: - QueryResponse(QueryResponseImpl* impl); - ~QueryResponse(); - uint32_t getStatus() const; - const Value* getException() const; - uint32_t getObjectCount() const; - const Object* getObject(uint32_t idx) const; - - private: - friend class QueryContext; - QueryResponseImpl *impl; - }; - - /** - * - */ - struct ConsoleEvent { - enum EventKind { - AGENT_ADDED = 1, - AGENT_DELETED = 2, - NEW_PACKAGE = 3, - NEW_CLASS = 4, - OBJECT_UPDATE = 5, - EVENT_RECEIVED = 7, - AGENT_HEARTBEAT = 8, - METHOD_RESPONSE = 9 - }; - - EventKind kind; - AgentProxy* agent; // (AGENT_[ADDED|DELETED|HEARTBEAT]) - char* name; // (NEW_PACKAGE) - SchemaClassKey* classKey; // (NEW_CLASS) - Object* object; // (OBJECT_UPDATE) - void* context; // (OBJECT_UPDATE) - Event* event; // (EVENT_RECEIVED) - uint64_t timestamp; // (AGENT_HEARTBEAT) - uint32_t methodHandle; // (METHOD_RESPONSE) - MethodResponse* methodResponse; // (METHOD_RESPONSE) - QueryResponse* queryResponse; // (QUERY_COMPLETE) - }; - - /** - * - */ - struct BrokerEvent { - enum EventKind { - BROKER_INFO = 10, - DECLARE_QUEUE = 11, - DELETE_QUEUE = 12, - BIND = 13, - UNBIND = 14, - SETUP_COMPLETE = 15, - STABLE = 16, - QUERY_COMPLETE = 17 - }; - - EventKind kind; - char* name; // ([DECLARE|DELETE]_QUEUE, [UN]BIND) - char* exchange; // ([UN]BIND) - char* bindingKey; // ([UN]BIND) - void* context; // (QUERY_COMPLETE) - QueryResponse* queryResponse; // (QUERY_COMPLETE) - }; - - /** - * - */ - class AgentProxy { - public: - AgentProxy(AgentProxyImpl* impl); - ~AgentProxy(); - const char* getLabel() const; - - private: - friend class BrokerProxyImpl; - AgentProxyImpl* impl; - }; - - /** - * - */ - class BrokerProxy { - public: - BrokerProxy(ConsoleEngine& console); - ~BrokerProxy(); - - void sessionOpened(SessionHandle& sh); - void sessionClosed(); - void startProtocol(); - - void handleRcvMessage(Message& message); - bool getXmtMessage(Message& item) const; - void popXmt(); - - bool getEvent(BrokerEvent& event) const; - void popEvent(); - - uint32_t agentCount() const; - const AgentProxy* getAgent(uint32_t idx) const; - void sendQuery(const Query& query, void* context, const AgentProxy* agent = 0); - - private: - friend class ConsoleEngineImpl; - BrokerProxyImpl* impl; - }; - - // TODO - move this to a public header - struct ConsoleSettings { - bool rcvObjects; - bool rcvEvents; - bool rcvHeartbeats; - bool userBindings; - - ConsoleSettings() : - rcvObjects(true), - rcvEvents(true), - rcvHeartbeats(true), - userBindings(false) {} - }; - - class ConsoleEngine { - public: - ConsoleEngine(const ConsoleSettings& settings = ConsoleSettings()); - ~ConsoleEngine(); - - bool getEvent(ConsoleEvent& event) const; - void popEvent(); - - void addConnection(BrokerProxy& broker, void* context); - void delConnection(BrokerProxy& broker); - - uint32_t packageCount() const; - const char* getPackageName(uint32_t idx) const; - - uint32_t classCount(const char* packageName) const; - const SchemaClassKey* getClass(const char* packageName, uint32_t idx) const; - - ClassKind getClassKind(const SchemaClassKey* key) const; - const SchemaObjectClass* getObjectClass(const SchemaClassKey* key) const; - const SchemaEventClass* getEventClass(const SchemaClassKey* key) const; - - void bindPackage(const char* packageName); - void bindClass(const SchemaClassKey* key); - void bindClass(const char* packageName, const char* className); - - /* - void startSync(const Query& query, void* context, SyncQuery& sync); - void touchSync(SyncQuery& sync); - void endSync(SyncQuery& sync); - */ - - private: - friend class BrokerProxyImpl; - friend class AgentProxyImpl; - ConsoleEngineImpl* impl; - }; -} - -#endif - diff --git a/qpid/cpp/src/qmf/Event.h b/qpid/cpp/src/qmf/Event.h deleted file mode 100644 index f20c6d2fb1..0000000000 --- a/qpid/cpp/src/qmf/Event.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef _QmfEvent_ -#define _QmfEvent_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -namespace qmf { - - class Event { - }; -} - -#endif - diff --git a/qpid/cpp/src/qmf/Message.h b/qpid/cpp/src/qmf/Message.h deleted file mode 100644 index 52b8ba72d3..0000000000 --- a/qpid/cpp/src/qmf/Message.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef _QmfMessage_ -#define _QmfMessage_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include "qpid/sys/IntegerTypes.h" - -namespace qmf { - - struct Message { - char* body; - uint32_t length; - char* destination; - char* routingKey; - char* replyExchange; - char* replyKey; - char* userId; - }; - -} - -#endif diff --git a/qpid/cpp/src/qmf/MessageImpl.cpp b/qpid/cpp/src/qmf/MessageImpl.cpp deleted file mode 100644 index f2625c7202..0000000000 --- a/qpid/cpp/src/qmf/MessageImpl.cpp +++ /dev/null @@ -1,43 +0,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. - */ - -#include "qmf/MessageImpl.h" -#include - -using namespace std; -using namespace qmf; - -#define STRING_REF(s) {if (!s.empty()) item.s = const_cast(s.c_str());} - -Message MessageImpl::copy() -{ - Message item; - - ::memset(&item, 0, sizeof(Message)); - item.body = const_cast(body.c_str()); - item.length = body.length(); - STRING_REF(destination); - STRING_REF(routingKey); - STRING_REF(replyExchange); - STRING_REF(replyKey); - STRING_REF(userId); - - return item; -} - diff --git a/qpid/cpp/src/qmf/MessageImpl.h b/qpid/cpp/src/qmf/MessageImpl.h deleted file mode 100644 index 137f435699..0000000000 --- a/qpid/cpp/src/qmf/MessageImpl.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _QmfMessageImpl_ -#define _QmfMessageImpl_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include "qmf/Message.h" -#include -#include - -namespace qmf { - - struct MessageImpl { - typedef boost::shared_ptr Ptr; - std::string body; - std::string destination; - std::string routingKey; - std::string replyExchange; - std::string replyKey; - std::string userId; - - Message copy(); - }; -} - -#endif diff --git a/qpid/cpp/src/qmf/Object.h b/qpid/cpp/src/qmf/Object.h deleted file mode 100644 index 9cb3224d9b..0000000000 --- a/qpid/cpp/src/qmf/Object.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef _QmfObject_ -#define _QmfObject_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include -#include -#include - -namespace qmf { - - struct ObjectImpl; - class Object { - public: - Object(const SchemaObjectClass* type); - Object(ObjectImpl* impl); - Object(const Object& from); - virtual ~Object(); - - void destroy(); - const ObjectId* getObjectId() const; - void setObjectId(ObjectId* oid); - const SchemaObjectClass* getClass() const; - Value* getValue(char* key) const; - - ObjectImpl* impl; - }; -} - -#endif - diff --git a/qpid/cpp/src/qmf/ObjectId.h b/qpid/cpp/src/qmf/ObjectId.h deleted file mode 100644 index e894e0b39c..0000000000 --- a/qpid/cpp/src/qmf/ObjectId.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef _QmfObjectId_ -#define _QmfObjectId_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include - -namespace qmf { - - // TODO: Add to/from string and << operator - - struct ObjectIdImpl; - class ObjectId { - public: - ObjectId(); - ObjectId(const ObjectId& from); - ObjectId(ObjectIdImpl* impl); - ~ObjectId(); - - uint64_t getObjectNum() const; - uint32_t getObjectNumHi() const; - uint32_t getObjectNumLo() const; - bool isDurable() const; - - bool operator==(const ObjectId& other) const; - bool operator<(const ObjectId& other) const; - bool operator>(const ObjectId& other) const; - bool operator<=(const ObjectId& other) const; - bool operator>=(const ObjectId& other) const; - - ObjectIdImpl* impl; - }; -} - -#endif - diff --git a/qpid/cpp/src/qmf/ObjectIdImpl.cpp b/qpid/cpp/src/qmf/ObjectIdImpl.cpp deleted file mode 100644 index c0618ccc49..0000000000 --- a/qpid/cpp/src/qmf/ObjectIdImpl.cpp +++ /dev/null @@ -1,192 +0,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. - */ - -#include "qmf/ObjectIdImpl.h" -#include - -using namespace std; -using namespace qmf; -using qpid::framing::Buffer; - - -void AgentAttachment::setBanks(uint32_t broker, uint32_t agent) -{ - first = - ((uint64_t) (broker & 0x000fffff)) << 28 | - ((uint64_t) (agent & 0x0fffffff)); -} - -ObjectIdImpl::ObjectIdImpl(Buffer& buffer) : envelope(new ObjectId(this)), agent(0) -{ - decode(buffer); -} - -ObjectIdImpl::ObjectIdImpl(AgentAttachment* a, uint8_t flags, uint16_t seq, uint64_t object) : - envelope(new ObjectId(this)), agent(a) -{ - first = - ((uint64_t) (flags & 0x0f)) << 60 | - ((uint64_t) (seq & 0x0fff)) << 48; - second = object; -} - -void ObjectIdImpl::decode(Buffer& buffer) -{ - first = buffer.getLongLong(); - second = buffer.getLongLong(); -} - -void ObjectIdImpl::encode(Buffer& buffer) const -{ - if (agent == 0) - buffer.putLongLong(first); - else - buffer.putLongLong(first | agent->first); - buffer.putLongLong(second); -} - -void ObjectIdImpl::fromString(const std::string& repr) -{ -#define FIELDS 5 -#if defined (_WIN32) && !defined (atoll) -# define atoll(X) _atoi64(X) -#endif - - std::string copy(repr.c_str()); - char* cText; - char* field[FIELDS]; - bool atFieldStart = true; - int idx = 0; - - cText = const_cast(copy.c_str()); - for (char* cursor = cText; *cursor; cursor++) { - if (atFieldStart) { - if (idx >= FIELDS) - return; // TODO error - field[idx++] = cursor; - atFieldStart = false; - } else { - if (*cursor == '-') { - *cursor = '\0'; - atFieldStart = true; - } - } - } - - if (idx != FIELDS) - return; // TODO error - - first = (atoll(field[0]) << 60) + - (atoll(field[1]) << 48) + - (atoll(field[2]) << 28) + - atoll(field[3]); - second = atoll(field[4]); - agent = 0; -} - -std::string ObjectIdImpl::asString() const -{ - stringstream val; - - val << getFlags() << "-" << getSequence() << "-" << getBrokerBank() << "-" << - getAgentBank() << "-" << getObjectNum(); - return val.str(); -} - -bool ObjectIdImpl::operator==(const ObjectIdImpl& other) const -{ - uint64_t otherFirst = agent == 0 ? other.first : other.first & 0xffff000000000000LL; - - return first == otherFirst && second == other.second; -} - -bool ObjectIdImpl::operator<(const ObjectIdImpl& other) const -{ - uint64_t otherFirst = agent == 0 ? other.first : other.first & 0xffff000000000000LL; - - return (first < otherFirst) || ((first == otherFirst) && (second < other.second)); -} - -bool ObjectIdImpl::operator>(const ObjectIdImpl& other) const -{ - uint64_t otherFirst = agent == 0 ? other.first : other.first & 0xffff000000000000LL; - - return (first > otherFirst) || ((first == otherFirst) && (second > other.second)); -} - - -//================================================================== -// Wrappers -//================================================================== - -ObjectId::ObjectId() : impl(new ObjectIdImpl(this)) {} - -ObjectId::ObjectId(const ObjectId& from) : impl(new ObjectIdImpl(*(from.impl))) {} - -ObjectId::ObjectId(ObjectIdImpl* i) : impl(i) {} - -ObjectId::~ObjectId() -{ - delete impl; -} - -uint64_t ObjectId::getObjectNum() const -{ - return impl->getObjectNum(); -} - -uint32_t ObjectId::getObjectNumHi() const -{ - return impl->getObjectNumHi(); -} - -uint32_t ObjectId::getObjectNumLo() const -{ - return impl->getObjectNumLo(); -} - -bool ObjectId::isDurable() const -{ - return impl->isDurable(); -} - -bool ObjectId::operator==(const ObjectId& other) const -{ - return *impl == *other.impl; -} - -bool ObjectId::operator<(const ObjectId& other) const -{ - return *impl < *other.impl; -} - -bool ObjectId::operator>(const ObjectId& other) const -{ - return *impl > *other.impl; -} - -bool ObjectId::operator<=(const ObjectId& other) const -{ - return !(*impl > *other.impl); -} - -bool ObjectId::operator>=(const ObjectId& other) const -{ - return !(*impl < *other.impl); -} diff --git a/qpid/cpp/src/qmf/ObjectIdImpl.h b/qpid/cpp/src/qmf/ObjectIdImpl.h deleted file mode 100644 index 38d231237f..0000000000 --- a/qpid/cpp/src/qmf/ObjectIdImpl.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef _QmfObjectIdImpl_ -#define _QmfObjectIdImpl_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include -#include - -namespace qmf { - - struct AgentAttachment { - uint64_t first; - - AgentAttachment() : first(0) {} - void setBanks(uint32_t broker, uint32_t bank); - uint64_t getFirst() const { return first; } - }; - - struct ObjectIdImpl { - ObjectId* envelope; - AgentAttachment* agent; - uint64_t first; - uint64_t second; - - ObjectIdImpl(ObjectId* e) : envelope(e), agent(0), first(0), second(0) {} - ObjectIdImpl(qpid::framing::Buffer& buffer); - ObjectIdImpl(AgentAttachment* agent, uint8_t flags, uint16_t seq, uint64_t object); - - void decode(qpid::framing::Buffer& buffer); - void encode(qpid::framing::Buffer& buffer) const; - void fromString(const std::string& repr); - std::string asString() const; - uint8_t getFlags() const { return (first & 0xF000000000000000LL) >> 60; } - uint16_t getSequence() const { return (first & 0x0FFF000000000000LL) >> 48; } - uint32_t getBrokerBank() const { return (first & 0x0000FFFFF0000000LL) >> 28; } - uint32_t getAgentBank() const { return first & 0x000000000FFFFFFFLL; } - uint64_t getObjectNum() const { return second; } - uint32_t getObjectNumHi() const { return (uint32_t) (second >> 32); } - uint32_t getObjectNumLo() const { return (uint32_t) (second & 0x00000000FFFFFFFFLL); } - bool isDurable() const { return getSequence() == 0; } - void setValue(uint64_t f, uint64_t s) { first = f; second = s; } - - bool operator==(const ObjectIdImpl& other) const; - bool operator<(const ObjectIdImpl& other) const; - bool operator>(const ObjectIdImpl& other) const; - }; -} - -#endif - diff --git a/qpid/cpp/src/qmf/ObjectImpl.cpp b/qpid/cpp/src/qmf/ObjectImpl.cpp deleted file mode 100644 index 1ea2d54527..0000000000 --- a/qpid/cpp/src/qmf/ObjectImpl.cpp +++ /dev/null @@ -1,208 +0,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. - */ - -#include "qmf/ObjectImpl.h" -#include "qmf/ValueImpl.h" -#include - -using namespace std; -using namespace qmf; -using namespace qpid::sys; -using qpid::framing::Buffer; - -ObjectImpl::ObjectImpl(Object* e, const SchemaObjectClass* type) : - envelope(e), objectClass(type), createTime(uint64_t(Duration(now()))), - destroyTime(0), lastUpdatedTime(createTime) -{ - int propCount = objectClass->getPropertyCount(); - int statCount = objectClass->getStatisticCount(); - int idx; - - for (idx = 0; idx < propCount; idx++) { - const SchemaProperty* prop = objectClass->getProperty(idx); - properties[prop->getName()] = ValuePtr(new Value(prop->getType())); - } - - for (idx = 0; idx < statCount; idx++) { - const SchemaStatistic* stat = objectClass->getStatistic(idx); - statistics[stat->getName()] = ValuePtr(new Value(stat->getType())); - } -} - -ObjectImpl::ObjectImpl(const SchemaObjectClass* type, Buffer& buffer, bool prop, bool stat, bool managed) : - envelope(new Object(this)), objectClass(type), createTime(0), destroyTime(0), lastUpdatedTime(0) -{ - int idx; - - if (managed) { - lastUpdatedTime = buffer.getLongLong(); - createTime = buffer.getLongLong(); - destroyTime = buffer.getLongLong(); - objectId.reset(new ObjectIdImpl(buffer)); - } - - if (prop) { - int propCount = objectClass->getPropertyCount(); - set excludes; - parsePresenceMasks(buffer, excludes); - for (idx = 0; idx < propCount; idx++) { - const SchemaProperty* prop = objectClass->getProperty(idx); - if (excludes.count(prop->getName()) != 0) { - properties[prop->getName()] = ValuePtr(new Value(prop->getType())); - } else { - ValueImpl* pval = new ValueImpl(prop->getType(), buffer); - properties[prop->getName()] = ValuePtr(pval->envelope); - } - } - } - - if (stat) { - int statCount = objectClass->getStatisticCount(); - for (idx = 0; idx < statCount; idx++) { - const SchemaStatistic* stat = objectClass->getStatistic(idx); - ValueImpl* sval = new ValueImpl(stat->getType(), buffer); - statistics[stat->getName()] = ValuePtr(sval->envelope); - } - } -} - -ObjectImpl::~ObjectImpl() -{ -} - -void ObjectImpl::destroy() -{ - destroyTime = uint64_t(Duration(now())); - // TODO - flag deletion -} - -Value* ObjectImpl::getValue(const string& key) const -{ - map::const_iterator iter; - - iter = properties.find(key); - if (iter != properties.end()) - return iter->second.get(); - - iter = statistics.find(key); - if (iter != statistics.end()) - return iter->second.get(); - - return 0; -} - -void ObjectImpl::parsePresenceMasks(Buffer& buffer, set& excludeList) -{ - int propCount = objectClass->getPropertyCount(); - excludeList.clear(); - uint8_t bit = 0; - uint8_t mask = 0; - - for (int idx = 0; idx < propCount; idx++) { - const SchemaProperty* prop = objectClass->getProperty(idx); - if (prop->isOptional()) { - if (bit == 0) { - mask = buffer.getOctet(); - bit = 1; - } - if ((mask & bit) == 0) - excludeList.insert(string(prop->getName())); - if (bit == 0x80) - bit = 0; - else - bit = bit << 1; - } - } -} - -void ObjectImpl::encodeSchemaKey(qpid::framing::Buffer& buffer) const -{ - buffer.putShortString(objectClass->getClassKey()->getPackageName()); - buffer.putShortString(objectClass->getClassKey()->getClassName()); - buffer.putBin128(const_cast(objectClass->getClassKey()->getHash())); -} - -void ObjectImpl::encodeManagedObjectData(qpid::framing::Buffer& buffer) const -{ - buffer.putLongLong(lastUpdatedTime); - buffer.putLongLong(createTime); - buffer.putLongLong(destroyTime); - objectId->encode(buffer); -} - -void ObjectImpl::encodeProperties(qpid::framing::Buffer& buffer) const -{ - int propCount = objectClass->getPropertyCount(); - uint8_t bit = 0; - uint8_t mask = 0; - ValuePtr value; - - for (int idx = 0; idx < propCount; idx++) { - const SchemaProperty* prop = objectClass->getProperty(idx); - if (prop->isOptional()) { - value = properties[prop->getName()]; - if (bit == 0) - bit = 1; - if (!value->isNull()) - mask |= bit; - if (bit == 0x80) { - buffer.putOctet(mask); - bit = 0; - mask = 0; - } else - bit = bit << 1; - } - } - if (bit != 0) { - buffer.putOctet(mask); - } - - for (int idx = 0; idx < propCount; idx++) { - const SchemaProperty* prop = objectClass->getProperty(idx); - value = properties[prop->getName()]; - if (!prop->isOptional() || !value->isNull()) { - value->impl->encode(buffer); - } - } -} - -void ObjectImpl::encodeStatistics(qpid::framing::Buffer& buffer) const -{ - int statCount = objectClass->getStatisticCount(); - for (int idx = 0; idx < statCount; idx++) { - const SchemaStatistic* stat = objectClass->getStatistic(idx); - ValuePtr value = statistics[stat->getName()]; - value->impl->encode(buffer); - } -} - -//================================================================== -// Wrappers -//================================================================== - -Object::Object(const SchemaObjectClass* type) : impl(new ObjectImpl(this, type)) {} -Object::Object(ObjectImpl* i) : impl(i) {} -Object::Object(const Object& from) : impl(new ObjectImpl(*(from.impl))) {} -Object::~Object() { delete impl; } -void Object::destroy() { impl->destroy(); } -const ObjectId* Object::getObjectId() const { return impl->getObjectId(); } -void Object::setObjectId(ObjectId* oid) { impl->setObjectId(oid); } -const SchemaObjectClass* Object::getClass() const { return impl->getClass(); } -Value* Object::getValue(char* key) const { return impl->getValue(key); } - diff --git a/qpid/cpp/src/qmf/ObjectImpl.h b/qpid/cpp/src/qmf/ObjectImpl.h deleted file mode 100644 index d69979e0da..0000000000 --- a/qpid/cpp/src/qmf/ObjectImpl.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef _QmfObjectImpl_ -#define _QmfObjectImpl_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace qmf { - - struct ObjectImpl { - typedef boost::shared_ptr Ptr; - typedef boost::shared_ptr ValuePtr; - Object* envelope; - const SchemaObjectClass* objectClass; - boost::shared_ptr objectId; - uint64_t createTime; - uint64_t destroyTime; - uint64_t lastUpdatedTime; - mutable std::map properties; - mutable std::map statistics; - - ObjectImpl(Object* e, const SchemaObjectClass* type); - ObjectImpl(const SchemaObjectClass* type, qpid::framing::Buffer& buffer, bool prop, bool stat, bool managed); - ~ObjectImpl(); - - void destroy(); - const ObjectId* getObjectId() const { return objectId.get() ? objectId->envelope : 0; } - void setObjectId(ObjectId* oid) { objectId.reset(oid->impl); } - const SchemaObjectClass* getClass() const { return objectClass; } - Value* getValue(const std::string& key) const; - - void parsePresenceMasks(qpid::framing::Buffer& buffer, std::set& excludeList); - void encodeSchemaKey(qpid::framing::Buffer& buffer) const; - void encodeManagedObjectData(qpid::framing::Buffer& buffer) const; - void encodeProperties(qpid::framing::Buffer& buffer) const; - void encodeStatistics(qpid::framing::Buffer& buffer) const; - }; -} - -#endif - diff --git a/qpid/cpp/src/qmf/Protocol.cpp b/qpid/cpp/src/qmf/Protocol.cpp deleted file mode 100644 index 0a3beeb276..0000000000 --- a/qpid/cpp/src/qmf/Protocol.cpp +++ /dev/null @@ -1,52 +0,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. - */ - -#include "qmf/Protocol.h" -#include "qpid/framing/Buffer.h" - -using namespace std; -using namespace qmf; -using namespace qpid::framing; - - -bool Protocol::checkHeader(Buffer& buf, uint8_t *opcode, uint32_t *seq) -{ - if (buf.available() < 8) - return false; - - uint8_t h1 = buf.getOctet(); - uint8_t h2 = buf.getOctet(); - uint8_t h3 = buf.getOctet(); - - *opcode = buf.getOctet(); - *seq = buf.getLong(); - - return h1 == 'A' && h2 == 'M' && h3 == '3'; -} - -void Protocol::encodeHeader(qpid::framing::Buffer& buf, uint8_t opcode, uint32_t seq) -{ - buf.putOctet('A'); - buf.putOctet('M'); - buf.putOctet('3'); - buf.putOctet(opcode); - buf.putLong (seq); -} - - diff --git a/qpid/cpp/src/qmf/Protocol.h b/qpid/cpp/src/qmf/Protocol.h deleted file mode 100644 index d5da08c1db..0000000000 --- a/qpid/cpp/src/qmf/Protocol.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef _QmfProtocol_ -#define _QmfProtocol_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include - -namespace qpid { - namespace framing { - class Buffer; - } -} - -namespace qmf { - - class Protocol { - public: - static bool checkHeader(qpid::framing::Buffer& buf, uint8_t *opcode, uint32_t *seq); - static void encodeHeader(qpid::framing::Buffer& buf, uint8_t opcode, uint32_t seq = 0); - - const static uint8_t OP_ATTACH_REQUEST = 'A'; - const static uint8_t OP_ATTACH_RESPONSE = 'a'; - - const static uint8_t OP_BROKER_REQUEST = 'B'; - const static uint8_t OP_BROKER_RESPONSE = 'b'; - - const static uint8_t OP_CONSOLE_ADDED_INDICATION = 'x'; - const static uint8_t OP_COMMAND_COMPLETE = 'z'; - const static uint8_t OP_HEARTBEAT_INDICATION = 'h'; - - const static uint8_t OP_PACKAGE_REQUEST = 'P'; - const static uint8_t OP_PACKAGE_INDICATION = 'p'; - const static uint8_t OP_CLASS_QUERY = 'Q'; - const static uint8_t OP_CLASS_INDICATION = 'q'; - const static uint8_t OP_SCHEMA_REQUEST = 'S'; - const static uint8_t OP_SCHEMA_RESPONSE = 's'; - - const static uint8_t OP_METHOD_REQUEST = 'M'; - const static uint8_t OP_METHOD_RESPONSE = 'm'; - const static uint8_t OP_GET_QUERY = 'G'; - const static uint8_t OP_OBJECT_INDICATION = 'g'; - const static uint8_t OP_PROPERTY_INDICATION = 'c'; - const static uint8_t OP_STATISTIC_INDICATION = 'i'; - const static uint8_t OP_EVENT_INDICATION = 'e'; - }; - -} - -#endif - diff --git a/qpid/cpp/src/qmf/Query.h b/qpid/cpp/src/qmf/Query.h deleted file mode 100644 index 875749862e..0000000000 --- a/qpid/cpp/src/qmf/Query.h +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef _QmfQuery_ -#define _QmfQuery_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include -#include - -namespace qmf { - - struct Object; - struct QueryElementImpl; - struct QueryImpl; - struct QueryExpressionImpl; - struct SchemaClassKey; - - enum ValueOper { - O_EQ = 1, - O_NE = 2, - O_LT = 3, - O_LE = 4, - O_GT = 5, - O_GE = 6, - O_RE_MATCH = 7, - O_RE_NOMATCH = 8 - }; - - struct QueryOperand { - virtual ~QueryOperand() {} - virtual bool evaluate(const Object* object) const = 0; - }; - - struct QueryElement : public QueryOperand { - QueryElement(const char* attrName, const Value* value, ValueOper oper); - QueryElement(QueryElementImpl* impl); - virtual ~QueryElement(); - bool evaluate(const Object* object) const; - - QueryElementImpl* impl; - }; - - enum ExprOper { - E_NOT = 1, - E_AND = 2, - E_OR = 3, - E_XOR = 4 - }; - - struct QueryExpression : public QueryOperand { - QueryExpression(ExprOper oper, const QueryOperand* operand1, const QueryOperand* operand2); - QueryExpression(QueryExpressionImpl* impl); - virtual ~QueryExpression(); - bool evaluate(const Object* object) const; - - QueryExpressionImpl* impl; - }; - - class Query { - public: - Query(const char* className, const char* packageName); - Query(const SchemaClassKey* key); - Query(const ObjectId* oid); - Query(QueryImpl* impl); - ~Query(); - - void setSelect(const QueryOperand* criterion); - void setLimit(uint32_t maxResults); - void setOrderBy(const char* attrName, bool decreasing); - - const char* getPackage() const; - const char* getClass() const; - const ObjectId* getObjectId() const; - - bool haveSelect() const; - bool haveLimit() const; - bool haveOrderBy() const; - const QueryOperand* getSelect() const; - uint32_t getLimit() const; - const char* getOrderBy() const; - bool getDecreasing() const; - - QueryImpl* impl; - }; -} - -#endif - diff --git a/qpid/cpp/src/qmf/QueryImpl.cpp b/qpid/cpp/src/qmf/QueryImpl.cpp deleted file mode 100644 index f75a9aa5d5..0000000000 --- a/qpid/cpp/src/qmf/QueryImpl.cpp +++ /dev/null @@ -1,94 +0,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. - */ - -#include "qmf/QueryImpl.h" -#include "qmf/ObjectIdImpl.h" -#include "qpid/framing/Buffer.h" -#include "qpid/framing/FieldTable.h" - -using namespace std; -using namespace qmf; -using namespace qpid::framing; - -bool QueryElementImpl::evaluate(const Object* /*object*/) const -{ - // TODO: Implement this - return false; -} - -bool QueryExpressionImpl::evaluate(const Object* /*object*/) const -{ - // TODO: Implement this - return false; -} - -QueryImpl::QueryImpl(Buffer& buffer) -{ - FieldTable ft; - ft.decode(buffer); - // TODO -} - -void QueryImpl::encode(Buffer& buffer) const -{ - FieldTable ft; - - if (oid.get() != 0) { - ft.setString("_objectid", oid->impl->asString()); - } else { - if (!packageName.empty()) - ft.setString("_package", packageName); - ft.setString("_class", className); - } - - ft.encode(buffer); -} - - -//================================================================== -// Wrappers -//================================================================== - -QueryElement::QueryElement(const char* attrName, const Value* value, ValueOper oper) : impl(new QueryElementImpl(attrName, value, oper)) {} -QueryElement::QueryElement(QueryElementImpl* i) : impl(i) {} -QueryElement::~QueryElement() { delete impl; } -bool QueryElement::evaluate(const Object* object) const { return impl->evaluate(object); } -QueryExpression::QueryExpression(ExprOper oper, const QueryOperand* operand1, const QueryOperand* operand2) : impl(new QueryExpressionImpl(oper, operand1, operand2)) {} -QueryExpression::QueryExpression(QueryExpressionImpl* i) : impl(i) {} -QueryExpression::~QueryExpression() { delete impl; } -bool QueryExpression::evaluate(const Object* object) const { return impl->evaluate(object); } -Query::Query(const char* className, const char* packageName) : impl(new QueryImpl(className, packageName)) {} -Query::Query(const SchemaClassKey* key) : impl(new QueryImpl(key)) {} -Query::Query(const ObjectId* oid) : impl(new QueryImpl(oid)) {} -Query::Query(QueryImpl* i) : impl(i) {} -Query::~Query() { delete impl; } -void Query::setSelect(const QueryOperand* criterion) { impl->setSelect(criterion); } -void Query::setLimit(uint32_t maxResults) { impl->setLimit(maxResults); } -void Query::setOrderBy(const char* attrName, bool decreasing) { impl->setOrderBy(attrName, decreasing); } -const char* Query::getPackage() const { return impl->getPackage().c_str(); } -const char* Query::getClass() const { return impl->getClass().c_str(); } -const ObjectId* Query::getObjectId() const { return impl->getObjectId(); } -bool Query::haveSelect() const { return impl->haveSelect(); } -bool Query::haveLimit() const { return impl->haveLimit(); } -bool Query::haveOrderBy() const { return impl->haveOrderBy(); } -const QueryOperand* Query::getSelect() const { return impl->getSelect(); } -uint32_t Query::getLimit() const { return impl->getLimit(); } -const char* Query::getOrderBy() const { return impl->getOrderBy().c_str(); } -bool Query::getDecreasing() const { return impl->getDecreasing(); } - diff --git a/qpid/cpp/src/qmf/QueryImpl.h b/qpid/cpp/src/qmf/QueryImpl.h deleted file mode 100644 index 4a56a457c0..0000000000 --- a/qpid/cpp/src/qmf/QueryImpl.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef _QmfQueryImpl_ -#define _QmfQueryImpl_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include "qmf/Query.h" -#include "qmf/Schema.h" -#include -#include - -namespace qpid { - namespace framing { - class Buffer; - } -} - -namespace qmf { - - struct QueryElementImpl { - QueryElementImpl(const std::string& a, const Value* v, ValueOper o) : - envelope(new QueryElement(this)), attrName(a), value(v), oper(o) {} - ~QueryElementImpl() {} - bool evaluate(const Object* object) const; - - QueryElement* envelope; - std::string attrName; - const Value* value; - ValueOper oper; - }; - - struct QueryExpressionImpl { - QueryExpressionImpl(ExprOper o, const QueryOperand* operand1, const QueryOperand* operand2) : - envelope(new QueryExpression(this)), oper(o), left(operand1), right(operand2) {} - ~QueryExpressionImpl() {} - bool evaluate(const Object* object) const; - - QueryExpression* envelope; - ExprOper oper; - const QueryOperand* left; - const QueryOperand* right; - }; - - struct QueryImpl { - QueryImpl(Query* e) : envelope(e), select(0) {} - QueryImpl(const std::string& c, const std::string& p) : - envelope(new Query(this)), packageName(p), className(c) {} - QueryImpl(const SchemaClassKey* key) : - envelope(new Query(this)), packageName(key->getPackageName()), className(key->getClassName()) {} - QueryImpl(const ObjectId* oid) : - envelope(new Query(this)), oid(new ObjectId(*oid)) {} - QueryImpl(qpid::framing::Buffer& buffer); - ~QueryImpl() {}; - - void setSelect(const QueryOperand* criterion) { select = criterion; } - void setLimit(uint32_t maxResults) { resultLimit = maxResults; } - void setOrderBy(const std::string& attrName, bool decreasing) { - orderBy = attrName; orderDecreasing = decreasing; - } - - const std::string& getPackage() const { return packageName; } - const std::string& getClass() const { return className; } - const ObjectId* getObjectId() const { return oid.get(); } - - bool haveSelect() const { return select != 0; } - bool haveLimit() const { return resultLimit > 0; } - bool haveOrderBy() const { return !orderBy.empty(); } - const QueryOperand* getSelect() const { return select; } - uint32_t getLimit() const { return resultLimit; } - const std::string& getOrderBy() const { return orderBy; } - bool getDecreasing() const { return orderDecreasing; } - - void encode(qpid::framing::Buffer& buffer) const; - - Query* envelope; - std::string packageName; - std::string className; - boost::shared_ptr oid; - const QueryOperand* select; - uint32_t resultLimit; - std::string orderBy; - bool orderDecreasing; - }; -} - -#endif diff --git a/qpid/cpp/src/qmf/ResilientConnection.cpp b/qpid/cpp/src/qmf/ResilientConnection.cpp deleted file mode 100644 index 7ec03cf4da..0000000000 --- a/qpid/cpp/src/qmf/ResilientConnection.cpp +++ /dev/null @@ -1,472 +0,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. - */ - -#include "qmf/ResilientConnection.h" -#include "qmf/MessageImpl.h" -#include "qmf/ConnectionSettingsImpl.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace qmf; -using namespace qpid; -using qpid::sys::Mutex; - -namespace qmf { - struct ResilientConnectionEventImpl { - ResilientConnectionEvent::EventKind kind; - void* sessionContext; - string errorText; - MessageImpl message; - - ResilientConnectionEventImpl(ResilientConnectionEvent::EventKind k, - const MessageImpl& m = MessageImpl()) : - kind(k), sessionContext(0), message(m) {} - ResilientConnectionEvent copy(); - }; - - struct RCSession : public client::MessageListener, public qpid::sys::Runnable, public qpid::RefCounted { - typedef boost::intrusive_ptr Ptr; - ResilientConnectionImpl& connImpl; - string name; - client::Connection& connection; - client::Session session; - client::SubscriptionManager* subscriptions; - void* userContext; - vector dests; - qpid::sys::Thread thread; - - RCSession(ResilientConnectionImpl& ci, const string& n, client::Connection& c, void* uc) : - connImpl(ci), name(n), connection(c), session(connection.newSession(name)), - subscriptions(new client::SubscriptionManager(session)), userContext(uc), thread(*this) {} - ~RCSession(); - void received(client::Message& msg); - void run(); - void stop(); - }; - - class ResilientConnectionImpl : public qpid::sys::Runnable { - public: - ResilientConnectionImpl(const ConnectionSettings& settings); - ~ResilientConnectionImpl(); - - bool isConnected() const; - bool getEvent(ResilientConnectionEvent& event); - void popEvent(); - bool createSession(const char* name, void* sessionContext, SessionHandle& handle); - void destroySession(SessionHandle handle); - void sendMessage(SessionHandle handle, qmf::Message& message); - void declareQueue(SessionHandle handle, char* queue); - void deleteQueue(SessionHandle handle, char* queue); - void bind(SessionHandle handle, char* exchange, char* queue, char* key); - void unbind(SessionHandle handle, char* exchange, char* queue, char* key); - void setNotifyFd(int fd); - - void run(); - void failure(); - void sessionClosed(RCSession* sess); - - void EnqueueEvent(ResilientConnectionEvent::EventKind kind, - void* sessionContext = 0, - const MessageImpl& message = MessageImpl(), - const string& errorText = ""); - - private: - int notifyFd; - bool connected; - bool shutdown; - string lastError; - const ConnectionSettings settings; - client::Connection connection; - mutable qpid::sys::Mutex lock; - int delayMin; - int delayMax; - int delayFactor; - qpid::sys::Condition cond; - qpid::sys::Thread connThread; - deque eventQueue; - set sessions; - }; -} - -ResilientConnectionEvent ResilientConnectionEventImpl::copy() -{ - ResilientConnectionEvent item; - - ::memset(&item, 0, sizeof(ResilientConnectionEvent)); - item.kind = kind; - item.sessionContext = sessionContext; - item.message = message.copy(); - item.errorText = const_cast(errorText.c_str()); - - return item; -} - -RCSession::~RCSession() -{ - subscriptions->stop(); - thread.join(); - session.close(); - delete subscriptions; -} - -void RCSession::run() -{ - try { - subscriptions->run(); - } catch (exception& /*e*/) { - connImpl.sessionClosed(this); - } -} - -void RCSession::stop() -{ - subscriptions->stop(); -} - -void RCSession::received(client::Message& msg) -{ - qmf::MessageImpl qmsg; - qmsg.body = msg.getData(); - - qpid::framing::MessageProperties p = msg.getMessageProperties(); - if (p.hasReplyTo()) { - const qpid::framing::ReplyTo& rt = p.getReplyTo(); - qmsg.replyExchange = rt.getExchange(); - qmsg.replyKey = rt.getRoutingKey(); - } - - if (p.hasUserId()) { - qmsg.userId = p.getUserId(); - } - - connImpl.EnqueueEvent(ResilientConnectionEvent::RECV, userContext, qmsg); -} - -ResilientConnectionImpl::ResilientConnectionImpl(const ConnectionSettings& _settings) : - notifyFd(-1), connected(false), shutdown(false), settings(_settings), delayMin(1), connThread(*this) -{ - connection.registerFailureCallback(boost::bind(&ResilientConnectionImpl::failure, this)); - settings.impl->getRetrySettings(&delayMin, &delayMax, &delayFactor); -} - -ResilientConnectionImpl::~ResilientConnectionImpl() -{ - shutdown = true; - connected = false; - cond.notify(); - connThread.join(); - connection.close(); -} - -bool ResilientConnectionImpl::isConnected() const -{ - Mutex::ScopedLock _lock(lock); - return connected; -} - -bool ResilientConnectionImpl::getEvent(ResilientConnectionEvent& event) -{ - Mutex::ScopedLock _lock(lock); - if (eventQueue.empty()) - return false; - event = eventQueue.front().copy(); - return true; -} - -void ResilientConnectionImpl::popEvent() -{ - Mutex::ScopedLock _lock(lock); - if (!eventQueue.empty()) - eventQueue.pop_front(); -} - -bool ResilientConnectionImpl::createSession(const char* name, void* sessionContext, - SessionHandle& handle) -{ - Mutex::ScopedLock _lock(lock); - if (!connected) - return false; - - RCSession::Ptr sess = RCSession::Ptr(new RCSession(*this, name, connection, sessionContext)); - - handle.impl = (void*) sess.get(); - sessions.insert(sess); - - return true; -} - -void ResilientConnectionImpl::destroySession(SessionHandle handle) -{ - Mutex::ScopedLock _lock(lock); - RCSession::Ptr sess = RCSession::Ptr((RCSession*) handle.impl); - set::iterator iter = sessions.find(sess); - if (iter != sessions.end()) { - for (vector::iterator dIter = sess->dests.begin(); dIter != sess->dests.end(); dIter++) - sess->subscriptions->cancel(dIter->c_str()); - sess->subscriptions->stop(); - sess->subscriptions->wait(); - - sessions.erase(iter); - return; - } -} - -void ResilientConnectionImpl::sendMessage(SessionHandle handle, qmf::Message& message) -{ - Mutex::ScopedLock _lock(lock); - RCSession::Ptr sess = RCSession::Ptr((RCSession*) handle.impl); - set::iterator iter = sessions.find(sess); - qpid::client::Message msg; - string data(message.body, message.length); - msg.getDeliveryProperties().setRoutingKey(message.routingKey); - msg.getMessageProperties().setReplyTo(qpid::framing::ReplyTo(message.replyExchange, message.replyKey)); - msg.setData(data); - - try { - sess->session.messageTransfer(client::arg::content=msg, client::arg::destination=message.destination); - } catch(exception& e) { - QPID_LOG(error, "Session Exception during message-transfer: " << e.what()); - sessions.erase(iter); - EnqueueEvent(ResilientConnectionEvent::SESSION_CLOSED, (*iter)->userContext); - } -} - -void ResilientConnectionImpl::declareQueue(SessionHandle handle, char* queue) -{ - Mutex::ScopedLock _lock(lock); - RCSession* sess = (RCSession*) handle.impl; - - sess->session.queueDeclare(client::arg::queue=queue, client::arg::autoDelete=true, client::arg::exclusive=true); - sess->subscriptions->setAcceptMode(client::ACCEPT_MODE_NONE); - sess->subscriptions->setAcquireMode(client::ACQUIRE_MODE_PRE_ACQUIRED); - sess->subscriptions->subscribe(*sess, queue, queue); - sess->subscriptions->setFlowControl(queue, client::FlowControl::unlimited()); - sess->dests.push_back(string(queue)); -} - -void ResilientConnectionImpl::deleteQueue(SessionHandle handle, char* queue) -{ - Mutex::ScopedLock _lock(lock); - RCSession* sess = (RCSession*) handle.impl; - - sess->session.queueDelete(client::arg::queue=queue); - for (vector::iterator iter = sess->dests.begin(); - iter != sess->dests.end(); iter++) - if (*iter == queue) { - sess->subscriptions->cancel(queue); - sess->dests.erase(iter); - break; - } -} - -void ResilientConnectionImpl::bind(SessionHandle handle, - char* exchange, char* queue, char* key) -{ - Mutex::ScopedLock _lock(lock); - RCSession* sess = (RCSession*) handle.impl; - - sess->session.exchangeBind(client::arg::exchange=exchange, client::arg::queue=queue, client::arg::bindingKey=key); -} - -void ResilientConnectionImpl::unbind(SessionHandle handle, - char* exchange, char* queue, char* key) -{ - Mutex::ScopedLock _lock(lock); - RCSession* sess = (RCSession*) handle.impl; - - sess->session.exchangeUnbind(client::arg::exchange=exchange, client::arg::queue=queue, client::arg::bindingKey=key); -} - -void ResilientConnectionImpl::setNotifyFd(int fd) -{ - notifyFd = fd; -} - -void ResilientConnectionImpl::run() -{ - int delay(delayMin); - - while (true) { - try { - QPID_LOG(trace, "Trying to open connection..."); - connection.open(settings.impl->getClientSettings()); - { - Mutex::ScopedLock _lock(lock); - connected = true; - EnqueueEvent(ResilientConnectionEvent::CONNECTED); - - while (connected) - cond.wait(lock); - delay = delayMin; - - while (!sessions.empty()) { - set::iterator iter = sessions.begin(); - RCSession::Ptr sess = *iter; - sessions.erase(iter); - EnqueueEvent(ResilientConnectionEvent::SESSION_CLOSED, sess->userContext); - Mutex::ScopedUnlock _u(lock); - sess->stop(); - - // Nullify the intrusive pointer within the scoped unlock, otherwise, - // the reference is held until overwritted above (under lock) which causes - // the session destructor to be called with the lock held. - sess = 0; - } - - EnqueueEvent(ResilientConnectionEvent::DISCONNECTED); - - if (shutdown) - return; - } - connection.close(); - } catch (exception &e) { - QPID_LOG(debug, "connection.open exception: " << e.what()); - Mutex::ScopedLock _lock(lock); - lastError = e.what(); - if (delay < delayMax) - delay *= delayFactor; - } - - ::qpid::sys::sleep(delay); - } -} - -void ResilientConnectionImpl::failure() -{ - Mutex::ScopedLock _lock(lock); - - connected = false; - lastError = "Closed by Peer"; - cond.notify(); -} - -void ResilientConnectionImpl::sessionClosed(RCSession*) -{ - Mutex::ScopedLock _lock(lock); - connected = false; - lastError = "Closed due to Session failure"; - cond.notify(); -} - -void ResilientConnectionImpl::EnqueueEvent(ResilientConnectionEvent::EventKind kind, - void* sessionContext, - const qmf::MessageImpl& message, - const string& errorText) -{ - Mutex::ScopedLock _lock(lock); - ResilientConnectionEventImpl event(kind, message); - - event.sessionContext = sessionContext; - event.errorText = errorText; - - eventQueue.push_back(event); - if (notifyFd != -1) - { - int unused_ret; //Suppress warnings about ignoring return value. - unused_ret = ::write(notifyFd, ".", 1); - } -} - - -//================================================================== -// Wrappers -//================================================================== - -ResilientConnection::ResilientConnection(const ConnectionSettings& settings) -{ - impl = new ResilientConnectionImpl(settings); -} - -ResilientConnection::~ResilientConnection() -{ - delete impl; -} - -bool ResilientConnection::isConnected() const -{ - return impl->isConnected(); -} - -bool ResilientConnection::getEvent(ResilientConnectionEvent& event) -{ - return impl->getEvent(event); -} - -void ResilientConnection::popEvent() -{ - impl->popEvent(); -} - -bool ResilientConnection::createSession(const char* name, void* sessionContext, SessionHandle& handle) -{ - return impl->createSession(name, sessionContext, handle); -} - -void ResilientConnection::destroySession(SessionHandle handle) -{ - impl->destroySession(handle); -} - -void ResilientConnection::sendMessage(SessionHandle handle, qmf::Message& message) -{ - impl->sendMessage(handle, message); -} - -void ResilientConnection::declareQueue(SessionHandle handle, char* queue) -{ - impl->declareQueue(handle, queue); -} - -void ResilientConnection::deleteQueue(SessionHandle handle, char* queue) -{ - impl->deleteQueue(handle, queue); -} - -void ResilientConnection::bind(SessionHandle handle, char* exchange, char* queue, char* key) -{ - impl->bind(handle, exchange, queue, key); -} - -void ResilientConnection::unbind(SessionHandle handle, char* exchange, char* queue, char* key) -{ - impl->unbind(handle, exchange, queue, key); -} - -void ResilientConnection::setNotifyFd(int fd) -{ - impl->setNotifyFd(fd); -} - diff --git a/qpid/cpp/src/qmf/ResilientConnection.h b/qpid/cpp/src/qmf/ResilientConnection.h deleted file mode 100644 index 03f1b9c0d5..0000000000 --- a/qpid/cpp/src/qmf/ResilientConnection.h +++ /dev/null @@ -1,163 +0,0 @@ -#ifndef _QmfResilientConnection_ -#define _QmfResilientConnection_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include -#include -#include - -namespace qmf { - - class ResilientConnectionImpl; - - /** - * Represents events that occur, unsolicited, from ResilientConnection. - */ - struct ResilientConnectionEvent { - enum EventKind { - CONNECTED = 1, - DISCONNECTED = 2, - SESSION_CLOSED = 3, - RECV = 4 - }; - - EventKind kind; - void* sessionContext; // SESSION_CLOSED, RECV - char* errorText; // DISCONNECTED, SESSION_CLOSED - Message message; // RECV - }; - - class SessionHandle { - friend class ResilientConnectionImpl; - void* impl; - }; - - /** - * ResilientConnection represents a Qpid connection that is resilient. - * - * Upon creation, ResilientConnection attempts to establish a connection to the - * messaging broker. If it fails, it will continue to retry at an interval that - * increases over time (to a maximum interval). If an extablished connection is - * dropped, a reconnect will be attempted. - */ - class ResilientConnection { - public: - - /** - * Create a new resilient connection. - *@param settings Settings that define how the connection is to be made. - *@param delayMin Minimum delay (in seconds) between retries. - *@param delayMax Maximum delay (in seconds) between retries. - *@param delayFactor Factor to multiply retry delay by after each failure. - */ - ResilientConnection(const ConnectionSettings& settings); - ~ResilientConnection(); - - /** - * Get the connected status of the resilient connection. - *@return true iff the connection is established. - */ - bool isConnected() const; - - /** - * Get the next event (if present) from the connection. - *@param event Returned event if one is available. - *@return true if event is valid, false if there are no more events to handle. - */ - bool getEvent(ResilientConnectionEvent& event); - - /** - * Discard the event on the front of the queue. This should be invoked after processing - * the event from getEvent. - */ - void popEvent(); - - /** - * Create a new AMQP session. - *@param name Unique name for the session. - *@param sessionContext Optional user-context value that will be provided in events - * pertaining to this session. - *@param handle Output handle to be stored and used in subsequent calls pertaining to - * this session. - *@return true iff the session was successfully created. - */ - bool createSession(const char* name, void* sessionContext, SessionHandle& handle); - - /** - * Destroy a created session. - *@param handle SessionHandle returned by createSession. - */ - void destroySession(SessionHandle handle); - - /** - * Send a message into the AMQP broker via a session. - *@param handle The session handle of the session to transmit through. - *@param message The QMF message to transmit. - */ - void sendMessage(SessionHandle handle, Message& message); - - /** - * Declare an exclusive, auto-delete queue for a session. - *@param handle The session handle for the owner of the queue. - *@param queue The name of the queue. - */ - void declareQueue(SessionHandle handle, char* queue); - - /** - * Delete a queue. - *@param handle The session handle for the owner of the queue. - *@param queue The name of the queue. - */ - void deleteQueue(SessionHandle handle, char* queue); - - /** - * Bind a queue to an exchange. - *@param handle The session handle of the session to use for binding. - *@param exchange The name of the exchange for binding. - *@param queue The name of the queue for binding. - *@param key The binding key. - */ - void bind(SessionHandle handle, char* exchange, char* queue, char* key); - - /** - * Remove a binding. - *@param handle The session handle of the session to use for un-binding. - *@param exchange The name of the exchange. - *@param queue The name of the queue. - *@param key The binding key. - */ - void unbind(SessionHandle handle, char* exchange, char* queue, char* key); - - /** - * Establish a file descriptor for event notification. - *@param fd A file descriptor into which the connection shall write a character each - * time an event is enqueued. This fd may be in a pair, the other fd of which - * is used in a select loop to control execution. - */ - void setNotifyFd(int fd); - - private: - ResilientConnectionImpl* impl; - }; -} - -#endif - diff --git a/qpid/cpp/src/qmf/Schema.h b/qpid/cpp/src/qmf/Schema.h deleted file mode 100644 index 1123acc3b8..0000000000 --- a/qpid/cpp/src/qmf/Schema.h +++ /dev/null @@ -1,171 +0,0 @@ -#ifndef _QmfSchema_ -#define _QmfSchema_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include -#include - -namespace qmf { - - enum Access { ACCESS_READ_CREATE = 1, ACCESS_READ_WRITE = 2, ACCESS_READ_ONLY = 3 }; - enum Direction { DIR_IN = 1, DIR_OUT = 2, DIR_IN_OUT = 3 }; - enum ClassKind { CLASS_OBJECT = 1, CLASS_EVENT = 2 }; - - struct SchemaArgumentImpl; - struct SchemaMethodImpl; - struct SchemaPropertyImpl; - struct SchemaStatisticImpl; - struct SchemaObjectClassImpl; - struct SchemaEventClassImpl; - struct SchemaClassKeyImpl; - - /** - */ - class SchemaArgument { - public: - SchemaArgument(const char* name, Typecode typecode); - SchemaArgument(SchemaArgumentImpl* impl); - ~SchemaArgument(); - void setDirection(Direction dir); - void setUnit(const char* val); - void setDesc(const char* desc); - const char* getName() const; - Typecode getType() const; - Direction getDirection() const; - const char* getUnit() const; - const char* getDesc() const; - - SchemaArgumentImpl* impl; - }; - - /** - */ - class SchemaMethod { - public: - SchemaMethod(const char* name); - SchemaMethod(SchemaMethodImpl* impl); - ~SchemaMethod(); - void addArgument(const SchemaArgument& argument); - void setDesc(const char* desc); - const char* getName() const; - const char* getDesc() const; - int getArgumentCount() const; - const SchemaArgument* getArgument(int idx) const; - - SchemaMethodImpl* impl; - }; - - /** - */ - class SchemaProperty { - public: - SchemaProperty(const char* name, Typecode typecode); - SchemaProperty(SchemaPropertyImpl* impl); - ~SchemaProperty(); - void setAccess(Access access); - void setIndex(bool val); - void setOptional(bool val); - void setUnit(const char* val); - void setDesc(const char* desc); - const char* getName() const; - Typecode getType() const; - Access getAccess() const; - bool isIndex() const; - bool isOptional() const; - const char* getUnit() const; - const char* getDesc() const; - - SchemaPropertyImpl* impl; - }; - - /** - */ - class SchemaStatistic { - public: - SchemaStatistic(const char* name, Typecode typecode); - SchemaStatistic(SchemaStatisticImpl* impl); - ~SchemaStatistic(); - void setUnit(const char* val); - void setDesc(const char* desc); - const char* getName() const; - Typecode getType() const; - const char* getUnit() const; - const char* getDesc() const; - - SchemaStatisticImpl* impl; - }; - - /** - */ - class SchemaClassKey { - public: - SchemaClassKey(SchemaClassKeyImpl* impl); - ~SchemaClassKey(); - - const char* getPackageName() const; - const char* getClassName() const; - const uint8_t* getHash() const; - - SchemaClassKeyImpl* impl; - }; - - /** - */ - class SchemaObjectClass { - public: - SchemaObjectClass(const char* package, const char* name); - SchemaObjectClass(SchemaObjectClassImpl* impl); - ~SchemaObjectClass(); - void addProperty(const SchemaProperty& property); - void addStatistic(const SchemaStatistic& statistic); - void addMethod(const SchemaMethod& method); - - const SchemaClassKey* getClassKey() const; - int getPropertyCount() const; - int getStatisticCount() const; - int getMethodCount() const; - const SchemaProperty* getProperty(int idx) const; - const SchemaStatistic* getStatistic(int idx) const; - const SchemaMethod* getMethod(int idx) const; - - SchemaObjectClassImpl* impl; - }; - - /** - */ - class SchemaEventClass { - public: - SchemaEventClass(const char* package, const char* name); - SchemaEventClass(SchemaEventClassImpl* impl); - ~SchemaEventClass(); - void addArgument(const SchemaArgument& argument); - void setDesc(const char* desc); - - const SchemaClassKey* getClassKey() const; - int getArgumentCount() const; - const SchemaArgument* getArgument(int idx) const; - - SchemaEventClassImpl* impl; - }; -} - -#endif - diff --git a/qpid/cpp/src/qmf/SchemaImpl.cpp b/qpid/cpp/src/qmf/SchemaImpl.cpp deleted file mode 100644 index 3eb14c3952..0000000000 --- a/qpid/cpp/src/qmf/SchemaImpl.cpp +++ /dev/null @@ -1,550 +0,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. - */ - -#include "qmf/SchemaImpl.h" -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace qmf; -using qpid::framing::Buffer; -using qpid::framing::FieldTable; -using qpid::framing::Uuid; - -SchemaHash::SchemaHash() -{ - for (int idx = 0; idx < 16; idx++) - hash[idx] = 0x5A; -} - -void SchemaHash::encode(Buffer& buffer) const -{ - buffer.putBin128(hash); -} - -void SchemaHash::decode(Buffer& buffer) -{ - buffer.getBin128(hash); -} - -void SchemaHash::update(uint8_t data) -{ - update((char*) &data, 1); -} - -void SchemaHash::update(const char* data, uint32_t len) -{ - uint64_t* first = (uint64_t*) hash; - uint64_t* second = (uint64_t*) hash + 1; - - for (uint32_t idx = 0; idx < len; idx++) { - *first = *first ^ (uint64_t) data[idx]; - *second = *second << 1; - *second |= ((*first & 0x8000000000000000LL) >> 63); - *first = *first << 1; - *first = *first ^ *second; - } -} - -bool SchemaHash::operator==(const SchemaHash& other) const -{ - return ::memcmp(&hash, &other.hash, 16) == 0; -} - -bool SchemaHash::operator<(const SchemaHash& other) const -{ - return ::memcmp(&hash, &other.hash, 16) < 0; -} - -bool SchemaHash::operator>(const SchemaHash& other) const -{ - return ::memcmp(&hash, &other.hash, 16) > 0; -} - -SchemaArgumentImpl::SchemaArgumentImpl(Buffer& buffer) : envelope(new SchemaArgument(this)) -{ - FieldTable map; - map.decode(buffer); - - name = map.getAsString("name"); - typecode = (Typecode) map.getAsInt("type"); - unit = map.getAsString("unit"); - description = map.getAsString("desc"); - - dir = DIR_IN; - string dstr(map.getAsString("dir")); - if (dstr == "O") - dir = DIR_OUT; - else if (dstr == "IO") - dir = DIR_IN_OUT; -} - -void SchemaArgumentImpl::encode(Buffer& buffer) const -{ - FieldTable map; - - map.setString("name", name); - map.setInt("type", (int) typecode); - if (dir == DIR_IN) - map.setString("dir", "I"); - else if (dir == DIR_OUT) - map.setString("dir", "O"); - else - map.setString("dir", "IO"); - if (!unit.empty()) - map.setString("unit", unit); - if (!description.empty()) - map.setString("desc", description); - - map.encode(buffer); -} - -void SchemaArgumentImpl::updateHash(SchemaHash& hash) const -{ - hash.update(name); - hash.update(typecode); - hash.update(dir); - hash.update(unit); - hash.update(description); -} - -SchemaMethodImpl::SchemaMethodImpl(Buffer& buffer) : envelope(new SchemaMethod(this)) -{ - FieldTable map; - int argCount; - - map.decode(buffer); - name = map.getAsString("name"); - argCount = map.getAsInt("argCount"); - description = map.getAsString("desc"); - - for (int idx = 0; idx < argCount; idx++) { - SchemaArgumentImpl* arg = new SchemaArgumentImpl(buffer); - addArgument(*arg->envelope); - } -} - -void SchemaMethodImpl::encode(Buffer& buffer) const -{ - FieldTable map; - - map.setString("name", name); - map.setInt("argCount", arguments.size()); - if (!description.empty()) - map.setString("desc", description); - map.encode(buffer); - - for (vector::const_iterator iter = arguments.begin(); - iter != arguments.end(); iter++) - (*iter)->encode(buffer); -} - -void SchemaMethodImpl::addArgument(const SchemaArgument& argument) -{ - arguments.push_back(argument.impl); -} - -const SchemaArgument* SchemaMethodImpl::getArgument(int idx) const -{ - int count = 0; - for (vector::const_iterator iter = arguments.begin(); - iter != arguments.end(); iter++, count++) - if (idx == count) - return (*iter)->envelope; - return 0; -} - -void SchemaMethodImpl::updateHash(SchemaHash& hash) const -{ - hash.update(name); - hash.update(description); - for (vector::const_iterator iter = arguments.begin(); - iter != arguments.end(); iter++) - (*iter)->updateHash(hash); -} - -SchemaPropertyImpl::SchemaPropertyImpl(Buffer& buffer) : envelope(new SchemaProperty(this)) -{ - FieldTable map; - map.decode(buffer); - - name = map.getAsString("name"); - typecode = (Typecode) map.getAsInt("type"); - access = (Access) map.getAsInt("access"); - index = map.getAsInt("index") != 0; - optional = map.getAsInt("optional") != 0; - unit = map.getAsString("unit"); - description = map.getAsString("desc"); -} - -void SchemaPropertyImpl::encode(Buffer& buffer) const -{ - FieldTable map; - - map.setString("name", name); - map.setInt("type", (int) typecode); - map.setInt("access", (int) access); - map.setInt("index", index ? 1 : 0); - map.setInt("optional", optional ? 1 : 0); - if (!unit.empty()) - map.setString("unit", unit); - if (!description.empty()) - map.setString("desc", description); - - map.encode(buffer); -} - -void SchemaPropertyImpl::updateHash(SchemaHash& hash) const -{ - hash.update(name); - hash.update(typecode); - hash.update(access); - hash.update(index); - hash.update(optional); - hash.update(unit); - hash.update(description); -} - -SchemaStatisticImpl::SchemaStatisticImpl(Buffer& buffer) : envelope(new SchemaStatistic(this)) -{ - FieldTable map; - map.decode(buffer); - - name = map.getAsString("name"); - typecode = (Typecode) map.getAsInt("type"); - unit = map.getAsString("unit"); - description = map.getAsString("desc"); -} - -void SchemaStatisticImpl::encode(Buffer& buffer) const -{ - FieldTable map; - - map.setString("name", name); - map.setInt("type", (int) typecode); - if (!unit.empty()) - map.setString("unit", unit); - if (!description.empty()) - map.setString("desc", description); - - map.encode(buffer); -} - -void SchemaStatisticImpl::updateHash(SchemaHash& hash) const -{ - hash.update(name); - hash.update(typecode); - hash.update(unit); - hash.update(description); -} - -SchemaClassKeyImpl::SchemaClassKeyImpl(const string& p, const string& n, const SchemaHash& h) : - envelope(new SchemaClassKey(this)), package(p), name(n), hash(h) {} - -SchemaClassKeyImpl::SchemaClassKeyImpl(Buffer& buffer) : - envelope(new SchemaClassKey(this)), package(packageContainer), name(nameContainer), hash(hashContainer) -{ - buffer.getShortString(packageContainer); - buffer.getShortString(nameContainer); - hashContainer.decode(buffer); -} - -void SchemaClassKeyImpl::encode(Buffer& buffer) const -{ - buffer.putShortString(package); - buffer.putShortString(name); - hash.encode(buffer); -} - -bool SchemaClassKeyImpl::operator==(const SchemaClassKeyImpl& other) const -{ - return package == other.package && - name == other.name && - hash == other.hash; -} - -bool SchemaClassKeyImpl::operator<(const SchemaClassKeyImpl& other) const -{ - if (package < other.package) return true; - if (package > other.package) return false; - if (name < other.name) return true; - if (name > other.name) return false; - return hash < other.hash; -} - -string SchemaClassKeyImpl::str() const -{ - Uuid printableHash(hash.get()); - stringstream str; - str << package << ":" << name << "(" << printableHash << ")"; - return str.str(); -} - -SchemaObjectClassImpl::SchemaObjectClassImpl(Buffer& buffer) : - envelope(new SchemaObjectClass(this)), hasHash(true), classKey(package, name, hash) -{ - buffer.getShortString(package); - buffer.getShortString(name); - hash.decode(buffer); - - /*uint8_t hasParentClass =*/ buffer.getOctet(); // TODO: Parse parent-class indicator - uint16_t propCount = buffer.getShort(); - uint16_t statCount = buffer.getShort(); - uint16_t methodCount = buffer.getShort(); - - for (uint16_t idx = 0; idx < propCount; idx++) { - SchemaPropertyImpl* property = new SchemaPropertyImpl(buffer); - addProperty(*property->envelope); - } - - for (uint16_t idx = 0; idx < statCount; idx++) { - SchemaStatisticImpl* statistic = new SchemaStatisticImpl(buffer); - addStatistic(*statistic->envelope); - } - - for (uint16_t idx = 0; idx < methodCount; idx++) { - SchemaMethodImpl* method = new SchemaMethodImpl(buffer); - addMethod(*method->envelope); - } -} - -void SchemaObjectClassImpl::encode(Buffer& buffer) const -{ - buffer.putOctet((uint8_t) CLASS_OBJECT); - buffer.putShortString(package); - buffer.putShortString(name); - hash.encode(buffer); - buffer.putOctet(0); // No parent class - buffer.putShort((uint16_t) properties.size()); - buffer.putShort((uint16_t) statistics.size()); - buffer.putShort((uint16_t) methods.size()); - - for (vector::const_iterator iter = properties.begin(); - iter != properties.end(); iter++) - (*iter)->encode(buffer); - for (vector::const_iterator iter = statistics.begin(); - iter != statistics.end(); iter++) - (*iter)->encode(buffer); - for (vector::const_iterator iter = methods.begin(); - iter != methods.end(); iter++) - (*iter)->encode(buffer); -} - -const SchemaClassKey* SchemaObjectClassImpl::getClassKey() const -{ - if (!hasHash) { - hasHash = true; - hash.update(package); - hash.update(name); - for (vector::const_iterator iter = properties.begin(); - iter != properties.end(); iter++) - (*iter)->updateHash(hash); - for (vector::const_iterator iter = statistics.begin(); - iter != statistics.end(); iter++) - (*iter)->updateHash(hash); - for (vector::const_iterator iter = methods.begin(); - iter != methods.end(); iter++) - (*iter)->updateHash(hash); - } - - return classKey.envelope; -} - -void SchemaObjectClassImpl::addProperty(const SchemaProperty& property) -{ - properties.push_back(property.impl); -} - -void SchemaObjectClassImpl::addStatistic(const SchemaStatistic& statistic) -{ - statistics.push_back(statistic.impl); -} - -void SchemaObjectClassImpl::addMethod(const SchemaMethod& method) -{ - methods.push_back(method.impl); -} - -const SchemaProperty* SchemaObjectClassImpl::getProperty(int idx) const -{ - int count = 0; - for (vector::const_iterator iter = properties.begin(); - iter != properties.end(); iter++, count++) - if (idx == count) - return (*iter)->envelope; - return 0; -} - -const SchemaStatistic* SchemaObjectClassImpl::getStatistic(int idx) const -{ - int count = 0; - for (vector::const_iterator iter = statistics.begin(); - iter != statistics.end(); iter++, count++) - if (idx == count) - return (*iter)->envelope; - return 0; -} - -const SchemaMethod* SchemaObjectClassImpl::getMethod(int idx) const -{ - int count = 0; - for (vector::const_iterator iter = methods.begin(); - iter != methods.end(); iter++, count++) - if (idx == count) - return (*iter)->envelope; - return 0; -} - -SchemaEventClassImpl::SchemaEventClassImpl(Buffer& buffer) : - envelope(new SchemaEventClass(this)), hasHash(true), classKey(package, name, hash) -{ - buffer.getShortString(package); - buffer.getShortString(name); - hash.decode(buffer); - buffer.putOctet(0); // No parent class - - uint16_t argCount = buffer.getShort(); - - for (uint16_t idx = 0; idx < argCount; idx++) { - SchemaArgumentImpl* argument = new SchemaArgumentImpl(buffer); - addArgument(*argument->envelope); - } -} - -void SchemaEventClassImpl::encode(Buffer& buffer) const -{ - buffer.putOctet((uint8_t) CLASS_EVENT); - buffer.putShortString(package); - buffer.putShortString(name); - hash.encode(buffer); - buffer.putShort((uint16_t) arguments.size()); - - for (vector::const_iterator iter = arguments.begin(); - iter != arguments.end(); iter++) - (*iter)->encode(buffer); -} - -const SchemaClassKey* SchemaEventClassImpl::getClassKey() const -{ - if (!hasHash) { - hasHash = true; - hash.update(package); - hash.update(name); - for (vector::const_iterator iter = arguments.begin(); - iter != arguments.end(); iter++) - (*iter)->updateHash(hash); - } - return classKey.envelope; -} - -void SchemaEventClassImpl::addArgument(const SchemaArgument& argument) -{ - arguments.push_back(argument.impl); -} - -const SchemaArgument* SchemaEventClassImpl::getArgument(int idx) const -{ - int count = 0; - for (vector::const_iterator iter = arguments.begin(); - iter != arguments.end(); iter++, count++) - if (idx == count) - return (*iter)->envelope; - return 0; -} - - -//================================================================== -// Wrappers -//================================================================== - -SchemaArgument::SchemaArgument(const char* name, Typecode typecode) { impl = new SchemaArgumentImpl(this, name, typecode); } -SchemaArgument::SchemaArgument(SchemaArgumentImpl* i) : impl(i) {} -SchemaArgument::~SchemaArgument() { delete impl; } -void SchemaArgument::setDirection(Direction dir) { impl->setDirection(dir); } -void SchemaArgument::setUnit(const char* val) { impl->setUnit(val); } -void SchemaArgument::setDesc(const char* desc) { impl->setDesc(desc); } -const char* SchemaArgument::getName() const { return impl->getName().c_str(); } -Typecode SchemaArgument::getType() const { return impl->getType(); } -Direction SchemaArgument::getDirection() const { return impl->getDirection(); } -const char* SchemaArgument::getUnit() const { return impl->getUnit().c_str(); } -const char* SchemaArgument::getDesc() const { return impl->getDesc().c_str(); } -SchemaMethod::SchemaMethod(const char* name) { impl = new SchemaMethodImpl(this, name); } -SchemaMethod::SchemaMethod(SchemaMethodImpl* i) : impl(i) {} -SchemaMethod::~SchemaMethod() { delete impl; } -void SchemaMethod::addArgument(const SchemaArgument& argument) { impl->addArgument(argument); } -void SchemaMethod::setDesc(const char* desc) { impl->setDesc(desc); } -const char* SchemaMethod::getName() const { return impl->getName().c_str(); } -const char* SchemaMethod::getDesc() const { return impl->getDesc().c_str(); } -int SchemaMethod::getArgumentCount() const { return impl->getArgumentCount(); } -const SchemaArgument* SchemaMethod::getArgument(int idx) const { return impl->getArgument(idx); } -SchemaProperty::SchemaProperty(const char* name, Typecode typecode) { impl = new SchemaPropertyImpl(this, name, typecode); } -SchemaProperty::SchemaProperty(SchemaPropertyImpl* i) : impl(i) {} -SchemaProperty::~SchemaProperty() { delete impl; } -void SchemaProperty::setAccess(Access access) { impl->setAccess(access); } -void SchemaProperty::setIndex(bool val) { impl->setIndex(val); } -void SchemaProperty::setOptional(bool val) { impl->setOptional(val); } -void SchemaProperty::setUnit(const char* val) { impl->setUnit(val); } -void SchemaProperty::setDesc(const char* desc) { impl->setDesc(desc); } -const char* SchemaProperty::getName() const { return impl->getName().c_str(); } -Typecode SchemaProperty::getType() const { return impl->getType(); } -Access SchemaProperty::getAccess() const { return impl->getAccess(); } -bool SchemaProperty::isIndex() const { return impl->isIndex(); } -bool SchemaProperty::isOptional() const { return impl->isOptional(); } -const char* SchemaProperty::getUnit() const { return impl->getUnit().c_str(); } -const char* SchemaProperty::getDesc() const { return impl->getDesc().c_str(); } -SchemaStatistic::SchemaStatistic(const char* name, Typecode typecode) { impl = new SchemaStatisticImpl(this, name, typecode); } -SchemaStatistic::SchemaStatistic(SchemaStatisticImpl* i) : impl(i) {} -SchemaStatistic::~SchemaStatistic() { delete impl; } -void SchemaStatistic::setUnit(const char* val) { impl->setUnit(val); } -void SchemaStatistic::setDesc(const char* desc) { impl->setDesc(desc); } -const char* SchemaStatistic::getName() const { return impl->getName().c_str(); } -Typecode SchemaStatistic::getType() const { return impl->getType(); } -const char* SchemaStatistic::getUnit() const { return impl->getUnit().c_str(); } -const char* SchemaStatistic::getDesc() const { return impl->getDesc().c_str(); } -SchemaClassKey::SchemaClassKey(SchemaClassKeyImpl* i) : impl(i) {} -SchemaClassKey::~SchemaClassKey() { delete impl; } -const char* SchemaClassKey::getPackageName() const { return impl->getPackageName().c_str(); } -const char* SchemaClassKey::getClassName() const { return impl->getClassName().c_str(); } -const uint8_t* SchemaClassKey::getHash() const { return impl->getHash(); } -SchemaObjectClass::SchemaObjectClass(const char* package, const char* name) { impl = new SchemaObjectClassImpl(this, package, name); } -SchemaObjectClass::SchemaObjectClass(SchemaObjectClassImpl* i) : impl(i) {} -SchemaObjectClass::~SchemaObjectClass() { delete impl; } -void SchemaObjectClass::addProperty(const SchemaProperty& property) { impl->addProperty(property); } -void SchemaObjectClass::addStatistic(const SchemaStatistic& statistic) { impl->addStatistic(statistic); } -void SchemaObjectClass::addMethod(const SchemaMethod& method) { impl->addMethod(method); } -const SchemaClassKey* SchemaObjectClass::getClassKey() const { return impl->getClassKey(); } -int SchemaObjectClass::getPropertyCount() const { return impl->getPropertyCount(); } -int SchemaObjectClass::getStatisticCount() const { return impl->getStatisticCount(); } -int SchemaObjectClass::getMethodCount() const { return impl->getMethodCount(); } -const SchemaProperty* SchemaObjectClass::getProperty(int idx) const { return impl->getProperty(idx); } -const SchemaStatistic* SchemaObjectClass::getStatistic(int idx) const { return impl->getStatistic(idx); } -const SchemaMethod* SchemaObjectClass::getMethod(int idx) const { return impl->getMethod(idx); } -SchemaEventClass::SchemaEventClass(const char* package, const char* name) { impl = new SchemaEventClassImpl(this, package, name); } -SchemaEventClass::SchemaEventClass(SchemaEventClassImpl* i) : impl(i) {} -SchemaEventClass::~SchemaEventClass() { delete impl; } -void SchemaEventClass::addArgument(const SchemaArgument& argument) { impl->addArgument(argument); } -void SchemaEventClass::setDesc(const char* desc) { impl->setDesc(desc); } -const SchemaClassKey* SchemaEventClass::getClassKey() const { return impl->getClassKey(); } -int SchemaEventClass::getArgumentCount() const { return impl->getArgumentCount(); } -const SchemaArgument* SchemaEventClass::getArgument(int idx) const { return impl->getArgument(idx); } - diff --git a/qpid/cpp/src/qmf/SchemaImpl.h b/qpid/cpp/src/qmf/SchemaImpl.h deleted file mode 100644 index 035d99aecd..0000000000 --- a/qpid/cpp/src/qmf/SchemaImpl.h +++ /dev/null @@ -1,224 +0,0 @@ -#ifndef _QmfSchemaImpl_ -#define _QmfSchemaImpl_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include "qmf/Schema.h" -#include -#include -#include -#include - -namespace qmf { - - // TODO: Destructors for schema classes - // TODO: Add "frozen" attribute for schema classes so they can't be modified after - // they've been registered. - - class SchemaHash { - uint8_t hash[16]; - public: - SchemaHash(); - void encode(qpid::framing::Buffer& buffer) const; - void decode(qpid::framing::Buffer& buffer); - void update(const char* data, uint32_t len); - void update(uint8_t data); - void update(const std::string& data) { update(data.c_str(), data.size()); } - void update(Typecode t) { update((uint8_t) t); } - void update(Direction d) { update((uint8_t) d); } - void update(Access a) { update((uint8_t) a); } - void update(bool b) { update((uint8_t) (b ? 1 : 0)); } - const uint8_t* get() const { return hash; } - bool operator==(const SchemaHash& other) const; - bool operator<(const SchemaHash& other) const; - bool operator>(const SchemaHash& other) const; - }; - - struct SchemaArgumentImpl { - SchemaArgument* envelope; - std::string name; - Typecode typecode; - Direction dir; - std::string unit; - std::string description; - - SchemaArgumentImpl(SchemaArgument* e, const char* n, Typecode t) : - envelope(e), name(n), typecode(t), dir(DIR_IN) {} - SchemaArgumentImpl(qpid::framing::Buffer& buffer); - void encode(qpid::framing::Buffer& buffer) const; - void setDirection(Direction d) { dir = d; } - void setUnit(const char* val) { unit = val; } - void setDesc(const char* desc) { description = desc; } - const std::string& getName() const { return name; } - Typecode getType() const { return typecode; } - Direction getDirection() const { return dir; } - const std::string& getUnit() const { return unit; } - const std::string& getDesc() const { return description; } - void updateHash(SchemaHash& hash) const; - }; - - struct SchemaMethodImpl { - SchemaMethod* envelope; - std::string name; - std::string description; - std::vector arguments; - - SchemaMethodImpl(SchemaMethod* e, const char* n) : envelope(e), name(n) {} - SchemaMethodImpl(qpid::framing::Buffer& buffer); - void encode(qpid::framing::Buffer& buffer) const; - void addArgument(const SchemaArgument& argument); - void setDesc(const char* desc) { description = desc; } - const std::string& getName() const { return name; } - const std::string& getDesc() const { return description; } - int getArgumentCount() const { return arguments.size(); } - const SchemaArgument* getArgument(int idx) const; - void updateHash(SchemaHash& hash) const; - }; - - struct SchemaPropertyImpl { - SchemaProperty* envelope; - std::string name; - Typecode typecode; - Access access; - bool index; - bool optional; - std::string unit; - std::string description; - - SchemaPropertyImpl(SchemaProperty* e, const char* n, Typecode t) : - envelope(e), name(n), typecode(t), access(ACCESS_READ_ONLY), - index(false), optional(false) {} - SchemaPropertyImpl(qpid::framing::Buffer& buffer); - void encode(qpid::framing::Buffer& buffer) const; - void setAccess(Access a) { access = a; } - void setIndex(bool val) { index = val; } - void setOptional(bool val) { optional = val; } - void setUnit(const char* val) { unit = val; } - void setDesc(const char* desc) { description = desc; } - const std::string& getName() const { return name; } - Typecode getType() const { return typecode; } - Access getAccess() const { return access; } - bool isIndex() const { return index; } - bool isOptional() const { return optional; } - const std::string& getUnit() const { return unit; } - const std::string& getDesc() const { return description; } - void updateHash(SchemaHash& hash) const; - }; - - struct SchemaStatisticImpl { - SchemaStatistic* envelope; - std::string name; - Typecode typecode; - std::string unit; - std::string description; - - SchemaStatisticImpl(SchemaStatistic* e, const char* n, Typecode t) : - envelope(e), name(n), typecode(t) {} - SchemaStatisticImpl(qpid::framing::Buffer& buffer); - void encode(qpid::framing::Buffer& buffer) const; - void setUnit(const char* val) { unit = val; } - void setDesc(const char* desc) { description = desc; } - const std::string& getName() const { return name; } - Typecode getType() const { return typecode; } - const std::string& getUnit() const { return unit; } - const std::string& getDesc() const { return description; } - void updateHash(SchemaHash& hash) const; - }; - - struct SchemaClassKeyImpl { - const SchemaClassKey* envelope; - const std::string& package; - const std::string& name; - const SchemaHash& hash; - - // The *Container elements are only used if there isn't an external place to - // store these values. - std::string packageContainer; - std::string nameContainer; - SchemaHash hashContainer; - - SchemaClassKeyImpl(const std::string& package, const std::string& name, const SchemaHash& hash); - SchemaClassKeyImpl(qpid::framing::Buffer& buffer); - - const std::string& getPackageName() const { return package; } - const std::string& getClassName() const { return name; } - const uint8_t* getHash() const { return hash.get(); } - - void encode(qpid::framing::Buffer& buffer) const; - bool operator==(const SchemaClassKeyImpl& other) const; - bool operator<(const SchemaClassKeyImpl& other) const; - std::string str() const; - }; - - struct SchemaObjectClassImpl { - typedef boost::shared_ptr Ptr; - SchemaObjectClass* envelope; - std::string package; - std::string name; - mutable SchemaHash hash; - mutable bool hasHash; - SchemaClassKeyImpl classKey; - std::vector properties; - std::vector statistics; - std::vector methods; - - SchemaObjectClassImpl(SchemaObjectClass* e, const char* p, const char* n) : - envelope(e), package(p), name(n), hasHash(false), classKey(package, name, hash) {} - SchemaObjectClassImpl(qpid::framing::Buffer& buffer); - void encode(qpid::framing::Buffer& buffer) const; - void addProperty(const SchemaProperty& property); - void addStatistic(const SchemaStatistic& statistic); - void addMethod(const SchemaMethod& method); - - const SchemaClassKey* getClassKey() const; - int getPropertyCount() const { return properties.size(); } - int getStatisticCount() const { return statistics.size(); } - int getMethodCount() const { return methods.size(); } - const SchemaProperty* getProperty(int idx) const; - const SchemaStatistic* getStatistic(int idx) const; - const SchemaMethod* getMethod(int idx) const; - }; - - struct SchemaEventClassImpl { - typedef boost::shared_ptr Ptr; - SchemaEventClass* envelope; - std::string package; - std::string name; - mutable SchemaHash hash; - mutable bool hasHash; - SchemaClassKeyImpl classKey; - std::string description; - std::vector arguments; - - SchemaEventClassImpl(SchemaEventClass* e, const char* p, const char* n) : - envelope(e), package(p), name(n), hasHash(false), classKey(package, name, hash) {} - SchemaEventClassImpl(qpid::framing::Buffer& buffer); - void encode(qpid::framing::Buffer& buffer) const; - void addArgument(const SchemaArgument& argument); - void setDesc(const char* desc) { description = desc; } - - const SchemaClassKey* getClassKey() const; - int getArgumentCount() const { return arguments.size(); } - const SchemaArgument* getArgument(int idx) const; - }; -} - -#endif - diff --git a/qpid/cpp/src/qmf/SequenceManager.cpp b/qpid/cpp/src/qmf/SequenceManager.cpp deleted file mode 100644 index 3171e66fac..0000000000 --- a/qpid/cpp/src/qmf/SequenceManager.cpp +++ /dev/null @@ -1,96 +0,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. - */ - -#include "qmf/SequenceManager.h" - -using namespace std; -using namespace qmf; -using namespace qpid::sys; - -SequenceManager::SequenceManager() : nextSequence(1) {} - -void SequenceManager::setUnsolicitedContext(SequenceContext::Ptr ctx) -{ - unsolicitedContext = ctx; -} - -uint32_t SequenceManager::reserve(SequenceContext::Ptr ctx) -{ - Mutex::ScopedLock _lock(lock); - if (ctx.get() == 0) - ctx = unsolicitedContext; - uint32_t seq = nextSequence; - while (contextMap.find(seq) != contextMap.end()) - seq = seq < 0xFFFFFFFF ? seq + 1 : 1; - nextSequence = seq < 0xFFFFFFFF ? seq + 1 : 1; - contextMap[seq] = ctx; - ctx->reserve(); - return seq; -} - -void SequenceManager::release(uint32_t sequence) -{ - Mutex::ScopedLock _lock(lock); - - if (sequence == 0) { - if (unsolicitedContext.get() != 0) - unsolicitedContext->release(); - return; - } - - map::iterator iter = contextMap.find(sequence); - if (iter != contextMap.end()) { - if (iter->second != 0) - iter->second->release(); - contextMap.erase(iter); - } -} - -void SequenceManager::releaseAll() -{ - Mutex::ScopedLock _lock(lock); - contextMap.clear(); -} - -void SequenceManager::dispatch(uint8_t opcode, uint32_t sequence, qpid::framing::Buffer& buffer) -{ - Mutex::ScopedLock _lock(lock); - bool done; - - if (sequence == 0) { - if (unsolicitedContext.get() != 0) { - done = unsolicitedContext->handleMessage(opcode, sequence, buffer); - if (done) - unsolicitedContext->release(); - } - return; - } - - map::iterator iter = contextMap.find(sequence); - if (iter != contextMap.end()) { - if (iter->second != 0) { - done = iter->second->handleMessage(opcode, sequence, buffer); - if (done) { - iter->second->release(); - contextMap.erase(iter); - } - } - } -} - diff --git a/qpid/cpp/src/qmf/SequenceManager.h b/qpid/cpp/src/qmf/SequenceManager.h deleted file mode 100644 index bbfd0728a7..0000000000 --- a/qpid/cpp/src/qmf/SequenceManager.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef _QmfSequenceManager_ -#define _QmfSequenceManager_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include "qpid/sys/Mutex.h" -#include -#include - -namespace qpid { - namespace framing { - class Buffer; - } -} - -namespace qmf { - - class SequenceContext { - public: - typedef boost::shared_ptr Ptr; - SequenceContext() {} - virtual ~SequenceContext() {} - - virtual void reserve() = 0; - virtual bool handleMessage(uint8_t opcode, uint32_t sequence, qpid::framing::Buffer& buffer) = 0; - virtual void release() = 0; - }; - - class SequenceManager { - public: - SequenceManager(); - - void setUnsolicitedContext(SequenceContext::Ptr ctx); - uint32_t reserve(SequenceContext::Ptr ctx = SequenceContext::Ptr()); - void release(uint32_t sequence); - void releaseAll(); - void dispatch(uint8_t opcode, uint32_t sequence, qpid::framing::Buffer& buffer); - - private: - mutable qpid::sys::Mutex lock; - uint32_t nextSequence; - SequenceContext::Ptr unsolicitedContext; - std::map contextMap; - }; - -} - -#endif - diff --git a/qpid/cpp/src/qmf/Typecode.h b/qpid/cpp/src/qmf/Typecode.h deleted file mode 100644 index 94614d2977..0000000000 --- a/qpid/cpp/src/qmf/Typecode.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef _QmfTypecode_ -#define _QmfTypecode_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -namespace qmf { - - enum Typecode { - TYPE_UINT8 = 1, - TYPE_UINT16 = 2, - TYPE_UINT32 = 3, - TYPE_UINT64 = 4, - TYPE_SSTR = 6, - TYPE_LSTR = 7, - TYPE_ABSTIME = 8, - TYPE_DELTATIME = 9, - TYPE_REF = 10, - TYPE_BOOL = 11, - TYPE_FLOAT = 12, - TYPE_DOUBLE = 13, - TYPE_UUID = 14, - TYPE_MAP = 15, - TYPE_INT8 = 16, - TYPE_INT16 = 17, - TYPE_INT32 = 18, - TYPE_INT64 = 19, - TYPE_OBJECT = 20, - TYPE_LIST = 21, - TYPE_ARRAY = 22 - }; -} - -#endif - diff --git a/qpid/cpp/src/qmf/Value.h b/qpid/cpp/src/qmf/Value.h deleted file mode 100644 index bb946d31d3..0000000000 --- a/qpid/cpp/src/qmf/Value.h +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef _QmfValue_ -#define _QmfValue_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include -#include - -namespace qmf { - - class Object; - struct ValueImpl; - - class Value { - public: - // Value(); - Value(Typecode t, Typecode arrayType = TYPE_UINT8); - Value(ValueImpl* impl); - ~Value(); - - Typecode getType() const; - bool isNull() const; - void setNull(); - - bool isObjectId() const; - const ObjectId& asObjectId() const; - void setObjectId(const ObjectId& oid); - - bool isUint() const; - uint32_t asUint() const; - void setUint(uint32_t val); - - bool isInt() const; - int32_t asInt() const; - void setInt(int32_t val); - - bool isUint64() const; - uint64_t asUint64() const; - void setUint64(uint64_t val); - - bool isInt64() const; - int64_t asInt64() const; - void setInt64(int64_t val); - - bool isString() const; - const char* asString() const; - void setString(const char* val); - - bool isBool() const; - bool asBool() const; - void setBool(bool val); - - bool isFloat() const; - float asFloat() const; - void setFloat(float val); - - bool isDouble() const; - double asDouble() const; - void setDouble(double val); - - bool isUuid() const; - const uint8_t* asUuid() const; - void setUuid(const uint8_t* val); - - bool isObject() const; - Object* asObject() const; - void setObject(Object* val); - - bool isMap() const; - bool keyInMap(const char* key) const; - Value* byKey(const char* key); - const Value* byKey(const char* key) const; - void deleteKey(const char* key); - void insert(const char* key, Value* val); - uint32_t keyCount() const; - const char* key(uint32_t idx) const; - - bool isList() const; - uint32_t listItemCount() const; - Value* listItem(uint32_t idx); - void appendToList(Value* val); - void deleteListItem(uint32_t idx); - - bool isArray() const; - Typecode arrayType() const; - uint32_t arrayItemCount() const; - Value* arrayItem(uint32_t idx); - void appendToArray(Value* val); - void deleteArrayItem(uint32_t idx); - - ValueImpl* impl; - }; -} - -#endif - diff --git a/qpid/cpp/src/qmf/ValueImpl.cpp b/qpid/cpp/src/qmf/ValueImpl.cpp deleted file mode 100644 index f42c85eb33..0000000000 --- a/qpid/cpp/src/qmf/ValueImpl.cpp +++ /dev/null @@ -1,478 +0,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. - */ - -#include "qmf/ValueImpl.h" -#include - -using namespace std; -using namespace qmf; -using qpid::framing::Buffer; - -ValueImpl::ValueImpl(Typecode t, Buffer& buf) : envelope(new Value(this)), typecode(t) -{ - uint64_t first; - uint64_t second; - qpid::framing::FieldTable ft; - - switch (typecode) { - case TYPE_UINT8 : value.u32 = (uint32_t) buf.getOctet(); break; - case TYPE_UINT16 : value.u32 = (uint32_t) buf.getShort(); break; - case TYPE_UINT32 : value.u32 = (uint32_t) buf.getLong(); break; - case TYPE_UINT64 : value.u64 = buf.getLongLong(); break; - case TYPE_SSTR : buf.getShortString(stringVal); break; - case TYPE_LSTR : buf.getMediumString(stringVal); break; - case TYPE_ABSTIME : value.s64 = buf.getLongLong(); break; - case TYPE_DELTATIME : value.u64 = buf.getLongLong(); break; - case TYPE_BOOL : value.boolVal = (buf.getOctet() != 0); break; - case TYPE_FLOAT : value.floatVal = buf.getFloat(); break; - case TYPE_DOUBLE : value.doubleVal = buf.getDouble(); break; - case TYPE_INT8 : value.s32 = (int32_t) buf.getOctet(); break; - case TYPE_INT16 : value.s32 = (int32_t) buf.getShort(); break; - case TYPE_INT32 : value.s32 = (int32_t) buf.getLong(); break; - case TYPE_INT64 : value.s64 = buf.getLongLong(); break; - case TYPE_UUID : buf.getBin128(value.uuidVal); break; - case TYPE_REF: - first = buf.getLongLong(); - second = buf.getLongLong(); - refVal.impl->setValue(first, second); - break; - - case TYPE_MAP: - ft.decode(buf); - // TODO: either update to recursively use QMF types or reduce to int/string/... - // (maybe use another ctor with a FieldValue argument) - break; - - case TYPE_LIST: - case TYPE_ARRAY: - case TYPE_OBJECT: - default: - break; - } -} - -ValueImpl::ValueImpl(Typecode t) : envelope(new Value(this)), typecode(t) -{ - ::memset(&value, 0, sizeof(value)); -} - -ValueImpl::~ValueImpl() -{ -} - -void ValueImpl::encode(Buffer& buf) const -{ - switch (typecode) { - case TYPE_UINT8 : buf.putOctet((uint8_t) value.u32); break; - case TYPE_UINT16 : buf.putShort((uint16_t) value.u32); break; - case TYPE_UINT32 : buf.putLong(value.u32); break; - case TYPE_UINT64 : buf.putLongLong(value.u64); break; - case TYPE_SSTR : buf.putShortString(stringVal); break; - case TYPE_LSTR : buf.putMediumString(stringVal); break; - case TYPE_ABSTIME : buf.putLongLong(value.s64); break; - case TYPE_DELTATIME : buf.putLongLong(value.u64); break; - case TYPE_BOOL : buf.putOctet(value.boolVal ? 1 : 0); break; - case TYPE_FLOAT : buf.putFloat(value.floatVal); break; - case TYPE_DOUBLE : buf.putDouble(value.doubleVal); break; - case TYPE_INT8 : buf.putOctet((uint8_t) value.s32); break; - case TYPE_INT16 : buf.putShort((uint16_t) value.s32); break; - case TYPE_INT32 : buf.putLong(value.s32); break; - case TYPE_INT64 : buf.putLongLong(value.s64); break; - case TYPE_UUID : buf.putBin128(value.uuidVal); break; - case TYPE_REF : refVal.impl->encode(buf); break; - case TYPE_MAP: // TODO - case TYPE_LIST: - case TYPE_ARRAY: - case TYPE_OBJECT: - default: - break; - } -} - -bool ValueImpl::keyInMap(const char* key) const -{ - return typecode == TYPE_MAP && mapVal.count(key) > 0; -} - -Value* ValueImpl::byKey(const char* key) -{ - if (keyInMap(key)) { - map::iterator iter = mapVal.find(key); - if (iter != mapVal.end()) - return iter->second.get(); - } - return 0; -} - -const Value* ValueImpl::byKey(const char* key) const -{ - if (keyInMap(key)) { - map::const_iterator iter = mapVal.find(key); - if (iter != mapVal.end()) - return iter->second.get(); - } - return 0; -} - -void ValueImpl::deleteKey(const char* key) -{ - mapVal.erase(key); -} - -void ValueImpl::insert(const char* key, Value* val) -{ - mapVal[key] = VPtr(val); -} - -const char* ValueImpl::key(uint32_t idx) const -{ - map::const_iterator iter = mapVal.begin(); - for (uint32_t i = 0; i < idx; i++) { - if (iter == mapVal.end()) - break; - iter++; - } - - if (iter == mapVal.end()) - return 0; - else - return iter->first.c_str(); -} - -Value* ValueImpl::listItem(uint32_t) -{ - return 0; -} - -void ValueImpl::appendToList(Value*) -{ -} - -void ValueImpl::deleteListItem(uint32_t) -{ -} - -Value* ValueImpl::arrayItem(uint32_t) -{ - return 0; -} - -void ValueImpl::appendToArray(Value*) -{ -} - -void ValueImpl::deleteArrayItem(uint32_t) -{ -} - - -//================================================================== -// Wrappers -//================================================================== - -Value::Value(Typecode t, Typecode at) -{ - impl = new ValueImpl(this, t, at); -} - -Value::Value(ValueImpl* i) -{ - impl = i; -} - -Value::~Value() -{ - delete impl; -} - -Typecode Value::getType() const -{ - return impl->getType(); -} - -bool Value::isNull() const -{ - return impl->isNull(); -} - -void Value::setNull() -{ - impl->setNull(); -} - -bool Value::isObjectId() const -{ - return impl->isObjectId(); -} - -const ObjectId& Value::asObjectId() const -{ - return impl->asObjectId(); -} - -void Value::setObjectId(const ObjectId& oid) -{ - impl->setObjectId(oid); -} - -bool Value::isUint() const -{ - return impl->isUint(); -} - -uint32_t Value::asUint() const -{ - return impl->asUint(); -} - -void Value::setUint(uint32_t val) -{ - impl->setUint(val); -} - -bool Value::isInt() const -{ - return impl->isInt(); -} - -int32_t Value::asInt() const -{ - return impl->asInt(); -} - -void Value::setInt(int32_t val) -{ - impl->setInt(val); -} - -bool Value::isUint64() const -{ - return impl->isUint64(); -} - -uint64_t Value::asUint64() const -{ - return impl->asUint64(); -} - -void Value::setUint64(uint64_t val) -{ - impl->setUint64(val); -} - -bool Value::isInt64() const -{ - return impl->isInt64(); -} - -int64_t Value::asInt64() const -{ - return impl->asInt64(); -} - -void Value::setInt64(int64_t val) -{ - impl->setInt64(val); -} - -bool Value::isString() const -{ - return impl->isString(); -} - -const char* Value::asString() const -{ - return impl->asString(); -} - -void Value::setString(const char* val) -{ - impl->setString(val); -} - -bool Value::isBool() const -{ - return impl->isBool(); -} - -bool Value::asBool() const -{ - return impl->asBool(); -} - -void Value::setBool(bool val) -{ - impl->setBool(val); -} - -bool Value::isFloat() const -{ - return impl->isFloat(); -} - -float Value::asFloat() const -{ - return impl->asFloat(); -} - -void Value::setFloat(float val) -{ - impl->setFloat(val); -} - -bool Value::isDouble() const -{ - return impl->isDouble(); -} - -double Value::asDouble() const -{ - return impl->asDouble(); -} - -void Value::setDouble(double val) -{ - impl->setDouble(val); -} - -bool Value::isUuid() const -{ - return impl->isUuid(); -} - -const uint8_t* Value::asUuid() const -{ - return impl->asUuid(); -} - -void Value::setUuid(const uint8_t* val) -{ - impl->setUuid(val); -} - -bool Value::isObject() const -{ - return impl->isObject(); -} - -Object* Value::asObject() const -{ - return impl->asObject(); -} - -void Value::setObject(Object* val) -{ - impl->setObject(val); -} - -bool Value::isMap() const -{ - return impl->isMap(); -} - -bool Value::keyInMap(const char* key) const -{ - return impl->keyInMap(key); -} - -Value* Value::byKey(const char* key) -{ - return impl->byKey(key); -} - -const Value* Value::byKey(const char* key) const -{ - return impl->byKey(key); -} - -void Value::deleteKey(const char* key) -{ - impl->deleteKey(key); -} - -void Value::insert(const char* key, Value* val) -{ - impl->insert(key, val); -} - -uint32_t Value::keyCount() const -{ - return impl->keyCount(); -} - -const char* Value::key(uint32_t idx) const -{ - return impl->key(idx); -} - -bool Value::isList() const -{ - return impl->isList(); -} - -uint32_t Value::listItemCount() const -{ - return impl->listItemCount(); -} - -Value* Value::listItem(uint32_t idx) -{ - return impl->listItem(idx); -} - -void Value::appendToList(Value* val) -{ - impl->appendToList(val); -} - -void Value::deleteListItem(uint32_t idx) -{ - impl->deleteListItem(idx); -} - -bool Value::isArray() const -{ - return impl->isArray(); -} - -Typecode Value::arrayType() const -{ - return impl->arrayType(); -} - -uint32_t Value::arrayItemCount() const -{ - return impl->arrayItemCount(); -} - -Value* Value::arrayItem(uint32_t idx) -{ - return impl->arrayItem(idx); -} - -void Value::appendToArray(Value* val) -{ - impl->appendToArray(val); -} - -void Value::deleteArrayItem(uint32_t idx) -{ - impl->deleteArrayItem(idx); -} - diff --git a/qpid/cpp/src/qmf/ValueImpl.h b/qpid/cpp/src/qmf/ValueImpl.h deleted file mode 100644 index cf33035bf7..0000000000 --- a/qpid/cpp/src/qmf/ValueImpl.h +++ /dev/null @@ -1,144 +0,0 @@ -#ifndef _QmfValueImpl_ -#define _QmfValueImpl_ - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace qmf { - - // TODO: set valid flag on all value settors - // TODO: add a modified flag and accessors - - struct ValueImpl { - typedef boost::shared_ptr VPtr; - typedef boost::shared_ptr OPtr; - Value* envelope; - const Typecode typecode; - bool valid; - - ObjectId refVal; - std::string stringVal; - OPtr objectVal; - std::map mapVal; - std::vector vectorVal; - Typecode arrayTypecode; - - union { - uint32_t u32; - uint64_t u64; - int32_t s32; - int64_t s64; - bool boolVal; - float floatVal; - double doubleVal; - uint8_t uuidVal[16]; - } value; - - ValueImpl(Value* e, Typecode t, Typecode at) : - envelope(e), typecode(t), valid(false), arrayTypecode(at) {} - ValueImpl(Typecode t, qpid::framing::Buffer& b); - ValueImpl(Typecode t); - ~ValueImpl(); - - void encode(qpid::framing::Buffer& b) const; - - Typecode getType() const { return typecode; } - bool isNull() const { return !valid; } - void setNull() { valid = false; } - - bool isObjectId() const { return typecode == TYPE_REF; } - const ObjectId& asObjectId() const { return refVal; } - void setObjectId(const ObjectId& o) { refVal = o; } // TODO - - bool isUint() const { return typecode >= TYPE_UINT8 && typecode <= TYPE_UINT32; } - uint32_t asUint() const { return value.u32; } - void setUint(uint32_t val) { value.u32 = val; } - - bool isInt() const { return typecode >= TYPE_INT8 && typecode <= TYPE_INT32; } - int32_t asInt() const { return value.s32; } - void setInt(int32_t val) { value.s32 = val; } - - bool isUint64() const { return typecode == TYPE_UINT64 || typecode == TYPE_DELTATIME; } - uint64_t asUint64() const { return value.u64; } - void setUint64(uint64_t val) { value.u64 = val; } - - bool isInt64() const { return typecode == TYPE_INT64 || typecode == TYPE_ABSTIME; } - int64_t asInt64() const { return value.s64; } - void setInt64(int64_t val) { value.s64 = val; } - - bool isString() const { return typecode == TYPE_SSTR || typecode == TYPE_LSTR; } - const char* asString() const { return stringVal.c_str(); } - void setString(const char* val) { stringVal = val; } - - bool isBool() const { return typecode == TYPE_BOOL; } - bool asBool() const { return value.boolVal; } - void setBool(bool val) { value.boolVal = val; } - - bool isFloat() const { return typecode == TYPE_FLOAT; } - float asFloat() const { return value.floatVal; } - void setFloat(float val) { value.floatVal = val; } - - bool isDouble() const { return typecode == TYPE_DOUBLE; } - double asDouble() const { return value.doubleVal; } - void setDouble(double val) { value.doubleVal = val; } - - bool isUuid() const { return typecode == TYPE_UUID; } - const uint8_t* asUuid() const { return value.uuidVal; } - void setUuid(const uint8_t* val) { ::memcpy(value.uuidVal, val, 16); } - - bool isObject() const { return typecode == TYPE_OBJECT; } - Object* asObject() const { return objectVal.get(); } - void setObject(Object* val) { objectVal.reset(val); } - - bool isMap() const { return typecode == TYPE_MAP; } - bool keyInMap(const char* key) const; - Value* byKey(const char* key); - const Value* byKey(const char* key) const; - void deleteKey(const char* key); - void insert(const char* key, Value* val); - uint32_t keyCount() const { return mapVal.size(); } - const char* key(uint32_t idx) const; - - bool isList() const { return typecode == TYPE_LIST; } - uint32_t listItemCount() const { return vectorVal.size(); } - Value* listItem(uint32_t idx); - void appendToList(Value* val); - void deleteListItem(uint32_t idx); - - bool isArray() const { return typecode == TYPE_ARRAY; } - Typecode arrayType() const { return arrayTypecode; } - uint32_t arrayItemCount() const { return vectorVal.size(); } - Value* arrayItem(uint32_t idx); - void appendToArray(Value* val); - void deleteArrayItem(uint32_t idx); - }; -} - -#endif - diff --git a/qpid/cpp/src/qmf/engine/Agent.cpp b/qpid/cpp/src/qmf/engine/Agent.cpp new file mode 100644 index 0000000000..c5d1bff2e0 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/Agent.cpp @@ -0,0 +1,857 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/engine/Agent.h" +#include "qmf/engine/MessageImpl.h" +#include "qmf/engine/SchemaImpl.h" +#include "qmf/engine/Typecode.h" +#include "qmf/engine/ObjectImpl.h" +#include "qmf/engine/ObjectIdImpl.h" +#include "qmf/engine/QueryImpl.h" +#include "qmf/engine/ValueImpl.h" +#include "qmf/engine/Protocol.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace qmf::engine; +using namespace qpid::framing; +using namespace qpid::sys; + +namespace qmf { +namespace engine { + + struct AgentEventImpl { + typedef boost::shared_ptr Ptr; + AgentEvent::EventKind kind; + uint32_t sequence; + string authUserId; + string authToken; + string name; + Object* object; + boost::shared_ptr objectId; + boost::shared_ptr query; + boost::shared_ptr arguments; + string exchange; + string bindingKey; + const SchemaObjectClass* objectClass; + + AgentEventImpl(AgentEvent::EventKind k) : + kind(k), sequence(0), object(0), objectClass(0) {} + ~AgentEventImpl() {} + AgentEvent copy(); + }; + + struct AgentQueryContext { + typedef boost::shared_ptr Ptr; + uint32_t sequence; + string exchange; + string key; + const SchemaMethod* schemaMethod; + AgentQueryContext() : schemaMethod(0) {} + }; + + class AgentImpl : public boost::noncopyable { + public: + AgentImpl(char* label, bool internalStore); + ~AgentImpl(); + + void setStoreDir(const char* path); + void setTransferDir(const char* path); + void handleRcvMessage(Message& message); + bool getXmtMessage(Message& item) const; + void popXmt(); + bool getEvent(AgentEvent& event) const; + void popEvent(); + void newSession(); + void startProtocol(); + void heartbeat(); + void methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments); + void queryResponse(uint32_t sequence, Object& object, bool prop, bool stat); + void queryComplete(uint32_t sequence); + void registerClass(SchemaObjectClass* cls); + void registerClass(SchemaEventClass* cls); + const ObjectId* addObject(Object& obj, uint64_t persistId); + const ObjectId* allocObjectId(uint64_t persistId); + const ObjectId* allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi); + void raiseEvent(Event& event); + + private: + mutable Mutex lock; + Mutex addLock; + string label; + string queueName; + string storeDir; + string transferDir; + bool internalStore; + uint64_t nextTransientId; + Uuid systemId; + uint32_t requestedBrokerBank; + uint32_t requestedAgentBank; + uint32_t assignedBrokerBank; + uint32_t assignedAgentBank; + AgentAttachment attachment; + uint16_t bootSequence; + uint64_t nextObjectId; + uint32_t nextContextNum; + deque eventQueue; + deque xmtQueue; + map contextMap; + + static const char* QMF_EXCHANGE; + static const char* DIR_EXCHANGE; + static const char* BROKER_KEY; + static const uint32_t MERR_UNKNOWN_METHOD = 2; + static const uint32_t MERR_UNKNOWN_PACKAGE = 8; + static const uint32_t MERR_UNKNOWN_CLASS = 9; + static const uint32_t MERR_INTERNAL_ERROR = 10; +# define MA_BUFFER_SIZE 65536 + char outputBuffer[MA_BUFFER_SIZE]; + + struct AgentClassKey { + string name; + uint8_t hash[16]; + AgentClassKey(const string& n, const uint8_t* h) : name(n) { + memcpy(hash, h, 16); + } + AgentClassKey(Buffer& buffer) { + buffer.getShortString(name); + buffer.getBin128(hash); + } + string repr() { + return name; + } + }; + + struct AgentClassKeyComp { + bool operator() (const AgentClassKey& lhs, const AgentClassKey& rhs) const + { + if (lhs.name != rhs.name) + return lhs.name < rhs.name; + else + for (int i = 0; i < 16; i++) + if (lhs.hash[i] != rhs.hash[i]) + return lhs.hash[i] < rhs.hash[i]; + return false; + } + }; + + typedef map ObjectClassMap; + typedef map EventClassMap; + + struct ClassMaps { + ObjectClassMap objectClasses; + EventClassMap eventClasses; + }; + + map packages; + + AgentEventImpl::Ptr eventDeclareQueue(const string& queueName); + AgentEventImpl::Ptr eventBind(const string& exchange, const string& queue, const string& key); + AgentEventImpl::Ptr eventSetupComplete(); + AgentEventImpl::Ptr eventQuery(uint32_t num, const string& userId, const string& package, const string& cls, + boost::shared_ptr oid); + AgentEventImpl::Ptr eventMethod(uint32_t num, const string& userId, const string& method, + boost::shared_ptr oid, boost::shared_ptr argMap, + const SchemaObjectClass* objectClass); + void sendBufferLH(Buffer& buf, const string& destination, const string& routingKey); + + void sendPackageIndicationLH(const string& packageName); + void sendClassIndicationLH(ClassKind kind, const string& packageName, const AgentClassKey& key); + void sendCommandCompleteLH(const string& exchange, const string& key, uint32_t seq, + uint32_t code = 0, const string& text = "OK"); + void sendMethodErrorLH(uint32_t sequence, const string& key, uint32_t code, const string& text=""); + void handleAttachResponse(Buffer& inBuffer); + void handlePackageRequest(Buffer& inBuffer); + void handleClassQuery(Buffer& inBuffer); + void handleSchemaRequest(Buffer& inBuffer, uint32_t sequence, + const string& replyToExchange, const string& replyToKey); + void handleGetQuery(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId); + void handleMethodRequest(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId); + void handleConsoleAddedIndication(); + }; +} +} + +const char* AgentImpl::QMF_EXCHANGE = "qpid.management"; +const char* AgentImpl::DIR_EXCHANGE = "amq.direct"; +const char* AgentImpl::BROKER_KEY = "broker"; + +#define STRING_REF(s) {if (!s.empty()) item.s = const_cast(s.c_str());} + +AgentEvent AgentEventImpl::copy() +{ + AgentEvent item; + + ::memset(&item, 0, sizeof(AgentEvent)); + item.kind = kind; + item.sequence = sequence; + item.object = object; + item.objectId = objectId.get(); + item.query = query.get(); + item.arguments = arguments.get(); + item.objectClass = objectClass; + + STRING_REF(authUserId); + STRING_REF(authToken); + STRING_REF(name); + STRING_REF(exchange); + STRING_REF(bindingKey); + + return item; +} + +AgentImpl::AgentImpl(char* _label, bool i) : + label(_label), queueName("qmfa-"), internalStore(i), nextTransientId(1), + requestedBrokerBank(0), requestedAgentBank(0), + assignedBrokerBank(0), assignedAgentBank(0), + bootSequence(1), nextObjectId(1), nextContextNum(1) +{ + queueName += label; +} + +AgentImpl::~AgentImpl() +{ +} + +void AgentImpl::setStoreDir(const char* path) +{ + Mutex::ScopedLock _lock(lock); + if (path) + storeDir = path; + else + storeDir.clear(); +} + +void AgentImpl::setTransferDir(const char* path) +{ + Mutex::ScopedLock _lock(lock); + if (path) + transferDir = path; + else + transferDir.clear(); +} + +void AgentImpl::handleRcvMessage(Message& message) +{ + Buffer inBuffer(message.body, message.length); + uint8_t opcode; + uint32_t sequence; + string replyToExchange(message.replyExchange ? message.replyExchange : ""); + string replyToKey(message.replyKey ? message.replyKey : ""); + string userId(message.userId ? message.userId : ""); + + while (Protocol::checkHeader(inBuffer, &opcode, &sequence)) { + if (opcode == Protocol::OP_ATTACH_RESPONSE) handleAttachResponse(inBuffer); + else if (opcode == Protocol::OP_SCHEMA_REQUEST) handleSchemaRequest(inBuffer, sequence, replyToExchange, replyToKey); + else if (opcode == Protocol::OP_CONSOLE_ADDED_INDICATION) handleConsoleAddedIndication(); + else if (opcode == Protocol::OP_GET_QUERY) handleGetQuery(inBuffer, sequence, replyToKey, userId); + else if (opcode == Protocol::OP_METHOD_REQUEST) handleMethodRequest(inBuffer, sequence, replyToKey, userId); + else { + QPID_LOG(error, "AgentImpl::handleRcvMessage invalid opcode=" << opcode); + break; + } + } +} + +bool AgentImpl::getXmtMessage(Message& item) const +{ + Mutex::ScopedLock _lock(lock); + if (xmtQueue.empty()) + return false; + item = xmtQueue.front()->copy(); + return true; +} + +void AgentImpl::popXmt() +{ + Mutex::ScopedLock _lock(lock); + if (!xmtQueue.empty()) + xmtQueue.pop_front(); +} + +bool AgentImpl::getEvent(AgentEvent& event) const +{ + Mutex::ScopedLock _lock(lock); + if (eventQueue.empty()) + return false; + event = eventQueue.front()->copy(); + return true; +} + +void AgentImpl::popEvent() +{ + Mutex::ScopedLock _lock(lock); + if (!eventQueue.empty()) + eventQueue.pop_front(); +} + +void AgentImpl::newSession() +{ + Mutex::ScopedLock _lock(lock); + eventQueue.clear(); + xmtQueue.clear(); + eventQueue.push_back(eventDeclareQueue(queueName)); + eventQueue.push_back(eventBind("amq.direct", queueName, queueName)); + eventQueue.push_back(eventSetupComplete()); +} + +void AgentImpl::startProtocol() +{ + Mutex::ScopedLock _lock(lock); + char rawbuffer[512]; + Buffer buffer(rawbuffer, 512); + + Protocol::encodeHeader(buffer, Protocol::OP_ATTACH_REQUEST); + buffer.putShortString("qmfa"); + systemId.encode(buffer); + buffer.putLong(requestedBrokerBank); + buffer.putLong(requestedAgentBank); + sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT AttachRequest: reqBroker=" << requestedBrokerBank << + " reqAgent=" << requestedAgentBank); +} + +void AgentImpl::heartbeat() +{ + Mutex::ScopedLock _lock(lock); + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + + Protocol::encodeHeader(buffer, Protocol::OP_HEARTBEAT_INDICATION); + buffer.putLongLong(uint64_t(Duration(now()))); + stringstream key; + key << "console.heartbeat." << assignedBrokerBank << "." << assignedAgentBank; + sendBufferLH(buffer, QMF_EXCHANGE, key.str()); + QPID_LOG(trace, "SENT HeartbeatIndication"); +} + +void AgentImpl::methodResponse(uint32_t sequence, uint32_t status, char* text, + const Value& argMap) +{ + Mutex::ScopedLock _lock(lock); + map::iterator iter = contextMap.find(sequence); + if (iter == contextMap.end()) + return; + AgentQueryContext::Ptr context = iter->second; + contextMap.erase(iter); + + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_METHOD_RESPONSE, context->sequence); + buffer.putLong(status); + buffer.putMediumString(text); + if (status == 0) { + for (vector::const_iterator aIter = context->schemaMethod->impl->arguments.begin(); + aIter != context->schemaMethod->impl->arguments.end(); aIter++) { + const SchemaArgument* schemaArg = *aIter; + if (schemaArg->getDirection() == DIR_OUT || schemaArg->getDirection() == DIR_IN_OUT) { + if (argMap.keyInMap(schemaArg->getName())) { + const Value* val = argMap.byKey(schemaArg->getName()); + val->impl->encode(buffer); + } else { + Value val(schemaArg->getType()); + val.impl->encode(buffer); + } + } + } + } + sendBufferLH(buffer, context->exchange, context->key); + QPID_LOG(trace, "SENT MethodResponse seq=" << context->sequence << " status=" << status << " text=" << text); +} + +void AgentImpl::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat) +{ + Mutex::ScopedLock _lock(lock); + map::iterator iter = contextMap.find(sequence); + if (iter == contextMap.end()) + return; + AgentQueryContext::Ptr context = iter->second; + + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_OBJECT_INDICATION, context->sequence); + + object.impl->encodeSchemaKey(buffer); + object.impl->encodeManagedObjectData(buffer); + if (prop) + object.impl->encodeProperties(buffer); + if (stat) + object.impl->encodeStatistics(buffer); + + sendBufferLH(buffer, context->exchange, context->key); + QPID_LOG(trace, "SENT ContentIndication seq=" << context->sequence); +} + +void AgentImpl::queryComplete(uint32_t sequence) +{ + Mutex::ScopedLock _lock(lock); + map::iterator iter = contextMap.find(sequence); + if (iter == contextMap.end()) + return; + + AgentQueryContext::Ptr context = iter->second; + contextMap.erase(iter); + sendCommandCompleteLH(context->exchange, context->key, context->sequence, 0, "OK"); +} + +void AgentImpl::registerClass(SchemaObjectClass* cls) +{ + Mutex::ScopedLock _lock(lock); + + map::iterator iter = packages.find(cls->getClassKey()->getPackageName()); + if (iter == packages.end()) { + packages[cls->getClassKey()->getPackageName()] = ClassMaps(); + iter = packages.find(cls->getClassKey()->getPackageName()); + // TODO: Indicate this package if connected + } + + AgentClassKey key(cls->getClassKey()->getClassName(), cls->getClassKey()->getHash()); + iter->second.objectClasses[key] = cls; + + // TODO: Indicate this schema if connected. +} + +void AgentImpl::registerClass(SchemaEventClass* cls) +{ + Mutex::ScopedLock _lock(lock); + + map::iterator iter = packages.find(cls->getClassKey()->getPackageName()); + if (iter == packages.end()) { + packages[cls->getClassKey()->getPackageName()] = ClassMaps(); + iter = packages.find(cls->getClassKey()->getPackageName()); + // TODO: Indicate this package if connected + } + + AgentClassKey key(cls->getClassKey()->getClassName(), cls->getClassKey()->getHash()); + iter->second.eventClasses[key] = cls; + + // TODO: Indicate this schema if connected. +} + +const ObjectId* AgentImpl::addObject(Object&, uint64_t) +{ + Mutex::ScopedLock _lock(lock); + return 0; +} + +const ObjectId* AgentImpl::allocObjectId(uint64_t persistId) +{ + Mutex::ScopedLock _lock(lock); + uint16_t sequence = persistId ? 0 : bootSequence; + uint64_t objectNum = persistId ? persistId : nextObjectId++; + + ObjectId* oid = ObjectIdImpl::factory(&attachment, 0, sequence, objectNum); + return oid; +} + +const ObjectId* AgentImpl::allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi) +{ + return allocObjectId(((uint64_t) persistIdHi) << 32 | (uint64_t) persistIdLo); +} + +void AgentImpl::raiseEvent(Event&) +{ + Mutex::ScopedLock _lock(lock); +} + +AgentEventImpl::Ptr AgentImpl::eventDeclareQueue(const string& name) +{ + AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::DECLARE_QUEUE)); + event->name = name; + + return event; +} + +AgentEventImpl::Ptr AgentImpl::eventBind(const string& exchange, const string& queue, + const string& key) +{ + AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::BIND)); + event->name = queue; + event->exchange = exchange; + event->bindingKey = key; + + return event; +} + +AgentEventImpl::Ptr AgentImpl::eventSetupComplete() +{ + AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::SETUP_COMPLETE)); + return event; +} + +AgentEventImpl::Ptr AgentImpl::eventQuery(uint32_t num, const string& userId, const string& package, + const string& cls, boost::shared_ptr oid) +{ + AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::GET_QUERY)); + event->sequence = num; + event->authUserId = userId; + if (oid.get()) + event->query.reset(new Query(oid.get())); + else + event->query.reset(new Query(cls.c_str(), package.c_str())); + return event; +} + +AgentEventImpl::Ptr AgentImpl::eventMethod(uint32_t num, const string& userId, const string& method, + boost::shared_ptr oid, boost::shared_ptr argMap, + const SchemaObjectClass* objectClass) +{ + AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::METHOD_CALL)); + event->sequence = num; + event->authUserId = userId; + event->name = method; + event->objectId = oid; + event->arguments = argMap; + event->objectClass = objectClass; + return event; +} + +void AgentImpl::sendBufferLH(Buffer& buf, const string& destination, const string& routingKey) +{ + uint32_t length = buf.getPosition(); + MessageImpl::Ptr message(new MessageImpl); + + buf.reset(); + buf.getRawData(message->body, length); + message->destination = destination; + message->routingKey = routingKey; + message->replyExchange = "amq.direct"; + message->replyKey = queueName; + + xmtQueue.push_back(message); +} + +void AgentImpl::sendPackageIndicationLH(const string& packageName) +{ + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_PACKAGE_INDICATION); + buffer.putShortString(packageName); + sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT PackageIndication: package_name=" << packageName); +} + +void AgentImpl::sendClassIndicationLH(ClassKind kind, const string& packageName, const AgentClassKey& key) +{ + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_CLASS_INDICATION); + buffer.putOctet((int) kind); + buffer.putShortString(packageName); + buffer.putShortString(key.name); + buffer.putBin128(const_cast(key.hash)); // const_cast needed for older Qpid libraries + sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT ClassIndication: package_name=" << packageName << " class_name=" << key.name); +} + +void AgentImpl::sendCommandCompleteLH(const string& exchange, const string& replyToKey, + uint32_t sequence, uint32_t code, const string& text) +{ + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_COMMAND_COMPLETE, sequence); + buffer.putLong(code); + buffer.putShortString(text); + sendBufferLH(buffer, exchange, replyToKey); + QPID_LOG(trace, "SENT CommandComplete: seq=" << sequence << " code=" << code << " text=" << text); +} + +void AgentImpl::sendMethodErrorLH(uint32_t sequence, const string& key, uint32_t code, const string& text) +{ + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_METHOD_RESPONSE, sequence); + buffer.putLong(code); + + string fulltext; + switch (code) { + case MERR_UNKNOWN_PACKAGE: fulltext = "Unknown Package"; break; + case MERR_UNKNOWN_CLASS: fulltext = "Unknown Class"; break; + case MERR_UNKNOWN_METHOD: fulltext = "Unknown Method"; break; + case MERR_INTERNAL_ERROR: fulltext = "Internal Error"; break; + default: fulltext = "Unspecified Error"; break; + } + + if (!text.empty()) { + fulltext += " ("; + fulltext += text; + fulltext += ")"; + } + + buffer.putMediumString(fulltext); + sendBufferLH(buffer, DIR_EXCHANGE, key); + QPID_LOG(trace, "SENT MethodResponse: errorCode=" << code << " text=" << fulltext); +} + +void AgentImpl::handleAttachResponse(Buffer& inBuffer) +{ + Mutex::ScopedLock _lock(lock); + + assignedBrokerBank = inBuffer.getLong(); + assignedAgentBank = inBuffer.getLong(); + + QPID_LOG(trace, "RCVD AttachResponse: broker=" << assignedBrokerBank << " agent=" << assignedAgentBank); + + if ((assignedBrokerBank != requestedBrokerBank) || + (assignedAgentBank != requestedAgentBank)) { + if (requestedAgentBank == 0) { + QPID_LOG(notice, "Initial object-id bank assigned: " << assignedBrokerBank << "." << + assignedAgentBank); + } else { + QPID_LOG(warning, "Collision in object-id! New bank assigned: " << assignedBrokerBank << + "." << assignedAgentBank); + } + //storeData(); // TODO + requestedBrokerBank = assignedBrokerBank; + requestedAgentBank = assignedAgentBank; + } + + attachment.setBanks(assignedBrokerBank, assignedAgentBank); + + // Bind to qpid.management to receive commands + stringstream key; + key << "agent." << assignedBrokerBank << "." << assignedAgentBank; + eventQueue.push_back(eventBind(QMF_EXCHANGE, queueName, key.str())); + + // Send package indications for all local packages + for (map::iterator pIter = packages.begin(); + pIter != packages.end(); + pIter++) { + sendPackageIndicationLH(pIter->first); + + // Send class indications for all local classes + ClassMaps cMap = pIter->second; + for (ObjectClassMap::iterator cIter = cMap.objectClasses.begin(); + cIter != cMap.objectClasses.end(); cIter++) + sendClassIndicationLH(CLASS_OBJECT, pIter->first, cIter->first); + for (EventClassMap::iterator cIter = cMap.eventClasses.begin(); + cIter != cMap.eventClasses.end(); cIter++) + sendClassIndicationLH(CLASS_EVENT, pIter->first, cIter->first); + } +} + +void AgentImpl::handlePackageRequest(Buffer&) +{ + Mutex::ScopedLock _lock(lock); +} + +void AgentImpl::handleClassQuery(Buffer&) +{ + Mutex::ScopedLock _lock(lock); +} + +void AgentImpl::handleSchemaRequest(Buffer& inBuffer, uint32_t sequence, + const string& replyExchange, const string& replyKey) +{ + Mutex::ScopedLock _lock(lock); + string rExchange(replyExchange); + string rKey(replyKey); + string packageName; + inBuffer.getShortString(packageName); + AgentClassKey key(inBuffer); + + if (rExchange.empty()) + rExchange = QMF_EXCHANGE; + if (rKey.empty()) + rKey = BROKER_KEY; + + QPID_LOG(trace, "RCVD SchemaRequest: package=" << packageName << " class=" << key.name); + + map::iterator pIter = packages.find(packageName); + if (pIter == packages.end()) { + sendCommandCompleteLH(rExchange, rKey, sequence, 1, "package not found"); + return; + } + + ClassMaps cMap = pIter->second; + ObjectClassMap::iterator ocIter = cMap.objectClasses.find(key); + if (ocIter != cMap.objectClasses.end()) { + SchemaObjectClass* oImpl = ocIter->second; + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_SCHEMA_RESPONSE, sequence); + oImpl->impl->encode(buffer); + sendBufferLH(buffer, rExchange, rKey); + QPID_LOG(trace, "SENT SchemaResponse: (object) package=" << packageName << " class=" << key.name); + return; + } + + EventClassMap::iterator ecIter = cMap.eventClasses.find(key); + if (ecIter != cMap.eventClasses.end()) { + SchemaEventClass* eImpl = ecIter->second; + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_SCHEMA_RESPONSE, sequence); + eImpl->impl->encode(buffer); + sendBufferLH(buffer, rExchange, rKey); + QPID_LOG(trace, "SENT SchemaResponse: (event) package=" << packageName << " class=" << key.name); + return; + } + + sendCommandCompleteLH(rExchange, rKey, sequence, 1, "class not found"); +} + +void AgentImpl::handleGetQuery(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId) +{ + Mutex::ScopedLock _lock(lock); + FieldTable ft; + FieldTable::ValuePtr value; + map::const_iterator pIter = packages.end(); + string pname; + string cname; + string oidRepr; + boost::shared_ptr oid; + + ft.decode(inBuffer); + + QPID_LOG(trace, "RCVD GetQuery: seq=" << sequence << " map=" << ft); + + value = ft.get("_package"); + if (value.get() && value->convertsTo()) { + pname = value->get(); + pIter = packages.find(pname); + if (pIter == packages.end()) { + sendCommandCompleteLH(DIR_EXCHANGE, replyTo, sequence); + return; + } + } + + value = ft.get("_class"); + if (value.get() && value->convertsTo()) { + cname = value->get(); + // TODO - check for validity of class (in package or any package) + if (pIter == packages.end()) { + } else { + + } + } + + value = ft.get("_objectid"); + if (value.get() && value->convertsTo()) { + oidRepr = value->get(); + oid.reset(new ObjectId()); + oid->impl->fromString(oidRepr); + } + + AgentQueryContext::Ptr context(new AgentQueryContext); + uint32_t contextNum = nextContextNum++; + context->sequence = sequence; + context->exchange = DIR_EXCHANGE; + context->key = replyTo; + contextMap[contextNum] = context; + + eventQueue.push_back(eventQuery(contextNum, userId, pname, cname, oid)); +} + +void AgentImpl::handleMethodRequest(Buffer& buffer, uint32_t sequence, const string& replyTo, const string& userId) +{ + Mutex::ScopedLock _lock(lock); + string pname; + string method; + boost::shared_ptr oid(ObjectIdImpl::factory(buffer)); + buffer.getShortString(pname); + AgentClassKey classKey(buffer); + buffer.getShortString(method); + + QPID_LOG(trace, "RCVD MethodRequest seq=" << sequence << " method=" << method); + + map::const_iterator pIter = packages.find(pname); + if (pIter == packages.end()) { + sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_PACKAGE, pname); + return; + } + + ObjectClassMap::const_iterator cIter = pIter->second.objectClasses.find(classKey); + if (cIter == pIter->second.objectClasses.end()) { + sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_CLASS, classKey.repr()); + return; + } + + const SchemaObjectClass* schema = cIter->second; + vector::const_iterator mIter = schema->impl->methods.begin(); + for (; mIter != schema->impl->methods.end(); mIter++) { + if ((*mIter)->getName() == method) + break; + } + + if (mIter == schema->impl->methods.end()) { + sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_METHOD, method); + return; + } + + const SchemaMethod* schemaMethod = *mIter; + boost::shared_ptr argMap(new Value(TYPE_MAP)); + Value* value; + for (vector::const_iterator aIter = schemaMethod->impl->arguments.begin(); + aIter != schemaMethod->impl->arguments.end(); aIter++) { + const SchemaArgument* schemaArg = *aIter; + if (schemaArg->getDirection() == DIR_IN || schemaArg->getDirection() == DIR_IN_OUT) + value = ValueImpl::factory(schemaArg->getType(), buffer); + else + value = ValueImpl::factory(schemaArg->getType()); + argMap->insert(schemaArg->getName(), value); + } + + AgentQueryContext::Ptr context(new AgentQueryContext); + uint32_t contextNum = nextContextNum++; + context->sequence = sequence; + context->exchange = DIR_EXCHANGE; + context->key = replyTo; + context->schemaMethod = schemaMethod; + contextMap[contextNum] = context; + + eventQueue.push_back(eventMethod(contextNum, userId, method, oid, argMap, schema)); +} + +void AgentImpl::handleConsoleAddedIndication() +{ + Mutex::ScopedLock _lock(lock); +} + +//================================================================== +// Wrappers +//================================================================== + +Agent::Agent(char* label, bool internalStore) { impl = new AgentImpl(label, internalStore); } +Agent::~Agent() { delete impl; } +void Agent::setStoreDir(const char* path) { impl->setStoreDir(path); } +void Agent::setTransferDir(const char* path) { impl->setTransferDir(path); } +void Agent::handleRcvMessage(Message& message) { impl->handleRcvMessage(message); } +bool Agent::getXmtMessage(Message& item) const { return impl->getXmtMessage(item); } +void Agent::popXmt() { impl->popXmt(); } +bool Agent::getEvent(AgentEvent& event) const { return impl->getEvent(event); } +void Agent::popEvent() { impl->popEvent(); } +void Agent::newSession() { impl->newSession(); } +void Agent::startProtocol() { impl->startProtocol(); } +void Agent::heartbeat() { impl->heartbeat(); } +void Agent::methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments) { impl->methodResponse(sequence, status, text, arguments); } +void Agent::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat) { impl->queryResponse(sequence, object, prop, stat); } +void Agent::queryComplete(uint32_t sequence) { impl->queryComplete(sequence); } +void Agent::registerClass(SchemaObjectClass* cls) { impl->registerClass(cls); } +void Agent::registerClass(SchemaEventClass* cls) { impl->registerClass(cls); } +const ObjectId* Agent::addObject(Object& obj, uint64_t persistId) { return impl->addObject(obj, persistId); } +const ObjectId* Agent::allocObjectId(uint64_t persistId) { return impl->allocObjectId(persistId); } +const ObjectId* Agent::allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi) { return impl->allocObjectId(persistIdLo, persistIdHi); } +void Agent::raiseEvent(Event& event) { impl->raiseEvent(event); } + diff --git a/qpid/cpp/src/qmf/engine/BrokerProxyImpl.cpp b/qpid/cpp/src/qmf/engine/BrokerProxyImpl.cpp new file mode 100644 index 0000000000..1a2b3e6555 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/BrokerProxyImpl.cpp @@ -0,0 +1,763 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/engine/BrokerProxyImpl.h" +#include "qmf/engine/ConsoleImpl.h" +#include "qmf/engine/Protocol.h" +#include "qpid/Address.h" +#include "qpid/sys/SystemInfo.h" +#include +#include +#include +#include +#include + +using namespace std; +using namespace qmf::engine; +using namespace qpid::framing; +using namespace qpid::sys; + +namespace { + const char* QMF_EXCHANGE = "qpid.management"; + const char* DIR_EXCHANGE = "amq.direct"; + const char* BROKER_KEY = "broker"; + const char* BROKER_PACKAGE = "org.apache.qpid.broker"; + const char* AGENT_CLASS = "agent"; + const char* BROKER_AGENT_KEY = "agent.1.0"; +} + +const Object* QueryResponseImpl::getObject(uint32_t idx) const +{ + vector::const_iterator iter = results.begin(); + + while (idx > 0) { + if (iter == results.end()) + return 0; + iter++; + idx--; + } + + return iter->get(); +} + +#define STRING_REF(s) {if (!s.empty()) item.s = const_cast(s.c_str());} + +BrokerEvent BrokerEventImpl::copy() +{ + BrokerEvent item; + + ::memset(&item, 0, sizeof(BrokerEvent)); + item.kind = kind; + + STRING_REF(name); + STRING_REF(exchange); + STRING_REF(bindingKey); + item.context = context; + item.queryResponse = queryResponse.get(); + item.methodResponse = methodResponse.get(); + + return item; +} + +BrokerProxyImpl::BrokerProxyImpl(BrokerProxy& pub, Console& _console) : publicObject(pub), console(_console) +{ + stringstream qn; + qpid::TcpAddress addr; + + SystemInfo::getLocalHostname(addr); + qn << "qmfc-" << SystemInfo::getProcessName() << "-" << addr << "-" << SystemInfo::getProcessId(); + queueName = qn.str(); + + seqMgr.setUnsolicitedContext(SequenceContext::Ptr(new StaticContext(*this))); +} + +void BrokerProxyImpl::sessionOpened(SessionHandle& /*sh*/) +{ + Mutex::ScopedLock _lock(lock); + agentList.clear(); + eventQueue.clear(); + xmtQueue.clear(); + eventQueue.push_back(eventDeclareQueue(queueName)); + eventQueue.push_back(eventBind(DIR_EXCHANGE, queueName, queueName)); + eventQueue.push_back(eventSetupComplete()); + + // TODO: Store session handle +} + +void BrokerProxyImpl::sessionClosed() +{ + Mutex::ScopedLock _lock(lock); + agentList.clear(); + eventQueue.clear(); + xmtQueue.clear(); +} + +void BrokerProxyImpl::startProtocol() +{ + AgentProxyPtr agent(AgentProxyImpl::factory(console, publicObject, 0, "Agent embedded in broker")); + { + Mutex::ScopedLock _lock(lock); + char rawbuffer[512]; + Buffer buffer(rawbuffer, 512); + + agentList[0] = agent; + + requestsOutstanding = 1; + topicBound = false; + uint32_t sequence(seqMgr.reserve()); + Protocol::encodeHeader(buffer, Protocol::OP_BROKER_REQUEST, sequence); + sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT BrokerRequest seq=" << sequence); + } + + console.impl->eventAgentAdded(agent); +} + +void BrokerProxyImpl::sendBufferLH(Buffer& buf, const string& destination, const string& routingKey) +{ + uint32_t length = buf.getPosition(); + MessageImpl::Ptr message(new MessageImpl); + + buf.reset(); + buf.getRawData(message->body, length); + message->destination = destination; + message->routingKey = routingKey; + message->replyExchange = DIR_EXCHANGE; + message->replyKey = queueName; + + xmtQueue.push_back(message); +} + +void BrokerProxyImpl::handleRcvMessage(Message& message) +{ + Buffer inBuffer(message.body, message.length); + uint8_t opcode; + uint32_t sequence; + + while (Protocol::checkHeader(inBuffer, &opcode, &sequence)) + seqMgr.dispatch(opcode, sequence, message.routingKey ? string(message.routingKey) : string(), inBuffer); +} + +bool BrokerProxyImpl::getXmtMessage(Message& item) const +{ + Mutex::ScopedLock _lock(lock); + if (xmtQueue.empty()) + return false; + item = xmtQueue.front()->copy(); + return true; +} + +void BrokerProxyImpl::popXmt() +{ + Mutex::ScopedLock _lock(lock); + if (!xmtQueue.empty()) + xmtQueue.pop_front(); +} + +bool BrokerProxyImpl::getEvent(BrokerEvent& event) const +{ + Mutex::ScopedLock _lock(lock); + if (eventQueue.empty()) + return false; + event = eventQueue.front()->copy(); + return true; +} + +void BrokerProxyImpl::popEvent() +{ + Mutex::ScopedLock _lock(lock); + if (!eventQueue.empty()) + eventQueue.pop_front(); +} + +uint32_t BrokerProxyImpl::agentCount() const +{ + Mutex::ScopedLock _lock(lock); + return agentList.size(); +} + +const AgentProxy* BrokerProxyImpl::getAgent(uint32_t idx) const +{ + Mutex::ScopedLock _lock(lock); + for (map::const_iterator iter = agentList.begin(); + iter != agentList.end(); iter++) + if (idx-- == 0) + return iter->second.get(); + return 0; +} + +void BrokerProxyImpl::sendQuery(const Query& query, void* context, const AgentProxy* agent) +{ + SequenceContext::Ptr queryContext(new QueryContext(*this, context)); + Mutex::ScopedLock _lock(lock); + if (agent != 0) { + sendGetRequestLH(queryContext, query, agent); + } else { + // TODO (optimization) only send queries to agents that have the requested class+package + for (map::const_iterator iter = agentList.begin(); + iter != agentList.end(); iter++) { + sendGetRequestLH(queryContext, query, iter->second.get()); + } + } +} + +void BrokerProxyImpl::sendGetRequestLH(SequenceContext::Ptr queryContext, const Query& query, const AgentProxy* agent) +{ + stringstream key; + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve(queryContext)); + agent->impl->addSequence(sequence); + + Protocol::encodeHeader(outBuffer, Protocol::OP_GET_QUERY, sequence); + query.impl->encode(outBuffer); + key << "agent.1." << agent->impl->agentBank; + sendBufferLH(outBuffer, QMF_EXCHANGE, key.str()); + QPID_LOG(trace, "SENT GetQuery seq=" << sequence << " key=" << key.str()); +} + +string BrokerProxyImpl::encodeMethodArguments(const SchemaMethod* schema, const Value* argmap, Buffer& buffer) +{ + int argCount = schema->getArgumentCount(); + + if (argmap == 0 || !argmap->isMap()) + return string("Arguments must be in a map value"); + + for (int aIdx = 0; aIdx < argCount; aIdx++) { + const SchemaArgument* arg(schema->getArgument(aIdx)); + if (arg->getDirection() == DIR_IN || arg->getDirection() == DIR_IN_OUT) { + if (argmap->keyInMap(arg->getName())) { + const Value* argVal(argmap->byKey(arg->getName())); + if (argVal->getType() != arg->getType()) + return string("Argument is the wrong type: ") + arg->getName(); + argVal->impl->encode(buffer); + } else { + Value defaultValue(arg->getType()); + defaultValue.impl->encode(buffer); + } + } + } + + return string(); +} + +void BrokerProxyImpl::sendMethodRequest(ObjectId* oid, const SchemaObjectClass* cls, + const string& methodName, const Value* args, void* userContext) +{ + int methodCount = cls->getMethodCount(); + int idx; + for (idx = 0; idx < methodCount; idx++) { + const SchemaMethod* method = cls->getMethod(idx); + if (string(method->getName()) == methodName) { + Mutex::ScopedLock _lock(lock); + SequenceContext::Ptr methodContext(new MethodContext(*this, userContext, method)); + stringstream key; + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve(methodContext)); + + Protocol::encodeHeader(outBuffer, Protocol::OP_METHOD_REQUEST, sequence); + oid->impl->encode(outBuffer); + cls->getClassKey()->impl->encode(outBuffer); + outBuffer.putShortString(methodName); + + string argErrorString = encodeMethodArguments(method, args, outBuffer); + if (argErrorString.empty()) { + key << "agent.1." << oid->impl->getAgentBank(); + sendBufferLH(outBuffer, QMF_EXCHANGE, key.str()); + QPID_LOG(trace, "SENT MethodRequest seq=" << sequence << " method=" << methodName << " key=" << key.str()); + } else { + MethodResponsePtr argError(MethodResponseImpl::factory(1, argErrorString)); + eventQueue.push_back(eventMethodResponse(userContext, argError)); + } + return; + } + } + + MethodResponsePtr error(MethodResponseImpl::factory(1, string("Unknown method: ") + methodName)); + Mutex::ScopedLock _lock(lock); + eventQueue.push_back(eventMethodResponse(userContext, error)); +} + +void BrokerProxyImpl::addBinding(const string& exchange, const string& key) +{ + Mutex::ScopedLock _lock(lock); + eventQueue.push_back(eventBind(exchange, queueName, key)); +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventDeclareQueue(const string& queueName) +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::DECLARE_QUEUE)); + event->name = queueName; + return event; +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventBind(const string& exchange, const string& queue, const string& key) +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::BIND)); + event->name = queue; + event->exchange = exchange; + event->bindingKey = key; + + return event; +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventSetupComplete() +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::SETUP_COMPLETE)); + return event; +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventStable() +{ + QPID_LOG(trace, "Console Link to Broker Stable"); + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::STABLE)); + return event; +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventQueryComplete(void* context, QueryResponsePtr response) +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::QUERY_COMPLETE)); + event->context = context; + event->queryResponse = response; + return event; +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventMethodResponse(void* context, MethodResponsePtr response) +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::METHOD_RESPONSE)); + event->context = context; + event->methodResponse = response; + return event; +} + +void BrokerProxyImpl::handleBrokerResponse(Buffer& inBuffer, uint32_t seq) +{ + brokerId.decode(inBuffer); + QPID_LOG(trace, "RCVD BrokerResponse seq=" << seq << " brokerId=" << brokerId); + Mutex::ScopedLock _lock(lock); + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve()); + incOutstandingLH(); + Protocol::encodeHeader(outBuffer, Protocol::OP_PACKAGE_REQUEST, sequence); + sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT PackageRequest seq=" << sequence); +} + +void BrokerProxyImpl::handlePackageIndication(Buffer& inBuffer, uint32_t seq) +{ + string package; + + inBuffer.getShortString(package); + QPID_LOG(trace, "RCVD PackageIndication seq=" << seq << " package=" << package); + console.impl->learnPackage(package); + + Mutex::ScopedLock _lock(lock); + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve()); + incOutstandingLH(); + Protocol::encodeHeader(outBuffer, Protocol::OP_CLASS_QUERY, sequence); + outBuffer.putShortString(package); + sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT ClassQuery seq=" << sequence << " package=" << package); +} + +void BrokerProxyImpl::handleCommandComplete(Buffer& inBuffer, uint32_t seq) +{ + string text; + uint32_t code = inBuffer.getLong(); + inBuffer.getShortString(text); + QPID_LOG(trace, "RCVD CommandComplete seq=" << seq << " code=" << code << " text=" << text); +} + +void BrokerProxyImpl::handleClassIndication(Buffer& inBuffer, uint32_t seq) +{ + uint8_t kind = inBuffer.getOctet(); + auto_ptr classKey(SchemaClassKeyImpl::factory(inBuffer)); + + QPID_LOG(trace, "RCVD ClassIndication seq=" << seq << " kind=" << (int) kind << " key=" << classKey->impl->str()); + + if (!console.impl->haveClass(classKey.get())) { + Mutex::ScopedLock _lock(lock); + incOutstandingLH(); + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve()); + Protocol::encodeHeader(outBuffer, Protocol::OP_SCHEMA_REQUEST, sequence); + classKey->impl->encode(outBuffer); + sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT SchemaRequest seq=" << sequence <<" key=" << classKey->impl->str()); + } +} + +MethodResponsePtr BrokerProxyImpl::handleMethodResponse(Buffer& inBuffer, uint32_t seq, const SchemaMethod* schema) +{ + MethodResponsePtr response(MethodResponseImpl::factory(inBuffer, schema)); + + QPID_LOG(trace, "RCVD MethodResponse seq=" << seq << " status=" << response->getStatus() << " text=" << + response->getException()->asString()); + + return response; +} + +void BrokerProxyImpl::handleHeartbeatIndication(Buffer& inBuffer, uint32_t seq, const string& routingKey) +{ + vector tokens = qpid::split(routingKey, "."); + uint32_t agentBank; + uint64_t timestamp; + + if (routingKey.empty() || tokens.size() != 4) + agentBank = 0; + else + agentBank = ::atoi(tokens[3].c_str()); + + timestamp = inBuffer.getLongLong(); + map::const_iterator iter = agentList.find(agentBank); + if (iter != agentList.end()) { + console.impl->eventAgentHeartbeat(iter->second, timestamp); + } + QPID_LOG(trace, "RCVD HeartbeatIndication seq=" << seq << " agentBank=" << agentBank); +} + +void BrokerProxyImpl::handleEventIndication(Buffer& /*inBuffer*/, uint32_t /*seq*/) +{ + // TODO +} + +void BrokerProxyImpl::handleSchemaResponse(Buffer& inBuffer, uint32_t seq) +{ + SchemaObjectClass* oClassPtr; + SchemaEventClass* eClassPtr; + uint8_t kind = inBuffer.getOctet(); + const SchemaClassKey* key; + if (kind == CLASS_OBJECT) { + oClassPtr = SchemaObjectClassImpl::factory(inBuffer); + console.impl->learnClass(oClassPtr); + key = oClassPtr->getClassKey(); + QPID_LOG(trace, "RCVD SchemaResponse seq=" << seq << " kind=object key=" << key->impl->str()); + + // + // If we have just learned about the org.apache.qpid.broker:agent class, send a get + // request for the current list of agents so we can have it on-hand before we declare + // this session "stable". + // + if (key->impl->getClassName() == AGENT_CLASS && key->impl->getPackageName() == BROKER_PACKAGE) { + Mutex::ScopedLock _lock(lock); + incOutstandingLH(); + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve()); + Protocol::encodeHeader(outBuffer, Protocol::OP_GET_QUERY, sequence); + FieldTable ft; + ft.setString("_class", AGENT_CLASS); + ft.setString("_package", BROKER_PACKAGE); + ft.encode(outBuffer); + sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_AGENT_KEY); + QPID_LOG(trace, "SENT GetQuery seq=" << sequence << " key=" << BROKER_AGENT_KEY); + } + } else if (kind == CLASS_EVENT) { + eClassPtr = SchemaEventClassImpl::factory(inBuffer); + console.impl->learnClass(eClassPtr); + key = eClassPtr->getClassKey(); + QPID_LOG(trace, "RCVD SchemaResponse seq=" << seq << " kind=event key=" << key->impl->str()); + } + else { + QPID_LOG(error, "BrokerProxyImpl::handleSchemaResponse received unknown class kind: " << (int) kind); + } +} + +ObjectPtr BrokerProxyImpl::handleObjectIndication(Buffer& inBuffer, uint32_t seq, bool prop, bool stat) +{ + auto_ptr classKey(SchemaClassKeyImpl::factory(inBuffer)); + QPID_LOG(trace, "RCVD ObjectIndication seq=" << seq << " key=" << classKey->impl->str()); + + SchemaObjectClass* schema = console.impl->getSchema(classKey.get()); + if (schema == 0) { + QPID_LOG(trace, "No Schema Found for ObjectIndication. seq=" << seq << " key=" << classKey->impl->str()); + return ObjectPtr(); + } + + ObjectPtr optr(ObjectImpl::factory(schema, this, inBuffer, prop, stat, true)); + if (prop && classKey->impl->getPackageName() == BROKER_PACKAGE && classKey->impl->getClassName() == AGENT_CLASS) { + // + // We've intercepted information about a remote agent... update the agent list accordingly + // + updateAgentList(optr); + } + return optr; +} + +void BrokerProxyImpl::updateAgentList(ObjectPtr obj) +{ + Value* value = obj->getValue("agentBank"); + Mutex::ScopedLock _lock(lock); + if (value != 0 && value->isUint()) { + uint32_t agentBank = value->asUint(); + if (obj->isDeleted()) { + map::iterator iter = agentList.find(agentBank); + if (iter != agentList.end()) { + AgentProxyPtr agent(iter->second); + console.impl->eventAgentDeleted(agent); + agentList.erase(agentBank); + QPID_LOG(trace, "Agent at bank " << agentBank << " removed from agent list"); + + // + // Release all sequence numbers for requests in-flight to this agent. + // Since the agent is no longer connected, these requests would not + // otherwise complete. + // + agent->impl->releaseInFlight(seqMgr); + } + } else { + Value* str = obj->getValue("label"); + string label; + if (str != 0 && str->isString()) + label = str->asString(); + map::const_iterator iter = agentList.find(agentBank); + if (iter == agentList.end()) { + AgentProxyPtr agent(AgentProxyImpl::factory(console, publicObject, agentBank, label)); + agentList[agentBank] = agent; + console.impl->eventAgentAdded(agent); + QPID_LOG(trace, "Agent '" << label << "' found at bank " << agentBank); + } + } + } +} + +void BrokerProxyImpl::incOutstandingLH() +{ + requestsOutstanding++; +} + +void BrokerProxyImpl::decOutstanding() +{ + Mutex::ScopedLock _lock(lock); + requestsOutstanding--; + if (requestsOutstanding == 0 && !topicBound) { + topicBound = true; + for (vector >::const_iterator iter = console.impl->bindingList.begin(); + iter != console.impl->bindingList.end(); iter++) { + string exchange(iter->first.empty() ? QMF_EXCHANGE : iter->first); + string key(iter->second); + eventQueue.push_back(eventBind(exchange, queueName, key)); + } + eventQueue.push_back(eventStable()); + } +} + +MethodResponseImpl::MethodResponseImpl(const MethodResponseImpl& from) : + status(from.status), schema(from.schema) +{ + if (from.exception.get()) + exception.reset(new Value(*(from.exception))); + if (from.arguments.get()) + arguments.reset(new Value(*(from.arguments))); +} + +MethodResponseImpl::MethodResponseImpl(Buffer& buf, const SchemaMethod* s) : schema(s) +{ + string text; + + status = buf.getLong(); + buf.getMediumString(text); + exception.reset(new Value(TYPE_LSTR)); + exception->setString(text.c_str()); + + if (status != 0) + return; + + arguments.reset(new Value(TYPE_MAP)); + int argCount(schema->getArgumentCount()); + for (int idx = 0; idx < argCount; idx++) { + const SchemaArgument* arg = schema->getArgument(idx); + if (arg->getDirection() == DIR_OUT || arg->getDirection() == DIR_IN_OUT) { + Value* value(ValueImpl::factory(arg->getType(), buf)); + arguments->insert(arg->getName(), value); + } + } +} + +MethodResponseImpl::MethodResponseImpl(uint32_t s, const string& text) : schema(0) +{ + status = s; + exception.reset(new Value(TYPE_LSTR)); + exception->setString(text.c_str()); +} + +MethodResponse* MethodResponseImpl::factory(Buffer& buf, const SchemaMethod* schema) +{ + MethodResponseImpl* impl(new MethodResponseImpl(buf, schema)); + return new MethodResponse(impl); +} + +MethodResponse* MethodResponseImpl::factory(uint32_t status, const std::string& text) +{ + MethodResponseImpl* impl(new MethodResponseImpl(status, text)); + return new MethodResponse(impl); +} + +bool StaticContext::handleMessage(uint8_t opcode, uint32_t sequence, const string& routingKey, Buffer& buffer) +{ + ObjectPtr object; + bool completeContext = false; + + if (opcode == Protocol::OP_BROKER_RESPONSE) { + broker.handleBrokerResponse(buffer, sequence); + completeContext = true; + } + else if (opcode == Protocol::OP_COMMAND_COMPLETE) { + broker.handleCommandComplete(buffer, sequence); + completeContext = true; + } + else if (opcode == Protocol::OP_SCHEMA_RESPONSE) { + broker.handleSchemaResponse(buffer, sequence); + completeContext = true; + } + else if (opcode == Protocol::OP_PACKAGE_INDICATION) + broker.handlePackageIndication(buffer, sequence); + else if (opcode == Protocol::OP_CLASS_INDICATION) + broker.handleClassIndication(buffer, sequence); + else if (opcode == Protocol::OP_HEARTBEAT_INDICATION) + broker.handleHeartbeatIndication(buffer, sequence, routingKey); + else if (opcode == Protocol::OP_EVENT_INDICATION) + broker.handleEventIndication(buffer, sequence); + else if (opcode == Protocol::OP_PROPERTY_INDICATION) { + object = broker.handleObjectIndication(buffer, sequence, true, false); + broker.console.impl->eventObjectUpdate(object, true, false); + } + else if (opcode == Protocol::OP_STATISTIC_INDICATION) { + object = broker.handleObjectIndication(buffer, sequence, false, true); + broker.console.impl->eventObjectUpdate(object, false, true); + } + else if (opcode == Protocol::OP_OBJECT_INDICATION) { + object = broker.handleObjectIndication(buffer, sequence, true, true); + broker.console.impl->eventObjectUpdate(object, true, true); + } + else { + QPID_LOG(trace, "StaticContext::handleMessage invalid opcode: " << opcode); + completeContext = true; + } + + return completeContext; +} + +void QueryContext::reserve() +{ + Mutex::ScopedLock _lock(lock); + requestsOutstanding++; +} + +void QueryContext::release() +{ + { + Mutex::ScopedLock _lock(lock); + if (--requestsOutstanding > 0) + return; + } + + Mutex::ScopedLock _block(broker.lock); + broker.eventQueue.push_back(broker.eventQueryComplete(userContext, queryResponse)); +} + +bool QueryContext::handleMessage(uint8_t opcode, uint32_t sequence, const string& /*routingKey*/, Buffer& buffer) +{ + bool completeContext = false; + ObjectPtr object; + + if (opcode == Protocol::OP_COMMAND_COMPLETE) { + broker.handleCommandComplete(buffer, sequence); + completeContext = true; + + // + // Visit each agent and remove the sequence from that agent's in-flight list. + // This could be made more efficient because only one agent will have this sequence + // in its list. + // + map copy; + { + Mutex::ScopedLock _block(broker.lock); + copy = broker.agentList; + } + for (map::iterator iter = copy.begin(); iter != copy.end(); iter++) + iter->second->impl->delSequence(sequence); + } + else if (opcode == Protocol::OP_OBJECT_INDICATION) { + object = broker.handleObjectIndication(buffer, sequence, true, true); + if (object.get() != 0) + queryResponse->impl->results.push_back(object); + } + else { + QPID_LOG(trace, "QueryContext::handleMessage invalid opcode: " << opcode); + completeContext = true; + } + + return completeContext; +} + +void MethodContext::release() +{ + Mutex::ScopedLock _block(broker.lock); + broker.eventQueue.push_back(broker.eventMethodResponse(userContext, methodResponse)); +} + +bool MethodContext::handleMessage(uint8_t opcode, uint32_t sequence, const string& /*routingKey*/, Buffer& buffer) +{ + if (opcode == Protocol::OP_METHOD_RESPONSE) + methodResponse = broker.handleMethodResponse(buffer, sequence, schema); + else + QPID_LOG(trace, "QueryContext::handleMessage invalid opcode: " << opcode); + + return true; +} + + +//================================================================== +// Wrappers +//================================================================== + +AgentProxy::AgentProxy(AgentProxyImpl* i) : impl(i) {} +AgentProxy::~AgentProxy() { delete impl; } +const char* AgentProxy::getLabel() const { return impl->getLabel().c_str(); } +uint32_t AgentProxy::getBrokerBank() const { return impl->getBrokerBank(); } +uint32_t AgentProxy::getAgentBank() const { return impl->getAgentBank(); } + +BrokerProxy::BrokerProxy(Console& console) : impl(new BrokerProxyImpl(*this, console)) {} +BrokerProxy::~BrokerProxy() { delete impl; } +void BrokerProxy::sessionOpened(SessionHandle& sh) { impl->sessionOpened(sh); } +void BrokerProxy::sessionClosed() { impl->sessionClosed(); } +void BrokerProxy::startProtocol() { impl->startProtocol(); } +void BrokerProxy::handleRcvMessage(Message& message) { impl->handleRcvMessage(message); } +bool BrokerProxy::getXmtMessage(Message& item) const { return impl->getXmtMessage(item); } +void BrokerProxy::popXmt() { impl->popXmt(); } +bool BrokerProxy::getEvent(BrokerEvent& event) const { return impl->getEvent(event); } +void BrokerProxy::popEvent() { impl->popEvent(); } +uint32_t BrokerProxy::agentCount() const { return impl->agentCount(); } +const AgentProxy* BrokerProxy::getAgent(uint32_t idx) const { return impl->getAgent(idx); } +void BrokerProxy::sendQuery(const Query& query, void* context, const AgentProxy* agent) { impl->sendQuery(query, context, agent); } + +MethodResponse::MethodResponse(const MethodResponse& from) : impl(new MethodResponseImpl(*(from.impl))) {} +MethodResponse::MethodResponse(MethodResponseImpl* i) : impl(i) {} +MethodResponse::~MethodResponse() {} +uint32_t MethodResponse::getStatus() const { return impl->getStatus(); } +const Value* MethodResponse::getException() const { return impl->getException(); } +const Value* MethodResponse::getArgs() const { return impl->getArgs(); } + +QueryResponse::QueryResponse(QueryResponseImpl* i) : impl(i) {} +QueryResponse::~QueryResponse() {} +uint32_t QueryResponse::getStatus() const { return impl->getStatus(); } +const Value* QueryResponse::getException() const { return impl->getException(); } +uint32_t QueryResponse::getObjectCount() const { return impl->getObjectCount(); } +const Object* QueryResponse::getObject(uint32_t idx) const { return impl->getObject(idx); } + diff --git a/qpid/cpp/src/qmf/engine/BrokerProxyImpl.h b/qpid/cpp/src/qmf/engine/BrokerProxyImpl.h new file mode 100644 index 0000000000..798a5fdc76 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/BrokerProxyImpl.h @@ -0,0 +1,239 @@ +#ifndef _QmfEngineBrokerProxyImpl_ +#define _QmfEngineBrokerProxyImpl_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/engine/Console.h" +#include "qmf/engine/ObjectImpl.h" +#include "qmf/engine/SchemaImpl.h" +#include "qmf/engine/ValueImpl.h" +#include "qmf/engine/QueryImpl.h" +#include "qmf/engine/SequenceManager.h" +#include "qmf/engine/MessageImpl.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/Uuid.h" +#include "qpid/sys/Mutex.h" +#include "boost/shared_ptr.hpp" +#include "boost/noncopyable.hpp" +#include +#include +#include +#include +#include +#include + +namespace qmf { +namespace engine { + + typedef boost::shared_ptr MethodResponsePtr; + struct MethodResponseImpl { + uint32_t status; + const SchemaMethod* schema; + std::auto_ptr exception; + std::auto_ptr arguments; + + MethodResponseImpl(const MethodResponseImpl& from); + MethodResponseImpl(qpid::framing::Buffer& buf, const SchemaMethod* schema); + MethodResponseImpl(uint32_t status, const std::string& text); + static MethodResponse* factory(qpid::framing::Buffer& buf, const SchemaMethod* schema); + static MethodResponse* factory(uint32_t status, const std::string& text); + ~MethodResponseImpl() {} + uint32_t getStatus() const { return status; } + const Value* getException() const { return exception.get(); } + const Value* getArgs() const { return arguments.get(); } + }; + + typedef boost::shared_ptr QueryResponsePtr; + struct QueryResponseImpl { + uint32_t status; + std::auto_ptr exception; + std::vector results; + + QueryResponseImpl() : status(0) {} + static QueryResponse* factory() { + QueryResponseImpl* impl(new QueryResponseImpl()); + return new QueryResponse(impl); + } + ~QueryResponseImpl() {} + uint32_t getStatus() const { return status; } + const Value* getException() const { return exception.get(); } + uint32_t getObjectCount() const { return results.size(); } + const Object* getObject(uint32_t idx) const; + }; + + struct BrokerEventImpl { + typedef boost::shared_ptr Ptr; + BrokerEvent::EventKind kind; + std::string name; + std::string exchange; + std::string bindingKey; + void* context; + QueryResponsePtr queryResponse; + MethodResponsePtr methodResponse; + + BrokerEventImpl(BrokerEvent::EventKind k) : kind(k), context(0) {} + ~BrokerEventImpl() {} + BrokerEvent copy(); + }; + + typedef boost::shared_ptr AgentProxyPtr; + struct AgentProxyImpl { + Console& console; + BrokerProxy& broker; + uint32_t agentBank; + std::string label; + std::set inFlightSequences; + + AgentProxyImpl(Console& c, BrokerProxy& b, uint32_t ab, const std::string& l) : console(c), broker(b), agentBank(ab), label(l) {} + static AgentProxy* factory(Console& c, BrokerProxy& b, uint32_t ab, const std::string& l) { + AgentProxyImpl* impl(new AgentProxyImpl(c, b, ab, l)); + return new AgentProxy(impl); + } + ~AgentProxyImpl() {} + const std::string& getLabel() const { return label; } + uint32_t getBrokerBank() const { return 1; } + uint32_t getAgentBank() const { return agentBank; } + void addSequence(uint32_t seq) { inFlightSequences.insert(seq); } + void delSequence(uint32_t seq) { inFlightSequences.erase(seq); } + void releaseInFlight(SequenceManager& seqMgr) { + for (std::set::iterator iter = inFlightSequences.begin(); iter != inFlightSequences.end(); iter++) + seqMgr.release(*iter); + inFlightSequences.clear(); + } + }; + + class BrokerProxyImpl : public boost::noncopyable { + public: + BrokerProxyImpl(BrokerProxy& pub, Console& _console); + ~BrokerProxyImpl() {} + + void sessionOpened(SessionHandle& sh); + void sessionClosed(); + void startProtocol(); + + void sendBufferLH(qpid::framing::Buffer& buf, const std::string& destination, const std::string& routingKey); + void handleRcvMessage(Message& message); + bool getXmtMessage(Message& item) const; + void popXmt(); + + bool getEvent(BrokerEvent& event) const; + void popEvent(); + + uint32_t agentCount() const; + const AgentProxy* getAgent(uint32_t idx) const; + void sendQuery(const Query& query, void* context, const AgentProxy* agent); + void sendGetRequestLH(SequenceContext::Ptr queryContext, const Query& query, const AgentProxy* agent); + std::string encodeMethodArguments(const SchemaMethod* schema, const Value* args, qpid::framing::Buffer& buffer); + void sendMethodRequest(ObjectId* oid, const SchemaObjectClass* cls, const std::string& method, const Value* args, void* context); + + void addBinding(const std::string& exchange, const std::string& key); + void staticRelease() { decOutstanding(); } + + private: + friend struct StaticContext; + friend struct QueryContext; + friend struct MethodContext; + BrokerProxy& publicObject; + mutable qpid::sys::Mutex lock; + Console& console; + std::string queueName; + qpid::framing::Uuid brokerId; + SequenceManager seqMgr; + uint32_t requestsOutstanding; + bool topicBound; + std::map agentList; + std::deque xmtQueue; + std::deque eventQueue; + +# define MA_BUFFER_SIZE 65536 + char outputBuffer[MA_BUFFER_SIZE]; + + BrokerEventImpl::Ptr eventDeclareQueue(const std::string& queueName); + BrokerEventImpl::Ptr eventBind(const std::string& exchange, const std::string& queue, const std::string& key); + BrokerEventImpl::Ptr eventSetupComplete(); + BrokerEventImpl::Ptr eventStable(); + BrokerEventImpl::Ptr eventQueryComplete(void* context, QueryResponsePtr response); + BrokerEventImpl::Ptr eventMethodResponse(void* context, MethodResponsePtr response); + + void handleBrokerResponse(qpid::framing::Buffer& inBuffer, uint32_t seq); + void handlePackageIndication(qpid::framing::Buffer& inBuffer, uint32_t seq); + void handleCommandComplete(qpid::framing::Buffer& inBuffer, uint32_t seq); + void handleClassIndication(qpid::framing::Buffer& inBuffer, uint32_t seq); + MethodResponsePtr handleMethodResponse(qpid::framing::Buffer& inBuffer, uint32_t seq, const SchemaMethod* schema); + void handleHeartbeatIndication(qpid::framing::Buffer& inBuffer, uint32_t seq, const std::string& routingKey); + void handleEventIndication(qpid::framing::Buffer& inBuffer, uint32_t seq); + void handleSchemaResponse(qpid::framing::Buffer& inBuffer, uint32_t seq); + ObjectPtr handleObjectIndication(qpid::framing::Buffer& inBuffer, uint32_t seq, bool prop, bool stat); + void updateAgentList(ObjectPtr obj); + void incOutstandingLH(); + void decOutstanding(); + }; + + // + // StaticContext is used to handle: + // + // 1) Responses to console-level requests (for schema info, etc.) + // 2) Unsolicited messages from agents (events, published updates, etc.) + // + struct StaticContext : public SequenceContext { + StaticContext(BrokerProxyImpl& b) : broker(b) {} + virtual ~StaticContext() {} + void reserve() {} + void release() { broker.staticRelease(); } + bool handleMessage(uint8_t opcode, uint32_t sequence, const std::string& routingKey, qpid::framing::Buffer& buffer); + BrokerProxyImpl& broker; + }; + + // + // QueryContext is used to track and handle responses associated with a single Get Query + // + struct QueryContext : public SequenceContext { + QueryContext(BrokerProxyImpl& b, void* u) : + broker(b), userContext(u), requestsOutstanding(0), queryResponse(QueryResponseImpl::factory()) {} + virtual ~QueryContext() {} + void reserve(); + void release(); + bool handleMessage(uint8_t opcode, uint32_t sequence, const std::string& routingKey, qpid::framing::Buffer& buffer); + + mutable qpid::sys::Mutex lock; + BrokerProxyImpl& broker; + void* userContext; + uint32_t requestsOutstanding; + QueryResponsePtr queryResponse; + }; + + struct MethodContext : public SequenceContext { + MethodContext(BrokerProxyImpl& b, void* u, const SchemaMethod* s) : broker(b), userContext(u), schema(s) {} + virtual ~MethodContext() {} + void reserve() {} + void release(); + bool handleMessage(uint8_t opcode, uint32_t sequence, const std::string& routingKey, qpid::framing::Buffer& buffer); + + BrokerProxyImpl& broker; + void* userContext; + const SchemaMethod* schema; + MethodResponsePtr methodResponse; + }; + +} +} + +#endif + diff --git a/qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.cpp b/qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.cpp new file mode 100644 index 0000000000..2cd6af10f8 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.cpp @@ -0,0 +1,273 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/engine/ConnectionSettingsImpl.h" +#include "qmf/engine/Typecode.h" + +using namespace std; +using namespace qmf::engine; +using namespace qpid; + +const string attrProtocol("protocol"); +const string attrHost("host"); +const string attrPort("port"); +const string attrVirtualhost("virtualhost"); +const string attrUsername("username"); +const string attrPassword("password"); +const string attrMechanism("mechanism"); +const string attrLocale("locale"); +const string attrHeartbeat("heartbeat"); +const string attrMaxChannels("maxChannels"); +const string attrMaxFrameSize("maxFrameSize"); +const string attrBounds("bounds"); +const string attrTcpNoDelay("tcpNoDelay"); +const string attrService("service"); +const string attrMinSsf("minSsf"); +const string attrMaxSsf("maxSsf"); +const string attrRetryDelayMin("retryDelayMin"); +const string attrRetryDelayMax("retryDelayMax"); +const string attrRetryDelayFactor("retryDelayFactor"); +const string attrSendUserId("sendUserId"); + +ConnectionSettingsImpl::ConnectionSettingsImpl() : + retryDelayMin(1), retryDelayMax(64), retryDelayFactor(2), sendUserId(true) +{ +} + +ConnectionSettingsImpl::ConnectionSettingsImpl(const string& /*url*/) : + retryDelayMin(1), retryDelayMax(64), retryDelayFactor(2), sendUserId(true) +{ + // TODO: Parse the URL +} + +bool ConnectionSettingsImpl::setAttr(const string& key, const Value& value) +{ + if (key == attrProtocol) clientSettings.protocol = value.asString(); + else if (key == attrHost) clientSettings.host = value.asString(); + else if (key == attrPort) clientSettings.port = value.asUint(); + else if (key == attrVirtualhost) clientSettings.virtualhost = value.asString(); + else if (key == attrUsername) clientSettings.username = value.asString(); + else if (key == attrPassword) clientSettings.password = value.asString(); + else if (key == attrMechanism) clientSettings.mechanism = value.asString(); + else if (key == attrLocale) clientSettings.locale = value.asString(); + else if (key == attrHeartbeat) clientSettings.heartbeat = value.asUint(); + else if (key == attrMaxChannels) clientSettings.maxChannels = value.asUint(); + else if (key == attrMaxFrameSize) clientSettings.maxFrameSize = value.asUint(); + else if (key == attrBounds) clientSettings.bounds = value.asUint(); + else if (key == attrTcpNoDelay) clientSettings.tcpNoDelay = value.asBool(); + else if (key == attrService) clientSettings.service = value.asString(); + else if (key == attrMinSsf) clientSettings.minSsf = value.asUint(); + else if (key == attrMaxSsf) clientSettings.maxSsf = value.asUint(); + + else if (key == attrRetryDelayMin) retryDelayMin = value.asUint(); + else if (key == attrRetryDelayMax) retryDelayMax = value.asUint(); + else if (key == attrRetryDelayFactor) retryDelayFactor = value.asUint(); + else if (key == attrSendUserId) sendUserId = value.asBool(); + else + return false; + return true; +} + +Value ConnectionSettingsImpl::getAttr(const string& key) const +{ + Value strval(TYPE_LSTR); + Value intval(TYPE_UINT32); + Value boolval(TYPE_BOOL); + + if (key == attrProtocol) { + strval.setString(clientSettings.protocol.c_str()); + return strval; + } + + if (key == attrHost) { + strval.setString(clientSettings.host.c_str()); + return strval; + } + + if (key == attrPort) { + intval.setUint(clientSettings.port); + return intval; + } + + if (key == attrVirtualhost) { + strval.setString(clientSettings.virtualhost.c_str()); + return strval; + } + + if (key == attrUsername) { + strval.setString(clientSettings.username.c_str()); + return strval; + } + + if (key == attrPassword) { + strval.setString(clientSettings.password.c_str()); + return strval; + } + + if (key == attrMechanism) { + strval.setString(clientSettings.mechanism.c_str()); + return strval; + } + + if (key == attrLocale) { + strval.setString(clientSettings.locale.c_str()); + return strval; + } + + if (key == attrHeartbeat) { + intval.setUint(clientSettings.heartbeat); + return intval; + } + + if (key == attrMaxChannels) { + intval.setUint(clientSettings.maxChannels); + return intval; + } + + if (key == attrMaxFrameSize) { + intval.setUint(clientSettings.maxFrameSize); + return intval; + } + + if (key == attrBounds) { + intval.setUint(clientSettings.bounds); + return intval; + } + + if (key == attrTcpNoDelay) { + boolval.setBool(clientSettings.tcpNoDelay); + return boolval; + } + + if (key == attrService) { + strval.setString(clientSettings.service.c_str()); + return strval; + } + + if (key == attrMinSsf) { + intval.setUint(clientSettings.minSsf); + return intval; + } + + if (key == attrMaxSsf) { + intval.setUint(clientSettings.maxSsf); + return intval; + } + + if (key == attrRetryDelayMin) { + intval.setUint(retryDelayMin); + return intval; + } + + if (key == attrRetryDelayMax) { + intval.setUint(retryDelayMax); + return intval; + } + + if (key == attrRetryDelayFactor) { + intval.setUint(retryDelayFactor); + return intval; + } + + return strval; +} + +const string& ConnectionSettingsImpl::getAttrString() const +{ + // TODO: build and return attribute string + return attrString; +} + +void ConnectionSettingsImpl::transportTcp(uint16_t port) +{ + clientSettings.protocol = "tcp"; + clientSettings.port = port; +} + +void ConnectionSettingsImpl::transportSsl(uint16_t port) +{ + clientSettings.protocol = "ssl"; + clientSettings.port = port; +} + +void ConnectionSettingsImpl::transportRdma(uint16_t port) +{ + clientSettings.protocol = "rdma"; + clientSettings.port = port; +} + +void ConnectionSettingsImpl::authAnonymous(const string& username) +{ + clientSettings.mechanism = "ANONYMOUS"; + clientSettings.username = username; +} + +void ConnectionSettingsImpl::authPlain(const string& username, const string& password) +{ + clientSettings.mechanism = "PLAIN"; + clientSettings.username = username; + clientSettings.password = password; +} + +void ConnectionSettingsImpl::authGssapi(const string& serviceName, uint32_t minSsf, uint32_t maxSsf) +{ + clientSettings.mechanism = "GSSAPI"; + clientSettings.service = serviceName; + clientSettings.minSsf = minSsf; + clientSettings.maxSsf = maxSsf; +} + +void ConnectionSettingsImpl::setRetry(int delayMin, int delayMax, int delayFactor) +{ + retryDelayMin = delayMin; + retryDelayMax = delayMax; + retryDelayFactor = delayFactor; +} + +const client::ConnectionSettings& ConnectionSettingsImpl::getClientSettings() const +{ + return clientSettings; +} + +void ConnectionSettingsImpl::getRetrySettings(int* min, int* max, int* factor) const +{ + *min = retryDelayMin; + *max = retryDelayMax; + *factor = retryDelayFactor; +} + +//================================================================== +// Wrappers +//================================================================== + +ConnectionSettings::ConnectionSettings(const ConnectionSettings& from) { impl = new ConnectionSettingsImpl(*from.impl); } +ConnectionSettings::ConnectionSettings() { impl = new ConnectionSettingsImpl(); } +ConnectionSettings::ConnectionSettings(const char* url) { impl = new ConnectionSettingsImpl(url); } +ConnectionSettings::~ConnectionSettings() { delete impl; } +bool ConnectionSettings::setAttr(const char* key, const Value& value) { return impl->setAttr(key, value); } +Value ConnectionSettings::getAttr(const char* key) const { return impl->getAttr(key); } +const char* ConnectionSettings::getAttrString() const { return impl->getAttrString().c_str(); } +void ConnectionSettings::transportTcp(uint16_t port) { impl->transportTcp(port); } +void ConnectionSettings::transportSsl(uint16_t port) { impl->transportSsl(port); } +void ConnectionSettings::transportRdma(uint16_t port) { impl->transportRdma(port); } +void ConnectionSettings::authAnonymous(const char* username) { impl->authAnonymous(username); } +void ConnectionSettings::authPlain(const char* username, const char* password) { impl->authPlain(username, password); } +void ConnectionSettings::authGssapi(const char* serviceName, uint32_t minSsf, uint32_t maxSsf) { impl->authGssapi(serviceName, minSsf, maxSsf); } +void ConnectionSettings::setRetry(int delayMin, int delayMax, int delayFactor) { impl->setRetry(delayMin, delayMax, delayFactor); } + diff --git a/qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.h b/qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.h new file mode 100644 index 0000000000..98bf87868b --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.h @@ -0,0 +1,63 @@ +#ifndef _QmfEngineConnectionSettingsImpl_ +#define _QmfEngineConnectionSettingsImpl_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/engine/ConnectionSettings.h" +#include "qmf/engine/Value.h" +#include "qpid/client/ConnectionSettings.h" +#include +#include + +namespace qmf { +namespace engine { + + class ConnectionSettingsImpl { + qpid::client::ConnectionSettings clientSettings; + mutable std::string attrString; + int retryDelayMin; + int retryDelayMax; + int retryDelayFactor; + bool sendUserId; + + public: + ConnectionSettingsImpl(); + ConnectionSettingsImpl(const std::string& url); + ~ConnectionSettingsImpl() {} + bool setAttr(const std::string& key, const Value& value); + Value getAttr(const std::string& key) const; + const std::string& getAttrString() const; + void transportTcp(uint16_t port); + void transportSsl(uint16_t port); + void transportRdma(uint16_t port); + void authAnonymous(const std::string& username); + void authPlain(const std::string& username, const std::string& password); + void authGssapi(const std::string& serviceName, uint32_t minSsf, uint32_t maxSsf); + void setRetry(int delayMin, int delayMax, int delayFactor); + + const qpid::client::ConnectionSettings& getClientSettings() const; + void getRetrySettings(int* delayMin, int* delayMax, int* delayFactor) const; + bool getSendUserId() const { return sendUserId; } + }; + +} +} + +#endif diff --git a/qpid/cpp/src/qmf/engine/ConsoleImpl.cpp b/qpid/cpp/src/qmf/engine/ConsoleImpl.cpp new file mode 100644 index 0000000000..c2d1f51f2b --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ConsoleImpl.cpp @@ -0,0 +1,419 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/engine/ConsoleImpl.h" +#include "qmf/engine/MessageImpl.h" +#include "qmf/engine/SchemaImpl.h" +#include "qmf/engine/Typecode.h" +#include "qmf/engine/ObjectImpl.h" +#include "qmf/engine/ObjectIdImpl.h" +#include "qmf/engine/QueryImpl.h" +#include "qmf/engine/ValueImpl.h" +#include "qmf/engine/Protocol.h" +#include "qmf/engine/SequenceManager.h" +#include "qmf/engine/BrokerProxyImpl.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace qmf::engine; +using namespace qpid::framing; +using namespace qpid::sys; + +namespace { + const char* QMF_EXCHANGE = "qpid.management"; +} + +#define STRING_REF(s) {if (!s.empty()) item.s = const_cast(s.c_str());} + +ConsoleEvent ConsoleEventImpl::copy() +{ + ConsoleEvent item; + + ::memset(&item, 0, sizeof(ConsoleEvent)); + item.kind = kind; + item.agent = agent.get(); + item.classKey = classKey; + item.object = object.get(); + item.context = context; + item.event = event; + item.timestamp = timestamp; + item.hasProps = hasProps; + item.hasStats = hasStats; + + STRING_REF(name); + + return item; +} + +ConsoleImpl::ConsoleImpl(const ConsoleSettings& s) : settings(s) +{ + bindingList.push_back(pair(string(), "schema.#")); + if (settings.rcvObjects && settings.rcvEvents && settings.rcvHeartbeats && !settings.userBindings) { + bindingList.push_back(pair(string(), "console.#")); + } else { + if (settings.rcvObjects && !settings.userBindings) + bindingList.push_back(pair(string(), "console.obj.#")); + else + bindingList.push_back(pair(string(), "console.obj.*.*.org.apache.qpid.broker.agent")); + if (settings.rcvEvents) + bindingList.push_back(pair(string(), "console.event.#")); + if (settings.rcvHeartbeats) + bindingList.push_back(pair(string(), "console.heartbeat.#")); + } +} + +ConsoleImpl::~ConsoleImpl() +{ + // This function intentionally left blank. +} + +bool ConsoleImpl::getEvent(ConsoleEvent& event) const +{ + Mutex::ScopedLock _lock(lock); + if (eventQueue.empty()) + return false; + event = eventQueue.front()->copy(); + return true; +} + +void ConsoleImpl::popEvent() +{ + Mutex::ScopedLock _lock(lock); + if (!eventQueue.empty()) + eventQueue.pop_front(); +} + +void ConsoleImpl::addConnection(BrokerProxy& broker, void* /*context*/) +{ + Mutex::ScopedLock _lock(lock); + brokerList.push_back(broker.impl); +} + +void ConsoleImpl::delConnection(BrokerProxy& broker) +{ + Mutex::ScopedLock _lock(lock); + for (vector::iterator iter = brokerList.begin(); + iter != brokerList.end(); iter++) + if (*iter == broker.impl) { + brokerList.erase(iter); + break; + } +} + +uint32_t ConsoleImpl::packageCount() const +{ + Mutex::ScopedLock _lock(lock); + return packages.size(); +} + +const string& ConsoleImpl::getPackageName(uint32_t idx) const +{ + const static string empty; + + Mutex::ScopedLock _lock(lock); + if (idx >= packages.size()) + return empty; + + PackageList::const_iterator iter = packages.begin(); + for (uint32_t i = 0; i < idx; i++) iter++; + return iter->first; +} + +uint32_t ConsoleImpl::classCount(const char* packageName) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(packageName); + if (pIter == packages.end()) + return 0; + + const ObjectClassList& oList = pIter->second.first; + const EventClassList& eList = pIter->second.second; + + return oList.size() + eList.size(); +} + +const SchemaClassKey* ConsoleImpl::getClass(const char* packageName, uint32_t idx) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(packageName); + if (pIter == packages.end()) + return 0; + + const ObjectClassList& oList = pIter->second.first; + const EventClassList& eList = pIter->second.second; + uint32_t count = 0; + + for (ObjectClassList::const_iterator oIter = oList.begin(); + oIter != oList.end(); oIter++) { + if (count == idx) + return oIter->second->getClassKey(); + count++; + } + + for (EventClassList::const_iterator eIter = eList.begin(); + eIter != eList.end(); eIter++) { + if (count == idx) + return eIter->second->getClassKey(); + count++; + } + + return 0; +} + +ClassKind ConsoleImpl::getClassKind(const SchemaClassKey* key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return CLASS_OBJECT; + + const EventClassList& eList = pIter->second.second; + if (eList.find(key) != eList.end()) + return CLASS_EVENT; + return CLASS_OBJECT; +} + +const SchemaObjectClass* ConsoleImpl::getObjectClass(const SchemaClassKey* key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return 0; + + const ObjectClassList& oList = pIter->second.first; + ObjectClassList::const_iterator iter = oList.find(key); + if (iter == oList.end()) + return 0; + return iter->second; +} + +const SchemaEventClass* ConsoleImpl::getEventClass(const SchemaClassKey* key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return 0; + + const EventClassList& eList = pIter->second.second; + EventClassList::const_iterator iter = eList.find(key); + if (iter == eList.end()) + return 0; + return iter->second; +} + +void ConsoleImpl::bindPackage(const char* packageName) +{ + stringstream key; + key << "console.obj.*.*." << packageName << ".#"; + Mutex::ScopedLock _lock(lock); + bindingList.push_back(pair(string(), key.str())); + for (vector::iterator iter = brokerList.begin(); + iter != brokerList.end(); iter++) + (*iter)->addBinding(QMF_EXCHANGE, key.str()); +} + +void ConsoleImpl::bindClass(const SchemaClassKey* classKey) +{ + stringstream key; + key << "console.obj.*.*." << classKey->getPackageName() << "." << classKey->getClassName() << ".#"; + Mutex::ScopedLock _lock(lock); + bindingList.push_back(pair(string(), key.str())); + for (vector::iterator iter = brokerList.begin(); + iter != brokerList.end(); iter++) + (*iter)->addBinding(QMF_EXCHANGE, key.str()); +} + +void ConsoleImpl::bindClass(const char* packageName, const char* className) +{ + stringstream key; + key << "console.obj.*.*." << packageName << "." << className << ".#"; + Mutex::ScopedLock _lock(lock); + bindingList.push_back(pair(string(), key.str())); + for (vector::iterator iter = brokerList.begin(); + iter != brokerList.end(); iter++) + (*iter)->addBinding(QMF_EXCHANGE, key.str()); +} + +/* +void ConsoleImpl::startSync(const Query& query, void* context, SyncQuery& sync) +{ +} + +void ConsoleImpl::touchSync(SyncQuery& sync) +{ +} + +void ConsoleImpl::endSync(SyncQuery& sync) +{ +} +*/ + +void ConsoleImpl::learnPackage(const string& packageName) +{ + Mutex::ScopedLock _lock(lock); + if (packages.find(packageName) == packages.end()) { + packages.insert(pair > + (packageName, pair(ObjectClassList(), EventClassList()))); + eventNewPackage(packageName); + } +} + +void ConsoleImpl::learnClass(SchemaObjectClass* cls) +{ + Mutex::ScopedLock _lock(lock); + const SchemaClassKey* key = cls->getClassKey(); + PackageList::iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return; + + ObjectClassList& list = pIter->second.first; + if (list.find(key) == list.end()) { + list[key] = cls; + eventNewClass(key); + } +} + +void ConsoleImpl::learnClass(SchemaEventClass* cls) +{ + Mutex::ScopedLock _lock(lock); + const SchemaClassKey* key = cls->getClassKey(); + PackageList::iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return; + + EventClassList& list = pIter->second.second; + if (list.find(key) == list.end()) { + list[key] = cls; + eventNewClass(key); + } +} + +bool ConsoleImpl::haveClass(const SchemaClassKey* key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return false; + + const ObjectClassList& oList = pIter->second.first; + const EventClassList& eList = pIter->second.second; + + return oList.find(key) != oList.end() || eList.find(key) != eList.end(); +} + +SchemaObjectClass* ConsoleImpl::getSchema(const SchemaClassKey* key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return 0; + + const ObjectClassList& oList = pIter->second.first; + ObjectClassList::const_iterator iter = oList.find(key); + if (iter == oList.end()) + return 0; + + return iter->second; +} + +void ConsoleImpl::eventAgentAdded(boost::shared_ptr agent) +{ + ConsoleEventImpl::Ptr event(new ConsoleEventImpl(ConsoleEvent::AGENT_ADDED)); + event->agent = agent; + Mutex::ScopedLock _lock(lock); + eventQueue.push_back(event); +} + +void ConsoleImpl::eventAgentDeleted(boost::shared_ptr agent) +{ + ConsoleEventImpl::Ptr event(new ConsoleEventImpl(ConsoleEvent::AGENT_DELETED)); + event->agent = agent; + Mutex::ScopedLock _lock(lock); + eventQueue.push_back(event); +} + +void ConsoleImpl::eventNewPackage(const string& packageName) +{ + ConsoleEventImpl::Ptr event(new ConsoleEventImpl(ConsoleEvent::NEW_PACKAGE)); + event->name = packageName; + Mutex::ScopedLock _lock(lock); + eventQueue.push_back(event); +} + +void ConsoleImpl::eventNewClass(const SchemaClassKey* key) +{ + ConsoleEventImpl::Ptr event(new ConsoleEventImpl(ConsoleEvent::NEW_CLASS)); + event->classKey = key; + Mutex::ScopedLock _lock(lock); + eventQueue.push_back(event); +} + +void ConsoleImpl::eventObjectUpdate(ObjectPtr object, bool prop, bool stat) +{ + ConsoleEventImpl::Ptr event(new ConsoleEventImpl(ConsoleEvent::OBJECT_UPDATE)); + event->object = object; + event->hasProps = prop; + event->hasStats = stat; + Mutex::ScopedLock _lock(lock); + eventQueue.push_back(event); +} + +void ConsoleImpl::eventAgentHeartbeat(boost::shared_ptr agent, uint64_t timestamp) +{ + ConsoleEventImpl::Ptr event(new ConsoleEventImpl(ConsoleEvent::AGENT_HEARTBEAT)); + event->agent = agent; + event->timestamp = timestamp; + Mutex::ScopedLock _lock(lock); + eventQueue.push_back(event); +} + +//================================================================== +// Wrappers +//================================================================== + +Console::Console(const ConsoleSettings& settings) : impl(new ConsoleImpl(settings)) {} +Console::~Console() { delete impl; } +bool Console::getEvent(ConsoleEvent& event) const { return impl->getEvent(event); } +void Console::popEvent() { impl->popEvent(); } +void Console::addConnection(BrokerProxy& broker, void* context) { impl->addConnection(broker, context); } +void Console::delConnection(BrokerProxy& broker) { impl->delConnection(broker); } +uint32_t Console::packageCount() const { return impl->packageCount(); } +const char* Console::getPackageName(uint32_t idx) const { return impl->getPackageName(idx).c_str(); } +uint32_t Console::classCount(const char* packageName) const { return impl->classCount(packageName); } +const SchemaClassKey* Console::getClass(const char* packageName, uint32_t idx) const { return impl->getClass(packageName, idx); } +ClassKind Console::getClassKind(const SchemaClassKey* key) const { return impl->getClassKind(key); } +const SchemaObjectClass* Console::getObjectClass(const SchemaClassKey* key) const { return impl->getObjectClass(key); } +const SchemaEventClass* Console::getEventClass(const SchemaClassKey* key) const { return impl->getEventClass(key); } +void Console::bindPackage(const char* packageName) { impl->bindPackage(packageName); } +void Console::bindClass(const SchemaClassKey* key) { impl->bindClass(key); } +void Console::bindClass(const char* packageName, const char* className) { impl->bindClass(packageName, className); } +//void Console::startSync(const Query& query, void* context, SyncQuery& sync) { impl->startSync(query, context, sync); } +//void Console::touchSync(SyncQuery& sync) { impl->touchSync(sync); } +//void Console::endSync(SyncQuery& sync) { impl->endSync(sync); } + + diff --git a/qpid/cpp/src/qmf/engine/ConsoleImpl.h b/qpid/cpp/src/qmf/engine/ConsoleImpl.h new file mode 100644 index 0000000000..8f99c5e6b9 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ConsoleImpl.h @@ -0,0 +1,145 @@ +#ifndef _QmfEngineConsoleEngineImpl_ +#define _QmfEngineConsoleEngineImpl_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/engine/Console.h" +#include "qmf/engine/MessageImpl.h" +#include "qmf/engine/SchemaImpl.h" +#include "qmf/engine/Typecode.h" +#include "qmf/engine/ObjectImpl.h" +#include "qmf/engine/ObjectIdImpl.h" +#include "qmf/engine/QueryImpl.h" +#include "qmf/engine/ValueImpl.h" +#include "qmf/engine/Protocol.h" +#include "qmf/engine/SequenceManager.h" +#include "qmf/engine/BrokerProxyImpl.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace qmf { +namespace engine { + + struct ConsoleEventImpl { + typedef boost::shared_ptr Ptr; + ConsoleEvent::EventKind kind; + boost::shared_ptr agent; + std::string name; + const SchemaClassKey* classKey; + boost::shared_ptr object; + void* context; + Event* event; + uint64_t timestamp; + bool hasProps; + bool hasStats; + + ConsoleEventImpl(ConsoleEvent::EventKind k) : + kind(k), classKey(0), context(0), event(0), timestamp(0) {} + ~ConsoleEventImpl() {} + ConsoleEvent copy(); + }; + + class ConsoleImpl : public boost::noncopyable { + public: + ConsoleImpl(const ConsoleSettings& settings = ConsoleSettings()); + ~ConsoleImpl(); + + bool getEvent(ConsoleEvent& event) const; + void popEvent(); + + void addConnection(BrokerProxy& broker, void* context); + void delConnection(BrokerProxy& broker); + + uint32_t packageCount() const; + const std::string& getPackageName(uint32_t idx) const; + + uint32_t classCount(const char* packageName) const; + const SchemaClassKey* getClass(const char* packageName, uint32_t idx) const; + + ClassKind getClassKind(const SchemaClassKey* key) const; + const SchemaObjectClass* getObjectClass(const SchemaClassKey* key) const; + const SchemaEventClass* getEventClass(const SchemaClassKey* key) const; + + void bindPackage(const char* packageName); + void bindClass(const SchemaClassKey* key); + void bindClass(const char* packageName, const char* className); + + /* + void startSync(const Query& query, void* context, SyncQuery& sync); + void touchSync(SyncQuery& sync); + void endSync(SyncQuery& sync); + */ + + private: + friend class BrokerProxyImpl; + friend struct StaticContext; + const ConsoleSettings& settings; + mutable qpid::sys::Mutex lock; + std::deque eventQueue; + std::vector brokerList; + std::vector > bindingList; // exchange/key (empty exchange => QMF_EXCHANGE) + + // Declare a compare class for the class maps that compares the dereferenced + // class key pointers. The default behavior would be to compare the pointer + // addresses themselves. + struct KeyCompare { + bool operator()(const SchemaClassKey* left, const SchemaClassKey* right) const { + return *left < *right; + } + }; + + typedef std::map ObjectClassList; + typedef std::map EventClassList; + typedef std::map > PackageList; + + PackageList packages; + + void learnPackage(const std::string& packageName); + void learnClass(SchemaObjectClass* cls); + void learnClass(SchemaEventClass* cls); + bool haveClass(const SchemaClassKey* key) const; + SchemaObjectClass* getSchema(const SchemaClassKey* key) const; + + void eventAgentAdded(boost::shared_ptr agent); + void eventAgentDeleted(boost::shared_ptr agent); + void eventNewPackage(const std::string& packageName); + void eventNewClass(const SchemaClassKey* key); + void eventObjectUpdate(ObjectPtr object, bool prop, bool stat); + void eventAgentHeartbeat(boost::shared_ptr agent, uint64_t timestamp); + }; +} +} + +#endif + diff --git a/qpid/cpp/src/qmf/engine/MessageImpl.cpp b/qpid/cpp/src/qmf/engine/MessageImpl.cpp new file mode 100644 index 0000000000..0047d3eb9d --- /dev/null +++ b/qpid/cpp/src/qmf/engine/MessageImpl.cpp @@ -0,0 +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. + */ + +#include "qmf/engine/MessageImpl.h" +#include + +using namespace std; +using namespace qmf::engine; + +#define STRING_REF(s) {if (!s.empty()) item.s = const_cast(s.c_str());} + +Message MessageImpl::copy() +{ + Message item; + + ::memset(&item, 0, sizeof(Message)); + item.body = const_cast(body.c_str()); + item.length = body.length(); + STRING_REF(destination); + STRING_REF(routingKey); + STRING_REF(replyExchange); + STRING_REF(replyKey); + STRING_REF(userId); + + return item; +} + diff --git a/qpid/cpp/src/qmf/engine/MessageImpl.h b/qpid/cpp/src/qmf/engine/MessageImpl.h new file mode 100644 index 0000000000..b91291d2e4 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/MessageImpl.h @@ -0,0 +1,44 @@ +#ifndef _QmfEngineMessageImpl_ +#define _QmfEngineMessageImpl_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/engine/Message.h" +#include +#include + +namespace qmf { +namespace engine { + + struct MessageImpl { + typedef boost::shared_ptr Ptr; + std::string body; + std::string destination; + std::string routingKey; + std::string replyExchange; + std::string replyKey; + std::string userId; + + Message copy(); + }; +} +} + +#endif diff --git a/qpid/cpp/src/qmf/engine/ObjectIdImpl.cpp b/qpid/cpp/src/qmf/engine/ObjectIdImpl.cpp new file mode 100644 index 0000000000..b08ae2756c --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ObjectIdImpl.cpp @@ -0,0 +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. + */ + +#include "qmf/engine/ObjectIdImpl.h" +#include + +using namespace std; +using namespace qmf::engine; +using qpid::framing::Buffer; + + +void AgentAttachment::setBanks(uint32_t broker, uint32_t agent) +{ + first = + ((uint64_t) (broker & 0x000fffff)) << 28 | + ((uint64_t) (agent & 0x0fffffff)); +} + +ObjectIdImpl::ObjectIdImpl(Buffer& buffer) : agent(0) +{ + decode(buffer); +} + +ObjectIdImpl::ObjectIdImpl(AgentAttachment* a, uint8_t flags, uint16_t seq, uint64_t object) : agent(a) +{ + first = + ((uint64_t) (flags & 0x0f)) << 60 | + ((uint64_t) (seq & 0x0fff)) << 48; + second = object; +} + +ObjectId* ObjectIdImpl::factory(Buffer& buffer) +{ + ObjectIdImpl* impl(new ObjectIdImpl(buffer)); + return new ObjectId(impl); +} + +ObjectId* ObjectIdImpl::factory(AgentAttachment* agent, uint8_t flags, uint16_t seq, uint64_t object) +{ + ObjectIdImpl* impl(new ObjectIdImpl(agent, flags, seq, object)); + return new ObjectId(impl); +} + +void ObjectIdImpl::decode(Buffer& buffer) +{ + first = buffer.getLongLong(); + second = buffer.getLongLong(); +} + +void ObjectIdImpl::encode(Buffer& buffer) const +{ + if (agent == 0) + buffer.putLongLong(first); + else + buffer.putLongLong(first | agent->first); + buffer.putLongLong(second); +} + +void ObjectIdImpl::fromString(const std::string& repr) +{ +#define FIELDS 5 +#if defined (_WIN32) && !defined (atoll) +# define atoll(X) _atoi64(X) +#endif + + std::string copy(repr.c_str()); + char* cText; + char* field[FIELDS]; + bool atFieldStart = true; + int idx = 0; + + cText = const_cast(copy.c_str()); + for (char* cursor = cText; *cursor; cursor++) { + if (atFieldStart) { + if (idx >= FIELDS) + return; // TODO error + field[idx++] = cursor; + atFieldStart = false; + } else { + if (*cursor == '-') { + *cursor = '\0'; + atFieldStart = true; + } + } + } + + if (idx != FIELDS) + return; // TODO error + + first = (atoll(field[0]) << 60) + + (atoll(field[1]) << 48) + + (atoll(field[2]) << 28) + + atoll(field[3]); + second = atoll(field[4]); + agent = 0; +} + +const string& ObjectIdImpl::asString() const +{ + stringstream val; + + val << (int) getFlags() << "-" << getSequence() << "-" << getBrokerBank() << "-" << + getAgentBank() << "-" << getObjectNum(); + repr = val.str(); + return repr; +} + +bool ObjectIdImpl::operator==(const ObjectIdImpl& other) const +{ + uint64_t otherFirst = agent == 0 ? other.first : other.first & 0xffff000000000000LL; + + return first == otherFirst && second == other.second; +} + +bool ObjectIdImpl::operator<(const ObjectIdImpl& other) const +{ + uint64_t otherFirst = agent == 0 ? other.first : other.first & 0xffff000000000000LL; + + return (first < otherFirst) || ((first == otherFirst) && (second < other.second)); +} + +bool ObjectIdImpl::operator>(const ObjectIdImpl& other) const +{ + uint64_t otherFirst = agent == 0 ? other.first : other.first & 0xffff000000000000LL; + + return (first > otherFirst) || ((first == otherFirst) && (second > other.second)); +} + + +//================================================================== +// Wrappers +//================================================================== + +ObjectId::ObjectId() : impl(new ObjectIdImpl()) {} +ObjectId::ObjectId(const ObjectId& from) : impl(new ObjectIdImpl(*(from.impl))) {} +ObjectId::ObjectId(ObjectIdImpl* i) : impl(i) {} +ObjectId::~ObjectId() { delete impl; } +uint64_t ObjectId::getObjectNum() const { return impl->getObjectNum(); } +uint32_t ObjectId::getObjectNumHi() const { return impl->getObjectNumHi(); } +uint32_t ObjectId::getObjectNumLo() const { return impl->getObjectNumLo(); } +bool ObjectId::isDurable() const { return impl->isDurable(); } +const char* ObjectId::str() const { return impl->asString().c_str(); } +uint8_t ObjectId::getFlags() const { return impl->getFlags(); } +uint16_t ObjectId::getSequence() const { return impl->getSequence(); } +uint32_t ObjectId::getBrokerBank() const { return impl->getBrokerBank(); } +uint32_t ObjectId::getAgentBank() const { return impl->getAgentBank(); } +bool ObjectId::operator==(const ObjectId& other) const { return *impl == *other.impl; } +bool ObjectId::operator<(const ObjectId& other) const { return *impl < *other.impl; } +bool ObjectId::operator>(const ObjectId& other) const { return *impl > *other.impl; } +bool ObjectId::operator<=(const ObjectId& other) const { return !(*impl > *other.impl); } +bool ObjectId::operator>=(const ObjectId& other) const { return !(*impl < *other.impl); } + diff --git a/qpid/cpp/src/qmf/engine/ObjectIdImpl.h b/qpid/cpp/src/qmf/engine/ObjectIdImpl.h new file mode 100644 index 0000000000..d9871ac217 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ObjectIdImpl.h @@ -0,0 +1,72 @@ +#ifndef _QmfEngineObjectIdImpl_ +#define _QmfEngineObjectIdImpl_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +namespace qmf { +namespace engine { + + struct AgentAttachment { + uint64_t first; + + AgentAttachment() : first(0) {} + void setBanks(uint32_t broker, uint32_t bank); + uint64_t getFirst() const { return first; } + }; + + struct ObjectIdImpl { + AgentAttachment* agent; + uint64_t first; + uint64_t second; + mutable std::string repr; + + ObjectIdImpl() : agent(0), first(0), second(0) {} + ObjectIdImpl(qpid::framing::Buffer& buffer); + ObjectIdImpl(AgentAttachment* agent, uint8_t flags, uint16_t seq, uint64_t object); + + static ObjectId* factory(qpid::framing::Buffer& buffer); + static ObjectId* factory(AgentAttachment* agent, uint8_t flags, uint16_t seq, uint64_t object); + + void decode(qpid::framing::Buffer& buffer); + void encode(qpid::framing::Buffer& buffer) const; + void fromString(const std::string& repr); + const std::string& asString() const; + uint8_t getFlags() const { return (first & 0xF000000000000000LL) >> 60; } + uint16_t getSequence() const { return (first & 0x0FFF000000000000LL) >> 48; } + uint32_t getBrokerBank() const { return (first & 0x0000FFFFF0000000LL) >> 28; } + uint32_t getAgentBank() const { return first & 0x000000000FFFFFFFLL; } + uint64_t getObjectNum() const { return second; } + uint32_t getObjectNumHi() const { return (uint32_t) (second >> 32); } + uint32_t getObjectNumLo() const { return (uint32_t) (second & 0x00000000FFFFFFFFLL); } + bool isDurable() const { return getSequence() == 0; } + void setValue(uint64_t f, uint64_t s) { first = f; second = s; } + + bool operator==(const ObjectIdImpl& other) const; + bool operator<(const ObjectIdImpl& other) const; + bool operator>(const ObjectIdImpl& other) const; + }; +} +} + +#endif + diff --git a/qpid/cpp/src/qmf/engine/ObjectImpl.cpp b/qpid/cpp/src/qmf/engine/ObjectImpl.cpp new file mode 100644 index 0000000000..cae0e0da68 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ObjectImpl.cpp @@ -0,0 +1,232 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/engine/ObjectImpl.h" +#include "qmf/engine/ValueImpl.h" +#include "qmf/engine/BrokerProxyImpl.h" +#include + +using namespace std; +using namespace qmf::engine; +using namespace qpid::sys; +using qpid::framing::Buffer; + +ObjectImpl::ObjectImpl(const SchemaObjectClass* type) : objectClass(type), broker(0), createTime(uint64_t(Duration(now()))), destroyTime(0), lastUpdatedTime(createTime) +{ + int propCount = objectClass->getPropertyCount(); + int statCount = objectClass->getStatisticCount(); + int idx; + + for (idx = 0; idx < propCount; idx++) { + const SchemaProperty* prop = objectClass->getProperty(idx); + properties[prop->getName()] = ValuePtr(new Value(prop->getType())); + } + + for (idx = 0; idx < statCount; idx++) { + const SchemaStatistic* stat = objectClass->getStatistic(idx); + statistics[stat->getName()] = ValuePtr(new Value(stat->getType())); + } +} + +ObjectImpl::ObjectImpl(const SchemaObjectClass* type, BrokerProxyImpl* b, Buffer& buffer, bool prop, bool stat, bool managed) : + objectClass(type), broker(b), createTime(0), destroyTime(0), lastUpdatedTime(0) +{ + int idx; + + if (managed) { + lastUpdatedTime = buffer.getLongLong(); + createTime = buffer.getLongLong(); + destroyTime = buffer.getLongLong(); + objectId.reset(ObjectIdImpl::factory(buffer)); + } + + if (prop) { + int propCount = objectClass->getPropertyCount(); + set excludes; + parsePresenceMasks(buffer, excludes); + for (idx = 0; idx < propCount; idx++) { + const SchemaProperty* prop = objectClass->getProperty(idx); + if (excludes.count(prop->getName()) != 0) { + properties[prop->getName()] = ValuePtr(new Value(prop->getType())); + } else { + Value* pval = ValueImpl::factory(prop->getType(), buffer); + properties[prop->getName()] = ValuePtr(pval); + } + } + } + + if (stat) { + int statCount = objectClass->getStatisticCount(); + for (idx = 0; idx < statCount; idx++) { + const SchemaStatistic* stat = objectClass->getStatistic(idx); + Value* sval = ValueImpl::factory(stat->getType(), buffer); + statistics[stat->getName()] = ValuePtr(sval); + } + } +} + +Object* ObjectImpl::factory(const SchemaObjectClass* type, BrokerProxyImpl* b, Buffer& buffer, bool prop, bool stat, bool managed) +{ + ObjectImpl* impl(new ObjectImpl(type, b, buffer, prop, stat, managed)); + return new Object(impl); +} + +ObjectImpl::~ObjectImpl() +{ +} + +void ObjectImpl::destroy() +{ + destroyTime = uint64_t(Duration(now())); + // TODO - flag deletion +} + +Value* ObjectImpl::getValue(const string& key) const +{ + map::const_iterator iter; + + iter = properties.find(key); + if (iter != properties.end()) + return iter->second.get(); + + iter = statistics.find(key); + if (iter != statistics.end()) + return iter->second.get(); + + return 0; +} + +void ObjectImpl::invokeMethod(const string& methodName, const Value* inArgs, void* context) const +{ + if (broker != 0 && objectId.get() != 0) + broker->sendMethodRequest(objectId.get(), objectClass, methodName, inArgs, context); +} + +void ObjectImpl::merge(const Object& from) +{ + for (map::const_iterator piter = from.impl->properties.begin(); + piter != from.impl->properties.end(); piter++) + properties[piter->first] = piter->second; + for (map::const_iterator siter = from.impl->statistics.begin(); + siter != from.impl->statistics.end(); siter++) + statistics[siter->first] = siter->second; +} + +void ObjectImpl::parsePresenceMasks(Buffer& buffer, set& excludeList) +{ + int propCount = objectClass->getPropertyCount(); + excludeList.clear(); + uint8_t bit = 0; + uint8_t mask = 0; + + for (int idx = 0; idx < propCount; idx++) { + const SchemaProperty* prop = objectClass->getProperty(idx); + if (prop->isOptional()) { + if (bit == 0) { + mask = buffer.getOctet(); + bit = 1; + } + if ((mask & bit) == 0) + excludeList.insert(string(prop->getName())); + if (bit == 0x80) + bit = 0; + else + bit = bit << 1; + } + } +} + +void ObjectImpl::encodeSchemaKey(qpid::framing::Buffer& buffer) const +{ + buffer.putShortString(objectClass->getClassKey()->getPackageName()); + buffer.putShortString(objectClass->getClassKey()->getClassName()); + buffer.putBin128(const_cast(objectClass->getClassKey()->getHash())); +} + +void ObjectImpl::encodeManagedObjectData(qpid::framing::Buffer& buffer) const +{ + buffer.putLongLong(lastUpdatedTime); + buffer.putLongLong(createTime); + buffer.putLongLong(destroyTime); + objectId->impl->encode(buffer); +} + +void ObjectImpl::encodeProperties(qpid::framing::Buffer& buffer) const +{ + int propCount = objectClass->getPropertyCount(); + uint8_t bit = 0; + uint8_t mask = 0; + ValuePtr value; + + for (int idx = 0; idx < propCount; idx++) { + const SchemaProperty* prop = objectClass->getProperty(idx); + if (prop->isOptional()) { + value = properties[prop->getName()]; + if (bit == 0) + bit = 1; + if (!value->isNull()) + mask |= bit; + if (bit == 0x80) { + buffer.putOctet(mask); + bit = 0; + mask = 0; + } else + bit = bit << 1; + } + } + if (bit != 0) { + buffer.putOctet(mask); + } + + for (int idx = 0; idx < propCount; idx++) { + const SchemaProperty* prop = objectClass->getProperty(idx); + value = properties[prop->getName()]; + if (!prop->isOptional() || !value->isNull()) { + value->impl->encode(buffer); + } + } +} + +void ObjectImpl::encodeStatistics(qpid::framing::Buffer& buffer) const +{ + int statCount = objectClass->getStatisticCount(); + for (int idx = 0; idx < statCount; idx++) { + const SchemaStatistic* stat = objectClass->getStatistic(idx); + ValuePtr value = statistics[stat->getName()]; + value->impl->encode(buffer); + } +} + +//================================================================== +// Wrappers +//================================================================== + +Object::Object(const SchemaObjectClass* type) : impl(new ObjectImpl(type)) {} +Object::Object(ObjectImpl* i) : impl(i) {} +Object::Object(const Object& from) : impl(new ObjectImpl(*(from.impl))) {} +Object::~Object() { delete impl; } +void Object::destroy() { impl->destroy(); } +const ObjectId* Object::getObjectId() const { return impl->getObjectId(); } +void Object::setObjectId(ObjectId* oid) { impl->setObjectId(oid); } +const SchemaObjectClass* Object::getClass() const { return impl->getClass(); } +Value* Object::getValue(const char* key) const { return impl->getValue(key); } +void Object::invokeMethod(const char* m, const Value* a, void* c) const { impl->invokeMethod(m, a, c); } +bool Object::isDeleted() const { return impl->isDeleted(); } +void Object::merge(const Object& from) { impl->merge(from); } + diff --git a/qpid/cpp/src/qmf/engine/ObjectImpl.h b/qpid/cpp/src/qmf/engine/ObjectImpl.h new file mode 100644 index 0000000000..ddd20bfea2 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ObjectImpl.h @@ -0,0 +1,76 @@ +#ifndef _QmfEngineObjectImpl_ +#define _QmfEngineObjectImpl_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace qmf { +namespace engine { + + class BrokerProxyImpl; + + typedef boost::shared_ptr ObjectPtr; + + struct ObjectImpl { + typedef boost::shared_ptr ValuePtr; + const SchemaObjectClass* objectClass; + BrokerProxyImpl* broker; + boost::shared_ptr objectId; + uint64_t createTime; + uint64_t destroyTime; + uint64_t lastUpdatedTime; + mutable std::map properties; + mutable std::map statistics; + + ObjectImpl(const SchemaObjectClass* type); + ObjectImpl(const SchemaObjectClass* type, BrokerProxyImpl* b, qpid::framing::Buffer& buffer, + bool prop, bool stat, bool managed); + static Object* factory(const SchemaObjectClass* type, BrokerProxyImpl* b, qpid::framing::Buffer& buffer, + bool prop, bool stat, bool managed); + ~ObjectImpl(); + + void destroy(); + const ObjectId* getObjectId() const { return objectId.get(); } + void setObjectId(ObjectId* oid) { objectId.reset(oid); } + const SchemaObjectClass* getClass() const { return objectClass; } + Value* getValue(const std::string& key) const; + void invokeMethod(const std::string& methodName, const Value* inArgs, void* context) const; + bool isDeleted() const { return destroyTime != 0; } + void merge(const Object& from); + + void parsePresenceMasks(qpid::framing::Buffer& buffer, std::set& excludeList); + void encodeSchemaKey(qpid::framing::Buffer& buffer) const; + void encodeManagedObjectData(qpid::framing::Buffer& buffer) const; + void encodeProperties(qpid::framing::Buffer& buffer) const; + void encodeStatistics(qpid::framing::Buffer& buffer) const; + }; +} +} + +#endif + diff --git a/qpid/cpp/src/qmf/engine/Protocol.cpp b/qpid/cpp/src/qmf/engine/Protocol.cpp new file mode 100644 index 0000000000..6061b70a8d --- /dev/null +++ b/qpid/cpp/src/qmf/engine/Protocol.cpp @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/engine/Protocol.h" +#include "qpid/framing/Buffer.h" + +using namespace std; +using namespace qmf::engine; +using namespace qpid::framing; + + +bool Protocol::checkHeader(Buffer& buf, uint8_t *opcode, uint32_t *seq) +{ + if (buf.available() < 8) + return false; + + uint8_t h1 = buf.getOctet(); + uint8_t h2 = buf.getOctet(); + uint8_t h3 = buf.getOctet(); + + *opcode = buf.getOctet(); + *seq = buf.getLong(); + + return h1 == 'A' && h2 == 'M' && h3 == '3'; +} + +void Protocol::encodeHeader(qpid::framing::Buffer& buf, uint8_t opcode, uint32_t seq) +{ + buf.putOctet('A'); + buf.putOctet('M'); + buf.putOctet('3'); + buf.putOctet(opcode); + buf.putLong (seq); +} + + diff --git a/qpid/cpp/src/qmf/engine/Protocol.h b/qpid/cpp/src/qmf/engine/Protocol.h new file mode 100644 index 0000000000..1cdfa60c84 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/Protocol.h @@ -0,0 +1,69 @@ +#ifndef _QmfEngineProtocol_ +#define _QmfEngineProtocol_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +namespace qpid { + namespace framing { + class Buffer; + } +} + +namespace qmf { +namespace engine { + + class Protocol { + public: + static bool checkHeader(qpid::framing::Buffer& buf, uint8_t *opcode, uint32_t *seq); + static void encodeHeader(qpid::framing::Buffer& buf, uint8_t opcode, uint32_t seq = 0); + + const static uint8_t OP_ATTACH_REQUEST = 'A'; + const static uint8_t OP_ATTACH_RESPONSE = 'a'; + + const static uint8_t OP_BROKER_REQUEST = 'B'; + const static uint8_t OP_BROKER_RESPONSE = 'b'; + + const static uint8_t OP_CONSOLE_ADDED_INDICATION = 'x'; + const static uint8_t OP_COMMAND_COMPLETE = 'z'; + const static uint8_t OP_HEARTBEAT_INDICATION = 'h'; + + const static uint8_t OP_PACKAGE_REQUEST = 'P'; + const static uint8_t OP_PACKAGE_INDICATION = 'p'; + const static uint8_t OP_CLASS_QUERY = 'Q'; + const static uint8_t OP_CLASS_INDICATION = 'q'; + const static uint8_t OP_SCHEMA_REQUEST = 'S'; + const static uint8_t OP_SCHEMA_RESPONSE = 's'; + + const static uint8_t OP_METHOD_REQUEST = 'M'; + const static uint8_t OP_METHOD_RESPONSE = 'm'; + const static uint8_t OP_GET_QUERY = 'G'; + const static uint8_t OP_OBJECT_INDICATION = 'g'; + const static uint8_t OP_PROPERTY_INDICATION = 'c'; + const static uint8_t OP_STATISTIC_INDICATION = 'i'; + const static uint8_t OP_EVENT_INDICATION = 'e'; + }; + +} +} + +#endif + diff --git a/qpid/cpp/src/qmf/engine/QueryImpl.cpp b/qpid/cpp/src/qmf/engine/QueryImpl.cpp new file mode 100644 index 0000000000..6f2beeee87 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/QueryImpl.cpp @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/engine/QueryImpl.h" +#include "qmf/engine/ObjectIdImpl.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/FieldTable.h" + +using namespace std; +using namespace qmf::engine; +using namespace qpid::framing; + +bool QueryElementImpl::evaluate(const Object* /*object*/) const +{ + // TODO: Implement this + return false; +} + +bool QueryExpressionImpl::evaluate(const Object* /*object*/) const +{ + // TODO: Implement this + return false; +} + +QueryImpl::QueryImpl(Buffer& buffer) +{ + FieldTable ft; + ft.decode(buffer); + // TODO +} + +Query* QueryImpl::factory(Buffer& buffer) +{ + QueryImpl* impl(new QueryImpl(buffer)); + return new Query(impl); +} + +void QueryImpl::encode(Buffer& buffer) const +{ + FieldTable ft; + + if (oid.get() != 0) { + ft.setString("_objectid", oid->impl->asString()); + } else { + if (!packageName.empty()) + ft.setString("_package", packageName); + ft.setString("_class", className); + } + + ft.encode(buffer); +} + + +//================================================================== +// Wrappers +//================================================================== + +QueryElement::QueryElement(const char* attrName, const Value* value, ValueOper oper) : impl(new QueryElementImpl(attrName, value, oper)) {} +QueryElement::QueryElement(QueryElementImpl* i) : impl(i) {} +QueryElement::~QueryElement() { delete impl; } +bool QueryElement::evaluate(const Object* object) const { return impl->evaluate(object); } + +QueryExpression::QueryExpression(ExprOper oper, const QueryOperand* operand1, const QueryOperand* operand2) : impl(new QueryExpressionImpl(oper, operand1, operand2)) {} +QueryExpression::QueryExpression(QueryExpressionImpl* i) : impl(i) {} +QueryExpression::~QueryExpression() { delete impl; } +bool QueryExpression::evaluate(const Object* object) const { return impl->evaluate(object); } + +Query::Query(const char* className, const char* packageName) : impl(new QueryImpl(className, packageName)) {} +Query::Query(const SchemaClassKey* key) : impl(new QueryImpl(key)) {} +Query::Query(const ObjectId* oid) : impl(new QueryImpl(oid)) {} +Query::Query(QueryImpl* i) : impl(i) {} +Query::Query(const Query& from) : impl(new QueryImpl(*(from.impl))) {} +Query::~Query() { delete impl; } +void Query::setSelect(const QueryOperand* criterion) { impl->setSelect(criterion); } +void Query::setLimit(uint32_t maxResults) { impl->setLimit(maxResults); } +void Query::setOrderBy(const char* attrName, bool decreasing) { impl->setOrderBy(attrName, decreasing); } +const char* Query::getPackage() const { return impl->getPackage().c_str(); } +const char* Query::getClass() const { return impl->getClass().c_str(); } +const ObjectId* Query::getObjectId() const { return impl->getObjectId(); } +bool Query::haveSelect() const { return impl->haveSelect(); } +bool Query::haveLimit() const { return impl->haveLimit(); } +bool Query::haveOrderBy() const { return impl->haveOrderBy(); } +const QueryOperand* Query::getSelect() const { return impl->getSelect(); } +uint32_t Query::getLimit() const { return impl->getLimit(); } +const char* Query::getOrderBy() const { return impl->getOrderBy().c_str(); } +bool Query::getDecreasing() const { return impl->getDecreasing(); } + diff --git a/qpid/cpp/src/qmf/engine/QueryImpl.h b/qpid/cpp/src/qmf/engine/QueryImpl.h new file mode 100644 index 0000000000..2c64c6739c --- /dev/null +++ b/qpid/cpp/src/qmf/engine/QueryImpl.h @@ -0,0 +1,100 @@ +#ifndef _QmfEngineQueryImpl_ +#define _QmfEngineQueryImpl_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/engine/Query.h" +#include "qmf/engine/Schema.h" +#include +#include + +namespace qpid { + namespace framing { + class Buffer; + } +} + +namespace qmf { +namespace engine { + + struct QueryElementImpl { + QueryElementImpl(const std::string& a, const Value* v, ValueOper o) : attrName(a), value(v), oper(o) {} + ~QueryElementImpl() {} + bool evaluate(const Object* object) const; + + std::string attrName; + const Value* value; + ValueOper oper; + }; + + struct QueryExpressionImpl { + QueryExpressionImpl(ExprOper o, const QueryOperand* operand1, const QueryOperand* operand2) : oper(o), left(operand1), right(operand2) {} + ~QueryExpressionImpl() {} + bool evaluate(const Object* object) const; + + ExprOper oper; + const QueryOperand* left; + const QueryOperand* right; + }; + + struct QueryImpl { + // Constructors mapped to public + QueryImpl(const std::string& c, const std::string& p) : packageName(p), className(c), select(0), resultLimit(0) {} + QueryImpl(const SchemaClassKey* key) : packageName(key->getPackageName()), className(key->getClassName()), select(0), resultLimit(0) {} + QueryImpl(const ObjectId* oid) : oid(new ObjectId(*oid)), select(0), resultLimit(0) {} + + // Factory constructors + QueryImpl(qpid::framing::Buffer& buffer); + + ~QueryImpl() {}; + static Query* factory(qpid::framing::Buffer& buffer); + + void setSelect(const QueryOperand* criterion) { select = criterion; } + void setLimit(uint32_t maxResults) { resultLimit = maxResults; } + void setOrderBy(const std::string& attrName, bool decreasing) { + orderBy = attrName; orderDecreasing = decreasing; + } + + const std::string& getPackage() const { return packageName; } + const std::string& getClass() const { return className; } + const ObjectId* getObjectId() const { return oid.get(); } + + bool haveSelect() const { return select != 0; } + bool haveLimit() const { return resultLimit > 0; } + bool haveOrderBy() const { return !orderBy.empty(); } + const QueryOperand* getSelect() const { return select; } + uint32_t getLimit() const { return resultLimit; } + const std::string& getOrderBy() const { return orderBy; } + bool getDecreasing() const { return orderDecreasing; } + + void encode(qpid::framing::Buffer& buffer) const; + + std::string packageName; + std::string className; + boost::shared_ptr oid; + const QueryOperand* select; + uint32_t resultLimit; + std::string orderBy; + bool orderDecreasing; + }; +} +} + +#endif diff --git a/qpid/cpp/src/qmf/engine/ResilientConnection.cpp b/qpid/cpp/src/qmf/engine/ResilientConnection.cpp new file mode 100644 index 0000000000..9502130288 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ResilientConnection.cpp @@ -0,0 +1,489 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/engine/ResilientConnection.h" +#include "qmf/engine/MessageImpl.h" +#include "qmf/engine/ConnectionSettingsImpl.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace qmf::engine; +using namespace qpid; +using qpid::sys::Mutex; + +namespace qmf { +namespace engine { + struct ResilientConnectionEventImpl { + ResilientConnectionEvent::EventKind kind; + void* sessionContext; + string errorText; + MessageImpl message; + + ResilientConnectionEventImpl(ResilientConnectionEvent::EventKind k, + const MessageImpl& m = MessageImpl()) : + kind(k), sessionContext(0), message(m) {} + ResilientConnectionEvent copy(); + }; + + struct RCSession : public client::MessageListener, public qpid::sys::Runnable, public qpid::RefCounted { + typedef boost::intrusive_ptr Ptr; + ResilientConnectionImpl& connImpl; + string name; + client::Connection& connection; + client::Session session; + client::SubscriptionManager* subscriptions; + string userId; + void* userContext; + vector dests; + qpid::sys::Thread thread; + + RCSession(ResilientConnectionImpl& ci, const string& n, client::Connection& c, void* uc); + ~RCSession(); + void received(client::Message& msg); + void run(); + void stop(); + }; + + class ResilientConnectionImpl : public qpid::sys::Runnable, public boost::noncopyable { + public: + ResilientConnectionImpl(const ConnectionSettings& settings); + ~ResilientConnectionImpl(); + + bool isConnected() const; + bool getEvent(ResilientConnectionEvent& event); + void popEvent(); + bool createSession(const char* name, void* sessionContext, SessionHandle& handle); + void destroySession(SessionHandle handle); + void sendMessage(SessionHandle handle, qmf::engine::Message& message); + void declareQueue(SessionHandle handle, char* queue); + void deleteQueue(SessionHandle handle, char* queue); + void bind(SessionHandle handle, char* exchange, char* queue, char* key); + void unbind(SessionHandle handle, char* exchange, char* queue, char* key); + void setNotifyFd(int fd); + + void run(); + void failure(); + void sessionClosed(RCSession* sess); + + void EnqueueEvent(ResilientConnectionEvent::EventKind kind, + void* sessionContext = 0, + const MessageImpl& message = MessageImpl(), + const string& errorText = ""); + + private: + int notifyFd; + bool connected; + bool shutdown; + string lastError; + const ConnectionSettings settings; + client::Connection connection; + mutable qpid::sys::Mutex lock; + int delayMin; + int delayMax; + int delayFactor; + qpid::sys::Condition cond; + qpid::sys::Thread connThread; + deque eventQueue; + set sessions; + }; +} +} + +ResilientConnectionEvent ResilientConnectionEventImpl::copy() +{ + ResilientConnectionEvent item; + + ::memset(&item, 0, sizeof(ResilientConnectionEvent)); + item.kind = kind; + item.sessionContext = sessionContext; + item.message = message.copy(); + item.errorText = const_cast(errorText.c_str()); + + return item; +} + +RCSession::RCSession(ResilientConnectionImpl& ci, const string& n, client::Connection& c, void* uc) : + connImpl(ci), name(n), connection(c), session(connection.newSession(name)), + subscriptions(new client::SubscriptionManager(session)), userContext(uc), thread(*this) +{ + const qpid::client::ConnectionSettings& operSettings = connection.getNegotiatedSettings(); + userId = operSettings.username; +} + +RCSession::~RCSession() +{ + subscriptions->stop(); + thread.join(); + session.close(); + delete subscriptions; +} + +void RCSession::run() +{ + try { + subscriptions->run(); + } catch (exception& /*e*/) { + connImpl.sessionClosed(this); + } +} + +void RCSession::stop() +{ + subscriptions->stop(); +} + +void RCSession::received(client::Message& msg) +{ + MessageImpl qmsg; + qmsg.body = msg.getData(); + + qpid::framing::DeliveryProperties dp = msg.getDeliveryProperties(); + if (dp.hasRoutingKey()) { + qmsg.routingKey = dp.getRoutingKey(); + } + + qpid::framing::MessageProperties mp = msg.getMessageProperties(); + if (mp.hasReplyTo()) { + const qpid::framing::ReplyTo& rt = mp.getReplyTo(); + qmsg.replyExchange = rt.getExchange(); + qmsg.replyKey = rt.getRoutingKey(); + } + + if (mp.hasUserId()) { + qmsg.userId = mp.getUserId(); + } + + connImpl.EnqueueEvent(ResilientConnectionEvent::RECV, userContext, qmsg); +} + +ResilientConnectionImpl::ResilientConnectionImpl(const ConnectionSettings& _settings) : + notifyFd(-1), connected(false), shutdown(false), settings(_settings), delayMin(1), connThread(*this) +{ + connection.registerFailureCallback(boost::bind(&ResilientConnectionImpl::failure, this)); + settings.impl->getRetrySettings(&delayMin, &delayMax, &delayFactor); +} + +ResilientConnectionImpl::~ResilientConnectionImpl() +{ + shutdown = true; + connected = false; + cond.notify(); + connThread.join(); + connection.close(); +} + +bool ResilientConnectionImpl::isConnected() const +{ + Mutex::ScopedLock _lock(lock); + return connected; +} + +bool ResilientConnectionImpl::getEvent(ResilientConnectionEvent& event) +{ + Mutex::ScopedLock _lock(lock); + if (eventQueue.empty()) + return false; + event = eventQueue.front().copy(); + return true; +} + +void ResilientConnectionImpl::popEvent() +{ + Mutex::ScopedLock _lock(lock); + if (!eventQueue.empty()) + eventQueue.pop_front(); +} + +bool ResilientConnectionImpl::createSession(const char* name, void* sessionContext, + SessionHandle& handle) +{ + Mutex::ScopedLock _lock(lock); + if (!connected) + return false; + + RCSession::Ptr sess = RCSession::Ptr(new RCSession(*this, name, connection, sessionContext)); + + handle.impl = (void*) sess.get(); + sessions.insert(sess); + + return true; +} + +void ResilientConnectionImpl::destroySession(SessionHandle handle) +{ + Mutex::ScopedLock _lock(lock); + RCSession::Ptr sess = RCSession::Ptr((RCSession*) handle.impl); + set::iterator iter = sessions.find(sess); + if (iter != sessions.end()) { + for (vector::iterator dIter = sess->dests.begin(); dIter != sess->dests.end(); dIter++) + sess->subscriptions->cancel(dIter->c_str()); + sess->subscriptions->stop(); + sess->subscriptions->wait(); + + sessions.erase(iter); + return; + } +} + +void ResilientConnectionImpl::sendMessage(SessionHandle handle, qmf::engine::Message& message) +{ + Mutex::ScopedLock _lock(lock); + RCSession::Ptr sess = RCSession::Ptr((RCSession*) handle.impl); + set::iterator iter = sessions.find(sess); + qpid::client::Message msg; + string data(message.body, message.length); + msg.getDeliveryProperties().setRoutingKey(message.routingKey); + msg.getMessageProperties().setReplyTo(qpid::framing::ReplyTo(message.replyExchange, message.replyKey)); + if (settings.impl->getSendUserId()) + msg.getMessageProperties().setUserId(sess->userId); + msg.setData(data); + + try { + sess->session.messageTransfer(client::arg::content=msg, client::arg::destination=message.destination); + } catch(exception& e) { + QPID_LOG(error, "Session Exception during message-transfer: " << e.what()); + sessions.erase(iter); + EnqueueEvent(ResilientConnectionEvent::SESSION_CLOSED, (*iter)->userContext); + } +} + +void ResilientConnectionImpl::declareQueue(SessionHandle handle, char* queue) +{ + Mutex::ScopedLock _lock(lock); + RCSession* sess = (RCSession*) handle.impl; + + sess->session.queueDeclare(client::arg::queue=queue, client::arg::autoDelete=true, client::arg::exclusive=true); + sess->subscriptions->setAcceptMode(client::ACCEPT_MODE_NONE); + sess->subscriptions->setAcquireMode(client::ACQUIRE_MODE_PRE_ACQUIRED); + sess->subscriptions->subscribe(*sess, queue, queue); + sess->subscriptions->setFlowControl(queue, client::FlowControl::unlimited()); + sess->dests.push_back(string(queue)); +} + +void ResilientConnectionImpl::deleteQueue(SessionHandle handle, char* queue) +{ + Mutex::ScopedLock _lock(lock); + RCSession* sess = (RCSession*) handle.impl; + + sess->session.queueDelete(client::arg::queue=queue); + for (vector::iterator iter = sess->dests.begin(); + iter != sess->dests.end(); iter++) + if (*iter == queue) { + sess->subscriptions->cancel(queue); + sess->dests.erase(iter); + break; + } +} + +void ResilientConnectionImpl::bind(SessionHandle handle, + char* exchange, char* queue, char* key) +{ + Mutex::ScopedLock _lock(lock); + RCSession* sess = (RCSession*) handle.impl; + + sess->session.exchangeBind(client::arg::exchange=exchange, client::arg::queue=queue, client::arg::bindingKey=key); +} + +void ResilientConnectionImpl::unbind(SessionHandle handle, + char* exchange, char* queue, char* key) +{ + Mutex::ScopedLock _lock(lock); + RCSession* sess = (RCSession*) handle.impl; + + sess->session.exchangeUnbind(client::arg::exchange=exchange, client::arg::queue=queue, client::arg::bindingKey=key); +} + +void ResilientConnectionImpl::setNotifyFd(int fd) +{ + notifyFd = fd; +} + +void ResilientConnectionImpl::run() +{ + int delay(delayMin); + + while (true) { + try { + QPID_LOG(trace, "Trying to open connection..."); + connection.open(settings.impl->getClientSettings()); + { + Mutex::ScopedLock _lock(lock); + connected = true; + EnqueueEvent(ResilientConnectionEvent::CONNECTED); + + while (connected) + cond.wait(lock); + delay = delayMin; + + while (!sessions.empty()) { + set::iterator iter = sessions.begin(); + RCSession::Ptr sess = *iter; + sessions.erase(iter); + EnqueueEvent(ResilientConnectionEvent::SESSION_CLOSED, sess->userContext); + Mutex::ScopedUnlock _u(lock); + sess->stop(); + + // Nullify the intrusive pointer within the scoped unlock, otherwise, + // the reference is held until overwritted above (under lock) which causes + // the session destructor to be called with the lock held. + sess = 0; + } + + EnqueueEvent(ResilientConnectionEvent::DISCONNECTED); + + if (shutdown) + return; + } + connection.close(); + } catch (exception &e) { + QPID_LOG(debug, "connection.open exception: " << e.what()); + Mutex::ScopedLock _lock(lock); + lastError = e.what(); + if (delay < delayMax) + delay *= delayFactor; + } + + ::qpid::sys::sleep(delay); + } +} + +void ResilientConnectionImpl::failure() +{ + Mutex::ScopedLock _lock(lock); + + connected = false; + lastError = "Closed by Peer"; + cond.notify(); +} + +void ResilientConnectionImpl::sessionClosed(RCSession*) +{ + Mutex::ScopedLock _lock(lock); + connected = false; + lastError = "Closed due to Session failure"; + cond.notify(); +} + +void ResilientConnectionImpl::EnqueueEvent(ResilientConnectionEvent::EventKind kind, + void* sessionContext, + const MessageImpl& message, + const string& errorText) +{ + Mutex::ScopedLock _lock(lock); + ResilientConnectionEventImpl event(kind, message); + + event.sessionContext = sessionContext; + event.errorText = errorText; + + eventQueue.push_back(event); + if (notifyFd != -1) + { + int unused_ret; //Suppress warnings about ignoring return value. + unused_ret = ::write(notifyFd, ".", 1); + } +} + + +//================================================================== +// Wrappers +//================================================================== + +ResilientConnection::ResilientConnection(const ConnectionSettings& settings) +{ + impl = new ResilientConnectionImpl(settings); +} + +ResilientConnection::~ResilientConnection() +{ + delete impl; +} + +bool ResilientConnection::isConnected() const +{ + return impl->isConnected(); +} + +bool ResilientConnection::getEvent(ResilientConnectionEvent& event) +{ + return impl->getEvent(event); +} + +void ResilientConnection::popEvent() +{ + impl->popEvent(); +} + +bool ResilientConnection::createSession(const char* name, void* sessionContext, SessionHandle& handle) +{ + return impl->createSession(name, sessionContext, handle); +} + +void ResilientConnection::destroySession(SessionHandle handle) +{ + impl->destroySession(handle); +} + +void ResilientConnection::sendMessage(SessionHandle handle, qmf::engine::Message& message) +{ + impl->sendMessage(handle, message); +} + +void ResilientConnection::declareQueue(SessionHandle handle, char* queue) +{ + impl->declareQueue(handle, queue); +} + +void ResilientConnection::deleteQueue(SessionHandle handle, char* queue) +{ + impl->deleteQueue(handle, queue); +} + +void ResilientConnection::bind(SessionHandle handle, char* exchange, char* queue, char* key) +{ + impl->bind(handle, exchange, queue, key); +} + +void ResilientConnection::unbind(SessionHandle handle, char* exchange, char* queue, char* key) +{ + impl->unbind(handle, exchange, queue, key); +} + +void ResilientConnection::setNotifyFd(int fd) +{ + impl->setNotifyFd(fd); +} + diff --git a/qpid/cpp/src/qmf/engine/SchemaImpl.cpp b/qpid/cpp/src/qmf/engine/SchemaImpl.cpp new file mode 100644 index 0000000000..e366a66826 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/SchemaImpl.cpp @@ -0,0 +1,611 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/engine/SchemaImpl.h" +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace qmf::engine; +using qpid::framing::Buffer; +using qpid::framing::FieldTable; +using qpid::framing::Uuid; + +SchemaHash::SchemaHash() +{ + for (int idx = 0; idx < 16; idx++) + hash[idx] = 0x5A; +} + +void SchemaHash::encode(Buffer& buffer) const +{ + buffer.putBin128(hash); +} + +void SchemaHash::decode(Buffer& buffer) +{ + buffer.getBin128(hash); +} + +void SchemaHash::update(uint8_t data) +{ + update((char*) &data, 1); +} + +void SchemaHash::update(const char* data, uint32_t len) +{ + uint64_t* first = (uint64_t*) hash; + uint64_t* second = (uint64_t*) hash + 1; + + for (uint32_t idx = 0; idx < len; idx++) { + *first = *first ^ (uint64_t) data[idx]; + *second = *second << 1; + *second |= ((*first & 0x8000000000000000LL) >> 63); + *first = *first << 1; + *first = *first ^ *second; + } +} + +bool SchemaHash::operator==(const SchemaHash& other) const +{ + return ::memcmp(&hash, &other.hash, 16) == 0; +} + +bool SchemaHash::operator<(const SchemaHash& other) const +{ + return ::memcmp(&hash, &other.hash, 16) < 0; +} + +bool SchemaHash::operator>(const SchemaHash& other) const +{ + return ::memcmp(&hash, &other.hash, 16) > 0; +} + +SchemaArgumentImpl::SchemaArgumentImpl(Buffer& buffer) +{ + FieldTable map; + map.decode(buffer); + + name = map.getAsString("name"); + typecode = (Typecode) map.getAsInt("type"); + unit = map.getAsString("unit"); + description = map.getAsString("desc"); + + dir = DIR_IN; + string dstr(map.getAsString("dir")); + if (dstr == "O") + dir = DIR_OUT; + else if (dstr == "IO") + dir = DIR_IN_OUT; +} + +SchemaArgument* SchemaArgumentImpl::factory(Buffer& buffer) +{ + SchemaArgumentImpl* impl(new SchemaArgumentImpl(buffer)); + return new SchemaArgument(impl); +} + +void SchemaArgumentImpl::encode(Buffer& buffer) const +{ + FieldTable map; + + map.setString("name", name); + map.setInt("type", (int) typecode); + if (dir == DIR_IN) + map.setString("dir", "I"); + else if (dir == DIR_OUT) + map.setString("dir", "O"); + else + map.setString("dir", "IO"); + if (!unit.empty()) + map.setString("unit", unit); + if (!description.empty()) + map.setString("desc", description); + + map.encode(buffer); +} + +void SchemaArgumentImpl::updateHash(SchemaHash& hash) const +{ + hash.update(name); + hash.update(typecode); + hash.update(dir); + hash.update(unit); + hash.update(description); +} + +SchemaMethodImpl::SchemaMethodImpl(Buffer& buffer) +{ + FieldTable map; + int argCount; + + map.decode(buffer); + name = map.getAsString("name"); + argCount = map.getAsInt("argCount"); + description = map.getAsString("desc"); + + for (int idx = 0; idx < argCount; idx++) { + SchemaArgument* arg = SchemaArgumentImpl::factory(buffer); + addArgument(arg); + } +} + +SchemaMethod* SchemaMethodImpl::factory(Buffer& buffer) +{ + SchemaMethodImpl* impl(new SchemaMethodImpl(buffer)); + return new SchemaMethod(impl); +} + +void SchemaMethodImpl::encode(Buffer& buffer) const +{ + FieldTable map; + + map.setString("name", name); + map.setInt("argCount", arguments.size()); + if (!description.empty()) + map.setString("desc", description); + map.encode(buffer); + + for (vector::const_iterator iter = arguments.begin(); + iter != arguments.end(); iter++) + (*iter)->impl->encode(buffer); +} + +void SchemaMethodImpl::addArgument(const SchemaArgument* argument) +{ + arguments.push_back(argument); +} + +const SchemaArgument* SchemaMethodImpl::getArgument(int idx) const +{ + int count = 0; + for (vector::const_iterator iter = arguments.begin(); + iter != arguments.end(); iter++, count++) + if (idx == count) + return (*iter); + return 0; +} + +void SchemaMethodImpl::updateHash(SchemaHash& hash) const +{ + hash.update(name); + hash.update(description); + for (vector::const_iterator iter = arguments.begin(); + iter != arguments.end(); iter++) + (*iter)->impl->updateHash(hash); +} + +SchemaPropertyImpl::SchemaPropertyImpl(Buffer& buffer) +{ + FieldTable map; + map.decode(buffer); + + name = map.getAsString("name"); + typecode = (Typecode) map.getAsInt("type"); + access = (Access) map.getAsInt("access"); + index = map.getAsInt("index") != 0; + optional = map.getAsInt("optional") != 0; + unit = map.getAsString("unit"); + description = map.getAsString("desc"); +} + +SchemaProperty* SchemaPropertyImpl::factory(Buffer& buffer) +{ + SchemaPropertyImpl* impl(new SchemaPropertyImpl(buffer)); + return new SchemaProperty(impl); +} + +void SchemaPropertyImpl::encode(Buffer& buffer) const +{ + FieldTable map; + + map.setString("name", name); + map.setInt("type", (int) typecode); + map.setInt("access", (int) access); + map.setInt("index", index ? 1 : 0); + map.setInt("optional", optional ? 1 : 0); + if (!unit.empty()) + map.setString("unit", unit); + if (!description.empty()) + map.setString("desc", description); + + map.encode(buffer); +} + +void SchemaPropertyImpl::updateHash(SchemaHash& hash) const +{ + hash.update(name); + hash.update(typecode); + hash.update(access); + hash.update(index); + hash.update(optional); + hash.update(unit); + hash.update(description); +} + +SchemaStatisticImpl::SchemaStatisticImpl(Buffer& buffer) +{ + FieldTable map; + map.decode(buffer); + + name = map.getAsString("name"); + typecode = (Typecode) map.getAsInt("type"); + unit = map.getAsString("unit"); + description = map.getAsString("desc"); +} + +SchemaStatistic* SchemaStatisticImpl::factory(Buffer& buffer) +{ + SchemaStatisticImpl* impl(new SchemaStatisticImpl(buffer)); + return new SchemaStatistic(impl); +} + +void SchemaStatisticImpl::encode(Buffer& buffer) const +{ + FieldTable map; + + map.setString("name", name); + map.setInt("type", (int) typecode); + if (!unit.empty()) + map.setString("unit", unit); + if (!description.empty()) + map.setString("desc", description); + + map.encode(buffer); +} + +void SchemaStatisticImpl::updateHash(SchemaHash& hash) const +{ + hash.update(name); + hash.update(typecode); + hash.update(unit); + hash.update(description); +} + +SchemaClassKeyImpl::SchemaClassKeyImpl(const string& p, const string& n, const SchemaHash& h) : package(p), name(n), hash(h) {} + +SchemaClassKeyImpl::SchemaClassKeyImpl(Buffer& buffer) : package(packageContainer), name(nameContainer), hash(hashContainer) +{ + buffer.getShortString(packageContainer); + buffer.getShortString(nameContainer); + hashContainer.decode(buffer); +} + +SchemaClassKey* SchemaClassKeyImpl::factory(const string& package, const string& name, const SchemaHash& hash) +{ + SchemaClassKeyImpl* impl(new SchemaClassKeyImpl(package, name, hash)); + return new SchemaClassKey(impl); +} + +SchemaClassKey* SchemaClassKeyImpl::factory(Buffer& buffer) +{ + SchemaClassKeyImpl* impl(new SchemaClassKeyImpl(buffer)); + return new SchemaClassKey(impl); +} + +void SchemaClassKeyImpl::encode(Buffer& buffer) const +{ + buffer.putShortString(package); + buffer.putShortString(name); + hash.encode(buffer); +} + +bool SchemaClassKeyImpl::operator==(const SchemaClassKeyImpl& other) const +{ + return package == other.package && + name == other.name && + hash == other.hash; +} + +bool SchemaClassKeyImpl::operator<(const SchemaClassKeyImpl& other) const +{ + if (package < other.package) return true; + if (package > other.package) return false; + if (name < other.name) return true; + if (name > other.name) return false; + return hash < other.hash; +} + +const string& SchemaClassKeyImpl::str() const +{ + Uuid printableHash(hash.get()); + stringstream str; + str << package << ":" << name << "(" << printableHash << ")"; + repr = str.str(); + return repr; +} + +SchemaObjectClassImpl::SchemaObjectClassImpl(Buffer& buffer) : hasHash(true), classKey(SchemaClassKeyImpl::factory(package, name, hash)) +{ + buffer.getShortString(package); + buffer.getShortString(name); + hash.decode(buffer); + + /*uint8_t hasParentClass =*/ buffer.getOctet(); // TODO: Parse parent-class indicator + uint16_t propCount = buffer.getShort(); + uint16_t statCount = buffer.getShort(); + uint16_t methodCount = buffer.getShort(); + + for (uint16_t idx = 0; idx < propCount; idx++) { + const SchemaProperty* property = SchemaPropertyImpl::factory(buffer); + addProperty(property); + } + + for (uint16_t idx = 0; idx < statCount; idx++) { + const SchemaStatistic* statistic = SchemaStatisticImpl::factory(buffer); + addStatistic(statistic); + } + + for (uint16_t idx = 0; idx < methodCount; idx++) { + SchemaMethod* method = SchemaMethodImpl::factory(buffer); + addMethod(method); + } +} + +SchemaObjectClass* SchemaObjectClassImpl::factory(Buffer& buffer) +{ + SchemaObjectClassImpl* impl(new SchemaObjectClassImpl(buffer)); + return new SchemaObjectClass(impl); +} + +void SchemaObjectClassImpl::encode(Buffer& buffer) const +{ + buffer.putOctet((uint8_t) CLASS_OBJECT); + buffer.putShortString(package); + buffer.putShortString(name); + hash.encode(buffer); + buffer.putOctet(0); // No parent class + buffer.putShort((uint16_t) properties.size()); + buffer.putShort((uint16_t) statistics.size()); + buffer.putShort((uint16_t) methods.size()); + + for (vector::const_iterator iter = properties.begin(); + iter != properties.end(); iter++) + (*iter)->impl->encode(buffer); + for (vector::const_iterator iter = statistics.begin(); + iter != statistics.end(); iter++) + (*iter)->impl->encode(buffer); + for (vector::const_iterator iter = methods.begin(); + iter != methods.end(); iter++) + (*iter)->impl->encode(buffer); +} + +const SchemaClassKey* SchemaObjectClassImpl::getClassKey() const +{ + if (!hasHash) { + hasHash = true; + hash.update(package); + hash.update(name); + for (vector::const_iterator iter = properties.begin(); + iter != properties.end(); iter++) + (*iter)->impl->updateHash(hash); + for (vector::const_iterator iter = statistics.begin(); + iter != statistics.end(); iter++) + (*iter)->impl->updateHash(hash); + for (vector::const_iterator iter = methods.begin(); + iter != methods.end(); iter++) + (*iter)->impl->updateHash(hash); + } + + return classKey.get(); +} + +void SchemaObjectClassImpl::addProperty(const SchemaProperty* property) +{ + properties.push_back(property); +} + +void SchemaObjectClassImpl::addStatistic(const SchemaStatistic* statistic) +{ + statistics.push_back(statistic); +} + +void SchemaObjectClassImpl::addMethod(const SchemaMethod* method) +{ + methods.push_back(method); +} + +const SchemaProperty* SchemaObjectClassImpl::getProperty(int idx) const +{ + int count = 0; + for (vector::const_iterator iter = properties.begin(); + iter != properties.end(); iter++, count++) + if (idx == count) + return *iter; + return 0; +} + +const SchemaStatistic* SchemaObjectClassImpl::getStatistic(int idx) const +{ + int count = 0; + for (vector::const_iterator iter = statistics.begin(); + iter != statistics.end(); iter++, count++) + if (idx == count) + return *iter; + return 0; +} + +const SchemaMethod* SchemaObjectClassImpl::getMethod(int idx) const +{ + int count = 0; + for (vector::const_iterator iter = methods.begin(); + iter != methods.end(); iter++, count++) + if (idx == count) + return *iter; + return 0; +} + +SchemaEventClassImpl::SchemaEventClassImpl(Buffer& buffer) : hasHash(true), classKey(SchemaClassKeyImpl::factory(package, name, hash)) +{ + buffer.getShortString(package); + buffer.getShortString(name); + hash.decode(buffer); + buffer.putOctet(0); // No parent class + + uint16_t argCount = buffer.getShort(); + + for (uint16_t idx = 0; idx < argCount; idx++) { + SchemaArgument* argument = SchemaArgumentImpl::factory(buffer); + addArgument(argument); + } +} + +SchemaEventClass* SchemaEventClassImpl::factory(Buffer& buffer) +{ + SchemaEventClassImpl* impl(new SchemaEventClassImpl(buffer)); + return new SchemaEventClass(impl); +} + +void SchemaEventClassImpl::encode(Buffer& buffer) const +{ + buffer.putOctet((uint8_t) CLASS_EVENT); + buffer.putShortString(package); + buffer.putShortString(name); + hash.encode(buffer); + buffer.putShort((uint16_t) arguments.size()); + + for (vector::const_iterator iter = arguments.begin(); + iter != arguments.end(); iter++) + (*iter)->impl->encode(buffer); +} + +const SchemaClassKey* SchemaEventClassImpl::getClassKey() const +{ + if (!hasHash) { + hasHash = true; + hash.update(package); + hash.update(name); + for (vector::const_iterator iter = arguments.begin(); + iter != arguments.end(); iter++) + (*iter)->impl->updateHash(hash); + } + return classKey.get(); +} + +void SchemaEventClassImpl::addArgument(const SchemaArgument* argument) +{ + arguments.push_back(argument); +} + +const SchemaArgument* SchemaEventClassImpl::getArgument(int idx) const +{ + int count = 0; + for (vector::const_iterator iter = arguments.begin(); + iter != arguments.end(); iter++, count++) + if (idx == count) + return (*iter); + return 0; +} + + +//================================================================== +// Wrappers +//================================================================== + +SchemaArgument::SchemaArgument(const char* name, Typecode typecode) { impl = new SchemaArgumentImpl(name, typecode); } +SchemaArgument::SchemaArgument(SchemaArgumentImpl* i) : impl(i) {} +SchemaArgument::SchemaArgument(const SchemaArgument& from) : impl(new SchemaArgumentImpl(*(from.impl))) {} +SchemaArgument::~SchemaArgument() { delete impl; } +void SchemaArgument::setDirection(Direction dir) { impl->setDirection(dir); } +void SchemaArgument::setUnit(const char* val) { impl->setUnit(val); } +void SchemaArgument::setDesc(const char* desc) { impl->setDesc(desc); } +const char* SchemaArgument::getName() const { return impl->getName().c_str(); } +Typecode SchemaArgument::getType() const { return impl->getType(); } +Direction SchemaArgument::getDirection() const { return impl->getDirection(); } +const char* SchemaArgument::getUnit() const { return impl->getUnit().c_str(); } +const char* SchemaArgument::getDesc() const { return impl->getDesc().c_str(); } + +SchemaMethod::SchemaMethod(const char* name) : impl(new SchemaMethodImpl(name)) {} +SchemaMethod::SchemaMethod(SchemaMethodImpl* i) : impl(i) {} +SchemaMethod::SchemaMethod(const SchemaMethod& from) : impl(new SchemaMethodImpl(*(from.impl))) {} +SchemaMethod::~SchemaMethod() { delete impl; } +void SchemaMethod::addArgument(const SchemaArgument* argument) { impl->addArgument(argument); } +void SchemaMethod::setDesc(const char* desc) { impl->setDesc(desc); } +const char* SchemaMethod::getName() const { return impl->getName().c_str(); } +const char* SchemaMethod::getDesc() const { return impl->getDesc().c_str(); } +int SchemaMethod::getArgumentCount() const { return impl->getArgumentCount(); } +const SchemaArgument* SchemaMethod::getArgument(int idx) const { return impl->getArgument(idx); } + +SchemaProperty::SchemaProperty(const char* name, Typecode typecode) : impl(new SchemaPropertyImpl(name, typecode)) {} +SchemaProperty::SchemaProperty(SchemaPropertyImpl* i) : impl(i) {} +SchemaProperty::SchemaProperty(const SchemaProperty& from) : impl(new SchemaPropertyImpl(*(from.impl))) {} +SchemaProperty::~SchemaProperty() { delete impl; } +void SchemaProperty::setAccess(Access access) { impl->setAccess(access); } +void SchemaProperty::setIndex(bool val) { impl->setIndex(val); } +void SchemaProperty::setOptional(bool val) { impl->setOptional(val); } +void SchemaProperty::setUnit(const char* val) { impl->setUnit(val); } +void SchemaProperty::setDesc(const char* desc) { impl->setDesc(desc); } +const char* SchemaProperty::getName() const { return impl->getName().c_str(); } +Typecode SchemaProperty::getType() const { return impl->getType(); } +Access SchemaProperty::getAccess() const { return impl->getAccess(); } +bool SchemaProperty::isIndex() const { return impl->isIndex(); } +bool SchemaProperty::isOptional() const { return impl->isOptional(); } +const char* SchemaProperty::getUnit() const { return impl->getUnit().c_str(); } +const char* SchemaProperty::getDesc() const { return impl->getDesc().c_str(); } + +SchemaStatistic::SchemaStatistic(const char* name, Typecode typecode) : impl(new SchemaStatisticImpl(name, typecode)) {} +SchemaStatistic::SchemaStatistic(SchemaStatisticImpl* i) : impl(i) {} +SchemaStatistic::SchemaStatistic(const SchemaStatistic& from) : impl(new SchemaStatisticImpl(*(from.impl))) {} +SchemaStatistic::~SchemaStatistic() { delete impl; } +void SchemaStatistic::setUnit(const char* val) { impl->setUnit(val); } +void SchemaStatistic::setDesc(const char* desc) { impl->setDesc(desc); } +const char* SchemaStatistic::getName() const { return impl->getName().c_str(); } +Typecode SchemaStatistic::getType() const { return impl->getType(); } +const char* SchemaStatistic::getUnit() const { return impl->getUnit().c_str(); } +const char* SchemaStatistic::getDesc() const { return impl->getDesc().c_str(); } + +SchemaClassKey::SchemaClassKey(SchemaClassKeyImpl* i) : impl(i) {} +SchemaClassKey::SchemaClassKey(const SchemaClassKey& from) : impl(new SchemaClassKeyImpl(*(from.impl))) {} +SchemaClassKey::~SchemaClassKey() { delete impl; } +const char* SchemaClassKey::getPackageName() const { return impl->getPackageName().c_str(); } +const char* SchemaClassKey::getClassName() const { return impl->getClassName().c_str(); } +const uint8_t* SchemaClassKey::getHash() const { return impl->getHash(); } +const char* SchemaClassKey::asString() const { return impl->str().c_str(); } +bool SchemaClassKey::operator==(const SchemaClassKey& other) const { return *impl == *(other.impl); } +bool SchemaClassKey::operator<(const SchemaClassKey& other) const { return *impl < *(other.impl); } + +SchemaObjectClass::SchemaObjectClass(const char* package, const char* name) : impl(new SchemaObjectClassImpl(package, name)) {} +SchemaObjectClass::SchemaObjectClass(SchemaObjectClassImpl* i) : impl(i) {} +SchemaObjectClass::SchemaObjectClass(const SchemaObjectClass& from) : impl(new SchemaObjectClassImpl(*(from.impl))) {} +SchemaObjectClass::~SchemaObjectClass() { delete impl; } +void SchemaObjectClass::addProperty(const SchemaProperty* property) { impl->addProperty(property); } +void SchemaObjectClass::addStatistic(const SchemaStatistic* statistic) { impl->addStatistic(statistic); } +void SchemaObjectClass::addMethod(const SchemaMethod* method) { impl->addMethod(method); } +const SchemaClassKey* SchemaObjectClass::getClassKey() const { return impl->getClassKey(); } +int SchemaObjectClass::getPropertyCount() const { return impl->getPropertyCount(); } +int SchemaObjectClass::getStatisticCount() const { return impl->getStatisticCount(); } +int SchemaObjectClass::getMethodCount() const { return impl->getMethodCount(); } +const SchemaProperty* SchemaObjectClass::getProperty(int idx) const { return impl->getProperty(idx); } +const SchemaStatistic* SchemaObjectClass::getStatistic(int idx) const { return impl->getStatistic(idx); } +const SchemaMethod* SchemaObjectClass::getMethod(int idx) const { return impl->getMethod(idx); } + +SchemaEventClass::SchemaEventClass(const char* package, const char* name) : impl(new SchemaEventClassImpl(package, name)) {} +SchemaEventClass::SchemaEventClass(SchemaEventClassImpl* i) : impl(i) {} +SchemaEventClass::SchemaEventClass(const SchemaEventClass& from) : impl(new SchemaEventClassImpl(*(from.impl))) {} +SchemaEventClass::~SchemaEventClass() { delete impl; } +void SchemaEventClass::addArgument(const SchemaArgument* argument) { impl->addArgument(argument); } +void SchemaEventClass::setDesc(const char* desc) { impl->setDesc(desc); } +const SchemaClassKey* SchemaEventClass::getClassKey() const { return impl->getClassKey(); } +int SchemaEventClass::getArgumentCount() const { return impl->getArgumentCount(); } +const SchemaArgument* SchemaEventClass::getArgument(int idx) const { return impl->getArgument(idx); } + diff --git a/qpid/cpp/src/qmf/engine/SchemaImpl.h b/qpid/cpp/src/qmf/engine/SchemaImpl.h new file mode 100644 index 0000000000..af3a1d98e4 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/SchemaImpl.h @@ -0,0 +1,223 @@ +#ifndef _QmfEngineSchemaImpl_ +#define _QmfEngineSchemaImpl_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/engine/Schema.h" +#include +#include +#include + +namespace qmf { +namespace engine { + + // TODO: Destructors for schema classes + // TODO: Add "frozen" attribute for schema classes so they can't be modified after + // they've been registered. + + class SchemaHash { + uint8_t hash[16]; + public: + SchemaHash(); + void encode(qpid::framing::Buffer& buffer) const; + void decode(qpid::framing::Buffer& buffer); + void update(const char* data, uint32_t len); + void update(uint8_t data); + void update(const std::string& data) { update(data.c_str(), data.size()); } + void update(Typecode t) { update((uint8_t) t); } + void update(Direction d) { update((uint8_t) d); } + void update(Access a) { update((uint8_t) a); } + void update(bool b) { update((uint8_t) (b ? 1 : 0)); } + const uint8_t* get() const { return hash; } + bool operator==(const SchemaHash& other) const; + bool operator<(const SchemaHash& other) const; + bool operator>(const SchemaHash& other) const; + }; + + struct SchemaArgumentImpl { + std::string name; + Typecode typecode; + Direction dir; + std::string unit; + std::string description; + + SchemaArgumentImpl(const char* n, Typecode t) : name(n), typecode(t), dir(DIR_IN) {} + SchemaArgumentImpl(qpid::framing::Buffer& buffer); + static SchemaArgument* factory(qpid::framing::Buffer& buffer); + void encode(qpid::framing::Buffer& buffer) const; + void setDirection(Direction d) { dir = d; } + void setUnit(const char* val) { unit = val; } + void setDesc(const char* desc) { description = desc; } + const std::string& getName() const { return name; } + Typecode getType() const { return typecode; } + Direction getDirection() const { return dir; } + const std::string& getUnit() const { return unit; } + const std::string& getDesc() const { return description; } + void updateHash(SchemaHash& hash) const; + }; + + struct SchemaMethodImpl { + std::string name; + std::string description; + std::vector arguments; + + SchemaMethodImpl(const char* n) : name(n) {} + SchemaMethodImpl(qpid::framing::Buffer& buffer); + static SchemaMethod* factory(qpid::framing::Buffer& buffer); + void encode(qpid::framing::Buffer& buffer) const; + void addArgument(const SchemaArgument* argument); + void setDesc(const char* desc) { description = desc; } + const std::string& getName() const { return name; } + const std::string& getDesc() const { return description; } + int getArgumentCount() const { return arguments.size(); } + const SchemaArgument* getArgument(int idx) const; + void updateHash(SchemaHash& hash) const; + }; + + struct SchemaPropertyImpl { + std::string name; + Typecode typecode; + Access access; + bool index; + bool optional; + std::string unit; + std::string description; + + SchemaPropertyImpl(const char* n, Typecode t) : name(n), typecode(t), access(ACCESS_READ_ONLY), index(false), optional(false) {} + SchemaPropertyImpl(qpid::framing::Buffer& buffer); + static SchemaProperty* factory(qpid::framing::Buffer& buffer); + void encode(qpid::framing::Buffer& buffer) const; + void setAccess(Access a) { access = a; } + void setIndex(bool val) { index = val; } + void setOptional(bool val) { optional = val; } + void setUnit(const char* val) { unit = val; } + void setDesc(const char* desc) { description = desc; } + const std::string& getName() const { return name; } + Typecode getType() const { return typecode; } + Access getAccess() const { return access; } + bool isIndex() const { return index; } + bool isOptional() const { return optional; } + const std::string& getUnit() const { return unit; } + const std::string& getDesc() const { return description; } + void updateHash(SchemaHash& hash) const; + }; + + struct SchemaStatisticImpl { + std::string name; + Typecode typecode; + std::string unit; + std::string description; + + SchemaStatisticImpl(const char* n, Typecode t) : name(n), typecode(t) {} + SchemaStatisticImpl(qpid::framing::Buffer& buffer); + static SchemaStatistic* factory(qpid::framing::Buffer& buffer); + void encode(qpid::framing::Buffer& buffer) const; + void setUnit(const char* val) { unit = val; } + void setDesc(const char* desc) { description = desc; } + const std::string& getName() const { return name; } + Typecode getType() const { return typecode; } + const std::string& getUnit() const { return unit; } + const std::string& getDesc() const { return description; } + void updateHash(SchemaHash& hash) const; + }; + + struct SchemaClassKeyImpl { + const std::string& package; + const std::string& name; + const SchemaHash& hash; + mutable std::string repr; + + // The *Container elements are only used if there isn't an external place to + // store these values. + std::string packageContainer; + std::string nameContainer; + SchemaHash hashContainer; + + SchemaClassKeyImpl(const std::string& package, const std::string& name, const SchemaHash& hash); + SchemaClassKeyImpl(qpid::framing::Buffer& buffer); + static SchemaClassKey* factory(const std::string& package, const std::string& name, const SchemaHash& hash); + static SchemaClassKey* factory(qpid::framing::Buffer& buffer); + + const std::string& getPackageName() const { return package; } + const std::string& getClassName() const { return name; } + const uint8_t* getHash() const { return hash.get(); } + + void encode(qpid::framing::Buffer& buffer) const; + bool operator==(const SchemaClassKeyImpl& other) const; + bool operator<(const SchemaClassKeyImpl& other) const; + const std::string& str() const; + }; + + struct SchemaObjectClassImpl { + std::string package; + std::string name; + mutable SchemaHash hash; + mutable bool hasHash; + std::auto_ptr classKey; + std::vector properties; + std::vector statistics; + std::vector methods; + + SchemaObjectClassImpl(const char* p, const char* n) : + package(p), name(n), hasHash(false), classKey(SchemaClassKeyImpl::factory(package, name, hash)) {} + SchemaObjectClassImpl(qpid::framing::Buffer& buffer); + static SchemaObjectClass* factory(qpid::framing::Buffer& buffer); + + void encode(qpid::framing::Buffer& buffer) const; + void addProperty(const SchemaProperty* property); + void addStatistic(const SchemaStatistic* statistic); + void addMethod(const SchemaMethod* method); + + const SchemaClassKey* getClassKey() const; + int getPropertyCount() const { return properties.size(); } + int getStatisticCount() const { return statistics.size(); } + int getMethodCount() const { return methods.size(); } + const SchemaProperty* getProperty(int idx) const; + const SchemaStatistic* getStatistic(int idx) const; + const SchemaMethod* getMethod(int idx) const; + }; + + struct SchemaEventClassImpl { + std::string package; + std::string name; + mutable SchemaHash hash; + mutable bool hasHash; + std::auto_ptr classKey; + std::string description; + std::vector arguments; + + SchemaEventClassImpl(const char* p, const char* n) : + package(p), name(n), hasHash(false), classKey(SchemaClassKeyImpl::factory(package, name, hash)) {} + SchemaEventClassImpl(qpid::framing::Buffer& buffer); + static SchemaEventClass* factory(qpid::framing::Buffer& buffer); + + void encode(qpid::framing::Buffer& buffer) const; + void addArgument(const SchemaArgument* argument); + void setDesc(const char* desc) { description = desc; } + + const SchemaClassKey* getClassKey() const; + int getArgumentCount() const { return arguments.size(); } + const SchemaArgument* getArgument(int idx) const; + }; +} +} + +#endif + diff --git a/qpid/cpp/src/qmf/engine/SequenceManager.cpp b/qpid/cpp/src/qmf/engine/SequenceManager.cpp new file mode 100644 index 0000000000..4a4644a8b9 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/SequenceManager.cpp @@ -0,0 +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. + */ + +#include "qmf/engine/SequenceManager.h" + +using namespace std; +using namespace qmf::engine; +using namespace qpid::sys; + +SequenceManager::SequenceManager() : nextSequence(1) {} + +void SequenceManager::setUnsolicitedContext(SequenceContext::Ptr ctx) +{ + unsolicitedContext = ctx; +} + +uint32_t SequenceManager::reserve(SequenceContext::Ptr ctx) +{ + Mutex::ScopedLock _lock(lock); + if (ctx.get() == 0) + ctx = unsolicitedContext; + uint32_t seq = nextSequence; + while (contextMap.find(seq) != contextMap.end()) + seq = seq < 0xFFFFFFFF ? seq + 1 : 1; + nextSequence = seq < 0xFFFFFFFF ? seq + 1 : 1; + contextMap[seq] = ctx; + ctx->reserve(); + return seq; +} + +void SequenceManager::release(uint32_t sequence) +{ + Mutex::ScopedLock _lock(lock); + + if (sequence == 0) { + if (unsolicitedContext.get() != 0) + unsolicitedContext->release(); + return; + } + + map::iterator iter = contextMap.find(sequence); + if (iter != contextMap.end()) { + if (iter->second != 0) + iter->second->release(); + contextMap.erase(iter); + } +} + +void SequenceManager::releaseAll() +{ + Mutex::ScopedLock _lock(lock); + contextMap.clear(); +} + +void SequenceManager::dispatch(uint8_t opcode, uint32_t sequence, const string& routingKey, qpid::framing::Buffer& buffer) +{ + Mutex::ScopedLock _lock(lock); + bool done; + + if (sequence == 0) { + if (unsolicitedContext.get() != 0) { + done = unsolicitedContext->handleMessage(opcode, sequence, routingKey, buffer); + if (done) + unsolicitedContext->release(); + } + return; + } + + map::iterator iter = contextMap.find(sequence); + if (iter != contextMap.end()) { + if (iter->second != 0) { + done = iter->second->handleMessage(opcode, sequence, routingKey, buffer); + if (done) { + iter->second->release(); + contextMap.erase(iter); + } + } + } +} + diff --git a/qpid/cpp/src/qmf/engine/SequenceManager.h b/qpid/cpp/src/qmf/engine/SequenceManager.h new file mode 100644 index 0000000000..9e47e38610 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/SequenceManager.h @@ -0,0 +1,68 @@ +#ifndef _QmfEngineSequenceManager_ +#define _QmfEngineSequenceManager_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qpid/sys/Mutex.h" +#include +#include + +namespace qpid { + namespace framing { + class Buffer; + } +} + +namespace qmf { +namespace engine { + + class SequenceContext { + public: + typedef boost::shared_ptr Ptr; + SequenceContext() {} + virtual ~SequenceContext() {} + + virtual void reserve() = 0; + virtual bool handleMessage(uint8_t opcode, uint32_t sequence, const std::string& routingKey, qpid::framing::Buffer& buffer) = 0; + virtual void release() = 0; + }; + + class SequenceManager { + public: + SequenceManager(); + + void setUnsolicitedContext(SequenceContext::Ptr ctx); + uint32_t reserve(SequenceContext::Ptr ctx = SequenceContext::Ptr()); + void release(uint32_t sequence); + void releaseAll(); + void dispatch(uint8_t opcode, uint32_t sequence, const std::string& routingKey, qpid::framing::Buffer& buffer); + + private: + mutable qpid::sys::Mutex lock; + uint32_t nextSequence; + SequenceContext::Ptr unsolicitedContext; + std::map contextMap; + }; + +} +} + +#endif + diff --git a/qpid/cpp/src/qmf/engine/ValueImpl.cpp b/qpid/cpp/src/qmf/engine/ValueImpl.cpp new file mode 100644 index 0000000000..f80bdab866 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ValueImpl.cpp @@ -0,0 +1,266 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/engine/ValueImpl.h" +#include + +using namespace std; +using namespace qmf::engine; +using qpid::framing::Buffer; + +ValueImpl::ValueImpl(Typecode t, Buffer& buf) : typecode(t) +{ + uint64_t first; + uint64_t second; + qpid::framing::FieldTable ft; + + switch (typecode) { + case TYPE_UINT8 : value.u32 = (uint32_t) buf.getOctet(); break; + case TYPE_UINT16 : value.u32 = (uint32_t) buf.getShort(); break; + case TYPE_UINT32 : value.u32 = (uint32_t) buf.getLong(); break; + case TYPE_UINT64 : value.u64 = buf.getLongLong(); break; + case TYPE_SSTR : buf.getShortString(stringVal); break; + case TYPE_LSTR : buf.getMediumString(stringVal); break; + case TYPE_ABSTIME : value.s64 = buf.getLongLong(); break; + case TYPE_DELTATIME : value.u64 = buf.getLongLong(); break; + case TYPE_BOOL : value.boolVal = (buf.getOctet() != 0); break; + case TYPE_FLOAT : value.floatVal = buf.getFloat(); break; + case TYPE_DOUBLE : value.doubleVal = buf.getDouble(); break; + case TYPE_INT8 : value.s32 = (int32_t) ((int8_t) buf.getOctet()); break; + case TYPE_INT16 : value.s32 = (int32_t) ((int16_t) buf.getShort()); break; + case TYPE_INT32 : value.s32 = (int32_t) buf.getLong(); break; + case TYPE_INT64 : value.s64 = buf.getLongLong(); break; + case TYPE_UUID : buf.getBin128(value.uuidVal); break; + case TYPE_REF: + first = buf.getLongLong(); + second = buf.getLongLong(); + refVal.impl->setValue(first, second); + break; + + case TYPE_MAP: + ft.decode(buf); + // TODO: either update to recursively use QMF types or reduce to int/string/... + // (maybe use another ctor with a FieldValue argument) + break; + + case TYPE_LIST: + case TYPE_ARRAY: + case TYPE_OBJECT: + default: + break; + } +} + +ValueImpl::ValueImpl(Typecode t, Typecode at) : typecode(t), valid(false), arrayTypecode(at) +{ +} + +ValueImpl::ValueImpl(Typecode t) : typecode(t) +{ + ::memset(&value, 0, sizeof(value)); +} + +Value* ValueImpl::factory(Typecode t, Buffer& b) +{ + ValueImpl* impl(new ValueImpl(t, b)); + return new Value(impl); +} + +Value* ValueImpl::factory(Typecode t) +{ + ValueImpl* impl(new ValueImpl(t)); + return new Value(impl); +} + +ValueImpl::~ValueImpl() +{ +} + +void ValueImpl::encode(Buffer& buf) const +{ + switch (typecode) { + case TYPE_UINT8 : buf.putOctet((uint8_t) value.u32); break; + case TYPE_UINT16 : buf.putShort((uint16_t) value.u32); break; + case TYPE_UINT32 : buf.putLong(value.u32); break; + case TYPE_UINT64 : buf.putLongLong(value.u64); break; + case TYPE_SSTR : buf.putShortString(stringVal); break; + case TYPE_LSTR : buf.putMediumString(stringVal); break; + case TYPE_ABSTIME : buf.putLongLong(value.s64); break; + case TYPE_DELTATIME : buf.putLongLong(value.u64); break; + case TYPE_BOOL : buf.putOctet(value.boolVal ? 1 : 0); break; + case TYPE_FLOAT : buf.putFloat(value.floatVal); break; + case TYPE_DOUBLE : buf.putDouble(value.doubleVal); break; + case TYPE_INT8 : buf.putOctet((uint8_t) value.s32); break; + case TYPE_INT16 : buf.putShort((uint16_t) value.s32); break; + case TYPE_INT32 : buf.putLong(value.s32); break; + case TYPE_INT64 : buf.putLongLong(value.s64); break; + case TYPE_UUID : buf.putBin128(value.uuidVal); break; + case TYPE_REF : refVal.impl->encode(buf); break; + case TYPE_MAP: // TODO + case TYPE_LIST: + case TYPE_ARRAY: + case TYPE_OBJECT: + default: + break; + } +} + +bool ValueImpl::keyInMap(const char* key) const +{ + return typecode == TYPE_MAP && mapVal.count(key) > 0; +} + +Value* ValueImpl::byKey(const char* key) +{ + if (keyInMap(key)) { + map::iterator iter = mapVal.find(key); + if (iter != mapVal.end()) + return &iter->second; + } + return 0; +} + +const Value* ValueImpl::byKey(const char* key) const +{ + if (keyInMap(key)) { + map::const_iterator iter = mapVal.find(key); + if (iter != mapVal.end()) + return &iter->second; + } + return 0; +} + +void ValueImpl::deleteKey(const char* key) +{ + mapVal.erase(key); +} + +void ValueImpl::insert(const char* key, Value* val) +{ + pair entry(key, *val); + mapVal.insert(entry); +} + +const char* ValueImpl::key(uint32_t idx) const +{ + map::const_iterator iter = mapVal.begin(); + for (uint32_t i = 0; i < idx; i++) { + if (iter == mapVal.end()) + break; + iter++; + } + + if (iter == mapVal.end()) + return 0; + else + return iter->first.c_str(); +} + +Value* ValueImpl::listItem(uint32_t) +{ + return 0; +} + +void ValueImpl::appendToList(Value*) +{ +} + +void ValueImpl::deleteListItem(uint32_t) +{ +} + +Value* ValueImpl::arrayItem(uint32_t) +{ + return 0; +} + +void ValueImpl::appendToArray(Value*) +{ +} + +void ValueImpl::deleteArrayItem(uint32_t) +{ +} + + +//================================================================== +// Wrappers +//================================================================== + +Value::Value(const Value& from) : impl(new ValueImpl(*(from.impl))) {} +Value::Value(Typecode t, Typecode at) : impl(new ValueImpl(t, at)) {} +Value::Value(ValueImpl* i) : impl(i) {} +Value::~Value() { delete impl;} + +Typecode Value::getType() const { return impl->getType(); } +bool Value::isNull() const { return impl->isNull(); } +void Value::setNull() { impl->setNull(); } +bool Value::isObjectId() const { return impl->isObjectId(); } +const ObjectId& Value::asObjectId() const { return impl->asObjectId(); } +void Value::setObjectId(const ObjectId& oid) { impl->setObjectId(oid); } +bool Value::isUint() const { return impl->isUint(); } +uint32_t Value::asUint() const { return impl->asUint(); } +void Value::setUint(uint32_t val) { impl->setUint(val); } +bool Value::isInt() const { return impl->isInt(); } +int32_t Value::asInt() const { return impl->asInt(); } +void Value::setInt(int32_t val) { impl->setInt(val); } +bool Value::isUint64() const { return impl->isUint64(); } +uint64_t Value::asUint64() const { return impl->asUint64(); } +void Value::setUint64(uint64_t val) { impl->setUint64(val); } +bool Value::isInt64() const { return impl->isInt64(); } +int64_t Value::asInt64() const { return impl->asInt64(); } +void Value::setInt64(int64_t val) { impl->setInt64(val); } +bool Value::isString() const { return impl->isString(); } +const char* Value::asString() const { return impl->asString(); } +void Value::setString(const char* val) { impl->setString(val); } +bool Value::isBool() const { return impl->isBool(); } +bool Value::asBool() const { return impl->asBool(); } +void Value::setBool(bool val) { impl->setBool(val); } +bool Value::isFloat() const { return impl->isFloat(); } +float Value::asFloat() const { return impl->asFloat(); } +void Value::setFloat(float val) { impl->setFloat(val); } +bool Value::isDouble() const { return impl->isDouble(); } +double Value::asDouble() const { return impl->asDouble(); } +void Value::setDouble(double val) { impl->setDouble(val); } +bool Value::isUuid() const { return impl->isUuid(); } +const uint8_t* Value::asUuid() const { return impl->asUuid(); } +void Value::setUuid(const uint8_t* val) { impl->setUuid(val); } +bool Value::isObject() const { return impl->isObject(); } +const Object* Value::asObject() const { return impl->asObject(); } +void Value::setObject(Object* val) { impl->setObject(val); } +bool Value::isMap() const { return impl->isMap(); } +bool Value::keyInMap(const char* key) const { return impl->keyInMap(key); } +Value* Value::byKey(const char* key) { return impl->byKey(key); } +const Value* Value::byKey(const char* key) const { return impl->byKey(key); } +void Value::deleteKey(const char* key) { impl->deleteKey(key); } +void Value::insert(const char* key, Value* val) { impl->insert(key, val); } +uint32_t Value::keyCount() const { return impl->keyCount(); } +const char* Value::key(uint32_t idx) const { return impl->key(idx); } +bool Value::isList() const { return impl->isList(); } +uint32_t Value::listItemCount() const { return impl->listItemCount(); } +Value* Value::listItem(uint32_t idx) { return impl->listItem(idx); } +void Value::appendToList(Value* val) { impl->appendToList(val); } +void Value::deleteListItem(uint32_t idx) { impl->deleteListItem(idx); } +bool Value::isArray() const { return impl->isArray(); } +Typecode Value::arrayType() const { return impl->arrayType(); } +uint32_t Value::arrayItemCount() const { return impl->arrayItemCount(); } +Value* Value::arrayItem(uint32_t idx) { return impl->arrayItem(idx); } +void Value::appendToArray(Value* val) { impl->appendToArray(val); } +void Value::deleteArrayItem(uint32_t idx) { impl->deleteArrayItem(idx); } + diff --git a/qpid/cpp/src/qmf/engine/ValueImpl.h b/qpid/cpp/src/qmf/engine/ValueImpl.h new file mode 100644 index 0000000000..b6adae5d93 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ValueImpl.h @@ -0,0 +1,150 @@ +#ifndef _QmfEngineValueImpl_ +#define _QmfEngineValueImpl_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace qmf { +namespace engine { + + // TODO: set valid flag on all value settors + // TODO: add a modified flag and accessors + + struct ValueImpl { + const Typecode typecode; + bool valid; + + ObjectId refVal; + std::string stringVal; + std::auto_ptr objectVal; + std::map mapVal; + std::vector vectorVal; + Typecode arrayTypecode; + + union { + uint32_t u32; + uint64_t u64; + int32_t s32; + int64_t s64; + bool boolVal; + float floatVal; + double doubleVal; + uint8_t uuidVal[16]; + } value; + + ValueImpl(const ValueImpl& from) : + typecode(from.typecode), valid(from.valid), refVal(from.refVal), stringVal(from.stringVal), + objectVal(from.objectVal.get() ? new Object(*(from.objectVal)) : 0), + mapVal(from.mapVal), vectorVal(from.vectorVal), arrayTypecode(from.arrayTypecode), + value(from.value) {} + + ValueImpl(Typecode t, Typecode at); + ValueImpl(Typecode t, qpid::framing::Buffer& b); + ValueImpl(Typecode t); + static Value* factory(Typecode t, qpid::framing::Buffer& b); + static Value* factory(Typecode t); + ~ValueImpl(); + + void encode(qpid::framing::Buffer& b) const; + + Typecode getType() const { return typecode; } + bool isNull() const { return !valid; } + void setNull() { valid = false; } + + bool isObjectId() const { return typecode == TYPE_REF; } + const ObjectId& asObjectId() const { return refVal; } + void setObjectId(const ObjectId& o) { refVal = o; } // TODO + + bool isUint() const { return typecode >= TYPE_UINT8 && typecode <= TYPE_UINT32; } + uint32_t asUint() const { return value.u32; } + void setUint(uint32_t val) { value.u32 = val; } + + bool isInt() const { return typecode >= TYPE_INT8 && typecode <= TYPE_INT32; } + int32_t asInt() const { return value.s32; } + void setInt(int32_t val) { value.s32 = val; } + + bool isUint64() const { return typecode == TYPE_UINT64 || typecode == TYPE_DELTATIME; } + uint64_t asUint64() const { return value.u64; } + void setUint64(uint64_t val) { value.u64 = val; } + + bool isInt64() const { return typecode == TYPE_INT64 || typecode == TYPE_ABSTIME; } + int64_t asInt64() const { return value.s64; } + void setInt64(int64_t val) { value.s64 = val; } + + bool isString() const { return typecode == TYPE_SSTR || typecode == TYPE_LSTR; } + const char* asString() const { return stringVal.c_str(); } + void setString(const char* val) { stringVal = val; } + + bool isBool() const { return typecode == TYPE_BOOL; } + bool asBool() const { return value.boolVal; } + void setBool(bool val) { value.boolVal = val; } + + bool isFloat() const { return typecode == TYPE_FLOAT; } + float asFloat() const { return value.floatVal; } + void setFloat(float val) { value.floatVal = val; } + + bool isDouble() const { return typecode == TYPE_DOUBLE; } + double asDouble() const { return value.doubleVal; } + void setDouble(double val) { value.doubleVal = val; } + + bool isUuid() const { return typecode == TYPE_UUID; } + const uint8_t* asUuid() const { return value.uuidVal; } + void setUuid(const uint8_t* val) { ::memcpy(value.uuidVal, val, 16); } + + bool isObject() const { return typecode == TYPE_OBJECT; } + Object* asObject() const { return objectVal.get(); } + void setObject(Object* val) { objectVal.reset(val); } + + bool isMap() const { return typecode == TYPE_MAP; } + bool keyInMap(const char* key) const; + Value* byKey(const char* key); + const Value* byKey(const char* key) const; + void deleteKey(const char* key); + void insert(const char* key, Value* val); + uint32_t keyCount() const { return mapVal.size(); } + const char* key(uint32_t idx) const; + + bool isList() const { return typecode == TYPE_LIST; } + uint32_t listItemCount() const { return vectorVal.size(); } + Value* listItem(uint32_t idx); + void appendToList(Value* val); + void deleteListItem(uint32_t idx); + + bool isArray() const { return typecode == TYPE_ARRAY; } + Typecode arrayType() const { return arrayTypecode; } + uint32_t arrayItemCount() const { return vectorVal.size(); } + Value* arrayItem(uint32_t idx); + void appendToArray(Value* val); + void deleteArrayItem(uint32_t idx); + }; +} +} + +#endif + diff --git a/qpid/cpp/src/qpid/acl/AclData.cpp b/qpid/cpp/src/qpid/acl/AclData.cpp index 81519c3311..5d7a028736 100644 --- a/qpid/cpp/src/qpid/acl/AclData.cpp +++ b/qpid/cpp/src/qpid/acl/AclData.cpp @@ -18,7 +18,8 @@ #include "qpid/acl/AclData.h" #include "qpid/log/Statement.h" - +#include "qpid/sys/IntegerTypes.h" +#include namespace qpid { namespace acl { @@ -57,14 +58,15 @@ AclResult AclData::lookup(const std::string& id, const Action& action, const Obj const std::string& name, std::map* params) { QPID_LOG(debug, "ACL: Lookup for id:" << id << " action:" << AclHelper::getActionStr((Action) action) - << " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType) << " name:" << name - << " with params " << AclHelper::propertyMapToString(params)); + << " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType) << " name:" << name + << " with params " << AclHelper::propertyMapToString(params)); AclResult aclresult = decisionMode; if (actionList[action] && actionList[action][objType]) { AclData::actObjItr itrRule = actionList[action][objType]->find(id); if (itrRule == actionList[action][objType]->end()) itrRule = actionList[action][objType]->find("*"); + if (itrRule != actionList[action][objType]->end()) { QPID_LOG(debug, "ACL: checking the following rules for : " << itrRule->first ); @@ -79,25 +81,48 @@ AclResult AclData::lookup(const std::string& id, const Action& action, const Obj if (pMItr->first == acl::PROP_NAME) { if (matchProp(pMItr->second, name)){ QPID_LOG(debug, "ACL: name '" << name << "' matched with name '" - << pMItr->second << "' given in the rule"); - }else{ + << pMItr->second << "' given in the rule"); + }else{ match = false; QPID_LOG(debug, "ACL: name '" << name << "' didn't match with name '" - << pMItr->second << "' given in the rule"); + << pMItr->second << "' given in the rule"); } } else if (params) { //match pMItr against params propertyMapItr paramItr = params->find(pMItr->first); if (paramItr == params->end()) { match = false; QPID_LOG(debug, "ACL: the given parameter map in lookup doesn't contain the property '" - << AclHelper::getPropertyStr(pMItr->first) << "'"); - } else if (!matchProp(pMItr->second, paramItr->second)) { + << AclHelper::getPropertyStr(pMItr->first) << "'"); + }else if ( pMItr->first == acl::PROP_MAXQUEUECOUNT || pMItr->first == acl::PROP_MAXQUEUESIZE ) { + if ( pMItr->first == paramItr->first ) { + uint64_t aclMax = boost::lexical_cast(pMItr->second); + uint64_t paramMax = boost::lexical_cast(paramItr->second); + QPID_LOG(debug, "ACL: Numeric comparison for property " << + AclHelper::getPropertyStr(paramItr->first) << + " (value given in lookup = " << + boost::lexical_cast(paramItr->second) << + ", value give in rule = " << + boost::lexical_cast(pMItr->second) << " )"); + if (( aclMax ) && ( paramMax == 0 || paramMax > aclMax)){ + match = decisionMode == qpid::acl::ALLOW ; + QPID_LOG(debug, "ACL: Limit exceeded and match=" << + (match ? "true": "false") << + " as decision mode is " << AclHelper::getAclResultStr(decisionMode)); + } + } + }else if (matchProp(pMItr->second, paramItr->second)) { + QPID_LOG(debug, "ACL: the pair(" + << AclHelper::getPropertyStr(paramItr->first) << "," << paramItr->second + << ") given in lookup matched the pair(" + << AclHelper::getPropertyStr(pMItr->first) << "," << pMItr->second << ") given in the rule"); + } else { QPID_LOG(debug, "ACL: the pair(" - << AclHelper::getPropertyStr(paramItr->first) << "," << paramItr->second - << ") given in lookup doesn't match the pair(" - << AclHelper::getPropertyStr(pMItr->first) << "," << pMItr->second << ") given in the rule"); + << AclHelper::getPropertyStr(paramItr->first) << "," << paramItr->second + << ") given in lookup doesn't match the pair(" + << AclHelper::getPropertyStr(pMItr->first) << "," << pMItr->second << ") given in the rule"); match = false; - } + + } } } if (match) @@ -116,37 +141,63 @@ AclResult AclData::lookup(const std::string& id, const Action& action, const Obj AclResult AclData::lookup(const std::string& id, const Action& action, const ObjectType& objType, const std::string& /*Exchange*/ name, const std::string& RoutingKey) { - AclResult aclresult = decisionMode; + + QPID_LOG(debug, "ACL: Lookup for id:" << id << " action:" << AclHelper::getActionStr((Action) action) + << " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType) << " exchange name:" << name + << " with routing key " << RoutingKey); + + AclResult aclresult = decisionMode; - if (actionList[action] && actionList[action][objType]){ - AclData::actObjItr itrRule = actionList[action][objType]->find(id); - if (itrRule == actionList[action][objType]->end()) + if (actionList[action] && actionList[action][objType]){ + AclData::actObjItr itrRule = actionList[action][objType]->find(id); + + if (itrRule == actionList[action][objType]->end()) itrRule = actionList[action][objType]->find("*"); + if (itrRule != actionList[action][objType]->end() ) { + QPID_LOG(debug, "ACL: checking the following rules for : " << itrRule->first ); + //loop the vector - for (ruleSetItr i=itrRule->second.begin(); isecond.end(); i++) { - + for (ruleSetItr i=itrRule->second.begin(); isecond.end(); i++) { + QPID_LOG(debug, "ACL: checking rule " << i->toString()); + // loop the names looking for match bool match =true; for (propertyMapItr pMItr = i->props.begin(); (pMItr != i->props.end()) && match; pMItr++) { - //match name is exists first + //match name is exists first if (pMItr->first == acl::PROP_NAME){ - if (!matchProp(pMItr->second, name)){ - match= false; - } + if (matchProp(pMItr->second, name)){ + QPID_LOG(debug, "ACL: name '" << name << "' matched with name '" + << pMItr->second << "' given in the rule"); + + }else{ + match= false; + QPID_LOG(debug, "ACL: name '" << name << "' didn't match with name '" + << pMItr->second << "' given in the rule"); + } }else if (pMItr->first == acl::PROP_ROUTINGKEY){ - if (!matchProp(pMItr->second, RoutingKey)){ - match= false; - } + if (matchProp(pMItr->second, RoutingKey)){ + QPID_LOG(debug, "ACL: name '" << name << "' matched with routing_key '" + << pMItr->second << "' given in the rule"); + }else{ + match= false; + QPID_LOG(debug, "ACL: name '" << name << "' didn't match with routing_key '" + << pMItr->second << "' given in the rule"); + } } } - if (match) return getACLResult(i->logOnly, i->log); - } + if (match){ + aclresult = getACLResult(i->logOnly, i->log); + QPID_LOG(debug,"Successful match, the decision is:" << AclHelper::getAclResultStr(aclresult)); + return aclresult; + } + } } - } - return aclresult; + } + QPID_LOG(debug,"No successful match, defaulting to the decision mode " << AclHelper::getAclResultStr(aclresult)); + return aclresult; } diff --git a/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp index 093e9cea32..f9f39316e2 100644 --- a/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp +++ b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp @@ -78,7 +78,7 @@ ManagementAgent* ManagementAgent::Singleton::getInstance() const string ManagementAgentImpl::storeMagicNumber("MA02"); ManagementAgentImpl::ManagementAgentImpl() : - interval(10), extThread(false), + interval(10), extThread(false), pipeHandle(0), initialized(false), connected(false), lastFailure("never connected"), clientWasAdded(true), requestedBrokerBank(0), requestedAgentBank(0), assignedBrokerBank(0), assignedAgentBank(0), bootSequence(0), @@ -89,13 +89,12 @@ ManagementAgentImpl::ManagementAgentImpl() : ManagementAgentImpl::~ManagementAgentImpl() { + // shutdown & cleanup all threads connThreadBody.close(); + pubThreadBody.close(); - // If the thread is doing work on the connection, we must wait for it to - // complete before shutting down. - if (!connThreadBody.isSleeping()) { - connThread.join(); - } + connThread.join(); + pubThread.join(); // Release the memory associated with stored management objects. { @@ -777,6 +776,7 @@ void ManagementAgentImpl::ConnectionThread::run() static const int delayFactor(2); int delay(delayMin); string dest("qmfagent"); + ConnectionThread::shared_ptr tmp; sessionId.generate(); queueName << "qmfagent-" << sessionId; @@ -787,7 +787,7 @@ void ManagementAgentImpl::ConnectionThread::run() QPID_LOG(debug, "QMF Agent attempting to connect to the broker..."); connection.open(agent.connectionSettings); session = connection.newSession(queueName.str()); - subscriptions = new client::SubscriptionManager(session); + subscriptions.reset(new client::SubscriptionManager(session)); session.queueDeclare(arg::queue=queueName.str(), arg::autoDelete=true, arg::exclusive=true); @@ -811,11 +811,12 @@ void ManagementAgentImpl::ConnectionThread::run() operational = false; agent.connected = false; + tmp = subscriptions; + subscriptions.reset(); } + tmp.reset(); // frees the subscription outside the lock delay = delayMin; connection.close(); - delete subscriptions; - subscriptions = 0; } } catch (exception &e) { if (delay < delayMax) @@ -824,14 +825,19 @@ void ManagementAgentImpl::ConnectionThread::run() } { + // sleep for "delay" seconds, but peridically check if the + // agent is shutting down so we don't hang for up to delayMax + // seconds during agent shutdown Mutex::ScopedLock _lock(connLock); if (shutdown) return; sleeping = true; - { - Mutex::ScopedUnlock _unlock(connLock); - ::sleep(delay); - } + int totalSleep = 0; + do { + Mutex::ScopedUnlock _unlock(connLock); + ::sleep(delayMin); + totalSleep += delayMin; + } while (totalSleep < delay && !shutdown); sleeping = false; if (shutdown) return; @@ -848,10 +854,12 @@ void ManagementAgentImpl::ConnectionThread::sendBuffer(Buffer& buf, const string& exchange, const string& routingKey) { + ConnectionThread::shared_ptr s; { Mutex::ScopedLock _lock(connLock); if (!operational) return; + s = subscriptions; } Message msg; @@ -866,8 +874,8 @@ void ManagementAgentImpl::ConnectionThread::sendBuffer(Buffer& buf, } catch(exception& e) { QPID_LOG(error, "Exception caught in sendBuffer: " << e.what()); // Bounce the connection - if (subscriptions) - subscriptions->stop(); + if (s) + s->stop(); } } @@ -881,12 +889,14 @@ void ManagementAgentImpl::ConnectionThread::bindToBank(uint32_t brokerBank, uint void ManagementAgentImpl::ConnectionThread::close() { + ConnectionThread::shared_ptr s; { Mutex::ScopedLock _lock(connLock); shutdown = true; + s = subscriptions; } - if (subscriptions) - subscriptions->stop(); + if (s) + s->stop(); } bool ManagementAgentImpl::ConnectionThread::isSleeping() const @@ -898,8 +908,13 @@ bool ManagementAgentImpl::ConnectionThread::isSleeping() const void ManagementAgentImpl::PublishThread::run() { - while (true) { + uint16_t totalSleep; + + while (!shutdown) { agent.periodicProcessing(); - ::sleep(agent.getInterval()); + totalSleep = 0; + while (totalSleep++ < agent.getInterval() && !shutdown) { + ::sleep(1); + } } } diff --git a/qpid/cpp/src/qpid/agent/ManagementAgentImpl.h b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.h index f9cad9ebf5..a876496e98 100644 --- a/qpid/cpp/src/qpid/agent/ManagementAgentImpl.h +++ b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.h @@ -163,12 +163,14 @@ class ManagementAgentImpl : public ManagementAgent, public client::MessageListen friend class ConnectionThread; class ConnectionThread : public sys::Runnable { + typedef boost::shared_ptr shared_ptr; + bool operational; ManagementAgentImpl& agent; framing::Uuid sessionId; client::Connection connection; client::Session session; - client::SubscriptionManager* subscriptions; + ConnectionThread::shared_ptr subscriptions; std::stringstream queueName; mutable sys::Mutex connLock; bool shutdown; @@ -176,7 +178,7 @@ class ManagementAgentImpl : public ManagementAgent, public client::MessageListen void run(); public: ConnectionThread(ManagementAgentImpl& _agent) : - operational(false), agent(_agent), subscriptions(0), + operational(false), agent(_agent), shutdown(false), sleeping(false) {} ~ConnectionThread(); void sendBuffer(qpid::framing::Buffer& buf, @@ -192,8 +194,11 @@ class ManagementAgentImpl : public ManagementAgent, public client::MessageListen { ManagementAgentImpl& agent; void run(); + bool shutdown; public: - PublishThread(ManagementAgentImpl& _agent) : agent(_agent) {} + PublishThread(ManagementAgentImpl& _agent) : + agent(_agent), shutdown(false) {} + void close() { shutdown = true; } }; ConnectionThread connThreadBody; diff --git a/qpid/cpp/src/qpid/amqp_0_10/Connection.cpp b/qpid/cpp/src/qpid/amqp_0_10/Connection.cpp index 96d5146c30..bf2e7d5713 100644 --- a/qpid/cpp/src/qpid/amqp_0_10/Connection.cpp +++ b/qpid/cpp/src/qpid/amqp_0_10/Connection.cpp @@ -31,7 +31,7 @@ namespace amqp_0_10 { using sys::Mutex; Connection::Connection(sys::OutputControl& o, const std::string& id, bool _isClient) - : frameQueueClosed(false), output(o), identifier(id), initialized(false), + : pushClosed(false), popClosed(false), output(o), identifier(id), initialized(false), isClient(_isClient), buffered(0), version(0,10) {} @@ -61,19 +61,23 @@ size_t Connection::decode(const char* buffer, size_t size) { } bool Connection::canEncode() { - if (!frameQueueClosed) connection->doOutput(); Mutex::ScopedLock l(frameQueueLock); - return (!isClient && !initialized) || !frameQueue.empty(); + if (!popClosed) { + Mutex::ScopedUnlock u(frameQueueLock); + connection->doOutput(); + } + return !popClosed && ((!isClient && !initialized) || !frameQueue.empty()); } bool Connection::isClosed() const { Mutex::ScopedLock l(frameQueueLock); - return frameQueueClosed; + return pushClosed && popClosed; } size_t Connection::encode(const char* buffer, size_t size) { { // Swap frameQueue data into workQueue to avoid holding lock while we encode. Mutex::ScopedLock l(frameQueueLock); + if (popClosed) return 0; // Can't pop any more frames. assert(workQueue.empty()); workQueue.swap(frameQueue); } @@ -102,6 +106,8 @@ size_t Connection::encode(const char* buffer, size_t size) { // Put back any frames we did not encode. frameQueue.insert(frameQueue.begin(), workQueue.begin(), workQueue.end()); workQueue.clear(); + if (frameQueue.empty() && pushClosed) + popClosed = true; } return out.getPosition(); } @@ -111,9 +117,10 @@ void Connection::activateOutput() { output.activateOutput(); } void Connection::giveReadCredit(int32_t credit) { output.giveReadCredit(credit); } void Connection::close() { - // Close the output queue. + // No more frames can be pushed onto the queue. + // Frames aleady on the queue can be popped. Mutex::ScopedLock l(frameQueueLock); - frameQueueClosed = true; + pushClosed = true; } void Connection::closed() { @@ -123,7 +130,7 @@ void Connection::closed() { void Connection::send(framing::AMQFrame& f) { { Mutex::ScopedLock l(frameQueueLock); - if (!frameQueueClosed) + if (!pushClosed) frameQueue.push_back(f); buffered += f.encodedSize(); } diff --git a/qpid/cpp/src/qpid/amqp_0_10/Connection.h b/qpid/cpp/src/qpid/amqp_0_10/Connection.h index 6fd51381fc..995d824796 100644 --- a/qpid/cpp/src/qpid/amqp_0_10/Connection.h +++ b/qpid/cpp/src/qpid/amqp_0_10/Connection.h @@ -47,7 +47,7 @@ class Connection : public sys::ConnectionCodec, FrameQueue frameQueue; FrameQueue workQueue; - bool frameQueueClosed; + bool pushClosed, popClosed; mutable sys::Mutex frameQueueLock; sys::OutputControl& output; std::auto_ptr connection; diff --git a/qpid/cpp/src/qpid/broker/AclModule.h b/qpid/cpp/src/qpid/broker/AclModule.h index 536fa21b2b..2f4f7eaacc 100644 --- a/qpid/cpp/src/qpid/broker/AclModule.h +++ b/qpid/cpp/src/qpid/broker/AclModule.h @@ -40,7 +40,8 @@ enum Action {ACT_CONSUME, ACT_PUBLISH, ACT_CREATE, ACT_ACCESS, ACT_BIND, enum Property {PROP_NAME, PROP_DURABLE, PROP_OWNER, PROP_ROUTINGKEY, PROP_PASSIVE, PROP_AUTODELETE, PROP_EXCLUSIVE, PROP_TYPE, PROP_ALTERNATE, PROP_QUEUENAME, PROP_SCHEMAPACKAGE, - PROP_SCHEMACLASS}; + PROP_SCHEMACLASS, PROP_POLICYTYPE, PROP_MAXQUEUESIZE, + PROP_MAXQUEUECOUNT}; enum AclResult {ALLOW, ALLOWLOG, DENY, DENYLOG}; } // namespace acl @@ -132,6 +133,9 @@ class AclHelper { if (str.compare("queuename") == 0) return PROP_QUEUENAME; if (str.compare("schemapackage") == 0) return PROP_SCHEMAPACKAGE; if (str.compare("schemaclass") == 0) return PROP_SCHEMACLASS; + if (str.compare("policytype") == 0) return PROP_POLICYTYPE; + if (str.compare("maxqueuesize") == 0) return PROP_MAXQUEUESIZE; + if (str.compare("maxqueuecount") == 0) return PROP_MAXQUEUECOUNT; throw str; } static inline std::string getPropertyStr(const Property p) { @@ -148,6 +152,9 @@ class AclHelper { case PROP_QUEUENAME: return "queuename"; case PROP_SCHEMAPACKAGE: return "schemapackage"; case PROP_SCHEMACLASS: return "schemaclass"; + case PROP_POLICYTYPE: return "policytype"; + case PROP_MAXQUEUESIZE: return "maxqueuesize"; + case PROP_MAXQUEUECOUNT: return "maxqueuecount"; default: assert(false); // should never get here } return ""; @@ -217,11 +224,14 @@ class AclHelper { // == Queues == propSetPtr p4(new propSet); - p3->insert(PROP_ALTERNATE); - p3->insert(PROP_PASSIVE); - p3->insert(PROP_DURABLE); - p3->insert(PROP_EXCLUSIVE); - p3->insert(PROP_AUTODELETE); + p4->insert(PROP_ALTERNATE); + p4->insert(PROP_PASSIVE); + p4->insert(PROP_DURABLE); + p4->insert(PROP_EXCLUSIVE); + p4->insert(PROP_AUTODELETE); + p4->insert(PROP_POLICYTYPE); + p4->insert(PROP_MAXQUEUESIZE); + p4->insert(PROP_MAXQUEUECOUNT); actionMapPtr a1(new actionMap); a1->insert(actionPair(ACT_ACCESS, p0)); diff --git a/qpid/cpp/src/qpid/broker/Broker.cpp b/qpid/cpp/src/qpid/broker/Broker.cpp index 13cf88fb11..4259bb2f31 100644 --- a/qpid/cpp/src/qpid/broker/Broker.cpp +++ b/qpid/cpp/src/qpid/broker/Broker.cpp @@ -91,7 +91,8 @@ Broker::Options::Options(const std::string& name) : queueLimit(100*1048576/*100M default limit*/), tcpNoDelay(false), requireEncrypted(false), - maxSessionRate(0) + maxSessionRate(0), + asyncQueueEvents(true) { int c = sys::SystemInfo::concurrency(); workerThreads=c+1; @@ -121,7 +122,8 @@ Broker::Options::Options(const std::string& name) : ("tcp-nodelay", optValue(tcpNoDelay), "Set TCP_NODELAY on TCP connections") ("require-encryption", optValue(requireEncrypted), "Only accept connections that are encrypted") ("known-hosts-url", optValue(knownHosts, "URL or 'none'"), "URL to send as 'known-hosts' to clients ('none' implies empty list)") - ("max-session-rate", optValue(maxSessionRate, "MESSAGES/S"), "Sets the maximum message rate per session (0=unlimited)"); + ("max-session-rate", optValue(maxSessionRate, "MESSAGES/S"), "Sets the maximum message rate per session (0=unlimited)") + ("async-queue-events", optValue(asyncQueueEvents, "yes|no"), "Set Queue Events async, used for services like replication"); } const std::string empty; @@ -150,7 +152,7 @@ Broker::Broker(const Broker::Options& conf) : *this), managementAgent(conf.enableMgmt ? new ManagementAgent() : 0), queueCleaner(queues, timer), - queueEvents(poller), + queueEvents(poller,!conf.asyncQueueEvents), recovery(true), expiryPolicy(new ExpiryPolicy), getKnownBrokers(boost::bind(&Broker::getKnownBrokersImpl, this)) @@ -208,8 +210,10 @@ Broker::Broker(const Broker::Options& conf) : (*i)->earlyInitialize(*this); // If no plugin store module registered itself, set up the null store. - if (store.get() == 0) - setStore (new NullMessageStore()); + if (store.get() == 0) { + boost::shared_ptr p(new NullMessageStore()); + setStore (p); + } exchanges.declare(empty, DirectExchange::typeName); // Default exchange. @@ -296,7 +300,7 @@ boost::intrusive_ptr Broker::create(const Options& opts) return boost::intrusive_ptr(new Broker(opts)); } -void Broker::setStore (MessageStore* _store) +void Broker::setStore (boost::shared_ptr& _store) { store.reset(new MessageStoreModule (_store)); queues.setStore (store.get()); diff --git a/qpid/cpp/src/qpid/broker/Broker.h b/qpid/cpp/src/qpid/broker/Broker.h index 0517ceca95..5ca01e0867 100644 --- a/qpid/cpp/src/qpid/broker/Broker.h +++ b/qpid/cpp/src/qpid/broker/Broker.h @@ -111,6 +111,7 @@ public: bool requireEncrypted; std::string knownHosts; uint32_t maxSessionRate; + bool asyncQueueEvents; private: std::string getHome(); @@ -171,7 +172,7 @@ public: /** Shut down the broker */ virtual void shutdown(); - QPID_BROKER_EXTERN void setStore (MessageStore*); + QPID_BROKER_EXTERN void setStore (boost::shared_ptr& store); MessageStore& getStore() { return *store; } void setAcl (AclModule* _acl) {acl = _acl;} AclModule* getAcl() { return acl; } diff --git a/qpid/cpp/src/qpid/broker/DirectExchange.cpp b/qpid/cpp/src/qpid/broker/DirectExchange.cpp index b9f24dee5f..29fe47beac 100644 --- a/qpid/cpp/src/qpid/broker/DirectExchange.cpp +++ b/qpid/cpp/src/qpid/broker/DirectExchange.cpp @@ -145,39 +145,12 @@ bool DirectExchange::unbind(Queue::shared_ptr queue, const string& routingKey, c void DirectExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/) { PreRoute pr(msg, this); - Queues::ConstPtr p; + ConstBindingList b; { Mutex::ScopedLock l(lock); - p = bindings[routingKey].queues.snapshot(); - } - int count(0); - - if (p) { - for(std::vector::const_iterator i = p->begin(); i != p->end(); i++, count++) { - msg.deliverTo((*i)->queue); - if ((*i)->mgmtBinding != 0) - (*i)->mgmtBinding->inc_msgMatched(); - } - } - - if(!count){ - QPID_LOG(info, "DirectExchange " << getName() << " could not route message with key " << routingKey - << "; no matching binding found"); - if (mgmtExchange != 0) { - mgmtExchange->inc_msgDrops(); - mgmtExchange->inc_byteDrops(msg.contentSize()); - } - } else { - if (mgmtExchange != 0) { - mgmtExchange->inc_msgRoutes(count); - mgmtExchange->inc_byteRoutes(count * msg.contentSize()); - } - } - - if (mgmtExchange != 0) { - mgmtExchange->inc_msgReceives(); - mgmtExchange->inc_byteReceives(msg.contentSize()); + b = bindings[routingKey].queues.snapshot(); } + doRoute(msg, b); } diff --git a/qpid/cpp/src/qpid/broker/DtxAck.cpp b/qpid/cpp/src/qpid/broker/DtxAck.cpp index b189ef4cdb..bca3f90bbe 100644 --- a/qpid/cpp/src/qpid/broker/DtxAck.cpp +++ b/qpid/cpp/src/qpid/broker/DtxAck.cpp @@ -48,12 +48,26 @@ bool DtxAck::prepare(TransactionContext* ctxt) throw() void DtxAck::commit() throw() { - for_each(pending.begin(), pending.end(), mem_fun_ref(&DeliveryRecord::committed)); - pending.clear(); + try { + for_each(pending.begin(), pending.end(), mem_fun_ref(&DeliveryRecord::committed)); + pending.clear(); + } catch (const std::exception& e) { + QPID_LOG(error, "Failed to commit: " << e.what()); + } catch(...) { + QPID_LOG(error, "Failed to commit (unknown error)"); + } + } void DtxAck::rollback() throw() { - for_each(pending.begin(), pending.end(), mem_fun_ref(&DeliveryRecord::requeue)); - pending.clear(); + try { + for_each(pending.begin(), pending.end(), mem_fun_ref(&DeliveryRecord::requeue)); + pending.clear(); + } catch (const std::exception& e) { + QPID_LOG(error, "Failed to complete rollback: " << e.what()); + } catch(...) { + QPID_LOG(error, "Failed to complete rollback (unknown error)"); + } + } diff --git a/qpid/cpp/src/qpid/broker/Exchange.cpp b/qpid/cpp/src/qpid/broker/Exchange.cpp index 90d81b81c6..757127eef2 100644 --- a/qpid/cpp/src/qpid/broker/Exchange.cpp +++ b/qpid/cpp/src/qpid/broker/Exchange.cpp @@ -76,6 +76,40 @@ Exchange::PreRoute::~PreRoute(){ } } +void Exchange::doRoute(Deliverable& msg, ConstBindingList b) +{ + int count = 0; + + if (b.get()) { + // Block the content release if the message is transient AND there is more than one binding + if (!msg.getMessage().isPersistent() && b->size() > 1) + msg.getMessage().blockContentRelease(); + + for(std::vector::const_iterator i = b->begin(); i != b->end(); i++, count++) { + msg.deliverTo((*i)->queue); + if ((*i)->mgmtBinding != 0) + (*i)->mgmtBinding->inc_msgMatched(); + } + } + + if (mgmtExchange != 0) + { + mgmtExchange->inc_msgReceives (); + mgmtExchange->inc_byteReceives (msg.contentSize ()); + if (count == 0) + { + //QPID_LOG(warning, "Exchange " << getName() << " could not route message; no matching binding found"); + mgmtExchange->inc_msgDrops (); + mgmtExchange->inc_byteDrops (msg.contentSize ()); + } + else + { + mgmtExchange->inc_msgRoutes (count); + mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); + } + } +} + void Exchange::routeIVE(){ if (ive && lastMsg.get()){ DeliverableMessage dmsg(lastMsg); diff --git a/qpid/cpp/src/qpid/broker/Exchange.h b/qpid/cpp/src/qpid/broker/Exchange.h index c1e878200f..9bea376c28 100644 --- a/qpid/cpp/src/qpid/broker/Exchange.h +++ b/qpid/cpp/src/qpid/broker/Exchange.h @@ -79,6 +79,9 @@ protected: Exchange* parent; }; + typedef boost::shared_ptr > > ConstBindingList; + typedef boost::shared_ptr< std::vector > > BindingList; + void doRoute(Deliverable& msg, ConstBindingList b); void routeIVE(); diff --git a/qpid/cpp/src/qpid/broker/FanOutExchange.cpp b/qpid/cpp/src/qpid/broker/FanOutExchange.cpp index e9007ba682..b7d46a33fe 100644 --- a/qpid/cpp/src/qpid/broker/FanOutExchange.cpp +++ b/qpid/cpp/src/qpid/broker/FanOutExchange.cpp @@ -106,36 +106,12 @@ bool FanOutExchange::unbind(Queue::shared_ptr queue, const string& /*key*/, cons return true; } -void FanOutExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* /*args*/){ +void FanOutExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* /*args*/) +{ PreRoute pr(msg, this); - uint32_t count(0); - - BindingsArray::ConstPtr p = bindings.snapshot(); - if (p.get()){ - for(std::vector::const_iterator i = p->begin(); i != p->end(); ++i, count++){ - msg.deliverTo((*i)->queue); - if ((*i)->mgmtBinding != 0) - (*i)->mgmtBinding->inc_msgMatched (); - } - } - - if (mgmtExchange != 0) - { - mgmtExchange->inc_msgReceives (); - mgmtExchange->inc_byteReceives (msg.contentSize ()); - if (count == 0) - { - mgmtExchange->inc_msgDrops (); - mgmtExchange->inc_byteDrops (msg.contentSize ()); - } - else - { - mgmtExchange->inc_msgRoutes (count); - mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); - } - } + doRoute(msg, bindings.snapshot()); } - + bool FanOutExchange::isBound(Queue::shared_ptr queue, const string* const, const FieldTable* const) { BindingsArray::ConstPtr ptr = bindings.snapshot(); diff --git a/qpid/cpp/src/qpid/broker/HeadersExchange.cpp b/qpid/cpp/src/qpid/broker/HeadersExchange.cpp index c628c44909..a7c90156e1 100644 --- a/qpid/cpp/src/qpid/broker/HeadersExchange.cpp +++ b/qpid/cpp/src/qpid/broker/HeadersExchange.cpp @@ -104,7 +104,8 @@ bool HeadersExchange::unbind(Queue::shared_ptr queue, const string& bindingKey, } -void HeadersExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* args){ +void HeadersExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* args) +{ if (!args) { //can't match if there were no headers passed in if (mgmtExchange != 0) { @@ -118,31 +119,17 @@ void HeadersExchange::route(Deliverable& msg, const string& /*routingKey*/, cons PreRoute pr(msg, this); - uint32_t count(0); - - Bindings::ConstPtr p = bindings.snapshot(); - if (p.get()){ + ConstBindingList p = bindings.snapshot(); + BindingList b(new std::vector >); + if (p.get()) + { for (std::vector::const_iterator i = p->begin(); i != p->end(); ++i) { if (match((*i)->args, *args)) { - msg.deliverTo((*i)->queue); - count++; - if ((*i)->mgmtBinding != 0) - (*i)->mgmtBinding->inc_msgMatched(); + b->push_back(*i); } } } - - if (mgmtExchange != 0) { - mgmtExchange->inc_msgReceives(); - mgmtExchange->inc_byteReceives(msg.contentSize()); - if (count == 0) { - mgmtExchange->inc_msgDrops(); - mgmtExchange->inc_byteDrops(msg.contentSize()); - } else { - mgmtExchange->inc_msgRoutes(count); - mgmtExchange->inc_byteRoutes(count * msg.contentSize()); - } - } + doRoute(msg, b); } diff --git a/qpid/cpp/src/qpid/broker/Message.cpp b/qpid/cpp/src/qpid/broker/Message.cpp index 7360010192..e2799b0bff 100644 --- a/qpid/cpp/src/qpid/broker/Message.cpp +++ b/qpid/cpp/src/qpid/broker/Message.cpp @@ -49,7 +49,7 @@ TransferAdapter Message::TRANSFER; Message::Message(const framing::SequenceNumber& id) : frames(id), persistenceId(0), redelivered(false), loaded(false), staged(false), forcePersistentPolicy(false), publisher(0), adapter(0), - expiration(FAR_FUTURE), enqueueCallback(0), dequeueCallback(0) {} + expiration(FAR_FUTURE), enqueueCallback(0), dequeueCallback(0), requiredCredit(0) {} Message::~Message() { @@ -98,7 +98,7 @@ const FieldTable* Message::getApplicationHeaders() const return getAdapter().getApplicationHeaders(frames); } -bool Message::isPersistent() +bool Message::isPersistent() const { return (getAdapter().isPersistent(frames) || forcePersistentPolicy); } @@ -108,12 +108,16 @@ bool Message::requiresAccept() return getAdapter().requiresAccept(frames); } -uint32_t Message::getRequiredCredit() const +uint32_t Message::getRequiredCredit() { - //add up payload for all header and content frames in the frameset - SumBodySize sum; - frames.map_if(sum, TypeFilter2()); - return sum.getSize(); + sys::Mutex::ScopedLock l(lock); + if (!requiredCredit) { + //add up payload for all header and content frames in the frameset + SumBodySize sum; + frames.map_if(sum, TypeFilter2()); + requiredCredit = sum.getSize(); + } + return requiredCredit; } void Message::encode(framing::Buffer& buffer) const @@ -181,17 +185,31 @@ void Message::decodeContent(framing::Buffer& buffer) loaded = true; } -void Message::releaseContent(MessageStore* _store) +void Message::tryReleaseContent() { - if (!store) { - store = _store; + if (checkContentReleasable()) { + releaseContent(); } +} + +void Message::releaseContent(MessageStore* s) +{ + //deprecated, use setStore(store); releaseContent(); instead + if (!store) setStore(s); + releaseContent(); +} + +void Message::releaseContent() +{ + sys::Mutex::ScopedLock l(lock); if (store) { if (!getPersistenceId()) { intrusive_ptr pmsg(this); store->stage(pmsg); staged = true; } + //ensure required credit is cached before content frames are released + getRequiredCredit(); //remove any content frames from the frameset frames.remove(TypeFilter()); setContentReleased(); @@ -211,31 +229,29 @@ void Message::destroy() bool Message::getContentFrame(const Queue& queue, AMQFrame& frame, uint16_t maxContentSize, uint64_t offset) const { - if (isContentReleased()) { - intrusive_ptr pmsg(this); - - bool done = false; - string& data = frame.castBody()->getData(); - store->loadContent(queue, pmsg, data, offset, maxContentSize); - done = data.size() < maxContentSize; - frame.setBof(false); - frame.setEof(true); - QPID_LOG(debug, "loaded frame" << frame); - if (offset > 0) { - frame.setBos(false); - } - if (!done) { - frame.setEos(false); - } else return false; - return true; + intrusive_ptr pmsg(this); + + bool done = false; + string& data = frame.castBody()->getData(); + store->loadContent(queue, pmsg, data, offset, maxContentSize); + done = data.size() < maxContentSize; + frame.setBof(false); + frame.setEof(true); + QPID_LOG(debug, "loaded frame" << frame); + if (offset > 0) { + frame.setBos(false); } - else return false; + if (!done) { + frame.setEos(false); + } else return false; + return true; } void Message::sendContent(const Queue& queue, framing::FrameHandler& out, uint16_t maxFrameSize) const { + sys::Mutex::ScopedLock l(lock); if (isContentReleased() && !frames.isComplete()) { - + sys::Mutex::ScopedUnlock u(lock); uint16_t maxContentSize = maxFrameSize - AMQFrame::frameOverhead(); bool morecontent = true; for (uint64_t offset = 0; morecontent; offset += maxContentSize) @@ -373,28 +389,36 @@ void Message::setReplacementMessage(boost::intrusive_ptr msg, const Que } void Message::allEnqueuesComplete() { - MessageCallback* cb = 0; - { - sys::Mutex::ScopedLock l(lock); - std::swap(cb, enqueueCallback); - } + sys::Mutex::ScopedLock l(callbackLock); + MessageCallback* cb = enqueueCallback; if (cb && *cb) (*cb)(intrusive_ptr(this)); } void Message::allDequeuesComplete() { - MessageCallback* cb = 0; - { - sys::Mutex::ScopedLock l(lock); - std::swap(cb, dequeueCallback); - } + sys::Mutex::ScopedLock l(callbackLock); + MessageCallback* cb = dequeueCallback; if (cb && *cb) (*cb)(intrusive_ptr(this)); } -void Message::setEnqueueCompleteCallback(MessageCallback& cb) { enqueueCallback = &cb; } -void Message::resetEnqueueCompleteCallback() { enqueueCallback = 0; } +void Message::setEnqueueCompleteCallback(MessageCallback& cb) { + sys::Mutex::ScopedLock l(callbackLock); + enqueueCallback = &cb; +} + +void Message::resetEnqueueCompleteCallback() { + sys::Mutex::ScopedLock l(callbackLock); + enqueueCallback = 0; +} -void Message::setDequeueCompleteCallback(MessageCallback& cb) { dequeueCallback = &cb; } -void Message::resetDequeueCompleteCallback() { dequeueCallback = 0; } +void Message::setDequeueCompleteCallback(MessageCallback& cb) { + sys::Mutex::ScopedLock l(callbackLock); + dequeueCallback = &cb; +} + +void Message::resetDequeueCompleteCallback() { + sys::Mutex::ScopedLock l(callbackLock); + dequeueCallback = 0; +} framing::FieldTable& Message::getOrInsertHeaders() { diff --git a/qpid/cpp/src/qpid/broker/Message.h b/qpid/cpp/src/qpid/broker/Message.h index e4d09b1042..3894960c95 100644 --- a/qpid/cpp/src/qpid/broker/Message.h +++ b/qpid/cpp/src/qpid/broker/Message.h @@ -74,7 +74,7 @@ public: bool isImmediate() const; QPID_BROKER_EXTERN const framing::FieldTable* getApplicationHeaders() const; framing::FieldTable& getOrInsertHeaders(); - QPID_BROKER_EXTERN bool isPersistent(); + QPID_BROKER_EXTERN bool isPersistent() const; bool requiresAccept(); QPID_BROKER_EXTERN void setTimestamp(const boost::intrusive_ptr& e); @@ -108,7 +108,7 @@ public: return frames.isA(); } - uint32_t getRequiredCredit() const; + uint32_t getRequiredCredit(); void encode(framing::Buffer& buffer) const; void encodeContent(framing::Buffer& buffer) const; @@ -129,12 +129,9 @@ public: QPID_BROKER_EXTERN void decodeHeader(framing::Buffer& buffer); QPID_BROKER_EXTERN void decodeContent(framing::Buffer& buffer); - /** - * Releases the in-memory content data held by this - * message. Must pass in a store from which the data can - * be reloaded. - */ - void releaseContent(MessageStore* store); + void QPID_BROKER_EXTERN tryReleaseContent(); + void releaseContent(); + void releaseContent(MessageStore* s);//deprecated, use 'setStore(store); releaseContent();' instead void destroy(); bool getContentFrame(const Queue& queue, framing::AMQFrame& frame, uint16_t maxContentSize, uint64_t offset) const; @@ -187,8 +184,12 @@ public: mutable Replacement replacement; mutable boost::intrusive_ptr empty; + + sys::Mutex callbackLock; MessageCallback* enqueueCallback; MessageCallback* dequeueCallback; + + uint32_t requiredCredit; static std::string updateDestination; }; diff --git a/qpid/cpp/src/qpid/broker/MessageBuilder.cpp b/qpid/cpp/src/qpid/broker/MessageBuilder.cpp index 14b233fd6c..b1a2b77b05 100644 --- a/qpid/cpp/src/qpid/broker/MessageBuilder.cpp +++ b/qpid/cpp/src/qpid/broker/MessageBuilder.cpp @@ -80,7 +80,7 @@ void MessageBuilder::handle(AMQFrame& frame) && !NullMessageStore::isNullStore(store) && message->getExchangeName() != QPID_MANAGEMENT /* don't stage mgnt messages */) { - message->releaseContent(store); + message->releaseContent(); staging = true; } } @@ -96,6 +96,7 @@ void MessageBuilder::end() void MessageBuilder::start(const SequenceNumber& id) { message = intrusive_ptr(new Message(id)); + message->setStore(store); state = METHOD; staging = false; } diff --git a/qpid/cpp/src/qpid/broker/MessageStoreModule.cpp b/qpid/cpp/src/qpid/broker/MessageStoreModule.cpp index 0b8a5db1c7..5f7cceebd3 100644 --- a/qpid/cpp/src/qpid/broker/MessageStoreModule.cpp +++ b/qpid/cpp/src/qpid/broker/MessageStoreModule.cpp @@ -32,11 +32,11 @@ using qpid::framing::FieldTable; namespace qpid { namespace broker { -MessageStoreModule::MessageStoreModule(MessageStore* _store) : store(_store) {} +MessageStoreModule::MessageStoreModule(boost::shared_ptr& _store) + : store(_store) {} MessageStoreModule::~MessageStoreModule() { - delete store; } bool MessageStoreModule::init(const Options*) { return true; } @@ -173,7 +173,7 @@ void MessageStoreModule::collectPreparedXids(std::set& xids) bool MessageStoreModule::isNull() const { - return NullMessageStore::isNullStore(store); + return NullMessageStore::isNullStore(store.get()); } }} // namespace qpid::broker diff --git a/qpid/cpp/src/qpid/broker/MessageStoreModule.h b/qpid/cpp/src/qpid/broker/MessageStoreModule.h index 02cbd13cf1..56b5a3c1ae 100644 --- a/qpid/cpp/src/qpid/broker/MessageStoreModule.h +++ b/qpid/cpp/src/qpid/broker/MessageStoreModule.h @@ -26,6 +26,7 @@ #include "qpid/broker/RecoveryManager.h" #include +#include namespace qpid { namespace broker { @@ -35,9 +36,9 @@ namespace broker { */ class MessageStoreModule : public MessageStore { - MessageStore* store; + boost::shared_ptr store; public: - MessageStoreModule(MessageStore* store); + MessageStoreModule(boost::shared_ptr& store); bool init(const Options* options); void truncateInit(const bool pushDownStoreFiles = false); diff --git a/qpid/cpp/src/qpid/broker/PersistableMessage.cpp b/qpid/cpp/src/qpid/broker/PersistableMessage.cpp index 2ef223aa81..303a0501f4 100644 --- a/qpid/cpp/src/qpid/broker/PersistableMessage.cpp +++ b/qpid/cpp/src/qpid/broker/PersistableMessage.cpp @@ -36,7 +36,6 @@ PersistableMessage::~PersistableMessage() {} PersistableMessage::PersistableMessage() : asyncEnqueueCounter(0), asyncDequeueCounter(0), - contentReleased(false), store(0) {} @@ -59,9 +58,15 @@ void PersistableMessage::flush() } } -void PersistableMessage::setContentReleased() {contentReleased = true; } +void PersistableMessage::setContentReleased() +{ + contentReleaseState.released = true; +} -bool PersistableMessage::isContentReleased()const { return contentReleased; } +bool PersistableMessage::isContentReleased() const +{ + return contentReleaseState.released; +} bool PersistableMessage::isEnqueueComplete() { sys::ScopedLock l(asyncEnqueueLock); @@ -153,6 +158,26 @@ void PersistableMessage::dequeueAsync() { asyncDequeueCounter++; } +PersistableMessage::ContentReleaseState::ContentReleaseState() : blocked(false), requested(false), released(false) {} + +void PersistableMessage::setStore(MessageStore* s) +{ + store = s; +} + +void PersistableMessage::requestContentRelease() +{ + contentReleaseState.requested = true; +} +void PersistableMessage::blockContentRelease() +{ + contentReleaseState.blocked = true; +} +bool PersistableMessage::checkContentReleasable() +{ + return contentReleaseState.requested && !contentReleaseState.blocked; +} + }} diff --git a/qpid/cpp/src/qpid/broker/PersistableMessage.h b/qpid/cpp/src/qpid/broker/PersistableMessage.h index 0274b41375..2576e266d2 100644 --- a/qpid/cpp/src/qpid/broker/PersistableMessage.h +++ b/qpid/cpp/src/qpid/broker/PersistableMessage.h @@ -68,8 +68,16 @@ class PersistableMessage : public Persistable void enqueueAsync(); void dequeueAsync(); - bool contentReleased; syncList synclist; + struct ContentReleaseState + { + bool blocked; + bool requested; + bool released; + + ContentReleaseState(); + }; + ContentReleaseState contentReleaseState; protected: /** Called when all enqueues are complete for this message. */ @@ -96,8 +104,15 @@ class PersistableMessage : public Persistable void flush(); - bool isContentReleased() const; - + bool QPID_BROKER_EXTERN isContentReleased() const; + + void QPID_BROKER_EXTERN setStore(MessageStore*); + void requestContentRelease(); + void blockContentRelease(); + bool checkContentReleasable(); + + virtual QPID_BROKER_EXTERN bool isPersistent() const = 0; + QPID_BROKER_EXTERN bool isEnqueueComplete(); QPID_BROKER_EXTERN void enqueueComplete(); diff --git a/qpid/cpp/src/qpid/broker/Queue.cpp b/qpid/cpp/src/qpid/broker/Queue.cpp index b2a8e223c5..86de96468d 100644 --- a/qpid/cpp/src/qpid/broker/Queue.cpp +++ b/qpid/cpp/src/qpid/broker/Queue.cpp @@ -181,6 +181,8 @@ void Queue::deliver(boost::intrusive_ptr& msg){ void Queue::recover(boost::intrusive_ptr& msg){ + if (policy.get()) policy->recoverEnqueued(msg); + push(msg, true); if (store){ // setup synclist for recovered messages, so they don't get re-stored on lastNodeFailure @@ -206,11 +208,10 @@ void Queue::process(boost::intrusive_ptr& msg){ } void Queue::requeue(const QueuedMessage& msg){ - if (!isEnqueued(msg)) return; - QueueListeners::NotificationSet copy; { Mutex::ScopedLock locker(messageLock); + if (!isEnqueued(msg)) return; msg.payload->enqueueComplete(); // mark the message as enqueued messages.push_front(msg); listeners.populate(copy); @@ -563,16 +564,10 @@ void Queue::popMsg(QueuedMessage& qmsg) } void Queue::push(boost::intrusive_ptr& msg, bool isRecovery){ - Messages dequeues; QueueListeners::NotificationSet copy; { Mutex::ScopedLock locker(messageLock); QueuedMessage qm(this, msg, ++sequence); - if (policy.get()) { - policy->tryEnqueue(qm); - //depending on policy, may have some dequeues - if (!isRecovery) pendingDequeues.swap(dequeues); - } if (insertSeqNo) msg->getOrInsertHeaders().setInt64(seqNoKey, sequence); LVQ::iterator i; @@ -606,12 +601,11 @@ void Queue::push(boost::intrusive_ptr& msg, bool isRecovery){ if (eventMgr) eventMgr->enqueued(qm); else QPID_LOG(warning, "Enqueue manager not set, events not generated for " << getName()); } + if (policy.get()) { + policy->enqueued(qm); + } } copy.notify(); - if (!dequeues.empty()) { - //depending on policy, may have some dequeues - for_each(dequeues.begin(), dequeues.end(), boost::bind(&Queue::dequeue, this, (TransactionContext*) 0, _1)); - } } QueuedMessage Queue::getFront() @@ -697,8 +691,19 @@ void Queue::setLastNodeFailure() } // return true if store exists, -bool Queue::enqueue(TransactionContext* ctxt, boost::intrusive_ptr msg) +bool Queue::enqueue(TransactionContext* ctxt, boost::intrusive_ptr msg, bool suppressPolicyCheck) { + if (policy.get() && !suppressPolicyCheck) { + Messages dequeues; + { + Mutex::ScopedLock locker(messageLock); + policy->tryEnqueue(msg); + policy->getPendingDequeues(dequeues); + } + //depending on policy, may have some dequeues that need to performed without holding the lock + for_each(dequeues.begin(), dequeues.end(), boost::bind(&Queue::dequeue, this, (TransactionContext*) 0, _1)); + } + if (inLastNodeFailure && persistLastNode){ msg->forcePersistent(); } @@ -707,15 +712,27 @@ bool Queue::enqueue(TransactionContext* ctxt, boost::intrusive_ptr msg) msg->addTraceId(traceId); } - if (msg->isPersistent() && store) { + if ((msg->isPersistent() || msg->checkContentReleasable()) && store) { msg->enqueueAsync(shared_from_this(), store); //increment to async counter -- for message sent to more than one queue boost::intrusive_ptr pmsg = boost::static_pointer_cast(msg); store->enqueue(ctxt, pmsg, *this); return true; } + if (!store) { + //Messages enqueued on a transient queue should be prevented + //from having their content released as it may not be + //recoverable by these queue for delivery + msg->blockContentRelease(); + } return false; } +void Queue::enqueueAborted(boost::intrusive_ptr msg) +{ + Mutex::ScopedLock locker(messageLock); + if (policy.get()) policy->enqueueAborted(msg); +} + // return true if store exists, bool Queue::dequeue(TransactionContext* ctxt, const QueuedMessage& msg) { @@ -726,7 +743,7 @@ bool Queue::dequeue(TransactionContext* ctxt, const QueuedMessage& msg) dequeued(msg); } } - if (msg.payload->isPersistent() && store) { + if ((msg.payload->isPersistent() || msg.payload->checkContentReleasable()) && store) { msg.payload->dequeueAsync(shared_from_this(), store); //increment to async counter -- for message sent to more than one queue boost::intrusive_ptr pmsg = boost::static_pointer_cast(msg.payload); store->dequeue(ctxt, pmsg, *this); @@ -781,22 +798,37 @@ void Queue::create(const FieldTable& _settings) void Queue::configure(const FieldTable& _settings, bool recovering) { - setPolicy(QueuePolicy::createQueuePolicy(_settings)); + + eventMode = _settings.getAsInt(qpidQueueEventGeneration); + + if (QueuePolicy::getType(_settings) == QueuePolicy::FLOW_TO_DISK && + (!store || NullMessageStore::isNullStore(store) || (eventMode && eventMgr && !eventMgr->isSync()) )) { + if ( NullMessageStore::isNullStore(store)) { + QPID_LOG(warning, "Flow to disk not valid for non-persisted queue:" << getName()); + } else if (eventMgr && !eventMgr->isSync() ) { + QPID_LOG(warning, "Flow to disk not valid with async Queue Events:" << getName()); + } + FieldTable copy(_settings); + copy.erase(QueuePolicy::typeKey); + setPolicy(QueuePolicy::createQueuePolicy(getName(), copy)); + } else { + setPolicy(QueuePolicy::createQueuePolicy(getName(), _settings)); + } //set this regardless of owner to allow use of no-local with exclusive consumers also noLocal = _settings.get(qpidNoLocal); - QPID_LOG(debug, "Configured queue with no-local=" << noLocal); + QPID_LOG(debug, "Configured queue " << getName() << " with no-local=" << noLocal); lastValueQueue= _settings.get(qpidLastValueQueue); - if (lastValueQueue) QPID_LOG(debug, "Configured queue as Last Value Queue"); + if (lastValueQueue) QPID_LOG(debug, "Configured queue as Last Value Queue for: " << getName()); lastValueQueueNoBrowse = _settings.get(qpidLastValueQueueNoBrowse); if (lastValueQueueNoBrowse){ - QPID_LOG(debug, "Configured queue as Last Value Queue No Browse"); + QPID_LOG(debug, "Configured queue as Last Value Queue No Browse for: " << getName()); lastValueQueue = lastValueQueueNoBrowse; } persistLastNode= _settings.get(qpidPersistLastNode); - if (persistLastNode) QPID_LOG(debug, "Configured queue to Persist data if cluster fails to one node"); + if (persistLastNode) QPID_LOG(debug, "Configured queue to Persist data if cluster fails to one node for: " << getName()); traceId = _settings.getAsString(qpidTraceIdentity); std::string excludeList = _settings.getAsString(qpidTraceExclude); @@ -806,8 +838,6 @@ void Queue::configure(const FieldTable& _settings, bool recovering) QPID_LOG(debug, "Configured queue " << getName() << " with qpid.trace.id='" << traceId << "' and qpid.trace.exclude='"<< excludeList << "' i.e. " << traceExclude.size() << " elements"); - eventMode = _settings.getAsInt(qpidQueueEventGeneration); - FieldTable::ValuePtr p =_settings.get(qpidInsertSequenceNumbers); if (p && p->convertsTo()) insertSequenceNumbers(p->get()); @@ -975,19 +1005,6 @@ void Queue::setExternalQueueStore(ExternalQueueStore* inst) { } } -bool Queue::releaseMessageContent(const QueuedMessage& m) -{ - if (store && !NullMessageStore::isNullStore(store)) { - QPID_LOG(debug, "Message " << m.position << " on " << name << " released from memory"); - m.payload->releaseContent(store); - return true; - } else { - QPID_LOG(warning, "Message " << m.position << " on " << name - << " cannot be released from memory as the queue is not durable"); - return false; - } -} - ManagementObject* Queue::GetManagementObject (void) const { return (ManagementObject*) mgmtObject; @@ -1044,11 +1061,12 @@ void Queue::insertSequenceNumbers(const std::string& key) void Queue::enqueued(const QueuedMessage& m) { if (m.payload) { - if (policy.get()) policy->tryEnqueue(m); - mgntEnqStats(m.payload); - if (m.payload->isPersistent()) { - enqueue ( 0, m.payload ); + if (policy.get()) { + policy->recoverEnqueued(m.payload); + policy->enqueued(m); } + mgntEnqStats(m.payload); + enqueue ( 0, m.payload, true ); } else { QPID_LOG(warning, "Queue informed of enqueued message that has no payload"); } @@ -1059,10 +1077,4 @@ bool Queue::isEnqueued(const QueuedMessage& msg) return !policy.get() || policy->isEnqueued(msg); } -void Queue::addPendingDequeue(const QueuedMessage& msg) -{ - //assumes lock is held - true at present but rather nasty as this is a public method - pendingDequeues.push_back(msg); -} - QueueListeners& Queue::getListeners() { return listeners; } diff --git a/qpid/cpp/src/qpid/broker/Queue.h b/qpid/cpp/src/qpid/broker/Queue.h index 77799fd967..661e46f619 100644 --- a/qpid/cpp/src/qpid/broker/Queue.h +++ b/qpid/cpp/src/qpid/broker/Queue.h @@ -239,7 +239,8 @@ namespace qpid { QPID_BROKER_EXTERN void setLastNodeFailure(); QPID_BROKER_EXTERN void clearLastNodeFailure(); - bool enqueue(TransactionContext* ctxt, boost::intrusive_ptr msg); + bool enqueue(TransactionContext* ctxt, boost::intrusive_ptr msg, bool suppressPolicyCheck = false); + void enqueueAborted(boost::intrusive_ptr msg); /** * dequeue from store (only done once messages is acknowledged) */ @@ -315,8 +316,6 @@ namespace qpid { bindings.eachBinding(f); } - bool releaseMessageContent(const QueuedMessage&); - void popMsg(QueuedMessage& qmsg); /** Set the position sequence number for the next message on the queue. @@ -335,18 +334,6 @@ namespace qpid { */ void recoveryComplete(); - /** - * This is a hack to avoid deadlocks in durable ring - * queues. It is used for dequeueing messages in response - * to an enqueue while avoid holding lock over call to - * store. - * - * Assumes messageLock is held - true for curent use case - * (QueuePolicy::tryEnqueue()) but rather nasty as this is a public - * method - **/ - void addPendingDequeue(const QueuedMessage &msg); - // For cluster update QueueListeners& getListeners(); }; diff --git a/qpid/cpp/src/qpid/broker/QueueEvents.cpp b/qpid/cpp/src/qpid/broker/QueueEvents.cpp index 6df869673d..bba054b0b8 100644 --- a/qpid/cpp/src/qpid/broker/QueueEvents.cpp +++ b/qpid/cpp/src/qpid/broker/QueueEvents.cpp @@ -25,25 +25,41 @@ namespace qpid { namespace broker { -QueueEvents::QueueEvents(const boost::shared_ptr& poller) : - eventQueue(boost::bind(&QueueEvents::handle, this, _1), poller), enabled(true) +QueueEvents::QueueEvents(const boost::shared_ptr& poller, bool isSync) : + eventQueue(boost::bind(&QueueEvents::handle, this, _1), poller), enabled(true), sync(isSync) { - eventQueue.start(); + if (!sync) eventQueue.start(); } QueueEvents::~QueueEvents() { - eventQueue.stop(); + if (!sync) eventQueue.stop(); } void QueueEvents::enqueued(const QueuedMessage& m) { - if (enabled) eventQueue.push(Event(ENQUEUE, m)); + if (enabled) { + Event enq(ENQUEUE, m); + if (sync) { + for (Listeners::iterator j = listeners.begin(); j != listeners.end(); j++) + j->second(enq); + } else { + eventQueue.push(enq); + } + } } void QueueEvents::dequeued(const QueuedMessage& m) { - if (enabled) eventQueue.push(Event(DEQUEUE, m)); + if (enabled) { + Event deq(DEQUEUE, m); + if (sync) { + for (Listeners::iterator j = listeners.begin(); j != listeners.end(); j++) + j->second(deq); + } else { + eventQueue.push(Event(DEQUEUE, m)); + } + } } void QueueEvents::registerListener(const std::string& id, const EventListener& listener) @@ -70,15 +86,16 @@ QueueEvents::EventQueue::Batch::const_iterator QueueEvents::handle(const EventQueue::Batch& events) { qpid::sys::Mutex::ScopedLock l(lock); for (EventQueue::Batch::const_iterator i = events.begin(); i != events.end(); ++i) { - for (Listeners::iterator j = listeners.begin(); j != listeners.end(); j++) - j->second(*i); + for (Listeners::iterator j = listeners.begin(); j != listeners.end(); j++) { + j->second(*i); + } } return events.end(); } void QueueEvents::shutdown() { - if (!eventQueue.empty() && !listeners.empty()) eventQueue.shutdown(); + if (!sync && !eventQueue.empty() && !listeners.empty()) eventQueue.shutdown(); } void QueueEvents::enable() @@ -93,6 +110,12 @@ void QueueEvents::disable() QPID_LOG(debug, "Queue events disabled"); } +bool QueueEvents::isSync() +{ + return sync; +} + + QueueEvents::Event::Event(EventType t, const QueuedMessage& m) : type(t), msg(m) {} diff --git a/qpid/cpp/src/qpid/broker/QueueEvents.h b/qpid/cpp/src/qpid/broker/QueueEvents.h index 6826c6e79a..c42752133e 100644 --- a/qpid/cpp/src/qpid/broker/QueueEvents.h +++ b/qpid/cpp/src/qpid/broker/QueueEvents.h @@ -54,7 +54,7 @@ class QueueEvents typedef boost::function EventListener; - QPID_BROKER_EXTERN QueueEvents(const boost::shared_ptr& poller); + QPID_BROKER_EXTERN QueueEvents(const boost::shared_ptr& poller, bool isSync = false); QPID_BROKER_EXTERN ~QueueEvents(); QPID_BROKER_EXTERN void enqueued(const QueuedMessage&); QPID_BROKER_EXTERN void dequeued(const QueuedMessage&); @@ -65,6 +65,7 @@ class QueueEvents void disable(); //process all outstanding events QPID_BROKER_EXTERN void shutdown(); + QPID_BROKER_EXTERN bool isSync(); private: typedef qpid::sys::PollableQueue EventQueue; typedef std::map Listeners; @@ -73,6 +74,7 @@ class QueueEvents Listeners listeners; volatile bool enabled; qpid::sys::Mutex lock;//protect listeners from concurrent access + bool sync; EventQueue::Batch::const_iterator handle(const EventQueue::Batch& e); diff --git a/qpid/cpp/src/qpid/broker/QueuePolicy.cpp b/qpid/cpp/src/qpid/broker/QueuePolicy.cpp index 39afe90134..a8aa674c53 100644 --- a/qpid/cpp/src/qpid/broker/QueuePolicy.cpp +++ b/qpid/cpp/src/qpid/broker/QueuePolicy.cpp @@ -28,8 +28,8 @@ using namespace qpid::broker; using namespace qpid::framing; -QueuePolicy::QueuePolicy(uint32_t _maxCount, uint64_t _maxSize, const std::string& _type) : - maxCount(_maxCount), maxSize(_maxSize), type(_type), count(0), size(0), policyExceeded(false) {} +QueuePolicy::QueuePolicy(const std::string& _name, uint32_t _maxCount, uint64_t _maxSize, const std::string& _type) : + maxCount(_maxCount), maxSize(_maxSize), type(_type), count(0), size(0), policyExceeded(false), name(_name) {} void QueuePolicy::enqueued(uint64_t _size) { @@ -39,18 +39,15 @@ void QueuePolicy::enqueued(uint64_t _size) void QueuePolicy::dequeued(uint64_t _size) { - //Note: underflow detection is not reliable in the face of - //concurrent updates (at present locking in Queue.cpp prevents - //these anyway); updates are atomic and are safe regardless. if (maxCount) { - if (count.get() > 0) { + if (count > 0) { --count; } else { throw Exception(QPID_MSG("Attempted count underflow on dequeue(" << _size << "): " << *this)); } } if (maxSize) { - if (_size > size.get()) { + if (_size > size) { throw Exception(QPID_MSG("Attempted size underflow on dequeue(" << _size << "): " << *this)); } else { size -= _size; @@ -58,47 +55,47 @@ void QueuePolicy::dequeued(uint64_t _size) } } -bool QueuePolicy::checkLimit(const QueuedMessage& m) +bool QueuePolicy::checkLimit(boost::intrusive_ptr m) { - bool sizeExceeded = maxSize && (size.get() + m.payload->contentSize()) > maxSize; - bool countExceeded = maxCount && (count.get() + 1) > maxCount; + bool sizeExceeded = maxSize && (size + m->contentSize()) > maxSize; + bool countExceeded = maxCount && (count + 1) > maxCount; bool exceeded = sizeExceeded || countExceeded; if (exceeded) { if (!policyExceeded) { - policyExceeded = true; - if (m.queue) { - if (sizeExceeded) QPID_LOG(info, "Queue cumulative message size exceeded policy for " << m.queue->getName()); - if (countExceeded) QPID_LOG(info, "Queue message count exceeded policy for " << m.queue->getName()); - } + policyExceeded = true; + if (sizeExceeded) QPID_LOG(info, "Queue cumulative message size exceeded policy for " << name); + if (countExceeded) QPID_LOG(info, "Queue message count exceeded policy for " << name); } } else { if (policyExceeded) { policyExceeded = false; - if (m.queue) { - QPID_LOG(info, "Queue cumulative message size and message count within policy for " << m.queue->getName()); - } + QPID_LOG(info, "Queue cumulative message size and message count within policy for " << name); } } return !exceeded; } -void QueuePolicy::tryEnqueue(const QueuedMessage& m) +void QueuePolicy::tryEnqueue(boost::intrusive_ptr m) { if (checkLimit(m)) { - enqueued(m); + enqueued(m->contentSize()); } else { - std::string queue = m.queue ? m.queue->getName() : std::string("unknown queue"); - throw ResourceLimitExceededException( - QPID_MSG("Policy exceeded on " << queue << " by message " << m.position - << " of size " << m.payload->contentSize() << " , policy: " << *this)); + throw ResourceLimitExceededException(QPID_MSG("Policy exceeded on " << name << ", policy: " << *this)); } } -void QueuePolicy::enqueued(const QueuedMessage& m) +void QueuePolicy::recoverEnqueued(boost::intrusive_ptr m) { - enqueued(m.payload->contentSize()); + enqueued(m->contentSize()); } +void QueuePolicy::enqueueAborted(boost::intrusive_ptr m) +{ + dequeued(m->contentSize()); +} + +void QueuePolicy::enqueued(const QueuedMessage&) {} + void QueuePolicy::dequeued(const QueuedMessage& m) { dequeued(m.payload->contentSize()); @@ -132,7 +129,7 @@ std::string QueuePolicy::getType(const FieldTable& settings) std::transform(t.begin(), t.end(), t.begin(), tolower); if (t == REJECT || t == FLOW_TO_DISK || t == RING || t == RING_STRICT) return t; } - return FLOW_TO_DISK; + return REJECT; } void QueuePolicy::setDefaultMaxSize(uint64_t s) @@ -140,6 +137,7 @@ void QueuePolicy::setDefaultMaxSize(uint64_t s) defaultMaxSize = s; } +void QueuePolicy::getPendingDequeues(Messages&) {} @@ -148,8 +146,8 @@ void QueuePolicy::encode(Buffer& buffer) const { buffer.putLong(maxCount); buffer.putLongLong(maxSize); - buffer.putLong(count.get()); - buffer.putLongLong(size.get()); + buffer.putLong(count); + buffer.putLongLong(size); } void QueuePolicy::decode ( Buffer& buffer ) @@ -179,16 +177,18 @@ const std::string QueuePolicy::RING("ring"); const std::string QueuePolicy::RING_STRICT("ring_strict"); uint64_t QueuePolicy::defaultMaxSize(0); -FlowToDiskPolicy::FlowToDiskPolicy(uint32_t _maxCount, uint64_t _maxSize) : - QueuePolicy(_maxCount, _maxSize, FLOW_TO_DISK) {} +FlowToDiskPolicy::FlowToDiskPolicy(const std::string& _name, uint32_t _maxCount, uint64_t _maxSize) : + QueuePolicy(_name, _maxCount, _maxSize, FLOW_TO_DISK) {} -bool FlowToDiskPolicy::checkLimit(const QueuedMessage& m) +bool FlowToDiskPolicy::checkLimit(boost::intrusive_ptr m) { - return QueuePolicy::checkLimit(m) || m.queue->releaseMessageContent(m); + if (!QueuePolicy::checkLimit(m)) m->requestContentRelease(); + return true; } -RingQueuePolicy::RingQueuePolicy(uint32_t _maxCount, uint64_t _maxSize, const std::string& _type) : - QueuePolicy(_maxCount, _maxSize, _type), strict(_type == RING_STRICT) {} +RingQueuePolicy::RingQueuePolicy(const std::string& _name, + uint32_t _maxCount, uint64_t _maxSize, const std::string& _type) : + QueuePolicy(_name, _maxCount, _maxSize, _type), strict(_type == RING_STRICT) {} bool before(const QueuedMessage& a, const QueuedMessage& b) { @@ -197,15 +197,12 @@ bool before(const QueuedMessage& a, const QueuedMessage& b) void RingQueuePolicy::enqueued(const QueuedMessage& m) { - QueuePolicy::enqueued(m); - qpid::sys::Mutex::ScopedLock l(lock); //need to insert in correct location based on position queue.insert(lower_bound(queue.begin(), queue.end(), m, before), m); } void RingQueuePolicy::dequeued(const QueuedMessage& m) { - qpid::sys::Mutex::ScopedLock l(lock); //find and remove m from queue if (find(m, pendingDequeues, true) || find(m, queue, true)) { //now update count and size @@ -215,49 +212,32 @@ void RingQueuePolicy::dequeued(const QueuedMessage& m) bool RingQueuePolicy::isEnqueued(const QueuedMessage& m) { - qpid::sys::Mutex::ScopedLock l(lock); //for non-strict ring policy, a message can be replaced (and //therefore dequeued) before it is accepted or released by //subscriber; need to detect this return find(m, pendingDequeues, false) || find(m, queue, false); } -bool RingQueuePolicy::checkLimit(const QueuedMessage& m) +bool RingQueuePolicy::checkLimit(boost::intrusive_ptr m) { if (QueuePolicy::checkLimit(m)) return true;//if haven't hit limit, ok to accept QueuedMessage oldest; - { - qpid::sys::Mutex::ScopedLock l(lock); - if (queue.empty()) { - QPID_LOG(debug, "Message too large for ring queue " - << (m.queue ? m.queue->getName() : std::string("unknown queue")) - << " [" << *this << "] " - << ": message size = " << m.payload->contentSize() << " bytes"); - return false; - } - oldest = queue.front(); + if (queue.empty()) { + QPID_LOG(debug, "Message too large for ring queue " << name + << " [" << *this << "] " + << ": message size = " << m->contentSize() << " bytes"); + return false; } + oldest = queue.front(); if (oldest.queue->acquire(oldest) || !strict) { - { - //TODO: fix this! In the current code, this method is - //only ever called with the Queue lock already taken. This - //should not be relied upon going forward however and - //clearly the locking in this class is insufficient as - //there is no guarantee that the message previously atthe - //front is still there. - qpid::sys::Mutex::ScopedLock l(lock); - queue.pop_front(); - pendingDequeues.push_back(oldest); - } - oldest.queue->addPendingDequeue(oldest); - QPID_LOG(debug, "Ring policy triggered in queue " - << (m.queue ? m.queue->getName() : std::string("unknown queue")) - << ": removed message " << oldest.position << " to make way for " << m.position); + queue.pop_front(); + pendingDequeues.push_back(oldest); + QPID_LOG(debug, "Ring policy triggered in " << name + << ": removed message " << oldest.position << " to make way for new message"); return true; } else { - QPID_LOG(debug, "Ring policy could not be triggered in queue " - << (m.queue ? m.queue->getName() : std::string("unknown queue")) + QPID_LOG(debug, "Ring policy could not be triggered in " << name << ": oldest message (seq-no=" << oldest.position << ") has been delivered but not yet acknowledged or requeued"); //in strict mode, if oldest message has been delivered (hence //cannot be acquired) but not yet acked, it should not be @@ -266,6 +246,11 @@ bool RingQueuePolicy::checkLimit(const QueuedMessage& m) } } +void RingQueuePolicy::getPendingDequeues(Messages& result) +{ + result = pendingDequeues; +} + bool RingQueuePolicy::find(const QueuedMessage& m, Messages& q, bool remove) { for (Messages::iterator i = q.begin(); i != q.end(); i++) { @@ -277,25 +262,36 @@ bool RingQueuePolicy::find(const QueuedMessage& m, Messages& q, bool remove) return false; } +std::auto_ptr QueuePolicy::createQueuePolicy(uint32_t maxCount, uint64_t maxSize, const std::string& type) +{ + return createQueuePolicy("", maxCount, maxSize, type); +} + std::auto_ptr QueuePolicy::createQueuePolicy(const qpid::framing::FieldTable& settings) +{ + return createQueuePolicy("", settings); +} + +std::auto_ptr QueuePolicy::createQueuePolicy(const std::string& name, const qpid::framing::FieldTable& settings) { uint32_t maxCount = getInt(settings, maxCountKey, 0); uint32_t maxSize = getInt(settings, maxSizeKey, defaultMaxSize); if (maxCount || maxSize) { - return createQueuePolicy(maxCount, maxSize, getType(settings)); + return createQueuePolicy(name, maxCount, maxSize, getType(settings)); } else { return std::auto_ptr(); } } -std::auto_ptr QueuePolicy::createQueuePolicy(uint32_t maxCount, uint64_t maxSize, const std::string& type) +std::auto_ptr QueuePolicy::createQueuePolicy(const std::string& name, + uint32_t maxCount, uint64_t maxSize, const std::string& type) { if (type == RING || type == RING_STRICT) { - return std::auto_ptr(new RingQueuePolicy(maxCount, maxSize, type)); + return std::auto_ptr(new RingQueuePolicy(name, maxCount, maxSize, type)); } else if (type == FLOW_TO_DISK) { - return std::auto_ptr(new FlowToDiskPolicy(maxCount, maxSize)); + return std::auto_ptr(new FlowToDiskPolicy(name, maxCount, maxSize)); } else { - return std::auto_ptr(new QueuePolicy(maxCount, maxSize, type)); + return std::auto_ptr(new QueuePolicy(name, maxCount, maxSize, type)); } } @@ -305,10 +301,10 @@ namespace qpid { std::ostream& operator<<(std::ostream& out, const QueuePolicy& p) { - if (p.maxSize) out << "size: max=" << p.maxSize << ", current=" << p.size.get(); + if (p.maxSize) out << "size: max=" << p.maxSize << ", current=" << p.size; else out << "size: unlimited"; out << "; "; - if (p.maxCount) out << "count: max=" << p.maxCount << ", current=" << p.count.get(); + if (p.maxCount) out << "count: max=" << p.maxCount << ", current=" << p.count; else out << "count: unlimited"; out << "; type=" << p.type; return out; diff --git a/qpid/cpp/src/qpid/broker/QueuePolicy.h b/qpid/cpp/src/qpid/broker/QueuePolicy.h index 54745876d5..b2937e94c7 100644 --- a/qpid/cpp/src/qpid/broker/QueuePolicy.h +++ b/qpid/cpp/src/qpid/broker/QueuePolicy.h @@ -40,14 +40,14 @@ class QueuePolicy uint32_t maxCount; uint64_t maxSize; const std::string type; - qpid::sys::AtomicValue count; - qpid::sys::AtomicValue size; + uint32_t count; + uint64_t size; bool policyExceeded; static int getInt(const qpid::framing::FieldTable& settings, const std::string& key, int defaultValue); - static std::string getType(const qpid::framing::FieldTable& settings); public: + typedef std::deque Messages; static QPID_BROKER_EXTERN const std::string maxCountKey; static QPID_BROKER_EXTERN const std::string maxSizeKey; static QPID_BROKER_EXTERN const std::string typeKey; @@ -57,27 +57,34 @@ class QueuePolicy static QPID_BROKER_EXTERN const std::string RING_STRICT; virtual ~QueuePolicy() {} - QPID_BROKER_EXTERN void tryEnqueue(const QueuedMessage&); + QPID_BROKER_EXTERN void tryEnqueue(boost::intrusive_ptr msg); + QPID_BROKER_EXTERN void recoverEnqueued(boost::intrusive_ptr msg); + QPID_BROKER_EXTERN void enqueueAborted(boost::intrusive_ptr msg); + virtual void enqueued(const QueuedMessage&); virtual void dequeued(const QueuedMessage&); virtual bool isEnqueued(const QueuedMessage&); - virtual bool checkLimit(const QueuedMessage&); QPID_BROKER_EXTERN void update(qpid::framing::FieldTable& settings); uint32_t getMaxCount() const { return maxCount; } uint64_t getMaxSize() const { return maxSize; } void encode(framing::Buffer& buffer) const; void decode ( framing::Buffer& buffer ); uint32_t encodedSize() const; + virtual void getPendingDequeues(Messages& result); - + static QPID_BROKER_EXTERN std::auto_ptr createQueuePolicy(const std::string& name, const qpid::framing::FieldTable& settings); + static QPID_BROKER_EXTERN std::auto_ptr createQueuePolicy(const std::string& name, uint32_t maxCount, uint64_t maxSize, const std::string& type = REJECT); static QPID_BROKER_EXTERN std::auto_ptr createQueuePolicy(const qpid::framing::FieldTable& settings); static QPID_BROKER_EXTERN std::auto_ptr createQueuePolicy(uint32_t maxCount, uint64_t maxSize, const std::string& type = REJECT); + static std::string getType(const qpid::framing::FieldTable& settings); static void setDefaultMaxSize(uint64_t); friend QPID_BROKER_EXTERN std::ostream& operator<<(std::ostream&, const QueuePolicy&); protected: - QueuePolicy(uint32_t maxCount, uint64_t maxSize, const std::string& type = REJECT); + const std::string name; - virtual void enqueued(const QueuedMessage&); + QueuePolicy(const std::string& name, uint32_t maxCount, uint64_t maxSize, const std::string& type = REJECT); + + virtual bool checkLimit(boost::intrusive_ptr msg); void enqueued(uint64_t size); void dequeued(uint64_t size); }; @@ -86,21 +93,20 @@ class QueuePolicy class FlowToDiskPolicy : public QueuePolicy { public: - FlowToDiskPolicy(uint32_t maxCount, uint64_t maxSize); - bool checkLimit(const QueuedMessage&); + FlowToDiskPolicy(const std::string& name, uint32_t maxCount, uint64_t maxSize); + bool checkLimit(boost::intrusive_ptr msg); }; class RingQueuePolicy : public QueuePolicy { public: - RingQueuePolicy(uint32_t maxCount, uint64_t maxSize, const std::string& type = RING); + RingQueuePolicy(const std::string& name, uint32_t maxCount, uint64_t maxSize, const std::string& type = RING); void enqueued(const QueuedMessage&); void dequeued(const QueuedMessage&); bool isEnqueued(const QueuedMessage&); - bool checkLimit(const QueuedMessage&); + bool checkLimit(boost::intrusive_ptr msg); + void getPendingDequeues(Messages& result); private: - typedef std::deque Messages; - qpid::sys::Mutex lock; Messages pendingDequeues; Messages queue; const bool strict; diff --git a/qpid/cpp/src/qpid/broker/SemanticState.cpp b/qpid/cpp/src/qpid/broker/SemanticState.cpp index bdd5f33601..7e3090bf17 100644 --- a/qpid/cpp/src/qpid/broker/SemanticState.cpp +++ b/qpid/cpp/src/qpid/broker/SemanticState.cpp @@ -65,7 +65,7 @@ SemanticState::SemanticState(DeliveryAdapter& da, SessionContext& ss) tagGenerator("sgen"), dtxSelected(false), authMsg(getSession().getBroker().getOptions().auth && !getSession().getConnection().isFederationLink()), - userID(getSession().getConnection().getUserId().substr(0,getSession().getConnection().getUserId().find('@'))) + userID(getSession().getConnection().getUserId()) { acl = getSession().getBroker().getAcl(); } @@ -302,6 +302,18 @@ bool SemanticState::ConsumerImpl::accept(intrusive_ptr msg) return !blocked; } +namespace { +struct ConsumerName { + const SemanticState::ConsumerImpl& consumer; + ConsumerName(const SemanticState::ConsumerImpl& ci) : consumer(ci) {} +}; + +ostream& operator<<(ostream& o, const ConsumerName& pc) { + return o << pc.consumer.getName() << " on " + << pc.consumer.getParent().getSession().getSessionId(); +} +} + void SemanticState::ConsumerImpl::allocateCredit(intrusive_ptr& msg) { uint32_t originalMsgCredit = msgCredit; @@ -312,7 +324,7 @@ void SemanticState::ConsumerImpl::allocateCredit(intrusive_ptr& msg) if (byteCredit != 0xFFFFFFFF) { byteCredit -= msg->getRequiredCredit(); } - QPID_LOG(debug, "Credit allocated for '" << name << "' on " << parent + QPID_LOG(debug, "Credit allocated for " << ConsumerName(*this) << ", was " << " bytes: " << originalByteCredit << " msgs: " << originalMsgCredit << " now bytes: " << byteCredit << " msgs: " << msgCredit); @@ -320,15 +332,13 @@ void SemanticState::ConsumerImpl::allocateCredit(intrusive_ptr& msg) bool SemanticState::ConsumerImpl::checkCredit(intrusive_ptr& msg) { - if (msgCredit == 0 || (byteCredit != 0xFFFFFFFF && byteCredit < msg->getRequiredCredit())) { - QPID_LOG(debug, "Not enough credit for '" << name << "' on " << parent - << ", bytes: " << byteCredit << " msgs: " << msgCredit); - return false; - } else { - QPID_LOG(debug, "Credit available for '" << name << "' on " << parent - << " bytes: " << byteCredit << " msgs: " << msgCredit); - return true; - } + bool enoughCredit = msgCredit > 0 && + (byteCredit == 0xFFFFFFFF || byteCredit >= msg->getRequiredCredit()); + QPID_LOG(debug, (enoughCredit ? "Sufficient credit for " : "Insufficient credit for ") + << ConsumerName(*this) + << ", have bytes: " << byteCredit << " msgs: " << msgCredit + << ", need " << msg->getRequiredCredit() << " bytes"); + return enoughCredit; } SemanticState::ConsumerImpl::~ConsumerImpl() {} @@ -356,6 +366,9 @@ void SemanticState::handle(intrusive_ptr msg) { } else { DeliverableMessage deliverable(msg); route(msg, deliverable); + if (msg->checkContentReleasable()) { + msg->releaseContent(); + } } } diff --git a/qpid/cpp/src/qpid/broker/SemanticState.h b/qpid/cpp/src/qpid/broker/SemanticState.h index da8383fc12..89fe7b83dd 100644 --- a/qpid/cpp/src/qpid/broker/SemanticState.h +++ b/qpid/cpp/src/qpid/broker/SemanticState.h @@ -129,6 +129,7 @@ class SemanticState : private boost::noncopyable { const framing::FieldTable& getArguments() const { return arguments; } SemanticState& getParent() { return *parent; } + const SemanticState& getParent() const { return *parent; } }; private: @@ -163,6 +164,7 @@ class SemanticState : private boost::noncopyable { ~SemanticState(); SessionContext& getSession() { return session; } + const SessionContext& getSession() const { return session; } ConsumerImpl& find(const std::string& destination); diff --git a/qpid/cpp/src/qpid/broker/SessionAdapter.cpp b/qpid/cpp/src/qpid/broker/SessionAdapter.cpp index a1ad5a0a30..2ac6d66e62 100644 --- a/qpid/cpp/src/qpid/broker/SessionAdapter.cpp +++ b/qpid/cpp/src/qpid/broker/SessionAdapter.cpp @@ -337,6 +337,10 @@ void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string& params.insert(make_pair(acl::PROP_DURABLE, std::string(durable ? _TRUE : _FALSE))); params.insert(make_pair(acl::PROP_EXCLUSIVE, std::string(exclusive ? _TRUE : _FALSE))); params.insert(make_pair(acl::PROP_AUTODELETE, std::string(autoDelete ? _TRUE : _FALSE))); + params.insert(make_pair(acl::PROP_POLICYTYPE, arguments.getAsString("qpid.policy_type"))); + params.insert(make_pair(acl::PROP_MAXQUEUECOUNT, boost::lexical_cast(arguments.getAsInt("qpid.max_count")))); + params.insert(make_pair(acl::PROP_MAXQUEUESIZE, boost::lexical_cast(arguments.getAsInt64("qpid.max_size")))); + if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_QUEUE,name,¶ms) ) throw NotAllowedException(QPID_MSG("ACL denied queue create request from " << getConnection().getUserId())); } @@ -472,8 +476,7 @@ SessionAdapter::MessageHandlerImpl::subscribe(const string& queueName, AclModule* acl = getBroker().getAcl(); if (acl) - { - // add flags as needed + { if (!acl->authorise(getConnection().getUserId(),acl::ACT_CONSUME,acl::OBJ_QUEUE,queueName,NULL) ) throw NotAllowedException(QPID_MSG("ACL denied Queue subscribe request from " << getConnection().getUserId())); } diff --git a/qpid/cpp/src/qpid/broker/SessionContext.h b/qpid/cpp/src/qpid/broker/SessionContext.h index cfdbd100c3..afbbb2cc22 100644 --- a/qpid/cpp/src/qpid/broker/SessionContext.h +++ b/qpid/cpp/src/qpid/broker/SessionContext.h @@ -28,7 +28,7 @@ #include "qpid/sys/OutputControl.h" #include "qpid/broker/ConnectionState.h" #include "qpid/broker/OwnershipToken.h" - +#include "qpid/SessionId.h" #include @@ -45,6 +45,7 @@ class SessionContext : public OwnershipToken, public sys::OutputControl virtual framing::AMQP_ClientProxy& getProxy() = 0; virtual Broker& getBroker() = 0; virtual uint16_t getChannel() const = 0; + virtual const SessionId& getSessionId() const = 0; }; }} // namespace qpid::broker diff --git a/qpid/cpp/src/qpid/broker/SessionState.h b/qpid/cpp/src/qpid/broker/SessionState.h index 67fd4f4f38..eade93ddaa 100644 --- a/qpid/cpp/src/qpid/broker/SessionState.h +++ b/qpid/cpp/src/qpid/broker/SessionState.h @@ -118,6 +118,8 @@ class SessionState : public qpid::SessionState, bool processSendCredit(uint32_t msgs); + const SessionId& getSessionId() const { return getId(); } + private: void handleCommand(framing::AMQMethodBody* method, const framing::SequenceNumber& id); diff --git a/qpid/cpp/src/qpid/broker/SignalHandler.cpp b/qpid/cpp/src/qpid/broker/SignalHandler.cpp index f4a3822554..b565cfd419 100644 --- a/qpid/cpp/src/qpid/broker/SignalHandler.cpp +++ b/qpid/cpp/src/qpid/broker/SignalHandler.cpp @@ -38,6 +38,8 @@ void SignalHandler::setBroker(const boost::intrusive_ptr& b) { signal(SIGCHLD,SIG_IGN); } +void SignalHandler::shutdown() { shutdownHandler(0); } + void SignalHandler::shutdownHandler(int) { if (broker.get()) { broker->shutdown(); diff --git a/qpid/cpp/src/qpid/broker/SignalHandler.h b/qpid/cpp/src/qpid/broker/SignalHandler.h index d2cdfae07c..bbe831b61d 100644 --- a/qpid/cpp/src/qpid/broker/SignalHandler.h +++ b/qpid/cpp/src/qpid/broker/SignalHandler.h @@ -38,6 +38,9 @@ class SignalHandler /** Set the broker to be shutdown on signals */ static void setBroker(const boost::intrusive_ptr& broker); + /** Initiate shut-down of broker */ + static void shutdown(); + private: static void shutdownHandler(int); static boost::intrusive_ptr broker; diff --git a/qpid/cpp/src/qpid/broker/TopicExchange.cpp b/qpid/cpp/src/qpid/broker/TopicExchange.cpp index 6bf0b104ea..cb04742677 100644 --- a/qpid/cpp/src/qpid/broker/TopicExchange.cpp +++ b/qpid/cpp/src/qpid/broker/TopicExchange.cpp @@ -293,44 +293,23 @@ bool TopicExchange::isBound(Queue::shared_ptr queue, const string& pattern) return q != qv.end(); } -void TopicExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/){ +void TopicExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/) +{ Binding::vector mb; + BindingList b(new std::vector >); PreRoute pr(msg, this); - uint32_t count(0); - { - RWlock::ScopedRlock l(lock); - for (BindingMap::iterator i = bindings.begin(); i != bindings.end(); ++i) { - if (match(i->first, routingKey)) { - Binding::vector& qv(i->second.bindingVector); - for(Binding::vector::iterator j = qv.begin(); j != qv.end(); j++, count++){ - mb.push_back(*j); + RWlock::ScopedRlock l(lock); + for (BindingMap::iterator i = bindings.begin(); i != bindings.end(); ++i) { + if (match(i->first, routingKey)) { + Binding::vector& qv(i->second.bindingVector); + for(Binding::vector::iterator j = qv.begin(); j != qv.end(); j++){ + b->push_back(*j); + } } } } - } - - for (Binding::vector::iterator j = mb.begin(); j != mb.end(); ++j) { - msg.deliverTo((*j)->queue); - if ((*j)->mgmtBinding != 0) - (*j)->mgmtBinding->inc_msgMatched (); - } - - if (mgmtExchange != 0) - { - mgmtExchange->inc_msgReceives (); - mgmtExchange->inc_byteReceives (msg.contentSize ()); - if (count == 0) - { - mgmtExchange->inc_msgDrops (); - mgmtExchange->inc_byteDrops (msg.contentSize ()); - } - else - { - mgmtExchange->inc_msgRoutes (count); - mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); - } - } + doRoute(msg, b); } bool TopicExchange::isBound(Queue::shared_ptr queue, const string* const routingKey, const FieldTable* const) diff --git a/qpid/cpp/src/qpid/broker/TxAccept.cpp b/qpid/cpp/src/qpid/broker/TxAccept.cpp index e47ac84990..928ac12c10 100644 --- a/qpid/cpp/src/qpid/broker/TxAccept.cpp +++ b/qpid/cpp/src/qpid/broker/TxAccept.cpp @@ -88,7 +88,13 @@ bool TxAccept::prepare(TransactionContext* ctxt) throw() void TxAccept::commit() throw() { - ops.commit(); + try { + ops.commit(); + } catch (const std::exception& e) { + QPID_LOG(error, "Failed to commit: " << e.what()); + } catch(...) { + QPID_LOG(error, "Failed to commit (unknown error)"); + } } void TxAccept::rollback() throw() {} diff --git a/qpid/cpp/src/qpid/broker/TxPublish.cpp b/qpid/cpp/src/qpid/broker/TxPublish.cpp index 17b99fd883..4b083033ea 100644 --- a/qpid/cpp/src/qpid/broker/TxPublish.cpp +++ b/qpid/cpp/src/qpid/broker/TxPublish.cpp @@ -26,9 +26,14 @@ using namespace qpid::broker; TxPublish::TxPublish(intrusive_ptr _msg) : msg(_msg) {} -bool TxPublish::prepare(TransactionContext* ctxt) throw(){ +bool TxPublish::prepare(TransactionContext* ctxt) throw() +{ try{ - for_each(queues.begin(), queues.end(), Prepare(ctxt, msg)); + while (!queues.empty()) { + prepare(ctxt, queues.front()); + prepared.push_back(queues.front()); + queues.pop_front(); + } return true; }catch(const std::exception& e){ QPID_LOG(error, "Failed to prepare: " << e.what()); @@ -38,11 +43,30 @@ bool TxPublish::prepare(TransactionContext* ctxt) throw(){ return false; } -void TxPublish::commit() throw(){ - for_each(queues.begin(), queues.end(), Commit(msg)); +void TxPublish::commit() throw() +{ + try { + for_each(prepared.begin(), prepared.end(), Commit(msg)); + if (msg->checkContentReleasable()) { + msg->releaseContent(); + } + } catch (const std::exception& e) { + QPID_LOG(error, "Failed to commit: " << e.what()); + } catch(...) { + QPID_LOG(error, "Failed to commit (unknown error)"); + } } -void TxPublish::rollback() throw(){ +void TxPublish::rollback() throw() +{ + try { + for_each(prepared.begin(), prepared.end(), Rollback(msg)); + } catch (const std::exception& e) { + QPID_LOG(error, "Failed to complete rollback: " << e.what()); + } catch(...) { + QPID_LOG(error, "Failed to complete rollback (unknown error)"); + } + } void TxPublish::deliverTo(const boost::shared_ptr& queue){ @@ -54,16 +78,14 @@ void TxPublish::deliverTo(const boost::shared_ptr& queue){ } } -TxPublish::Prepare::Prepare(TransactionContext* _ctxt, intrusive_ptr& _msg) - : ctxt(_ctxt), msg(_msg){} - -void TxPublish::Prepare::operator()(const boost::shared_ptr& queue){ +void TxPublish::prepare(TransactionContext* ctxt, const boost::shared_ptr queue) +{ if (!queue->enqueue(ctxt, msg)){ /** - * if not store then mark message for ack and deleivery once - * commit happens, as async IO will never set it when no store - * exists - */ + * if not store then mark message for ack and deleivery once + * commit happens, as async IO will never set it when no store + * exists + */ msg->enqueueComplete(); } } @@ -74,6 +96,12 @@ void TxPublish::Commit::operator()(const boost::shared_ptr& queue){ queue->process(msg); } +TxPublish::Rollback::Rollback(intrusive_ptr& _msg) : msg(_msg){} + +void TxPublish::Rollback::operator()(const boost::shared_ptr& queue){ + queue->enqueueAborted(msg); +} + uint64_t TxPublish::contentSize () { return msg->contentSize (); diff --git a/qpid/cpp/src/qpid/broker/TxPublish.h b/qpid/cpp/src/qpid/broker/TxPublish.h index d5cf5639c4..b6ab9767ab 100644 --- a/qpid/cpp/src/qpid/broker/TxPublish.h +++ b/qpid/cpp/src/qpid/broker/TxPublish.h @@ -47,23 +47,25 @@ namespace qpid { * dispatch or to be added to the in-memory queue. */ class TxPublish : public TxOp, public Deliverable{ - class Prepare{ - TransactionContext* ctxt; + + class Commit{ boost::intrusive_ptr& msg; public: - Prepare(TransactionContext* ctxt, boost::intrusive_ptr& msg); + Commit(boost::intrusive_ptr& msg); void operator()(const boost::shared_ptr& queue); }; - - class Commit{ + class Rollback{ boost::intrusive_ptr& msg; public: - Commit(boost::intrusive_ptr& msg); + Rollback(boost::intrusive_ptr& msg); void operator()(const boost::shared_ptr& queue); }; boost::intrusive_ptr msg; std::list queues; + std::list prepared; + + void prepare(TransactionContext* ctxt, boost::shared_ptr); public: QPID_BROKER_EXTERN TxPublish(boost::intrusive_ptr msg); diff --git a/qpid/cpp/src/qpid/client/ConnectionHandler.cpp b/qpid/cpp/src/qpid/client/ConnectionHandler.cpp index 9b2f662c8e..bb348675c6 100644 --- a/qpid/cpp/src/qpid/client/ConnectionHandler.cpp +++ b/qpid/cpp/src/qpid/client/ConnectionHandler.cpp @@ -257,6 +257,7 @@ void ConnectionHandler::openOk ( const Array& knownBrokers ) knownBrokersUrls.push_back(Url((*i)->get())); if (sasl.get()) { securityLayer = sasl->getSecurityLayer(maxFrameSize); + operUserId = sasl->getUserId(); } setState(OPEN); QPID_LOG(debug, "Known-brokers for connection: " << log::formatList(knownBrokersUrls)); diff --git a/qpid/cpp/src/qpid/client/ConnectionHandler.h b/qpid/cpp/src/qpid/client/ConnectionHandler.h index b1fd5be7c3..e9cc5194ae 100644 --- a/qpid/cpp/src/qpid/client/ConnectionHandler.h +++ b/qpid/cpp/src/qpid/client/ConnectionHandler.h @@ -71,6 +71,7 @@ class ConnectionHandler : private StateManager, std::auto_ptr sasl; std::auto_ptr securityLayer; boost::intrusive_ptr rcvTimeoutTask; + std::string operUserId; void checkState(STATES s, const std::string& msg); @@ -120,6 +121,7 @@ public: std::vector knownBrokersUrls; static framing::connection::CloseCode convert(uint16_t replyCode); + const std::string& getUserId() const { return operUserId; } }; }} diff --git a/qpid/cpp/src/qpid/client/ConnectionImpl.cpp b/qpid/cpp/src/qpid/client/ConnectionImpl.cpp index 45ad819ebd..c56d6a6807 100644 --- a/qpid/cpp/src/qpid/client/ConnectionImpl.cpp +++ b/qpid/cpp/src/qpid/client/ConnectionImpl.cpp @@ -151,6 +151,12 @@ void ConnectionImpl::open() handler.waitForOpen(); + // If the SASL layer has provided an "operational" userId for the connection, + // put it in the negotiated settings. + const std::string& userId(handler.getUserId()); + if (!userId.empty()) + handler.username = userId; + //enable security layer if one has been negotiated: std::auto_ptr securityLayer = handler.getSecurityLayer(); if (securityLayer.get()) { diff --git a/qpid/cpp/src/qpid/client/Connector.cpp b/qpid/cpp/src/qpid/client/Connector.cpp index f69032b26d..fbb571d40a 100644 --- a/qpid/cpp/src/qpid/client/Connector.cpp +++ b/qpid/cpp/src/qpid/client/Connector.cpp @@ -51,10 +51,10 @@ using boost::str; // Stuff for the registry of protocol connectors (maybe should be moved to its own file) namespace { typedef std::map ProtocolRegistry; - + ProtocolRegistry& theProtocolRegistry() { static ProtocolRegistry protocolRegistry; - + return protocolRegistry; } } @@ -93,7 +93,7 @@ class TCPConnector : public Connector, public sys::Codec, private sys::Runnable size_t lastEof; // Position after last EOF in frames uint64_t currentSize; Bounds* bounds; - + framing::ProtocolVersion version; bool initiated; bool closed; @@ -118,16 +118,17 @@ class TCPConnector : public Connector, public sys::Codec, private sys::Runnable void run(); void handleClosed(); bool closeInternal(); - + + void connected(const Socket&); + void connectFailed(const std::string& msg); bool readbuff(qpid::sys::AsynchIO&, qpid::sys::AsynchIOBufferBase*); void writebuff(qpid::sys::AsynchIO&); void writeDataBlock(const framing::AMQDataBlock& data); void eof(qpid::sys::AsynchIO&); boost::weak_ptr impl; - + void connect(const std::string& host, int port); - void init(); void close(); void send(framing::AMQFrame& frame); void abort(); @@ -142,7 +143,6 @@ class TCPConnector : public Connector, public sys::Codec, private sys::Runnable size_t decode(const char* buffer, size_t size); size_t encode(const char* buffer, size_t size); bool canEncode(); - public: TCPConnector(framing::ProtocolVersion pVersion, @@ -163,6 +163,11 @@ namespace { } init; } +struct TCPConnector::Buff : public AsynchIO::BufferBase { + Buff(size_t size) : AsynchIO::BufferBase(new char[size], size) {} + ~Buff() { delete [] bytes;} +}; + TCPConnector::TCPConnector(ProtocolVersion ver, const ConnectionSettings& settings, ConnectionImpl* cimpl) @@ -189,15 +194,19 @@ TCPConnector::~TCPConnector() { void TCPConnector::connect(const std::string& host, int port){ Mutex::ScopedLock l(lock); assert(closed); - try { - socket.connect(host, port); - } catch (const std::exception& /*e*/) { - socket.close(); - throw; - } - - identifier = str(format("[%1% %2%]") % socket.getLocalPort() % socket.getPeerAddress()); + assert(joined); poller = Poller::shared_ptr(new Poller); + AsynchConnector::create(socket, + poller, + host, port, + boost::bind(&TCPConnector::connected, this, _1), + boost::bind(&TCPConnector::connectFailed, this, _3)); + closed = false; + joined = false; + receiver = Thread(this); +} + +void TCPConnector::connected(const Socket&) { aio = AsynchIO::create(socket, boost::bind(&TCPConnector::readbuff, this, _1, _2), boost::bind(&TCPConnector::eof, this, _1), @@ -205,16 +214,23 @@ void TCPConnector::connect(const std::string& host, int port){ 0, // closed 0, // nobuffs boost::bind(&TCPConnector::writebuff, this, _1)); - closed = false; -} + for (int i = 0; i < 32; i++) { + aio->queueReadBuffer(new Buff(maxFrameSize)); + } + aio->start(poller); -void TCPConnector::init(){ - Mutex::ScopedLock l(lock); - assert(joined); + identifier = str(format("[%1% %2%]") % socket.getLocalPort() % socket.getPeerAddress()); ProtocolInitiation init(version); writeDataBlock(init); - joined = false; - receiver = Thread(this); +} + +void TCPConnector::connectFailed(const std::string& msg) { + QPID_LOG(warning, "Connecting failed: " << msg); + closed = true; + poller->shutdown(); + closeInternal(); + if (shutdownHandler) + shutdownHandler->shutdown(); } bool TCPConnector::closeInternal() { @@ -235,7 +251,7 @@ bool TCPConnector::closeInternal() { receiver.join(); return ret; } - + void TCPConnector::close() { closeInternal(); } @@ -243,7 +259,13 @@ void TCPConnector::close() { void TCPConnector::abort() { // Can't abort a closed connection if (!closed) { - aio->requestCallback(boost::bind(&TCPConnector::eof, this, _1)); + if (aio) { + // Established connection + aio->requestCallback(boost::bind(&TCPConnector::eof, this, _1)); + } else { + // We're still connecting + connectFailed("Connection timedout"); + } } } @@ -288,18 +310,13 @@ void TCPConnector::handleClosed() { shutdownHandler->shutdown(); } -struct TCPConnector::Buff : public AsynchIO::BufferBase { - Buff(size_t size) : AsynchIO::BufferBase(new char[size], size) {} - ~Buff() { delete [] bytes;} -}; - void TCPConnector::writebuff(AsynchIO& /*aio*/) { Codec* codec = securityLayer.get() ? (Codec*) securityLayer.get() : (Codec*) this; if (codec->canEncode()) { std::auto_ptr buffer = std::auto_ptr(aio->getQueuedBuffer()); if (!buffer.get()) buffer = std::auto_ptr(new Buff(maxFrameSize)); - + size_t encoded = codec->encode(buffer->bytes, buffer->byteCount); buffer->dataStart = 0; @@ -395,11 +412,6 @@ void TCPConnector::run() { try { Dispatcher d(poller); - for (int i = 0; i < 32; i++) { - aio->queueReadBuffer(new Buff(maxFrameSize)); - } - - aio->start(poller); d.run(); } catch (const std::exception& e) { QPID_LOG(error, QPID_MSG("FAIL " << identifier << ": " << e.what())); diff --git a/qpid/cpp/src/qpid/client/RdmaConnector.cpp b/qpid/cpp/src/qpid/client/RdmaConnector.cpp index 0aefcc04cf..0692c3d85c 100644 --- a/qpid/cpp/src/qpid/client/RdmaConnector.cpp +++ b/qpid/cpp/src/qpid/client/RdmaConnector.cpp @@ -167,20 +167,9 @@ void RdmaConnector::connect(const std::string& host, int port){ assert(joined); poller = Poller::shared_ptr(new Poller); - // This stuff needs to abstracted out of here to a platform specific file - ::addrinfo *res; - ::addrinfo hints; - hints.ai_flags = 0; - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = 0; - int n = ::getaddrinfo(host.c_str(), boost::lexical_cast(port).c_str(), &hints, &res); - if (n<0) { - throw Exception(QPID_MSG("Cannot resolve " << host << ": " << ::gai_strerror(n))); - } - + SocketAddress sa(host, boost::lexical_cast(port)); Rdma::Connector* c = new Rdma::Connector( - *res->ai_addr, + sa, Rdma::ConnectionParams(maxFrameSize, Rdma::DEFAULT_WR_ENTRIES), boost::bind(&RdmaConnector::connected, this, poller, _1, _2), boost::bind(&RdmaConnector::connectionError, this, poller, _1, _2), diff --git a/qpid/cpp/src/qpid/client/Sasl.h b/qpid/cpp/src/qpid/client/Sasl.h index 9dc5817f3d..d773609655 100644 --- a/qpid/cpp/src/qpid/client/Sasl.h +++ b/qpid/cpp/src/qpid/client/Sasl.h @@ -45,6 +45,7 @@ class Sasl virtual std::string start(const std::string& mechanisms) = 0; virtual std::string step(const std::string& challenge) = 0; virtual std::string getMechanism() = 0; + virtual std::string getUserId() = 0; virtual std::auto_ptr getSecurityLayer(uint16_t maxFrameSize) = 0; virtual ~Sasl() {} }; diff --git a/qpid/cpp/src/qpid/client/SaslFactory.cpp b/qpid/cpp/src/qpid/client/SaslFactory.cpp index 884f527f01..2258163ec8 100644 --- a/qpid/cpp/src/qpid/client/SaslFactory.cpp +++ b/qpid/cpp/src/qpid/client/SaslFactory.cpp @@ -82,6 +82,7 @@ class CyrusSasl : public Sasl std::string start(const std::string& mechanisms); std::string step(const std::string& challenge); std::string getMechanism(); + std::string getUserId(); std::auto_ptr getSecurityLayer(uint16_t maxFrameSize); private: sasl_conn_t* conn; @@ -266,6 +267,18 @@ std::string CyrusSasl::getMechanism() return mechanism; } +std::string CyrusSasl::getUserId() +{ + int propResult; + const void* operName; + + propResult = sasl_getprop(conn, SASL_USERNAME, &operName); + if (propResult == SASL_OK) + return std::string((const char*) operName); + + return std::string(); +} + void CyrusSasl::interact(sasl_interact_t* client_interact) { diff --git a/qpid/cpp/src/qpid/client/SessionImpl.cpp b/qpid/cpp/src/qpid/client/SessionImpl.cpp index 8ead44a172..32541dceac 100644 --- a/qpid/cpp/src/qpid/client/SessionImpl.cpp +++ b/qpid/cpp/src/qpid/client/SessionImpl.cpp @@ -64,7 +64,8 @@ SessionImpl::SessionImpl(const std::string& name, boost::shared_ptr(header.getBody()); - if (headerp && headerp->get()) - headerp->get(true)->clearExchangeFlag(); - + // doClearDeliveryPropertiesExchange is set by cluster update client so + // it can send messages with delivery-properties.exchange set. + // + if (doClearDeliveryPropertiesExchange) { + // Normal client is not allowed to set the delivery-properties.exchange + // so clear it here. + AMQHeaderBody* headerp = static_cast(header.getBody()); + if (headerp && headerp->get()) + headerp->get(true)->clearExchangeFlag(); + } header.setFirstSegment(false); uint64_t data_length = content.getData().length(); if(data_length > 0){ diff --git a/qpid/cpp/src/qpid/client/SessionImpl.h b/qpid/cpp/src/qpid/client/SessionImpl.h index 49d268c44d..0624bb8b3c 100644 --- a/qpid/cpp/src/qpid/client/SessionImpl.h +++ b/qpid/cpp/src/qpid/client/SessionImpl.h @@ -130,6 +130,8 @@ public: */ boost::shared_ptr getConnection(); + void setDoClearDeliveryPropertiesExchange(bool b=true) { doClearDeliveryPropertiesExchange = b; } + private: enum State { INACTIVE, @@ -243,6 +245,8 @@ private: // Only keep track of message credit sys::Semaphore* sendMsgCredit; + bool doClearDeliveryPropertiesExchange; + friend class client::SessionHandler; }; diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp index 9b9f06ec57..f51a96efd9 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp @@ -362,21 +362,12 @@ void QueueSink::send(qpid::client::AsyncSession& session, const std::string&, Ou void QueueSink::cancel(qpid::client::AsyncSession&, const std::string&) {} -template void encode(qpid::messaging::Message& from) -{ - T codec; - from.encode(codec); - from.setContentType(T::contentType); -} - void translate(const Variant::Map& from, FieldTable& to);//implementation in Codecs.cpp void convert(qpid::messaging::Message& from, qpid::client::Message& to) { //TODO: need to avoid copying as much as possible - if (from.getContent().isList()) encode(from); - if (from.getContent().isMap()) encode(from); - to.setData(from.getBytes()); + to.setData(from.getContent()); to.getDeliveryProperties().setRoutingKey(from.getSubject()); //TODO: set other delivery properties to.getMessageProperties().setContentType(from.getContentType()); diff --git a/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp index d22208368b..8e060c62d7 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp @@ -269,18 +269,9 @@ void populate(qpid::messaging::Message& message, FrameSet& command) //e.g. for rejecting. MessageImplAccess::get(message).setInternalId(command.getId()); - command.getContent(message.getBytes()); + command.getContent(message.getContent()); populateHeaders(message, command.getHeaders()); - - //decode content if necessary - if (message.getContentType() == ListCodec::contentType) { - ListCodec codec; - message.decode(codec); - } else if (message.getContentType() == MapCodec::contentType) { - MapCodec codec; - message.decode(codec); - } } diff --git a/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp b/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp index 716f955f98..cbc95b44fb 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp @@ -33,24 +33,11 @@ namespace amqp0_10 { using qpid::messaging::Address; using qpid::messaging::MessageImplAccess; -template void encode(const qpid::messaging::Message& from, qpid::client::Message& to) -{ - T codec; - MessageImplAccess::get(from).getEncodedContent(codec, to.getData()); - to.getMessageProperties().setContentType(T::contentType); -} - void OutgoingMessage::convert(const qpid::messaging::Message& from) { //TODO: need to avoid copying as much as possible - if (from.getContent().isList()) { - encode(from, message); - } else if (from.getContent().isMap()) { - encode(from, message); - } else { - message.setData(from.getBytes()); - message.getMessageProperties().setContentType(from.getContentType()); - } + message.setData(from.getContent()); + message.getMessageProperties().setContentType(from.getContentType()); const Address& address = from.getReplyTo(); if (!address.value.empty()) { message.getMessageProperties().setReplyTo(AddressResolution::convert(address)); diff --git a/qpid/cpp/src/qpid/client/windows/SaslFactory.cpp b/qpid/cpp/src/qpid/client/windows/SaslFactory.cpp index 58956609a4..3a662463c1 100644 --- a/qpid/cpp/src/qpid/client/windows/SaslFactory.cpp +++ b/qpid/cpp/src/qpid/client/windows/SaslFactory.cpp @@ -43,6 +43,7 @@ class WindowsSasl : public Sasl std::string start(const std::string& mechanisms); std::string step(const std::string& challenge); std::string getMechanism(); + std::string getUserId(); std::auto_ptr getSecurityLayer(uint16_t maxFrameSize); private: ConnectionSettings settings; @@ -131,6 +132,11 @@ std::string WindowsSasl::getMechanism() return mechanism; } +std::string WindowsSasl::getUserId() +{ + return std::string(); // TODO - when GSSAPI is supported, return userId for connection. +} + std::auto_ptr WindowsSasl::getSecurityLayer(uint16_t maxFrameSize) { return std::auto_ptr(0); diff --git a/qpid/cpp/src/qpid/cluster/Cluster.cpp b/qpid/cpp/src/qpid/cluster/Cluster.cpp index e35d3e4175..0706fc72e8 100644 --- a/qpid/cpp/src/qpid/cluster/Cluster.cpp +++ b/qpid/cpp/src/qpid/cluster/Cluster.cpp @@ -99,6 +99,7 @@ #include "qpid/broker/Connection.h" #include "qpid/broker/QueueRegistry.h" #include "qpid/broker/SessionState.h" +#include "qpid/broker/SignalHandler.h" #include "qpid/framing/AMQFrame.h" #include "qpid/framing/AMQP_AllOperations.h" #include "qpid/framing/AllInvoker.h" @@ -120,7 +121,6 @@ #include "qpid/management/ManagementAgent.h" #include "qpid/memory.h" #include "qpid/sys/Thread.h" -#include "qpid/sys/LatencyTracker.h" #include #include @@ -144,12 +144,16 @@ using qpid::management::Manageable; using qpid::management::Args; namespace _qmf = ::qmf::org::apache::qpid::cluster; -/** NOTE: increment this number whenever any incompatible changes in +/** + * NOTE: must increment this number whenever any incompatible changes in * cluster protocol/behavior are made. It allows early detection and * sensible reporting of an attempt to mix different versions in a * cluster. + * + * Currently use SVN revision to avoid clashes with versions from + * different branches. */ -const uint32_t Cluster::CLUSTER_VERSION = 2; +const uint32_t Cluster::CLUSTER_VERSION = 820783; struct ClusterDispatcher : public framing::AMQP_AllOperations::ClusterHandler { qpid::cluster::Cluster& cluster; @@ -308,7 +312,7 @@ void Cluster::leave(Lock&) { // Finalize connections now now to avoid problems later in destructor. LEAVE_TRY(localConnections.clear()); LEAVE_TRY(connections.clear()); - LEAVE_TRY(broker.shutdown()); + LEAVE_TRY(broker::SignalHandler::shutdown()); } } @@ -324,20 +328,14 @@ void Cluster::deliver( MemberId from(nodeid, pid); framing::Buffer buf(static_cast(msg), msg_len); Event e(Event::decodeCopy(from, buf)); - LATENCY_TRACK(if (e.getConnectionId().getMember() == self) mcast.cpgLatency.finish()); deliverEvent(e); } -LATENCY_TRACK(sys::LatencyTracker eventQueueLatencyTracker("EventQueue");) - LATENCY_TRACK(sys::LatencyTracker frameQueueLatencyTracker("FrameQueue");) - - void Cluster::deliverEvent(const Event& e) { - LATENCY_TRACK(eventQueueLatencyTracker.start(e.getData());) - deliverEventQueue.push(e); +void Cluster::deliverEvent(const Event& e) { + deliverEventQueue.push(e); } void Cluster::deliverFrame(const EventFrame& e) { - LATENCY_TRACK(frameQueueLatencyTracker.start(e.frame.getBody())); deliverFrameQueue.push(e); } @@ -350,7 +348,6 @@ const ClusterUpdateOfferBody* castUpdateOffer(const framing::AMQBody* body) { // Handler for deliverEventQueue. // This thread decodes frames from events. void Cluster::deliveredEvent(const Event& e) { - LATENCY_TRACK(eventQueueLatencyTracker.finish(e.getData())); if (e.isCluster()) { QPID_LOG(trace, *this << " DLVR: " << e); EventFrame ef(e, e.getFrame()); @@ -396,13 +393,9 @@ void Cluster::flagError( error.error(connection, type, map.getFrameSeq(), map.getMembers(), msg); } -LATENCY_TRACK(sys::LatencyTracker doOutputTracker("DoOutput");) - // Handler for deliverFrameQueue. // This thread executes the main logic. - void Cluster::deliveredFrame(const EventFrame& efConst) { - LATENCY_TRACK(frameQueueLatencyTracker.finish(e.frame.getBody())); - LATENCY_TRACK(if (e.frame.getBody()->type() == CONTENT_BODY) doOutputTracker.start(e.frame.getBody())); +void Cluster::deliveredFrame(const EventFrame& efConst) { Mutex::ScopedLock l(lock); if (state == LEFT) return; EventFrame e(efConst); @@ -434,7 +427,6 @@ void Cluster::processFrame(const EventFrame& e, Lock& l) { throw Exception(QPID_MSG("Invalid cluster control")); } else if (state >= CATCHUP) { - LATENCY_TRACK(LatencyScope ls(processLatency)); map.incrementFrameSeq(); ConnectionPtr connection = getConnection(e, l); if (connection) { diff --git a/qpid/cpp/src/qpid/cluster/ErrorCheck.cpp b/qpid/cpp/src/qpid/cluster/ErrorCheck.cpp index 35be055d06..5b7011047b 100644 --- a/qpid/cpp/src/qpid/cluster/ErrorCheck.cpp +++ b/qpid/cpp/src/qpid/cluster/ErrorCheck.cpp @@ -45,7 +45,8 @@ ostream& operator<<(ostream& o, const ErrorCheck::MemberSet& ms) { } void ErrorCheck::error( - Connection& c, ErrorType t, framing::SequenceNumber seq, const MemberSet& ms, const std::string& msg) + Connection& c, ErrorType t, framing::SequenceNumber seq, const MemberSet& ms, + const std::string& msg) { // Detected a local error, inform cluster and set error state. assert(t != ERROR_TYPE_NONE); // Must be an error. @@ -54,10 +55,11 @@ void ErrorCheck::error( unresolved = ms; frameSeq = seq; connection = &c; - QPID_LOG(error, cluster - << (type == ERROR_TYPE_SESSION ? " channel" : " connection") - << " error " << frameSeq << " on " << c << ": " << msg - << " must be resolved with: " << unresolved); + message = msg; + QPID_LOG(debug, cluster<< (type == ERROR_TYPE_SESSION ? " channel" : " connection") + << " error " << frameSeq << " on " << c + << " must be resolved with: " << unresolved + << ": " << message); mcast.mcastControl( ClusterErrorCheckBody(ProtocolVersion(), type, frameSeq), cluster.getId()); // If there are already frames queued up by a previous error, review @@ -84,13 +86,15 @@ ErrorCheck::FrameQueue::iterator ErrorCheck::review(const FrameQueue::iterator& if (errorCheck->getFrameSeq() == frameSeq) { // Addresses current error next = frames.erase(i); // Drop matching error check controls if (errorCheck->getType() < type) { // my error is worse than his - QPID_LOG(critical, cluster << " error " << frameSeq - << " did not occur on " << i->getMemberId()); - throw Exception(QPID_MSG("Error " << frameSeq - << " did not occur on all members")); + QPID_LOG(critical, cluster + << " local error " << frameSeq << " did not occur on member " + << i->getMemberId() + << ": " << message); + throw Exception( + QPID_MSG("local error did not occur on all cluster members " << ": " << message)); } else { // his error is worse/same as mine. - QPID_LOG(info, cluster << " error " << frameSeq + QPID_LOG(debug, cluster << " error " << frameSeq << " resolved with " << i->getMemberId()); unresolved.erase(i->getMemberId()); checkResolved(); @@ -128,10 +132,10 @@ ErrorCheck::FrameQueue::iterator ErrorCheck::review(const FrameQueue::iterator& void ErrorCheck::checkResolved() { if (unresolved.empty()) { // No more potentially conflicted members, we're clear. type = ERROR_TYPE_NONE; - QPID_LOG(info, cluster << " error " << frameSeq << " resolved."); + QPID_LOG(debug, cluster << " error " << frameSeq << " resolved."); } else - QPID_LOG(info, cluster << " error " << frameSeq + QPID_LOG(debug, cluster << " error " << frameSeq << " must be resolved with " << unresolved); } @@ -146,7 +150,7 @@ void ErrorCheck::respondNone(const MemberId& from, uint8_t type, framing::Sequen // Don't respond to non-errors or to my own errors. if (type == ERROR_TYPE_NONE || from == cluster.getId()) return; - QPID_LOG(info, cluster << " error " << frameSeq << " did not occur locally."); + QPID_LOG(debug, cluster << " error " << frameSeq << " did not occur locally."); mcast.mcastControl( ClusterErrorCheckBody(ProtocolVersion(), ERROR_TYPE_NONE, frameSeq), cluster.getId() diff --git a/qpid/cpp/src/qpid/cluster/ErrorCheck.h b/qpid/cpp/src/qpid/cluster/ErrorCheck.h index 09028391ac..c975b9af64 100644 --- a/qpid/cpp/src/qpid/cluster/ErrorCheck.h +++ b/qpid/cpp/src/qpid/cluster/ErrorCheck.h @@ -84,6 +84,7 @@ class ErrorCheck SequenceNumber frameSeq; ErrorType type; Connection* connection; + std::string message; }; }} // namespace qpid::cluster diff --git a/qpid/cpp/src/qpid/cluster/Event.cpp b/qpid/cpp/src/qpid/cluster/Event.cpp index 30866d3154..4831e7eabe 100644 --- a/qpid/cpp/src/qpid/cluster/Event.cpp +++ b/qpid/cpp/src/qpid/cluster/Event.cpp @@ -38,9 +38,6 @@ const size_t EventHeader::HEADER_SIZE = sizeof(uint8_t) + // type sizeof(uint64_t) + // connection pointer only, CPG provides member ID. sizeof(uint32_t) // payload size -#ifdef QPID_LATENCY_METRIC - + sizeof(int64_t) // timestamp -#endif ; EventHeader::EventHeader(EventType t, const ConnectionId& c, size_t s) @@ -61,9 +58,6 @@ void EventHeader::decode(const MemberId& m, framing::Buffer& buf) { throw Exception("Invalid multicast event type"); connectionId = ConnectionId(m, buf.getLongLong()); size = buf.getLong(); -#ifdef QPID_LATENCY_METRIC - latency_metric_timestamp = buf.getLongLong(); -#endif } Event Event::decodeCopy(const MemberId& m, framing::Buffer& buf) { @@ -97,9 +91,6 @@ void EventHeader::encode(Buffer& b) const { b.putOctet(type); b.putLongLong(connectionId.getNumber()); b.putLong(size); -#ifdef QPID_LATENCY_METRIC - b.putLongLong(latency_metric_timestamp); -#endif } // Encode my header in my buffer. diff --git a/qpid/cpp/src/qpid/cluster/Multicaster.cpp b/qpid/cpp/src/qpid/cluster/Multicaster.cpp index 7e97963318..72fc1533f8 100644 --- a/qpid/cpp/src/qpid/cluster/Multicaster.cpp +++ b/qpid/cpp/src/qpid/cluster/Multicaster.cpp @@ -31,9 +31,6 @@ namespace cluster { Multicaster::Multicaster(Cpg& cpg_, const boost::shared_ptr& poller, boost::function onError_) : -#if defined (QPID_LATENCY_TRACKER) - cpgLatency("CPG"), -#endif onError(onError_), cpg(cpg_), queue(boost::bind(&Multicaster::sendMcast, this, _1), poller), holding(true) @@ -61,7 +58,6 @@ void Multicaster::mcastBuffer(const char* data, size_t size, const ConnectionId& void Multicaster::mcast(const Event& e) { { sys::Mutex::ScopedLock l(lock); - LATENCY_TRACK(cpgLatency.start()); if (e.isConnection() && holding) { holdingQueue.push_back(e); return; diff --git a/qpid/cpp/src/qpid/cluster/Multicaster.h b/qpid/cpp/src/qpid/cluster/Multicaster.h index f2ee5099bb..c1a0ddffc6 100644 --- a/qpid/cpp/src/qpid/cluster/Multicaster.h +++ b/qpid/cpp/src/qpid/cluster/Multicaster.h @@ -26,7 +26,6 @@ #include "qpid/cluster/Event.h" #include "qpid/sys/PollableQueue.h" #include "qpid/sys/Mutex.h" -#include "qpid/sys/LatencyTracker.h" #include #include @@ -58,8 +57,6 @@ class Multicaster /** End holding mode, held events are mcast */ void release(); - LATENCY_TRACK(sys::LatencyCounter cpgLatency;) - private: typedef sys::PollableQueue PollableEventQueue; typedef std::deque PlainEventQueue; diff --git a/qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp b/qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp index cb8f01386c..cb75fe5561 100644 --- a/qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp +++ b/qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp @@ -24,7 +24,6 @@ #include "qpid/framing/ClusterConnectionDeliverDoOutputBody.h" #include "qpid/framing/AMQFrame.h" #include "qpid/log/Statement.h" -#include "qpid/sys/LatencyTracker.h" #include @@ -40,16 +39,9 @@ OutputInterceptor::OutputInterceptor(Connection& p, sys::ConnectionOutputHandler : parent(p), closing(false), next(&h), sendMax(1), sent(0), sentDoOutput(false) {} -#if defined QPID_LATENCY_TRACKER -extern sys::LatencyTracker doOutputTracker; -#endif - void OutputInterceptor::send(framing::AMQFrame& f) { - LATENCY_TRACK(doOutputTracker.finish(f.getBody())); - { - sys::Mutex::ScopedLock l(lock); - next->send(f); - } + sys::Mutex::ScopedLock l(lock); + next->send(f); } void OutputInterceptor::activateOutput() { diff --git a/qpid/cpp/src/qpid/cluster/UpdateClient.cpp b/qpid/cpp/src/qpid/cluster/UpdateClient.cpp index 2e557f2ab6..d6df8bd5ac 100644 --- a/qpid/cpp/src/qpid/cluster/UpdateClient.cpp +++ b/qpid/cpp/src/qpid/cluster/UpdateClient.cpp @@ -209,9 +209,16 @@ class MessageUpdater { ClusterConnectionProxy(session).expiryId(*expiryId); } + // We can't send a broker::Message via the normal client API, + // and it would be expensive to copy it into a client::Message + // so we go a bit under the client API covers here. + // SessionBase_0_10Access sb(session); + // Disable client code that clears the delivery-properties.exchange + sb.get()->setDoClearDeliveryPropertiesExchange(false); framing::MessageTransferBody transfer( - framing::ProtocolVersion(), UpdateClient::UPDATE, message::ACCEPT_MODE_NONE, message::ACQUIRE_MODE_PRE_ACQUIRED); + framing::ProtocolVersion(), UpdateClient::UPDATE, message::ACCEPT_MODE_NONE, + message::ACQUIRE_MODE_PRE_ACQUIRED); sb.get()->send(transfer, message.payload->getFrames(), !message.payload->isContentReleased()); if (message.payload->isContentReleased()){ diff --git a/qpid/cpp/src/qpid/cluster/UpdateExchange.h b/qpid/cpp/src/qpid/cluster/UpdateExchange.h index 194a3d386d..00a92c7f1e 100644 --- a/qpid/cpp/src/qpid/cluster/UpdateExchange.h +++ b/qpid/cpp/src/qpid/cluster/UpdateExchange.h @@ -30,7 +30,7 @@ namespace qpid { namespace cluster { /** - * A keyless exchange (like fanout exchange) that does not modify deliver-properties.exchange + * A keyless exchange (like fanout exchange) that does not modify delivery-properties.exchange * on messages. */ class UpdateExchange : public broker::FanOutExchange diff --git a/qpid/cpp/src/qpid/messaging/ListContent.cpp b/qpid/cpp/src/qpid/messaging/ListContent.cpp new file mode 100644 index 0000000000..0c3ca5fc62 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/ListContent.cpp @@ -0,0 +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. + * + */ +#include "qpid/messaging/ListContent.h" +#include "qpid/messaging/Message.h" +#include "qpid/client/amqp0_10/Codecs.h" + +namespace qpid { +namespace messaging { + +class ListContentImpl : public Variant +{ + Message* msg; + public: + ListContentImpl(Message& m) : Variant(Variant::List()), msg(&m) + { + if (msg->getContent().size()) { + qpid::client::amqp0_10::ListCodec codec; + codec.decode(msg->getContent(), *this); + } + } + + void encode() + { + qpid::client::amqp0_10::ListCodec codec; + codec.encode(*this, msg->getContent()); + } +}; + +ListContent::ListContent(Message& m) : impl(new ListContentImpl(m)) {} +ListContent::~ListContent() { delete impl; } +ListContent& ListContent::operator=(const ListContent& l) { *impl = *l.impl; return *this; } + +ListContent::const_iterator ListContent::begin() const { return impl->asList().begin(); } +ListContent::const_iterator ListContent::end() const { return impl->asList().end(); } +ListContent::const_reverse_iterator ListContent::rbegin() const { return impl->asList().rbegin(); } +ListContent::const_reverse_iterator ListContent::rend() const { return impl->asList().rend(); } + +ListContent::iterator ListContent::begin() { return impl->asList().begin(); } +ListContent::iterator ListContent::end() { return impl->asList().end(); } +ListContent::reverse_iterator ListContent::rbegin() { return impl->asList().rbegin(); } +ListContent::reverse_iterator ListContent::rend() { return impl->asList().rend(); } + +bool ListContent::empty() const { return impl->asList().empty(); } +size_t ListContent::size() const { return impl->asList().size(); } + +const Variant& ListContent::front() const { return impl->asList().front(); } +Variant& ListContent::front() { return impl->asList().front(); } +const Variant& ListContent::back() const { return impl->asList().back(); } +Variant& ListContent::back() { return impl->asList().back(); } + +void ListContent::push_front(const Variant& v) { impl->asList().push_front(v); } +void ListContent::push_back(const Variant& v) { impl->asList().push_back(v); } + +void ListContent::pop_front() { impl->asList().pop_front(); } +void ListContent::pop_back() { impl->asList().pop_back(); } + +ListContent::iterator ListContent::insert(iterator position, const Variant& v) +{ + return impl->asList().insert(position, v); +} +void ListContent::insert(iterator position, size_t n, const Variant& v) +{ + impl->asList().insert(position, n, v); +} +ListContent::iterator ListContent::erase(iterator position) { return impl->asList().erase(position); } +ListContent::iterator ListContent::erase(iterator first, iterator last) { return impl->asList().erase(first, last); } +void ListContent::clear() { impl->asList().clear(); } + +void ListContent::encode() { impl->encode(); } + +const Variant::List& ListContent::asList() const { return impl->asList(); } +Variant::List& ListContent::asList() { return impl->asList(); } + +std::ostream& operator<<(std::ostream& out, const ListContent& m) +{ + out << m.asList(); + return out; +} + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/ListView.cpp b/qpid/cpp/src/qpid/messaging/ListView.cpp new file mode 100644 index 0000000000..b717d157fa --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/ListView.cpp @@ -0,0 +1,63 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/ListView.h" +#include "qpid/messaging/Message.h" +#include "qpid/client/amqp0_10/Codecs.h" + +namespace qpid { +namespace messaging { + +class ListViewImpl : public Variant +{ + public: + ListViewImpl(const Message& msg) : Variant(Variant::List()) + { + if (msg.getContent().size()) { + qpid::client::amqp0_10::ListCodec codec; + codec.decode(msg.getContent(), *this); + } + } +}; + +ListView::ListView(const Message& m) :impl(new ListViewImpl(m)) {} +ListView::~ListView() { delete impl; } +ListView& ListView::operator=(const ListView& l) { *impl = *l.impl; return *this; } + +ListView::const_iterator ListView::begin() const { return impl->asList().begin(); } +ListView::const_iterator ListView::end() const { return impl->asList().end(); } +ListView::const_reverse_iterator ListView::rbegin() const { return impl->asList().rbegin(); } +ListView::const_reverse_iterator ListView::rend() const { return impl->asList().rend(); } + +bool ListView::empty() const { return impl->asList().empty(); } +size_t ListView::size() const { return impl->asList().size(); } + +const Variant& ListView::front() const { return impl->asList().front(); } +const Variant& ListView::back() const { return impl->asList().back(); } + +const Variant::List& ListView::asList() const { return impl->asList(); } + +std::ostream& operator<<(std::ostream& out, const ListView& m) +{ + out << m.asList(); + return out; +} + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/MapContent.cpp b/qpid/cpp/src/qpid/messaging/MapContent.cpp new file mode 100644 index 0000000000..c653561fc9 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/MapContent.cpp @@ -0,0 +1,87 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/MapContent.h" +#include "qpid/messaging/Message.h" +#include "qpid/client/amqp0_10/Codecs.h" + +namespace qpid { +namespace messaging { + +class MapContentImpl : public Variant +{ + Message* msg; + public: + MapContentImpl(Message& m) : Variant(Variant::Map()), msg(&m) + { + if (msg->getContent().size()) { + qpid::client::amqp0_10::MapCodec codec; + codec.decode(msg->getContent(), *this); + } + } + + void encode() + { + qpid::client::amqp0_10::MapCodec codec; + codec.encode(*this, msg->getContent()); + } +}; + +MapContent::MapContent(Message& m) : impl(new MapContentImpl(m)) {} +MapContent::~MapContent() { delete impl; } +MapContent& MapContent::operator=(const MapContent& m) { *impl = *m.impl; return *this; } + +MapContent::const_iterator MapContent::begin() const { return impl->asMap().begin(); } +MapContent::const_iterator MapContent::end() const { return impl->asMap().end(); } +MapContent::const_reverse_iterator MapContent::rbegin() const { return impl->asMap().rbegin(); } +MapContent::const_reverse_iterator MapContent::rend() const { return impl->asMap().rend(); } +MapContent::iterator MapContent::begin() { return impl->asMap().begin(); } +MapContent::iterator MapContent::end() { return impl->asMap().end(); } +MapContent::reverse_iterator MapContent::rbegin() { return impl->asMap().rbegin(); } +MapContent::reverse_iterator MapContent::rend() { return impl->asMap().rend(); } + +bool MapContent::empty() const { return impl->asMap().empty(); } +size_t MapContent::size() const { return impl->asMap().size(); } + +MapContent::const_iterator MapContent::find(const key_type& key) const { return impl->asMap().find(key); } +MapContent::iterator MapContent::find(const key_type& key) { return impl->asMap().find(key); } +const Variant& MapContent::operator[](const key_type& key) const { return impl->asMap()[key]; } +Variant& MapContent::operator[](const key_type& key) { return impl->asMap()[key]; } + +std::pair MapContent::insert(const value_type& item) { return impl->asMap().insert(item); } +MapContent::iterator MapContent::insert(iterator position, const value_type& item) { return impl->asMap().insert(position, item); } +void MapContent::erase(iterator position) { impl->asMap().erase(position); } +void MapContent::erase(iterator first, iterator last) { impl->asMap().erase(first, last); } +size_t MapContent::erase(const key_type& key) { return impl->asMap().erase(key); } +void MapContent::clear() { impl->asMap().clear(); } + +void MapContent::encode() { impl->encode(); } + +const std::map& MapContent::asMap() const { return impl->asMap(); } +std::map& MapContent::asMap() { return impl->asMap(); } + + +std::ostream& operator<<(std::ostream& out, const MapContent& m) +{ + out << m.asMap(); + return out; +} + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/MapView.cpp b/qpid/cpp/src/qpid/messaging/MapView.cpp new file mode 100644 index 0000000000..ffa6e91a16 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/MapView.cpp @@ -0,0 +1,63 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/MapView.h" +#include "qpid/messaging/Message.h" +#include "qpid/client/amqp0_10/Codecs.h" + +namespace qpid { +namespace messaging { + +class MapViewImpl : public Variant +{ + public: + MapViewImpl(const Message& msg) : Variant(Variant::Map()) + { + if (msg.getContent().size()) { + qpid::client::amqp0_10::MapCodec codec; + codec.decode(msg.getContent(), *this); + } + } +}; + +MapView::MapView(const Message& m) : impl(new MapViewImpl(m)) {} +MapView::~MapView() { delete impl; } +MapView& MapView::operator=(const MapView& m) { *impl = *m.impl; return *this; } + +MapView::const_iterator MapView::begin() const { return impl->asMap().begin(); } +MapView::const_iterator MapView::end() const { return impl->asMap().end(); } +MapView::const_reverse_iterator MapView::rbegin() const { return impl->asMap().rbegin(); } +MapView::const_reverse_iterator MapView::rend() const { return impl->asMap().rend(); } + +bool MapView::empty() const { return impl->asMap().empty(); } +size_t MapView::size() const { return impl->asMap().size(); } + +MapView::const_iterator MapView::find(const key_type& key) const { return impl->asMap().find(key); } +const Variant& MapView::operator[](const key_type& key) const { return impl->asMap()[key]; } + +const std::map& MapView::asMap() const { return impl->asMap(); } + +std::ostream& operator<<(std::ostream& out, const MapView& m) +{ + out << m.asMap(); + return out; +} + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/Message.cpp b/qpid/cpp/src/qpid/messaging/Message.cpp index 1d844b3027..fb4e800eaa 100644 --- a/qpid/cpp/src/qpid/messaging/Message.cpp +++ b/qpid/cpp/src/qpid/messaging/Message.cpp @@ -27,7 +27,7 @@ namespace messaging { Message::Message(const std::string& bytes) : impl(new MessageImpl(bytes)) {} Message::Message(const char* bytes, size_t count) : impl(new MessageImpl(bytes, count)) {} -Message::Message(const Message& m) : impl(new MessageImpl(m.getBytes())) {} +Message::Message(const Message& m) : impl(new MessageImpl(m.getContent())) {} Message::~Message() { delete impl; } Message& Message::operator=(const Message& m) { *impl = *m.impl; return *this; } @@ -44,27 +44,15 @@ const std::string& Message::getContentType() const { return impl->getContentType const VariantMap& Message::getHeaders() const { return impl->getHeaders(); } VariantMap& Message::getHeaders() { return impl->getHeaders(); } -void Message::setBytes(const std::string& c) { impl->setBytes(c); } -void Message::setBytes(const char* chars, size_t count) { impl->setBytes(chars, count); } -const std::string& Message::getBytes() const { return impl->getBytes(); } -std::string& Message::getBytes() { return impl->getBytes(); } +void Message::setContent(const std::string& c) { impl->setBytes(c); } +void Message::setContent(const char* chars, size_t count) { impl->setBytes(chars, count); } +const std::string& Message::getContent() const { return impl->getBytes(); } +std::string& Message::getContent() { return impl->getBytes(); } -const char* Message::getRawContent() const { return impl->getBytes().data(); } -size_t Message::getContentSize() const { return impl->getBytes().size(); } - -MessageContent& Message::getContent() { return *impl; } -const MessageContent& Message::getContent() const { return *impl; } -void Message::setContent(const std::string& s) { *impl = s; } -void Message::setContent(const Variant::Map& m) { *impl = m; } -void Message::setContent(const Variant::List& l) { *impl = l; } - -void Message::encode(Codec& codec) { impl->encode(codec); } - -void Message::decode(Codec& codec) { impl->decode(codec); } - -std::ostream& operator<<(std::ostream& out, const MessageContent& content) +void Message::getContent(std::pair& content) const { - return content.print(out); + content.first = impl->getBytes().data(); + content.second = impl->getBytes().size(); } }} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/MessageImpl.cpp b/qpid/cpp/src/qpid/messaging/MessageImpl.cpp index 5df9218e03..e17fccd64f 100644 --- a/qpid/cpp/src/qpid/messaging/MessageImpl.cpp +++ b/qpid/cpp/src/qpid/messaging/MessageImpl.cpp @@ -28,8 +28,8 @@ namespace { const std::string EMPTY_STRING = ""; } -MessageImpl::MessageImpl(const std::string& c) : bytes(c), type(VAR_VOID), internalId(0) {} -MessageImpl::MessageImpl(const char* chars, size_t count) : bytes(chars, count), type(VAR_VOID), internalId(0) {} +MessageImpl::MessageImpl(const std::string& c) : bytes(c), internalId(0) {} +MessageImpl::MessageImpl(const char* chars, size_t count) : bytes(chars, count), internalId(0) {} void MessageImpl::setReplyTo(const Address& d) { replyTo = d; } const Address& MessageImpl::getReplyTo() const { return replyTo; } @@ -44,155 +44,14 @@ const VariantMap& MessageImpl::getHeaders() const { return headers; } VariantMap& MessageImpl::getHeaders() { return headers; } //should these methods be on MessageContent? -void MessageImpl::setBytes(const std::string& c) { clear(); bytes = c; } -void MessageImpl::setBytes(const char* chars, size_t count) { clear(); bytes.assign(chars, count); } +void MessageImpl::setBytes(const std::string& c) { bytes = c; } +void MessageImpl::setBytes(const char* chars, size_t count) { bytes.assign(chars, count); } const std::string& MessageImpl::getBytes() const { return bytes; } std::string& MessageImpl::getBytes() { return bytes; } - -Variant& MessageImpl::operator[](const std::string& key) { return asMap()[key]; } - -std::ostream& MessageImpl::print(std::ostream& out) const -{ - if (type == VAR_MAP) { - return out << content.asMap(); - } else if (type == VAR_LIST) { - return out << content.asList(); - } else { - return out << bytes; - } -} - -template MessageContent& MessageImpl::append(T& t) -{ - if (type == VAR_VOID) { - //TODO: this is inefficient, probably want to hold on to the stream object - std::stringstream s; - s << bytes; - s << t; - bytes = s.str(); - } else if (type == VAR_LIST) { - content.asList().push_back(Variant(t)); - } else { - throw InvalidConversion("<< operator only valid on strings and lists"); - } - return *this; -} - -MessageContent& MessageImpl::operator<<(const std::string& v) { return append(v); } -MessageContent& MessageImpl::operator<<(const char* v) { return append(v); } -MessageContent& MessageImpl::operator<<(bool v) { return append(v); } -MessageContent& MessageImpl::operator<<(int8_t v) { return append(v); } -MessageContent& MessageImpl::operator<<(int16_t v) { return append(v); } -MessageContent& MessageImpl::operator<<(int32_t v) { return append(v); } -MessageContent& MessageImpl::operator<<(int64_t v) { return append(v); } -MessageContent& MessageImpl::operator<<(uint8_t v) { return append(v); } -MessageContent& MessageImpl::operator<<(uint16_t v) { return append(v); } -MessageContent& MessageImpl::operator<<(uint32_t v) { return append(v); } -MessageContent& MessageImpl::operator<<(uint64_t v) { return append(v); } -MessageContent& MessageImpl::operator<<(double v) { return append(v); } -MessageContent& MessageImpl::operator<<(float v) { return append(v); } -MessageContent& MessageImpl::operator=(const std::string& s) -{ - type = VAR_VOID; - bytes = s; - return *this; -} -MessageContent& MessageImpl::operator=(const char* c) -{ - type = VAR_VOID; - bytes = c; - return *this; -} -MessageContent& MessageImpl::operator=(const Variant::Map& m) -{ - type = VAR_MAP; - content = m; - return *this; -} - -MessageContent& MessageImpl::operator=(const Variant::List& l) -{ - type = VAR_LIST; - content = l; - return *this; -} - -void MessageImpl::encode(Codec& codec) -{ - if (content.getType() != VAR_VOID) { - bytes = EMPTY_STRING; - codec.encode(content, bytes); - } -} - -void MessageImpl::getEncodedContent(Codec& codec, std::string& out) const -{ - if (content.getType() != VAR_VOID) { - codec.encode(content, out); - } else { - out = bytes; - } -} - -void MessageImpl::decode(Codec& codec) -{ - codec.decode(bytes, content); - if (content.getType() == VAR_MAP) type = VAR_MAP; - else if (content.getType() == VAR_LIST) type = VAR_LIST; - else type = VAR_VOID;//TODO: what if codec set some type other than map or list?? -} - void MessageImpl::setInternalId(qpid::framing::SequenceNumber i) { internalId = i; } qpid::framing::SequenceNumber MessageImpl::getInternalId() { return internalId; } -bool MessageImpl::isVoid() const { return type == VAR_VOID; } - -const std::string& MessageImpl::asString() const -{ - if (isVoid()) return getBytes(); - else return content.getString();//will throw an error -} -std::string& MessageImpl::asString() -{ - if (isVoid()) return getBytes(); - else return content.getString();//will throw an error -} - -const char* MessageImpl::asChars() const -{ - if (!isVoid()) throw InvalidConversion("Content is of structured type."); - return bytes.data(); -} -size_t MessageImpl::size() const -{ - return bytes.size(); -} - -const Variant::Map& MessageImpl::asMap() const { return content.asMap(); } -Variant::Map& MessageImpl::asMap() -{ - if (isVoid()) { - content = Variant::Map(); - type = VAR_MAP; - } - return content.asMap(); -} -bool MessageImpl::isMap() const { return type == VAR_MAP; } - -const Variant::List& MessageImpl::asList() const { return content.asList(); } -Variant::List& MessageImpl::asList() -{ - if (isVoid()) { - content = Variant::List(); - type = VAR_LIST; - } - return content.asList(); -} -bool MessageImpl::isList() const { return type == VAR_LIST; } - -void MessageImpl::clear() { bytes = EMPTY_STRING; content.reset(); type = VAR_VOID; } - MessageImpl& MessageImplAccess::get(Message& msg) { return *msg.impl; diff --git a/qpid/cpp/src/qpid/messaging/MessageImpl.h b/qpid/cpp/src/qpid/messaging/MessageImpl.h index 1173e7570a..4939cdc5cc 100644 --- a/qpid/cpp/src/qpid/messaging/MessageImpl.h +++ b/qpid/cpp/src/qpid/messaging/MessageImpl.h @@ -22,15 +22,13 @@ * */ #include "qpid/messaging/Address.h" -#include "qpid/messaging/Codec.h" -#include "qpid/messaging/MessageContent.h" #include "qpid/messaging/Variant.h" #include "qpid/framing/SequenceNumber.h" namespace qpid { namespace messaging { -struct MessageImpl : MessageContent +struct MessageImpl { Address replyTo; std::string subject; @@ -38,8 +36,6 @@ struct MessageImpl : MessageContent Variant::Map headers; std::string bytes; - Variant content;//used only for LIST and MAP - VariantType type;//if LIST, MAP content holds the value; if VOID bytes holds the value qpid::framing::SequenceNumber internalId; @@ -66,54 +62,6 @@ struct MessageImpl : MessageContent void setInternalId(qpid::framing::SequenceNumber id); qpid::framing::SequenceNumber getInternalId(); - bool isVoid() const; - - const std::string& asString() const; - std::string& asString(); - - const char* asChars() const; - size_t size() const; - - const Variant::Map& asMap() const; - Variant::Map& asMap(); - bool isMap() const; - - const Variant::List& asList() const; - Variant::List& asList(); - bool isList() const; - - void clear(); - - void getEncodedContent(Codec& codec, std::string&) const; - void encode(Codec& codec); - void decode(Codec& codec); - - Variant& operator[](const std::string&); - - std::ostream& print(std::ostream& out) const; - - //operator<< for variety of types... - MessageContent& operator<<(const std::string&); - MessageContent& operator<<(const char*); - MessageContent& operator<<(bool); - MessageContent& operator<<(int8_t); - MessageContent& operator<<(int16_t); - MessageContent& operator<<(int32_t); - MessageContent& operator<<(int64_t); - MessageContent& operator<<(uint8_t); - MessageContent& operator<<(uint16_t); - MessageContent& operator<<(uint32_t); - MessageContent& operator<<(uint64_t); - MessageContent& operator<<(double); - MessageContent& operator<<(float); - - //assignment from string, map and list - MessageContent& operator=(const std::string&); - MessageContent& operator=(const char*); - MessageContent& operator=(const Variant::Map&); - MessageContent& operator=(const Variant::List&); - - template MessageContent& append(T& t); }; class Message; diff --git a/qpid/cpp/src/qpid/sys/AsynchIO.h b/qpid/cpp/src/qpid/sys/AsynchIO.h index fb02183359..419770568a 100644 --- a/qpid/cpp/src/qpid/sys/AsynchIO.h +++ b/qpid/cpp/src/qpid/sys/AsynchIO.h @@ -57,7 +57,7 @@ public: class AsynchConnector { public: typedef boost::function1 ConnectedCallback; - typedef boost::function2 FailedCallback; + typedef boost::function3 FailedCallback; // Call create() to allocate a new AsynchConnector object with the // specified poller, addressing, and callbacks. @@ -70,7 +70,7 @@ public: std::string hostname, uint16_t port, ConnectedCallback connCb, - FailedCallback failCb = 0); + FailedCallback failCb); protected: AsynchConnector() {} @@ -108,7 +108,7 @@ class AsynchIO { public: typedef AsynchIOBufferBase BufferBase; - typedef boost::function2 ReadCallback; + typedef boost::function2 ReadCallback; typedef boost::function1 EofCallback; typedef boost::function1 DisconnectCallback; typedef boost::function2 ClosedCallback; diff --git a/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp b/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp index 8094abd43d..eb0f213547 100644 --- a/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp +++ b/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp @@ -103,10 +103,31 @@ void AsynchIOHandler::giveReadCredit(int32_t credit) { aio->startReading(); } -bool AsynchIOHandler::readbuff(AsynchIO& , AsynchIO::BufferBase* buff) { +void AsynchIOHandler::readbuff(AsynchIO& , AsynchIO::BufferBase* buff) { if (readError) { - return false; + return; + } + + // Check here for read credit + if (readCredit.get() != InfiniteCredit) { + if (readCredit.get() == 0) { + // FIXME aconway 2009-10-01: Workaround to avoid "false wakeups". + // readbuff is sometimes called with no credit. + // This should be fixed somewhere else to avoid such calls. + aio->unread(buff); + return; + } + // TODO In theory should be able to use an atomic operation before taking the lock + // but in practice there seems to be an unexplained race in that case + ScopedLock l(creditLock); + if (--readCredit == 0) { + assert(readCredit.get() >= 0); + if (readCredit.get() == 0) { + aio->stopReading(); + } + } } + size_t decoded = 0; if (codec) { // Already initiated try { @@ -149,20 +170,6 @@ bool AsynchIOHandler::readbuff(AsynchIO& , AsynchIO::BufferBase* buff) { // Give whole buffer back to aio subsystem aio->queueReadBuffer(buff); } - // Check here for read credit - if (readCredit.get() != InfiniteCredit) { - // TODO In theory should be able to use an atomic operation before taking the lock - // but in practice there seems to be an unexplained race in that case - ScopedLock l(creditLock); - if (--readCredit == 0) { - assert(readCredit.get() >= 0); - if (readCredit.get() == 0) { - aio->stopReading(); - return false; - } - } - } - return true; } void AsynchIOHandler::eof(AsynchIO&) { diff --git a/qpid/cpp/src/qpid/sys/AsynchIOHandler.h b/qpid/cpp/src/qpid/sys/AsynchIOHandler.h index 9785f445a4..e1885bac79 100644 --- a/qpid/cpp/src/qpid/sys/AsynchIOHandler.h +++ b/qpid/cpp/src/qpid/sys/AsynchIOHandler.h @@ -65,7 +65,7 @@ class AsynchIOHandler : public OutputControl { QPID_COMMON_EXTERN void giveReadCredit(int32_t credit); // Input side - QPID_COMMON_EXTERN bool readbuff(AsynchIO& aio, AsynchIOBufferBase* buff); + QPID_COMMON_EXTERN void readbuff(AsynchIO& aio, AsynchIOBufferBase* buff); QPID_COMMON_EXTERN void eof(AsynchIO& aio); QPID_COMMON_EXTERN void disconnect(AsynchIO& aio); diff --git a/qpid/cpp/src/qpid/sys/LatencyTracker.h b/qpid/cpp/src/qpid/sys/LatencyTracker.h deleted file mode 100644 index 3294528ff6..0000000000 --- a/qpid/cpp/src/qpid/sys/LatencyTracker.h +++ /dev/null @@ -1,157 +0,0 @@ -#ifndef QPID_SYS_LATENCYTRACKER_H -#define QPID_SYS_LATENCYTRACKER_H - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include "qpid/sys/Time.h" -#include -#include -#include - -namespace qpid { -namespace sys { - -/**@file Tools for measuring latency. NOT SUITABLE FOR PROUDCTION BUILDS. - * Uses should be compiled only if QPID_LATENCY_TRACKER is defined. - * See the convenience macros at the end of this file. - */ - -/** Used by LatencyCounter and LatencyTracker below */ -class LatencyStatistic { - public: - LatencyStatistic(std::string name_) : name(name_), count(0), total(0), min(std::numeric_limits::max()), max(0) {} - ~LatencyStatistic() { print(); } - - void record(Duration d) { - total += d; - ++count; - if (d > max) max=d; - if (d < min) min=d; - } - - void print() { - if (count) { - double meanMsec = (double(total)/count)/TIME_MSEC; - printf("\n==== Latency metric %s: samples=%lu mean=%fms (%f-%f)\n", name.c_str(), count, meanMsec, double(min)/TIME_MSEC, double(max)/TIME_MSEC); - } - else - printf("\n==== Latency metric %s: no samples.\n", name.c_str()); - } - - private: - std::string name; - unsigned long count; - int64_t total, min, max; -}; - -/** Measure delay between seeing the same value at start and finish. */ -template class LatencyTracker { - public: - LatencyTracker(std::string name) : measuring(false), stat(name) {} - - void start(T value) { - sys::Mutex::ScopedLock l(lock); - if (!measuring) { - measureAt = value; - measuring = true; - startTime = AbsTime::now(); - } - } - - void finish(T value) { - sys::Mutex::ScopedLock l(lock); - if(measuring && measureAt == value) { - stat.record(Duration(startTime, AbsTime::now())); - measuring = false; - } - } - - private: - sys::Mutex lock; - bool measuring; - T measureAt; - AbsTime startTime; - LatencyStatistic stat; -}; - - -/** Measures delay between the nth call to start and the nth call to finish. - * E.g. to measure latency between sending & receiving an ordered stream of messages. - */ -class LatencyCounter { - public: - LatencyCounter(std::string name) : measuring(false), startCount(0), finishCount(0), stat(name) {} - - void start() { - sys::Mutex::ScopedLock l(lock); - if (!measuring) { - measureAt = startCount; - measuring = true; - startTime = AbsTime::now(); - } - ++startCount; - } - - void finish() { - sys::Mutex::ScopedLock l(lock); - if (measuring && measureAt == finishCount) { - stat.record(Duration(startTime, AbsTime::now())); - measuring = false; - } - ++finishCount; - } - - private: - sys::Mutex lock; - bool measuring; - uint64_t startCount, finishCount, measureAt; - AbsTime startTime; - LatencyStatistic stat; -}; - -/** Measures time spent in a scope. */ -class LatencyScope { - public: - LatencyScope(LatencyStatistic& s) : stat(s), startTime(AbsTime::now()) {} - - ~LatencyScope() { - sys::Mutex::ScopedLock l(lock); - stat.record(Duration(startTime, AbsTime::now())); - } - - private: - sys::Mutex lock; - LatencyStatistic& stat; - AbsTime startTime; -}; - - -/** Macros to wrap latency tracking so disabled unless QPID_LATENCY_TRACKER is defined */ - -#if defined(QPID_LATENCY_TRACKER) -#define LATENCY_TRACK(X) X -#else -#define LATENCY_TRACK(X) -#endif -}} // namespace qpid::sys - -#endif /*!QPID_SYS_LATENCYTRACKER_H*/ diff --git a/qpid/cpp/src/qpid/sys/RdmaIOPlugin.cpp b/qpid/cpp/src/qpid/sys/RdmaIOPlugin.cpp index 6eafb6cf0b..28ff140237 100644 --- a/qpid/cpp/src/qpid/sys/RdmaIOPlugin.cpp +++ b/qpid/cpp/src/qpid/sys/RdmaIOPlugin.cpp @@ -29,6 +29,7 @@ #include "qpid/sys/OutputControl.h" #include +#include #include #include @@ -304,8 +305,9 @@ void RdmaIOProtocolFactory::accept(Poller::shared_ptr poller, ConnectionCodec::F sin.sin_port = htons(listeningPort); sin.sin_addr.s_addr = INADDR_ANY; + SocketAddress sa("",boost::lexical_cast(listeningPort)); listener.reset( - new Rdma::Listener((const sockaddr&)(sin), + new Rdma::Listener(sa, Rdma::ConnectionParams(65536, Rdma::DEFAULT_WR_ENTRIES), boost::bind(&RdmaIOProtocolFactory::established, this, poller, _1), boost::bind(&RdmaIOProtocolFactory::connectionError, this, _1, _2), @@ -331,24 +333,14 @@ void RdmaIOProtocolFactory::connected(Poller::shared_ptr poller, Rdma::Connectio void RdmaIOProtocolFactory::connect( Poller::shared_ptr poller, - const std::string& host, int16_t p, + const std::string& host, int16_t port, ConnectionCodec::Factory* f, ConnectFailedCallback failed) { - ::addrinfo *res; - ::addrinfo hints = {}; - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - stringstream ss; ss << p; - string port = ss.str(); - int n = ::getaddrinfo(host.c_str(), port.c_str(), &hints, &res); - if (n<0) { - throw Exception(QPID_MSG("Rdma: Cannot resolve " << host << ": " << ::gai_strerror(n))); - } - + SocketAddress sa(host, boost::lexical_cast(port)); Rdma::Connector* c = new Rdma::Connector( - *res->ai_addr, + sa, Rdma::ConnectionParams(8000, Rdma::DEFAULT_WR_ENTRIES), boost::bind(&RdmaIOProtocolFactory::connected, this, poller, _1, _2, f), boost::bind(&RdmaIOProtocolFactory::connectionError, this, _1, _2), diff --git a/qpid/cpp/src/qpid/sys/Socket.h b/qpid/cpp/src/qpid/sys/Socket.h index f389e99cb8..d108402682 100644 --- a/qpid/cpp/src/qpid/sys/Socket.h +++ b/qpid/cpp/src/qpid/sys/Socket.h @@ -31,6 +31,7 @@ namespace qpid { namespace sys { class Duration; +class SocketAddress; class Socket : public IOHandle { @@ -48,6 +49,7 @@ public: void setNonblocking() const; QPID_COMMON_EXTERN void connect(const std::string& host, uint16_t port) const; + QPID_COMMON_EXTERN void connect(const SocketAddress&) const; QPID_COMMON_EXTERN void close() const; diff --git a/qpid/cpp/src/qpid/sys/SocketAddress.h b/qpid/cpp/src/qpid/sys/SocketAddress.h new file mode 100644 index 0000000000..fcb9c81d43 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/SocketAddress.h @@ -0,0 +1,51 @@ +#ifndef _sys_SocketAddress_h +#define _sys_SocketAddress_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/IntegerTypes.h" +#include "qpid/CommonImportExport.h" +#include + +struct addrinfo; + +namespace qpid { +namespace sys { + +class SocketAddress { + friend const ::addrinfo& getAddrInfo(const SocketAddress&); + +public: + /** Create a SocketAddress from hostname and port*/ + QPID_COMMON_EXTERN SocketAddress(const std::string& host, const std::string& port); + QPID_COMMON_EXTERN ~SocketAddress(); + + std::string asString() const; + +private: + std::string host; + std::string port; + ::addrinfo* addrInfo; +}; + +}} +#endif /*!_sys_SocketAddress_h*/ diff --git a/qpid/cpp/src/qpid/sys/TCPIOPlugin.cpp b/qpid/cpp/src/qpid/sys/TCPIOPlugin.cpp index b456beb098..3377be98f1 100644 --- a/qpid/cpp/src/qpid/sys/TCPIOPlugin.cpp +++ b/qpid/cpp/src/qpid/sys/TCPIOPlugin.cpp @@ -46,7 +46,7 @@ class AsynchIOProtocolFactory : public ProtocolFactory { void accept(Poller::shared_ptr, ConnectionCodec::Factory*); void connect(Poller::shared_ptr, const std::string& host, int16_t port, ConnectionCodec::Factory*, - boost::function2 failed); + ConnectFailedCallback); uint16_t getPort() const; std::string getHost() const; @@ -54,6 +54,7 @@ class AsynchIOProtocolFactory : public ProtocolFactory { private: void established(Poller::shared_ptr, const Socket&, ConnectionCodec::Factory*, bool isClient); + void connectFailed(const Socket&, int, const std::string&, ConnectFailedCallback); }; // Static instance to initialise plugin @@ -118,6 +119,15 @@ void AsynchIOProtocolFactory::accept(Poller::shared_ptr poller, acceptor->start(poller); } +void AsynchIOProtocolFactory::connectFailed( + const Socket& s, int ec, const std::string& emsg, + ConnectFailedCallback failedCb) +{ + failedCb(ec, emsg); + s.close(); + delete &s; +} + void AsynchIOProtocolFactory::connect( Poller::shared_ptr poller, const std::string& host, int16_t port, @@ -131,13 +141,14 @@ void AsynchIOProtocolFactory::connect( // is no longer needed. Socket* socket = new Socket(); - AsynchConnector::create (*socket, - poller, - host, - port, - boost::bind(&AsynchIOProtocolFactory::established, - this, poller, _1, fact, true), - failed); + AsynchConnector::create(*socket, + poller, + host, + port, + boost::bind(&AsynchIOProtocolFactory::established, + this, poller, _1, fact, true), + boost::bind(&AsynchIOProtocolFactory::connectFailed, + this, _1, _2, _3, failed)); } }} // namespace qpid::sys diff --git a/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp b/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp index 8545ebd9cb..c042dcef01 100644 --- a/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp +++ b/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp @@ -21,6 +21,7 @@ #include "qpid/sys/AsynchIO.h" #include "qpid/sys/Socket.h" +#include "qpid/sys/SocketAddress.h" #include "qpid/sys/Poller.h" #include "qpid/sys/DispatchHandle.h" #include "qpid/sys/Time.h" @@ -37,6 +38,7 @@ #include #include +#include using namespace qpid::sys; @@ -161,11 +163,12 @@ class AsynchConnector : public qpid::sys::AsynchConnector, private: void connComplete(DispatchHandle& handle); - void failure(int, std::string); + void failure(int, const std::string&); private: ConnectedCallback connCallback; FailedCallback failCallback; + std::string errMsg; const Socket& socket; public: @@ -174,7 +177,7 @@ public: std::string hostname, uint16_t port, ConnectedCallback connCb, - FailedCallback failCb = 0); + FailedCallback failCb); }; AsynchConnector::AsynchConnector(const Socket& s, @@ -192,12 +195,17 @@ AsynchConnector::AsynchConnector(const Socket& s, socket(s) { socket.setNonblocking(); + SocketAddress sa(hostname, boost::lexical_cast(port)); try { - socket.connect(hostname, port); - startWatch(poller); + socket.connect(sa); } catch(std::exception& e) { - failure(-1, std::string(e.what())); + // Defer reporting failure + startWatch(poller); + errMsg = e.what(); + DispatchHandle::call(boost::bind(&AsynchConnector::failure, this, -1, errMsg)); + return; } + startWatch(poller); } void AsynchConnector::connComplete(DispatchHandle& h) @@ -209,17 +217,13 @@ void AsynchConnector::connComplete(DispatchHandle& h) connCallback(socket); DispatchHandle::doDelete(); } else { - failure(errCode, std::string(strError(errCode))); + failure(errCode, strError(errCode)); } } -void AsynchConnector::failure(int errCode, std::string message) +void AsynchConnector::failure(int errCode, const std::string& message) { - if (failCallback) - failCallback(errCode, message); - - socket.close(); - delete &socket; + failCallback(socket, errCode, message); DispatchHandle::doDelete(); } @@ -467,7 +471,8 @@ void AsynchIO::readable(DispatchHandle& h) { threadReadTotal += rc; readTotal += rc; - if (!readCallback(*this, buff)) { + readCallback(*this, buff); + if (readingStopped) { // We have been flow controlled. break; } diff --git a/qpid/cpp/src/qpid/sys/posix/Socket.cpp b/qpid/cpp/src/qpid/sys/posix/Socket.cpp index 31044be9ca..02004b1999 100644 --- a/qpid/cpp/src/qpid/sys/posix/Socket.cpp +++ b/qpid/cpp/src/qpid/sys/posix/Socket.cpp @@ -21,6 +21,7 @@ #include "qpid/sys/Socket.h" +#include "qpid/sys/SocketAddress.h" #include "qpid/sys/posix/check.h" #include "qpid/sys/posix/PrivatePosix.h" @@ -36,6 +37,7 @@ #include #include +#include namespace qpid { namespace sys { @@ -126,42 +128,23 @@ void Socket::setNonblocking() const { QPID_POSIX_CHECK(::fcntl(impl->fd, F_SETFL, O_NONBLOCK)); } -namespace { -const char* h_errstr(int e) { - switch (e) { - case HOST_NOT_FOUND: return "Host not found"; - case NO_ADDRESS: return "Name does not have an IP address"; - case TRY_AGAIN: return "A temporary error occurred on an authoritative name server."; - case NO_RECOVERY: return "Non-recoverable name server error"; - default: return "Unknown error"; - } -} +void Socket::connect(const std::string& host, uint16_t port) const +{ + SocketAddress sa(host, boost::lexical_cast(port)); + connect(sa); } -void Socket::connect(const std::string& host, uint16_t p) const +void Socket::connect(const SocketAddress& addr) const { - std::stringstream portstream; - portstream << p; - std::string port = portstream.str(); - connectname = host + ":" + port; + connectname = addr.asString(); const int& socket = impl->fd; - ::addrinfo *res; - ::addrinfo hints; - ::memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; // In order to allow AF_INET6 we'd have to change createTcp() as well - hints.ai_socktype = SOCK_STREAM; - int n = ::getaddrinfo(host.c_str(), port.c_str(), &hints, &res); - if (n != 0) - throw Exception(QPID_MSG("Cannot resolve " << host << ": " << ::gai_strerror(n))); // TODO the correct thing to do here is loop on failure until you've used all the returned addresses - if ((::connect(socket, res->ai_addr, res->ai_addrlen) < 0) && + if ((::connect(socket, getAddrInfo(addr).ai_addr, getAddrInfo(addr).ai_addrlen) < 0) && (errno != EINPROGRESS)) { - ::freeaddrinfo(res); - throw qpid::Exception(QPID_MSG(strError(errno) << ": " << host << ":" << port)); + throw Exception(QPID_MSG(strError(errno) << ": " << connectname)); } - ::freeaddrinfo(res); } void @@ -178,15 +161,14 @@ int Socket::listen(uint16_t port, int backlog) const const int& socket = impl->fd; int yes=1; QPID_POSIX_CHECK(setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes))); - struct sockaddr_in name; - name.sin_family = AF_INET; - name.sin_port = htons(port); - name.sin_addr.s_addr = 0; - if (::bind(socket, (struct sockaddr*)&name, sizeof(name)) < 0) + + SocketAddress sa("", boost::lexical_cast(port)); + if (::bind(socket, getAddrInfo(sa).ai_addr, getAddrInfo(sa).ai_addrlen) < 0) throw Exception(QPID_MSG("Can't bind to port " << port << ": " << strError(errno))); if (::listen(socket, backlog) < 0) throw Exception(QPID_MSG("Can't listen on port " << port << ": " << strError(errno))); - + + struct sockaddr_in name; socklen_t namelen = sizeof(name); if (::getsockname(socket, (struct sockaddr*)&name, &namelen) < 0) throw QPID_POSIX_ERROR(errno); @@ -226,9 +208,10 @@ std::string Socket::getPeername() const std::string Socket::getPeerAddress() const { - if (!connectname.empty()) - return std::string (connectname); - return getName(impl->fd, false, true); + if (connectname.empty()) { + connectname = getName(impl->fd, false, true); + } + return connectname; } std::string Socket::getLocalAddress() const diff --git a/qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp b/qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp new file mode 100644 index 0000000000..fe8812299c --- /dev/null +++ b/qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp @@ -0,0 +1,71 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/SocketAddress.h" + +#include "qpid/sys/posix/check.h" + +#include +#include +#include + +namespace qpid { +namespace sys { + +SocketAddress::SocketAddress(const std::string& host0, const std::string& port0) : + host(host0), + port(port0), + addrInfo(0) +{ + ::addrinfo hints; + ::memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; // In order to allow AF_INET6 we'd have to change createTcp() as well + hints.ai_socktype = SOCK_STREAM; + + const char* node = 0; + if (host.empty()) { + hints.ai_flags |= AI_PASSIVE; + } else { + node = host.c_str(); + } + const char* service = port.empty() ? "0" : port.c_str(); + + int n = ::getaddrinfo(node, service, &hints, &addrInfo); + if (n != 0) + throw Exception(QPID_MSG("Cannot resolve " << host << ": " << ::gai_strerror(n))); +} + +SocketAddress::~SocketAddress() +{ + ::freeaddrinfo(addrInfo); +} + +std::string SocketAddress::asString() const +{ + return host + ":" + port; +} + +const ::addrinfo& getAddrInfo(const SocketAddress& sa) +{ + return *sa.addrInfo; +} + +}} diff --git a/qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp b/qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp index 9da6c835ce..d39f7885a5 100644 --- a/qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp +++ b/qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp @@ -40,6 +40,7 @@ using std::rand; using qpid::sys::Poller; using qpid::sys::Dispatcher; +using qpid::sys::SocketAddress; using qpid::sys::AbsTime; using qpid::sys::Duration; using qpid::sys::TIME_SEC; @@ -154,18 +155,8 @@ using namespace qpid::tests; int main(int argc, char* argv[]) { vector args(&argv[0], &argv[argc]); - ::addrinfo *res; - ::addrinfo hints = {}; - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; + string host = args[1]; string port = (args.size() < 3) ? "20079" : args[2]; - int n = ::getaddrinfo(args[1].c_str(), port.c_str(), &hints, &res); - if (n<0) { - cerr << "Can't find information for: " << args[1] << "\n"; - return 1; - } else { - cout << "Connecting to: " << args[1] << ":" << port <<"\n"; - } if (args.size() > 3) msgsize = atoi(args[3].c_str()); @@ -181,8 +172,10 @@ int main(int argc, char* argv[]) { boost::shared_ptr p(new Poller()); Dispatcher d(p); + SocketAddress sa(host, port); + cout << "Connecting to: " << sa.asString() <<"\n"; Rdma::Connector c( - *res->ai_addr, + sa, Rdma::ConnectionParams(msgsize, Rdma::DEFAULT_WR_ENTRIES), boost::bind(&connected, p, _1, _2), boost::bind(&connectionError, p, _1, _2), diff --git a/qpid/cpp/src/qpid/sys/rdma/RdmaIO.cpp b/qpid/cpp/src/qpid/sys/rdma/RdmaIO.cpp index 491c1612fd..8d06fccba1 100644 --- a/qpid/cpp/src/qpid/sys/rdma/RdmaIO.cpp +++ b/qpid/cpp/src/qpid/sys/rdma/RdmaIO.cpp @@ -26,6 +26,7 @@ #include #include +using qpid::sys::SocketAddress; using qpid::sys::DispatchHandle; using qpid::sys::Poller; @@ -461,7 +462,7 @@ namespace Rdma { } Listener::Listener( - const sockaddr& src, + const SocketAddress& src, const ConnectionParams& cp, EstablishedCallback ec, ErrorCallback errc, @@ -541,7 +542,7 @@ namespace Rdma { } Connector::Connector( - const sockaddr& dst, + const SocketAddress& dst, const ConnectionParams& cp, ConnectedCallback cc, ErrorCallback errc, diff --git a/qpid/cpp/src/qpid/sys/rdma/RdmaIO.h b/qpid/cpp/src/qpid/sys/rdma/RdmaIO.h index 697d9387ce..12a1b98d24 100644 --- a/qpid/cpp/src/qpid/sys/rdma/RdmaIO.h +++ b/qpid/cpp/src/qpid/sys/rdma/RdmaIO.h @@ -27,6 +27,7 @@ #include "qpid/sys/Dispatcher.h" #include "qpid/sys/DispatchHandle.h" #include "qpid/sys/Mutex.h" +#include "qpid/sys/SocketAddress.h" #include @@ -173,14 +174,14 @@ namespace Rdma { class Listener : public ConnectionManager { - sockaddr src_addr; + qpid::sys::SocketAddress src_addr; ConnectionParams checkConnectionParams; ConnectionRequestCallback connectionRequestCallback; EstablishedCallback establishedCallback; public: Listener( - const sockaddr& src, + const qpid::sys::SocketAddress& src, const ConnectionParams& cp, EstablishedCallback ec, ErrorCallback errc, @@ -198,14 +199,14 @@ namespace Rdma { class Connector : public ConnectionManager { - sockaddr dst_addr; + qpid::sys::SocketAddress dst_addr; ConnectionParams connectionParams; RejectedCallback rejectedCallback; ConnectedCallback connectedCallback; public: Connector( - const sockaddr& dst, + const qpid::sys::SocketAddress& dst, const ConnectionParams& cp, ConnectedCallback cc, ErrorCallback errc, diff --git a/qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp b/qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp index 07d6379362..4c11ba23eb 100644 --- a/qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp +++ b/qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp @@ -35,6 +35,7 @@ using std::string; using std::cout; using std::cerr; +using qpid::sys::SocketAddress; using qpid::sys::Poller; using qpid::sys::Dispatcher; @@ -144,20 +145,15 @@ using namespace qpid::tests; int main(int argc, char* argv[]) { vector args(&argv[0], &argv[argc]); - ::sockaddr_in sin; - - int port = (args.size() < 2) ? 20079 : atoi(args[1].c_str()); + std::string port = (args.size() < 2) ? "20079" : args[1]; cout << "Listening on port: " << port << "\n"; - sin.sin_family = AF_INET; - sin.sin_port = htons(port); - sin.sin_addr.s_addr = INADDR_ANY; - try { boost::shared_ptr p(new Poller()); Dispatcher d(p); - Rdma::Listener a((const sockaddr&)(sin), + SocketAddress sa("", port); + Rdma::Listener a(sa, Rdma::ConnectionParams(16384, Rdma::DEFAULT_WR_ENTRIES), boost::bind(connected, p, _1), connectionError, diff --git a/qpid/cpp/src/qpid/sys/rdma/rdma_wrap.h b/qpid/cpp/src/qpid/sys/rdma/rdma_wrap.h index aa2e516e6b..e11497dc02 100644 --- a/qpid/cpp/src/qpid/sys/rdma/rdma_wrap.h +++ b/qpid/cpp/src/qpid/sys/rdma/rdma_wrap.h @@ -350,9 +350,9 @@ namespace Rdma { return ConnectionEvent(e); } - void bind(sockaddr& src_addr) const { + void bind(qpid::sys::SocketAddress& src_addr) const { assert(id.get()); - CHECK(::rdma_bind_addr(id.get(), &src_addr)); + CHECK(::rdma_bind_addr(id.get(), getAddrInfo(src_addr).ai_addr)); } void listen(int backlog = DEFAULT_BACKLOG) const { @@ -361,12 +361,11 @@ namespace Rdma { } void resolve_addr( - sockaddr& dst_addr, - sockaddr* src_addr = 0, + qpid::sys::SocketAddress& dst_addr, int timeout_ms = DEFAULT_TIMEOUT) const { assert(id.get()); - CHECK(::rdma_resolve_addr(id.get(), src_addr, &dst_addr, timeout_ms)); + CHECK(::rdma_resolve_addr(id.get(), 0, getAddrInfo(dst_addr).ai_addr, timeout_ms)); } void resolve_route(int timeout_ms = DEFAULT_TIMEOUT) const { diff --git a/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp b/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp index 8905b87838..475b18600d 100644 --- a/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp +++ b/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp @@ -634,7 +634,7 @@ void AsynchIO::readComplete(AsynchReadResult *result) { if (status == 0 && bytes > 0) { bool restartRead = true; // May not if receiver doesn't want more if (readCallback) - restartRead = readCallback(*this, result->getBuff()); + readCallback(*this, result->getBuff()); if (restartRead) startReading(); } diff --git a/qpid/cpp/src/qpid/xml/XmlExchange.cpp b/qpid/cpp/src/qpid/xml/XmlExchange.cpp index 8a1ef6149e..472ca28954 100644 --- a/qpid/cpp/src/qpid/xml/XmlExchange.cpp +++ b/qpid/cpp/src/qpid/xml/XmlExchange.cpp @@ -206,45 +206,22 @@ void XmlExchange::route(Deliverable& msg, const string& routingKey, const FieldT PreRoute pr(msg, this); try { XmlBinding::vector::ConstPtr p; - { + BindingList b(new std::vector >); + { RWlock::ScopedRlock l(lock); - p = bindingsMap[routingKey].snapshot(); - if (!p) return; - } - int count(0); + p = bindingsMap[routingKey].snapshot(); + if (!p.get()) return; + } for (std::vector::const_iterator i = p->begin(); i != p->end(); i++) { if (matches((*i)->xquery, msg, args, (*i)->parse_message_content)) { - msg.deliverTo((*i)->queue); - count++; - QPID_LOG(trace, "Delivered to queue" ); - - if ((*i)->mgmtBinding != 0) - (*i)->mgmtBinding->inc_msgMatched (); + b->push_back(*i); } - } - if (!count) { - QPID_LOG(warning, "XMLExchange " << getName() << ": could not route message with query " << routingKey); - if (mgmtExchange != 0) { - mgmtExchange->inc_msgDrops (); - mgmtExchange->inc_byteDrops (msg.contentSize ()); - } - } else { - if (mgmtExchange != 0) { - mgmtExchange->inc_msgRoutes (count); - mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); - } - } - - if (mgmtExchange != 0) { - mgmtExchange->inc_msgReceives (); - mgmtExchange->inc_byteReceives (msg.contentSize ()); - } + } + doRoute(msg, b); } catch (...) { QPID_LOG(warning, "XMLExchange " << getName() << ": exception routing message with query " << routingKey); } - - } diff --git a/qpid/cpp/src/qpidd.cpp b/qpid/cpp/src/qpidd.cpp index 1839a62205..f2b46942fb 100644 --- a/qpid/cpp/src/qpidd.cpp +++ b/qpid/cpp/src/qpidd.cpp @@ -36,25 +36,29 @@ int main(int argc, char* argv[]) { try { - { - BootstrapOptions bootOptions(argv[0]); - string defaultPath (bootOptions.module.loadDir); - // Parse only the common, load, and log options to see which - // modules need to be loaded. Once the modules are loaded, - // the command line will be re-parsed with all of the - // module-supplied options. + BootstrapOptions bootOptions(argv[0]); + string defaultPath (bootOptions.module.loadDir); + // Parse only the common, load, and log options to see which + // modules need to be loaded. Once the modules are loaded, + // the command line will be re-parsed with all of the + // module-supplied options. + try { bootOptions.parse (argc, argv, bootOptions.common.config, true); qpid::log::Logger::instance().configure(bootOptions.log); + } catch (const std::exception& e) { + // Couldn't configure logging so write the message direct to stderr. + cerr << "Unexpected error: " << e.what() << endl; + return 1; + } - for (vector::iterator iter = bootOptions.module.load.begin(); - iter != bootOptions.module.load.end(); - iter++) - qpid::tryShlib (iter->data(), false); + for (vector::iterator iter = bootOptions.module.load.begin(); + iter != bootOptions.module.load.end(); + iter++) + qpid::tryShlib (iter->data(), false); - if (!bootOptions.module.noLoad) { - bool isDefault = defaultPath == bootOptions.module.loadDir; - qpid::loadModuleDir (bootOptions.module.loadDir, isDefault); - } + if (!bootOptions.module.noLoad) { + bool isDefault = defaultPath == bootOptions.module.loadDir; + qpid::loadModuleDir (bootOptions.module.loadDir, isDefault); } // Parse options diff --git a/qpid/cpp/src/tests/AsyncCompletion.cpp b/qpid/cpp/src/tests/AsyncCompletion.cpp index 4492e6b6bc..e32097106f 100644 --- a/qpid/cpp/src/tests/AsyncCompletion.cpp +++ b/qpid/cpp/src/tests/AsyncCompletion.cpp @@ -70,9 +70,11 @@ class AsyncCompletionMessageStore : public NullMessageStore { QPID_AUTO_TEST_SUITE(AsyncCompletionTestSuite) QPID_AUTO_TEST_CASE(testWaitTillComplete) { - AsyncCompletionMessageStore* store = new AsyncCompletionMessageStore; SessionFixture fix; - fix.broker->setStore(store); // Broker will delete store. + AsyncCompletionMessageStore* store = new AsyncCompletionMessageStore; + boost::shared_ptr p; + p.reset(store); + fix.broker->setStore(p); AsyncSession s = fix.session; static const int count = 3; diff --git a/qpid/cpp/src/tests/ClusterFixture.h b/qpid/cpp/src/tests/ClusterFixture.h index 5952cc1736..1eee32b9a4 100644 --- a/qpid/cpp/src/tests/ClusterFixture.h +++ b/qpid/cpp/src/tests/ClusterFixture.h @@ -75,10 +75,10 @@ class ClusterFixture : public vector { /** @param localIndex can be -1 meaning don't automatically start a local broker. * A local broker can be started with addLocal(). */ - ClusterFixture(size_t n, const Args& args, int localIndex=0); + ClusterFixture(size_t n, const Args& args, int localIndex=-1); /**@param updateArgs function is passed the index of the cluster member and can update the arguments. */ - ClusterFixture(size_t n, boost::function updateArgs, int localIndex); + ClusterFixture(size_t n, boost::function updateArgs, int localIndex=-1); void add(size_t n) { for (size_t i=0; i < n; ++i) add(); } void add(); // Add a broker. diff --git a/qpid/cpp/src/tests/ExchangeTest.cpp b/qpid/cpp/src/tests/ExchangeTest.cpp index 44835c6184..88a1cd99c2 100644 --- a/qpid/cpp/src/tests/ExchangeTest.cpp +++ b/qpid/cpp/src/tests/ExchangeTest.cpp @@ -60,7 +60,7 @@ QPID_AUTO_TEST_CASE(testMe) queue.reset(); queue2.reset(); - intrusive_ptr msgPtr(MessageUtils::createMessage("exchange", "key", "id")); + intrusive_ptr msgPtr(MessageUtils::createMessage("exchange", "key", false, "id")); DeliverableMessage msg(msgPtr); topic.route(msg, "abc", 0); direct.route(msg, "abc", 0); diff --git a/qpid/cpp/src/tests/Makefile.am b/qpid/cpp/src/tests/Makefile.am index a15ba3578c..15133505a3 100644 --- a/qpid/cpp/src/tests/Makefile.am +++ b/qpid/cpp/src/tests/Makefile.am @@ -22,6 +22,7 @@ INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src PUBLIC_INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include # Use public API only abs_builddir=@abs_builddir@ +abs_srcdir=@abs_srcdir@ extra_libs = lib_client = $(abs_builddir)/../libqpidclient.la lib_common = $(abs_builddir)/../libqpidcommon.la @@ -275,7 +276,6 @@ qpid_stream_INCLUDES=$(PUBLIC_INCLUDES) qpid_stream_SOURCES=qpid_stream.cpp qpid_stream_LDADD=$(lib_client) - TESTS_ENVIRONMENT = \ VALGRIND=$(VALGRIND) \ srcdir=$(srcdir) \ @@ -311,7 +311,7 @@ EXTRA_DIST += \ TxMocks.h \ replication_test \ run_perftest \ - ring_queue_test \ + ring_queue_test \ run_ring_queue_test check_LTLIBRARIES += libdlclose_noop.la @@ -327,25 +327,47 @@ LONG_TESTS+=start_broker fanout_perftest shared_perftest multiq_perftest topic_p run_failover_soak reliable_replication_test \ federated_cluster_test_with_node_failure -EXTRA_DIST+=fanout_perftest shared_perftest multiq_perftest topic_perftest run_failover_soak reliable_replication_test \ - federated_cluster_test_with_node_failure \ - tests.sln \ - client_test.vcproj \ - consume.vcproj \ - echotest.vcproj \ - header_test.vcproj \ - latencytest.vcproj \ - perftest.vcproj \ - publish.vcproj \ - receiver.vcproj \ - sender.vcproj \ - shlibtest.vcproj \ - topic_listener.vcproj \ - topic_publisher.vcproj \ - txjob.vcproj \ - txshift.vcproj \ - txtest.vcproj \ - unit_test.vcproj +EXTRA_DIST+= \ + python_env.sh \ + fanout_perftest \ + shared_perftest \ + multiq_perftest \ + topic_perftest \ + run_failover_soak \ + reliable_replication_test \ + federated_cluster_test_with_node_failure \ + tests.sln \ + client_test.vcproj \ + consume.vcproj \ + echotest.vcproj \ + header_test.vcproj \ + latencytest.vcproj \ + perftest.vcproj \ + publish.vcproj \ + receiver.vcproj \ + sender.vcproj \ + shlibtest.vcproj \ + topic_listener.vcproj \ + topic_publisher.vcproj \ + txjob.vcproj \ + txshift.vcproj \ + txtest.vcproj \ + unit_test.vcproj check-long: $(MAKE) check TESTS="$(LONG_TESTS)" VALGRIND= + +check: python_prep + +PYTHON_SRC_DIR=$(abs_srcdir)/../../../python +PYTHON_BLD_DIR=$(abs_builddir)/python +AMQP_SPEC_DIR=$(abs_srcdir)/../../../specs + +# Generate python client as part of the all-am target so it gets built before tests. +all-am: python_prep + +python_prep: + if test -d $(PYTHON_SRC_DIR) -a -d $(AMQP_SPEC_DIR); \ + then $(MAKE) -C $(PYTHON_SRC_DIR) install PREFIX=$(PYTHON_BLD_DIR) PYTHON_LIB=$(PYTHON_BLD_DIR) EXEC_PREFIX=$(PYTHON_BLD_DIR)/commands AMQP_SPEC_DIR=$(AMQP_SPEC_DIR); \ + else echo "WARNING: python client not built, missing one of $(PYTHON_SRC_DIR) $(AMQP_SPEC_DIR)"; fi + diff --git a/qpid/cpp/src/tests/MessageUtils.h b/qpid/cpp/src/tests/MessageUtils.h index dae74cce7d..a1b140d484 100644 --- a/qpid/cpp/src/tests/MessageUtils.h +++ b/qpid/cpp/src/tests/MessageUtils.h @@ -34,7 +34,8 @@ namespace tests { struct MessageUtils { static boost::intrusive_ptr createMessage(const string& exchange="", const string& routingKey="", - const Uuid& messageId=Uuid(true), uint64_t contentSize = 0) + const bool durable = false, const Uuid& messageId=Uuid(true), + uint64_t contentSize = 0) { boost::intrusive_ptr msg(new broker::Message()); @@ -47,6 +48,8 @@ struct MessageUtils props->setContentLength(contentSize); props->setMessageId(messageId); msg->getFrames().getHeaders()->get(true)->setRoutingKey(routingKey); + if (durable) + msg->getFrames().getHeaders()->get(true)->setDeliveryMode(2); return msg; } diff --git a/qpid/cpp/src/tests/MessagingSessionTests.cpp b/qpid/cpp/src/tests/MessagingSessionTests.cpp index f5a5420d3a..206f5ba691 100644 --- a/qpid/cpp/src/tests/MessagingSessionTests.cpp +++ b/qpid/cpp/src/tests/MessagingSessionTests.cpp @@ -22,6 +22,10 @@ #include "test_tools.h" #include "BrokerFixture.h" #include "qpid/messaging/Connection.h" +#include "qpid/messaging/ListContent.h" +#include "qpid/messaging/ListView.h" +#include "qpid/messaging/MapContent.h" +#include "qpid/messaging/MapView.h" #include "qpid/messaging/Message.h" #include "qpid/messaging/MessageListener.h" #include "qpid/messaging/Receiver.h" @@ -160,7 +164,7 @@ struct MessageDataCollector : MessageListener std::vector messageData; void received(Message& message) { - messageData.push_back(message.getBytes()); + messageData.push_back(message.getContent()); } }; @@ -169,7 +173,7 @@ std::vector fetch(Receiver& receiver, int count, qpid::sys::Duratio std::vector data; Message message; for (int i = 0; i < count && receiver.fetch(message, timeout); i++) { - data.push_back(message.getBytes()); + data.push_back(message.getContent()); } return data; } @@ -183,7 +187,7 @@ QPID_AUTO_TEST_CASE(testSimpleSendReceive) Receiver receiver = fix.session.createReceiver(fix.queue); Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); fix.session.acknowledge(); - BOOST_CHECK_EQUAL(in.getBytes(), out.getBytes()); + BOOST_CHECK_EQUAL(in.getContent(), out.getContent()); } QPID_AUTO_TEST_CASE(testSendReceiveHeaders) @@ -199,7 +203,7 @@ QPID_AUTO_TEST_CASE(testSendReceiveHeaders) Message in; for (uint i = 0; i < 10; ++i) { BOOST_CHECK(receiver.fetch(in, 5 * qpid::sys::TIME_SEC)); - BOOST_CHECK_EQUAL(in.getBytes(), out.getBytes()); + BOOST_CHECK_EQUAL(in.getContent(), out.getContent()); BOOST_CHECK_EQUAL(in.getHeaders()["a"].asUint32(), i); fix.session.acknowledge(); } @@ -229,22 +233,22 @@ QPID_AUTO_TEST_CASE(testSimpleTopic) Receiver sub1 = fix.session.createReceiver(fix.topic); sub1.setCapacity(10u); sub1.start(); - msg.setBytes("two"); + msg.setContent("two"); sender.send(msg); Receiver sub2 = fix.session.createReceiver(fix.topic); sub2.setCapacity(10u); sub2.start(); - msg.setBytes("three"); + msg.setContent("three"); sender.send(msg); Receiver sub3 = fix.session.createReceiver(fix.topic); sub3.setCapacity(10u); sub3.start(); - msg.setBytes("four"); + msg.setContent("four"); sender.send(msg); BOOST_CHECK_EQUAL(fetch(sub2, 2), boost::assign::list_of("three")("four")); sub2.cancel(); - msg.setBytes("five"); + msg.setContent("five"); sender.send(msg); BOOST_CHECK_EQUAL(fetch(sub1, 4), boost::assign::list_of("two")("three")("four")("five")); BOOST_CHECK_EQUAL(fetch(sub3, 2), boost::assign::list_of("four")("five")); @@ -274,7 +278,7 @@ QPID_AUTO_TEST_CASE(testSessionFetch) for (uint i = 0; i < fix.queues.size(); i++) { Message msg; BOOST_CHECK(fix.session.fetch(msg, qpid::sys::TIME_SEC)); - BOOST_CHECK_EQUAL(msg.getBytes(), (boost::format("Message_%1%") % (i+1)).str()); + BOOST_CHECK_EQUAL(msg.getContent(), (boost::format("Message_%1%") % (i+1)).str()); } } @@ -307,13 +311,16 @@ QPID_AUTO_TEST_CASE(testMapMessage) QueueFixture fix; Sender sender = fix.session.createSender(fix.queue); Message out; - out.getContent().asMap()["abc"] = "def"; - out.getContent().asMap()["pi"] = 3.14f; + MapContent content(out); + content["abc"] = "def"; + content["pi"] = 3.14f; + content.encode(); sender.send(out); Receiver receiver = fix.session.createReceiver(fix.queue); Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); - BOOST_CHECK_EQUAL(in.getContent().asMap()["abc"].asString(), "def"); - BOOST_CHECK_EQUAL(in.getContent().asMap()["pi"].asFloat(), 3.14f); + MapView view(in); + BOOST_CHECK_EQUAL(view["abc"].asString(), "def"); + BOOST_CHECK_EQUAL(view["pi"].asFloat(), 3.14f); fix.session.acknowledge(); } @@ -322,23 +329,31 @@ QPID_AUTO_TEST_CASE(testListMessage) QueueFixture fix; Sender sender = fix.session.createSender(fix.queue); Message out; - out.getContent() = Variant::List(); - out.getContent() << "abc"; - out.getContent() << 1234; - out.getContent() << "def"; - out.getContent() << 56.789; + ListContent content(out); + content.push_back(Variant("abc")); + content.push_back(Variant(1234)); + content.push_back(Variant("def")); + content.push_back(Variant(56.789)); + content.encode(); sender.send(out); Receiver receiver = fix.session.createReceiver(fix.queue); Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); - Variant::List& list = in.getContent().asList(); - BOOST_CHECK_EQUAL(list.size(), out.getContent().asList().size()); - BOOST_CHECK_EQUAL(list.front().asString(), "abc"); - list.pop_front(); - BOOST_CHECK_EQUAL(list.front().asInt64(), 1234); - list.pop_front(); - BOOST_CHECK_EQUAL(list.front().asString(), "def"); - list.pop_front(); - BOOST_CHECK_EQUAL(list.front().asDouble(), 56.789); + ListView view(in); + BOOST_CHECK_EQUAL(view.size(), content.size()); + BOOST_CHECK_EQUAL(view.front().asString(), "abc"); + BOOST_CHECK_EQUAL(view.back().asDouble(), 56.789); + + ListView::const_iterator i = view.begin(); + BOOST_CHECK(i != view.end()); + BOOST_CHECK_EQUAL(i->asString(), "abc"); + BOOST_CHECK(++i != view.end()); + BOOST_CHECK_EQUAL(i->asInt64(), 1234); + BOOST_CHECK(++i != view.end()); + BOOST_CHECK_EQUAL(i->asString(), "def"); + BOOST_CHECK(++i != view.end()); + BOOST_CHECK_EQUAL(i->asDouble(), 56.789); + BOOST_CHECK(++i == view.end()); + fix.session.acknowledge(); } @@ -352,10 +367,10 @@ QPID_AUTO_TEST_CASE(testReject) sender.send(m2); Receiver receiver = fix.session.createReceiver(fix.queue); Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); - BOOST_CHECK_EQUAL(in.getBytes(), m1.getBytes()); + BOOST_CHECK_EQUAL(in.getContent(), m1.getContent()); fix.session.reject(in); in = receiver.fetch(5 * qpid::sys::TIME_SEC); - BOOST_CHECK_EQUAL(in.getBytes(), m2.getBytes()); + BOOST_CHECK_EQUAL(in.getContent(), m2.getContent()); fix.session.acknowledge(); } @@ -384,15 +399,15 @@ QPID_AUTO_TEST_CASE(testAvailable) for (uint i = 0; i < 5; ++i) { BOOST_CHECK_EQUAL(fix.session.available(), 15u - 2*i); BOOST_CHECK_EQUAL(r1.available(), 10u - i); - BOOST_CHECK_EQUAL(r1.fetch().getBytes(), (boost::format("A_%1%") % (i+1)).str()); + BOOST_CHECK_EQUAL(r1.fetch().getContent(), (boost::format("A_%1%") % (i+1)).str()); BOOST_CHECK_EQUAL(r2.available(), 5u - i); - BOOST_CHECK_EQUAL(r2.fetch().getBytes(), (boost::format("B_%1%") % (i+1)).str()); + BOOST_CHECK_EQUAL(r2.fetch().getContent(), (boost::format("B_%1%") % (i+1)).str()); fix.session.acknowledge(); } for (uint i = 5; i < 10; ++i) { BOOST_CHECK_EQUAL(fix.session.available(), 10u - i); BOOST_CHECK_EQUAL(r1.available(), 10u - i); - BOOST_CHECK_EQUAL(r1.fetch().getBytes(), (boost::format("A_%1%") % (i+1)).str()); + BOOST_CHECK_EQUAL(r1.fetch().getContent(), (boost::format("A_%1%") % (i+1)).str()); } } @@ -405,7 +420,7 @@ QPID_AUTO_TEST_CASE(testPendingAck) } Receiver receiver = fix.session.createReceiver(fix.queue); for (uint i = 0; i < 10; ++i) { - BOOST_CHECK_EQUAL(receiver.fetch().getBytes(), (boost::format("Message_%1%") % (i+1)).str()); + BOOST_CHECK_EQUAL(receiver.fetch().getContent(), (boost::format("Message_%1%") % (i+1)).str()); } BOOST_CHECK_EQUAL(fix.session.pendingAck(), 0u); fix.session.acknowledge(); @@ -431,7 +446,7 @@ QPID_AUTO_TEST_CASE(testPendingSend) Receiver receiver = fix.session.createReceiver(fix.queue); for (uint i = 0; i < 10; ++i) { - BOOST_CHECK_EQUAL(receiver.fetch().getBytes(), (boost::format("Message_%1%") % (i+1)).str()); + BOOST_CHECK_EQUAL(receiver.fetch().getContent(), (boost::format("Message_%1%") % (i+1)).str()); } fix.session.acknowledge(); } diff --git a/qpid/cpp/src/tests/PartialFailure.cpp b/qpid/cpp/src/tests/PartialFailure.cpp index 8d9970f909..5de8ecb189 100644 --- a/qpid/cpp/src/tests/PartialFailure.cpp +++ b/qpid/cpp/src/tests/PartialFailure.cpp @@ -105,7 +105,6 @@ QPID_AUTO_TEST_CASE(testCoincidentErrors) { } } -#if 0 // FIXME aconway 2009-07-30: // Verify normal cluster-wide errors. QPID_AUTO_TEST_CASE(testNormalErrors) { // FIXME aconway 2009-04-10: Would like to put a scope just around @@ -120,7 +119,7 @@ QPID_AUTO_TEST_CASE(testNormalErrors) { { ScopedSuppressLogging allQuiet; - queueAndsub(c0); + queueAndSub(c0); c0.session.messageTransfer(content=Message("x", "c0")); BOOST_CHECK_EQUAL(c0.lq.get(TIMEOUT).getData(), "x"); @@ -258,7 +257,7 @@ QPID_AUTO_TEST_CASE(testPartialFailureMemberLeaves) { } } #endif -#endif // FIXME aconway 2009-07-30: + QPID_AUTO_TEST_SUITE_END() }} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/QueuePolicyTest.cpp b/qpid/cpp/src/tests/QueuePolicyTest.cpp index f40d30b588..875976db85 100644 --- a/qpid/cpp/src/tests/QueuePolicyTest.cpp +++ b/qpid/cpp/src/tests/QueuePolicyTest.cpp @@ -48,56 +48,56 @@ QueuedMessage createMessage(uint32_t size) QPID_AUTO_TEST_CASE(testCount) { - std::auto_ptr policy(QueuePolicy::createQueuePolicy(5, 0)); + std::auto_ptr policy(QueuePolicy::createQueuePolicy("test", 5, 0)); BOOST_CHECK_EQUAL((uint64_t) 0, policy->getMaxSize()); BOOST_CHECK_EQUAL((uint32_t) 5, policy->getMaxCount()); QueuedMessage msg = createMessage(10); for (size_t i = 0; i < 5; i++) { - policy->tryEnqueue(msg); + policy->tryEnqueue(msg.payload); } try { - policy->tryEnqueue(msg); + policy->tryEnqueue(msg.payload); BOOST_FAIL("Policy did not fail on enqueuing sixth message"); } catch (const ResourceLimitExceededException&) {} policy->dequeued(msg); - policy->tryEnqueue(msg); + policy->tryEnqueue(msg.payload); try { - policy->tryEnqueue(msg); + policy->tryEnqueue(msg.payload); BOOST_FAIL("Policy did not fail on enqueuing sixth message (after dequeue)"); } catch (const ResourceLimitExceededException&) {} } QPID_AUTO_TEST_CASE(testSize) { - std::auto_ptr policy(QueuePolicy::createQueuePolicy(0, 50)); + std::auto_ptr policy(QueuePolicy::createQueuePolicy("test", 0, 50)); QueuedMessage msg = createMessage(10); for (size_t i = 0; i < 5; i++) { - policy->tryEnqueue(msg); + policy->tryEnqueue(msg.payload); } try { - policy->tryEnqueue(msg); + policy->tryEnqueue(msg.payload); BOOST_FAIL("Policy did not fail on aggregate size exceeding 50. " << *policy); } catch (const ResourceLimitExceededException&) {} policy->dequeued(msg); - policy->tryEnqueue(msg); + policy->tryEnqueue(msg.payload); try { - policy->tryEnqueue(msg); + policy->tryEnqueue(msg.payload); BOOST_FAIL("Policy did not fail on aggregate size exceeding 50 (after dequeue). " << *policy); } catch (const ResourceLimitExceededException&) {} } QPID_AUTO_TEST_CASE(testBoth) { - std::auto_ptr policy(QueuePolicy::createQueuePolicy(5, 50)); + std::auto_ptr policy(QueuePolicy::createQueuePolicy("test", 5, 50)); try { QueuedMessage msg = createMessage(51); - policy->tryEnqueue(msg); + policy->tryEnqueue(msg.payload); BOOST_FAIL("Policy did not fail on single message exceeding 50. " << *policy); } catch (const ResourceLimitExceededException&) {} @@ -108,17 +108,17 @@ QPID_AUTO_TEST_CASE(testBoth) messages.push_back(createMessage(2)); messages.push_back(createMessage(7)); for (size_t i = 0; i < messages.size(); i++) { - policy->tryEnqueue(messages[i]); + policy->tryEnqueue(messages[i].payload); } //size = 45 at this point, count = 5 try { QueuedMessage msg = createMessage(5); - policy->tryEnqueue(msg); + policy->tryEnqueue(msg.payload); BOOST_FAIL("Policy did not fail on count exceeding 6. " << *policy); } catch (const ResourceLimitExceededException&) {} try { QueuedMessage msg = createMessage(10); - policy->tryEnqueue(msg); + policy->tryEnqueue(msg.payload); BOOST_FAIL("Policy did not fail on aggregate size exceeding 50. " << *policy); } catch (const ResourceLimitExceededException&) {} @@ -126,7 +126,7 @@ QPID_AUTO_TEST_CASE(testBoth) policy->dequeued(messages[0]); try { QueuedMessage msg = createMessage(20); - policy->tryEnqueue(msg); + policy->tryEnqueue(msg.payload); } catch (const ResourceLimitExceededException&) { BOOST_FAIL("Policy failed incorrectly after dequeue. " << *policy); } @@ -135,10 +135,10 @@ QPID_AUTO_TEST_CASE(testBoth) QPID_AUTO_TEST_CASE(testSettings) { //test reading and writing the policy from/to field table - std::auto_ptr a(QueuePolicy::createQueuePolicy(101, 303)); + std::auto_ptr a(QueuePolicy::createQueuePolicy("test", 101, 303)); FieldTable settings; a->update(settings); - std::auto_ptr b(QueuePolicy::createQueuePolicy(settings)); + std::auto_ptr b(QueuePolicy::createQueuePolicy("test", settings)); BOOST_CHECK_EQUAL(a->getMaxCount(), b->getMaxCount()); BOOST_CHECK_EQUAL(a->getMaxSize(), b->getMaxSize()); } @@ -146,7 +146,7 @@ QPID_AUTO_TEST_CASE(testSettings) QPID_AUTO_TEST_CASE(testRingPolicy) { FieldTable args; - std::auto_ptr policy = QueuePolicy::createQueuePolicy(5, 0, QueuePolicy::RING); + std::auto_ptr policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::RING); policy->update(args); ProxySessionFixture f; @@ -175,7 +175,7 @@ QPID_AUTO_TEST_CASE(testRingPolicy) QPID_AUTO_TEST_CASE(testStrictRingPolicy) { FieldTable args; - std::auto_ptr policy = QueuePolicy::createQueuePolicy(5, 0, QueuePolicy::RING_STRICT); + std::auto_ptr policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::RING_STRICT); policy->update(args); ProxySessionFixture f; @@ -201,7 +201,7 @@ QPID_AUTO_TEST_CASE(testStrictRingPolicy) QPID_AUTO_TEST_CASE(testPolicyWithDtx) { FieldTable args; - std::auto_ptr policy = QueuePolicy::createQueuePolicy(5, 0, QueuePolicy::REJECT); + std::auto_ptr policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::REJECT); policy->update(args); ProxySessionFixture f; @@ -282,6 +282,22 @@ QPID_AUTO_TEST_CASE(testFlowToDiskWithNoStore) } catch (const ResourceLimitExceededException&) {} } +QPID_AUTO_TEST_CASE(testPolicyFailureOnCommit) +{ + FieldTable args; + std::auto_ptr policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::REJECT); + policy->update(args); + + ProxySessionFixture f; + std::string q("q"); + f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args); + f.session.txSelect(); + for (int i = 0; i < 10; i++) { + f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q)); + } + ScopedSuppressLogging sl; // Suppress messages for expected errors. + BOOST_CHECK_THROW(f.session.txCommit(), InternalErrorException); +} QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/QueueTest.cpp b/qpid/cpp/src/tests/QueueTest.cpp index 841a19f7c1..3cfaa763ca 100644 --- a/qpid/cpp/src/tests/QueueTest.cpp +++ b/qpid/cpp/src/tests/QueueTest.cpp @@ -18,10 +18,13 @@ * under the License. * */ +#include "MessageUtils.h" #include "unit_test.h" #include "test_tools.h" #include "qpid/Exception.h" #include "qpid/broker/Broker.h" +#include "qpid/broker/DeliverableMessage.h" +#include "qpid/broker/FanOutExchange.h" #include "qpid/broker/Queue.h" #include "qpid/broker/Deliverable.h" #include "qpid/broker/ExchangeRegistry.h" @@ -30,12 +33,16 @@ #include "qpid/broker/ExpiryPolicy.h" #include "qpid/framing/MessageTransferBody.h" #include "qpid/client/QueueOptions.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/reply_exceptions.h" #include #include "boost/format.hpp" using boost::intrusive_ptr; using namespace qpid; using namespace qpid::broker; +using namespace qpid::client; using namespace qpid::framing; using namespace qpid::sys; @@ -61,13 +68,14 @@ public: class FailOnDeliver : public Deliverable { - Message msg; + boost::intrusive_ptr msg; public: + FailOnDeliver() : msg(MessageUtils::createMessage()) {} void deliverTo(const boost::shared_ptr& queue) { throw Exception(QPID_MSG("Invalid delivery to " << queue->getName())); } - Message& getMessage() { return msg; } + Message& getMessage() { return *(msg.get()); } }; intrusive_ptr create_message(std::string exchange, std::string routingKey) { @@ -210,8 +218,7 @@ QPID_AUTO_TEST_CASE(testDequeue){ } -QPID_AUTO_TEST_CASE(testBound) -{ +QPID_AUTO_TEST_CASE(testBound){ //test the recording of bindings, and use of those to allow a queue to be unbound string key("my-key"); FieldTable args; @@ -245,7 +252,6 @@ QPID_AUTO_TEST_CASE(testBound) } QPID_AUTO_TEST_CASE(testPersistLastNodeStanding){ - client::QueueOptions args; args.setPersistLastNode(); @@ -273,14 +279,35 @@ QPID_AUTO_TEST_CASE(testPersistLastNodeStanding){ } -class TestMessageStoreOC : public NullMessageStore +const std::string nullxid = ""; + +class SimpleDummyCtxt : public TransactionContext {}; + +class DummyCtxt : public TPCTransactionContext +{ + const std::string xid; + public: + DummyCtxt(const std::string& _xid) : xid(_xid) {} + static std::string getXid(TransactionContext& ctxt) + { + DummyCtxt* c(dynamic_cast(&ctxt)); + return c ? c->xid : nullxid; + } +}; + +class TestMessageStoreOC : public MessageStore { + std::set prepared; + uint64_t nextPersistenceId; public: uint enqCnt; uint deqCnt; bool error; + TestMessageStoreOC() : MessageStore(),nextPersistenceId(1),enqCnt(0),deqCnt(0),error(false) {} + ~TestMessageStoreOC(){} + virtual void dequeue(TransactionContext*, const boost::intrusive_ptr& /*msg*/, const PersistableQueue& /*queue*/) @@ -290,11 +317,12 @@ class TestMessageStoreOC : public NullMessageStore } virtual void enqueue(TransactionContext*, - const boost::intrusive_ptr& /*msg*/, + const boost::intrusive_ptr& msg, const PersistableQueue& /* queue */) { if (error) throw Exception("Enqueue error test"); enqCnt++; + msg->enqueueComplete(); } void createError() @@ -302,8 +330,32 @@ class TestMessageStoreOC : public NullMessageStore error=true; } - TestMessageStoreOC() : NullMessageStore(),enqCnt(0),deqCnt(0),error(false) {} - ~TestMessageStoreOC(){} + bool init(const Options*) { return true; } + void truncateInit(const bool) {} + void create(PersistableQueue& queue, const framing::FieldTable&) { queue.setPersistenceId(nextPersistenceId++); } + void destroy(PersistableQueue&) {} + void create(const PersistableExchange& exchange, const framing::FieldTable&) { exchange.setPersistenceId(nextPersistenceId++); } + void destroy(const PersistableExchange&) {} + void bind(const PersistableExchange&, const PersistableQueue&, const std::string&, const framing::FieldTable&) {} + void unbind(const PersistableExchange&, const PersistableQueue&, const std::string&, const framing::FieldTable&) {} + void create(const PersistableConfig& config) { config.setPersistenceId(nextPersistenceId++); } + void destroy(const PersistableConfig&) {} + void stage(const boost::intrusive_ptr&) {} + void destroy(PersistableMessage&) {} + void appendContent(const boost::intrusive_ptr&, const std::string&) {} + void loadContent(const qpid::broker::PersistableQueue&, const boost::intrusive_ptr&, + std::string&, uint64_t, uint32_t) { throw qpid::framing::InternalErrorException("Can't load content; persistence not enabled"); } + void flush(const qpid::broker::PersistableQueue&) {} + uint32_t outstandingQueueAIO(const PersistableQueue&) { return 0; } + + std::auto_ptr begin() { return std::auto_ptr(new SimpleDummyCtxt()); } + std::auto_ptr begin(const std::string& xid) { return std::auto_ptr(new DummyCtxt(xid)); } + void prepare(TPCTransactionContext& ctxt) { prepared.insert(DummyCtxt::getXid(ctxt)); } + void commit(TransactionContext& ctxt) { prepared.erase(DummyCtxt::getXid(ctxt)); } + void abort(TransactionContext& ctxt) { prepared.erase(DummyCtxt::getXid(ctxt)); } + void collectPreparedXids(std::set& out) { out.insert(prepared.begin(), prepared.end()); } + + void recover(RecoveryManager&) {} }; @@ -703,7 +755,7 @@ not requeued to the store. QPID_AUTO_TEST_CASE(testLastNodeJournalError){ /* -simulate store excption going into last node standing +simulate store exception going into last node standing */ TestMessageStoreOC testStore; @@ -727,16 +779,271 @@ simulate store excption going into last node standing } -intrusive_ptr mkMsg(std::string exchange, std::string routingKey) { - intrusive_ptr msg(new Message()); - AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0))); - AMQFrame header((AMQHeaderBody())); - msg->getFrames().append(method); - msg->getFrames().append(header); - msg->getFrames().getHeaders()->get(true)->setRoutingKey(routingKey); +intrusive_ptr mkMsg(MessageStore& store, std::string content = "", bool durable = false) +{ + intrusive_ptr msg = MessageUtils::createMessage("", "", durable); + if (content.size()) MessageUtils::addContent(msg, content); + msg->setStore(&store); return msg; } +QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ + + TestMessageStoreOC testStore; + client::QueueOptions args0; // No size policy + client::QueueOptions args1; + args1.setSizePolicy(FLOW_TO_DISK, 0, 1); + client::QueueOptions args2; + args2.setSizePolicy(FLOW_TO_DISK, 0, 2); + + // --- Fanout exchange bound to single transient queue ------------------------------------------------------------- + + FanOutExchange sbtFanout1("sbtFanout1", false, args0); // single binding to transient queue + Queue::shared_ptr tq1(new Queue("tq1", true)); // transient w/ limit + tq1->configure(args1); + sbtFanout1.bind(tq1, "", 0); + + intrusive_ptr msg01 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content + DeliverableMessage dmsg01(msg01); + sbtFanout1.route(dmsg01, "", 0); // Brings queue 1 to capacity limit + msg01->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg01->isContentReleased(), false); + BOOST_CHECK_EQUAL(1u, tq1->getMessageCount()); + + intrusive_ptr msg02 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content + DeliverableMessage dmsg02(msg02); + BOOST_CHECK_THROW(sbtFanout1.route(dmsg02, "", 0), ResourceLimitExceededException); + msg02->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg02->isContentReleased(), false); + BOOST_CHECK_EQUAL(1u, tq1->getMessageCount()); + + intrusive_ptr msg03 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content + DeliverableMessage dmsg03(msg03); + BOOST_CHECK_THROW(sbtFanout1.route(dmsg03, "", 0), ResourceLimitExceededException); + msg03->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg03->isContentReleased(), false); + BOOST_CHECK_EQUAL(1u, tq1->getMessageCount()); + + intrusive_ptr msg04 = mkMsg(testStore); // transient no content + DeliverableMessage dmsg04(msg04); + BOOST_CHECK_THROW(sbtFanout1.route(dmsg04, "", 0), ResourceLimitExceededException); + msg04->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg04->isContentReleased(), false); + BOOST_CHECK_EQUAL(1u, tq1->getMessageCount()); + + intrusive_ptr msg05 = mkMsg(testStore, "", true); // durable no content + DeliverableMessage dmsg05(msg05); + BOOST_CHECK_THROW(sbtFanout1.route(dmsg05, "", 0), ResourceLimitExceededException); + msg05->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg05->isContentReleased(), false); + BOOST_CHECK_EQUAL(1u, tq1->getMessageCount()); + + // --- Fanout exchange bound to single durable queue --------------------------------------------------------------- + + FanOutExchange sbdFanout2("sbdFanout2", false, args0); // single binding to durable queue + Queue::shared_ptr dq2(new Queue("dq2", true, &testStore)); // durable w/ limit + dq2->configure(args1); + sbdFanout2.bind(dq2, "", 0); + + intrusive_ptr msg06 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content + DeliverableMessage dmsg06(msg06); + sbdFanout2.route(dmsg06, "", 0); // Brings queue 2 to capacity limit + msg06->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg06->isContentReleased(), false); + BOOST_CHECK_EQUAL(1u, dq2->getMessageCount()); + + intrusive_ptr msg07 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content + DeliverableMessage dmsg07(msg07); + sbdFanout2.route(dmsg07, "", 0); + msg07->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg07->isContentReleased(), true); + BOOST_CHECK_EQUAL(2u, dq2->getMessageCount()); + + intrusive_ptr msg08 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content + DeliverableMessage dmsg08(msg08); + sbdFanout2.route(dmsg08, "", 0); + msg08->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg08->isContentReleased(), true); + BOOST_CHECK_EQUAL(3u, dq2->getMessageCount()); + + intrusive_ptr msg09 = mkMsg(testStore); // transient no content + DeliverableMessage dmsg09(msg09); + sbdFanout2.route(dmsg09, "", 0); + msg09->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg09->isContentReleased(), true); + BOOST_CHECK_EQUAL(4u, dq2->getMessageCount()); + + intrusive_ptr msg10 = mkMsg(testStore, "", true); // durable no content + DeliverableMessage dmsg10(msg10); + sbdFanout2.route(dmsg10, "", 0); + msg10->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg10->isContentReleased(), true); + BOOST_CHECK_EQUAL(5u, dq2->getMessageCount()); + + // --- Fanout exchange bound to multiple durable queues ------------------------------------------------------------ + + FanOutExchange mbdFanout3("mbdFanout3", false, args0); // multiple bindings to durable queues + Queue::shared_ptr dq3(new Queue("dq3", true, &testStore)); // durable w/ limit 2 + dq3->configure(args2); + mbdFanout3.bind(dq3, "", 0); + Queue::shared_ptr dq4(new Queue("dq4", true, &testStore)); // durable w/ limit 1 + dq4->configure(args1); + mbdFanout3.bind(dq4, "", 0); + Queue::shared_ptr dq5(new Queue("dq5", true, &testStore)); // durable no limit + dq5->configure(args0); + mbdFanout3.bind(dq5, "", 0); + + intrusive_ptr msg11 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content + DeliverableMessage dmsg11(msg11); + mbdFanout3.route(dmsg11, "", 0); // Brings queues 3 and 4 to capacity limit + msg11->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg11->isContentReleased(), false); + BOOST_CHECK_EQUAL(1u, dq3->getMessageCount()); + BOOST_CHECK_EQUAL(1u, dq4->getMessageCount()); + BOOST_CHECK_EQUAL(1u, dq5->getMessageCount()); + + intrusive_ptr msg12 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content + DeliverableMessage dmsg12(msg12); + mbdFanout3.route(dmsg12, "", 0); + msg12->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg12->isContentReleased(), false); // XXXX - consequence of transient msg multi-queue ftd policy-handling limitations, fix in broker at some point! + BOOST_CHECK_EQUAL(2u, dq3->getMessageCount()); + BOOST_CHECK_EQUAL(2u, dq4->getMessageCount()); + BOOST_CHECK_EQUAL(2u, dq5->getMessageCount()); + + intrusive_ptr msg13 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content + DeliverableMessage dmsg13(msg13); + mbdFanout3.route(dmsg13, "", 0); + msg13->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg13->isContentReleased(), true); + BOOST_CHECK_EQUAL(3u, dq3->getMessageCount()); + BOOST_CHECK_EQUAL(3u, dq4->getMessageCount()); + BOOST_CHECK_EQUAL(3u, dq5->getMessageCount()); + + intrusive_ptr msg14 = mkMsg(testStore); // transient no content + DeliverableMessage dmsg14(msg14); + mbdFanout3.route(dmsg14, "", 0); + msg14->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg14->isContentReleased(), false); // XXXX - consequence of transient msg multi-queue ftd policy-handling limitations, fix in broker at some point! + BOOST_CHECK_EQUAL(4u, dq3->getMessageCount()); + BOOST_CHECK_EQUAL(4u, dq4->getMessageCount()); + BOOST_CHECK_EQUAL(4u, dq5->getMessageCount()); + + intrusive_ptr msg15 = mkMsg(testStore, "", true); // durable no content + DeliverableMessage dmsg15(msg15); + mbdFanout3.route(dmsg15, "", 0); + msg15->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg15->isContentReleased(), true); + BOOST_CHECK_EQUAL(5u, dq3->getMessageCount()); + BOOST_CHECK_EQUAL(5u, dq4->getMessageCount()); + BOOST_CHECK_EQUAL(5u, dq5->getMessageCount()); + + // Bind a transient queue, this should block the release of any further messages. + // Note: this will result in a violation of the count policy of dq3 and dq4 - but this + // is expected until a better overall multi-queue design is implemented. Similarly + // for the other tests in this section. + + Queue::shared_ptr tq6(new Queue("tq6", true)); // transient no limit + tq6->configure(args0); + mbdFanout3.bind(tq6, "", 0); + + intrusive_ptr msg16 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content + DeliverableMessage dmsg16(msg16); + mbdFanout3.route(dmsg16, "", 0); + msg16->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg16->isContentReleased(), false); + BOOST_CHECK_EQUAL(6u, dq3->getMessageCount()); + BOOST_CHECK_EQUAL(6u, dq4->getMessageCount()); + BOOST_CHECK_EQUAL(6u, dq5->getMessageCount()); + + intrusive_ptr msg17 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content + DeliverableMessage dmsg17(msg17); + mbdFanout3.route(dmsg17, "", 0); + msg17->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg17->isContentReleased(), false); + BOOST_CHECK_EQUAL(7u, dq3->getMessageCount()); + BOOST_CHECK_EQUAL(7u, dq4->getMessageCount()); + BOOST_CHECK_EQUAL(7u, dq5->getMessageCount()); + + intrusive_ptr msg18 = mkMsg(testStore); // transient no content + DeliverableMessage dmsg18(msg18); + mbdFanout3.route(dmsg18, "", 0); + msg18->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg18->isContentReleased(), false); + BOOST_CHECK_EQUAL(8u, dq3->getMessageCount()); + BOOST_CHECK_EQUAL(8u, dq4->getMessageCount()); + BOOST_CHECK_EQUAL(8u, dq5->getMessageCount()); + + intrusive_ptr msg19 = mkMsg(testStore, "", true); // durable no content + DeliverableMessage dmsg19(msg19); + mbdFanout3.route(dmsg19, "", 0); + msg19->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg19->isContentReleased(), false); + BOOST_CHECK_EQUAL(9u, dq3->getMessageCount()); + BOOST_CHECK_EQUAL(9u, dq4->getMessageCount()); + BOOST_CHECK_EQUAL(9u, dq5->getMessageCount()); + + + // --- Fanout exchange bound to multiple durable and transient queues ---------------------------------------------- + + FanOutExchange mbmFanout4("mbmFanout4", false, args0); // multiple bindings to durable/transient queues + Queue::shared_ptr dq7(new Queue("dq7", true, &testStore)); // durable no limit + dq7->configure(args0); + mbmFanout4.bind(dq7, "", 0); + Queue::shared_ptr dq8(new Queue("dq8", true, &testStore)); // durable w/ limit + dq8->configure(args1); + mbmFanout4.bind(dq8, "", 0); + Queue::shared_ptr tq9(new Queue("tq9", true)); // transient no limit + tq9->configure(args0); + mbmFanout4.bind(tq9, "", 0); + + intrusive_ptr msg20 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content + DeliverableMessage dmsg20(msg20); + mbmFanout4.route(dmsg20, "", 0); // Brings queue 7 to capacity limit + msg20->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg20->isContentReleased(), false); + BOOST_CHECK_EQUAL(1u, dq7->getMessageCount()); + BOOST_CHECK_EQUAL(1u, dq8->getMessageCount()); + BOOST_CHECK_EQUAL(1u, tq9->getMessageCount()); + + intrusive_ptr msg21 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content + DeliverableMessage dmsg21(msg21); + mbmFanout4.route(dmsg21, "", 0); + msg21->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg21->isContentReleased(), false); + BOOST_CHECK_EQUAL(2u, dq7->getMessageCount()); // over limit + BOOST_CHECK_EQUAL(2u, dq8->getMessageCount()); + BOOST_CHECK_EQUAL(2u, tq9->getMessageCount()); + + intrusive_ptr msg22 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content + DeliverableMessage dmsg22(msg22); + mbmFanout4.route(dmsg22, "", 0); + msg22->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg22->isContentReleased(), false); + BOOST_CHECK_EQUAL(3u, dq7->getMessageCount()); // over limit + BOOST_CHECK_EQUAL(3u, dq8->getMessageCount()); // over limit + BOOST_CHECK_EQUAL(3u, tq9->getMessageCount()); + + intrusive_ptr msg23 = mkMsg(testStore); // transient no content + DeliverableMessage dmsg23(msg23); + mbmFanout4.route(dmsg23, "", 0); + msg23->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg23->isContentReleased(), false); + BOOST_CHECK_EQUAL(4u, dq7->getMessageCount()); // over limit + BOOST_CHECK_EQUAL(4u, dq8->getMessageCount()); // over limit + BOOST_CHECK_EQUAL(4u, tq9->getMessageCount()); + + intrusive_ptr msg24 = mkMsg(testStore, "", true); // durable no content + DeliverableMessage dmsg24(msg24); + mbmFanout4.route(dmsg24, "", 0); + msg24->tryReleaseContent(); + BOOST_CHECK_EQUAL(msg24->isContentReleased(), false); + BOOST_CHECK_EQUAL(5u, dq7->getMessageCount()); // over limit + BOOST_CHECK_EQUAL(5u, dq8->getMessageCount()); // over limit + BOOST_CHECK_EQUAL(5u, tq9->getMessageCount()); +} + + QPID_AUTO_TEST_SUITE_END() }} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/TxPublishTest.cpp b/qpid/cpp/src/tests/TxPublishTest.cpp index fabb01b864..6b44d95baa 100644 --- a/qpid/cpp/src/tests/TxPublishTest.cpp +++ b/qpid/cpp/src/tests/TxPublishTest.cpp @@ -50,7 +50,7 @@ struct TxPublishTest TxPublishTest() : queue1(new Queue("queue1", false, &store, 0)), queue2(new Queue("queue2", false, &store, 0)), - msg(MessageUtils::createMessage("exchange", "routing_key", "id")), + msg(MessageUtils::createMessage("exchange", "routing_key", false, "id")), op(msg) { msg->getProperties()->setDeliveryMode(PERSISTENT); diff --git a/qpid/cpp/src/tests/acl.py b/qpid/cpp/src/tests/acl.py index fc53d2ce8b..2d776e9941 100755 --- a/qpid/cpp/src/tests/acl.py +++ b/qpid/cpp/src/tests/acl.py @@ -220,10 +220,11 @@ class ACLTests(TestBase010): """ aclf = ACLFile() aclf.write('acl deny bob@QPID create queue name=q1 durable=true passive=true\n') - aclf.write('acl deny bob@QPID create queue name=q2 exclusive=true\n') + aclf.write('acl deny bob@QPID create queue name=q2 exclusive=true policytype=ring\n') aclf.write('acl deny bob@QPID access queue name=q3\n') aclf.write('acl deny bob@QPID purge queue name=q3\n') - aclf.write('acl deny bob@QPID delete queue name=q4\n') + aclf.write('acl deny bob@QPID delete queue name=q4\n') + aclf.write('acl deny bob@QPID create queue name=q5 maxqueuesize=1000 maxqueuecount=100\n') aclf.write('acl allow all all') aclf.close() @@ -241,18 +242,40 @@ class ACLTests(TestBase010): session = self.get_session('bob','bob') try: - session.queue_declare(queue="q2", exclusive=True) - self.fail("ACL should deny queue create request with name=q2 exclusive=true"); + queue_options = {} + queue_options["qpid.policy_type"] = "ring" + session.queue_declare(queue="q2", exclusive=True, arguments=queue_options) + self.fail("ACL should deny queue create request with name=q2 exclusive=true qpid.policy_type=ring"); except qpid.session.SessionException, e: self.assertEqual(530,e.args[0].error_code) session = self.get_session('bob','bob') try: - session.queue_declare(queue="q2", durable=True) + queue_options = {} + queue_options["qpid.policy_type"] = "ring_strict" + session.queue_declare(queue="q2", exclusive=True, arguments=queue_options) except qpid.session.SessionException, e: if (530 == e.args[0].error_code): - self.fail("ACL should allow queue create request for q2 with any parameter other than exclusive=true"); + self.fail("ACL should allow queue create request with name=q2 exclusive=true qpid.policy_type=ring_strict"); + try: + queue_options = {} + queue_options["qpid.max_count"] = 200 + queue_options["qpid.max_size"] = 500 + session.queue_declare(queue="q5", exclusive=True, arguments=queue_options) + self.fail("ACL should deny queue create request with name=q2, qpid.max_size=500 and qpid.max_count=200"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + queue_options = {} + queue_options["qpid.max_count"] = 200 + queue_options["qpid.max_size"] = 100 + session.queue_declare(queue="q2", exclusive=True, arguments=queue_options) + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue create request with name=q2, qpid.max_size=100 and qpid.max_count=200 "); try: session.queue_declare(queue="q3", exclusive=True) session.queue_declare(queue="q4", durable=True) @@ -300,12 +323,13 @@ class ACLTests(TestBase010): """ aclf = ACLFile() aclf.write('acl allow bob@QPID create queue name=q1 durable=true passive=true\n') - aclf.write('acl allow bob@QPID create queue name=q2 exclusive=true\n') + aclf.write('acl allow bob@QPID create queue name=q2 exclusive=true policytype=ring\n') aclf.write('acl allow bob@QPID access queue name=q3\n') aclf.write('acl allow bob@QPID purge queue name=q3\n') aclf.write('acl allow bob@QPID create queue name=q3\n') aclf.write('acl allow bob@QPID create queue name=q4\n') - aclf.write('acl allow bob@QPID delete queue name=q4\n') + aclf.write('acl allow bob@QPID delete queue name=q4\n') + aclf.write('acl allow bob@QPID create queue name=q5 maxqueuesize=1000 maxqueuecount=100\n') aclf.write('acl allow guest@QPID all all\n') aclf.write('acl deny all all') aclf.close() @@ -337,10 +361,31 @@ class ACLTests(TestBase010): session = self.get_session('bob','bob') try: - session.queue_declare(queue="q2", exclusive=True) + queue_options = {} + queue_options["qpid.max_count"] = 200 + queue_options["qpid.max_size"] = 500 + session.queue_declare(queue="q5", arguments=queue_options) + self.fail("ACL should deny queue create request with name=q2 maxqueuesize=500 maxqueuecount=200"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + queue_options = {} + queue_options["qpid.max_count"] = 100 + queue_options["qpid.max_size"] = 500 + session.queue_declare(queue="q5", arguments=queue_options) except qpid.session.SessionException, e: if (530 == e.args[0].error_code): - self.fail("ACL should allow queue create request for q2 with exclusive=true"); + self.fail("ACL should allow queue create request with name=q2 maxqueuesize=500 maxqueuecount=200"); + + try: + queue_options = {} + queue_options["qpid.policy_type"] = "ring" + session.queue_declare(queue="q2", exclusive=True, arguments=queue_options) + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue create request for q2 with exclusive=true policytype=ring"); try: session.queue_declare(queue="q3") @@ -733,7 +778,7 @@ class ACLTests(TestBase010): # ACL publish tests #===================================== - def test_publish_acl(self): + def test_publish_acl_allow_mode(self): """ Test various publish acl """ @@ -779,4 +824,61 @@ class ACLTests(TestBase010): session.message_transfer(destination="amq.direct", message=Message(props,"Test")) except qpid.session.SessionException, e: if (530 == e.args[0].error_code): - self.fail("ACL should allow message transfer to exchange amq.direct"); + self.fail("ACL should allow message transfer to exchange amq.direct with routing key rk2"); + + + def test_publish_acl_deny_mode(self): + """ + Test various publish acl + """ + aclf = ACLFile() + aclf.write('acl allow bob@QPID publish exchange name=amq.direct routingkey=rk1\n') + aclf.write('acl allow bob@QPID publish exchange name=amq.topic\n') + aclf.write('acl allow bob@QPID publish exchange name=myEx routingkey=rk2\n') + aclf.write('acl allow bob@QPID create exchange\n') + aclf.write('acl allow guest@QPID all all \n') + aclf.write('acl deny all all') + aclf.close() + + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) + + session = self.get_session('bob','bob') + + props = session.delivery_properties(routing_key="rk2") + + try: + session.message_transfer(destination="amq.direct", message=Message(props,"Test")) + self.fail("ACL should deny message transfer to name=amq.direct routingkey=rk2"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.message_transfer(destination="amq.topic", message=Message(props,"Test")) + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow message transfer to exchange amq.topic with any routing key"); + + try: + session.exchange_declare(exchange='myEx', type='direct', durable=False) + session.message_transfer(destination="myEx", message=Message(props,"Test")) + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow message transfer to exchange myEx with routing key=rk2"); + + props = session.delivery_properties(routing_key="rk1") + + try: + session.message_transfer(destination="myEx", message=Message(props,"Test")) + self.fail("ACL should deny message transfer to name=myEx routingkey=rk1"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.message_transfer(destination="amq.direct", message=Message(props,"Test")) + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow message transfer to exchange amq.direct with routing key rk1"); diff --git a/qpid/cpp/src/tests/ais_check b/qpid/cpp/src/tests/ais_check index 79862d7439..92eaa9dd39 100755 --- a/qpid/cpp/src/tests/ais_check +++ b/qpid/cpp/src/tests/ais_check @@ -21,35 +21,14 @@ srcdir=`dirname $0` # Check AIS requirements and run tests if found. -id -nG | grep '\' >/dev/null || \ - NOGROUP="You are not a member of the ais group." -ps -u root | grep 'aisexec\|corosync' >/dev/null || \ - NOAISEXEC="The aisexec or corosync daemon is not running as root" - -if test -n "$NOGROUP" -o -n "$NOAISEXEC"; then - cat </dev/null || { + echo WARNING: Skipping cluster tests, the aisexec or corosync daemon is not running. exit 0; # A warning, not a failure. -fi +} -# Execute command with the ais group set. +# Execute command with the ais group set if user is a member. with_ais_group() { - id -nG | grep '\' >/dev/null || { echo "You are not a member of the ais group."; exit 1; } - echo $* | newgrp ais + if id -nG | grep '\' >/dev/null; then sg ais -c "$*" + else "$@" + fi } - -# Run the tests -srcdir=`dirname $0` -with_ais_group $srcdir/run_test ./cluster_test || ERROR=1 -exit $ERROR - diff --git a/qpid/cpp/src/tests/background.ps1 b/qpid/cpp/src/tests/background.ps1 index 934078602b..36e9e4e6e9 100644 --- a/qpid/cpp/src/tests/background.ps1 +++ b/qpid/cpp/src/tests/background.ps1 @@ -30,11 +30,26 @@ trap { break } $encodedScript = [convert]::ToBase64String( [Text.Encoding]::Unicode.GetBytes([string] $script)) -$p = new-object System.Diagnostics.Process +#$p = new-object System.Diagnostics.Process $si = new-object System.Diagnostics.ProcessStartInfo $si.WorkingDirectory = $pwd -$si.UseShellExecute = $true $si.FileName = (get-command powershell.exe).Definition $si.Arguments = "-encodedCommand $encodedScript" -[diagnostics.process]::Start($si) +###### debugging setup +#$si.CreateNoWindow = $true +# UseShellExecute false required for RedirectStandard(Error, Output) +#$si.UseShellExecute = $false +#$si.RedirectStandardError = $true +#$si.RedirectStandardOutput = $true +###### +$si.UseShellExecute = $true + +##### Debugging, instead of the plain Start() above. +#$output = [io.File]::AppendText("start.out") +#$error = [io.File]::AppendText("start.err") +$p = [System.Diagnostics.Process]::Start($si) +#$output.WriteLine($p.StandardOutput.ReadToEnd()) +#$error.WriteLine($p.StandardError.ReadToEnd()) +#$p.WaitForExit() +#$output.Close() diff --git a/qpid/cpp/src/tests/cluster.mk b/qpid/cpp/src/tests/cluster.mk index 05e18ab9eb..bdec10ebb0 100644 --- a/qpid/cpp/src/tests/cluster.mk +++ b/qpid/cpp/src/tests/cluster.mk @@ -30,7 +30,8 @@ if HAVE_LIBCPG # ais_check checks pre-requisites for cluster tests and runs them if ok. TESTS += \ - ais_check \ + run_cluster_test \ + cluster_read_credit \ test_watchdog \ run_cluster_tests \ federated_cluster_test \ @@ -38,6 +39,8 @@ TESTS += \ EXTRA_DIST += \ ais_check \ + run_cluster_test \ + cluster_read_credit \ test_watchdog \ start_cluster \ stop_cluster \ diff --git a/qpid/cpp/src/tests/cluster_read_credit b/qpid/cpp/src/tests/cluster_read_credit new file mode 100755 index 0000000000..370d4098c5 --- /dev/null +++ b/qpid/cpp/src/tests/cluster_read_credit @@ -0,0 +1,27 @@ +#!/bin/sh +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Regression test for http://issues.apache.org/jira/browse/QPID-2086 + +srcdir=`dirname $0` +. $srcdir/ais_check +$srcdir/start_cluster 1 --cluster-read-max=2 || exit 1 +trap $srcdir/stop_cluster EXIT +seq 1 10000 | ./sender --port `cat cluster.ports` --routing-key no-such-queue diff --git a/qpid/cpp/src/tests/cluster_test.cpp b/qpid/cpp/src/tests/cluster_test.cpp index 28fcdd13ad..247aef1b2a 100644 --- a/qpid/cpp/src/tests/cluster_test.cpp +++ b/qpid/cpp/src/tests/cluster_test.cpp @@ -85,6 +85,12 @@ void prepareArgs(ClusterFixture::Args& args, const bool durableFlag = false) { args += "--no-data-dir"; } +ClusterFixture::Args prepareArgs(const bool durableFlag = false) { + ClusterFixture::Args args; + prepareArgs(args, durableFlag); + return args; +} + // Timeout for tests that wait for messages const sys::Duration TIMEOUT=sys::TIME_SEC/4; @@ -596,16 +602,19 @@ QPID_AUTO_TEST_CASE(testUpdateConsumers) { } } -QPID_AUTO_TEST_CASE(testCatchupSharedState) { +// Test that message data and delivery properties are updated properly. +QPID_AUTO_TEST_CASE(testUpdateMessages) { ClusterFixture::Args args; prepareArgs(args, durableFlag); ClusterFixture cluster(1, args, -1); Client c0(cluster[0], "c0"); - // Create some shared state. + // Create messages with different delivery properties c0.session.queueDeclare("q", arg::durable=durableFlag); + c0.session.exchangeBind(arg::exchange="amq.fanout", arg::queue="q"); c0.session.messageTransfer(arg::content=makeMessage("foo","q", durableFlag)); - c0.session.messageTransfer(arg::content=makeMessage("bar","q", durableFlag)); + c0.session.messageTransfer(arg::content=makeMessage("bar","q", durableFlag), + arg::destination="amq.fanout"); while (c0.session.queueQuery("q").getMessageCount() != 2) sys::usleep(1000); // Wait for message to show up on broker 0. @@ -628,9 +637,12 @@ QPID_AUTO_TEST_CASE(testCatchupSharedState) { BOOST_CHECK(c1.subs.get(m, "q", TIMEOUT)); BOOST_CHECK_EQUAL(m.getData(), "foo"); + BOOST_CHECK(m.getDeliveryProperties().hasExchange()); BOOST_CHECK_EQUAL(m.getDeliveryProperties().getExchange(), ""); BOOST_CHECK(c1.subs.get(m, "q", TIMEOUT)); BOOST_CHECK_EQUAL(m.getData(), "bar"); + BOOST_CHECK(m.getDeliveryProperties().hasExchange()); + BOOST_CHECK_EQUAL(m.getDeliveryProperties().getExchange(), "amq.fanout"); BOOST_CHECK_EQUAL(c1.session.queueQuery("q").getMessageCount(), 0u); // Add another broker, don't wait for join - should be stalled till ready. @@ -905,6 +917,7 @@ QPID_AUTO_TEST_CASE(testExclusiveQueueUpdate) { BOOST_CHECK(!result.getDurable()); BOOST_CHECK_EQUAL(result.getAlternateExchange(), std::string("amq.fanout")); BOOST_CHECK_THROW(c2.session.queueDeclare(arg::queue="q", arg::exclusive=true, arg::passive=true), framing::ResourceLockedException); + c1.session.close(); c1.connection.close(); c2.session = c2.connection.newSession(); BOOST_CHECK_THROW(c2.session.queueDeclare(arg::queue="q", arg::passive=true), framing::NotFoundException); @@ -1100,6 +1113,44 @@ QPID_AUTO_TEST_CASE(testRelease) { } } -QPID_AUTO_TEST_SUITE_END() +// Browse for 1 message with byte credit, return true if a message was +// received false if not. +bool browseByteCredit(Client& c, const string& q, int n, Message& m) { + SubscriptionSettings browseSettings( + FlowControl(1, n, false), // 1 message, n bytes credit, no window + ACCEPT_MODE_NONE, + ACQUIRE_MODE_NOT_ACQUIRED, + 0 // No auto-ack. + ); + LocalQueue lq; + Subscription s = c.subs.subscribe(lq, q, browseSettings); + c.session.messageFlush(arg::destination=q, arg::sync=true); + c.session.sync(); + c.subs.getSubscription(q).cancel(); + return lq.get(m, 0); // No timeout, flush should push message thru. +} + +// Ensure cluster update preserves exact message size, use byte credt as test. +QPID_AUTO_TEST_CASE(testExactByteCredit) { + ClusterFixture cluster(1, prepareArgs(), -1); + Client c0(cluster[0], "c0"); + c0.session.queueDeclare("q"); + c0.session.messageTransfer(arg::content=Message("MyMessage", "q")); + cluster.add(); + + int size=36; // Size of message on broker: headers+body + Client c1(cluster[1], "c1"); + Message m; + + // Ensure we get the message with exact credit. + BOOST_CHECK(browseByteCredit(c0, "q", size, m)); + BOOST_CHECK(browseByteCredit(c1, "q", size, m)); + // and not with one byte less. + BOOST_CHECK(!browseByteCredit(c0, "q", size-1, m)); + BOOST_CHECK(!browseByteCredit(c1, "q", size-1, m)); +} + + +QPID_AUTO_TEST_SUITE_END() }} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/clustered_replication_test b/qpid/cpp/src/tests/clustered_replication_test index cc331957ad..4f13b4672c 100755 --- a/qpid/cpp/src/tests/clustered_replication_test +++ b/qpid/cpp/src/tests/clustered_replication_test @@ -22,8 +22,7 @@ # Test reliability of the replication feature in the face of link # failures: srcdir=`dirname $0` -PYTHON_DIR=$srcdir/../../../python -export PYTHONPATH=$PYTHON_DIR +. $srcdir/python_env.sh trap stop_brokers INT EXIT @@ -31,10 +30,6 @@ fail() { echo $1 exit 1 } -with_ais_group() { - id -nG | grep '\' >/dev/null || { echo "You are not a member of the ais group." 1>&2; exit 1; } - echo $* | newgrp ais -} stop_brokers() { if [[ $PRIMARY1 ]] ; then @@ -55,26 +50,8 @@ stop_brokers() { fi } -if test -d ${PYTHON_DIR}; then - id -nG | grep '\' >/dev/null || \ - NOGROUP="You are not a member of the ais group." - ps -u root | grep 'aisexec\|corosync' >/dev/null || \ - NOAISEXEC="The aisexec or corosync daemon is not running as root" - - if test -n "$NOGROUP" -o -n "$NOAISEXEC"; then - cat <' >/dev/null || \ - NOGROUP="You are not a member of the ais group." - ps -u root | grep 'aisexec\|corosync' >/dev/null || \ - NOAISEXEC="The aisexec or corosync daemon is not running as root" - - if test -n "$NOGROUP" -o -n "$NOAISEXEC"; then - cat <B->C" fi - $PYTHON_DIR/commands/qpid-route route add $BROKER_B $BROKER_C amq.topic topic_control B B - $PYTHON_DIR/commands/qpid-route route add $BROKER_A $BROKER_B amq.topic topic_control A A + $PYTHON_COMMANDS/qpid-route route add $BROKER_B $BROKER_C amq.topic topic_control B B + $PYTHON_COMMANDS/qpid-route route add $BROKER_A $BROKER_B amq.topic topic_control A A if (($VERBOSE)); then echo "linked C->B->A" echo "Establishing routes for response queue..." fi - $PYTHON_DIR/commands/qpid-route route add $BROKER_B $BROKER_C amq.direct response B B - $PYTHON_DIR/commands/qpid-route route add $BROKER_A $BROKER_B amq.direct response A A + $PYTHON_COMMANDS/qpid-route route add $BROKER_B $BROKER_C amq.direct response B B + $PYTHON_COMMANDS/qpid-route route add $BROKER_A $BROKER_B amq.direct response A A if (($VERBOSE)); then echo "linked C->B->A" for b in $BROKER_A $BROKER_B $BROKER_C; do echo "Routes for $b" - $PYTHON_DIR/commands/qpid-route route list $b + $PYTHON_COMMANDS/qpid-route route list $b done fi } diff --git a/qpid/cpp/src/tests/python_env.sh b/qpid/cpp/src/tests/python_env.sh new file mode 100644 index 0000000000..f5dca97a56 --- /dev/null +++ b/qpid/cpp/src/tests/python_env.sh @@ -0,0 +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. +# + +# Environment for python tests +test -d python || { echo "WARNING: skipping `basename $0`, no python directory."; exit 0; } +PYTHON_DIR=$PWD/python +PYTHON_COMMANDS=$PYTHON_DIR/commands +PYTHONPATH=$PYTHON_DIR +export PYTHONPATH PYTHON_DIR + diff --git a/qpid/cpp/src/tests/python_tests b/qpid/cpp/src/tests/python_tests index e3906c1685..e2077ef7dc 100755 --- a/qpid/cpp/src/tests/python_tests +++ b/qpid/cpp/src/tests/python_tests @@ -20,14 +20,10 @@ # # Run the python tests. +. `dirname $0`/python_env.sh QPID_PORT=${QPID_PORT:-5672} PYTHON_TESTS=${PYTHON_TESTS:-$*} -QPID_PYTHON_DIR=${QPID_PYTHON_DIR:-`dirname $0`/../../../python} FAILING=${FAILING:-/dev/null} -if test -d $QPID_PYTHON_DIR; then - cd $QPID_PYTHON_DIR - ./qpid-python-test -b localhost:$QPID_PORT -I $FAILING $PYTHON_TESTS || { echo "FAIL python tests"; exit 1; } -else - echo "WARNING: No python tests. $QPID_PYTHON_DIR not found." -fi +cd $PYTHON_DIR +python commands/qpid-python-test -b localhost:$QPID_PORT -I $FAILING $PYTHON_TESTS || exit 1 diff --git a/qpid/cpp/src/tests/python_tests.ps1 b/qpid/cpp/src/tests/python_tests.ps1 new file mode 100644 index 0000000000..a7f6920783 --- /dev/null +++ b/qpid/cpp/src/tests/python_tests.ps1 @@ -0,0 +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. +# + +# Run the python tests; intended to be run by run_test.ps1 which sets up +# QPID_PORT +$srcdir = Split-Path $myInvocation.InvocationName +$PYTHON_DIR = "$srcdir\..\..\..\python" +if (!(Test-Path $PYTHON_DIR -pathType Container)) { + "Skipping header test as python libs not found" + exit 1 +} + +if (Test-Path env:FAILING) { + $fails = "-I $env:FAILING" +} +if (Test-Path env:PYTHON_TESTS) { + $tests = "$env:PYTHON_TESTS" +} +else { + $tests = "*" +} + +#cd $PYTHON_DIR +$env:PYTHONPATH="$PYTHON_DIR;$env:PYTHONPATH" +python $PYTHON_DIR/qpid-python-test -b localhost:$env:QPID_PORT $fails $tests +exit $LASTEXITCODE diff --git a/qpid/cpp/src/tests/qpid_stream.cpp b/qpid/cpp/src/tests/qpid_stream.cpp index 8e02baa8a0..8195bf390e 100644 --- a/qpid/cpp/src/tests/qpid_stream.cpp +++ b/qpid/cpp/src/tests/qpid_stream.cpp @@ -34,6 +34,9 @@ using namespace qpid::messaging; using namespace qpid::sys; +namespace qpid { +namespace tests { + struct Args : public qpid::Options { std::string url; @@ -139,10 +142,14 @@ struct Consume : Client << ", min=" << minLatency << ", max=" << maxLatency << std::endl; } - } + } } }; +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char** argv) { try { diff --git a/qpid/cpp/src/tests/quick_topictest.ps1 b/qpid/cpp/src/tests/quick_topictest.ps1 index 2b857edfff..b1e0ed1f7d 100644 --- a/qpid/cpp/src/tests/quick_topictest.ps1 +++ b/qpid/cpp/src/tests/quick_topictest.ps1 @@ -18,12 +18,13 @@ # # Quick and quiet topic test for make check. -$srcdir = Split-Path $myInvocation.ScriptName -$PsHome\powershell $srcdir\topictest.ps1 -subscribers 2 -messages 2 -batches 1 > topictest.log 2>&1 -if ($LastExitCode != 0) { - echo $0 FAILED: +[string]$me = $myInvocation.InvocationName +$srcdir = Split-Path $me +powershell "$srcdir\topictest.ps1" -subscribers 2 -messages 2 -batches 1 > topictest.log 2>&1 +if (!$?) { + "$me FAILED:" cat topictest.log exit $LastExitCode } -rm topictest.log +Remove-Item topictest.log exit 0 diff --git a/qpid/cpp/src/tests/reliable_replication_test b/qpid/cpp/src/tests/reliable_replication_test index a788d5a76b..db06259f0c 100755 --- a/qpid/cpp/src/tests/reliable_replication_test +++ b/qpid/cpp/src/tests/reliable_replication_test @@ -22,7 +22,7 @@ # Test reliability of the replication feature in the face of link # failures: MY_DIR=`dirname \`which $0\`` -PYTHON_DIR=${MY_DIR}/../../../python +. ${MY_DIR}/python_env.sh trap stop_brokers EXIT @@ -53,12 +53,12 @@ setup() { echo "Testing replication from port $BROKER_A to port $BROKER_B" export PYTHONPATH=$PYTHON_DIR - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_B" add exchange replication replication - $PYTHON_DIR/commands/qpid-route --ack 500 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add exchange replication replication + $PYTHON_COMMANDS/qpid-route --ack 500 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication #create test queue (only replicate enqueues for this test): - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_A" add queue queue-a --generate-queue-events 1 - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_B" add queue queue-a + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-a --generate-queue-events 1 + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-a } send() { @@ -72,10 +72,10 @@ receive() { bounce_link() { echo "Destroying link..." - $PYTHON_DIR/commands/qpid-route link del "localhost:$BROKER_B" "localhost:$BROKER_A" + $PYTHON_COMMANDS/qpid-route link del "localhost:$BROKER_B" "localhost:$BROKER_A" echo "Link destroyed; recreating route..." sleep 2 - $PYTHON_DIR/commands/qpid-route --ack 500 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication + $PYTHON_COMMANDS/qpid-route --ack 500 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication echo "Route re-established" } diff --git a/qpid/cpp/src/tests/replication_test b/qpid/cpp/src/tests/replication_test index 8b3022b260..000b4591da 100755 --- a/qpid/cpp/src/tests/replication_test +++ b/qpid/cpp/src/tests/replication_test @@ -21,7 +21,8 @@ # Run a test of the replication feature MY_DIR=`dirname \`which $0\`` -PYTHON_DIR=${MY_DIR}/../../../python +. `dirname $0`/python_env.sh + trap stop_brokers INT TERM QUIT stop_brokers() { @@ -47,21 +48,21 @@ if test -d ${PYTHON_DIR} && test -f ../.libs/replicating_listener.so && test -f export PYTHONPATH echo "Running replication test between localhost:$BROKER_A and localhost:$BROKER_B" - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_B" add exchange replication replication - $PYTHON_DIR/commands/qpid-route --ack 5 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add exchange replication replication + $PYTHON_COMMANDS/qpid-route --ack 5 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication #create test queues - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_A" add queue queue-a --generate-queue-events 2 - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_A" add queue queue-b --generate-queue-events 2 - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_A" add queue queue-c --generate-queue-events 1 - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_A" add queue queue-d --generate-queue-events 2 - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_A" add queue queue-e --generate-queue-events 1 + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-a --generate-queue-events 2 + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-b --generate-queue-events 2 + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-c --generate-queue-events 1 + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-d --generate-queue-events 2 + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-e --generate-queue-events 1 - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_B" add queue queue-a - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_B" add queue queue-b - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_B" add queue queue-c - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_B" add queue queue-e + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-a + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-b + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-c + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-e #queue-d deliberately not declared on DR; this error case should be handled #publish and consume from test queues on broker A: @@ -125,13 +126,13 @@ if test -d ${PYTHON_DIR} && test -f ../.libs/replicating_listener.so && test -f ../qpidd --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module ../.libs/replication_exchange.so --log-enable info+ --log-to-file replication-dest.log --log-to-stderr 0 > qpidd.port BROKER_B=`cat qpidd.port` - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_B" add exchange replication replication - $PYTHON_DIR/commands/qpid-route --ack 5 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add exchange replication replication + $PYTHON_COMMANDS/qpid-route --ack 5 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_A" add queue queue-e --generate-queue-events 2 - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_B" add queue queue-e - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_A" add queue queue-d --generate-queue-events 1 - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_B" add queue queue-d + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-e --generate-queue-events 2 + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-e + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-d --generate-queue-events 1 + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-d i=1 while [ $i -le 10 ]; do @@ -153,9 +154,9 @@ if test -d ${PYTHON_DIR} && test -f ../.libs/replicating_listener.so && test -f ../qpidd --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module ../.libs/replication_exchange.so --log-enable info+ --log-to-file replication-dest.log --log-to-stderr 0 > qpidd.port BROKER_B=`cat qpidd.port` - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_B" add queue queue-e - $PYTHON_DIR/commands/qpid-config -a "localhost:$BROKER_B" add exchange replication replication - $PYTHON_DIR/commands/qpid-route --ack 5 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-e + $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add exchange replication replication + $PYTHON_COMMANDS/qpid-route --ack 5 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication # now send another 15 i=11 while [ $i -le 15 ]; do diff --git a/qpid/cpp/src/tests/ring_queue_test b/qpid/cpp/src/tests/ring_queue_test index 5805989d7e..553746eb49 100755 --- a/qpid/cpp/src/tests/ring_queue_test +++ b/qpid/cpp/src/tests/ring_queue_test @@ -48,7 +48,7 @@ receive() { cleanup() { rm -f sender_${QUEUE_NAME}_* receiver_${QUEUE_NAME}_* - qpid-config $BROKER_URL add queue $QUEUE_NAME + qpid-config $BROKER_URL del queue $QUEUE_NAME --force } log() { @@ -64,10 +64,11 @@ validate() { if [[ $RECEIVERS -eq 0 ]]; then #queue should have $LIMIT messages on it, but need to send an eos also sender --routing-key $QUEUE_NAME --send-eos 1 < /dev/null - if [[ $(receiver --queue $QUEUE_NAME --browse | wc -l) -eq $(( $LIMIT - 1)) ]]; then + received=$(receiver --queue $QUEUE_NAME --browse | wc -l) + if [[ received -eq $(( $LIMIT - 1)) ]]; then log "queue contains $LIMIT messages as expected" else - fail "queue does not contain the expected $LIMIT messages" + fail "queue does not contain the expected $LIMIT messages (received $received)" fi elif [[ $CONCURRENT -eq 0 ]]; then #sum of length of all output files should be equal to $LIMIT - $RECEIVERS (1 eos message each) diff --git a/qpid/cpp/src/tests/run_acl_tests b/qpid/cpp/src/tests/run_acl_tests index d9b654c7cd..c67e2d421d 100755 --- a/qpid/cpp/src/tests/run_acl_tests +++ b/qpid/cpp/src/tests/run_acl_tests @@ -20,7 +20,7 @@ # # Run the acl tests. $srcdir is set by the Makefile. -PYTHON_DIR=$srcdir/../../../python +. `dirname $0`/python_env.sh DATA_DIR=`pwd`/data_dir trap stop_brokers INT TERM QUIT @@ -55,7 +55,7 @@ if test -d ${PYTHON_DIR} ; then echo "Running acl tests using brokers on ports $LOCAL_PORT" PYTHONPATH=$PYTHON_DIR:$srcdir export PYTHONPATH - $PYTHON_DIR/qpid-python-test -b localhost:$LOCAL_PORT -m acl || EXITCODE=1 + $PYTHON_COMMANDS/qpid-python-test -b localhost:$LOCAL_PORT -m acl || EXITCODE=1 stop_brokers || EXITCODE=1 test_loading_acl_from_absolute_path || EXITCODE=1 rm -rf $DATA_DIR diff --git a/qpid/cpp/src/tests/run_cli_tests b/qpid/cpp/src/tests/run_cli_tests index ea0d591176..bb9605410c 100755 --- a/qpid/cpp/src/tests/run_cli_tests +++ b/qpid/cpp/src/tests/run_cli_tests @@ -21,8 +21,8 @@ # Run the cli-utility tests. MY_DIR=`dirname \`which $0\`` -PYTHON_DIR=${MY_DIR}/../../../python -CLI_DIR=${PYTHON_DIR}/commands +. `dirname $0`/python_env.sh +CLI_DIR=$PYTHON_COMMANDS trap stop_brokers INT TERM QUIT @@ -43,7 +43,7 @@ if test -d ${PYTHON_DIR} ; then echo "Running CLI tests using brokers on ports $LOCAL_PORT $REMOTE_PORT" PYTHONPATH=${PYTHON_DIR}:${MY_DIR} export PYTHONPATH - ${PYTHON_DIR}/qpid-python-test -m cli_tests -b localhost:$LOCAL_PORT -Dremote-port=$REMOTE_PORT -Dcli-dir=$CLI_DIR $@ + $PYTHON_COMMANDS/qpid-python-test -m cli_tests -b localhost:$LOCAL_PORT -Dremote-port=$REMOTE_PORT -Dcli-dir=$CLI_DIR $@ RETCODE=$? stop_brokers if test x$RETCODE != x0; then diff --git a/qpid/cpp/src/tests/run_cluster_test b/qpid/cpp/src/tests/run_cluster_test new file mode 100755 index 0000000000..c022eea1fe --- /dev/null +++ b/qpid/cpp/src/tests/run_cluster_test @@ -0,0 +1,26 @@ +#!/bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +# Run the tests +srcdir=`dirname $0` +. $srcdir/ais_check +with_ais_group $srcdir/run_test ./cluster_test diff --git a/qpid/cpp/src/tests/run_cluster_tests b/qpid/cpp/src/tests/run_cluster_tests index 8b039346db..d1a58f9f6a 100755 --- a/qpid/cpp/src/tests/run_cluster_tests +++ b/qpid/cpp/src/tests/run_cluster_tests @@ -22,45 +22,21 @@ # Check that top_builddir and srcdir are set # If not, assume local run from test dir if [ -z ${top_builddir} -o -z ${srcdir} ]; then - srcdir=`pwd` + srcdir=`dirname $0` top_builddir=${srcdir}/../../ fi TEST_DIR=${top_builddir}/src/tests -PYTHON_DIR=${srcdir}/../../../python +. $srcdir/python_env.sh if test -z $1; then - CLUSTER_TEST="${PYTHON_DIR}/qpid-python-test -m cluster_tests cluster_tests.ShortTests.\*" + CLUSTER_TEST="$PYTHON_COMMANDS/qpid-python-test -m cluster_tests cluster_tests.ShortTests.\*" else - CLUSTER_TEST="${PYTHON_DIR}/qpid-python-test -m cluster_tests cluster_tests.LongTests.\*" + CLUSTER_TEST="$PYTHON_COMMANDS/qpid-python-test -m cluster_tests cluster_tests.LongTests.\*" echo "Running $1..." fi - # Check AIS requirements -id -nG | grep '\' > /dev/null || NOGROUP="You are not a member of the ais group." -ps -u root | grep 'aisexec\|corosync' > /dev/null || NOAISEXEC="The aisexec or corosync daemon is not running as root." -if ! test -d ${PYTHON_DIR}; then - NO_PYTHON_DIR="PYTHON_DIR=\"${PYTHON_DIR}\" not found or does not exist." -fi - -if test -n "${NOGROUP}" -o -n "${NOAISEXEC}" -o -n "${NO_PYTHON_DIR}"; then - cat <' >/dev/null || \ - NOGROUP="The ais group is not your primary group." -ps -u root | grep 'aisexec\|corosync' >/dev/null || \ - NOAISEXEC="The aisexec/corosync daemon is not running as root" - -if test -n "$NOGROUP" -o -n "$NOAISEXEC"; then - cat <' >/dev/null || { echo "You are not a member of the ais group." 1>&2; exit 1; } - echo $* | newgrp ais -} +. `dirname $0`/ais_check rm -f cluster*.log cluster.ports qpidd.port SIZE=${1:-3}; shift CLUSTER=`pwd` # Cluster name=pwd, avoid clashes. -OPTS="-d --no-module-dir --load-module ../.libs/cluster.so --cluster-name=$CLUSTER --auth=no $@" +OPTS="-d --no-module-dir --load-module ../.libs/cluster.so --cluster-name=$CLUSTER --auth=no --log-enable notice+ --log-enable debug+:cluster $@" for (( i=0; i> cluster.ports done diff --git a/qpid/cpp/src/tests/stop_broker.ps1 b/qpid/cpp/src/tests/stop_broker.ps1 index 165c7a63b0..4fdeb26e2b 100644 --- a/qpid/cpp/src/tests/stop_broker.ps1 +++ b/qpid/cpp/src/tests/stop_broker.ps1 @@ -21,8 +21,23 @@ Get-Content -path qpidd.port -totalCount 1 | Set-Variable -name qpid_port Remove-Item qpidd.port +# Test runs from the tests directory but the broker executable is one level +# up, and most likely in a subdirectory from there based on what build type. +# Look around for it before trying to start it. +$subs = "Debug","Release","MinSizeRel","RelWithDebInfo" +foreach ($sub in $subs) { + $prog = "..\$sub\qpidd.exe" + if (Test-Path $prog) { + break + } +} +if (!(Test-Path $prog)) { + "Cannot locate qpidd.exe" + exit 1 +} + # Piping the output makes the script wait for qpidd to finish. -..\Debug\qpidd --quit --port $qpid_port | Write-Output +Invoke-Expression "$prog --quit --port $qpid_port" | Write-Output $stopped = $? # Check qpidd.log. diff --git a/qpid/cpp/src/tests/test_store.cpp b/qpid/cpp/src/tests/test_store.cpp index 64a96bf71a..f2d3aa65a3 100644 --- a/qpid/cpp/src/tests/test_store.cpp +++ b/qpid/cpp/src/tests/test_store.cpp @@ -140,7 +140,8 @@ struct TestStorePlugin : public Plugin { { Broker* broker = dynamic_cast(&target); if (!broker) return; - broker->setStore (new TestStore(options.name, *broker)); + boost::shared_ptr p(new TestStore(options.name, *broker)); + broker->setStore (p); } void initialize(qpid::Plugin::Target&) {} diff --git a/qpid/cpp/src/tests/topictest.ps1 b/qpid/cpp/src/tests/topictest.ps1 index 04dae23ad9..58ae50c67c 100644 --- a/qpid/cpp/src/tests/topictest.ps1 +++ b/qpid/cpp/src/tests/topictest.ps1 @@ -19,9 +19,6 @@ # Run the C++ topic test -# Clean up old log files -Get-Item subscriber_*.log | Remove-Item - # Parameters with default values: s (subscribers) m (messages) b (batches) # h (host) t (false; use transactions) param ( @@ -32,23 +29,28 @@ param ( [switch] $t # transactional ) +# Clean up old log files +Get-Item subscriber_*.log | Remove-Item + +if ($t) { + $transactional = "--transactional --durable" +} + function subscribe { - "Start subscriber $args[0]" - $LOG = "subscriber_$args[0].log" - . $srcdir\background.ps1 { - $env:OUTDIR\topic_listener $TRANSACTIONAL > $LOG 2>&1 - if ($LastExitCode -ne 0) { Remove-Item $LOG } - } -inconsole + param ([int]$num) + "Start subscriber $num" + $LOG = "subscriber_$num.log" + $cmdline = "$env:OUTDIR\topic_listener $transactional > $LOG 2>&1 + if (`$LastExitCode -ne 0) { Remove-Item $LOG }" + $cmdblock = $executioncontext.invokecommand.NewScriptBlock($cmdline) + . $srcdir\background.ps1 $cmdblock } -publish() { - if ($t) { - $transactional = "--transactional --durable" - } - $env:OUTDIR\topic_publisher --messages $messages --batches $batches --subscribers $subscribers $host $transactional 2>&1 +function publish { + Invoke-Expression "$env:OUTDIR\topic_publisher --messages $messages --batches $batches --subscribers $subscribers $host $transactional" 2>&1 } -$srcdir = Split-Path $myInvocation.ScriptName +$srcdir = Split-Path $MyInvocation.MyCommand.Path if ($broker.length) { $broker = "-h$broker" } diff --git a/qpid/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp b/qpid/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp index d51ed90758..a0b665db73 100644 --- a/qpid/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp +++ b/qpid/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp @@ -28,9 +28,23 @@ #include #include +#include namespace { +// Instead of popping up a window for exceptions, just print something out +LONG _stdcall UnhandledExceptionFilter (PEXCEPTION_POINTERS pExceptionInfo) +{ + DWORD dwExceptionCode = pExceptionInfo->ExceptionRecord->ExceptionCode; + + if (dwExceptionCode == EXCEPTION_ACCESS_VIOLATION) + std::cerr << "\nERROR: ACCESS VIOLATION\n" << std::endl; + else + std::cerr << "\nERROR: UNHANDLED EXCEPTION\n" << std::endl; + + return EXCEPTION_EXECUTE_HANDLER; +} + struct redirect_errors_to_stderr { redirect_errors_to_stderr (); }; @@ -50,6 +64,9 @@ redirect_errors_to_stderr::redirect_errors_to_stderr() // and can't-open-file message boxes. SetErrorMode(SEM_FAILCRITICALERRORS); SetErrorMode(SEM_NOOPENFILEERRORBOX); + + // And this will catch all unhandled exceptions. + SetUnhandledExceptionFilter (&UnhandledExceptionFilter); } } // namespace diff --git a/qpid/cpp/src/windows/QpiddBroker.cpp b/qpid/cpp/src/windows/QpiddBroker.cpp index 5c6eef48f8..5bf9477e6a 100644 --- a/qpid/cpp/src/windows/QpiddBroker.cpp +++ b/qpid/cpp/src/windows/QpiddBroker.cpp @@ -133,6 +133,14 @@ void ShutdownHandler::run() { } } +// Console control handler to properly handle ctl-c. +BOOL CtrlHandler(DWORD ctl) +{ + ShutdownEvent shutter; // no pid specified == shut me down + shutter.signal(); + return ((ctl == CTRL_C_EVENT || ctl == CTRL_CLOSE_EVENT) ? TRUE : FALSE); +} + } struct ProcessControlOptions : public qpid::Options { @@ -245,6 +253,7 @@ int QpiddBroker::execute (QpiddOptions *options) { ShutdownHandler waitShut(brokerPtr); qpid::sys::Thread waitThr(waitShut); // Wait for shutdown event + SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); brokerPtr->run(); waitShut.signal(); // In case we shut down some other way waitThr.join(); diff --git a/qpid/java/broker/etc/log4j.xml b/qpid/java/broker/etc/log4j.xml index 8ca43ededd..4b71772a0e 100644 --- a/qpid/java/broker/etc/log4j.xml +++ b/qpid/java/broker/etc/log4j.xml @@ -87,6 +87,10 @@ + + + + diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java index add5e64ee8..644a33db01 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java @@ -27,13 +27,12 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentHashMap; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.ContentBody; -import org.apache.qpid.framing.ContentHeaderBody; -import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.*; import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.server.ack.UnacknowledgedMessageMap; import org.apache.qpid.server.ack.UnacknowledgedMessageMapImpl; @@ -42,12 +41,7 @@ import org.apache.qpid.server.exchange.NoRouteException; import org.apache.qpid.server.flow.FlowCreditManager; import org.apache.qpid.server.flow.Pre0_10CreditManager; import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.queue.AMQMessage; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.IncomingMessage; -import org.apache.qpid.server.queue.MessageHandleFactory; -import org.apache.qpid.server.queue.QueueEntry; -import org.apache.qpid.server.queue.UnauthorizedAccessException; +import org.apache.qpid.server.queue.*; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.subscription.SubscriptionFactoryImpl; import org.apache.qpid.server.subscription.ClientDeliveryMethod; @@ -59,6 +53,7 @@ import org.apache.qpid.server.txn.NonTransactionalContext; import org.apache.qpid.server.txn.TransactionalContext; import org.apache.qpid.server.logging.LogActor; import org.apache.qpid.server.logging.LogSubject; +import org.apache.qpid.server.logging.LogMessage; import org.apache.qpid.server.logging.messages.ChannelMessages; import org.apache.qpid.server.logging.subjects.ChannelLogSubject; import org.apache.qpid.server.logging.actors.AMQPChannelActor; @@ -119,6 +114,11 @@ public class AMQChannel private final AMQProtocolSession _session; private boolean _closing; + private final ConcurrentMap _blockingQueues = new ConcurrentHashMap(); + + private final AtomicBoolean _blocking = new AtomicBoolean(false); + + private LogActor _actor; private LogSubject _logSubject; @@ -783,16 +783,32 @@ public class AMQChannel return _unacknowledgedMessageMap; } - + /** + * Called from the ChannelFlowHandler to suspend this Channel + * @param suspended boolean, should this Channel be suspended + */ public void setSuspended(boolean suspended) { - - boolean wasSuspended = _suspended.getAndSet(suspended); if (wasSuspended != suspended) { - _actor.message(_logSubject, ChannelMessages.CHN_1002(suspended ? "Stopped" : "Started")); + // Log Flow Started before we start the subscriptions + if (!suspended) + { + _actor.message(_logSubject, ChannelMessages.CHN_1002("Started")); + } + + + // This section takes two different approaches to perform to perform + // the same function. Ensuring that the Subscription has taken note + // of the change in Channel State + // Here we have become unsuspended and so we ask each the queue to + // perform an Async delivery for each of the subscriptions in this + // Channel. The alternative would be to ensure that the subscription + // had received the change in suspension state. That way the logic + // behind decieding to start an async delivery was located with the + // Subscription. if (wasSuspended) { // may need to deliver queued messages @@ -801,6 +817,38 @@ public class AMQChannel s.getQueue().deliverAsync(s); } } + + + // Here we have become suspended so we need to ensure that each of + // the Subscriptions has noticed this change so that we can be sure + // they are not still sending messages. Again the code here is a + // very simplistic approach to ensure that the change of suspension + // has been noticed by each of the Subscriptions. Unlike the above + // case we don't actually need to do anything else. + if (!wasSuspended) + { + // may need to deliver queued messages + for (Subscription s : _tag2SubscriptionMap.values()) + { + try + { + s.getSendLock(); + } + finally + { + s.releaseSendLock(); + } + } + } + + + // Log Suspension only after we have confirmed all suspensions are + // stopped. + if (suspended) + { + _actor.message(_logSubject, ChannelMessages.CHN_1002("Stopped")); + } + } } @@ -931,4 +979,37 @@ public class AMQChannel { return _actor; } + + public void block(AMQQueue queue) + { + if(_blockingQueues.putIfAbsent(queue, Boolean.TRUE) == null) + { + + if(_blocking.compareAndSet(false,true)) + { + _actor.message(_logSubject, ChannelMessages.CHN_1005(queue.getName().toString())); + flow(false); + } + } + } + + public void unblock(AMQQueue queue) + { + if(_blockingQueues.remove(queue)) + { + if(_blocking.compareAndSet(true,false)) + { + _actor.message(_logSubject, ChannelMessages.CHN_1006()); + + flow(true); + } + } + } + + private void flow(boolean flow) + { + MethodRegistry methodRegistry = _session.getMethodRegistry(); + AMQMethodBody responseBody = methodRegistry.createChannelFlowBody(flow); + _session.writeFrame(responseBody.generateFrame(_channelId)); + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfiguration.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfiguration.java index 74bb7ee969..5c73e353de 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfiguration.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfiguration.java @@ -108,4 +108,14 @@ public class QueueConfiguration return _config.getLong("minimumAlertRepeatGap", _vHostConfig.getMinimumAlertRepeatGap()); } + public long getCapacity() + { + return _config.getLong("capacity", _vHostConfig.getCapacity()); + } + + public long getFlowResumeCapacity() + { + return _config.getLong("flowResumeCapacity", _vHostConfig.getFlowResumeCapacity()); + } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java index a72c2889d1..641b44bb18 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java @@ -104,6 +104,8 @@ public class ServerConfiguration implements SignalHandler envVarMap.put("QPID_MAXIMUMQUEUEDEPTH", "maximumQueueDepth"); envVarMap.put("QPID_MAXIMUMMESSAGESIZE", "maximumMessageSize"); envVarMap.put("QPID_MINIMUMALERTREPEATGAP", "minimumAlertRepeatGap"); + envVarMap.put("QPID_QUEUECAPACITY", "capacity"); + envVarMap.put("QPID_FLOWRESUMECAPACITY", "flowResumeCapacity"); envVarMap.put("QPID_SOCKETRECEIVEBUFFER", "connector.socketReceiveBuffer"); envVarMap.put("QPID_SOCKETWRITEBUFFER", "connector.socketWriteBuffer"); envVarMap.put("QPID_TCPNODELAY", "connector.tcpNoDelay"); @@ -290,7 +292,6 @@ public class ServerConfiguration implements SignalHandler return conf; } - @Override public void handle(Signal arg0) { try @@ -508,6 +509,16 @@ public class ServerConfiguration implements SignalHandler return getConfig().getLong("minimumAlertRepeatGap", 0); } + public long getCapacity() + { + return getConfig().getLong("capacity", 0L); + } + + public long getFlowResumeCapacity() + { + return getConfig().getLong("flowResumeCapacity", getCapacity()); + } + public int getProcessors() { return getConfig().getInt("connector.processors", 4); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java index 0273a13262..6c72025ec2 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java @@ -166,4 +166,15 @@ public class VirtualHostConfiguration return _config.getLong("queues.minimumAlertRepeatGap", 0); } + + public long getCapacity() + { + return _config.getLong("queues.capacity", 0l); + } + + public long getFlowResumeCapacity() + { + return _config.getLong("queues.flowResumeCapacity", getCapacity()); + } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/management/ConfigurationManagementMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/management/ConfigurationManagementMBean.java index 1541d3d892..9954719866 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/management/ConfigurationManagementMBean.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/management/ConfigurationManagementMBean.java @@ -35,13 +35,11 @@ public class ConfigurationManagementMBean extends AMQManagedObject implements Co super(ConfigurationManagement.class, ConfigurationManagement.TYPE, ConfigurationManagement.VERSION); } - @Override public String getObjectInstanceName() { return ConfigurationManagement.TYPE; } - @Override public void reloadSecurityConfiguration() throws Exception { ApplicationRegistry.getInstance().getConfiguration().reparseConfigFile(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java index c04b6c559c..620799a81f 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java @@ -73,7 +73,6 @@ public class DefaultExchangeFactory implements ExchangeFactory return e; } - @Override public void initialise(VirtualHostConfiguration hostConfig) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java index f3cab10ed7..fcf3fd4337 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java @@ -71,7 +71,7 @@ public class BasicRejectMethodHandler implements StateAwareMethodListener void setMinimumAlertRepeatGap(long value); + long getCapacity(); + + void setCapacity(long capacity); + + + long getFlowResumeCapacity(); + + void setFlowResumeCapacity(long flowResumeCapacity); + + + void deleteMessageFromTop(StoreContext storeContext) throws AMQException; long clearQueue(StoreContext storeContext) throws AMQException; @@ -180,6 +191,8 @@ public interface AMQQueue extends Managable, Comparable void stop(); + void checkCapacity(AMQChannel channel); + /** * ExistingExclusiveSubscription signals a failure to create a subscription, because an exclusive subscription * already exists. diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java index 7509350e65..267ccf43ea 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java @@ -26,11 +26,105 @@ import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.configuration.QueueConfiguration; import org.apache.qpid.server.virtualhost.VirtualHost; +import java.util.Map; +import java.util.HashMap; + public class AMQQueueFactory { public static final AMQShortString X_QPID_PRIORITIES = new AMQShortString("x-qpid-priorities"); + private abstract static class QueueProperty + { + + private final AMQShortString _argumentName; + + + public QueueProperty(String argumentName) + { + _argumentName = new AMQShortString(argumentName); + } + + public AMQShortString getArgumentName() + { + return _argumentName; + } + + + public abstract void setPropertyValue(AMQQueue queue, Object value); + + } + + private abstract static class QueueLongProperty extends QueueProperty + { + + public QueueLongProperty(String argumentName) + { + super(argumentName); + } + + public void setPropertyValue(AMQQueue queue, Object value) + { + if(value instanceof Number) + { + setPropertyValue(queue, ((Number)value).longValue()); + } + + } + + abstract void setPropertyValue(AMQQueue queue, long value); + + + } + + private static final QueueProperty[] DECLAREABLE_PROPERTIES = { + new QueueLongProperty("x-qpid-maximum-message-age") + { + public void setPropertyValue(AMQQueue queue, long value) + { + queue.setMaximumMessageAge(value); + } + }, + new QueueLongProperty("x-qpid-maximum-message-size") + { + public void setPropertyValue(AMQQueue queue, long value) + { + queue.setMaximumMessageSize(value); + } + }, + new QueueLongProperty("x-qpid-maximum-message-count") + { + public void setPropertyValue(AMQQueue queue, long value) + { + queue.setMaximumMessageCount(value); + } + }, + new QueueLongProperty("x-qpid-minimum-alert-repeat-gap") + { + public void setPropertyValue(AMQQueue queue, long value) + { + queue.setMinimumAlertRepeatGap(value); + } + }, + new QueueLongProperty("x-qpid-capacity") + { + public void setPropertyValue(AMQQueue queue, long value) + { + queue.setCapacity(value); + } + }, + new QueueLongProperty("x-qpid-flow-resume-capacity") + { + public void setPropertyValue(AMQQueue queue, long value) + { + queue.setFlowResumeCapacity(value); + } + } + + }; + + + public static AMQQueue createAMQQueueImpl(AMQShortString name, boolean durable, AMQShortString owner, @@ -53,6 +147,18 @@ public class AMQQueueFactory //Register the new queue virtualHost.getQueueRegistry().registerQueue(q); q.configure(virtualHost.getConfiguration().getQueueConfiguration(name.asString())); + + if(arguments != null) + { + for(QueueProperty p : DECLAREABLE_PROPERTIES) + { + if(arguments.containsKey(p.getArgumentName())) + { + p.setPropertyValue(q, arguments.get(p.getArgumentName())); + } + } + } + return q; } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java index dbad5438dc..3b58f05f93 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java @@ -42,8 +42,7 @@ public class QueueEntryImpl implements QueueEntry private final SimpleQueueEntryList _queueEntryList; - private AMQMessage _message; - + private final AMQMessage _message; private Set _rejectedBy = null; @@ -177,13 +176,21 @@ public class QueueEntryImpl implements QueueEntry public String debugIdentity() { - return getMessage().debugIdentity(); + AMQMessage message = getMessage(); + if (message == null) + { + return "null"; + } + else + { + return message.debugIdentity(); + } } public boolean immediateAndNotDelivered() { - return _message.immediateAndNotDelivered(); + return getMessage().immediateAndNotDelivered(); } public void setRedelivered(boolean b) @@ -385,4 +392,5 @@ public class QueueEntryImpl implements QueueEntry { return _queueEntryList; } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java index b14b92b014..d781dc4dea 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java @@ -1,11 +1,10 @@ package org.apache.qpid.server.queue; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @@ -35,6 +34,7 @@ import org.apache.qpid.server.logging.subjects.QueueLogSubject; import org.apache.qpid.server.logging.LogSubject; import org.apache.qpid.server.logging.LogActor; import org.apache.qpid.server.logging.messages.QueueMessages; +import org.apache.qpid.server.AMQChannel; /* * @@ -96,6 +96,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener private final Executor _asyncDelivery; private final AtomicLong _totalMessagesReceived = new AtomicLong(); + private final ConcurrentMap _blockedChannels = new ConcurrentHashMap(); + /** max allowed size(KB) of a single message */ public long _maximumMessageSize = ApplicationRegistry.getInstance().getConfiguration().getMaximumMessageSize(); @@ -122,6 +124,11 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener private LogSubject _logSubject; private LogActor _logActor; + + private long _capacity = ApplicationRegistry.getInstance().getConfiguration().getCapacity(); + private long _flowResumeCapacity = ApplicationRegistry.getInstance().getConfiguration().getFlowResumeCapacity(); + private final AtomicBoolean _overfull = new AtomicBoolean(false); + protected SimpleAMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost) throws AMQException { @@ -508,8 +515,11 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener throws AMQException { _deliveredMessages.incrementAndGet(); + if (_logger.isDebugEnabled()) + { + _logger.debug(sub + ": deliverMessage: " + entry.debugIdentity()); + } sub.send(entry); - } private boolean subscriptionReadyAndHasInterest(final Subscription sub, final QueueEntry entry) @@ -626,6 +636,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener throw new FailedDequeueException(_name.toString(), e); } + checkCapacity(); + } private void decrementQueueSize(final QueueEntry entry) @@ -1170,11 +1182,64 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } } - public void deliverAsync() + public void checkCapacity(AMQChannel channel) { - _stateChangeCount.incrementAndGet(); + if(_capacity != 0l) + { + if(_atomicQueueSize.get() > _capacity) + { + _overfull.set(true); + //Overfull log message + _logActor.message(_logSubject, QueueMessages.QUE_1003(_atomicQueueSize.get(), _capacity)); + + if(_blockedChannels.putIfAbsent(channel, Boolean.TRUE)==null) + { + channel.block(this); + } + + if(_atomicQueueSize.get() <= _flowResumeCapacity) + { + + //Underfull log message + _logActor.message(_logSubject, QueueMessages.QUE_1004(_atomicQueueSize.get(), _flowResumeCapacity)); + + channel.unblock(this); + _blockedChannels.remove(channel); + + } + + } + + + + } + } + + private void checkCapacity() + { + if(_capacity != 0L) + { + if(_overfull.get() && _atomicQueueSize.get() <= _flowResumeCapacity) + { + if(_overfull.compareAndSet(true,false)) + {//Underfull log message + _logActor.message(_logSubject, QueueMessages.QUE_1004(_atomicQueueSize.get(), _flowResumeCapacity)); + } - Runner runner = new Runner(); + + for(AMQChannel c : _blockedChannels.keySet()) + { + c.unblock(this); + _blockedChannels.remove(c); + } + } + } + } + + + public void deliverAsync() + { + Runner runner = new Runner(_stateChangeCount.incrementAndGet()); if (_asynchronousRunner.compareAndSet(null, runner)) { @@ -1187,13 +1252,23 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener _asyncDelivery.execute(new SubFlushRunner(sub)); } + private class Runner implements ReadWriteRunnable { + String _name; + public Runner(long count) + { + _name = "QueueRunner-" + count + "-" + _logActor; + } + public void run() { + String originalName = Thread.currentThread().getName(); try { + Thread.currentThread().setName(_name); CurrentActor.set(_logActor); + processQueue(this); } catch (AMQException e) @@ -1203,9 +1278,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener finally { CurrentActor.remove(); + Thread.currentThread().setName(originalName); } - - } public boolean isRead() @@ -1217,6 +1291,11 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener { return true; } + + public String toString() + { + return _name; + } } private class SubFlushRunner implements ReadWriteRunnable @@ -1230,27 +1309,36 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener public void run() { - boolean complete = false; - try - { - CurrentActor.set(_sub.getLogActor()); - complete = flushSubscription(_sub, new Long(MAX_ASYNC_DELIVERIES)); - } - catch (AMQException e) - { - _logger.error(e); + String originalName = Thread.currentThread().getName(); + try{ + Thread.currentThread().setName("SubFlushRunner-"+_sub); + + boolean complete = false; + try + { + CurrentActor.set(_sub.getLogActor()); + complete = flushSubscription(_sub, new Long(MAX_ASYNC_DELIVERIES)); + + } + catch (AMQException e) + { + _logger.error(e); + } + finally + { + CurrentActor.remove(); + } + if (!complete && !_sub.isSuspended()) + { + _asyncDelivery.execute(this); + } } finally { - CurrentActor.remove(); - } - if (!complete && !_sub.isSuspended()) - { - _asyncDelivery.execute(this); + Thread.currentThread().setName(originalName); } - } public boolean isRead() @@ -1278,7 +1366,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener try { sub.getSendLock(); - atTail = attemptDelivery(sub); + atTail = attemptDelivery(sub); if (atTail && sub.isAutoClose()) { unregisterSubscription(sub); @@ -1308,63 +1396,81 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener return atTail; } + /** + * Attempt delivery for the given subscription. + * + * Looks up the next node for the subscription and attempts to deliver it. + * + * @param sub + * @return true if we have completed all possible deliveries for this sub. + * @throws AMQException + */ private boolean attemptDelivery(Subscription sub) throws AMQException { boolean atTail = false; boolean advanced = false; - boolean subActive = sub.isActive(); + boolean subActive = sub.isActive() && !sub.isSuspended(); if (subActive) { QueueEntry node = moveSubscriptionToNextNode(sub); + if (_logger.isDebugEnabled()) + { + _logger.debug(sub + ": attempting Delivery: " + node.debugIdentity()); + } if (!(node.isAcquired() || node.isDeleted())) { - if (!sub.isSuspended()) + if (sub.hasInterest(node)) { - if (sub.hasInterest(node)) + if (!sub.wouldSuspend(node)) { - if (!sub.wouldSuspend(node)) + if (!sub.isBrowser() && !node.acquire(sub)) { - if (!sub.isBrowser() && !node.acquire(sub)) - { - sub.restoreCredit(node); - } - else + sub.restoreCredit(node); + } + else + { + deliverMessage(sub, node); + + if (sub.isBrowser()) { - deliverMessage(sub, node); + QueueEntry newNode = _entries.next(node); - if (sub.isBrowser()) + if (newNode != null) { - QueueEntry newNode = _entries.next(node); - - if (newNode != null) - { - advanced = true; - sub.setLastSeenEntry(node, newNode); - node = sub.getLastSeenEntry(); - } + advanced = true; + sub.setLastSeenEntry(node, newNode); + node = sub.getLastSeenEntry(); } + } - - } - else // Not enough Credit for message and wouldSuspend - { - //QPID-1187 - Treat the subscription as suspended for this message - // and wait for the message to be removed to continue delivery. - subActive = false; - node.addStateChangeListener(new QueueEntryListener(sub, node)); } + } - else + else // Not enough Credit for message and wouldSuspend { - // this subscription is not interested in this node so we can skip over it - QueueEntry newNode = _entries.next(node); - if (newNode != null) - { - sub.setLastSeenEntry(node, newNode); - } + //QPID-1187 - Treat the subscription as suspended for this message + // and wait for the message to be removed to continue delivery. + + // 2009-09-30 : MR : setting subActive = false only causes, this + // particular delivery attempt to end. This is called from + // flushSubscription and processQueue both of which attempt + // delivery a number of times. Won't a bytes limited + // subscriber with not enough credit for the next message + // create a lot of new QELs? How about a browser that calls + // this method LONG.MAX_LONG times! + subActive = false; + node.addStateChangeListener(new QueueEntryListener(sub, node)); + } + } + else + { + // this subscription is not interested in this node so we can skip over it + QueueEntry newNode = _entries.next(node); + if (newNode != null) + { + sub.setLastSeenEntry(node, newNode); } } - } atTail = (_entries.next(node) == null) && !advanced; } @@ -1409,6 +1515,12 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } } + + if (_logger.isDebugEnabled()) + { + _logger.debug(sub + ": nextNode: " + (node == null ? "null" : node.debugIdentity())); + } + return node; } @@ -1423,6 +1535,11 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener _asynchronousRunner.compareAndSet(runner, null); + // For every message enqueue/requeue the we fire deliveryAsync() which + // increases _stateChangeCount. If _sCC changes whilst we are in our loop + // (detected by setting previousStateChangeCount to stateChangeCount in the loop body) + // then we will continue to run for a maximum of iterations. + // So whilst delivery/rejection is going on a processQueue thread will be running while (iterations != 0 && ((previousStateChangeCount != (stateChangeCount = _stateChangeCount.get())) || deliveryIncomplete) && _asynchronousRunner.compareAndSet(null, runner)) { // we want to have one extra loop after every subscription has reached the point where it cannot move @@ -1442,20 +1559,11 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener //iterate over the subscribers and try to advance their pointer while (subscriptionIter.advance()) { - boolean closeConsumer = false; Subscription sub = subscriptionIter.getNode().getSubscription(); sub.getSendLock(); try { - if (sub != null) - { - - QueueEntry node = moveSubscriptionToNextNode(sub); - if (node != null) - { - done = attemptDelivery(sub); - } - } + done = attemptDelivery(sub); if (done) { if (extraLoops == 0) @@ -1492,11 +1600,14 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener // therefore we should schedule this runner again (unless someone beats us to it :-) ). if (iterations == 0 && _asynchronousRunner.compareAndSet(null, runner)) { + if (_logger.isDebugEnabled()) + { + _logger.debug("Rescheduling runner:" + runner); + } _asyncDelivery.execute(runner); } } - @Override public void checkMessageStatus() throws AMQException { @@ -1604,6 +1715,27 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } } + public long getCapacity() + { + return _capacity; + } + + public void setCapacity(long capacity) + { + _capacity = capacity; + } + + public long getFlowResumeCapacity() + { + return _flowResumeCapacity; + } + + public void setFlowResumeCapacity(long flowResumeCapacity) + { + _flowResumeCapacity = flowResumeCapacity; + } + + public Set getNotificationChecks() { return _notificationChecks; @@ -1673,6 +1805,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener setMaximumMessageSize(config.getMaximumMessageSize()); setMaximumMessageCount(config.getMaximumMessageCount()); setMinimumAlertRepeatGap(config.getMinimumAlertRepeatGap()); + _capacity = config.getCapacity(); + _flowResumeCapacity = config.getFlowResumeCapacity(); } } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java index 9575bfa1ec..1affdd6590 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java @@ -102,7 +102,7 @@ public abstract class ApplicationRegistry implements IApplicationRegistry try { - instance.initialise(); + instance.initialise(instanceID); } catch (Exception e) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java index a049d1eb09..831f928832 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java @@ -42,18 +42,22 @@ import java.io.File; public class ConfigurationFileApplicationRegistry extends ApplicationRegistry { + private String _registryName; public ConfigurationFileApplicationRegistry(File configurationURL) throws ConfigurationException { super(new ServerConfiguration(configurationURL)); } - public void initialise() throws Exception + public void initialise(int instanceID) throws Exception { _rootMessageLogger = new RootMessageLoggerImpl(_configuration, new Log4jMessageLogger()); + + _registryName = String.valueOf(instanceID); + // Set the Actor for current log messages - CurrentActor.set(new BrokerActor(_rootMessageLogger)); + CurrentActor.set(new BrokerActor(_registryName, _rootMessageLogger)); CurrentActor.get().message(BrokerMessages.BRK_1001(QpidProperties.getReleaseVersion(),QpidProperties.getBuildVersion())); @@ -83,7 +87,7 @@ public class ConfigurationFileApplicationRegistry extends ApplicationRegistry public void close() throws Exception { //Set the Actor for Broker Shutdown - CurrentActor.set(new BrokerActor(_rootMessageLogger)); + CurrentActor.set(new BrokerActor(_registryName, _rootMessageLogger)); try { super.close(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java index ddb3fce5d2..92accb3499 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java @@ -43,8 +43,9 @@ public interface IApplicationRegistry * Initialise the application registry. All initialisation must be done in this method so that any components * that need access to the application registry itself for initialisation are able to use it. Attempting to * initialise in the constructor will lead to failures since the registry reference will not have been set. + * @param instanceID the instanceID that we can use to identify this AR. */ - void initialise() throws Exception; + void initialise(int instanceID) throws Exception; /** * Shutdown this Registry diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalPermissions.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalPermissions.java index f852514444..d2bbb795e9 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalPermissions.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalPermissions.java @@ -20,16 +20,17 @@ */ package org.apache.qpid.server.security.access; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.QueueBindBody; -import org.apache.qpid.framing.QueueDeclareBody; -import org.apache.qpid.framing.ExchangeDeclareBody; +import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.security.access.ACLPlugin.AuthzResult; -import org.apache.qpid.server.exchange.Exchange; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; public class PrincipalPermissions { @@ -41,6 +42,7 @@ public class PrincipalPermissions private static final Object CREATE_QUEUES_KEY = new Object(); private static final Object CREATE_EXCHANGES_KEY = new Object(); + private static final Object CREATE_QUEUE_TEMPORARY_KEY = new Object(); private static final Object CREATE_QUEUE_QUEUES_KEY = new Object(); private static final Object CREATE_QUEUE_EXCHANGES_KEY = new Object(); @@ -80,248 +82,257 @@ public class PrincipalPermissions { switch (permission) { - case ACCESS: - break; // This is a no-op as the existence of this PrincipalPermission object is scoped per VHost for ACCESS - case BIND: - break; // All the details are currently included in the create setup. case CONSUME: // Parameters : AMQShortString queueName, Boolean Temporary, Boolean ownQueueOnly - Map consumeRights = (Map) _permissions.get(permission); - - if (consumeRights == null) - { - consumeRights = new ConcurrentHashMap(); - _permissions.put(permission, consumeRights); - } - - //if we have parametsre - if (parameters.length > 0) - { - AMQShortString queueName = (AMQShortString) parameters[0]; - Boolean temporary = (Boolean) parameters[1]; - Boolean ownQueueOnly = (Boolean) parameters[2]; - - if (temporary) - { - consumeRights.put(CONSUME_TEMPORARY_KEY, true); - } - else - { - consumeRights.put(CONSUME_TEMPORARY_KEY, false); - } - - if (ownQueueOnly) - { - consumeRights.put(CONSUME_OWN_QUEUES_ONLY_KEY, true); - } - else - { - consumeRights.put(CONSUME_OWN_QUEUES_ONLY_KEY, false); - } - - - LinkedList queues = (LinkedList) consumeRights.get(CONSUME_QUEUES_KEY); - if (queues == null) - { - queues = new LinkedList(); - consumeRights.put(CONSUME_QUEUES_KEY, queues); - } - - if (queueName != null) - { - queues.add(queueName); - } - } - - + grantConsume(permission, parameters); break; case CREATEQUEUE: // Parameters : Boolean temporary, AMQShortString queueName // , AMQShortString exchangeName , AMQShortString routingKey - - Map createRights = (Map) _permissions.get(permission); - - if (createRights == null) - { - createRights = new ConcurrentHashMap(); - _permissions.put(permission, createRights); - - } - - //The existence of the empty map mean permission to all. - if (parameters.length == 0) - { - return; - } - - Boolean temporary = (Boolean) parameters[0]; - - AMQShortString queueName = parameters.length > 1 ? (AMQShortString) parameters[1] : null; - AMQShortString exchangeName = parameters.length > 2 ? (AMQShortString) parameters[2] : null; - //Set the routingkey to the specified value or the queueName if present - AMQShortString routingKey = (parameters.length > 3 && null != parameters[3]) ? (AMQShortString) parameters[3] : queueName; - - // Get the queues map - Map create_queues = (Map) createRights.get(CREATE_QUEUES_KEY); - - if (create_queues == null) - { - create_queues = new ConcurrentHashMap(); - createRights.put(CREATE_QUEUES_KEY, create_queues); - } - - //Allow all temp queues to be created - create_queues.put(CREATE_QUEUE_TEMPORARY_KEY, temporary); - - //Create empty list of queues - Map create_queues_queues = (Map) create_queues.get(CREATE_QUEUE_QUEUES_KEY); - - if (create_queues_queues == null) - { - create_queues_queues = new ConcurrentHashMap(); - create_queues.put(CREATE_QUEUE_QUEUES_KEY, create_queues_queues); - } - - // We are granting CREATE rights to all temporary queues only - if (parameters.length == 1) - { - return; - } - - // if we have a queueName then we need to store any associated exchange / rk bindings - if (queueName != null) - { - Map queue = (Map) create_queues_queues.get(queueName); - if (queue == null) - { - queue = new ConcurrentHashMap(); - create_queues_queues.put(queueName, queue); - } - - if (exchangeName != null) - { - queue.put(exchangeName, routingKey); - } - - //If no exchange is specified then the presence of the queueName in the map says any exchange is ok - } - - // Store the exchange that we are being granted rights to. This will be used as part of binding - - //Lookup the list of exchanges - Map create_queues_exchanges = (Map) create_queues.get(CREATE_QUEUE_EXCHANGES_KEY); - - if (create_queues_exchanges == null) - { - create_queues_exchanges = new ConcurrentHashMap(); - create_queues.put(CREATE_QUEUE_EXCHANGES_KEY, create_queues_exchanges); - } - - //if we have an exchange - if (exchangeName != null) - { - //Retrieve the list of permitted exchanges. - Map exchanges = (Map) create_queues_exchanges.get(exchangeName); - - if (exchanges == null) - { - exchanges = new ConcurrentHashMap(); - create_queues_exchanges.put(exchangeName, exchanges); - } - - //Store the temporary setting CREATE_QUEUE_EXCHANGES_ROUTINGKEYS_KEY - exchanges.put(CREATE_QUEUE_EXCHANGES_TEMPORARY_KEY, temporary); - - //Store the binding details of queue/rk for this exchange. - if (queueName != null) - { - //Retrieve the list of permitted routingKeys. - Map rKeys = (Map) exchanges.get(exchangeName); - - if (rKeys == null) - { - rKeys = new ConcurrentHashMap(); - exchanges.put(CREATE_QUEUE_EXCHANGES_ROUTINGKEYS_KEY, rKeys); - } - - rKeys.put(queueName, routingKey); - } - } + grantCreateQueue(permission, parameters); break; case CREATEEXCHANGE: // Parameters AMQShortString exchangeName , AMQShortString Class - Map rights = (Map) _permissions.get(permission); - if (rights == null) - { - rights = new ConcurrentHashMap(); - _permissions.put(permission, rights); - } - - Map create_exchanges = (Map) rights.get(CREATE_EXCHANGES_KEY); - if (create_exchanges == null) - { - create_exchanges = new ConcurrentHashMap(); - rights.put(CREATE_EXCHANGES_KEY, create_exchanges); - } - - //Should perhaps error if parameters[0] is null; - AMQShortString name = parameters.length > 0 ? (AMQShortString) parameters[0] : null; - AMQShortString className = parameters.length > 1 ? (AMQShortString) parameters[1] : new AMQShortString("direct"); - - //Store the exchangeName / class mapping if the mapping is null - rights.put(name, className); - break; - case DELETE: + grantCreateExchange(permission, parameters); break; - case PUBLISH: // Parameters : Exchange exchange, AMQShortString routingKey - Map publishRights = (Map) _permissions.get(permission); - - if (publishRights == null) - { - publishRights = new ConcurrentHashMap(); - _permissions.put(permission, publishRights); - } - - if (parameters == null || parameters.length == 0) - { - //If we have no parameters then allow publish to all destinations - // this is signified by having a null value for publish_exchanges - } - else - { - Map publish_exchanges = (Map) publishRights.get(PUBLISH_EXCHANGES_KEY); - - if (publish_exchanges == null) - { - publish_exchanges = new ConcurrentHashMap(); - publishRights.put(PUBLISH_EXCHANGES_KEY, publish_exchanges); - } - - - HashSet routingKeys = (HashSet) publish_exchanges.get(parameters[0]); - - // Check to see if we have a routing key - if (parameters.length == 2) - { - if (routingKeys == null) - { - routingKeys = new HashSet(); - } - //Add routing key to permitted publish destinations - routingKeys.add(parameters[1]); - } - - // Add the updated routingkey list or null if all values allowed - publish_exchanges.put(parameters[0], routingKeys); - } + grantPublish(permission, parameters); break; + /* The other cases just fall through to no-op */ + case DELETE: + case ACCESS: // This is a no-op as the existence of this PrincipalPermission object is scoped per VHost for ACCESS + case BIND: // All the details are currently included in the create setup. case PURGE: - break; case UNBIND: break; } } + private void grantPublish(Permission permission, Object... parameters) { + Map publishRights = (Map) _permissions.get(permission); + + if (publishRights == null) + { + publishRights = new ConcurrentHashMap(); + _permissions.put(permission, publishRights); + } + + if (parameters == null || parameters.length == 0) + { + //If we have no parameters then allow publish to all destinations + // this is signified by having a null value for publish_exchanges + } + else + { + Map publish_exchanges = (Map) publishRights.get(PUBLISH_EXCHANGES_KEY); + + if (publish_exchanges == null) + { + publish_exchanges = new ConcurrentHashMap(); + publishRights.put(PUBLISH_EXCHANGES_KEY, publish_exchanges); + } + + + HashSet routingKeys = (HashSet) publish_exchanges.get(parameters[0]); + + // Check to see if we have a routing key + if (parameters.length == 2) + { + if (routingKeys == null) + { + routingKeys = new HashSet(); + } + //Add routing key to permitted publish destinations + routingKeys.add(parameters[1]); + } + + // Add the updated routingkey list or null if all values allowed + publish_exchanges.put(parameters[0], routingKeys); + } + } + + private void grantCreateExchange(Permission permission, Object... parameters) { + Map rights = (Map) _permissions.get(permission); + if (rights == null) + { + rights = new ConcurrentHashMap(); + _permissions.put(permission, rights); + } + + Map create_exchanges = (Map) rights.get(CREATE_EXCHANGES_KEY); + if (create_exchanges == null) + { + create_exchanges = new ConcurrentHashMap(); + rights.put(CREATE_EXCHANGES_KEY, create_exchanges); + } + + //Should perhaps error if parameters[0] is null; + AMQShortString name = parameters.length > 0 ? (AMQShortString) parameters[0] : null; + AMQShortString className = parameters.length > 1 ? (AMQShortString) parameters[1] : new AMQShortString("direct"); + + //Store the exchangeName / class mapping if the mapping is null + rights.put(name, className); + } + + private void grantCreateQueue(Permission permission, Object... parameters) { + Map createRights = (Map) _permissions.get(permission); + + if (createRights == null) + { + createRights = new ConcurrentHashMap(); + _permissions.put(permission, createRights); + + } + + //The existence of the empty map mean permission to all. + if (parameters.length == 0) + { + return; + } + + Boolean temporary = (Boolean) parameters[0]; + + AMQShortString queueName = parameters.length > 1 ? (AMQShortString) parameters[1] : null; + AMQShortString exchangeName = parameters.length > 2 ? (AMQShortString) parameters[2] : null; + //Set the routingkey to the specified value or the queueName if present + AMQShortString routingKey = (parameters.length > 3 && null != parameters[3]) ? (AMQShortString) parameters[3] : queueName; + + // Get the queues map + Map create_queues = (Map) createRights.get(CREATE_QUEUES_KEY); + + if (create_queues == null) + { + create_queues = new ConcurrentHashMap(); + createRights.put(CREATE_QUEUES_KEY, create_queues); + } + + //Allow all temp queues to be created + create_queues.put(CREATE_QUEUE_TEMPORARY_KEY, temporary); + + //Create empty list of queues + Map create_queues_queues = (Map) create_queues.get(CREATE_QUEUE_QUEUES_KEY); + + if (create_queues_queues == null) + { + create_queues_queues = new ConcurrentHashMap(); + create_queues.put(CREATE_QUEUE_QUEUES_KEY, create_queues_queues); + } + + // We are granting CREATE rights to all temporary queues only + if (parameters.length == 1) + { + return; + } + + // if we have a queueName then we need to store any associated exchange / rk bindings + if (queueName != null) + { + Map queue = (Map) create_queues_queues.get(queueName); + if (queue == null) + { + queue = new ConcurrentHashMap(); + create_queues_queues.put(queueName, queue); + } + + if (exchangeName != null) + { + queue.put(exchangeName, routingKey); + } + + //If no exchange is specified then the presence of the queueName in the map says any exchange is ok + } + + // Store the exchange that we are being granted rights to. This will be used as part of binding + + //Lookup the list of exchanges + Map create_queues_exchanges = (Map) create_queues.get(CREATE_QUEUE_EXCHANGES_KEY); + + if (create_queues_exchanges == null) + { + create_queues_exchanges = new ConcurrentHashMap(); + create_queues.put(CREATE_QUEUE_EXCHANGES_KEY, create_queues_exchanges); + } + + //if we have an exchange + if (exchangeName != null) + { + //Retrieve the list of permitted exchanges. + Map exchanges = (Map) create_queues_exchanges.get(exchangeName); + + if (exchanges == null) + { + exchanges = new ConcurrentHashMap(); + create_queues_exchanges.put(exchangeName, exchanges); + } + + //Store the temporary setting CREATE_QUEUE_EXCHANGES_ROUTINGKEYS_KEY + exchanges.put(CREATE_QUEUE_EXCHANGES_TEMPORARY_KEY, temporary); + + //Store the binding details of queue/rk for this exchange. + if (queueName != null) + { + //Retrieve the list of permitted routingKeys. + Map rKeys = (Map) exchanges.get(exchangeName); + + if (rKeys == null) + { + rKeys = new ConcurrentHashMap(); + exchanges.put(CREATE_QUEUE_EXCHANGES_ROUTINGKEYS_KEY, rKeys); + } + + rKeys.put(queueName, routingKey); + } + } + } + + private void grantConsume(Permission permission, Object... parameters) { + Map consumeRights = (Map) _permissions.get(permission); + + if (consumeRights == null) + { + consumeRights = new ConcurrentHashMap(); + _permissions.put(permission, consumeRights); + } + + //if we have parametsre + if (parameters.length > 0) + { + AMQShortString queueName = (AMQShortString) parameters[0]; + Boolean temporary = (Boolean) parameters[1]; + Boolean ownQueueOnly = (Boolean) parameters[2]; + + if (temporary) + { + consumeRights.put(CONSUME_TEMPORARY_KEY, true); + } + else + { + consumeRights.put(CONSUME_TEMPORARY_KEY, false); + } + + if (ownQueueOnly) + { + consumeRights.put(CONSUME_OWN_QUEUES_ONLY_KEY, true); + } + else + { + consumeRights.put(CONSUME_OWN_QUEUES_ONLY_KEY, false); + } + + + LinkedList queues = (LinkedList) consumeRights.get(CONSUME_QUEUES_KEY); + if (queues == null) + { + queues = new LinkedList(); + consumeRights.put(CONSUME_QUEUES_KEY, queues); + } + + if (queueName != null) + { + queues.add(queueName); + } + } + } + /** * * @param permission the type of permission to check @@ -346,267 +357,286 @@ public class PrincipalPermissions return AuthzResult.ALLOWED; // This is here for completeness but the SimpleXML ACLManager never calls it. // The existence of this user specific PP can be validated in the map SimpleXML maintains. case BIND: // Parameters : QueueBindMethod , Exchange , AMQQueue, AMQShortString routingKey - - Exchange exchange = (Exchange) parameters[1]; - - AMQQueue bind_queueName = (AMQQueue) parameters[2]; - AMQShortString routingKey = (AMQShortString) parameters[3]; - - //Get all Create Rights for this user - Map bindCreateRights = (Map) _permissions.get(Permission.CREATEQUEUE); - - //Look up the Queue Creation Rights - Map bind_create_queues = (Map) bindCreateRights.get(CREATE_QUEUES_KEY); - - //Lookup the list of queues - Map bind_create_queues_queues = (Map) bindCreateRights.get(CREATE_QUEUE_QUEUES_KEY); - - // Check and see if we have a queue white list to check - if (bind_create_queues_queues != null) - { - //There a white list for queues - Map exchangeDetails = (Map) bind_create_queues_queues.get(bind_queueName); - - if (exchangeDetails == null) //Then all queue can be bound to all exchanges. - { - return AuthzResult.ALLOWED; - } - - // Check to see if we have a white list of routingkeys to check - Map rkeys = (Map) exchangeDetails.get(exchange.getName()); - - // if keys is null then any rkey is allowed on this exchange - if (rkeys == null) - { - // There is no routingkey white list - return AuthzResult.ALLOWED; - } - else - { - // We have routingKeys so a match must be found to allowed binding - Iterator keys = rkeys.keySet().iterator(); - - boolean matched = false; - while (keys.hasNext() && !matched) - { - AMQShortString rkey = (AMQShortString) keys.next(); - if (rkey.endsWith("*")) - { - matched = routingKey.startsWith(rkey.subSequence(0, rkey.length() - 1).toString()); - } - else - { - matched = routingKey.equals(rkey); - } - } - - - return (matched) ? AuthzResult.ALLOWED : AuthzResult.DENIED; - } - - - } - else - { - //There a is no white list for queues - - // So can allow all queues to be bound - // but we should first check and see if we have a temp queue and validate that we are allowed - // to bind temp queues. - - //Check to see if we have a temporary queue - if (bind_queueName.isAutoDelete()) - { - // Check and see if we have an exchange white list. - Map bind_exchanges = (Map) bind_create_queues.get(CREATE_QUEUE_EXCHANGES_KEY); - - // If the exchange exists then we must check to see if temporary queues are allowed here - if (bind_exchanges != null) - { - // Check to see if the requested exchange is allowed. - Map exchangeDetails = (Map) bind_exchanges.get(exchange.getName()); - - return ((Boolean) exchangeDetails.get(CREATE_QUEUE_EXCHANGES_TEMPORARY_KEY)) ? AuthzResult.ALLOWED : AuthzResult.DENIED; - } - - //no white list so all allowed, drop through to return true below. - } - - // not a temporary queue and no white list so all allowed. - return AuthzResult.ALLOWED; - } - + return authoriseBind(parameters); case CREATEQUEUE:// Parameters : boolean autodelete, AMQShortString name - - Map createRights = (Map) _permissions.get(permission); - - // If there are no create rights then deny request - if (createRights == null) - { - return AuthzResult.DENIED; - } - - //Look up the Queue Creation Rights - Map create_queues = (Map) createRights.get(CREATE_QUEUES_KEY); - - //Lookup the list of queues allowed to be created - Map create_queues_queues = (Map) create_queues.get(CREATE_QUEUE_QUEUES_KEY); - - - AMQShortString queueName = (AMQShortString) parameters[1]; - Boolean autoDelete = (Boolean) parameters[0]; - - if (autoDelete)// we have a temporary queue - { - return ((Boolean) create_queues.get(CREATE_QUEUE_TEMPORARY_KEY)) ? AuthzResult.ALLOWED : AuthzResult.DENIED; - } - else - { - // If there is a white list then check - if (create_queues_queues == null || create_queues_queues.containsKey(queueName)) - { - return AuthzResult.ALLOWED; - } - else - { - return AuthzResult.DENIED; - } - - } + return authoriseCreateQueue(permission, parameters); case CREATEEXCHANGE: - Map rights = (Map) _permissions.get(permission); - - AMQShortString exchangeName = (AMQShortString) parameters[0]; - - // If the exchange list is doesn't exist then all is allowed else - // check the valid exchanges - if (rights == null || rights.containsKey(exchangeName)) - { - return AuthzResult.ALLOWED; - } - else - { - return AuthzResult.DENIED; - } + return authoriseCreateExchange(permission, parameters); case CONSUME: // Parameters : AMQQueue - - if (parameters.length == 1 && parameters[0] instanceof AMQQueue) - { - AMQQueue queue = ((AMQQueue) parameters[0]); - Map queuePermissions = (Map) _permissions.get(permission); - - List queues = (List) queuePermissions.get(CONSUME_QUEUES_KEY); - - Boolean temporayQueues = (Boolean) queuePermissions.get(CONSUME_TEMPORARY_KEY); - Boolean ownQueuesOnly = (Boolean) queuePermissions.get(CONSUME_OWN_QUEUES_ONLY_KEY); - - // If user is allowed to publish to temporary queues and this is a temp queue then allow it. - if (temporayQueues) - { - if (queue.isAutoDelete()) - // This will allow consumption from any temporary queue including ones not owned by this user. - // Of course the exclusivity will not be broken. - { - // if not limited to ownQueuesOnly then ok else check queue Owner. - return (!ownQueuesOnly || queue.getOwner().equals(_user)) ? AuthzResult.ALLOWED : AuthzResult.DENIED; - } - else - { - return AuthzResult.DENIED; - } - } - - // if queues are white listed then ensure it is ok - if (queues != null) - { - // if no queues are listed then ALL are ok othereise it must be specified. - if (ownQueuesOnly) - { - if (queue.getOwner().equals(_user)) - { - return (queues.size() == 0 || queues.contains(queue.getName())) ? AuthzResult.ALLOWED : AuthzResult.DENIED; - } - else - { - return AuthzResult.DENIED; - } - } - - // If we are - return (queues.size() == 0 || queues.contains(queue.getName())) ? AuthzResult.ALLOWED : AuthzResult.DENIED; - } - } - - // Can't authenticate without the right parameters - return AuthzResult.DENIED; - case DELETE: - break; - + return authoriseConsume(permission, parameters); case PUBLISH: // Parameters : Exchange exchange, AMQShortString routingKey - Map publishRights = (Map) _permissions.get(permission); - - if (publishRights == null) - { - return AuthzResult.DENIED; - } - - Map exchanges = (Map) publishRights.get(PUBLISH_EXCHANGES_KEY); - - // Having no exchanges listed gives full publish rights to all exchanges - if (exchanges == null) - { - return AuthzResult.ALLOWED; - } - // Otherwise exchange must be listed in the white list - - // If the map doesn't have the exchange then it isn't allowed - if (!exchanges.containsKey(((Exchange) parameters[0]).getName())) - { - return AuthzResult.DENIED; - } - else - { - - // Get valid routing keys - HashSet routingKeys = (HashSet) exchanges.get(((Exchange)parameters[0]).getName()); - - // Having no routingKeys in the map then all are allowed. - if (routingKeys == null) - { - return AuthzResult.ALLOWED; - } - else - { - // We have routingKeys so a match must be found to allowed binding - Iterator keys = routingKeys.iterator(); - - - AMQShortString publishRKey = (AMQShortString)parameters[1]; - - boolean matched = false; - while (keys.hasNext() && !matched) - { - AMQShortString rkey = (AMQShortString) keys.next(); - - if (rkey.endsWith("*")) - { - matched = publishRKey.startsWith(rkey.subSequence(0, rkey.length() - 1)); - } - else - { - matched = publishRKey.equals(rkey); - } - } - return (matched) ? AuthzResult.ALLOWED : AuthzResult.DENIED; - } - } + return authorisePublish(permission, parameters); + /* Fall through */ + case DELETE: case PURGE: - break; case UNBIND: - break; - + default: + return AuthzResult.DENIED; } - return AuthzResult.DENIED; } + + private AuthzResult authoriseConsume(Permission permission, Object... parameters) { + if (parameters.length == 1 && parameters[0] instanceof AMQQueue) + { + AMQQueue queue = ((AMQQueue) parameters[0]); + Map queuePermissions = (Map) _permissions.get(permission); + + if (queuePermissions == null) + { + //if the outer map is null, the user has no CONSUME rights at all + return AuthzResult.DENIED; + } + + List queues = (List) queuePermissions.get(CONSUME_QUEUES_KEY); + + Boolean temporayQueues = (Boolean) queuePermissions.get(CONSUME_TEMPORARY_KEY); + Boolean ownQueuesOnly = (Boolean) queuePermissions.get(CONSUME_OWN_QUEUES_ONLY_KEY); + + // If user is allowed to publish to temporary queues and this is a temp queue then allow it. + if (temporayQueues) + { + if (queue.isAutoDelete()) + // This will allow consumption from any temporary queue including ones not owned by this user. + // Of course the exclusivity will not be broken. + { + // if not limited to ownQueuesOnly then ok else check queue Owner. + return (!ownQueuesOnly || queue.getOwner().equals(_user)) ? AuthzResult.ALLOWED : AuthzResult.DENIED; + } + else + { + return AuthzResult.DENIED; + } + } + + // if queues are white listed then ensure it is ok + if (queues != null) + { + // if no queues are listed then ALL are ok othereise it must be specified. + if (ownQueuesOnly) + { + if (queue.getOwner().equals(_user)) + { + return (queues.size() == 0 || queues.contains(queue.getName())) ? AuthzResult.ALLOWED : AuthzResult.DENIED; + } + else + { + return AuthzResult.DENIED; + } + } + + // If we are + return (queues.size() == 0 || queues.contains(queue.getName())) ? AuthzResult.ALLOWED : AuthzResult.DENIED; + } + } + + // Can't authenticate without the right parameters + return AuthzResult.DENIED; + } + + private AuthzResult authorisePublish(Permission permission, Object... parameters) { + Map publishRights = (Map) _permissions.get(permission); + + if (publishRights == null) + { + return AuthzResult.DENIED; + } + + Map exchanges = (Map) publishRights.get(PUBLISH_EXCHANGES_KEY); + + // Having no exchanges listed gives full publish rights to all exchanges + if (exchanges == null) + { + return AuthzResult.ALLOWED; + } + // Otherwise exchange must be listed in the white list + + // If the map doesn't have the exchange then it isn't allowed + if (!exchanges.containsKey(((Exchange) parameters[0]).getName())) + { + return AuthzResult.DENIED; + } + else + { + + // Get valid routing keys + HashSet routingKeys = (HashSet) exchanges.get(((Exchange)parameters[0]).getName()); + + // Having no routingKeys in the map then all are allowed. + if (routingKeys == null) + { + return AuthzResult.ALLOWED; + } + else + { + // We have routingKeys so a match must be found to allowed binding + Iterator keys = routingKeys.iterator(); + + + AMQShortString publishRKey = (AMQShortString)parameters[1]; + + boolean matched = false; + while (keys.hasNext() && !matched) + { + AMQShortString rkey = (AMQShortString) keys.next(); + + if (rkey.endsWith("*")) + { + matched = publishRKey.startsWith(rkey.subSequence(0, rkey.length() - 1)); + } + else + { + matched = publishRKey.equals(rkey); + } + } + return (matched) ? AuthzResult.ALLOWED : AuthzResult.DENIED; + } + } + } + + private AuthzResult authoriseCreateExchange(Permission permission, Object... parameters) { + Map rights = (Map) _permissions.get(permission); + + AMQShortString exchangeName = (AMQShortString) parameters[0]; + + // If the exchange list is doesn't exist then all is allowed else + // check the valid exchanges + if (rights == null || rights.containsKey(exchangeName)) + { + return AuthzResult.ALLOWED; + } + else + { + return AuthzResult.DENIED; + } + } + + private AuthzResult authoriseCreateQueue(Permission permission, Object... parameters) { + Map createRights = (Map) _permissions.get(permission); + + // If there are no create rights then deny request + if (createRights == null) + { + return AuthzResult.DENIED; + } + + //Look up the Queue Creation Rights + Map create_queues = (Map) createRights.get(CREATE_QUEUES_KEY); + + //Lookup the list of queues allowed to be created + Map create_queues_queues = (Map) create_queues.get(CREATE_QUEUE_QUEUES_KEY); + + + AMQShortString queueName = (AMQShortString) parameters[1]; + Boolean autoDelete = (Boolean) parameters[0]; + + if (autoDelete)// we have a temporary queue + { + return ((Boolean) create_queues.get(CREATE_QUEUE_TEMPORARY_KEY)) ? AuthzResult.ALLOWED : AuthzResult.DENIED; + } + else + { + // If there is a white list then check + if (create_queues_queues == null || create_queues_queues.containsKey(queueName)) + { + return AuthzResult.ALLOWED; + } + else + { + return AuthzResult.DENIED; + } + + } + } + + private AuthzResult authoriseBind(Object... parameters) { + Exchange exchange = (Exchange) parameters[1]; + + AMQQueue bind_queueName = (AMQQueue) parameters[2]; + AMQShortString routingKey = (AMQShortString) parameters[3]; + + //Get all Create Rights for this user + Map bindCreateRights = (Map) _permissions.get(Permission.CREATEQUEUE); + + //Look up the Queue Creation Rights + Map bind_create_queues = (Map) bindCreateRights.get(CREATE_QUEUES_KEY); + + //Lookup the list of queues + Map bind_create_queues_queues = (Map) bindCreateRights.get(CREATE_QUEUE_QUEUES_KEY); + + // Check and see if we have a queue white list to check + if (bind_create_queues_queues != null) + { + //There a white list for queues + Map exchangeDetails = (Map) bind_create_queues_queues.get(bind_queueName); + + if (exchangeDetails == null) //Then all queue can be bound to all exchanges. + { + return AuthzResult.ALLOWED; + } + + // Check to see if we have a white list of routingkeys to check + Map rkeys = (Map) exchangeDetails.get(exchange.getName()); + + // if keys is null then any rkey is allowed on this exchange + if (rkeys == null) + { + // There is no routingkey white list + return AuthzResult.ALLOWED; + } + else + { + // We have routingKeys so a match must be found to allowed binding + Iterator keys = rkeys.keySet().iterator(); + + boolean matched = false; + while (keys.hasNext() && !matched) + { + AMQShortString rkey = (AMQShortString) keys.next(); + if (rkey.endsWith("*")) + { + matched = routingKey.startsWith(rkey.subSequence(0, rkey.length() - 1).toString()); + } + else + { + matched = routingKey.equals(rkey); + } + } + + + return (matched) ? AuthzResult.ALLOWED : AuthzResult.DENIED; + } + + + } + else + { + //There a is no white list for queues + + // So can allow all queues to be bound + // but we should first check and see if we have a temp queue and validate that we are allowed + // to bind temp queues. + + //Check to see if we have a temporary queue + if (bind_queueName.isAutoDelete()) + { + // Check and see if we have an exchange white list. + Map bind_exchanges = (Map) bind_create_queues.get(CREATE_QUEUE_EXCHANGES_KEY); + + // If the exchange exists then we must check to see if temporary queues are allowed here + if (bind_exchanges != null) + { + // Check to see if the requested exchange is allowed. + Map exchangeDetails = (Map) bind_exchanges.get(exchange.getName()); + + return ((Boolean) exchangeDetails.get(CREATE_QUEUE_EXCHANGES_TEMPORARY_KEY)) ? AuthzResult.ALLOWED : AuthzResult.DENIED; + } + + //no white list so all allowed, drop through to return true below. + } + + // not a temporary queue and no white list so all allowed. + return AuthzResult.ALLOWED; + } + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicACLPlugin.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicACLPlugin.java index a6fae053c2..ca6b68dafa 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicACLPlugin.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicACLPlugin.java @@ -35,28 +35,24 @@ public abstract class BasicACLPlugin implements ACLPlugin // Returns true or false if the plugin should authorise or deny the request protected abstract AuthzResult getResult(); - @Override public AuthzResult authoriseBind(AMQProtocolSession session, Exchange exch, AMQQueue queue, AMQShortString routingKey) { return getResult(); } - @Override public AuthzResult authoriseConnect(AMQProtocolSession session, VirtualHost virtualHost) { return getResult(); } - @Override public AuthzResult authoriseConsume(AMQProtocolSession session, boolean noAck, AMQQueue queue) { return getResult(); } - @Override public AuthzResult authoriseConsume(AMQProtocolSession session, boolean exclusive, boolean noAck, boolean noLocal, boolean nowait, AMQQueue queue) @@ -64,7 +60,6 @@ public abstract class BasicACLPlugin implements ACLPlugin return getResult(); } - @Override public AuthzResult authoriseCreateExchange(AMQProtocolSession session, boolean autoDelete, boolean durable, AMQShortString exchangeName, boolean internal, boolean nowait, boolean passive, @@ -73,7 +68,6 @@ public abstract class BasicACLPlugin implements ACLPlugin return getResult(); } - @Override public AuthzResult authoriseCreateQueue(AMQProtocolSession session, boolean autoDelete, boolean durable, boolean exclusive, boolean nowait, boolean passive, AMQShortString queue) @@ -81,19 +75,16 @@ public abstract class BasicACLPlugin implements ACLPlugin return getResult(); } - @Override public AuthzResult authoriseDelete(AMQProtocolSession session, AMQQueue queue) { return getResult(); } - @Override public AuthzResult authoriseDelete(AMQProtocolSession session, Exchange exchange) { return getResult(); } - @Override public AuthzResult authorisePublish(AMQProtocolSession session, boolean immediate, boolean mandatory, AMQShortString routingKey, Exchange e) @@ -101,20 +92,17 @@ public abstract class BasicACLPlugin implements ACLPlugin return getResult(); } - @Override public AuthzResult authorisePurge(AMQProtocolSession session, AMQQueue queue) { return getResult(); } - @Override public AuthzResult authoriseUnbind(AMQProtocolSession session, Exchange exch, AMQShortString routingKey, AMQQueue queue) { return getResult(); } - @Override public void setConfiguration(Configuration config) { // no-op diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/network/FirewallPlugin.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/network/FirewallPlugin.java index 3a81932123..7450322130 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/network/FirewallPlugin.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/network/FirewallPlugin.java @@ -211,7 +211,6 @@ public class FirewallPlugin extends AbstractACLPlugin } - @Override public void setConfiguration(Configuration config) throws ConfigurationException { // Get default action diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabaseManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabaseManager.java index 4efe381a8b..8658101cd8 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabaseManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabaseManager.java @@ -42,7 +42,6 @@ public class PropertiesPrincipalDatabaseManager implements PrincipalDatabaseMana return _databases; } - @Override public void initialiseManagement(ServerConfiguration _configuration) throws ConfigurationException { //todo diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java index 72d6afc65c..2893e916cc 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java @@ -630,11 +630,19 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage public QueueEntry getLastSeenEntry() { - return _queueContext.get(); + QueueEntry entry = _queueContext.get(); + + if(_logger.isDebugEnabled()) + { + _logger.debug(_logActor + ": lastSeenEntry: " + (entry == null ? "null" : entry.debugIdentity())); + } + + return entry; } public boolean setLastSeenEntry(QueueEntry expected, QueueEntry newvalue) { + _logger.debug(debugIdentity() + " Setting Last Seen To:" + (newvalue == null ? "nullNV" : newvalue.debugIdentity())); return _queueContext.compareAndSet(expected,newvalue); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java index 3c71282c57..450852cef7 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java @@ -97,6 +97,7 @@ public class LocalTransactionalContext implements TransactionalContext try { QueueEntry entry = _queue.enqueue(getStoreContext(),_message); + _queue.checkCapacity(_channel); if(entry.immediateAndNotDelivered()) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java index 28af36e3db..10d6021d27 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java @@ -91,6 +91,8 @@ public class NonTransactionalContext implements TransactionalContext public void deliver(final AMQQueue queue, AMQMessage message) throws AMQException { QueueEntry entry = queue.enqueue(_storeContext, message); + queue.checkCapacity(_channel); + //following check implements the functionality //required by the 'immediate' flag: diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java index b16a289f0a..a131c2a465 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java @@ -31,6 +31,7 @@ import org.apache.qpid.server.store.StoreContext; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.AMQChannel; import org.apache.qpid.AMQException; import org.apache.commons.configuration.Configuration; @@ -271,6 +272,15 @@ public class MockAMQQueue implements AMQQueue //To change body of implemented methods use File | Settings | File Templates. } + public boolean getBlockOnQueueFull() + { + return false; + } + + public void setBlockOnQueueFull(boolean block) + { + } + public long getMinimumAlertRepeatGap() { return 0; //To change body of implemented methods use File | Settings | File Templates. @@ -285,8 +295,8 @@ public class MockAMQQueue implements AMQQueue { return 0; //To change body of implemented methods use File | Settings | File Templates. } + - @Override public void checkMessageStatus() throws AMQException { //To change body of implemented methods use File | Settings | File Templates. @@ -317,6 +327,10 @@ public class MockAMQQueue implements AMQQueue //To change body of implemented methods use File | Settings | File Templates. } + public void checkCapacity(AMQChannel channel) + { + } + public ManagedObject getManagedObject() { return null; //To change body of implemented methods use File | Settings | File Templates. @@ -327,12 +341,31 @@ public class MockAMQQueue implements AMQQueue return 0; //To change body of implemented methods use File | Settings | File Templates. } - @Override public void setMinimumAlertRepeatGap(long value) { } + public long getCapacity() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public void setCapacity(long capacity) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public long getFlowResumeCapacity() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public void setFlowResumeCapacity(long flowResumeCapacity) + { + //To change body of implemented methods use File | Settings | File Templates. + } + public void configure(QueueConfiguration config) { diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/registry/ApplicationRegistryShutdownTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/registry/ApplicationRegistryShutdownTest.java index e75ed640aa..4c8ff01be0 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/registry/ApplicationRegistryShutdownTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/registry/ApplicationRegistryShutdownTest.java @@ -22,7 +22,6 @@ package org.apache.qpid.server.registry; import junit.framework.TestCase; import org.apache.qpid.server.util.TestApplicationRegistry; -import org.apache.qpid.AMQException; import java.security.Security; import java.security.Provider; @@ -69,7 +68,7 @@ public class ApplicationRegistryShutdownTest extends TestCase // Register new providers try { - _registry.initialise(); + _registry.initialise(ApplicationRegistry.DEFAULT_INSTANCE); } catch (Exception e) { diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/util/NullApplicationRegistry.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/NullApplicationRegistry.java index 8fef8baa02..6b8201eefb 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/util/NullApplicationRegistry.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/NullApplicationRegistry.java @@ -27,6 +27,7 @@ import org.apache.qpid.server.configuration.VirtualHostConfiguration; import org.apache.qpid.server.logging.RootMessageLoggerImpl; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.actors.TestLogActor; +import org.apache.qpid.server.logging.actors.BrokerActor; import org.apache.qpid.server.logging.rawloggers.Log4jMessageLogger; import org.apache.qpid.server.management.NoopManagedObjectRegistry; import org.apache.qpid.server.plugins.PluginManager; @@ -50,7 +51,7 @@ public class NullApplicationRegistry extends ApplicationRegistry super(new ServerConfiguration(new PropertiesConfiguration())); } - public void initialise() throws Exception + public void initialise(int instanceID) throws Exception { _logger.info("Initialising NullApplicationRegistry"); @@ -92,6 +93,8 @@ public class NullApplicationRegistry extends ApplicationRegistry @Override public void close() throws Exception { + CurrentActor.set(new BrokerActor(_rootMessageLogger)); + try { super.close(); diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java index 43948c05c4..7b7c86bb80 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java @@ -21,7 +21,6 @@ package org.apache.qpid.server.util; import org.apache.commons.configuration.ConfigurationException; -import org.apache.commons.configuration.MapConfiguration; import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.qpid.server.configuration.ServerConfiguration; import org.apache.qpid.server.configuration.VirtualHostConfiguration; @@ -31,7 +30,6 @@ import org.apache.qpid.server.management.NoopManagedObjectRegistry; import org.apache.qpid.server.queue.QueueRegistry; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.security.access.ACLManager; -import org.apache.qpid.server.security.access.ACLPlugin; import org.apache.qpid.server.security.access.plugins.AllowAll; import org.apache.qpid.server.security.auth.database.PropertiesPrincipalDatabaseManager; import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; @@ -45,7 +43,6 @@ import org.apache.qpid.server.logging.actors.TestLogActor; import org.apache.qpid.server.logging.rawloggers.Log4jMessageLogger; import java.util.Collection; -import java.util.HashMap; import java.util.Properties; import java.util.Arrays; @@ -75,7 +72,7 @@ public class TestApplicationRegistry extends ApplicationRegistry _config = config; } - public void initialise() throws Exception + public void initialise(int instanceID) throws Exception { _rootMessageLogger = new RootMessageLoggerImpl(_configuration, new Log4jMessageLogger()); diff --git a/qpid/java/client/src/main/java/log4j.xml b/qpid/java/client/src/main/java/log4j.xml new file mode 100644 index 0000000000..c27acba818 --- /dev/null +++ b/qpid/java/client/src/main/java/log4j.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java index 0d2adcec8a..ed122a772e 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java @@ -313,7 +313,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect protected AMQConnectionDelegate _delegate; // this connection maximum number of prefetched messages - protected int _maxPrefetch; + private int _maxPrefetch; //Indicates whether persistent messages are synchronized private boolean _syncPersistence; @@ -450,7 +450,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } else { - // use the defaul value set for all connections + // use the default value set for all connections _syncPublish = System.getProperty((ClientProperties.SYNC_ACK_PROP_NAME),_syncPublish); } @@ -512,7 +512,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect boolean retryAllowed = true; Exception connectionException = null; - while (!_connected && retryAllowed) + while (!_connected && retryAllowed && brokerDetails != null) { ProtocolVersion pe = null; try @@ -691,12 +691,12 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect public boolean attemptReconnection() { - while (_failoverPolicy.failoverAllowed()) + BrokerDetails broker = null; + while (_failoverPolicy.failoverAllowed() && (broker = _failoverPolicy.getNextBrokerDetails()) != null) { try { - makeBrokerConnection(_failoverPolicy.getNextBrokerDetails()); - + makeBrokerConnection(broker); return true; } catch (Exception e) diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate.java index e5980d8b7d..e6c3473cb1 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate.java @@ -39,6 +39,15 @@ public interface AMQConnectionDelegate Session createSession(final boolean transacted, final int acknowledgeMode, final int prefetchHigh, final int prefetchLow) throws JMSException; + /** + * Create an XASession with default prefetch values of: + * High = MaxPrefetch + * Low = MaxPrefetch / 2 + * @return XASession + * @throws JMSException thrown if there is a problem creating the session. + */ + XASession createXASession() throws JMSException; + XASession createXASession(int prefetchHigh, int prefetchLow) throws JMSException; void failoverPrep(); diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java index 927929c94a..4d10180667 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java @@ -99,6 +99,18 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec return session; } + /** + * Create an XASession with default prefetch values of: + * High = MaxPrefetch + * Low = MaxPrefetch / 2 + * @return XASession + * @throws JMSException + */ + public XASession createXASession() throws JMSException + { + return createXASession((int) _conn.getMaxPrefetch(), (int) _conn.getMaxPrefetch() / 2); + } + /** * create an XA Session and start it if required. */ @@ -285,7 +297,6 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec _qpidConnection.setIdleTimeout(l); } - @Override public int getMaxChannelID() { return Integer.MAX_VALUE; diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java index 9876393d4c..97d0d0516e 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java @@ -191,6 +191,18 @@ public class AMQConnectionDelegate_8_0 implements AMQConnectionDelegate }, _conn).execute(); } + /** + * Create an XASession with default prefetch values of: + * High = MaxPrefetch + * Low = MaxPrefetch / 2 + * @return XASession + * @throws JMSException thrown if there is a problem creating the session. + */ + public XASession createXASession() throws JMSException + { + return createXASession((int) _conn.getMaxPrefetch(), (int) _conn.getMaxPrefetch() / 2); + } + private void createChannelOverWire(int channelId, int prefetchHigh, int prefetchLow, boolean transacted) throws AMQException, FailoverException { @@ -290,7 +302,6 @@ public class AMQConnectionDelegate_8_0 implements AMQConnectionDelegate public void setIdleTimeout(long l){} - @Override public int getMaxChannelID() { return (int) (Math.pow(2, 16)-1); diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java index 2e3e417c95..dd9a00ce10 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java @@ -21,7 +21,6 @@ package org.apache.qpid.client; import java.io.Serializable; -import java.io.IOException; import java.net.URISyntaxException; import java.text.MessageFormat; import java.util.ArrayList; @@ -60,6 +59,7 @@ import javax.jms.Topic; import javax.jms.TopicPublisher; import javax.jms.TopicSession; import javax.jms.TopicSubscriber; +import javax.jms.TransactionRolledBackException; import org.apache.qpid.AMQDisconnectedException; import org.apache.qpid.AMQException; @@ -113,7 +113,6 @@ import org.slf4j.LoggerFactory; public abstract class AMQSession extends Closeable implements Session, QueueSession, TopicSession { - public static final class IdToConsumerMap { private final BasicMessageConsumer[] _fastAccessConsumers = new BasicMessageConsumer[16]; @@ -198,16 +197,32 @@ public abstract class AMQSession= System.currentTimeMillis() ) + { + + _flowControl.wait(FLOW_CONTROL_WAIT_PERIOD); + _logger.warn("Message send delayed by " + (System.currentTimeMillis() + FLOW_CONTROL_WAIT_FAILURE - expiryTime)/1000 + "s due to broker enforced flow control"); + } + if(!_flowControl.getFlowControl()) { - _flowControl.wait(); + _logger.error("Message send failed due to timeout waiting on broker enforced flow control"); + throw new JMSException("Unable to send message for " + FLOW_CONTROL_WAIT_FAILURE/1000 + " seconds due to broker enforced flow control"); } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java index 0644bd88a8..1587d6a6bf 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java @@ -414,9 +414,6 @@ public class AMQSession_0_10 extends AMQSession extends Closeable implements Messa else { _session.addDeliveredMessage(msg.getDeliveryTag()); + _session.markDirty(); } break; diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java index 5ff6066ddc..44ce59975a 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java @@ -60,7 +60,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac /** * Priority of messages created by this producer. */ - private int _messagePriority; + private int _messagePriority = Message.DEFAULT_PRIORITY; /** * Time to live of messages. Specified in milliseconds but AMQ has 1 second resolution. diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/XAConnectionImpl.java b/qpid/java/client/src/main/java/org/apache/qpid/client/XAConnectionImpl.java index 20fa68605a..43025bd724 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/XAConnectionImpl.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/XAConnectionImpl.java @@ -47,7 +47,7 @@ public class XAConnectionImpl extends AMQConnection implements XAConnection, XAQ public synchronized XASession createXASession() throws JMSException { checkNotClosed(); - return _delegate.createXASession(_maxPrefetch, _maxPrefetch / 2); + return _delegate.createXASession(); } //-- Interface XAQueueConnection diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/configuration/ClientProperties.java b/qpid/java/client/src/main/java/org/apache/qpid/client/configuration/ClientProperties.java index 3627618e68..be0d283470 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/configuration/ClientProperties.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/configuration/ClientProperties.java @@ -39,7 +39,7 @@ public class ClientProperties * type: long */ public static final String MAX_PREFETCH_PROP_NAME = "max_prefetch"; - public static final String MAX_PREFETCH_DEFAULT = "5000"; + public static final String MAX_PREFETCH_DEFAULT = "500"; /** * When true a sync command is sent after every persistent messages. diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java index 8223cd5394..7761215450 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java @@ -21,6 +21,7 @@ package org.apache.qpid.client.failover; import org.apache.qpid.AMQDisconnectedException; +import org.apache.qpid.AMQException; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.client.state.AMQStateManager; @@ -134,6 +135,7 @@ public class FailoverHandler implements Runnable // a slightly more complex state model therefore I felt it was worthwhile doing this. AMQStateManager existingStateManager = _amqProtocolHandler.getStateManager(); + // Use a fresh new StateManager for the reconnection attempts _amqProtocolHandler.setStateManager(new AMQStateManager()); 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 9c791730ca..0e3333940a 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 @@ -39,6 +39,7 @@ public class ClientMethodDispatcherImpl implements MethodDispatcher private static final BasicReturnMethodHandler _basicReturnMethodHandler = BasicReturnMethodHandler.getInstance(); private static final ChannelCloseMethodHandler _channelCloseMethodHandler = ChannelCloseMethodHandler.getInstance(); private static final ChannelFlowOkMethodHandler _channelFlowOkMethodHandler = ChannelFlowOkMethodHandler.getInstance(); + private static final ChannelFlowMethodHandler _channelFlowMethodHandler = ChannelFlowMethodHandler.getInstance(); private static final ConnectionCloseMethodHandler _connectionCloseMethodHandler = ConnectionCloseMethodHandler.getInstance(); private static final ConnectionOpenOkMethodHandler _connectionOpenOkMethodHandler = ConnectionOpenOkMethodHandler.getInstance(); private static final ConnectionRedirectMethodHandler _connectionRedirectMethodHandler = ConnectionRedirectMethodHandler.getInstance(); @@ -159,7 +160,8 @@ public class ClientMethodDispatcherImpl implements MethodDispatcher public boolean dispatchChannelFlow(ChannelFlowBody body, int channelId) throws AMQException { - return false; + _channelFlowMethodHandler.methodReceived(_session, body, channelId); + return true; } public boolean dispatchChannelFlowOk(ChannelFlowOkBody body, int channelId) throws AMQException diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java index 06a1fe2696..e645f4f214 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java @@ -308,7 +308,6 @@ public class AMQProtocolHandler implements ProtocolEngine */ public void exception(Throwable cause) { - _logger.info("AS: HELLO"); if (_failoverState == FailoverState.NOT_STARTED) { // if (!(cause instanceof AMQUndeliveredException) && (!(cause instanceof AMQAuthenticationException))) diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/util/BlockingWaiter.java b/qpid/java/client/src/main/java/org/apache/qpid/client/util/BlockingWaiter.java index 67cda957fb..a3d015eadc 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/util/BlockingWaiter.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/util/BlockingWaiter.java @@ -253,7 +253,7 @@ public abstract class BlockingWaiter } else { - System.err.println("WARNING: new error arrived while old one not yet processed"); + System.err.println("WARNING: new error '" + e == null ? "null" : e.getMessage() + "' arrived while old one not yet processed:" + _error.getMessage()); } try diff --git a/qpid/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverExchangeMethod.java b/qpid/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverExchangeMethod.java index e05a7ab6e2..960661daea 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverExchangeMethod.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverExchangeMethod.java @@ -189,7 +189,8 @@ public class FailoverExchangeMethod implements FailoverMethod, MessageListener { synchronized (_brokerListLock) { - return _connectionDetails.getBrokerDetails(_currentBrokerIndex); + _currentBrokerDetail = _connectionDetails.getBrokerDetails(_currentBrokerIndex); + return _currentBrokerDetail; } } @@ -214,7 +215,15 @@ public class FailoverExchangeMethod implements FailoverMethod, MessageListener broker.getHost().equals(_currentBrokerDetail.getHost()) && broker.getPort() == _currentBrokerDetail.getPort()) { - return getNextBrokerDetails(); + if (_connectionDetails.getBrokerCount() > 1) + { + return getNextBrokerDetails(); + } + else + { + _failedAttemps ++; + return null; + } } String delayStr = broker.getProperty(BrokerDetails.OPTIONS_CONNECT_DELAY); diff --git a/qpid/java/common/bin/qpid-run b/qpid/java/common/bin/qpid-run index 0b5070d937..63bb648fd8 100755 --- a/qpid/java/common/bin/qpid-run +++ b/qpid/java/common/bin/qpid-run @@ -111,6 +111,7 @@ if [ -n "$QPID_LOG_SUFFIX" ]; then fi log $INFO System Properties set to $SYSTEM_PROPS +log $INFO QPID_OPTS set to $QPID_OPTS program=$(basename $0) sourced=${BASH_SOURCE[0]} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/ConsoleOutput.java b/qpid/java/common/src/main/java/org/apache/qpid/ConsoleOutput.java index 3c1ea22595..7d8a5b7b36 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/ConsoleOutput.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/ConsoleOutput.java @@ -51,7 +51,6 @@ public class ConsoleOutput implements Sender System.out.println("CLOSED"); } - @Override public void setIdleTimeout(long l) { // TODO Auto-generated method stub diff --git a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/LoggingManagement.java b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/LoggingManagement.java index 98dba9fed1..cb3387c6d3 100644 --- a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/LoggingManagement.java +++ b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/LoggingManagement.java @@ -110,7 +110,7 @@ public interface LoggingManagement * Reloads the log4j configuration file, applying any changes made. * * @throws IOException - * @since Qpid JMX API 1.3 + * @since Qpid JMX API 1.4 */ @MBeanOperation(name = "reloadConfigFile", description = "Reload the log4j xml configuration file", impact = MBeanOperationInfo.ACTION) void reloadConfigFile() throws IOException; diff --git a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ServerInformation.java b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ServerInformation.java index c158c26857..5819631dba 100644 --- a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ServerInformation.java +++ b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ServerInformation.java @@ -43,7 +43,7 @@ public interface ServerInformation * Qpid JMX API 1.1 can be assumed. */ int QPID_JMX_API_MAJOR_VERSION = 1; - int QPID_JMX_API_MINOR_VERSION = 3; + int QPID_JMX_API_MINOR_VERSION = 4; /** diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java index cd6f7ff808..9259d36d79 100644 --- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java +++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java @@ -48,7 +48,7 @@ public abstract class ApplicationRegistry //max supported broker management interface supported by this release of the management console public static final int SUPPORTED_QPID_JMX_API_MAJOR_VERSION = 1; - public static final int SUPPORTED_QPID_JMX_API_MINOR_VERSION = 3; + public static final int SUPPORTED_QPID_JMX_API_MINOR_VERSION = 4; public static final String DATA_DIR = System.getProperty("user.home") + File.separator + ".qpidmc"; diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/logging/ConfigurationFileTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/logging/ConfigurationFileTabControl.java index 1b1d08aa67..536033368f 100644 --- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/logging/ConfigurationFileTabControl.java +++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/logging/ConfigurationFileTabControl.java @@ -349,7 +349,7 @@ public class ConfigurationFileTabControl extends TabControl _logWatchIntervalLabel.setFont(ApplicationRegistry.getFont(FONT_BOLD)); _logWatchIntervalLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, true)); - if(_ApiVersion.greaterThanOrEqualTo(1, 3)) + if(_ApiVersion.greaterThanOrEqualTo(1, 4)) { Group reloadConfigFileGroup = new Group(attributesComposite, SWT.SHADOW_NONE); reloadConfigFileGroup.setBackground(attributesComposite.getBackground()); diff --git a/qpid/java/module.xml b/qpid/java/module.xml index 0c32414647..5796af928a 100644 --- a/qpid/java/module.xml +++ b/qpid/java/module.xml @@ -261,6 +261,7 @@ + @@ -269,6 +270,7 @@ + diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/BrokerStartupTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/BrokerStartupTest.java index 52120019f5..e7975f8d24 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/BrokerStartupTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/BrokerStartupTest.java @@ -78,15 +78,23 @@ public class BrokerStartupTest extends AbstractTestLogging // Add an invalid value _broker += " -l invalid"; - // The release-bin build of the broker uses this log4j configuration - // so set up the broker environment to use it for this test. - // Also include -Dlog4j.debug so we can validate that it picked up this config - setBrokerEnvironment("QPID_OPTS", "-Dlog4j.debug -Dlog4j.configuration=file:" + System.getProperty(QPID_HOME) + "/../broker/src/main/java/log4j.properties"); + // The broker has a built in default log4j configuration set up + // so if the the broker cannot load the -l value it will use default + // use this default. Test that this is correctly loaded, by + // including -Dlog4j.debug so we can validate. + setBrokerEnvironment("QPID_OPTS", "-Dlog4j.debug"); // Disable all client logging so we can test for broker DEBUG only. - Logger.getRootLogger().setLevel(Level.WARN); - Logger.getLogger("qpid.protocol").setLevel(Level.WARN); - Logger.getLogger("org.apache.qpid").setLevel(Level.WARN); + setLoggerLevel(Logger.getRootLogger(), Level.WARN); + setLoggerLevel(Logger.getLogger("qpid.protocol"), Level.WARN); + setLoggerLevel(Logger.getLogger("org.apache.qpid"), Level.WARN); + + // Set the broker to use info level logging, which is the qpid-server + // default. Rather than debug which is the test default. + setBrokerOnlySystemProperty("amqj.server.logging.level", "info"); + // Set the logging defaults to info for this test. + setBrokerOnlySystemProperty("amqj.logging.level", "info"); + setBrokerOnlySystemProperty("root.logging.level", "info"); startBroker(); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/failover/MessageDisappearWithIOExceptionTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/failover/MessageDisappearWithIOExceptionTest.java new file mode 100644 index 0000000000..f1a1c1a9a8 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/failover/MessageDisappearWithIOExceptionTest.java @@ -0,0 +1,330 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.failover; + +import org.apache.mina.common.WriteTimeoutException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.jms.ConnectionListener; +import org.apache.qpid.test.utils.QpidTestCase; +import org.apache.qpid.test.utils.FailoverBaseCase; +import org.apache.qpid.AMQConnectionClosedException; + +import javax.jms.Destination; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Test case based on user reported error. + * + * Summary: + * A user has reported message loss from their application. On bouncing of + * the broker the 'lost' messages are delivered to the broker. + * + * Note: + * The client was using Spring so that may influence the situation. + * + * Issue: + * The log files show 7 instances of the following which result in 7 + * missing messages. + * + * The client log files show: + * + * The broker log file show: + * + * + * 7 missing messages have delivery tags 5-11. Which says that they are + * sequentially the next message from the broker. + * + * The only way for the 'without a handler' log to occur is if the consumer + * has been removed from the look up table of the dispatcher. + * And the only way for the 'null message' log to occur on the broker is is + * if the message does not exist in the unacked-map + * + * The consumer is only removed from the list during session + * closure and failover. + * + * If the session was closed then the broker would requeue the unacked + * messages so the potential exists to have an empty map but the broker + * will not send a message out after the unacked map has been cleared. + * + * When failover occurs the _consumer map is cleared and the consumers are + * resubscribed. This is down without first stopping any existing + * dispatcher so there exists the potential to receive a message after + * the _consumer map has been cleared which is how the 'without a handler' + * log statement occurs. + * + * Scenario: + * + * Looking over logs the sequence that best fits the events is as follows: + * - Something causes Mina to be delayed causing the WriteTimoutException. + * - This exception is recevied by AMQProtocolHandler#exceptionCaught + * - As the WriteTimeoutException is an IOException this will cause + * sessionClosed to be called to start failover. + * + This is potentially the issues here. All IOExceptions are treated + * as connection failure events. + * - Failover Runs + * + Failover assumes that the previous connection has been closed. + * + Failover binds the existing objects (AMQConnection/Session) to the + * new connection objects. + * - Everything is reported as being successfully failed over. + * However, what is neglected is that the original connection has not + * been closed. + * + So what occurs is that the broker sends a message to the consumer on + * the original connection, as it was not notified of the client + * failing over. + * As the client failover reuses the original AMQSession and Dispatcher + * the new messages the broker sends to the old consumer arrives at the + * client and is processed by the same AMQSession and Dispatcher. + * However, as the failover process cleared the _consumer map and + * resubscribe the consumers the Dispatcher does not recognise the + * delivery tag and so logs the 'without a handler' message. + * - The Dispatcher then attempts to reject the message, however, + * + The AMQSession/Dispatcher pair have been swapped to using a new Mina + * ProtocolSession as part of the failover process so the reject is + * sent down the second connection. The broker receives the Reject + * request but as the Message was sent on a different connection the + * unacknowledgemap is empty and a 'message is null' log message + * produced. + * + * Test Strategy: + * + * It should be easy to demonstrate if we can send an IOException to + * AMQProtocolHandler#exceptionCaught and then try sending a message. + * + * The current unknowns here are the type of consumers that are in use. + * If it was an exclusive queue(Durable Subscription) then why did the + * resubscribe not fail. + * + * If it was not exclusive then why did the messages not round robin? + */ +public class MessageDisappearWithIOExceptionTest extends FailoverBaseCase implements ConnectionListener +{ + private CountDownLatch _failoverOccured = new CountDownLatch(1); + AMQConnection _connection; + Session _session; + Queue _queue; + MessageConsumer _consumer; + + public void setUp() throws Exception + { + super.setUp(); + stopBroker(getFailingPort()); + + } + + /** + * Test Summary: + * + * Create a queue consumer and send 10 messages to the broker. + * + * Consume the first message. + * This will pull the rest into the prefetch + * + * Send an IOException to the MinaProtocolHandler. + * + * This will force failover to occur. + * + * 9 messages would normally be expected but it is expected that none will + * arrive. As they are still in the prefetch of the first session. + * + * To free the messages we need to close all connections. + * - Simply doing connection.close() and retesting will not be enough as + * the original connection's IO layer will still exist and is nolonger + * connected to the connection object as a result of failover. + * + * - Test will need to retain a reference to the original connection IO so + * that it can be closed releasing the messages to validate that the + * messages have indeed been 'lost' on that sesssion. + */ + public void test() throws Exception + { + initialiseConnection(); + + // Create Producer + // Send 10 messages + List messages = sendNumberedBytesMessage(_session, _queue, 10); + + // Consume first messasge + Message received = _consumer.receive(2000); + + // Verify received messages + assertNotNull("First message not received.", received); + assertEquals("Incorrect message Received", + messages.remove(0).getIntProperty("count"), + received.getIntProperty("count")); + + // Allow ack to be sent to broker, by performing a synchronous command + // along the session. +// _session.createConsumer(_session.createTemporaryQueue()).close(); + + //Retain IO Layer + AMQProtocolSession protocolSession = _connection.getProtocolHandler().getProtocolSession(); + + // Send IO Exception - causing failover + _connection.getProtocolHandler(). + exception(new WriteTimeoutException("WriteTimeoutException to cause failover.")); + + // Verify Failover occured through ConnectionListener + assertTrue("Failover did not occur", + _failoverOccured.await(4000, TimeUnit.MILLISECONDS)); + + //Verify new protocolSession is not the same as the original + assertNotSame("Protocol Session has not changed", + protocolSession, + _connection.getProtocolHandler().getProtocolSession()); + + /***********************************/ + // This verifies that the bug has been resolved + + // Attempt to consume again. Expect 9 messages + for (int count = 1; count < 10; count++) + { + received = _consumer.receive(2000); + assertNotNull("Expected message not received:" + count, received); + assertEquals(messages.remove(0).getIntProperty("count"), + received.getIntProperty("count")); + } + + //Verify there are no more messages + received = _consumer.receive(1000); + assertNull("Message receieved when there should be none:" + received, + received); + +// /***********************************/ +// // This verifies that the bug exists +// +// // Attempt to consume remaining 9 messages.. Expecting NONE. +// // receiving just one message should fail so no need to fail 9 times +// received = _consumer.receive(1000); +// assertNull("Message receieved when it should be null:" + received, received); +// +//// //Close the Connection which you would assume would free the messages +//// _connection.close(); +//// +//// // Reconnect +//// initialiseConnection(); +//// +//// // We should still be unable to receive messages +//// received = _consumer.receive(1000); +//// assertNull("Message receieved when it should be null:" + received, received); +//// +//// _connection.close(); +// +// // Close original IO layer. Expecting messages to be released +// protocolSession.closeProtocolSession(); +// +// // Reconnect and all should be good. +//// initialiseConnection(); +// +// // Attempt to consume again. Expect 9 messages +// for (int count = 1; count < 10; count++) +// { +// received = _consumer.receive(2000); +// assertNotNull("Expected message not received:" + count, received); +// assertEquals(messages.remove(0).getIntProperty("count"), +// received.getIntProperty("count")); +// } +// +// //Verify there are no more messages +// received = _consumer.receive(1000); +// assertNull("Message receieved when there should be none:" + received, +// received); + } + + private void initialiseConnection() + throws Exception + { + //Create Connection + _connection = (AMQConnection) getConnection(); + _connection.setConnectionListener(this); + + _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + _queue = _session.createQueue(getTestQueueName()); + + // Create Consumer + _consumer = _session.createConsumer(_queue); + + //Start connection + _connection.start(); + } + + /** QpidTestCase back port to this release */ + + // modified from QTC as sendMessage is not testable. + // - should be renamed sendBlankBytesMessage + // - should be renamed sendNumberedBytesMessage + public List sendNumberedBytesMessage(Session session, Destination destination, + int count) throws Exception + { + List messages = new ArrayList(count); + + MessageProducer producer = session.createProducer(destination); + + for (int i = 0; i < count; i++) + { + Message next = session.createMessage(); + + next.setIntProperty("count", i); + + producer.send(next); + + messages.add(next); + } + + producer.close(); + return messages; + } + + public void bytesSent(long count) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void bytesReceived(long count) + { + } + + public boolean preFailover(boolean redirect) + { + //Allow failover to occur + return true; + } + + public boolean preResubscribe() + { + //Allow failover to occur + return true; + } + + public void failoverComplete() + { + _failoverOccured.countDown(); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BrokerLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BrokerLoggingTest.java index 4f50aba61d..b00a71315e 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BrokerLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BrokerLoggingTest.java @@ -160,7 +160,7 @@ public class BrokerLoggingTest extends AbstractTestLogging // Set the broker.ready string to check for the _log4j default that // is still present on standard out. - System.setProperty(BROKER_READY, "Qpid Broker Ready"); + setTestClientSystemProperty(BROKER_READY, "Qpid Broker Ready"); startBroker(); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/DeepQueueConsumeWithSelector.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/DeepQueueConsumeWithSelector.java index dfb5cde247..83f0f87bc5 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/DeepQueueConsumeWithSelector.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/DeepQueueConsumeWithSelector.java @@ -63,7 +63,6 @@ import java.util.concurrent.TimeUnit; */ public class DeepQueueConsumeWithSelector extends QpidTestCase implements MessageListener { - private static final String INDEX = "index"; private static final int MESSAGE_COUNT = 10000; private static final int BATCH_SIZE = MESSAGE_COUNT / 10; @@ -129,9 +128,7 @@ public class DeepQueueConsumeWithSelector extends QpidTestCase implements Messag @Override public Message createNextMessage(Session session, int msgCount) throws JMSException { - Message message = session.createTextMessage("Message :" + msgCount); - - message.setIntProperty(INDEX, msgCount); + Message message = super.createNextMessage(session,msgCount); if ((msgCount % BATCH_SIZE) == 0 ) { diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ProducerFlowControlTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ProducerFlowControlTest.java new file mode 100644 index 0000000000..f220760511 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ProducerFlowControlTest.java @@ -0,0 +1,393 @@ +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.log4j.Logger; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.test.utils.QpidTestCase; +import org.apache.qpid.AMQException; +import org.apache.qpid.server.logging.AbstractTestLogging; +import org.apache.qpid.framing.AMQShortString; + +import javax.jms.*; +import javax.naming.NamingException; +import java.util.HashMap; +import java.util.Map; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.io.IOException; + +public class ProducerFlowControlTest extends AbstractTestLogging +{ + private static final int TIMEOUT = 1500; + + + private static final Logger _logger = Logger.getLogger(ProducerFlowControlTest.class); + + protected final String QUEUE = "ProducerFlowControl"; + + private static final int MSG_COUNT = 50; + + private Connection producerConnection; + private MessageProducer producer; + private Session producerSession; + private Queue queue; + private Connection consumerConnection; + private Session consumerSession; + + + private MessageConsumer consumer; + private final AtomicInteger _sentMessages = new AtomicInteger(); + + public void setUp() throws Exception + { + super.setUp(); + + _monitor.reset(); + + producerConnection = getConnection(); + producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + producerConnection.start(); + + consumerConnection = getConnection(); + consumerSession = consumerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + } + + public void tearDown() throws Exception + { + producerConnection.close(); + consumerConnection.close(); + super.tearDown(); + } + + public void testCapacityExceededCausesBlock() + throws JMSException, NamingException, AMQException, InterruptedException + { + final Map arguments = new HashMap(); + arguments.put("x-qpid-capacity",1000); + arguments.put("x-qpid-flow-resume-capacity",800); + ((AMQSession) producerSession).createQueue(new AMQShortString(QUEUE), true, false, false, arguments); + queue = new AMQQueue("amq.direct",QUEUE); + ((AMQSession) producerSession).declareAndBind((AMQDestination)queue); + producer = producerSession.createProducer(queue); + + _sentMessages.set(0); + + + // try to send 5 messages (should block after 4) + sendMessagesAsync(producer, producerSession, 5, 50L); + + Thread.sleep(5000); + + assertEquals("Incorrect number of message sent before blocking", 4, _sentMessages.get()); + + consumer = consumerSession.createConsumer(queue); + consumerConnection.start(); + + + consumer.receive(); + + Thread.sleep(1000); + + assertEquals("Message incorrectly sent after one message received", 4, _sentMessages.get()); + + + consumer.receive(); + + Thread.sleep(1000); + + assertEquals("Message not sent after two messages received", 5, _sentMessages.get()); + + } + + public void testBrokerLogMessages() + throws JMSException, NamingException, AMQException, InterruptedException, IOException + { + final Map arguments = new HashMap(); + arguments.put("x-qpid-capacity",1000); + arguments.put("x-qpid-flow-resume-capacity",800); + ((AMQSession) producerSession).createQueue(new AMQShortString(QUEUE), true, false, false, arguments); + queue = new AMQQueue("amq.direct",QUEUE); + ((AMQSession) producerSession).declareAndBind((AMQDestination)queue); + producer = producerSession.createProducer(queue); + + _sentMessages.set(0); + + + // try to send 5 messages (should block after 4) + sendMessagesAsync(producer, producerSession, 5, 50L); + + Thread.sleep(5000); + List results = _monitor.findMatches("QUE-1003"); + + assertEquals("Did not find correct number of QUE-1003 queue overfull messages", 1, results.size()); + + consumer = consumerSession.createConsumer(queue); + consumerConnection.start(); + + + while(consumer.receive(1000) != null); + + results = _monitor.findMatches("QUE-1004"); + + assertEquals("Did not find correct number of QUE_1004 queue underfull messages", 1, results.size()); + + + + } + + + public void testClientLogMessages() + throws JMSException, NamingException, AMQException, InterruptedException, IOException + { + setTestClientSystemProperty("qpid.flow_control_wait_failure","3000"); + setTestClientSystemProperty("qpid.flow_control_wait_notify_period","1000"); + + Session session = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + + final Map arguments = new HashMap(); + arguments.put("x-qpid-capacity",1000); + arguments.put("x-qpid-flow-resume-capacity",800); + ((AMQSession) session).createQueue(new AMQShortString(QUEUE), true, false, false, arguments); + queue = new AMQQueue("amq.direct",QUEUE); + ((AMQSession) session).declareAndBind((AMQDestination)queue); + producer = session.createProducer(queue); + + _sentMessages.set(0); + + + // try to send 5 messages (should block after 4) + MessageSender sender = sendMessagesAsync(producer, producerSession, 5, 50L); + + Thread.sleep(10000); + List results = _monitor.findMatches("Message send delayed by"); + assertEquals("Incorrect number of delay messages logged by client",3,results.size()); + results = _monitor.findMatches("Message send failed due to timeout waiting on broker enforced flow control"); + assertEquals("Incorrect number of send failure messages logged by client",1,results.size()); + + + + } + + + public void testFlowControlOnCapacityResumeEqual() + throws JMSException, NamingException, AMQException, InterruptedException + { + final Map arguments = new HashMap(); + arguments.put("x-qpid-capacity",1000); + arguments.put("x-qpid-flow-resume-capacity",1000); + ((AMQSession) producerSession).createQueue(new AMQShortString(QUEUE), true, false, false, arguments); + queue = new AMQQueue("amq.direct",QUEUE); + ((AMQSession) producerSession).declareAndBind((AMQDestination)queue); + producer = producerSession.createProducer(queue); + + _sentMessages.set(0); + + // try to send 5 messages (should block after 4) + sendMessagesAsync(producer, producerSession, 5, 50L); + + Thread.sleep(5000); + + assertEquals("Incorrect number of message sent before blocking", 4, _sentMessages.get()); + + consumer = consumerSession.createConsumer(queue); + consumerConnection.start(); + + + consumer.receive(); + + Thread.sleep(1000); + + assertEquals("Message incorrectly sent after one message received", 5, _sentMessages.get()); + + + } + + + public void testFlowControlSoak() + throws Exception, NamingException, AMQException, InterruptedException + { + _sentMessages.set(0); + final int numProducers = 10; + final int numMessages = 100; + + final Map arguments = new HashMap(); + arguments.put("x-qpid-capacity",6000); + arguments.put("x-qpid-flow-resume-capacity",3000); + + ((AMQSession) consumerSession).createQueue(new AMQShortString(QUEUE), false, false, false, arguments); + + queue = new AMQQueue("amq.direct",QUEUE); + ((AMQSession) consumerSession).declareAndBind((AMQDestination)queue); + consumerConnection.start(); + + Connection[] producers = new Connection[numProducers]; + for(int i = 0 ; i < numProducers; i ++) + { + + producers[i] = getConnection(); + producers[i].start(); + Session session = producers[i].createSession(false, Session.AUTO_ACKNOWLEDGE); + + MessageProducer myproducer = session.createProducer(queue); + MessageSender sender = sendMessagesAsync(myproducer, session, numMessages, 50L); + } + + consumer = consumerSession.createConsumer(queue); + consumerConnection.start(); + + for(int j = 0; j < numProducers * numMessages; j++) + { + + Message msg = consumer.receive(5000); + Thread.sleep(50L); + assertNotNull("Message not received("+j+"), sent: "+_sentMessages.get(), msg); + + } + + + + Message msg = consumer.receive(500); + assertNull("extra message received", msg); + + + for(int i = 0; i < numProducers; i++) + { + producers[i].close(); + } + + } + + + + public void testSendTimeout() + throws JMSException, NamingException, AMQException, InterruptedException + { + setTestClientSystemProperty("qpid.flow_control_wait_failure","3000"); + Session session = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + + final Map arguments = new HashMap(); + arguments.put("x-qpid-capacity",1000); + arguments.put("x-qpid-flow-resume-capacity",800); + ((AMQSession) session).createQueue(new AMQShortString(QUEUE), true, false, false, arguments); + queue = new AMQQueue("amq.direct",QUEUE); + ((AMQSession) session).declareAndBind((AMQDestination)queue); + producer = session.createProducer(queue); + + _sentMessages.set(0); + + + // try to send 5 messages (should block after 4) + MessageSender sender = sendMessagesAsync(producer, producerSession, 5, 50L); + + Thread.sleep(10000); + + Exception e = sender.getException(); + + assertNotNull("No timeout exception on sending", e); + + } + + private MessageSender sendMessagesAsync(final MessageProducer producer, + final Session producerSession, + final int numMessages, + long sleepPeriod) + { + MessageSender sender = new MessageSender(producer, producerSession, numMessages,sleepPeriod); + new Thread(sender).start(); + return sender; + } + + private void sendMessages(MessageProducer producer, Session producerSession, int numMessages, long sleepPeriod) + throws JMSException + { + + for (int msg = 0; msg < numMessages; msg++) + { + producer.send(nextMessage(msg, producerSession)); + _sentMessages.incrementAndGet(); + + try + { + Thread.sleep(sleepPeriod); + } + catch (InterruptedException e) + { + } + } + } + + private static final byte[] BYTE_300 = new byte[300]; + + + private Message nextMessage(int msg, Session producerSession) throws JMSException + { + BytesMessage send = producerSession.createBytesMessage(); + send.writeBytes(BYTE_300); + send.setIntProperty("msg", msg); + + return send; + } + + + private class MessageSender implements Runnable + { + private final MessageProducer _producer; + private final Session _producerSession; + private final int _numMessages; + + + + private JMSException _exception; + private long _sleepPeriod; + + public MessageSender(MessageProducer producer, Session producerSession, int numMessages, long sleepPeriod) + { + _producer = producer; + _producerSession = producerSession; + _numMessages = numMessages; + _sleepPeriod = sleepPeriod; + } + + public void run() + { + try + { + sendMessages(_producer, _producerSession, _numMessages, _sleepPeriod); + } + catch (JMSException e) + { + _exception = e; + } + } + + public JMSException getException() + { + return _exception; + } + } +} \ No newline at end of file diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java index bb7b5efc75..3e5470d5cb 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java @@ -21,27 +21,34 @@ package org.apache.qpid.server.security.acl; - -import junit.framework.TestCase; - -import org.apache.log4j.BasicConfigurator; -import org.apache.log4j.Logger; -import org.apache.qpid.client.transport.TransportConnection; -import org.apache.qpid.client.*; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; -import org.apache.qpid.AMQConnectionFailureException; +import org.apache.commons.configuration.ConfigurationException; import org.apache.qpid.AMQException; -import org.apache.qpid.test.utils.QpidTestCase; +import org.apache.qpid.AMQConnectionFailureException; +import org.apache.qpid.client.AMQAuthenticationException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.jms.ConnectionListener; +import org.apache.qpid.test.utils.QpidTestCase; import org.apache.qpid.url.URLSyntaxException; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; + -import javax.jms.*; +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.ExceptionListener; import javax.jms.IllegalStateException; +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; import javax.naming.NamingException; - import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -53,6 +60,14 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E private CountDownLatch _awaitError = new CountDownLatch(51); public void setUp() throws Exception + { + //Performing setUp here would result in a broker with the default ACL test config + + //Each test now calls the private setUpACLTest to allow them to make + //individual customisations to the base ACL settings + } + + private void setUpACLTest() throws Exception { final String QPID_HOME = System.getProperty("QPID_HOME"); @@ -73,8 +88,10 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E return "amqp://" + username + ":" + password + "@clientid/test?brokerlist='" + getBroker() + "?retries='0''"; } - public void testAccessAuthorized() throws AMQException, URLSyntaxException + public void testAccessAuthorized() throws AMQException, URLSyntaxException, Exception { + setUpACLTest(); + try { Connection conn = getConnection("client", "guest"); @@ -96,6 +113,8 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E public void testAccessNoRights() throws Exception { + setUpACLTest(); + try { Connection conn = getConnection("guest", "guest"); @@ -120,8 +139,40 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E } } - public void testClientConsumeFromTempQueueValid() throws AMQException, URLSyntaxException + public void testGuestConsumeWithCreateRightsAndWithoutConsumeRights() throws NamingException, ConfigurationException, IOException, Exception + { + //Customise the ACL config to give the guest user some create (could be any, non-consume) rights to + //force creation of a PrincipalPermissions instance to perform the consume rights check against. + setConfigurationProperty("virtualhosts.virtualhost.test.security.access_control_list.create.queues.queue.users.user", "guest"); + + setUpACLTest(); + + try + { + Connection conn = getConnection("guest", "guest"); + + Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + + conn.start(); + + sesh.createConsumer(sesh.createQueue("example.RequestQueue")); + + conn.close(); + } + catch (JMSException e) + { + Throwable cause = e.getLinkedException(); + + assertNotNull("There was no liked exception", cause); + assertEquals("Wrong linked exception type", AMQAuthenticationException.class, cause.getClass()); + assertEquals("Incorrect error code received", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode()); + } + } + + public void testClientConsumeFromTempQueueValid() throws AMQException, URLSyntaxException, Exception { + setUpACLTest(); + try { Connection conn = getConnection("client", "guest"); @@ -142,6 +193,8 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E public void testClientConsumeFromNamedQueueInvalid() throws NamingException, Exception { + setUpACLTest(); + try { Connection conn = getConnection("client", "guest"); @@ -167,8 +220,10 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E } } - public void testClientCreateTemporaryQueue() throws JMSException, URLSyntaxException + public void testClientCreateTemporaryQueue() throws JMSException, URLSyntaxException, Exception { + setUpACLTest(); + try { Connection conn = getConnection("client", "guest"); @@ -191,6 +246,8 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E public void testClientCreateNamedQueue() throws NamingException, JMSException, AMQException, Exception { + setUpACLTest(); + try { Connection conn = getConnection("client", "guest"); @@ -212,8 +269,10 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E } } - public void testClientPublishUsingTransactionSuccess() throws AMQException, URLSyntaxException + public void testClientPublishUsingTransactionSuccess() throws AMQException, URLSyntaxException, Exception { + setUpACLTest(); + try { Connection conn = getConnection("client", "guest"); @@ -239,8 +298,10 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E } } - public void testClientPublishValidQueueSuccess() throws AMQException, URLSyntaxException + public void testClientPublishValidQueueSuccess() throws AMQException, URLSyntaxException, Exception { + setUpACLTest(); + try { Connection conn = getConnection("client", "guest"); @@ -271,6 +332,8 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E public void testClientPublishInvalidQueueSuccess() throws AMQException, URLSyntaxException, JMSException, NamingException, Exception { + setUpACLTest(); + try { Connection conn = getConnection("client", "guest"); @@ -311,8 +374,10 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E assertTrue("Did not get AMQAuthenticationException thrown", foundCorrectException); } - public void testServerConsumeFromNamedQueueValid() throws AMQException, URLSyntaxException + public void testServerConsumeFromNamedQueueValid() throws AMQException, URLSyntaxException, Exception { + setUpACLTest(); + try { Connection conn = getConnection("server", "guest"); @@ -333,6 +398,8 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E public void testServerConsumeFromNamedQueueInvalid() throws AMQException, URLSyntaxException, NamingException, Exception { + setUpACLTest(); + try { Connection conn = getConnection("client", "guest"); @@ -358,6 +425,8 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E public void testServerConsumeFromTemporaryQueue() throws AMQException, URLSyntaxException, NamingException, Exception { + setUpACLTest(); + try { Connection conn = getConnection("server", "guest"); @@ -391,8 +460,10 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E return (Connection) connection; } - public void testServerCreateNamedQueueValid() throws JMSException, URLSyntaxException + public void testServerCreateNamedQueueValid() throws JMSException, URLSyntaxException, Exception { + setUpACLTest(); + try { Connection conn = getConnection("server", "guest"); @@ -414,6 +485,8 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E public void testServerCreateNamedQueueInvalid() throws JMSException, URLSyntaxException, AMQException, NamingException, Exception { + setUpACLTest(); + try { Connection conn = getConnection("server", "guest"); @@ -436,6 +509,8 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E public void testServerCreateTemporaryQueueInvalid() throws NamingException, Exception { + setUpACLTest(); + try { Connection conn = getConnection("server", "guest"); @@ -461,6 +536,8 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E public void testServerCreateAutoDeleteQueueInvalid() throws NamingException, JMSException, AMQException, Exception { + setUpACLTest(); + Connection connection = null; try { @@ -492,6 +569,8 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E */ public void testServerPublishUsingTransactionSuccess() throws AMQException, URLSyntaxException, JMSException, NamingException, Exception { + setUpACLTest(); + //Set up the Server Connection serverConnection = getConnection("server", "guest"); @@ -572,6 +651,8 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E public void testServerPublishInvalidQueueSuccess() throws AMQException, URLSyntaxException, JMSException, NamingException, Exception { + setUpACLTest(); + try { Connection conn = getConnection("server", "guest"); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserAutoAckTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserAutoAckTest.java index 62b54d3086..f9cf48a2b1 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserAutoAckTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserAutoAckTest.java @@ -61,7 +61,7 @@ public class QueueBrowserAutoAckTest extends FailoverBaseCase setupSession(); - _queue = _clientSession.createQueue(getName()+System.currentTimeMillis()); + _queue = _clientSession.createQueue(getTestQueueName()); _clientSession.createConsumer(_queue).close(); //Ensure there are no messages on the queue to start with. @@ -497,7 +497,7 @@ public class QueueBrowserAutoAckTest extends FailoverBaseCase if (msgCount == failPoint) { - failBroker(); + failBroker(getFailingPort()); } } @@ -529,7 +529,7 @@ public class QueueBrowserAutoAckTest extends FailoverBaseCase sendMessages("connection2", messages); } - failBroker(); + failBroker(getFailingPort()); checkQueueDepth(messages); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/RollbackOrderTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/RollbackOrderTest.java index 39e2b892a9..ff766c907d 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/RollbackOrderTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/RollbackOrderTest.java @@ -22,64 +22,172 @@ package org.apache.qpid.test.client; import org.apache.qpid.test.utils.*; import javax.jms.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import junit.framework.ComparisonFailure; +import junit.framework.AssertionFailedError; /** - * RollbackOrderTest + * RollbackOrderTest, QPID-1864, QPID-1871 + * + * Description: + * + * The problem that this test is exposing is that the dispatcher used to be capable + * of holding on to a message when stopped. This ment that when the rollback was + * called and the dispatcher stopped it may have hold of a message. So after all + * the local queues(preDeliveryQueue, SynchronousQueue, PostDeliveryTagQueue) + * have been cleared the client still had a single message, the one the + * dispatcher was holding on to. + * + * As a result the TxRollback operation would run and then release the dispatcher. + * Whilst the dispatcher would then proceed to reject the message it was holiding + * the Broker would already have resent that message so the rejection would silently + * fail. + * + * And the client would receieve that single message 'early', depending on the + * number of messages already recevied when rollback was called. + * + * + * Aims: + * + * The tests puts 50 messages on to the queue. + * + * The test then tries to cause the dispatcher to stop whilst it is in the process + * of moving a message from the preDeliveryQueue to a consumers sychronousQueue. + * + * To exercise this path we have 50 message flowing to the client to give the + * dispatcher a bit of work to do moving messages. + * + * Then we loop - 10 times + * - Validating that the first message received is always message 1. + * - Receive a few more so that there are a few messages to reject. + * - call rollback, to try and catch the dispatcher mid process. + * + * Outcome: + * + * The hope is that we catch the dispatcher mid process and cause a BasicReject + * to fail. Which will be indicated in the log but will also cause that failed + * rejected message to be the next to be delivered which will not be message 1 + * as expected. + * + * We are testing a race condition here but we can check through the log file if + * the race condition occured. However, performing that check will only validate + * the problem exists and will not be suitable as part of a system test. * */ - public class RollbackOrderTest extends QpidTestCase { - private Connection conn; - private Queue queue; - private Session ssn; - private MessageProducer prod; - private MessageConsumer cons; + private Connection _connection; + private Queue _queue; + private Session _session; + private MessageConsumer _consumer; @Override public void setUp() throws Exception { super.setUp(); - conn = getConnection(); - conn.start(); - ssn = conn.createSession(true, Session.AUTO_ACKNOWLEDGE); - queue = ssn.createQueue("rollback-order-test-queue"); - prod = ssn.createProducer(queue); - cons = ssn.createConsumer(queue); - for (int i = 0; i < 5; i++) - { - TextMessage msg = ssn.createTextMessage("message " + (i+1)); - prod.send(msg); - } - ssn.commit(); + _connection = getConnection(); + + _session = _connection.createSession(true, Session.SESSION_TRANSACTED); + _queue = _session.createQueue(getTestQueueName()); + _consumer = _session.createConsumer(_queue); + + //Send more messages so it is more likely that the dispatcher is + // processing on rollback. + sendMessage(_session, _queue, 50); + _session.commit(); + } public void testOrderingAfterRollback() throws Exception { - for (int i = 0; i < 10; i++) + //Start the session now so we + _connection.start(); + + for (int i = 0; i < 20; i++) { - TextMessage msg = (TextMessage) cons.receive(); - assertEquals("message 1", msg.getText()); - ssn.rollback(); + Message msg = _consumer.receive(); + assertEquals("Incorrect Message Received", 0, msg.getIntProperty(INDEX)); + + // Pull additional messages through so we have some reject work to do + for (int m=0; m < 5 ; m++) + { + _consumer.receive(); + } + + System.err.println("ROT-Rollback"); + _logger.warn("ROT-Rollback"); + _session.rollback(); } } - @Override public void tearDown() throws Exception + public void testOrderingAfterRollbackOnMessage() throws Exception { - while (true) + final CountDownLatch count= new CountDownLatch(20); + final Exception exceptions[] = new Exception[20]; + final AtomicBoolean failed = new AtomicBoolean(false); + + _consumer.setMessageListener(new MessageListener() { - Message msg = cons.receiveNoWait(); - if (msg == null) + + public void onMessage(Message message) { - break; + + Message msg = message; + try + { + count.countDown(); + assertEquals("Incorrect Message Received", 0, msg.getIntProperty(INDEX)); + + _session.rollback(); + } + catch (JMSException e) + { + System.out.println("Error:" + e.getMessage()); + exceptions[(int)count.getCount()] = e; + } + catch (AssertionFailedError cf) + { + // End Test if Equality test fails + while (count.getCount() != 0) + { + count.countDown(); + } + + System.out.println("Error:" + cf.getMessage()); + System.err.println(cf.getMessage()); + cf.printStackTrace(); + failed.set(true); + } } - else + }); + //Start the session now so we + _connection.start(); + + count.await(); + + for (Exception e : exceptions) + { + if (e != null) { - msg.acknowledge(); + System.err.println(e.getMessage()); + e.printStackTrace(); + failed.set(true); } } - ssn.commit(); + +// _consumer.close(); + _connection.close(); + + assertFalse("Exceptions thrown during test run, Check Std.err.", failed.get()); + } + + @Override public void tearDown() throws Exception + { + + drainQueue(_queue); + super.tearDown(); } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/failover/FailoverTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/failover/FailoverTest.java index dfc3bb7b42..c307176f3f 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/failover/FailoverTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/failover/FailoverTest.java @@ -37,7 +37,6 @@ import javax.naming.NamingException; import org.apache.log4j.Logger; import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.AMQSession_0_10; import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.jms.ConnectionListener; import org.apache.qpid.jms.ConnectionURL; @@ -58,13 +57,12 @@ public class FailoverTest extends FailoverBaseCase implements ConnectionListener private Session consumerSession; private MessageConsumer consumer; - private static int usedBrokers = 0; private CountDownLatch failoverComplete; - private static final long DEFAULT_FAILOVER_TIME = 10000L; private boolean CLUSTERED = Boolean.getBoolean("profile.clustered"); private int seed; private Random rand; - + private int _currentPort = getFailingPort(); + @Override protected void setUp() throws Exception { @@ -227,7 +225,7 @@ public class FailoverTest extends FailoverBaseCase implements ConnectionListener _logger.info("Failing over"); - causeFailure(DEFAULT_FAILOVER_TIME); + causeFailure(_currentPort, DEFAULT_FAILOVER_TIME); // Check that you produce and consume the rest of messages. _logger.debug("=================="); @@ -242,10 +240,10 @@ public class FailoverTest extends FailoverBaseCase implements ConnectionListener _logger.debug("=================="); } - private void causeFailure(long delay) + private void causeFailure(int port, long delay) { - failBroker(); + failBroker(port); _logger.info("Awaiting Failover completion"); try @@ -268,7 +266,7 @@ public class FailoverTest extends FailoverBaseCase implements ConnectionListener Message msg = consumer.receive(); assertNotNull("Expected msgs not received", msg); - causeFailure(DEFAULT_FAILOVER_TIME); + causeFailure(getFailingPort(), DEFAULT_FAILOVER_TIME); Exception failure = null; try @@ -314,7 +312,7 @@ public class FailoverTest extends FailoverBaseCase implements ConnectionListener long failTime = System.nanoTime() + FAILOVER_DELAY * 1000000; //Fail the first broker - causeFailure(FAILOVER_DELAY + DEFAULT_FAILOVER_TIME); + causeFailure(getFailingPort(), FAILOVER_DELAY + DEFAULT_FAILOVER_TIME); //Reconnection should occur assertTrue("Failover did not take long enough", System.nanoTime() > failTime); @@ -344,15 +342,15 @@ public class FailoverTest extends FailoverBaseCase implements ConnectionListener _logger.debug("==================================================================="); runP2PFailover(numMessages, false,false, false); - startBroker(getFailingPort()); + startBroker(_currentPort); if (useAltPort) { - setFailingPort(altPort); + _currentPort = altPort; useAltPort = false; } else { - setFailingPort(stdPort); + _currentPort = stdPort; useAltPort = true; } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/message/SelectorTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/message/SelectorTest.java index 5a5e23baa5..a09589b121 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/message/SelectorTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/message/SelectorTest.java @@ -1,31 +1,248 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.client.message; -import javax.jms.Connection; -import javax.jms.Destination; +import java.util.concurrent.CountDownLatch; + +import javax.jms.DeliveryMode; +import javax.jms.InvalidSelectorException; +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 junit.framework.Assert; -import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.BasicMessageProducer; import org.apache.qpid.test.utils.QpidTestCase; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class SelectorTest extends QpidTestCase +public class SelectorTest extends QpidTestCase implements MessageListener { - private static final Logger _logger = Logger.getLogger(SelectorTest.class); + private static final Logger _logger = LoggerFactory.getLogger(SelectorTest.class); - public void testSelectorWithJMSMessageID() throws Exception + private AMQConnection _connection; + private AMQDestination _destination; + private int count; + public String _connectionString = "vm://:1"; + private static final String INVALID_SELECTOR = "Cost LIKE 5"; + CountDownLatch _responseLatch = new CountDownLatch(1); + + private static final String BAD_MATHS_SELECTOR = " 1 % 5"; + + private static final long RECIEVE_TIMEOUT = 1000; + + protected void setUp() throws Exception + { + super.setUp(); + init((AMQConnection) getConnection("guest", "guest")); + } + + private void init(AMQConnection connection) throws JMSException + { + init(connection, new AMQQueue(connection, getTestQueueName(), true)); + } + + private void init(AMQConnection connection, AMQDestination destination) throws JMSException + { + _connection = connection; + _destination = destination; + connection.start(); + } + + public void onMessage(Message message) + { + count++; + _logger.info("Got Message:" + message); + _responseLatch.countDown(); + } + + public void testUsingOnMessage() throws Exception + { + String selector = "Cost = 2 AND \"property-with-hyphen\" = 'wibble'"; + // selector = "JMSType = Special AND Cost = 2 AND AMQMessageID > 0 AND JMSDeliveryMode=" + DeliveryMode.NON_PERSISTENT; + + Session session = (AMQSession) _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); + // _session.createConsumer(destination).setMessageListener(this); + session.createConsumer(_destination, selector).setMessageListener(this); + + try + { + Message msg = session.createTextMessage("Message"); + msg.setJMSPriority(1); + msg.setIntProperty("Cost", 2); + msg.setStringProperty("property-with-hyphen", "wibble"); + msg.setJMSType("Special"); + + _logger.info("Sending Message:" + msg); + + ((BasicMessageProducer) session.createProducer(_destination)).send(msg, DeliveryMode.NON_PERSISTENT); + _logger.info("Message sent, waiting for response..."); + + _responseLatch.await(); + + if (count > 0) + { + _logger.info("Got message"); + } + + if (count == 0) + { + fail("Did not get message!"); + // throw new RuntimeException("Did not get message!"); + } + } + catch (JMSException e) + { + _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); + if (!(e instanceof InvalidSelectorException)) + { + fail("Wrong exception:" + e.getMessage()); + } + else + { + System.out.println("SUCCESS!!"); + } + } + catch (InterruptedException e) + { + _logger.debug("IE :" + e.getClass().getSimpleName() + ":" + e.getMessage()); + } + + } + + public void testUnparsableSelectors() throws Exception { - Connection conn = getConnection(); - conn.start(); - Session session = conn.createSession(true, Session.AUTO_ACKNOWLEDGE); + AMQSession session = (AMQSession) _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); + boolean caught = false; - Destination dest = session.createQueue("SelectorQueue"); + //Try Creating a Browser + try + { + session.createBrowser(session.createQueue("Ping"), INVALID_SELECTOR); + } + catch (JMSException e) + { + _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); + if (!(e instanceof InvalidSelectorException)) + { + fail("Wrong exception:" + e.getMessage()); + } + caught = true; + } + assertTrue("No exception thrown!", caught); + caught = false; + + //Try Creating a Consumer + try + { + session.createConsumer(session.createQueue("Ping"), INVALID_SELECTOR); + } + catch (JMSException e) + { + _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); + if (!(e instanceof InvalidSelectorException)) + { + fail("Wrong exception:" + e.getMessage()); + } + caught = true; + } + assertTrue("No exception thrown!", caught); + caught = false; + + //Try Creating a Receiever + try + { + session.createReceiver(session.createQueue("Ping"), INVALID_SELECTOR); + } + catch (JMSException e) + { + _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); + if (!(e instanceof InvalidSelectorException)) + { + fail("Wrong exception:" + e.getMessage()); + } + caught = true; + } + assertTrue("No exception thrown!", caught); + caught = false; + + try + { + session.createReceiver(session.createQueue("Ping"), BAD_MATHS_SELECTOR); + } + catch (JMSException e) + { + _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); + if (!(e instanceof InvalidSelectorException)) + { + fail("Wrong exception:" + e.getMessage()); + } + caught = true; + } + assertTrue("No exception thrown!", caught); + caught = false; - MessageProducer prod = session.createProducer(dest); - MessageConsumer consumer = session.createConsumer(dest,"JMSMessageID IS NOT NULL"); + } + + public void testRuntimeSelectorError() throws JMSException + { + Session session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageConsumer consumer = session.createConsumer(_destination , "testproperty % 5 = 1"); + MessageProducer producer = session.createProducer(_destination); + Message sentMsg = session.createTextMessage(); + + sentMsg.setIntProperty("testproperty", 1); // 1 % 5 + producer.send(sentMsg); + Message recvd = consumer.receive(RECIEVE_TIMEOUT); + assertNotNull(recvd); + + sentMsg.setStringProperty("testproperty", "hello"); // "hello" % 5 makes no sense + producer.send(sentMsg); + try + { + recvd = consumer.receive(RECIEVE_TIMEOUT); + assertNull(recvd); + } + catch (Exception e) + { + + } + assertTrue("Connection should be closed", _connection.isClosed()); + } + + public void testSelectorWithJMSMessageID() throws Exception + { + Session session = _connection.createSession(true, Session.SESSION_TRANSACTED); + + MessageProducer prod = session.createProducer(_destination); + MessageConsumer consumer = session.createConsumer(_destination,"JMSMessageID IS NOT NULL"); for (int i=0; i<2; i++) { @@ -54,7 +271,7 @@ public class SelectorTest extends QpidTestCase Message msg3 = consumer.receive(1000); Assert.assertNull("Msg3 should be null", msg3); session.commit(); - consumer = session.createConsumer(dest,"JMSMessageID IS NULL"); + consumer = session.createConsumer(_destination,"JMSMessageID IS NULL"); Message msg4 = consumer.receive(1000); Message msg5 = consumer.receive(1000); @@ -62,4 +279,32 @@ public class SelectorTest extends QpidTestCase Assert.assertNotNull("Msg4 should not be null", msg4); Assert.assertNotNull("Msg5 should not be null", msg5); } + + public static void main(String[] argv) throws Exception + { + SelectorTest test = new SelectorTest(); + test._connectionString = (argv.length == 0) ? "localhost:3000" : argv[0]; + + try + { + while (true) + { + if (test._connectionString.contains("vm://:1")) + { + test.setUp(); + } + test.testUsingOnMessage(); + + if (test._connectionString.contains("vm://:1")) + { + test.tearDown(); + } + } + } + catch (Exception e) + { + System.err.println(e.getMessage()); + e.printStackTrace(); + } + } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/Acknowledge2ConsumersTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/Acknowledge2ConsumersTest.java new file mode 100644 index 0000000000..4b45a96c20 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/Acknowledge2ConsumersTest.java @@ -0,0 +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.test.unit.ack; + +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.test.utils.FailoverBaseCase; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.Queue; +import javax.jms.Session; + +public class Acknowledge2ConsumersTest extends FailoverBaseCase +{ + protected static int NUM_MESSAGES = 100; + protected Connection _con; + protected Queue _queue; + private Session _producerSession; + private Session _consumerSession; + private MessageConsumer _consumerA; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + _queue = (Queue) getInitialContext().lookup("queue"); + + //Create Producer put some messages on the queue + _con = getConnection(); + } + + private void init(boolean transacted, int mode) throws JMSException + { + _producerSession = _con.createSession(true, Session.SESSION_TRANSACTED); + _consumerSession = _con.createSession(transacted, mode); + _consumerA = _consumerSession.createConsumer(_queue); + _con.start(); + } + + /** + * Produces Messages that + * + * @param transacted + * @param mode + * + * @throws Exception + */ + private void test2ConsumersAcking(boolean transacted, int mode) throws Exception + { + init(transacted, mode); + + // These should all end up being prefetched by sessionA + sendMessage(_producerSession, _queue, NUM_MESSAGES / 2); + + //Create a second consumer (consumerB) to consume some of the messages + MessageConsumer consumerB = _consumerSession.createConsumer(_queue); + + // These messages should be roundrobined between A and B + sendMessage(_producerSession, _queue, NUM_MESSAGES / 2); + + int count = 0; + //Use consumerB to receive messages it has + Message msg = consumerB.receive(1500); + while (msg != null) + { + if (mode == Session.CLIENT_ACKNOWLEDGE) + { + msg.acknowledge(); + } + count++; + msg = consumerB.receive(1500); + } + if (transacted) + { + _consumerSession.commit(); + } + + // Close the consumers + _consumerA.close(); + consumerB.close(); + + // and close the session to release any prefetched messages. + _consumerSession.close(); + assertEquals("Wrong number of messages on queue", NUM_MESSAGES - count, + ((AMQSession) _producerSession).getQueueDepth((AMQDestination) _queue)); + + // Clean up messages that may be left on the queue + _consumerSession = _con.createSession(transacted, mode); + _consumerA = _consumerSession.createConsumer(_queue); + msg = _consumerA.receive(1500); + while (msg != null) + { + if (mode == Session.CLIENT_ACKNOWLEDGE) + { + msg.acknowledge(); + } + msg = _consumerA.receive(1500); + } + _consumerA.close(); + if (transacted) + { + _consumerSession.commit(); + } + _consumerSession.close(); + } + + public void test2ConsumersAutoAck() throws Exception + { + test2ConsumersAcking(false, Session.AUTO_ACKNOWLEDGE); + } + + public void test2ConsumersClientAck() throws Exception + { + test2ConsumersAcking(false, Session.CLIENT_ACKNOWLEDGE); + } + + public void test2ConsumersTx() throws Exception + { + test2ConsumersAcking(true, Session.SESSION_TRANSACTED); + } + + + +// +// /** +// * Check that session level acknowledge does correctly ack all previous +// * values. Send 3 messages(0,1,2) then ack 1 and 2. If session ack is +// * working correctly then acking 1 will also ack 0. Acking 2 will not +// * attempt to re-ack 0 and 1. +// * +// * @throws Exception +// */ +// public void testSessionAck() throws Exception +// { +// init(false, Session.CLIENT_ACKNOWLEDGE); +// +// sendMessage(_producerSession, _queue, 3); +// Message msg; +// +// // Drop msg 0 +// _consumerA.receive(RECEIVE_TIMEOUT); +// +// // Take msg 1 +// msg = _consumerA.receive(RECEIVE_TIMEOUT); +// +// assertNotNull("Message 1 not correctly received.", msg); +// assertEquals("Incorrect message received", 1, msg.getIntProperty(INDEX)); +// +// // This should also ack msg 0 +// msg.acknowledge(); +// +// // Take msg 2 +// msg = _consumerA.receive(RECEIVE_TIMEOUT); +// +// assertNotNull("Message 2 not correctly received.", msg); +// assertEquals("Incorrect message received", 2, msg.getIntProperty(INDEX)); +// +// // This should just ack msg 2 +// msg.acknowledge(); +// +// _consumerA.close(); +// _consumerSession.close(); +// +// assertEquals("Queue not empty.", 0, +// ((AMQSession) _producerSession).getQueueDepth((AMQDestination) _queue)); +// _con.close(); +// +// +// } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverOnMessageTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverOnMessageTest.java new file mode 100644 index 0000000000..f22a405fc3 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverOnMessageTest.java @@ -0,0 +1,356 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.ack; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.jms.ConnectionListener; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.Session; +import javax.jms.TransactionRolledBackException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class AcknowledgeAfterFailoverOnMessageTest extends AcknowledgeOnMessageTest implements ConnectionListener +{ + + protected CountDownLatch _failoverCompleted = new CountDownLatch(1); + private MessageListener _listener = null; + + @Override + public void setUp() throws Exception + { + super.setUp(); + NUM_MESSAGES = 10; + } + + /** + * Override default init to add connectionListener so we can verify that + * failover took place + * + * @param transacted create a transacted session for this test + * @param mode if not transacted what ack mode to use for this test + * + * @throws Exception if a problem occured during test setup. + */ + @Override + public void init(boolean transacted, int mode) throws Exception + { + super.init(transacted, mode); + ((AMQConnection) _connection).setConnectionListener(this); + // Override the listener for the dirtyAck testing. + if (_listener != null) + { + _consumer.setMessageListener(_listener); + } + } + + protected void prepBroker(int count) throws Exception + { + //Stop the connection whilst we repopulate the broker, or the no_ack + // test will drain the msgs before we can check we put the right number + // back on again. +// _connection.stop(); + + Connection connection = getConnection(); + Session session = connection.createSession(true, Session.SESSION_TRANSACTED); + // ensure destination is created. + session.createConsumer(_queue).close(); + + sendMessage(session, _queue, count, NUM_MESSAGES - count, 0); + + if (_consumerSession.getAcknowledgeMode() != AMQSession.NO_ACKNOWLEDGE) + { + assertEquals("Wrong number of messages on queue", count, + ((AMQSession) session).getQueueDepth((AMQDestination) _queue)); + } + + connection.close(); + +// _connection.start(); + } + + @Override + public void doAcknowlegement(Message msg) throws JMSException + { + //Acknowledge current message + super.doAcknowlegement(msg); + + int msgCount = msg.getIntProperty(INDEX); + + if (msgCount % 2 == 0) + { + failBroker(getFailingPort()); + } + else + { + failBroker(getPort()); + } + + try + { + prepBroker(NUM_MESSAGES - msgCount - 1); + } + catch (Exception e) + { + fail("Unable to prep new broker," + e.getMessage()); + } + + try + { + + if (msgCount % 2 == 0) + { + startBroker(getFailingPort()); + } + else + { + startBroker(getPort()); + } + } + catch (Exception e) + { + fail("Unable to start failover broker," + e.getMessage()); + } + + } + + int msgCount = 0; + boolean cleaned = false; + + class DirtyAckingHandler implements MessageListener + { + /** + * Validate first message but do nothing with it. + * + * Failover + * + * The receive the message again + * + * @param message + */ + public void onMessage(Message message) + { + // Stop processing if we have an error and had to stop running. + if (_receviedAll.getCount() == 0) + { + _logger.debug("Dumping msgs due to error(" + _causeOfFailure.get().getMessage() + "):" + message); + return; + } + + try + { + // Check we have the next message as expected + assertNotNull("Message " + msgCount + " not correctly received.", message); + assertEquals("Incorrect message received", msgCount, message.getIntProperty(INDEX)); + + if (msgCount == 0 && _failoverCompleted.getCount() != 0) + { + // This is the first message we've received so lets fail the broker + + failBroker(getFailingPort()); + + repopulateBroker(); + + _logger.error("Received first msg so failing over"); + + return; + } + + msgCount++; + + // Don't acknowlege the first message after failover so we can commit + // them together + if (msgCount == 1) + { + _logger.error("Received first msg after failover ignoring:" + msgCount); + + // Acknowledge the first message if we are now on the cleaned pass + if (cleaned) + { + _receviedAll.countDown(); + } + + return; + } + + if (_consumerSession.getTransacted()) + { + try + { + _consumerSession.commit(); + if (!cleaned) + { + fail("Session is dirty we should get an TransactionRolledBackException"); + } + } + catch (TransactionRolledBackException trbe) + { + //expected path + } + } + else + { + try + { + message.acknowledge(); + if (!cleaned) + { + fail("Session is dirty we should get an IllegalStateException"); + } + } + catch (javax.jms.IllegalStateException ise) + { + assertEquals("Incorrect Exception thrown", "has failed over", ise.getMessage()); + // Recover the sesion and try again. + _consumerSession.recover(); + } + } + + // Acknowledge the last message if we are in a clean state + // this will then trigger test teardown. + if (cleaned) + { + _receviedAll.countDown(); + } + + //Reset message count so we can try again. + msgCount = 0; + cleaned = true; + } + catch (Exception e) + { + // If something goes wrong stop and notifiy main thread. + fail(e); + } + } + } + + /** + * Test that Acking/Committing a message received before failover causes + * an exception at commit/ack time. + * + * Expected behaviour is that in: + * * tx mode commit() throws a transacted RolledBackException + * * client ack mode throws an IllegalStateException + * + * @param transacted is this session trasacted + * @param mode What ack mode should be used if not trasacted + * + * @throws Exception if something goes wrong. + */ + protected void testDirtyAcking(boolean transacted, int mode) throws Exception + { + NUM_MESSAGES = 2; + _listener = new DirtyAckingHandler(); + + super.testAcking(transacted, mode); + } + + public void testDirtyClientAck() throws Exception + { + testDirtyAcking(false, Session.CLIENT_ACKNOWLEDGE); + } + + public void testDirtyAckingTransacted() throws Exception + { + testDirtyAcking(true, Session.SESSION_TRANSACTED); + } + + private void repopulateBroker() throws Exception + { + // Repopulate this new broker so we can test what happends after failover + + //Get the connection to the first (main port) broker. + Connection connection = getConnection(); + // Use a transaction to send messages so we can be sure they arrive. + Session session = connection.createSession(true, Session.SESSION_TRANSACTED); + // ensure destination is created. + session.createConsumer(_queue).close(); + + sendMessage(session, _queue, NUM_MESSAGES); + + assertEquals("Wrong number of messages on queue", NUM_MESSAGES, + ((AMQSession) session).getQueueDepth((AMQDestination) _queue)); + + connection.close(); + } + + // AMQConnectionListener Interface.. used so we can validate that we + // actually failed over. + + public void bytesSent(long count) + { + } + + public void bytesReceived(long count) + { + } + + public boolean preFailover(boolean redirect) + { + //Allow failover + return true; + } + + public boolean preResubscribe() + { + //Allow failover + return true; + } + + public void failoverComplete() + { + _failoverCompleted.countDown(); + } + + /** + * Override so we can block until failover has completd + * + * @param port + */ + @Override + public void failBroker(int port) + { + super.failBroker(port); + + try + { + if (!_failoverCompleted.await(DEFAULT_FAILOVER_TIME, TimeUnit.MILLISECONDS)) + { + // Use an exception so that we use our local fail() that notifies the main thread of failure + throw new Exception("Failover did not occur in specified time:" + DEFAULT_FAILOVER_TIME); + } + + } + catch (Exception e) + { + // Use an exception so that we use our local fail() that notifies the main thread of failure + fail(e); + } + } + +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverTest.java new file mode 100644 index 0000000000..eb36522fac --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverTest.java @@ -0,0 +1,306 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.ack; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.jms.ConnectionListener; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TransactionRolledBackException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * + */ +public class AcknowledgeAfterFailoverTest extends AcknowledgeTest implements ConnectionListener +{ + + protected CountDownLatch _failoverCompleted = new CountDownLatch(1); + + @Override + public void setUp() throws Exception + { + super.setUp(); + // This must be even for the test to run correctly. + // Otherwise we will kill the standby broker + // not the one we are connected to. + // The test will still pass but it will not be exactly + // as described. + NUM_MESSAGES = 6; + } + + /** + * Override default init to add connectionListener so we can verify that + * failover took place + * + * @param transacted create a transacted session for this test + * @param mode if not transacted what ack mode to use for this test + * @throws Exception if a problem occured during test setup. + */ + @Override + protected void init(boolean transacted, int mode) throws Exception + { + super.init(transacted, mode); + ((AMQConnection) _connection).setConnectionListener(this); + } + + protected void prepBroker(int count) throws Exception + { + if (count % 2 == 1) + { + failBroker(getFailingPort()); + } + else + { + failBroker(getPort()); + } + + Connection connection = getConnection(); + Session session = connection.createSession(true, Session.SESSION_TRANSACTED); + // ensure destination is created. + session.createConsumer(_queue).close(); + + sendMessage(session, _queue, count, NUM_MESSAGES - count, 0); + + if (_consumerSession.getAcknowledgeMode() != AMQSession.NO_ACKNOWLEDGE) + { + assertEquals("Wrong number of messages on queue", count, + ((AMQSession) session).getQueueDepth((AMQDestination) _queue)); + } + + connection.close(); + + try + { + if (count % 2 == 1) + { + startBroker(getFailingPort()); + } + else + { + startBroker(getPort()); + } + } + catch (Exception e) + { + fail("Unable to start failover broker," + e.getMessage()); + } + } + + @Override + public void doAcknowlegement(Message msg) throws JMSException + { + //Acknowledge current message + super.doAcknowlegement(msg); + + try + { + prepBroker(NUM_MESSAGES - msg.getIntProperty(INDEX) - 1); + } + catch (Exception e) + { + fail("Unable to prep new broker," + e.getMessage()); + } + + } + + /** + * Test that Acking/Committing a message received before failover causes + * an exception at commit/ack time. + * + * Expected behaviour is that in: + * * tx mode commit() throws a transacted RolledBackException + * * client ack mode throws an IllegalStateException + * + * @param transacted is this session trasacted + * @param mode What ack mode should be used if not trasacted + * + * @throws Exception if something goes wrong. + */ + protected void testDirtyAcking(boolean transacted, int mode) throws Exception + { + NUM_MESSAGES = 2; + //Test Dirty Failover Fails + init(transacted, mode); + + _connection.start(); + + Message msg = _consumer.receive(1500); + + int count = 0; + assertNotNull("Message " + count + " not correctly received.", msg); + assertEquals("Incorrect message received", count, msg.getIntProperty(INDEX)); + + //Don't acknowledge just prep the next broker. Without changing count + // Prep the new broker to have all all the messages so we can validate + // that they can all be correctly received. + try + { + + //Stop the connection so we can validate the number of message count + // on the queue is correct after failover + _connection.stop(); + failBroker(getFailingPort()); + + //Get the connection to the first (main port) broker. + Connection connection = getConnection();//getConnectionFactory("connection1").getConnectionURL()); + // Use a transaction to send messages so we can be sure they arrive. + Session session = connection.createSession(true, Session.SESSION_TRANSACTED); + // ensure destination is created. + session.createConsumer(_queue).close(); + + sendMessage(session, _queue, NUM_MESSAGES); + + assertEquals("Wrong number of messages on queue", NUM_MESSAGES, + ((AMQSession) session).getQueueDepth((AMQDestination) _queue)); + + connection.close(); + + //restart connection + _connection.start(); + } + catch (Exception e) + { + fail("Unable to prep new broker," + e.getMessage()); + } + + // Consume the next message - don't check what it is as a normal would + // assume it is msg 1 but as we've fallen over it is msg 0 again. + msg = _consumer.receive(1500); + + if (_consumerSession.getTransacted()) + { + try + { + _consumerSession.commit(); + fail("Session is dirty we should get an TransactionRolledBackException"); + } + catch (TransactionRolledBackException trbe) + { + //expected path + } + } + else + { + try + { + msg.acknowledge(); + fail("Session is dirty we should get an IllegalStateException"); + } + catch (javax.jms.IllegalStateException ise) + { + assertEquals("Incorrect Exception thrown", "has failed over", ise.getMessage()); + // Recover the sesion and try again. + _consumerSession.recover(); + } + } + + msg = _consumer.receive(1500); + // Validate we now get the first message back + assertEquals(0, msg.getIntProperty(INDEX)); + + msg = _consumer.receive(1500); + // and the second message + assertEquals(1, msg.getIntProperty(INDEX)); + + // And now verify that we can now commit the clean session + if (_consumerSession.getTransacted()) + { + _consumerSession.commit(); + } + else + { + msg.acknowledge(); + } + + assertEquals("Wrong number of messages on queue", 0, + ((AMQSession) _consumerSession).getQueueDepth((AMQDestination) _queue)); + } + + public void testDirtyClientAck() throws Exception + { + testDirtyAcking(false, Session.CLIENT_ACKNOWLEDGE); + } + + public void testDirtyAckingTransacted() throws Exception + { + testDirtyAcking(true, Session.SESSION_TRANSACTED); + } + + // AMQConnectionListener Interface.. used so we can validate that we + // actually failed over. + + public void bytesSent(long count) + { + } + + public void bytesReceived(long count) + { + } + + public boolean preFailover(boolean redirect) + { + //Allow failover + return true; + } + + public boolean preResubscribe() + { + //Allow failover + return true; + } + + public void failoverComplete() + { + _failoverCompleted.countDown(); + } + + /** + * Override so we can block until failover has completd + * + * @param port + */ + @Override + public void failBroker(int port) + { + super.failBroker(port); + + try + { + if (!_failoverCompleted.await(DEFAULT_FAILOVER_TIME, TimeUnit.MILLISECONDS)) + { + fail("Failover did not occur in specified time:" + DEFAULT_FAILOVER_TIME); + } + } + catch (InterruptedException e) + { + fail("Failover was interuppted"); + } + } + +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeOnMessageTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeOnMessageTest.java new file mode 100644 index 0000000000..4254727d36 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeOnMessageTest.java @@ -0,0 +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.test.unit.ack; + +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.JMSAMQException; +import org.apache.qpid.client.failover.FailoverException; + +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.Session; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +public class AcknowledgeOnMessageTest extends AcknowledgeTest implements MessageListener +{ + protected CountDownLatch _receviedAll; + protected AtomicReference _causeOfFailure = new AtomicReference(null); + + @Override + public void setUp() throws Exception + { + super.setUp(); + } + + @Override + public void init(boolean transacted, int mode) throws Exception + { + _receviedAll = new CountDownLatch(NUM_MESSAGES); + + super.init(transacted, mode); + _consumer.setMessageListener(this); + } + + /** + * @param transacted + * @param mode + * + * @throws Exception + */ + protected void testAcking(boolean transacted, int mode) throws Exception + { + init(transacted, mode); + + _connection.start(); + + int lastCount = (int) _receviedAll.getCount(); + + boolean complete = _receviedAll.await(5000L, TimeUnit.MILLISECONDS); + + while (!complete) + { + int currentCount = (int) _receviedAll.getCount(); + + // make sure we have received a message in the last cycle. + if (lastCount == currentCount) + { + break; + } + // Remember the currentCount as the lastCount for the next cycle. + // so we can exit if things get locked up. + lastCount = currentCount; + + complete = _receviedAll.await(5000L, TimeUnit.MILLISECONDS); + } + + if (!complete) + { + // Check to see if we ended due to an exception in the onMessage handler + Exception cause = _causeOfFailure.get(); + if (cause != null) + { + cause.printStackTrace(); + fail(cause.getMessage()); + } + else + { + fail("All messages not received missing:" + _receviedAll.getCount() + "/" + NUM_MESSAGES); + } + } + + // Check to see if we ended due to an exception in the onMessage handler + Exception cause = _causeOfFailure.get(); + if (cause != null) + { + cause.printStackTrace(); + fail(cause.getMessage()); + } + + try + { + _consumer.close(); + } + catch (JMSAMQException amqe) + { + if (amqe.getLinkedException() instanceof FailoverException) + { + fail("QPID-143 : Auto Ack can acknowledge message from previous session after failver. If failover occurs between deliver and ack."); + } + // else Rethrow for TestCase to catch. + throw amqe; + } + + _consumerSession.close(); + + assertEquals("Wrong number of messages on queue", 0, + ((AMQSession) getConnection().createSession(false, Session.AUTO_ACKNOWLEDGE)).getQueueDepth((AMQDestination) _queue)); + } + + public void onMessage(Message message) + { + try + { + int count = NUM_MESSAGES - (int) _receviedAll.getCount(); + + assertEquals("Incorrect message received", count, message.getIntProperty(INDEX)); + + count++; + if (count < NUM_MESSAGES) + { + //Send the next message + _producer.send(createNextMessage(_consumerSession, count)); + } + + doAcknowlegement(message); + + _receviedAll.countDown(); + } + catch (Exception e) + { + // This will end the test run by counting down _receviedAll + fail(e); + } + } + + /** + * Pass the given exception back to the waiting thread to fail the test run. + * + * @param e The exception that is causing the test to fail. + */ + protected void fail(Exception e) + { + _causeOfFailure.set(e); + // End the test. + while (_receviedAll.getCount() != 0) + { + _receviedAll.countDown(); + } + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeTest.java index c367a0856c..7c9a77eb53 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeTest.java @@ -1,7 +1,5 @@ -package org.apache.qpid.test.unit.ack; - /* - * + * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information @@ -21,133 +19,138 @@ package org.apache.qpid.test.unit.ack; * */ +package org.apache.qpid.test.unit.ack; + +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.test.utils.FailoverBaseCase; + import javax.jms.Connection; 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.MessageProducer; -import org.apache.qpid.client.AMQDestination; -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.message.AbstractJMSMessage; -import org.apache.qpid.test.utils.QpidTestCase; - -public class AcknowledgeTest extends QpidTestCase +public class AcknowledgeTest extends FailoverBaseCase { - protected static int NUM_MESSAGES = 100; - protected Connection _con; + protected int NUM_MESSAGES; + protected Connection _connection; protected Queue _queue; - private MessageProducer _producer; - private Session _producerSession; - private Session _consumerSession; - private MessageConsumer _consumerA; + protected Session _consumerSession; + protected MessageConsumer _consumer; + protected MessageProducer _producer; @Override protected void setUp() throws Exception { super.setUp(); - _queue = (Queue) getInitialContext().lookup("queue"); + NUM_MESSAGES = 5; + + _queue = getTestQueue(); //Create Producer put some messages on the queue - _con = getConnection(); - _con.start(); + _connection = getConnection(); } - private void init(boolean transacted, int mode) throws JMSException { - _producerSession = _con.createSession(true, Session.AUTO_ACKNOWLEDGE); - _consumerSession = _con.createSession(transacted, mode); - _producer = _producerSession.createProducer(_queue); - _consumerA = _consumerSession.createConsumer(_queue); - } + protected void init(boolean transacted, int mode) throws Exception + { + _consumerSession = _connection.createSession(transacted, mode); + _consumer = _consumerSession.createConsumer(_queue); + _producer = _consumerSession.createProducer(_queue); + + // These should all end up being prefetched by session + sendMessage(_consumerSession, _queue, 1); + + assertEquals("Wrong number of messages on queue", 1, + ((AMQSession) _consumerSession).getQueueDepth((AMQDestination) _queue)); + } /** - * Produces and consumes messages an either ack or commit the receipt of those messages - * * @param transacted * @param mode + * * @throws Exception */ - private void testMessageAck(boolean transacted, int mode) throws Exception + protected void testAcking(boolean transacted, int mode) throws Exception { - init(transacted, mode); - sendMessage(_producerSession, _queue, NUM_MESSAGES/2); - _producerSession.commit(); - MessageConsumer consumerB = _consumerSession.createConsumer(_queue); - sendMessage(_producerSession, _queue, NUM_MESSAGES/2); - _producerSession.commit(); + init(transacted, mode); + + _connection.start(); + + Message msg = _consumer.receive(1500); + int count = 0; - Message msg = consumerB.receive(1500); - while (msg != null) + while (count < NUM_MESSAGES) { - if (mode == Session.CLIENT_ACKNOWLEDGE) + assertNotNull("Message " + count + " not correctly received.", msg); + assertEquals("Incorrect message received", count, msg.getIntProperty(INDEX)); + count++; + + if (count < NUM_MESSAGES) { - msg.acknowledge(); + //Send the next message + _producer.send(createNextMessage(_consumerSession, count)); } - count++; - msg = consumerB.receive(1500); + + doAcknowlegement(msg); + + msg = _consumer.receive(1500); } - if (transacted) - { - _consumerSession.commit(); - } - _consumerA.close(); - consumerB.close(); - _consumerSession.close(); - assertEquals("Wrong number of messages on queue", NUM_MESSAGES - count, - ((AMQSession) _producerSession).getQueueDepth((AMQDestination) _queue)); - - // Clean up messages that may be left on the queue - _consumerSession = _con.createSession(transacted, mode); - _consumerA = _consumerSession.createConsumer(_queue); - msg = _consumerA.receive(1500); - while (msg != null) + + assertEquals("Wrong number of messages on queue", 0, + ((AMQSession) _consumerSession).getQueueDepth((AMQDestination) _queue)); + } + + /** + * Perform the acknowledgement of messages if additionally required. + * + * @param msg + * + * @throws JMSException + */ + protected void doAcknowlegement(Message msg) throws JMSException + { + if (_consumerSession.getTransacted()) { - if (mode == Session.CLIENT_ACKNOWLEDGE) - { - msg.acknowledge(); - } - msg = _consumerA.receive(1500); + _consumerSession.commit(); } - _consumerA.close(); - if (transacted) + + if (_consumerSession.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE) { - _consumerSession.commit(); + msg.acknowledge(); } - _consumerSession.close(); } - - public void test2ConsumersAutoAck() throws Exception + + public void testClientAck() throws Exception { - testMessageAck(false, Session.AUTO_ACKNOWLEDGE); + testAcking(false, Session.CLIENT_ACKNOWLEDGE); } - public void test2ConsumersClientAck() throws Exception + public void testAutoAck() throws Exception { - testMessageAck(true, Session.CLIENT_ACKNOWLEDGE); + testAcking(false, Session.AUTO_ACKNOWLEDGE); } - - public void test2ConsumersTx() throws Exception + + public void testTransacted() throws Exception { - testMessageAck(true, Session.AUTO_ACKNOWLEDGE); + testAcking(true, Session.SESSION_TRANSACTED); } - - public void testIndividualAck() throws Exception + + public void testDupsOk() throws Exception { - init(false, Session.CLIENT_ACKNOWLEDGE); - sendMessage(_producerSession, _queue, 3); - _producerSession.commit(); - Message msg = null; - for (int i = 0; i < 2; i++) - { - msg = _consumerA.receive(RECEIVE_TIMEOUT); - ((AbstractJMSMessage)msg).acknowledgeThis(); - } - msg = _consumerA.receive(RECEIVE_TIMEOUT); - msg.acknowledge(); - _con.close(); + testAcking(false, Session.DUPS_OK_ACKNOWLEDGE); } - + + public void testNoAck() throws Exception + { + testAcking(false, AMQSession.NO_ACKNOWLEDGE); + } + + public void testPreAck() throws Exception + { + testAcking(false, AMQSession.PRE_ACKNOWLEDGE); + } + } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/FailoverBeforeConsumingRecoverTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/FailoverBeforeConsumingRecoverTest.java new file mode 100644 index 0000000000..834b17430b --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/FailoverBeforeConsumingRecoverTest.java @@ -0,0 +1,40 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.ack; + +import org.apache.qpid.jms.Session; + +import javax.jms.Message; +import javax.jms.Queue; + +public class FailoverBeforeConsumingRecoverTest extends RecoverTest +{ + + @Override + protected void initTest() throws Exception + { + super.initTest(); + failBroker(getFailingPort()); + + Queue queue = _consumerSession.createQueue(getTestQueueName()); + sendMessage(_connection.createSession(false, Session.AUTO_ACKNOWLEDGE), queue, SENT_COUNT); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/QuickAcking.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/QuickAcking.java new file mode 100644 index 0000000000..6c4b7ba01b --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/QuickAcking.java @@ -0,0 +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.test.unit.ack; + +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.jms.ConnectionListener; +import org.apache.qpid.test.utils.QpidTestCase; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; + +/** + * This is a quick manual test to validate acking after failover with a + * transacted session. + * + * Start an external broker then run this test. Std Err will print. + * Sent Message: 1 + * Received Message: 1 + * + * You can then restart the external broker, which will cause failover, which + * will be complete when the following appears. + * + * Failover Complete + * + * A second message send/receive cycle is then done to validate that the + * connection/session are still working. + * + */ +public class QuickAcking extends QpidTestCase implements ConnectionListener +{ + protected AMQConnection _connection; + protected Queue _queue; + protected Session _session; + protected MessageConsumer _consumer; + private CountDownLatch _failedOver; + private static final String INDEX = "INDEX"; + private int _count = 0; + + public void setUp() + { + // Prevent broker startup. Broker must be run manually. + } + + public void test() throws Exception + { + _failedOver = new CountDownLatch(1); + + _connection = new AMQConnection("amqp://guest:guest@client/test?brokerlist='localhost?retries='20'&connectdelay='2000''"); + + _session = _connection.createSession(true, Session.SESSION_TRANSACTED); + _queue = _session.createQueue("QAtest"); + _consumer = _session.createConsumer(_queue); + _connection.setConnectionListener(this); + _connection.start(); + + sendAndReceive(); + + _failedOver.await(); + + sendAndReceive(); + + } + + private void sendAndReceive() + throws Exception + { + sendMessage(); + + Message message = _consumer.receive(); + + if (message.getIntProperty(INDEX) != _count) + { + throw new Exception("Incorrect message recieved:" + _count); + } + + if (_session.getTransacted()) + { + _session.commit(); + } + System.err.println("Recevied Message:" + _count); + } + + private void sendMessage() throws JMSException + { + MessageProducer producer = _session.createProducer(_queue); + Message message = _session.createMessage(); + _count++; + message.setIntProperty(INDEX, _count); + + producer.send(message); + if (_session.getTransacted()) + { + _session.commit(); + } + producer.close(); + + System.err.println("Sent Message:" + _count); + } + + public void bytesSent(long count) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void bytesReceived(long count) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean preFailover(boolean redirect) + { + return true; + } + + public boolean preResubscribe() + { + return true; + } + + public void failoverComplete() + { + System.err.println("Failover Complete"); + _failedOver.countDown(); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/RecoverTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/RecoverTest.java index 7434fcbb30..4a123cb1dc 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/RecoverTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/RecoverTest.java @@ -23,8 +23,7 @@ import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQQueue; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.jms.Session; -import org.apache.qpid.test.utils.QpidTestCase; - +import org.apache.qpid.test.utils.FailoverBaseCase; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,16 +34,21 @@ import javax.jms.MessageListener; import javax.jms.MessageProducer; import javax.jms.Queue; import javax.jms.TextMessage; - import java.util.concurrent.atomic.AtomicInteger; -public class RecoverTest extends QpidTestCase +public class RecoverTest extends FailoverBaseCase { - private static final Logger _logger = LoggerFactory.getLogger(RecoverTest.class); + static final Logger _logger = LoggerFactory.getLogger(RecoverTest.class); private Exception _error; private AtomicInteger count; + protected AMQConnection _connection; + protected Session _consumerSession; + protected MessageConsumer _consumer; + static final int SENT_COUNT = 4; + + @Override protected void setUp() throws Exception { super.setUp(); @@ -52,134 +56,110 @@ public class RecoverTest extends QpidTestCase count = new AtomicInteger(); } - protected void tearDown() throws Exception + protected void initTest() throws Exception { - super.tearDown(); - count = null; - } + _connection = (AMQConnection) getConnection("guest", "guest"); - public void testRecoverResendsMsgs() throws Exception - { - AMQConnection con = (AMQConnection) getConnection("guest", "guest"); - - Session consumerSession = con.createSession(false, Session.CLIENT_ACKNOWLEDGE); - Queue queue = - new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("someQ"), - new AMQShortString("someQ"), false, true); - MessageConsumer consumer = consumerSession.createConsumer(queue); - // force synch to ensure the consumer has resulted in a bound queue - // ((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.DIRECT_EXCHANGE_NAME, ExchangeDefaults.DIRECT_EXCHANGE_CLASS); - // This is the default now + _consumerSession = _connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Queue queue = _consumerSession.createQueue(getTestQueueName()); - AMQConnection con2 = (AMQConnection) getConnection("guest", "guest"); - Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); - MessageProducer producer = producerSession.createProducer(queue); + _consumer = _consumerSession.createConsumer(queue); _logger.info("Sending four messages"); - producer.send(producerSession.createTextMessage("msg1")); - producer.send(producerSession.createTextMessage("msg2")); - producer.send(producerSession.createTextMessage("msg3")); - producer.send(producerSession.createTextMessage("msg4")); - - con2.close(); - + sendMessage(_connection.createSession(false, Session.AUTO_ACKNOWLEDGE), queue, SENT_COUNT); _logger.info("Starting connection"); - con.start(); - TextMessage tm = (TextMessage) consumer.receive(); - tm.acknowledge(); - _logger.info("Received and acknowledged first message"); - consumer.receive(); - consumer.receive(); - consumer.receive(); - _logger.info("Received all four messages. Calling recover with three outstanding messages"); - // no ack for last three messages so when I call recover I expect to get three messages back - consumerSession.recover(); - tm = (TextMessage) consumer.receive(3000); - assertEquals("msg2", tm.getText()); + _connection.start(); + } - tm = (TextMessage) consumer.receive(3000); - assertEquals("msg3", tm.getText()); + protected Message validateNextMessages(int nextCount, int startIndex) throws JMSException + { + Message message = null; + for (int index = 0; index < nextCount; index++) + { + message = _consumer.receive(3000); + assertEquals(startIndex + index, message.getIntProperty(INDEX)); + } + return message; + } - tm = (TextMessage) consumer.receive(3000); - assertEquals("msg4", tm.getText()); + protected void validateRemainingMessages(int remaining) throws JMSException + { + int index = SENT_COUNT - remaining; - _logger.info("Received redelivery of three messages. Acknowledging last message"); - tm.acknowledge(); + Message message = null; + while (index != SENT_COUNT) + { + message = _consumer.receive(3000); + assertEquals(index++, message.getIntProperty(INDEX)); + } + + if (message != null) + { + _logger.info("Received redelivery of three messages. Acknowledging last message"); + message.acknowledge(); + } _logger.info("Calling acknowledge with no outstanding messages"); // all acked so no messages to be delivered - consumerSession.recover(); + _consumerSession.recover(); - tm = (TextMessage) consumer.receiveNoWait(); - assertNull(tm); + message = _consumer.receiveNoWait(); + assertNull(message); _logger.info("No messages redelivered as is expected"); - - con.close(); } - public void testRecoverResendsMsgsAckOnEarlier() throws Exception + public void testRecoverResendsMsgs() throws Exception { - AMQConnection con = (AMQConnection) getConnection("guest", "guest"); + initTest(); - Session consumerSession = con.createSession(false, Session.CLIENT_ACKNOWLEDGE); - Queue queue = - new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("someQ"), - new AMQShortString("someQ"), false, true); - MessageConsumer consumer = consumerSession.createConsumer(queue); - // force synch to ensure the consumer has resulted in a bound queue - // ((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.DIRECT_EXCHANGE_NAME, ExchangeDefaults.DIRECT_EXCHANGE_CLASS); - // This is the default now + Message message = validateNextMessages(1, 0); + message.acknowledge(); + _logger.info("Received and acknowledged first message"); - AMQConnection con2 = (AMQConnection) getConnection("guest", "guest"); - Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); - MessageProducer producer = producerSession.createProducer(queue); + _consumer.receive(); + _consumer.receive(); + _consumer.receive(); + _logger.info("Received all four messages. Calling recover with three outstanding messages"); + // no ack for last three messages so when I call recover I expect to get three messages back - _logger.info("Sending four messages"); - producer.send(producerSession.createTextMessage("msg1")); - producer.send(producerSession.createTextMessage("msg2")); - producer.send(producerSession.createTextMessage("msg3")); - producer.send(producerSession.createTextMessage("msg4")); + _consumerSession.recover(); - con2.close(); + validateRemainingMessages(3); + } - _logger.info("Starting connection"); - con.start(); - TextMessage tm = (TextMessage) consumer.receive(); - consumer.receive(); - tm.acknowledge(); + public void testRecoverResendsMsgsAckOnEarlier() throws Exception + { + initTest(); + + Message message = validateNextMessages(2, 0); + message.acknowledge(); _logger.info("Received 2 messages, acknowledge() first message, should acknowledge both"); - consumer.receive(); - consumer.receive(); + _consumer.receive(); + _consumer.receive(); _logger.info("Received all four messages. Calling recover with two outstanding messages"); // no ack for last three messages so when I call recover I expect to get three messages back - consumerSession.recover(); - TextMessage tm3 = (TextMessage) consumer.receive(3000); - assertEquals("msg3", tm3.getText()); + _consumerSession.recover(); + + Message message2 = _consumer.receive(3000); + assertEquals(2, message2.getIntProperty(INDEX)); - TextMessage tm4 = (TextMessage) consumer.receive(3000); - assertEquals("msg4", tm4.getText()); + Message message3 = _consumer.receive(3000); + assertEquals(3, message3.getIntProperty(INDEX)); _logger.info("Received redelivery of two messages. calling acknolwedgeThis() first of those message"); - ((org.apache.qpid.jms.Message) tm3).acknowledgeThis(); + ((org.apache.qpid.jms.Message) message2).acknowledgeThis(); _logger.info("Calling recover"); // all acked so no messages to be delivered - consumerSession.recover(); + _consumerSession.recover(); - tm4 = (TextMessage) consumer.receive(3000); - assertEquals("msg4", tm4.getText()); - ((org.apache.qpid.jms.Message) tm4).acknowledgeThis(); + message3 = _consumer.receive(3000); + assertEquals(3, message3.getIntProperty(INDEX)); + ((org.apache.qpid.jms.Message) message3).acknowledgeThis(); - _logger.info("Calling recover"); // all acked so no messages to be delivered - consumerSession.recover(); - - tm = (TextMessage) consumer.receiveNoWait(); - assertNull(tm); - _logger.info("No messages redelivered as is expected"); - - con.close(); + validateRemainingMessages(0); } public void testAcknowledgePerConsumer() throws Exception @@ -188,11 +168,11 @@ public class RecoverTest extends QpidTestCase Session consumerSession = con.createSession(false, Session.CLIENT_ACKNOWLEDGE); Queue queue = - new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q1"), new AMQShortString("Q1"), - false, true); + new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q1"), new AMQShortString("Q1"), + false, true); Queue queue2 = - new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q2"), new AMQShortString("Q2"), - false, true); + new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q2"), new AMQShortString("Q2"), + false, true); MessageConsumer consumer = consumerSession.createConsumer(queue); MessageConsumer consumer2 = consumerSession.createConsumer(queue2); @@ -231,8 +211,8 @@ public class RecoverTest extends QpidTestCase final Session consumerSession = con.createSession(false, Session.AUTO_ACKNOWLEDGE); Queue queue = - new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q3"), new AMQShortString("Q3"), - false, true); + new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q3"), new AMQShortString("Q3"), + false, true); MessageConsumer consumer = consumerSession.createConsumer(queue); MessageProducer producer = consumerSession.createProducer(queue); producer.send(consumerSession.createTextMessage("hello")); @@ -240,50 +220,50 @@ public class RecoverTest extends QpidTestCase final Object lock = new Object(); consumer.setMessageListener(new MessageListener() - { + { - public void onMessage(Message message) + public void onMessage(Message message) + { + try { - try + count.incrementAndGet(); + if (count.get() == 1) { - count.incrementAndGet(); - if (count.get() == 1) + if (message.getJMSRedelivered()) { - if (message.getJMSRedelivered()) - { - setError( + setError( new Exception("Message marked as redilvered on what should be first delivery attempt")); - } - - consumerSession.recover(); } - else if (count.get() == 2) + + consumerSession.recover(); + } + else if (count.get() == 2) + { + if (!message.getJMSRedelivered()) { - if (!message.getJMSRedelivered()) - { - setError( + setError( new Exception( - "Message not marked as redilvered on what should be second delivery attempt")); - } - } - else - { - System.err.println(message); - fail("Message delivered too many times!: " + count); + "Message not marked as redilvered on what should be second delivery attempt")); } } - catch (JMSException e) + else { - _logger.error("Error recovering session: " + e, e); - setError(e); + System.err.println(message); + fail("Message delivered too many times!: " + count); } + } + catch (JMSException e) + { + _logger.error("Error recovering session: " + e, e); + setError(e); + } - synchronized (lock) - { - lock.notify(); - } + synchronized (lock) + { + lock.notify(); } - }); + } + }); con.start(); @@ -323,9 +303,4 @@ public class RecoverTest extends QpidTestCase { _error = e; } - - public static junit.framework.Test suite() - { - return new junit.framework.TestSuite(RecoverTest.class); - } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/SelectorTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/SelectorTest.java deleted file mode 100644 index c42e4c7582..0000000000 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/SelectorTest.java +++ /dev/null @@ -1,306 +0,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.test.unit.basic; - -import org.apache.qpid.AMQException; -import org.apache.qpid.test.utils.QpidTestCase; -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.AMQDestination; -import org.apache.qpid.client.AMQQueue; -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.client.BasicMessageProducer; -import org.apache.qpid.client.state.StateWaiter; -import org.apache.qpid.url.URLSyntaxException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.jms.Connection; -import javax.jms.DeliveryMode; -import javax.jms.InvalidSelectorException; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageListener; -import java.util.concurrent.CountDownLatch; - -public class SelectorTest extends QpidTestCase implements MessageListener -{ - private static final Logger _logger = LoggerFactory.getLogger(SelectorTest.class); - - private AMQConnection _connection; - private AMQDestination _destination; - private AMQSession _session; - private int count; - public String _connectionString = "vm://:1"; - private static final String INVALID_SELECTOR = "Cost LIKE 5"; - CountDownLatch _responseLatch = new CountDownLatch(1); - - protected void setUp() throws Exception - { - super.setUp(); - init((AMQConnection) getConnection("guest", "guest")); - } - - protected void tearDown() throws Exception - { - super.tearDown(); - } - - private void init(AMQConnection connection) throws JMSException - { - init(connection, new AMQQueue(connection, randomize("SessionStartTest"), true)); - } - - private void init(AMQConnection connection, AMQDestination destination) throws JMSException - { - _connection = connection; - _destination = destination; - connection.start(); - - String selector = null; - selector = "Cost = 2 AND \"property-with-hyphen\" = 'wibble'"; - // selector = "JMSType = Special AND Cost = 2 AND AMQMessageID > 0 AND JMSDeliveryMode=" + DeliveryMode.NON_PERSISTENT; - - _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); - // _session.createConsumer(destination).setMessageListener(this); - _session.createConsumer(destination, selector).setMessageListener(this); - } - - public void test() throws Exception - { - try - { - - init((AMQConnection) getConnection("guest", "guest", randomize("Client"))); - - Message msg = _session.createTextMessage("Message"); - msg.setJMSPriority(1); - msg.setIntProperty("Cost", 2); - msg.setStringProperty("property-with-hyphen", "wibble"); - msg.setJMSType("Special"); - - _logger.info("Sending Message:" + msg); - - ((BasicMessageProducer) _session.createProducer(_destination)).send(msg, DeliveryMode.NON_PERSISTENT); - _logger.info("Message sent, waiting for response..."); - - _responseLatch.await(); - - if (count > 0) - { - _logger.info("Got message"); - } - - if (count == 0) - { - fail("Did not get message!"); - // throw new RuntimeException("Did not get message!"); - } - } - catch (JMSException e) - { - _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); - if (!(e instanceof InvalidSelectorException)) - { - fail("Wrong exception:" + e.getMessage()); - } - else - { - System.out.println("SUCCESS!!"); - } - } - catch (InterruptedException e) - { - _logger.debug("IE :" + e.getClass().getSimpleName() + ":" + e.getMessage()); - } - catch (URLSyntaxException e) - { - _logger.debug("URL:" + e.getClass().getSimpleName() + ":" + e.getMessage()); - fail("Wrong exception"); - } - catch (AMQException e) - { - _logger.debug("AMQ:" + e.getClass().getSimpleName() + ":" + e.getMessage()); - fail("Wrong exception"); - } - - finally - { - if (_session != null) - { - _session.close(); - } - if (_connection != null) - { - _connection.close(); - } - } - } - - - public void testInvalidSelectors() throws Exception - { - Connection connection = null; - - try - { - connection = getConnection("guest", "guest", randomize("Client")); - _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); - } - catch (JMSException e) - { - fail(e.getMessage()); - } - catch (AMQException e) - { - fail(e.getMessage()); - } - catch (URLSyntaxException e) - { - fail("Error:" + e.getMessage()); - } - - //Try Creating a Browser - try - { - _session.createBrowser(_session.createQueue("Ping"), INVALID_SELECTOR); - } - catch (JMSException e) - { - _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); - if (!(e instanceof InvalidSelectorException)) - { - fail("Wrong exception:" + e.getMessage()); - } - else - { - _logger.debug("SUCCESS!!"); - } - } - - //Try Creating a Consumer - try - { - _session.createConsumer(_session.createQueue("Ping"), INVALID_SELECTOR); - } - catch (JMSException e) - { - _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); - if (!(e instanceof InvalidSelectorException)) - { - fail("Wrong exception:" + e.getMessage()); - } - else - { - _logger.debug("SUCCESS!!"); - } - } - - //Try Creating a Receiever - try - { - _session.createReceiver(_session.createQueue("Ping"), INVALID_SELECTOR); - } - catch (JMSException e) - { - _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); - if (!(e instanceof InvalidSelectorException)) - { - fail("Wrong exception:" + e.getMessage()); - } - else - { - _logger.debug("SUCCESS!!"); - } - } - - finally - { - if (_session != null) - { - try - { - _session.close(); - } - catch (JMSException e) - { - fail("Error cleaning up:" + e.getMessage()); - } - } - if (_connection != null) - { - try - { - _connection.close(); - } - catch (JMSException e) - { - fail("Error cleaning up:" + e.getMessage()); - } - } - } - } - - public void onMessage(Message message) - { - count++; - _logger.info("Got Message:" + message); - _responseLatch.countDown(); - } - - private static String randomize(String in) - { - return in + System.currentTimeMillis(); - } - - public static void main(String[] argv) throws Exception - { - SelectorTest test = new SelectorTest(); - test._connectionString = (argv.length == 0) ? "localhost:3000" : argv[0]; - - try - { - while (true) - { - if (test._connectionString.contains("vm://:1")) - { - test.setUp(); - } - test.test(); - - if (test._connectionString.contains("vm://:1")) - { - test.tearDown(); - } - } - } - catch (Exception e) - { - System.err.println(e.getMessage()); - e.printStackTrace(); - } - } - - public static junit.framework.Test suite() - { - return new junit.framework.TestSuite(SelectorTest.class); - } -} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java index 1f90f1e29f..6b69ccab6c 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java @@ -31,6 +31,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.jms.Destination; +import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageProducer; import javax.jms.ObjectMessage; @@ -105,7 +106,8 @@ public class JMSPropertiesTest extends QpidTestCase assertEquals("JMS Type mismatch", sentMsg.getJMSType(), rm.getJMSType()); assertEquals("JMS Reply To mismatch", sentMsg.getJMSReplyTo(), rm.getJMSReplyTo()); assertTrue("JMSMessageID Does not start ID:", rm.getJMSMessageID().startsWith("ID:")); - + assertEquals("JMS Default priority should be 4",Message.DEFAULT_PRIORITY,rm.getJMSPriority()); + //Validate that the JMSX values are correct assertEquals("JMSXGroupID is not as expected:", JMSXGroupID_VALUE, rm.getStringProperty("JMSXGroupID")); assertEquals("JMSXGroupSeq is not as expected:", JMSXGroupSeq_VALUE, rm.getIntProperty("JMSXGroupSeq")); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/publish/DirtyTrasactedPubilshTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/publish/DirtyTrasactedPubilshTest.java new file mode 100644 index 0000000000..248042d2c4 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/publish/DirtyTrasactedPubilshTest.java @@ -0,0 +1,403 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.publish; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.jms.ConnectionListener; +import org.apache.qpid.test.utils.FailoverBaseCase; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TransactionRolledBackException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +/** + * QPID-1816 : Whilst testing Acknoledgement after failover this completes testing + * of the client after failover. When we have a dirty session we should receive + * an error if we attempt to publish. This test ensures that both in the synchronous + * and asynchronous message delivery paths we receive the expected exceptions at + * the expected time. + */ +public class DirtyTrasactedPubilshTest extends FailoverBaseCase implements ConnectionListener +{ + protected CountDownLatch _failoverCompleted = new CountDownLatch(1); + + protected int NUM_MESSAGES; + protected Connection _connection; + protected Queue _queue; + protected Session _consumerSession; + protected MessageConsumer _consumer; + protected MessageProducer _producer; + + private static final String MSG = "MSG"; + private static final String SEND_FROM_ON_MESSAGE_TEXT = "sendFromOnMessage"; + protected CountDownLatch _receviedAll; + protected AtomicReference _causeOfFailure = new AtomicReference(null); + + @Override + protected void setUp() throws Exception + { + super.setUp(); + NUM_MESSAGES = 10; + + _queue = getTestQueue(); + + //Create Producer put some messages on the queue + _connection = getConnection(); + } + + /** + * Initialise the test variables + * @param transacted is this a transacted test + * @param mode if not trasacted then what ack mode to use + * @throws Exception if there is a setup issue. + */ + protected void init(boolean transacted, int mode) throws Exception + { + _consumerSession = _connection.createSession(transacted, mode); + _consumer = _consumerSession.createConsumer(_queue); + _producer = _consumerSession.createProducer(_queue); + + // These should all end up being prefetched by session + sendMessage(_consumerSession, _queue, 1); + + assertEquals("Wrong number of messages on queue", 1, + ((AMQSession) _consumerSession).getQueueDepth((AMQDestination) _queue)); + } + + /** + * If a transacted session has failed over whilst it has uncommitted sent + * data then we need to throw a TransactedRolledbackException on commit() + * + * The alternative would be to maintain a replay buffer so that the message + * could be resent. This is not currently implemented + * + * @throws Exception if something goes wrong. + */ + public void testDirtySendingSynchronousTransacted() throws Exception + { + Session producerSession = _connection.createSession(true, Session.SESSION_TRANSACTED); + + // Ensure we get failover notifications + ((AMQConnection) _connection).setConnectionListener(this); + + MessageProducer producer = producerSession.createProducer(_queue); + + // Create and send message 0 + Message msg = producerSession.createMessage(); + msg.setIntProperty(INDEX, 0); + producer.send(msg); + + // DON'T commit message .. fail connection + + failBroker(getFailingPort()); + + // Ensure destination exists for sending + producerSession.createConsumer(_queue).close(); + + // Send the next message + msg.setIntProperty(INDEX, 1); + try + { + producer.send(msg); + fail("Should fail with Qpid as we provide early warning of the dirty session via a JMSException."); + } + catch (JMSException jmse) + { + assertEquals("Early warning of dirty session not correct", + "Failover has occurred and session is dirty so unable to send.", jmse.getMessage()); + } + + // Ignore that the session is dirty and attempt to commit to validate the + // exception is thrown. AND that the above failure notification did NOT + // clean up the session. + + try + { + producerSession.commit(); + fail("Session is dirty we should get an TransactionRolledBackException"); + } + catch (TransactionRolledBackException trbe) + { + // Normal path. + } + + // Resending of messages should now work ok as the commit was forcilbly rolledback + msg.setIntProperty(INDEX, 0); + producer.send(msg); + msg.setIntProperty(INDEX, 1); + producer.send(msg); + + producerSession.commit(); + + assertEquals("Wrong number of messages on queue", 2, + ((AMQSession) producerSession).getQueueDepth((AMQDestination) _queue)); + } + + /** + * If a transacted session has failed over whilst it has uncommitted sent + * data then we need to throw a TransactedRolledbackException on commit() + * + * The alternative would be to maintain a replay buffer so that the message + * could be resent. This is not currently implemented + * + * @throws Exception if something goes wrong. + */ + public void testDirtySendingOnMessageTransacted() throws Exception + { + NUM_MESSAGES = 1; + _receviedAll = new CountDownLatch(NUM_MESSAGES); + ((AMQConnection) _connection).setConnectionListener(this); + + init(true, Session.SESSION_TRANSACTED); + + _consumer.setMessageListener(new MessageListener() + { + + public void onMessage(Message message) + { + try + { + // Create and send message 0 + Message msg = _consumerSession.createMessage(); + msg.setIntProperty(INDEX, 0); + _producer.send(msg); + + // DON'T commit message .. fail connection + + failBroker(getFailingPort()); + + // rep + repopulateBroker(); + + // Destination will exist as this failBroker will populate + // the queue with 1 message + + // Send the next message + msg.setIntProperty(INDEX, 1); + try + { + _producer.send(msg); + fail("Should fail with Qpid as we provide early warning of the dirty session via a JMSException."); + } + catch (JMSException jmse) + { + assertEquals("Early warning of dirty session not correct", + "Failover has occurred and session is dirty so unable to send.", jmse.getMessage()); + } + + // Ignore that the session is dirty and attempt to commit to validate the + // exception is thrown. AND that the above failure notification did NOT + // clean up the session. + + try + { + _consumerSession.commit(); + fail("Session is dirty we should get an TransactionRolledBackException"); + } + catch (TransactionRolledBackException trbe) + { + // Normal path. + } + + // Resend messages + msg.setIntProperty(INDEX, 0); + msg.setStringProperty(MSG, SEND_FROM_ON_MESSAGE_TEXT); + _producer.send(msg); + msg.setIntProperty(INDEX, 1); + msg.setStringProperty(MSG, SEND_FROM_ON_MESSAGE_TEXT); + _producer.send(msg); + + _consumerSession.commit(); + + // Stop this consumer .. can't do _consumer.stop == DEADLOCK + // this doesn't seem to stop dispatcher running + _connection.stop(); + + // Signal that the onMessage send part of test is complete + // main thread can validate that messages are correct + _receviedAll.countDown(); + + } + catch (Exception e) + { + fail(e); + } + + } + + }); + + _connection.start(); + + if (!_receviedAll.await(10000L, TimeUnit.MILLISECONDS)) + { + // Check to see if we ended due to an exception in the onMessage handler + Exception cause = _causeOfFailure.get(); + if (cause != null) + { + cause.printStackTrace(); + fail(cause.getMessage()); + } + else + { + fail("All messages not received:" + _receviedAll.getCount() + "/" + NUM_MESSAGES); + } + } + + // Check to see if we ended due to an exception in the onMessage handler + Exception cause = _causeOfFailure.get(); + if (cause != null) + { + cause.printStackTrace(); + fail(cause.getMessage()); + } + + _consumer.close(); + _consumerSession.close(); + + _consumerSession = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + _connection.start(); + + // Validate that we could send the messages as expected. + assertEquals("Wrong number of messages on queue", 3, + ((AMQSession) _consumerSession).getQueueDepth((AMQDestination) _queue)); + + MessageConsumer consumer = _consumerSession.createConsumer(_queue); + + //Validate the message sent to setup the failed over broker. + Message message = consumer.receive(1000); + assertNotNull("Message " + 0 + " not received.", message); + assertEquals("Incorrect message received", 0, message.getIntProperty(INDEX)); + + // Validate the two messages sent from within the onMessage + for (int index = 0; index <= 1; index++) + { + message = consumer.receive(1000); + assertNotNull("Message " + index + " not received.", message); + assertEquals("Incorrect message received", index, message.getIntProperty(INDEX)); + assertEquals("Incorrect message text for message:" + index, SEND_FROM_ON_MESSAGE_TEXT, message.getStringProperty(MSG)); + } + + assertNull("Extra message received.", consumer.receiveNoWait()); + + _consumerSession.close(); + + assertEquals("Wrong number of messages on queue", 0, + ((AMQSession) getConnection().createSession(false, Session.AUTO_ACKNOWLEDGE)).getQueueDepth((AMQDestination) _queue)); + } + + private void repopulateBroker() throws Exception + { + // Repopulate this new broker so we can test what happends after failover + + //Get the connection to the first (main port) broker. + Connection connection = getConnection(); + // Use a transaction to send messages so we can be sure they arrive. + Session session = connection.createSession(true, Session.SESSION_TRANSACTED); + // ensure destination is created. + session.createConsumer(_queue).close(); + + sendMessage(session, _queue, NUM_MESSAGES); + + assertEquals("Wrong number of messages on queue", NUM_MESSAGES, + ((AMQSession) session).getQueueDepth((AMQDestination) _queue)); + + connection.close(); + } + + // AMQConnectionListener Interface.. used so we can validate that we + // actually failed over. + + public void bytesSent(long count) + { + } + + public void bytesReceived(long count) + { + } + + public boolean preFailover(boolean redirect) + { + //Allow failover + return true; + } + + public boolean preResubscribe() + { + //Allow failover + return true; + } + + public void failoverComplete() + { + _failoverCompleted.countDown(); + } + + /** + * Override so we can block until failover has completd + * + * @param port int the port of the broker to fail. + */ + @Override + public void failBroker(int port) + { + super.failBroker(port); + + try + { + if (!_failoverCompleted.await(DEFAULT_FAILOVER_TIME, TimeUnit.MILLISECONDS)) + { + fail("Failover did not occur in specified time:" + DEFAULT_FAILOVER_TIME); + } + } + catch (InterruptedException e) + { + fail("Failover was interuppted"); + } + } + + /** + * Pass the given exception back to the waiting thread to fail the test run. + * + * @param e The exception that is causing the test to fail. + */ + protected void fail(Exception e) + { + _causeOfFailure.set(e); + // End the test. + while (_receviedAll.getCount() != 0) + { + _receviedAll.countDown(); + } + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/FailoverBaseCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/FailoverBaseCase.java index 64bd1503ba..0426c4f45f 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/FailoverBaseCase.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/FailoverBaseCase.java @@ -20,44 +20,43 @@ */ package org.apache.qpid.test.utils; -import javax.jms.Connection; -import javax.jms.JMSException; +import org.apache.qpid.util.FileUtils; + import javax.naming.NamingException; -import org.apache.qpid.util.FileUtils; +import org.apache.qpid.client.AMQConnectionFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class FailoverBaseCase extends QpidTestCase { + protected static final Logger _logger = LoggerFactory.getLogger(FailoverBaseCase.class); public static int FAILING_VM_PORT = 2; - public static int FAILING_PORT = DEFAULT_PORT + 100; + public static int FAILING_PORT = Integer.parseInt(System.getProperty("test.port.alt")); + public static final long DEFAULT_FAILOVER_TIME = 10000L; protected int failingPort; - - private boolean failedOver = false; - public FailoverBaseCase() + protected int getFailingPort() { if (_broker.equals(VM)) { - failingPort = FAILING_VM_PORT; + return FAILING_VM_PORT; } else { - failingPort = FAILING_PORT; + return FAILING_PORT; } } - - protected int getFailingPort() - { - return failingPort; - } protected void setUp() throws java.lang.Exception { super.setUp(); - setSystemProperty("QPID_WORK", System.getProperty("java.io.tmpdir")+"/"+getFailingPort()); - startBroker(failingPort); + // Set QPID_WORK to $QPID_WORK/ + // or /tmp/ if QPID_WORK not set. + setSystemProperty("QPID_WORK", System.getProperty("QPID_WORK") + "/" + getFailingPort()); + startBroker(getFailingPort()); } /** @@ -66,42 +65,52 @@ public class FailoverBaseCase extends QpidTestCase * @return a connection * @throws Exception */ - public Connection getConnection() throws JMSException, NamingException + @Override + public AMQConnectionFactory getConnectionFactory() throws NamingException { - Connection conn = - (Boolean.getBoolean("profile.use_ssl"))? - getConnectionFactory("failover.ssl").createConnection("guest", "guest"): - getConnectionFactory("failover").createConnection("guest", "guest"); - _connections.add(conn); - return conn; + _logger.info("get ConnectionFactory"); + if (_connectionFactory == null) + { + if (Boolean.getBoolean("profile.use_ssl")) + { + _connectionFactory = getConnectionFactory("failover.ssl"); + } + else + { + _connectionFactory = getConnectionFactory("failover"); + } + } + return _connectionFactory; } + public void tearDown() throws Exception { - stopBroker(_broker.equals(VM)?FAILING_PORT:FAILING_PORT); - super.tearDown(); - FileUtils.deleteDirectory(System.getProperty("java.io.tmpdir")+"/"+getFailingPort()); + try + { + super.tearDown(); + } + finally + { + // Ensure we shutdown any secondary brokers, even if we are unable + // to cleanly tearDown the QTC. + stopBroker(getFailingPort()); + FileUtils.deleteDirectory(System.getProperty("QPID_WORK") + "/" + getFailingPort()); + } } - /** - * Only used of VM borker. - */ - public void failBroker() + public void failBroker(int port) { - failedOver = true; try { - stopBroker(getFailingPort()); + stopBroker(port); } catch (Exception e) { throw new RuntimeException(e); } } - - protected void setFailingPort(int p) - { - failingPort = p; - } + + } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java index 76e4118c96..6c1b1c7b8d 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java @@ -22,8 +22,10 @@ import junit.framework.TestResult; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.XMLConfiguration; import org.apache.qpid.AMQException; +import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQConnectionFactory; +import org.apache.qpid.client.AMQQueue; import org.apache.qpid.client.transport.TransportConnection; import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.jms.ConnectionURL; @@ -32,6 +34,7 @@ import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; import org.apache.qpid.server.store.DerbyMessageStore; import org.apache.qpid.url.URLSyntaxException; +import org.apache.log4j.Level; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -70,13 +73,18 @@ public class QpidTestCase extends TestCase protected final String QpidHome = System.getProperty("QPID_HOME"); protected File _configFile = new File(System.getProperty("broker.config")); - private static final Logger _logger = LoggerFactory.getLogger(QpidTestCase.class); + protected static final Logger _logger = LoggerFactory.getLogger(QpidTestCase.class); protected long RECEIVE_TIMEOUT = 1000l; - private Map _setProperties = new HashMap(); + private Map _propertiesSetForTestOnly = new HashMap(); + private Map _propertiesSetForBroker = new HashMap(); + private Map _loggerLevelSetForTest = new HashMap(); + private XMLConfiguration _testConfiguration = new XMLConfiguration(); + protected static final String INDEX = "index"; + /** * Some tests are excluded when the property test.excludes is set to true. * An exclusion list is either a file (prop test.excludesfile) which contains one test name @@ -147,6 +155,7 @@ public class QpidTestCase extends TestCase private static final String BROKER_LANGUAGE = "broker.language"; private static final String BROKER = "broker"; private static final String BROKER_CLEAN = "broker.clean"; + private static final String BROKER_CLEAN_BETWEEN_TESTS = "broker.clean.between.tests"; private static final String BROKER_VERSION = "broker.version"; protected static final String BROKER_READY = "broker.ready"; private static final String BROKER_STOPPED = "broker.stopped"; @@ -169,6 +178,7 @@ public class QpidTestCase extends TestCase protected String _brokerLanguage = System.getProperty(BROKER_LANGUAGE, JAVA); protected String _broker = System.getProperty(BROKER, VM); private String _brokerClean = System.getProperty(BROKER_CLEAN, null); + private Boolean _brokerCleanBetweenTests = Boolean.getBoolean(BROKER_CLEAN_BETWEEN_TESTS); private String _brokerVersion = System.getProperty(BROKER_VERSION, VERSION_08); private String _output = System.getProperty(TEST_OUTPUT); @@ -177,7 +187,7 @@ public class QpidTestCase extends TestCase private Map _brokers = new HashMap(); private InitialContext _initialContext; - private AMQConnectionFactory _connectionFactory; + protected AMQConnectionFactory _connectionFactory; private String _testName; @@ -235,6 +245,19 @@ public class QpidTestCase extends TestCase { _logger.error("exception stopping broker", e); } + + if(_brokerCleanBetweenTests) + { + try + { + cleanBroker(); + } + catch (Exception e) + { + _logger.error("exception cleaning up broker", e); + } + } + _logger.info("========== stop " + _testName + " =========="); if (redirected) @@ -451,6 +474,15 @@ public class QpidTestCase extends TestCase env.put("QPID_PNAME", "-DPNAME=QPBRKR -DTNAME=\"" + _testName + "\""); env.put("QPID_WORK", System.getProperty("QPID_WORK")); + + // Use the environment variable to set amqj.logging.level for the broker + // The value used is a 'server' value in the test configuration to + // allow a differentiation between the client and broker logging levels. + if (System.getProperty("amqj.server.logging.level") != null) + { + setBrokerEnvironment("AMQJ_LOGGING_LEVEL", System.getProperty("amqj.server.logging.level")); + } + // Add all the environment settings the test requested if (!_env.isEmpty()) { @@ -460,13 +492,27 @@ public class QpidTestCase extends TestCase } } + + // Add default test logging levels that are used by the log4j-test + // Use the convenience methods to push the current logging setting + // in to the external broker's QPID_OPTS string. + if (System.getProperty("amqj.protocol.logging.level") != null) + { + setSystemProperty("amqj.protocol.logging.level"); + } + if (System.getProperty("root.logging.level") != null) + { + setSystemProperty("root.logging.level"); + } + + String QPID_OPTS = " "; // Add all the specified system properties to QPID_OPTS - if (!_setProperties.isEmpty()) + if (!_propertiesSetForBroker.isEmpty()) { - for (String key : _setProperties.keySet()) + for (String key : _propertiesSetForBroker.keySet()) { - QPID_OPTS += "-D" + key + "=" + System.getProperty(key) + " "; + QPID_OPTS += "-D" + key + "=" + _propertiesSetForBroker.get(key) + " "; } if (env.containsKey("QPID_OPTS")) @@ -489,7 +535,7 @@ public class QpidTestCase extends TestCase if (!p.await(30, TimeUnit.SECONDS)) { - _logger.info("broker failed to become ready:" + p.getStopLine()); + _logger.info("broker failed to become ready (" + p.ready + "):" + p.getStopLine()); //Ensure broker has stopped process.destroy(); cleanBroker(); @@ -666,29 +712,88 @@ public class QpidTestCase extends TestCase _testConfiguration.setProperty(property, value); } + /** + * Set a System property that is to be applied only to the external test + * broker. + * + * This is a convenience method to enable the setting of a -Dproperty=value + * entry in QPID_OPTS + * + * This is only useful for the External Java Broker tests. + * + * @param property the property name + * @param value the value to set the property to + */ + protected void setBrokerOnlySystemProperty(String property, String value) + { + if (!_propertiesSetForBroker.containsKey(property)) + { + _propertiesSetForBroker.put(property, value); + } + + } + + /** + * Set a System (-D) property for this test run. + * + * This convenience method copies the current VMs System Property + * for the external VM Broker. + * + * @param property the System property to set + */ + protected void setSystemProperty(String property) + { + setSystemProperty(property, System.getProperty(property)); + } + /** * Set a System property for the duration of this test. * * When the test run is complete the value will be reverted. * + * The values set using this method will also be propogated to the external + * Java Broker via a -D value defined in QPID_OPTS. + * + * If the value should not be set on the broker then use + * setTestClientSystemProperty(). + * * @param property the property to set * @param value the new value to use */ protected void setSystemProperty(String property, String value) { - if (!_setProperties.containsKey(property)) + // Record the value for the external broker + _propertiesSetForBroker.put(property, value); + + //Set the value for the test client vm aswell. + setTestClientSystemProperty(property, value); + } + + /** + * Set a System (-D) property for the external Broker of this test. + * + * @param property The property to set + * @param value the value to set it to. + */ + protected void setTestClientSystemProperty(String property, String value) + { + if (!_propertiesSetForTestOnly.containsKey(property)) { - _setProperties.put(property, System.getProperty(property)); - } + // Record the current value so we can revert it later. + _propertiesSetForTestOnly.put(property, System.getProperty(property)); + } System.setProperty(property, value); } + /** + * Restore the System property values that were set before this test run. + */ protected void revertSystemProperties() { - for (String key : _setProperties.keySet()) + for (String key : _propertiesSetForTestOnly.keySet()) { - String value = _setProperties.get(key); + String value = _propertiesSetForTestOnly.get(key); if (value != null) { System.setProperty(key, value); @@ -698,6 +803,12 @@ public class QpidTestCase extends TestCase System.clearProperty(key); } } + + _propertiesSetForTestOnly.clear(); + + // We don't change the current VMs settings for Broker only properties + // so we can just clear this map + _propertiesSetForBroker.clear(); } /** @@ -711,6 +822,40 @@ public class QpidTestCase extends TestCase _env.put(property, value); } + /** + * Adjust the VMs Log4j Settings just for this test run + * + * @param logger the logger to change + * @param level the level to set + */ + protected void setLoggerLevel(org.apache.log4j.Logger logger, Level level) + { + assertNotNull("Cannot set level of null logger", logger); + assertNotNull("Cannot set Logger("+logger.getName()+") to null level.",level); + + if (!_loggerLevelSetForTest.containsKey(logger)) + { + // Record the current value so we can revert it later. + _loggerLevelSetForTest.put(logger, logger.getLevel()); + } + + logger.setLevel(level); + } + + /** + * Restore the logging levels defined by this test. + */ + protected void revertLoggingLevels() + { + for (org.apache.log4j.Logger logger : _loggerLevelSetForTest.keySet()) + { + logger.setLevel(_loggerLevelSetForTest.get(logger)); + } + + _loggerLevelSetForTest.clear(); + + } + /** * Check whether the broker is an 0.8 * @@ -786,7 +931,7 @@ public class QpidTestCase extends TestCase { if (Boolean.getBoolean("profile.use_ssl")) { - _connectionFactory = getConnectionFactory("ssl"); + _connectionFactory = getConnectionFactory("default.ssl"); } else { @@ -876,15 +1021,32 @@ public class QpidTestCase extends TestCase return getClass().getSimpleName() + "-" + getName(); } + /** + * Return a Queue specific for this test. + * Uses getTestQueueName() as the name of the queue + * @return + */ + public Queue getTestQueue() + { + return new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, getTestQueueName()); + } + + protected void tearDown() throws java.lang.Exception { - // close all the connections used by this test. - for (Connection c : _connections) + try { - c.close(); + // close all the connections used by this test. + for (Connection c : _connections) + { + c.close(); + } + } + finally{ + // Ensure any problems with close does not interfer with property resets + revertSystemProperties(); + revertLoggingLevels(); } - - revertSystemProperties(); } /** @@ -921,17 +1083,23 @@ public class QpidTestCase extends TestCase public List sendMessage(Session session, Destination destination, int count) throws Exception { - return sendMessage(session, destination, count, 0); + return sendMessage(session, destination, count, 0, 0); } public List sendMessage(Session session, Destination destination, int count, int batchSize) throws Exception + { + return sendMessage(session, destination, count, 0, batchSize); + } + + public List sendMessage(Session session, Destination destination, + int count, int offset, int batchSize) throws Exception { List messages = new ArrayList(count); MessageProducer producer = session.createProducer(destination); - for (int i = 0; i < count; i++) + for (int i = offset; i < (count + offset); i++) { Message next = createNextMessage(session, i); @@ -950,8 +1118,11 @@ public class QpidTestCase extends TestCase } // Ensure we commit the last messages - if (session.getTransacted() && (batchSize > 0) && - (count / batchSize != 0)) + // Commit the session if we are transacted and + // we have no batchSize or + // our count is not divible by batchSize. + if (session.getTransacted() && + ( batchSize == 0 || count % batchSize != 0)) { session.commit(); } @@ -961,7 +1132,11 @@ public class QpidTestCase extends TestCase public Message createNextMessage(Session session, int msgCount) throws JMSException { - return session.createMessage(); + Message message = session.createMessage(); + message.setIntProperty(INDEX, msgCount); + + return message; + } public ConnectionURL getConnectionURL() throws NamingException diff --git a/qpid/java/test-profiles/010Excludes b/qpid/java/test-profiles/010Excludes old mode 100644 new mode 100755 index f03d62667d..757a1e425c --- a/qpid/java/test-profiles/010Excludes +++ b/qpid/java/test-profiles/010Excludes @@ -92,3 +92,12 @@ org.apache.qpid.test.unit.close.JavaServerCloseRaceConditionTest#* // QPID-2084 : this test needs more work for 0-10 org.apache.qpid.test.unit.client.DynamicQueueExchangeCreateTest#* + +// QPID-2118 : 0-10 Java client has differrent error handling to 0-8 code path +org.apache.qpid.test.client.message.SelectorTest#testRuntimeSelectorError + +//QPID-942 : Implemented Channel.Flow based Producer Side flow control to the Java Broker (not in CPP Broker) +org.apache.qpid.server.queue.ProducerFlowControlTest#* + +//QPID-1950 : Commit to test this failure. This is a MINA only failure so it cannot be tested when using 010. +org.apache.qpid.server.failover.MessageDisappearWithIOExceptionTest#* diff --git a/qpid/java/test-profiles/08Excludes b/qpid/java/test-profiles/08Excludes index b277c6d929..6f3898384d 100644 --- a/qpid/java/test-profiles/08Excludes +++ b/qpid/java/test-profiles/08Excludes @@ -14,8 +14,6 @@ org.apache.qpid.server.persistent.NoLocalAfterRecoveryTest#* // QPID-1823: this takes ages to run org.apache.qpid.client.SessionCreateTest#* -org.apache.qpid.test.client.RollbackOrderTest#* - // QPID-2097 exclude it from the InVM test runs until InVM JMX Interface is reliable org.apache.qpid.management.jmx.ManagementActorLoggingTest#* org.apache.qpid.server.queue.ModelTest#* diff --git a/qpid/java/test-profiles/08StandaloneExcludes b/qpid/java/test-profiles/08StandaloneExcludes index de876e06bb..ee781fb80f 100644 --- a/qpid/java/test-profiles/08StandaloneExcludes +++ b/qpid/java/test-profiles/08StandaloneExcludes @@ -39,8 +39,6 @@ org.apache.qpid.server.persistent.NoLocalAfterRecoveryTest#* // QPID-1823: this takes ages to run org.apache.qpid.client.SessionCreateTest#* -org.apache.qpid.test.client.RollbackOrderTest#* - // This test requires the standard configuration file for validation. // Excluding here does not reduce test coverage. org.apache.qpid.server.configuration.ServerConfigurationFileTest#* diff --git a/qpid/java/test-profiles/Excludes b/qpid/java/test-profiles/Excludes index a72d3bc86c..aa60554c04 100644 --- a/qpid/java/test-profiles/Excludes +++ b/qpid/java/test-profiles/Excludes @@ -19,3 +19,15 @@ org.apache.qpid.server.logging.DerbyMessageStoreLoggingTest#* // QPID-2081 :The configuration changes are now highlighting the close race condition org.apache.qpid.server.security.acl.SimpleACLTest#* + +// QPID-1816 : Client Ack has not been addressed +org.apache.qpid.test.unit.ack.AcknowledgeAfterFailoverOnMessageTest#testDirtyClientAck +org.apache.qpid.test.unit.ack.AcknowledgeAfterFailoverOnMessageTest#testClientAck +org.apache.qpid.test.unit.ack.AcknowledgeAfterFailoverTest#testDirtyClientAck +org.apache.qpid.test.unit.ack.AcknowledgeAfterFailoverTest#testClientAck + + +// QPID-143 : Failover can occur between receive and ack but we don't stop the ack. +org.apache.qpid.test.unit.ack.AcknowledgeAfterFailoverOnMessageTest#testAutoAck +org.apache.qpid.test.unit.ack.AcknowledgeAfterFailoverOnMessageTest#testDupsOk + diff --git a/qpid/java/test-profiles/default.testprofile b/qpid/java/test-profiles/default.testprofile index 86a5b2efb3..28127e5c4c 100644 --- a/qpid/java/test-profiles/default.testprofile +++ b/qpid/java/test-profiles/default.testprofile @@ -11,11 +11,14 @@ max_prefetch=1000 log=debug amqj.logging.level=${log} +amqj.server.logging.level=${log} amqj.protocol.logging.level=${log} root.logging.level=warn log4j.configuration=file:///${test.profiles}/log4j-test.xml log4j.debug=false +# Note test-provider.properties also has variables of same name. +# Keep in sync test.port=15672 test.mport=18999 #Note : Management will start open second port on: mport + 100 : 19099 diff --git a/qpid/java/test-profiles/log4j-test.xml b/qpid/java/test-profiles/log4j-test.xml index 0aaa7d8686..2d77942a81 100644 --- a/qpid/java/test-profiles/log4j-test.xml +++ b/qpid/java/test-profiles/log4j-test.xml @@ -29,10 +29,10 @@ + - @@ -46,6 +46,10 @@ + + + + diff --git a/qpid/java/test-profiles/test-provider.properties b/qpid/java/test-profiles/test-provider.properties index e5cb18da0b..8cea012c1d 100644 --- a/qpid/java/test-profiles/test-provider.properties +++ b/qpid/java/test-profiles/test-provider.properties @@ -19,20 +19,23 @@ # # -test.port=5672 -test.port.ssl=5671 -test.port.alt=5772 -test.port.alt.ssl=5771 +# Copied from default.testprofile +test.port=15672 +test.mport=18999 +#Note : Java Management will start open second port on: mport + 100 : 19099 +test.port.ssl=15671 +test.port.alt=25672 +test.port.alt.ssl=25671 + connectionfactory.default = amqp://username:password@clientid/test?brokerlist='tcp://localhost:${test.port}' +connectionfactory.default.ssl = amqp://username:password@clientid/test?brokerlist='tcp://localhost:${test.port.ssl}?ssl='true'' connectionfactory.default.vm = amqp://username:password@clientid/test?brokerlist='vm://:1' -connectionfactory.ssl = amqp://username:password@clientid/test?brokerlist='tcp://localhost:${test.port.ssl}?ssl='true'' connectionfactory.failover = amqp://username:password@clientid/test?brokerlist='tcp://localhost:${test.port.alt};tcp://localhost:${test.port}'&sync_ack='true'&sync_publish='all'&failover='roundrobin?cyclecount='20'' - connectionfactory.failover.ssl = amqp://username:password@clientid/test?brokerlist='tcp://localhost:${test.port.alt.ssl}?ssl='true';tcp://localhost:${test.port.ssl}?ssl='true''&sync_ack='true'&sync_publish='all'&failover='roundrobin?cyclecount='20'' +connectionfactory.failover.vm = amqp://username:password@clientid/test?brokerlist='vm://:2;vm://:1'&failover='roundrobin?cyclecount='20'' -connectionfactory.failover.vm = amqp://username:password@clientid/test?brokerlist='vm://:2;vm://:1' connectionfactory.connection1 = amqp://username:password@clientid/test?brokerlist='tcp://localhost:${test.port}' connectionfactory.connection2 = amqp://username:password@clientid/test?brokerlist='tcp://localhost:${test.port.alt}' connectionfactory.connection1.vm = amqp://username:password@clientid/test?brokerlist='vm://:1' diff --git a/qpid/python/Makefile b/qpid/python/Makefile index 31547c8f57..ff4a9af4f1 100644 --- a/qpid/python/Makefile +++ b/qpid/python/Makefile @@ -36,7 +36,7 @@ SRCS=$(shell find $(DIRS) -name "*.py") qpid_config.py BUILD=build TARGETS=$(SRCS:%.py=$(BUILD)/%.py) -PYCC=python -c "import compileall, sys; compileall.compile_dir(sys.argv[1])" +PYCC=python -O -c "import compileall; compileall.main()" all: build diff --git a/qpid/python/examples/api/drain b/qpid/python/examples/api/drain new file mode 100755 index 0000000000..485985f16d --- /dev/null +++ b/qpid/python/examples/api/drain @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT 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 optparse +from qpid.messaging import * +from qpid.util import URL + +parser = optparse.OptionParser(usage="usage: %prog [options] ADDRESS ...", + description="Drain messages from the supplied address.") +parser.add_option("-b", "--broker", default="localhost", + help="connect to specified BROKER (default %default)") +parser.add_option("-t", "--timeout", type=float, default=0, + help="timeout in seconds to wait before exiting (default %default)") +parser.add_option("-f", "--forever", action="store_true", + help="ignore timeout and wait forever") + +opts, args = parser.parse_args() + +url = URL(opts.broker) +if args: + addr = args.pop(0) +else: + parser.error("address is required") +if opts.forever: + timeout = None +else: + timeout = opts.timeout + +# XXX: should make URL default the port for us +conn = Connection.open(url.host, url.port or AMQP_PORT, + username=url.user, password=url.password) +ssn = conn.session() +rcv = ssn.receiver(addr) + +while True: + try: + print rcv.fetch(timeout=timeout) + ssn.acknowledge() + except Empty: + break + except ReceiveError, e: + print e + break + +conn.close() diff --git a/qpid/python/examples/api/ping b/qpid/python/examples/api/ping new file mode 100755 index 0000000000..59b367cca6 --- /dev/null +++ b/qpid/python/examples/api/ping @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT 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 optparse, time +from qpid.messaging import * +from qpid.util import URL + +parser = optparse.OptionParser(usage="usage: %prog [options] ADDRESS [ CONTENT ... ]", + description="Drain messages from the supplied address.") +parser.add_option("-b", "--broker", default="localhost", + help="connect to specified BROKER (default %default)") +parser.add_option("-c", "--count", type=int, default=1, + help="stop after count messages have been sent, zero disables (default %default)") +parser.add_option("-t", "--timeout", type=float, default=None, + help="exit after the specified time") +parser.add_option("-m", "--map", action="store_true", + help="interpret content as map") +parser.add_option("-i", "--id", help="use the supplied id instead of generating one") + +opts, args = parser.parse_args() + +url = URL(opts.broker) +if opts.id is None: + ping_id = str(uuid4()) +else: + ping_id = opts.id +if args: + addr = args.pop(0) +else: + parser.error("address is required") +if args: + content = " ".join(args) + if opts.map: + content = eval(content) +else: + content = None + +# XXX: should make URL default the port for us +conn = Connection.open(url.host, url.port or AMQP_PORT, + username=url.user, password=url.password) +ssn = conn.session() +snd = ssn.sender(addr) + +count = 0 +start = time.time() +while (opts.count == 0 or count < opts.count) and \ + (opts.timeout is None or time.time() - start < opts.timeout): + msg = Message(content) + msg.properties["ping-id"] = "%s:%s" % (ping_id, count) + + try: + snd.send(msg) + count += 1 + print msg + except SendError, e: + print e + break + +conn.close() diff --git a/qpid/python/qpid-python-test b/qpid/python/qpid-python-test index 528acaa124..b569020368 100755 --- a/qpid/python/qpid-python-test +++ b/qpid/python/qpid-python-test @@ -20,7 +20,7 @@ # TODO: summarize, test harness preconditions (e.g. broker is alive) -import fcntl, logging, optparse, os, struct, sys, termios, traceback, types +import logging, optparse, os, struct, sys, traceback, types from fnmatch import fnmatchcase as match from getopt import GetoptError from logging import getLogger, StreamHandler, Formatter, Filter, \ @@ -126,27 +126,33 @@ def is_included(path): def is_smart(): return sys.stdout.isatty() and os.environ.get("TERM", "dumb") != "dumb" -def width(): - if is_smart(): - s = struct.pack("HHHH", 0, 0, 0, 0) - fd_stdout = sys.stdout.fileno() - x = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, s) - rows, cols, xpx, ypx = struct.unpack("HHHH", x) - return cols - else: - try: - return int(os.environ.get("COLUMNS", "80")) - except ValueError: - return 80 +try: + import fcntl, termios -WIDTH = width() + def width(): + if is_smart(): + s = struct.pack("HHHH", 0, 0, 0, 0) + fd_stdout = sys.stdout.fileno() + x = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, s) + rows, cols, xpx, ypx = struct.unpack("HHHH", x) + return cols + else: + try: + return int(os.environ.get("COLUMNS", "80")) + except ValueError: + return 80 -def resize(sig, frm): - global WIDTH WIDTH = width() -import signal -signal.signal(signal.SIGWINCH, resize) + def resize(sig, frm): + global WIDTH + WIDTH = width() + + import signal + signal.signal(signal.SIGWINCH, resize) + +except ImportError: + WIDTH = 80 def vt100_attrs(*attrs): return "\x1B[%sm" % ";".join(map(str, attrs)) diff --git a/qpid/python/qpid/address.py b/qpid/python/qpid/address.py new file mode 100644 index 0000000000..5976d4889b --- /dev/null +++ b/qpid/python/qpid/address.py @@ -0,0 +1,171 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT 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 re + +TYPES = [] + +class Type: + + def __init__(self, name, pattern=None): + self.name = name + self.pattern = pattern + if self.pattern: + TYPES.append(self) + + def __repr__(self): + return self.name + +LBRACE = Type("LBRACE", r"\{") +RBRACE = Type("RBRACE", r"\}") +COLON = Type("COLON", r":") +COMMA = Type("COMMA", r",") +SLASH = Type("SLASH", r"/") +ID = Type("ID", r'[a-zA-Z_][a-zA-Z0-9_.-]*') +NUMBER = Type("NUMBER", r'[+-]?[0-9]*\.?[0-9]+') +STRING = Type("STRING", r""""(?:[^\\"]|\\.)*"|'(?:[^\\']|\\.)*'""") +WSPACE = Type("WSPACE", r"[ \n\r\t]+") +EOF = Type("EOF") + +class Token: + + def __init__(self, type, value): + self.type = type + self.value = value + + def __repr__(self): + return "%s: %r" % (self.type, self.value) + +joined = "|".join(["(%s)" % t.pattern for t in TYPES]) +LEXER = re.compile(joined) + +class LexError(Exception): + pass + +def line_info(st, pos): + idx = 0 + lineno = 1 + column = 0 + line_pos = 0 + while idx < pos: + if st[idx] == "\n": + lineno += 1 + column = 0 + line_pos = idx + column += 1 + idx += 1 + + end = st.find("\n", line_pos) + if end < 0: + end = len(st) + line = st[line_pos:end] + + return line, lineno, column + +def lex(st): + pos = 0 + while pos < len(st): + m = LEXER.match(st, pos) + if m is None: + line, ln, col = line_info(st, pos) + raise LexError("unrecognized character in :%s,%s: %s" % (ln, col, line)) + else: + idx = m.lastindex + t = Token(TYPES[idx - 1], m.group(idx)) + yield t + pos = m.end() + yield Token(EOF, None) + +class ParseError(Exception): pass + +class Parser: + + def __init__(self, tokens): + self.tokens = [t for t in tokens if t.type is not WSPACE] + self.idx = 0 + + def next(self): + return self.tokens[self.idx] + + def matches(self, *types): + return self.next().type in types + + def eat(self, *types): + if types and not self.matches(*types): + raise ParseError("expecting %s -- got %s" % (", ".join(map(str, types)), self.next())) + else: + t = self.next() + self.idx += 1 + return t + + def parse(self): + result = self.address() + self.eat(EOF) + return result + + def address(self): + name = self.eat(ID).value + subject = None + options = None + if self.matches(SLASH): + self.eat(SLASH) + if self.matches(ID): + subject = self.eat(ID).value + else: + subject = "" + elif self.matches(LBRACE): + options = self.map() + return name, subject, options + + def map(self): + self.eat(LBRACE) + result = {} + while True: + if self.matches(RBRACE): + self.eat(RBRACE) + break + else: + if self.matches(ID): + n, v = self.nameval() + result[n] = v + elif self.matches(COMMA): + self.eat(COMMA) + else: + raise ParseError("expecting (ID, COMMA), got %s" % self.next()) + return result + + def nameval(self): + name = self.eat(ID).value + self.eat(COLON) + val = self.value() + return (name, val) + + def value(self): + if self.matches(NUMBER, STRING): + return eval(self.eat().value) + elif self.matches(ID): + return self.eat().value + elif self.matches(LBRACE): + return self.map() + else: + raise ParseError("expecting (NUMBER, STRING, LBRACE) got %s" % self.next()) + +def parse(addr): + return Parser(lex(addr)).parse() + +__all__ = ["parse"] diff --git a/qpid/python/qpid/compat.py b/qpid/python/qpid/compat.py index 49273193df..53ab757e89 100644 --- a/qpid/python/qpid/compat.py +++ b/qpid/python/qpid/compat.py @@ -17,6 +17,8 @@ # under the License. # +import sys + try: set = set except NameError: @@ -30,6 +32,13 @@ except ImportError: try: from traceback import format_exc except ImportError: - import sys, traceback + import traceback def format_exc(): return "".join(traceback.format_exception(*sys.exc_info())) + +if tuple(sys.version_info[0:2]) < (2, 4): + from select import select as old_select + def select(rlist, wlist, xlist, timeout=None): + return old_select(list(rlist), list(wlist), list(xlist), timeout) +else: + from select import select diff --git a/qpid/python/qpid/driver.py b/qpid/python/qpid/driver.py index 2e07c82a0d..7c293fe146 100644 --- a/qpid/python/qpid/driver.py +++ b/qpid/python/qpid/driver.py @@ -17,25 +17,23 @@ # under the License. # -import compat, connection, socket, sys, time +import address, compat, connection, socket, struct, sys, time from concurrency import synchronized -from datatypes import RangedSet, Message as Message010 -from exceptions import Timeout +from datatypes import RangedSet, Serial +from exceptions import Timeout, VersionError +from framing import OpEncoder, SegmentEncoder, FrameEncoder, FrameDecoder, SegmentDecoder, OpDecoder from logging import getLogger from messaging import get_codec, ConnectError, Message, Pattern, UNLIMITED -from ops import delivery_mode -from session import Client, INCOMPLETE, SessionDetached +from ops import * +from selector import Selector from threading import Condition, Thread from util import connect log = getLogger("qpid.messaging") -def parse_addr(address): - parts = address.split("/", 1) - if len(parts) == 1: - return parts[0], None - else: - return parts[0], parts[i1] +def addr2reply_to(addr): + name, subject, options = address.parse(addr) + return ReplyTo(name, subject) def reply_to2addr(reply_to): if reply_to.routing_key is None: @@ -50,287 +48,617 @@ class Attachment: def __init__(self, target): self.target = target +# XXX + DURABLE_DEFAULT=True +# XXX + FILTER_DEFAULTS = { "topic": Pattern("*") } -def delegate(handler, session): - class Delegate(Client): - - def message_transfer(self, cmd): - return handler._message_transfer(session, cmd) - return Delegate +# XXX + +CLIENT_PROPERTIES = {"product": "qpid python client", + "version": "development", + "platform": os.name, + "qpid.client_process": os.path.basename(sys.argv[0]), + "qpid.client_pid": os.getpid(), + "qpid.client_ppid": os.getppid()} + +def noop(): pass + +class SessionState: + + def __init__(self, driver, session, name, channel): + self.driver = driver + self.session = session + self.name = name + self.channel = channel + self.detached = False + self.committing = False + self.aborting = False + + # sender state + self.sent = Serial(0) + self.acknowledged = RangedSet() + self.completions = {} + self.min_completion = self.sent + self.max_completion = self.sent + self.results = {} + + # receiver state + self.received = None + self.executed = RangedSet() + + # XXX: need to periodically exchange completion/known_completion + + def write_query(self, query, handler): + id = self.sent + query.sync = True + self.write_cmd(query, lambda: handler(self.results.pop(id))) + + def write_cmd(self, cmd, completion=noop): + if self.detached: + raise Exception("detached") + cmd.id = self.sent + self.sent += 1 + self.completions[cmd.id] = completion + self.max_completion = cmd.id + self.write_op(cmd) + + def write_op(self, op): + op.channel = self.channel + self.driver.write_op(op) + +# XXX +HEADER="!4s4B" + +EMPTY_DP = DeliveryProperties() +EMPTY_MP = MessageProperties() class Driver: def __init__(self, connection): self.connection = connection self._lock = self.connection._lock - self._wakeup_cond = Condition() - self._socket = None - self._conn = None + + self._selector = Selector.default() + self.reset() + + def reset(self): + self._opening = False + self._closing = False self._connected = False self._attachments = {} - self._modcount = self.connection._modcount - self.thread = Thread(target=self.run) - self.thread.setDaemon(True) - # XXX: need to figure out how to join on this thread + self._channel_max = 65536 + self._channels = 0 + self._sessions = {} + + self._socket = None + self._buf = "" + self._hdr = "" + self._op_enc = OpEncoder() + self._seg_enc = SegmentEncoder() + self._frame_enc = FrameEncoder() + self._frame_dec = FrameDecoder() + self._seg_dec = SegmentDecoder() + self._op_dec = OpDecoder() + self._timeout = None + + for ssn in self.connection.sessions.values(): + for m in ssn.acked + ssn.unacked + ssn.incoming: + m._transfer_id = None + for snd in ssn.senders: + snd.linked = False + for rcv in ssn.receivers: + rcv.impending = rcv.received + rcv.linked = False + + @synchronized def wakeup(self): - self._wakeup_cond.acquire() - try: - self._wakeup_cond.notifyAll() - finally: - self._wakeup_cond.release() + self.dispatch() + self._selector.wakeup() def start(self): - self.thread.start() + self._selector.register(self) + + def fileno(self): + return self._socket.fileno() - def run(self): - while True: - self._wakeup_cond.acquire() + @synchronized + def reading(self): + return self._socket is not None + + @synchronized + def writing(self): + return self._socket is not None and self._buf + + @synchronized + def timing(self): + return self._timeout + + @synchronized + def readable(self): + error = None + recoverable = False + try: + data = self._socket.recv(64*1024) + if data: + log.debug("READ: %r", data) + else: + log.debug("ABORTED: %s", self._socket.getpeername()) + error = "connection aborted" + recoverable = True + except socket.error, e: + error = e + recoverable = True + + if not error: try: - if self.connection._modcount <= self._modcount: - self._wakeup_cond.wait(10) - finally: - self._wakeup_cond.release() - self.dispatch(self.connection._modcount) + if len(self._hdr) < 8: + r = 8 - len(self._hdr) + self._hdr += data[:r] + data = data[r:] + + if len(self._hdr) == 8: + self.do_header(self._hdr) + + self._frame_dec.write(data) + self._seg_dec.write(*self._frame_dec.read()) + self._op_dec.write(*self._seg_dec.read()) + for op in self._op_dec.read(): + self.assign_id(op) + log.debug("RCVD: %r", op) + op.dispatch(self) + except VersionError, e: + error = e + except: + msg = compat.format_exc() + error = msg + + if error: + self._error(error, recoverable) + else: + self.dispatch() + + self.connection._waiter.notifyAll() + + def assign_id(self, op): + if isinstance(op, Command): + sst = self.get_sst(op) + op.id = sst.received + sst.received += 1 @synchronized - def dispatch(self, modcount): + def writeable(self): try: - if self._conn is None and self.connection._connected: + n = self._socket.send(self._buf) + log.debug("SENT: %r", self._buf[:n]) + self._buf = self._buf[n:] + except socket.error, e: + self._error(e, True) + self.connection._waiter.notifyAll() + + @synchronized + def timeout(self): + log.warn("retrying ...") + self.dispatch() + self.connection._waiter.notifyAll() + + def _error(self, err, recoverable): + if self._socket is not None: + self._socket.close() + self.reset() + if recoverable and self.connection.reconnect: + self._timeout = time.time() + 3 + log.warn("recoverable error: %s" % err) + log.warn("sleeping 3 seconds") + else: + self.connection.error = (err,) + + def write_op(self, op): + log.debug("SENT: %r", op) + self._op_enc.write(op) + self._seg_enc.write(*self._op_enc.read()) + self._frame_enc.write(*self._seg_enc.read()) + self._buf += self._frame_enc.read() + + def do_header(self, hdr): + cli_major = 0; cli_minor = 10 + magic, _, _, major, minor = struct.unpack(HEADER, hdr) + if major != cli_major or minor != cli_minor: + raise VersionError("client: %s-%s, server: %s-%s" % + (cli_major, cli_minor, major, minor)) + + def do_connection_start(self, start): + # XXX: should we use some sort of callback for this? + r = "\0%s\0%s" % (self.connection.username, self.connection.password) + m = self.connection.mechanism + self.write_op(ConnectionStartOk(client_properties=CLIENT_PROPERTIES, + mechanism=m, response=r)) + + def do_connection_tune(self, tune): + # XXX: is heartbeat protocol specific? + if tune.channel_max is not None: + self.channel_max = tune.channel_max + self.write_op(ConnectionTuneOk(heartbeat=self.connection.heartbeat, + channel_max=self.channel_max)) + self.write_op(ConnectionOpen()) + + def do_connection_open_ok(self, open_ok): + self._connected = True + + def connection_heartbeat(self, hrt): + self.write_op(ConnectionHeartbeat()) + + def do_connection_close(self, close): + self.write_op(ConnectionCloseOk()) + if close.reply_code != close_code.normal: + self.connection.error = (close.reply_code, close.reply_text) + # XXX: should we do a half shutdown on the socket here? + # XXX: we really need to test this, we may end up reporting a + # connection abort after this, if we were to do a shutdown on read + # and stop reading, then we wouldn't report the abort, that's + # probably the right thing to do + + def do_connection_close_ok(self, close_ok): + self._socket.close() + self.reset() + + def do_session_attached(self, atc): + pass + + def do_session_command_point(self, cp): + sst = self.get_sst(cp) + sst.received = cp.command_id + + def do_session_completed(self, sc): + sst = self.get_sst(sc) + for r in sc.commands: + sst.acknowledged.add(r.lower, r.upper) + + if not sc.commands.empty(): + while sst.min_completion in sc.commands: + if sst.completions.has_key(sst.min_completion): + sst.completions.pop(sst.min_completion)() + sst.min_completion += 1 + + def session_known_completed(self, kcmp): + sst = self.get_sst(kcmp) + executed = RangedSet() + for e in sst.executed.ranges: + for ke in kcmp.ranges: + if e.lower in ke and e.upper in ke: + break + else: + executed.add_range(e) + sst.executed = completed + + def do_session_flush(self, sf): + sst = self.get_sst(sf) + if sf.expected: + if sst.received is None: + exp = None + else: + exp = RangedSet(sst.received) + sst.write_op(SessionExpected(exp)) + if sf.confirmed: + sst.write_op(SessionConfirmed(sst.executed)) + if sf.completed: + sst.write_op(SessionCompleted(sst.executed)) + + def do_execution_result(self, er): + sst = self.get_sst(er) + sst.results[er.command_id] = er.value + + def do_execution_exception(self, ex): + sst = self.get_sst(ex) + sst.session.error = (ex,) + + def dispatch(self): + try: + if self._socket is None and self.connection._connected and not self._opening: self.connect() - elif self._conn is not None and not self.connection._connected: + elif self._socket is not None and not self.connection._connected and not self._closing: self.disconnect() - if self._conn is not None: + if self._connected and not self._closing: for ssn in self.connection.sessions.values(): self.attach(ssn) self.process(ssn) - - exi = None except: - exi = sys.exc_info() - - if exi: msg = compat.format_exc() - recoverable = ["aborted", "Connection refused", "SessionDetached", "Connection reset by peer", - "Bad file descriptor", "start timed out", "Broken pipe"] - for r in recoverable: - if self.connection.reconnect and r in msg: - print "waiting to retry" - self.reset() - time.sleep(3) - print "retrying..." - return - else: - self.connection.error = (msg,) - - self._modcount = modcount - self.connection._waiter.notifyAll() + self.connection.error = (msg,) def connect(self): - if self._conn is not None: - return try: + # XXX: should make this non blocking self._socket = connect(self.connection.host, self.connection.port) + self._timeout = None except socket.error, e: - raise ConnectError(e) - self._conn = connection.Connection(self._socket) - try: - self._conn.start(timeout=10) - self._connected = True - except connection.VersionError, e: - raise ConnectError(e) - except Timeout: - print "start timed out" - raise ConnectError("start timed out") + if self.connection.reconnect: + self._error(e, True) + return + else: + raise e + self._buf += struct.pack(HEADER, "AMQP", 1, 1, 0, 10) + self._opening = True def disconnect(self): - self._conn.close() - self.reset() - - def reset(self): - self._conn = None - self._connected = False - self._attachments.clear() - for ssn in self.connection.sessions.values(): - for m in ssn.acked + ssn.unacked + ssn.incoming: - m._transfer_id = None - for rcv in ssn.receivers: - rcv.impending = rcv.received - - def connected(self): - return self._conn is not None + self.write_op(ConnectionClose(close_code.normal)) + self._closing = True def attach(self, ssn): - _ssn = self._attachments.get(ssn) - if _ssn is None: - _ssn = self._conn.session(ssn.name, delegate=delegate(self, ssn)) - _ssn.auto_sync = False - _ssn.invoke_lock = self._lock - _ssn.lock = self._lock - _ssn.condition = self.connection._condition + sst = self._attachments.get(ssn) + if sst is None and not ssn.closed: + for i in xrange(0, self.channel_max): + if not self._sessions.has_key(i): + ch = i + break + else: + raise RuntimeError("all channels used") + sst = SessionState(self, ssn, ssn.name, ch) + sst.write_op(SessionAttach(name=ssn.name)) + sst.write_op(SessionCommandPoint(sst.sent, 0)) + sst.outgoing_idx = 0 + sst.acked = [] if ssn.transactional: - # XXX: adding an attribute to qpid.session.Session - _ssn.acked = [] - _ssn.tx_select() - self._attachments[ssn] = _ssn + sst.write_cmd(TxSelect()) + self._attachments[ssn] = sst + self._sessions[sst.channel] = sst for snd in ssn.senders: self.link_out(snd) for rcv in ssn.receivers: self.link_in(rcv) - if ssn.closing: - _ssn.close() - del self._attachments[ssn] - ssn.closed = True + if sst is not None and ssn.closing and not sst.detached: + sst.detached = True + sst.write_op(SessionDetach(name=ssn.name)) + + def get_sst(self, op): + return self._sessions[op.channel] - def _exchange_query(self, ssn, address): - # XXX: auto sync hack is to avoid deadlock on future - result = ssn.exchange_query(name=address, sync=True) - ssn.sync() - return result.get() + def do_session_detached(self, dtc): + sst = self._sessions.pop(dtc.channel) + ssn = sst.session + del self._attachments[ssn] + ssn.closed = True + + def do_session_detach(self, dtc): + sst = self.get_sst(dtc) + sst.write_op(SessionDetached(name=dtc.name)) + self.do_session_detached(dtc) def link_out(self, snd): - _ssn = self._attachments[snd.session] + sst = self._attachments.get(snd.session) _snd = self._attachments.get(snd) - if _snd is None: + if _snd is None and not snd.closing and not snd.closed: _snd = Attachment(snd) - node, _snd._subject = parse_addr(snd.target) - result = self._exchange_query(_ssn, node) - if result.not_found: - # XXX: should check 'create' option - _ssn.queue_declare(queue=node, durable=DURABLE_DEFAULT, sync=True) - _ssn.sync() - _snd._exchange = "" - _snd._routing_key = node - else: - _snd._exchange = node - _snd._routing_key = _snd._subject + + try: + _snd.name, _snd.subject, _snd.options = address.parse(snd.target) + except address.LexError, e: + snd.error = e + snd.closed = True + return + except address.ParseError, e: + snd.error = e + snd.closed = True + return + + # XXX: subject + if _snd.options is None: + _snd.options = {} + + def do_link(): + snd.linked = True + + def do_queue_q(result): + if sst.detached: + return + + if result.queue: + _snd._exchange = "" + _snd._routing_key = _snd.name + do_link() + else: + snd.error = ("no such queue: %s" % _snd.name,) + del self._attachments[snd] + snd.closed = True + + def do_exchange_q(result): + if sst.detached: + return + + if result.not_found: + if _snd.options.get("create") in ("always", "receiver"): + sst.write_cmd(QueueDeclare(queue=_snd.name, durable=DURABLE_DEFAULT)) + _snd._exchange = "" + _snd._routing_key = _snd.name + else: + sst.write_query(QueueQuery(queue=_snd.name), do_queue_q) + return + else: + _snd._exchange = _snd.name + _snd._routing_key = _snd.subject + do_link() + + sst.write_query(ExchangeQuery(name=_snd.name), do_exchange_q) self._attachments[snd] = _snd - if snd.closed: + if snd.closing and not snd.closed: del self._attachments[snd] - return None - else: - return _snd + snd.closed = True def link_in(self, rcv): - _ssn = self._attachments[rcv.session] + sst = self._attachments.get(rcv.session) _rcv = self._attachments.get(rcv) - if _rcv is None: + if _rcv is None and not rcv.closing and not rcv.closed: _rcv = Attachment(rcv) - result = self._exchange_query(_ssn, rcv.source) - if result.not_found: - _rcv._queue = rcv.source - # XXX: should check 'create' option - _ssn.queue_declare(queue=_rcv._queue, durable=DURABLE_DEFAULT) - else: - _rcv._queue = "%s.%s" % (rcv.session.name, rcv.destination) - _ssn.queue_declare(queue=_rcv._queue, durable=DURABLE_DEFAULT, exclusive=True, auto_delete=True) - if rcv.filter is None: - f = FILTER_DEFAULTS[result.type] + _rcv.canceled = False + _rcv.draining = False + + try: + _rcv.name, _rcv.subject, _rcv.options = address.parse(rcv.source) + except address.LexError, e: + rcv.error = e + rcv.closed = True + return + except address.ParseError, e: + rcv.error = e + rcv.closed = True + return + + # XXX: subject + if _rcv.options is None: + _rcv.options = {} + + def do_link(): + sst.write_cmd(MessageSubscribe(queue=_rcv._queue, destination=rcv.destination)) + sst.write_cmd(MessageSetFlowMode(rcv.destination, flow_mode.credit)) + rcv.linked = True + + def do_queue_q(result): + if sst.detached: + return + if result.queue: + _rcv._queue = _rcv.name + do_link() + else: + rcv.error = ("no such queue: %s" % _rcv.name,) + del self._attachments[rcv] + rcv.closed = True + + def do_exchange_q(result): + if sst.detached: + return + if result.not_found: + if _rcv.options.get("create") in ("always", "receiver"): + _rcv._queue = _rcv.name + sst.write_cmd(QueueDeclare(queue=_rcv._queue, durable=DURABLE_DEFAULT)) + else: + sst.write_query(QueueQuery(queue=_rcv.name), do_queue_q) + return else: - f = rcv.filter - f._bind(_ssn, rcv.source, _rcv._queue) - _ssn.message_subscribe(queue=_rcv._queue, destination=rcv.destination) - _ssn.message_set_flow_mode(rcv.destination, _ssn.flow_mode.credit, sync=True) + _rcv._queue = "%s.%s" % (rcv.session.name, rcv.destination) + sst.write_cmd(QueueDeclare(queue=_rcv._queue, durable=DURABLE_DEFAULT, exclusive=True, auto_delete=True)) + filter = _rcv.options.get("filter") + if _rcv.subject is None and filter is None: + f = FILTER_DEFAULTS[result.type] + elif _rcv.subject and filter: + # XXX + raise Exception("can't supply both subject and filter") + elif _rcv.subject: + # XXX + from messaging import Pattern + f = Pattern(_rcv.subject) + else: + f = filter + f._bind(sst, _rcv.name, _rcv._queue) + do_link() + sst.write_query(ExchangeQuery(name=_rcv.name), do_exchange_q) self._attachments[rcv] = _rcv - # XXX: need to kill syncs - _ssn.sync() - - if rcv.closing: - _ssn.message_cancel(rcv.destination, sync=True) - # XXX: need to kill syncs - _ssn.sync() - del self._attachments[rcv] - rcv.closed = True - return None - else: - return _rcv + + if rcv.closing and not rcv.closed: + if rcv.linked: + if not _rcv.canceled: + def close_rcv(): + del self._attachments[rcv] + rcv.closed = True + sst.write_cmd(MessageCancel(rcv.destination, sync=True), close_rcv) + _rcv.canceled = True + else: + rcv.closed = True def process(self, ssn): if ssn.closing: return - _ssn = self._attachments[ssn] + sst = self._attachments[ssn] - while ssn.outgoing: - msg = ssn.outgoing[0] + while sst.outgoing_idx < len(ssn.outgoing): + msg = ssn.outgoing[sst.outgoing_idx] snd = msg._sender - self.send(snd, msg) - ssn.outgoing.pop(0) + # XXX: should check for sender error here + _snd = self._attachments.get(snd) + if _snd and snd.linked: + self.send(snd, msg) + sst.outgoing_idx += 1 + else: + break for rcv in ssn.receivers: self.process_receiver(rcv) if ssn.acked: - messages = ssn.acked[:] - ids = RangedSet(*[m._transfer_id for m in messages if m._transfer_id is not None]) - for range in ids: - _ssn.receiver._completed.add_range(range) - ch = _ssn.channel - if ch is None: - raise SessionDetached() - ch.session_completed(_ssn.receiver._completed) - _ssn.message_accept(ids, sync=True) - # XXX: really need to make this async so that we don't give up the lock - _ssn.sync() - - # XXX: we're ignoring acks that get lost when disconnected - for m in messages: - ssn.acked.remove(m) - if ssn.transactional: - _ssn.acked.append(m) - - if ssn.committing: - _ssn.tx_commit(sync=True) - # XXX: need to kill syncs - _ssn.sync() - del _ssn.acked[:] - ssn.committing = False - ssn.committed = True - ssn.aborting = False - ssn.aborted = False - - if ssn.aborting: - for rcv in ssn.receivers: - _ssn.message_stop(rcv.destination) - _ssn.sync() - - messages = _ssn.acked + ssn.unacked + ssn.incoming - ids = RangedSet(*[m._transfer_id for m in messages]) - for range in ids: - _ssn.receiver._completed.add_range(range) - _ssn.channel.session_completed(_ssn.receiver._completed) - _ssn.message_release(ids) - _ssn.tx_rollback(sync=True) - _ssn.sync() - - del ssn.incoming[:] - del ssn.unacked[:] - del _ssn.acked[:] + messages = [m for m in ssn.acked if m not in sst.acked] + if messages: + # XXX: we're ignoring acks that get lost when disconnected, + # could we deal this via some message-id based purge? + ids = RangedSet(*[m._transfer_id for m in messages if m._transfer_id is not None]) + for range in ids: + sst.executed.add_range(range) + sst.write_op(SessionCompleted(sst.executed)) + def ack_ack(): + for m in messages: + ssn.acked.remove(m) + if not ssn.transactional: + sst.acked.remove(m) + sst.write_cmd(MessageAccept(ids, sync=True), ack_ack) + sst.acked.extend(messages) + + if ssn.committing and not sst.committing: + def commit_ok(): + del sst.acked[:] + ssn.committing = False + ssn.committed = True + ssn.aborting = False + ssn.aborted = False + sst.write_cmd(TxCommit(sync=True), commit_ok) + sst.committing = True + + if ssn.aborting and not sst.aborting: + sst.aborting = True + def do_rb(): + messages = sst.acked + ssn.unacked + ssn.incoming + ids = RangedSet(*[m._transfer_id for m in messages]) + for range in ids: + sst.executed.add_range(range) + sst.write_op(SessionCompleted(sst.executed)) + sst.write_cmd(MessageRelease(ids)) + sst.write_cmd(TxRollback(sync=True), do_rb_ok) + + def do_rb_ok(): + del ssn.incoming[:] + del ssn.unacked[:] + del sst.acked[:] + + for rcv in ssn.receivers: + rcv.impending = rcv.received + rcv.returned = rcv.received + # XXX: do we need to update granted here as well? + + for rcv in ssn.receivers: + self.process_receiver(rcv) + + ssn.aborting = False + ssn.aborted = True + ssn.committing = False + ssn.committed = False + sst.aborting = False for rcv in ssn.receivers: - rcv.impending = rcv.received - rcv.returned = rcv.received - # XXX: do we need to update granted here as well? - - for rcv in ssn.receivers: - self.process_receiver(rcv) - - ssn.aborting = False - ssn.aborted = True - ssn.committing = False - ssn.committed = False + sst.write_cmd(MessageStop(rcv.destination)) + sst.write_cmd(ExecutionSync(sync=True), do_rb) def grant(self, rcv): - _ssn = self._attachments[rcv.session] - _rcv = self.link_in(rcv) + sst = self._attachments[rcv.session] + _rcv = self._attachments.get(rcv) + if _rcv is None or not rcv.linked or _rcv.canceled or _rcv.draining: + return if rcv.granted is UNLIMITED: if rcv.impending is UNLIMITED: @@ -343,30 +671,37 @@ class Driver: delta = max(rcv.granted, rcv.received) - rcv.impending if delta is UNLIMITED: - _ssn.message_flow(rcv.destination, _ssn.credit_unit.byte, UNLIMITED.value) - _ssn.message_flow(rcv.destination, _ssn.credit_unit.message, UNLIMITED.value) + sst.write_cmd(MessageFlow(rcv.destination, credit_unit.byte, UNLIMITED.value)) + sst.write_cmd(MessageFlow(rcv.destination, credit_unit.message, UNLIMITED.value)) rcv.impending = UNLIMITED elif delta > 0: - _ssn.message_flow(rcv.destination, _ssn.credit_unit.byte, UNLIMITED.value) - _ssn.message_flow(rcv.destination, _ssn.credit_unit.message, delta) + sst.write_cmd(MessageFlow(rcv.destination, credit_unit.byte, UNLIMITED.value)) + sst.write_cmd(MessageFlow(rcv.destination, credit_unit.message, delta)) rcv.impending += delta - elif delta < 0: - if rcv.drain: - _ssn.message_flush(rcv.destination, sync=True) - else: - _ssn.message_stop(rcv.destination, sync=True) - # XXX: need to kill syncs - _ssn.sync() - rcv.impending = rcv.received - self.grant(rcv) + elif delta < 0 and not rcv.draining: + _rcv.draining = True + def do_stop(): + rcv.impending = rcv.received + _rcv.draining = False + self.grant(rcv) + sst.write_cmd(MessageStop(rcv.destination, sync=True), do_stop) + + if rcv.draining: + def do_flush(): + rcv.impending = rcv.received + rcv.granted = rcv.impending + _rcv.draining = False + rcv.draining = False + sst.write_cmd(MessageFlush(rcv.destination, sync=True), do_flush) + def process_receiver(self, rcv): if rcv.closed: return self.grant(rcv) def send(self, snd, msg): - _ssn = self._attachments[snd.session] - _snd = self.link_out(snd) + sst = self._attachments[snd.session] + _snd = self._attachments[snd] # XXX: what if subject is specified for a normal queue? if _snd._routing_key is None: @@ -375,16 +710,16 @@ class Driver: rk = _snd._routing_key # XXX: do we need to query to figure out how to create the reply-to interoperably? if msg.reply_to: - rt = _ssn.reply_to(*parse_addr(msg.reply_to)) + rt = addr2reply_to(msg.reply_to) else: rt = None - dp = _ssn.delivery_properties(routing_key=rk) - mp = _ssn.message_properties(message_id=msg.id, - user_id=msg.user_id, - reply_to=rt, - correlation_id=msg.correlation_id, - content_type=msg.content_type, - application_headers=msg.properties) + dp = DeliveryProperties(routing_key=rk) + mp = MessageProperties(message_id=msg.id, + user_id=msg.user_id, + reply_to=rt, + correlation_id=msg.correlation_id, + content_type=msg.content_type, + application_headers=msg.properties) if msg.subject is not None: if mp.application_headers is None: mp.application_headers = {} @@ -397,37 +732,42 @@ class Driver: dp.delivery_mode = delivery_mode.persistent enc, dec = get_codec(msg.content_type) body = enc(msg.content) - _ssn.message_transfer(destination=_snd._exchange, - message=Message010(dp, mp, body), - sync=True) - log.debug("SENT [%s] %s", snd.session, msg) - # XXX: really need to make this async so that we don't give up the lock - _ssn.sync() - # XXX: should we log the ack somehow too? - snd.acked += 1 - - @synchronized - def _message_transfer(self, ssn, cmd): - m = Message010(cmd.payload) - m.headers = cmd.headers - m.id = cmd.id - msg = self._decode(m) - rcv = ssn.receivers[int(cmd.destination)] + def msg_acked(): + # XXX: should we log the ack somehow too? + snd.acked += 1 + m = snd.session.outgoing.pop(0) + sst.outgoing_idx -= 1 + assert msg == m + sst.write_cmd(MessageTransfer(destination=_snd._exchange, headers=(dp, mp), + payload=body, sync=True), msg_acked) + + def do_message_transfer(self, xfr): + sst = self.get_sst(xfr) + ssn = sst.session + + msg = self._decode(xfr) + rcv = ssn.receivers[int(xfr.destination)] msg._receiver = rcv if rcv.impending is not UNLIMITED: - assert rcv.received < rcv.impending + assert rcv.received < rcv.impending, "%s, %s" % (rcv.received, rcv.impending) rcv.received += 1 log.debug("RECV [%s] %s", ssn, msg) ssn.incoming.append(msg) self.connection._waiter.notifyAll() - return INCOMPLETE - def _decode(self, message): - dp = message.get("delivery_properties") - mp = message.get("message_properties") + def _decode(self, xfr): + dp = EMPTY_DP + mp = EMPTY_MP + + for h in xfr.headers: + if isinstance(h, DeliveryProperties): + dp = h + elif isinstance(h, MessageProperties): + mp = h + ap = mp.application_headers enc, dec = get_codec(mp.content_type) - content = dec(message.body) + content = dec(xfr.payload) msg = Message(content) msg.id = mp.message_id if ap is not None: @@ -440,5 +780,5 @@ class Driver: msg.durable = dp.delivery_mode == delivery_mode.persistent msg.properties = mp.application_headers msg.content_type = mp.content_type - msg._transfer_id = message.id + msg._transfer_id = xfr.id return msg diff --git a/qpid/python/qpid/messaging.py b/qpid/python/qpid/messaging.py index d755aa5054..3e3c8f36cb 100644 --- a/qpid/python/qpid/messaging.py +++ b/qpid/python/qpid/messaging.py @@ -77,7 +77,8 @@ class Connection: """ @static - def open(host, port=None): + def open(host, port=None, username="guest", password="guest", + mechanism="PLAIN", heartbeat=None, **options): """ Creates an AMQP connection and connects it to the given host and port. @@ -88,11 +89,12 @@ class Connection: @rtype: Connection @return: a connected Connection """ - conn = Connection(host, port) + conn = Connection(host, port, username, password, mechanism, heartbeat, **options) conn.connect() return conn - def __init__(self, host, port=None): + def __init__(self, host, port=None, username="guest", password="guest", + mechanism="PLAIN", heartbeat=None, **options): """ Creates a connection. A newly created connection must be connected with the Connection.connect() method before it can be started. @@ -106,11 +108,16 @@ class Connection: """ self.host = host self.port = default(port, AMQP_PORT) + self.username = username + self.password = password + self.mechanism = mechanism + self.heartbeat = heartbeat + self.started = False self.id = str(uuid4()) self.session_counter = 0 self.sessions = {} - self.reconnect = False + self.reconnect = options.get("reconnect", False) self._connected = False self._lock = RLock() self._condition = Condition(self._lock) @@ -230,9 +237,10 @@ class Pattern: self.value = value # XXX: this should become part of the driver - def _bind(self, ssn, exchange, queue): - ssn.exchange_bind(exchange=exchange, queue=queue, - binding_key=self.value.replace("*", "#")) + def _bind(self, sst, exchange, queue): + from qpid.ops import ExchangeBind + sst.write_cmd(ExchangeBind(exchange=exchange, queue=queue, + binding_key=self.value.replace("*", "#"))) class SessionError(Exception): pass @@ -282,6 +290,7 @@ class Session: # XXX: I hate this name. self.ack_capacity = UNLIMITED + self.error = None self.closing = False self.closed = False @@ -302,12 +311,16 @@ class Session: def _check_error(self, exc=SessionError): self.connection._check_error(exc) + if self.error: + raise exc(*self.error) def _ewait(self, predicate, timeout=None, exc=SessionError): - return self.connection._ewait(predicate, timeout, exc) + result = self.connection._ewait(lambda: self.error or predicate(), timeout, exc) + self._check_error(exc) + return result @synchronized - def sender(self, target): + def sender(self, target, **options): """ Creates a L{Sender} that may be used to send L{Messages} to the specified target. @@ -317,7 +330,7 @@ class Session: @rtype: Sender @return: a new Sender for the specified target """ - sender = Sender(self, len(self.senders), target) + sender = Sender(self, len(self.senders), target, options) self.senders.append(sender) self._wakeup() # XXX: because of the lack of waiting here we can end up getting @@ -327,7 +340,7 @@ class Session: return sender @synchronized - def receiver(self, source, filter=None): + def receiver(self, source, **options): """ Creates a receiver that may be used to actively fetch or to listen for the arrival of L{Messages} from the specified source. @@ -337,7 +350,7 @@ class Session: @rtype: Receiver @return: a new Receiver for the specified source """ - receiver = Receiver(self, len(self.receivers), source, filter, + receiver = Receiver(self, len(self.receivers), source, options, self.started) self.receivers.append(receiver) self._wakeup() @@ -368,8 +381,8 @@ class Session: @synchronized def _get(self, predicate, timeout=None): - if self._wait(lambda: ((self._peek(predicate) is not None) or self.closing), - timeout): + if self._ewait(lambda: ((self._peek(predicate) is not None) or self.closing), + timeout): msg = self._pop(predicate) if msg is not None: msg._receiver.returned += 1 @@ -505,13 +518,18 @@ class Sender: Sends outgoing messages. """ - def __init__(self, session, index, target): + def __init__(self, session, index, target, options): self.session = session self.index = index self.target = target - self.capacity = UNLIMITED + self.options = options + self.capacity = options.get("capacity", UNLIMITED) + self.durable = options.get("durable") self.queued = Serial(0) self.acked = Serial(0) + self.error = None + self.linked = False + self.closing = False self.closed = False self._lock = self.session._lock @@ -520,9 +538,13 @@ class Sender: def _check_error(self, exc=SendError): self.session._check_error(exc) + if self.error: + raise exc(*self.error) def _ewait(self, predicate, timeout=None, exc=SendError): - return self.session._ewait(predicate, timeout, exc) + result = self.session._ewait(lambda: self.error or predicate(), timeout, exc) + self._check_error(exc) + return result @synchronized def pending(self): @@ -558,11 +580,16 @@ class Sender: if not self.session.connection._connected or self.session.closing: raise Disconnected() + self._ewait(lambda: self.linked) + if isinstance(object, Message): message = object else: message = Message(object) + if message.durable is None: + message.durable = self.durable + if self.capacity is not UNLIMITED: if self.capacity <= 0: raise InsufficientCapacity("capacity = %s" % self.capacity) @@ -573,14 +600,18 @@ class Sender: message._sender = self self.session.outgoing.append(message) self.queued += 1 - mno = self.queued self._wakeup() if sync: - self._ewait(lambda: self.acked >= mno) + self.sync() assert message not in self.session.outgoing + @synchronized + def sync(self): + mno = self.queued + self._ewait(lambda: self.acked >= mno) + @synchronized def close(self): """ @@ -609,21 +640,23 @@ class Receiver: L{listen}. """ - def __init__(self, session, index, source, filter, started): + def __init__(self, session, index, source, options, started): self.session = session self.index = index self.destination = str(self.index) self.source = source - self.filter = filter + self.options = options self.started = started - self.capacity = UNLIMITED + self.capacity = options.get("capacity", UNLIMITED) self.granted = Serial(0) - self.drain = False + self.draining = False self.impending = Serial(0) self.received = Serial(0) self.returned = Serial(0) + self.error = None + self.linked = False self.closing = False self.closed = False self.listener = None @@ -634,9 +667,13 @@ class Receiver: def _check_error(self, exc=ReceiveError): self.session._check_error(exc) + if self.error: + raise exc(*self.error) def _ewait(self, predicate, timeout=None, exc=ReceiveError): - return self.session._ewait(predicate, timeout, exc) + result = self.session._ewait(lambda: self.error or predicate(), timeout, exc) + self._check_error(exc) + return result @synchronized def pending(self): @@ -680,17 +717,18 @@ class Receiver: @type timeout: float @param timeout: the time to wait for a message to be available """ + + self._ewait(lambda: self.linked) + if self._capacity() == 0: self.granted = self.returned + 1 self._wakeup() self._ewait(lambda: self.impending >= self.granted) msg = self.session._get(self._pred, timeout=timeout) if msg is None: - self.drain = True - self.granted = self.received + self.draining = True self._wakeup() - self._ewait(lambda: self.impending == self.received) - self.drain = False + self._ewait(lambda: not self.draining) self._grant() self._wakeup() msg = self.session._get(self._pred, timeout=0) @@ -738,7 +776,7 @@ class Receiver: self.closing = True self._wakeup() try: - self._ewait(lambda: self.closed) + self.session._ewait(lambda: self.closed) finally: self.session.receivers.remove(self) @@ -778,6 +816,8 @@ def get_type(content): def get_codec(content_type): return TYPE_CODEC[content_type] +UNSPECIFIED = object() + class Message: """ @@ -802,7 +842,9 @@ class Message: @ivar content: the message content """ - def __init__(self, content=None): + def __init__(self, content=None, content_type=UNSPECIFIED, id=None, + subject=None, to=None, user_id=None, reply_to=None, + correlation_id=None, durable=None, properties=None): """ Construct a new message with the supplied content. The content-type of the message will be automatically inferred from @@ -810,20 +852,44 @@ class Message: @type content: str, unicode, buffer, dict, list @param content: the message content - """ - self.id = None - self.subject = None - self.user_id = None - self.to = None - self.reply_to = None - self.correlation_id = None - self.durable = False - self.properties = {} - self.content_type = get_type(content) + + @type content_type: str + @param content_type: the content-type of the message + """ + self.id = id + self.subject = subject + self.to = to + self.user_id = user_id + self.reply_to = reply_to + self.correlation_id = correlation_id + self.durable = durable + if properties is None: + self.properties = {} + else: + self.properties = properties + if content_type is UNSPECIFIED: + self.content_type = get_type(content) + else: + self.content_type = content_type self.content = content def __repr__(self): - return "Message(%r)" % self.content + args = [] + for name in ["id", "subject", "to", "user_id", "reply_to", + "correlation_id"]: + value = self.__dict__[name] + if value is not None: args.append("%s=%r" % (name, value)) + for name in ["durable", "properties"]: + value = self.__dict__[name] + if value: args.append("%s=%r" % (name, value)) + if self.content_type != get_type(self.content): + args.append("content_type=%r" % self.content_type) + if self.content is not None: + if args: + args.append("content=%r" % self.content) + else: + args.append(repr(self.content)) + return "Message(%s)" % ", ".join(args) __all__ = ["Connection", "Session", "Sender", "Receiver", "Pattern", "Message", "ConnectionError", "ConnectError", "SessionError", "Disconnected", diff --git a/qpid/python/qpid/ops.py b/qpid/python/qpid/ops.py index 11e7d11fe9..277d059203 100644 --- a/qpid/python/qpid/ops.py +++ b/qpid/python/qpid/ops.py @@ -80,7 +80,7 @@ class Compound(object): return "%s(%s)" % (self.__class__.__name__, ", ".join(["%s=%r" % (f.name, getattr(self, f.name)) for f in self.ARGS - if getattr(self, f.name) is not f.default])) + if getattr(self, f.name) != f.default])) class Command(Compound): UNENCODED=[Field("channel", "uint16", 0), @@ -209,8 +209,8 @@ def make(nd): from qpid_config import amqp_spec as file pclfile = "%s.ops.pcl" % file -if False and (os.path.exists(pclfile) and - os.path.getmtime(pclfile) > os.path.getmtime(file)): +if os.path.exists(pclfile) and \ + os.path.getmtime(pclfile) > os.path.getmtime(file): f = open(pclfile, "read") types = pickle.load(f) f.close() diff --git a/qpid/python/qpid/selector.py b/qpid/python/qpid/selector.py new file mode 100644 index 0000000000..46052e1108 --- /dev/null +++ b/qpid/python/qpid/selector.py @@ -0,0 +1,156 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT 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 atexit, os, time +from compat import select, set +from threading import Thread, Lock + +class Acceptor: + + def __init__(self, sock, handler): + self.sock = sock + self.handler = handler + + def fileno(self): + return self.sock.fileno() + + def reading(self): + return True + + def writing(self): + return False + + def readable(self): + sock, addr = self.sock.accept() + self.handler(sock) + +class Sink: + + def __init__(self, fd): + self.fd = fd + + def fileno(self): + return self.fd + + def reading(self): + return True + + def readable(self): + os.read(self.fd, 65536) + + def __repr__(self): + return "Sink(%r)" % self.fd + +class Selector: + + lock = Lock() + DEFAULT = None + + @staticmethod + def default(): + Selector.lock.acquire() + try: + if Selector.DEFAULT is None: + sel = Selector() + atexit.register(sel.stop) + sel.start() + Selector.DEFAULT = sel + return Selector.DEFAULT + finally: + Selector.lock.release() + + def __init__(self): + self.selectables = set() + self.reading = set() + self.writing = set() + self.wait_fd, self.wakeup_fd = os.pipe() + self.reading.add(Sink(self.wait_fd)) + self.stopped = False + self.thread = None + + def wakeup(self): + os.write(self.wakeup_fd, "\0") + + def register(self, selectable): + self.selectables.add(selectable) + self.modify(selectable) + + def _update(self, selectable): + if selectable.reading(): + self.reading.add(selectable) + else: + self.reading.discard(selectable) + if selectable.writing(): + self.writing.add(selectable) + else: + self.writing.discard(selectable) + return selectable.timing() + + def modify(self, selectable): + self._update(selectable) + self.wakeup() + + def unregister(self, selectable): + self.reading.discard(selectable) + self.writing.discard(selectable) + self.selectables.discard(selectable) + self.wakeup() + + def start(self): + self.stopped = False + self.thread = Thread(target=self.run) + self.thread.setDaemon(True) + self.thread.start(); + + def run(self): + while not self.stopped: + wakeup = None + for sel in self.selectables.copy(): + t = self._update(sel) + if t is not None: + if wakeup is None: + wakeup = t + else: + wakeup = min(wakeup, t) + + if wakeup is None: + timeout = None + else: + timeout = max(0, wakeup - time.time()) + + rd, wr, ex = select(self.reading, self.writing, (), timeout) + + for sel in wr: + if sel.writing(): + sel.writeable() + + for sel in rd: + if sel.reading(): + sel.readable() + + now = time.time() + for sel in self.selectables.copy(): + w = sel.timing() + if w is not None and now > w: + sel.timeout() + + def stop(self, timeout=None): + self.stopped = True + self.wakeup() + self.thread.join(timeout) + self.thread = None diff --git a/qpid/python/qpid/tests/messaging.py b/qpid/python/qpid/tests/messaging.py index 7623c1f93b..2e4c0ca1ab 100644 --- a/qpid/python/qpid/tests/messaging.py +++ b/qpid/python/qpid/tests/messaging.py @@ -24,7 +24,7 @@ import time from qpid.tests import Test from qpid.harness import Skipped from qpid.messaging import Connection, ConnectError, Disconnected, Empty, \ - InsufficientCapacity, Message, UNLIMITED, uuid4 + InsufficientCapacity, Message, ReceiveError, SendError, UNLIMITED, uuid4 from Queue import Queue, Empty as QueueEmpty class Base(Test): @@ -50,6 +50,8 @@ class Base(Test): raise Skipped(e) self.ssn = self.setup_session() self.snd = self.setup_sender() + if self.snd is not None: + self.snd.durable = self.durable() self.rcv = self.setup_receiver() def teardown(self): @@ -63,11 +65,12 @@ class Base(Test): return "%s[%s, %s]" % (base, count, self.test_id) def ping(self, ssn): + PING_Q = 'ping-queue {create: always}' # send a message - sender = ssn.sender("ping-queue") + sender = ssn.sender(PING_Q, durable=self.durable()) content = self.content("ping") sender.send(content) - receiver = ssn.receiver("ping-queue") + receiver = ssn.receiver(PING_Q) msg = receiver.fetch(0) ssn.acknowledge() assert msg.content == content, "expected %r, got %r" % (content, msg.content) @@ -97,16 +100,27 @@ class Base(Test): def delay(self): return float(self.config.defines.get("delay", "2")) + def get_bool(self, name): + return self.config.defines.get(name, "false").lower() in ("true", "yes", "1") + + def durable(self): + return self.get_bool("durable") + + def reconnect(self): + return self.get_bool("reconnect") + class SetupTests(Base): def testOpen(self): # XXX: need to flesh out URL support/syntax - self.conn = Connection.open(self.broker.host, self.broker.port) + self.conn = Connection.open(self.broker.host, self.broker.port, + reconnect=self.reconnect()) self.ping(self.conn.session()) def testConnect(self): # XXX: need to flesh out URL support/syntax - self.conn = Connection(self.broker.host, self.broker.port) + self.conn = Connection(self.broker.host, self.broker.port, + reconnect=self.reconnect()) self.conn.connect() self.ping(self.conn.session()) @@ -121,7 +135,8 @@ class SetupTests(Base): class ConnectionTests(Base): def setup_connection(self): - return Connection.open(self.broker.host, self.broker.port) + return Connection.open(self.broker.host, self.broker.port, + reconnect=self.reconnect()) def testSessionAnon(self): ssn1 = self.conn.session() @@ -174,17 +189,21 @@ class ConnectionTests(Base): self.conn.close() assert not self.conn.connected() +ACK_Q = 'test-ack-queue {create: always}' + class SessionTests(Base): def setup_connection(self): - return Connection.open(self.broker.host, self.broker.port) + return Connection.open(self.broker.host, self.broker.port, + reconnect=self.reconnect()) def setup_session(self): return self.conn.session() def testSender(self): - snd = self.ssn.sender("test-snd-queue") - snd2 = self.ssn.sender(snd.target) + snd = self.ssn.sender('test-snd-queue {create: always}', + durable=self.durable()) + snd2 = self.ssn.sender(snd.target, durable=self.durable()) assert snd is not snd2 snd2.close() @@ -196,47 +215,49 @@ class SessionTests(Base): self.ssn.acknowledge(msg) def testReceiver(self): - rcv = self.ssn.receiver("test-rcv-queue") + rcv = self.ssn.receiver('test-rcv-queue {create: always}') rcv2 = self.ssn.receiver(rcv.source) assert rcv is not rcv2 rcv2.close() content = self.content("testReceiver") - snd = self.ssn.sender(rcv.source) + snd = self.ssn.sender(rcv.source, durable=self.durable()) snd.send(content) msg = rcv.fetch(0) assert msg.content == content self.ssn.acknowledge(msg) def testStart(self): - rcv = self.ssn.receiver("test-start-queue") + START_Q = 'test-start-queue {create: always}' + rcv = self.ssn.receiver(START_Q) assert not rcv.started self.ssn.start() assert rcv.started - rcv = self.ssn.receiver("test-start-queue") + rcv = self.ssn.receiver(START_Q) assert rcv.started def testStop(self): + STOP_Q = 'test-stop-queue {create: always}' self.ssn.start() - rcv = self.ssn.receiver("test-stop-queue") + rcv = self.ssn.receiver(STOP_Q) assert rcv.started self.ssn.stop() assert not rcv.started - rcv = self.ssn.receiver("test-stop-queue") + rcv = self.ssn.receiver(STOP_Q) assert not rcv.started # XXX, we need a convenient way to assert that required queues are # empty on setup, and possibly also to drain queues on teardown def ackTest(self, acker, ack_capacity=None): # send a bunch of messages - snd = self.ssn.sender("test-ack-queue") + snd = self.ssn.sender(ACK_Q, durable=self.durable()) contents = [self.content("ackTest", i) for i in range(15)] for c in contents: snd.send(c) # drain the queue, verify the messages are there and then close # without acking - rcv = self.ssn.receiver(snd.target) + rcv = self.ssn.receiver(ACK_Q) self.drain(rcv, expected=contents) self.ssn.close() @@ -245,7 +266,7 @@ class SessionTests(Base): self.ssn = self.conn.session() if ack_capacity is not None: self.ssn.ack_capacity = ack_capacity - rcv = self.ssn.receiver("test-ack-queue") + rcv = self.ssn.receiver(ACK_Q) self.drain(rcv, expected=contents) acker(self.ssn) self.ssn.close() @@ -253,7 +274,7 @@ class SessionTests(Base): # drain the queue a final time and verify that the messages were # dequeued self.ssn = self.conn.session() - rcv = self.ssn.receiver("test-ack-queue") + rcv = self.ssn.receiver(ACK_Q) self.assertEmpty(rcv) def testAcknowledge(self): @@ -271,7 +292,7 @@ class SessionTests(Base): pass finally: self.ssn.ack_capacity = UNLIMITED - self.drain(self.ssn.receiver("test-ack-queue")) + self.drain(self.ssn.receiver(ACK_Q)) self.ssn.acknowledge() def testAcknowledgeAsyncAckCap1(self): @@ -284,7 +305,7 @@ class SessionTests(Base): self.ackTest(lambda ssn: ssn.acknowledge(sync=False), UNLIMITED) def send(self, ssn, queue, base, count=1): - snd = ssn.sender(queue) + snd = ssn.sender(queue, durable=self.durable()) contents = [] for i in range(count): c = self.content(base, i) @@ -294,10 +315,12 @@ class SessionTests(Base): return contents def txTest(self, commit): + TX_Q = 'test-tx-queue {create: always}' + TX_Q_COPY = 'test-tx-queue-copy {create: always}' txssn = self.conn.session(transactional=True) - contents = self.send(self.ssn, "test-tx-queue", "txTest", 3) - txrcv = txssn.receiver("test-tx-queue") - txsnd = txssn.sender("test-tx-queue-copy") + contents = self.send(self.ssn, TX_Q, "txTest", 3) + txrcv = txssn.receiver(TX_Q) + txsnd = txssn.sender(TX_Q_COPY, durable=self.durable()) rcv = self.ssn.receiver(txrcv.source) copy_rcv = self.ssn.receiver(txsnd.target) self.assertEmpty(copy_rcv) @@ -323,9 +346,10 @@ class SessionTests(Base): self.txTest(False) def txTestSend(self, commit): + TX_SEND_Q = 'test-tx-send-queue {create: always}' txssn = self.conn.session(transactional=True) - contents = self.send(txssn, "test-tx-send-queue", "txTestSend", 3) - rcv = self.ssn.receiver("test-tx-send-queue") + contents = self.send(txssn, TX_SEND_Q, "txTestSend", 3) + rcv = self.ssn.receiver(TX_SEND_Q) self.assertEmpty(rcv) if commit: @@ -345,10 +369,11 @@ class SessionTests(Base): self.txTestSend(False) def txTestAck(self, commit): + TX_ACK_Q = 'test-tx-ack-queue {create: always}' txssn = self.conn.session(transactional=True) - txrcv = txssn.receiver("test-tx-ack-queue") + txrcv = txssn.receiver(TX_ACK_Q) self.assertEmpty(txrcv) - contents = self.send(self.ssn, "test-tx-ack-queue", "txTestAck", 3) + contents = self.send(self.ssn, TX_ACK_Q, "txTestAck", 3) assert contents == self.drain(txrcv) if commit: @@ -366,11 +391,11 @@ class SessionTests(Base): txssn.close() txssn = self.conn.session(transactional=True) - txrcv = txssn.receiver("test-tx-ack-queue") + txrcv = txssn.receiver(TX_ACK_Q) assert contents == self.drain(txrcv) txssn.acknowledge() txssn.commit() - rcv = self.ssn.receiver("test-tx-ack-queue") + rcv = self.ssn.receiver(TX_ACK_Q) self.assertEmpty(rcv) txssn.close() self.assertEmpty(rcv) @@ -389,19 +414,22 @@ class SessionTests(Base): except Disconnected: pass +RECEIVER_Q = 'test-receiver-queue {create: always}' + class ReceiverTests(Base): def setup_connection(self): - return Connection.open(self.broker.host, self.broker.port) + return Connection.open(self.broker.host, self.broker.port, + reconnect=self.reconnect()) def setup_session(self): return self.conn.session() def setup_sender(self): - return self.ssn.sender("test-receiver-queue") + return self.ssn.sender(RECEIVER_Q) def setup_receiver(self): - return self.ssn.receiver("test-receiver-queue") + return self.ssn.receiver(RECEIVER_Q) def send(self, base, count = None): content = self.content(base, count) @@ -516,7 +544,7 @@ class ReceiverTests(Base): self.assertPending(self.rcv, 5) drained = self.drain(self.rcv) - assert len(drained) == 10 + assert len(drained) == 10, "%s, %s" % (len(drained), drained) self.assertPending(self.rcv, 0) self.ssn.acknowledge() @@ -538,19 +566,81 @@ class ReceiverTests(Base): # XXX: need testClose +NOSUCH_Q = "this-queue-should-not-exist" +UNPARSEABLE_ADDR = "{bad address}" +UNLEXABLE_ADDR = "\0x0\0x1\0x2\0x3" + +class AddressErrorTests(Base): + + def setup_connection(self): + return Connection.open(self.broker.host, self.broker.port, + reconnect=self.reconnect()) + + def setup_session(self): + return self.conn.session() + + def sendErrorTest(self, addr, exc, check=lambda e: True): + snd = self.ssn.sender(addr, durable=self.durable()) + try: + snd.send("hello") + assert False, "send succeeded" + except exc, e: + assert check(e), "unexpected error: %s" % e + snd.close() + + def fetchErrorTest(self, addr, exc, check=lambda e: True): + rcv = self.ssn.receiver(addr) + try: + rcv.fetch(timeout=0) + assert False, "fetch succeeded" + except exc, e: + assert check(e), "unexpected error: %s" % e + rcv.close() + + def testNoTarget(self): + # XXX: should have specific exception for this + self.sendErrorTest(NOSUCH_Q, SendError, lambda e: NOSUCH_Q in str(e)) + + def testNoSource(self): + # XXX: should have specific exception for this + self.fetchErrorTest(NOSUCH_Q, ReceiveError, lambda e: NOSUCH_Q in str(e)) + + def testUnparseableTarget(self): + # XXX: should have specific exception for this + self.sendErrorTest(UNPARSEABLE_ADDR, SendError, + lambda e: "expecting ID" in str(e)) + + def testUnparseableSource(self): + # XXX: should have specific exception for this + self.fetchErrorTest(UNPARSEABLE_ADDR, ReceiveError, + lambda e: "expecting ID" in str(e)) + + def testUnlexableTarget(self): + # XXX: should have specific exception for this + self.sendErrorTest(UNLEXABLE_ADDR, SendError, + lambda e: "unrecognized character" in str(e)) + + def testUnlexableSource(self): + # XXX: should have specific exception for this + self.fetchErrorTest(UNLEXABLE_ADDR, ReceiveError, + lambda e: "unrecognized character" in str(e)) + +SENDER_Q = 'test-sender-q {create: always}' + class SenderTests(Base): def setup_connection(self): - return Connection.open(self.broker.host, self.broker.port) + return Connection.open(self.broker.host, self.broker.port, + reconnect=self.reconnect()) def setup_session(self): return self.conn.session() def setup_sender(self): - return self.ssn.sender("test-sender-queue") + return self.ssn.sender(SENDER_Q) def setup_receiver(self): - return self.ssn.receiver("test-sender-queue") + return self.ssn.receiver(SENDER_Q) def checkContent(self, content): self.snd.send(content) @@ -611,6 +701,7 @@ class SenderTests(Base): except InsufficientCapacity: caught = True break + self.snd.sync() self.drain(self.rcv, expected=msgs) self.ssn.acknowledge() assert caught, "did not exceed capacity" @@ -643,19 +734,22 @@ class MessageTests(Base): m.content = u"" assert m.content_type == "text/html; charset=utf8" +ECHO_Q = 'test-message-echo-queue {create: always}' + class MessageEchoTests(Base): def setup_connection(self): - return Connection.open(self.broker.host, self.broker.port) + return Connection.open(self.broker.host, self.broker.port, + reconnect=self.reconnect()) def setup_session(self): return self.conn.session() def setup_sender(self): - return self.ssn.sender("test-message-echo-queue") + return self.ssn.sender(ECHO_Q) def setup_receiver(self): - return self.ssn.receiver("test-message-echo-queue") + return self.ssn.receiver(ECHO_Q) def check(self, msg): self.snd.send(msg) diff --git a/qpid/review/changeLogToWiki.py b/qpid/review/changeLogToWiki.py new file mode 100755 index 0000000000..4054b135df --- /dev/null +++ b/qpid/review/changeLogToWiki.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT 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 sys, re +from popen2 import popen2, popen3 +from optparse import OptionParser +from xml.dom.minidom import parse, parseString + +prereqs = ["tr", "svn", "xsltproc", "sed", "grep", "wget"] + +apacheSVN="https://svn.apache.org/repos/asf/qpid/trunk/qpid/java" + +svncmd = "svn log %s --xml -r %s:HEAD | tr '\\n\\r|' ' -' | xsltproc svnlog2wiki.xsl - | grep r | sed -e 's/^ *//' | sed -e 's/\\(QPID-[0-9]*\\)/\\[\\1 | https:\\/\\/issues.apache.org\\/jira\\/browse\\/\\1 \]/g'" + + +def get_commits(revision): + (stdout, stdin) = popen2(svncmd % (options.repo,revision)) + return add_jira_status(stdout.read()) + +def add_jira_status(commits): + commit_lines = commits.split("\n") + new_commits = [] + for commit in commit_lines: + if re.match(".*https://issues.apache.org/.*", commit): + jira = re.findall("QPID-[0-9]*", commit)[0] + jira_xml_url = "http://issues.apache.org/jira/si/jira.issueviews:issue-xml/%s/%s.xml" % (jira, jira) + (stdout, stdin) = popen2("wget -q -O - %s" % jira_xml_url) + + jira_dom = parse(stdout) + status = jira_dom.getElementsByTagName("status")[0] + new_commits.append("%s %s | " % (commit, status.lastChild.data)) + else: + new_commits.append(commit) + + return "\n".join(new_commits) + + +def main(): + global options + parser = OptionParser() + parser.add_option("-r", "--revision", dest="revision", action="store", + type="string", + help="The first revision to generate logs for") + + parser.add_option("-s", "--svn-repo", dest="repo", action="store", + default=apacheSVN, + type="string", + help="Provide a svn repository to process") + + + (options, args) = parser.parse_args() + + # Check that we have what's necessary + + notfound = re.compile('^which') + for cmd in prereqs: + (stdout, stdin, stderr) = popen3('which %s' % cmd) + if (notfound.match(stderr.read())): + parser.error ("Could not find command %s, try [apt-get|yum] install %s" % + (cmd, cmd)) + + if (options.revision == None): + parser.error("svn revision must be specified") + + print(get_commits(options.revision)) + +if __name__ == "__main__": + main() diff --git a/qpid/wcf/ReadMe.txt b/qpid/wcf/ReadMe.txt index 0ef3e06ce5..6f118ceac3 100644 --- a/qpid/wcf/ReadMe.txt +++ b/qpid/wcf/ReadMe.txt @@ -49,9 +49,9 @@ NOTE: In the following instructions %QPID_ROOT% refers to the root of qpid source code location e.g. C:\trunk\qpid 5. Build Qpid cpp -Run CMake and choose "%QPID_ROOT%\cpp\build" as the location for "Where to -build the binaries". Build at least the "qpidd", "qpidclient" and -"qpidcommon" projects. +Build at least the "qpidd", "qpidclient" and "qpidcommon" projects. +Create an environment variable called QPID_BUILD_ROOT and store the +path to the Qpid build directory in it. 4. Building the solution file @@ -81,7 +81,7 @@ System Development Edition, or Team System Team Suite SKU) %QPID_ROOT%\wcf\test\Apache\Qpid\Test\Channel\Functional\RunTests.bat has the correct values for the nunit_exe, qpid_dll_location and configuration_name variables as per your installation. -2. Start the qpid broker from the qpid build folder e.g. %QPID_ROOT%\cpp\build\src\Debug. +2. Start the qpid broker from the qpid build folder e.g. %QPID_BUILD_ROOT%\src\Debug. 3. Execute RunTests.bat from its location e.g. %QPID_ROOT%\wcf\test\Apache\Qpid\Test\Channel\Functional. diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinding.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinding.cs index b952faf9e5..b0b71c87f3 100644 --- a/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinding.cs +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinding.cs @@ -64,6 +64,12 @@ namespace Apache.Qpid.Channel set { transport.BrokerPort = value; } } + public int PrefetchLimit + { + get { return transport.PrefetchLimit; } + set { transport.PrefetchLimit = value; } + } + public bool Shared { get { return transport.Shared; } diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpBindingConfigurationElement.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBindingConfigurationElement.cs index 3ec62e809d..554824cea9 100644 --- a/qpid/wcf/src/Apache/Qpid/Channel/AmqpBindingConfigurationElement.cs +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBindingConfigurationElement.cs @@ -63,6 +63,13 @@ namespace Apache.Qpid.Channel set { brokerPort = value; } } + [ConfigurationProperty(AmqpConfigurationStrings.PrefetchLimit, DefaultValue = false)] + public int PrefetchLimit + { + get { return (int)base[AmqpConfigurationStrings.PrefetchLimit]; } + set { base[AmqpConfigurationStrings.PrefetchLimit] = value; } + } + [ConfigurationProperty(AmqpConfigurationStrings.Shared, DefaultValue = false)] public bool Shared { @@ -95,6 +102,8 @@ namespace Apache.Qpid.Channel get { ConfigurationPropertyCollection properties = base.Properties; + properties.Add(new ConfigurationProperty(AmqpConfigurationStrings.PrefetchLimit, + typeof(int), 0, null, null, ConfigurationPropertyOptions.None)); properties.Add(new ConfigurationProperty(AmqpConfigurationStrings.Shared, typeof(bool), false, null, null, ConfigurationPropertyOptions.None)); properties.Add(new ConfigurationProperty(AmqpConfigurationStrings.TransferMode, @@ -112,6 +121,7 @@ namespace Apache.Qpid.Channel this.BrokerPort = amqpBinding.BrokerPort; this.TransferMode = amqpBinding.TransferMode; this.Shared = amqpBinding.Shared; + this.PrefetchLimit = amqpBinding.PrefetchLimit; AmqpProperties props = amqpBinding.DefaultMessageProperties; } @@ -133,6 +143,7 @@ namespace Apache.Qpid.Channel amqpBinding.BrokerPort = this.BrokerPort; amqpBinding.TransferMode = this.TransferMode; amqpBinding.Shared = this.Shared; + amqpBinding.PrefetchLimit = this.PrefetchLimit; } protected override void PostDeserialize() diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelFactory.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelFactory.cs index b8e2811527..542f1a00a8 100644 --- a/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelFactory.cs +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelFactory.cs @@ -32,6 +32,7 @@ namespace Apache.Qpid.Channel AmqpChannelProperties channelProperties; long maxBufferPoolSize; bool shared; + int prefetchLimit; internal AmqpChannelFactory(AmqpTransportBindingElement bindingElement, BindingContext context) : base(context.Binding) @@ -39,6 +40,7 @@ namespace Apache.Qpid.Channel this.bindingElement = bindingElement; this.channelProperties = bindingElement.ChannelProperties.Clone(); this.shared = bindingElement.Shared; + this.prefetchLimit = bindingElement.PrefetchLimit; this.maxBufferPoolSize = bindingElement.MaxBufferPoolSize; Collection messageEncoderBindingElements = context.BindingParameters.FindAll(); @@ -91,7 +93,7 @@ namespace Apache.Qpid.Channel protected override TChannel OnCreateChannel(EndpointAddress remoteAddress, Uri via) { - return (TChannel)(object) new AmqpTransportChannel(this, this.channelProperties, remoteAddress, this.messageEncoderFactory.Encoder, this.maxBufferPoolSize, this.shared); + return (TChannel)(object) new AmqpTransportChannel(this, this.channelProperties, remoteAddress, this.messageEncoderFactory.Encoder, this.maxBufferPoolSize, this.shared, this.prefetchLimit); } } diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelHelpers.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelHelpers.cs index f1de30406a..0853b3d6f3 100644 --- a/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelHelpers.cs +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelHelpers.cs @@ -46,6 +46,7 @@ namespace Apache.Qpid.Channel public const string TransferMode = "transferMode"; public const string Brokers = "brokers"; public const string Shared = "shared"; + public const string PrefetchLimit = "prefetchLimit"; public const string MaxBufferPoolSize = "maxBufferPoolSize"; public const string MaxReceivedMessageSize = "maxReceivedMessageSize"; } @@ -55,7 +56,6 @@ namespace Apache.Qpid.Channel internal const string BrokerHost = "localhost"; internal const int BrokerPort = 5672; internal const TransferMode TransferMode = System.ServiceModel.TransferMode.Buffered; - internal const byte Priority = 4; internal const long MaxBufferPoolSize = 64 * 1024; internal const int MaxReceivedMessageSize = 5 * 1024 * 1024; //64 * 1024; } diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelListener.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelListener.cs index 44fecdaf62..8894b68584 100644 --- a/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelListener.cs +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelListener.cs @@ -32,6 +32,7 @@ namespace Apache.Qpid.Channel AmqpTransportBindingElement bindingElement; AmqpChannelProperties channelProperties; bool shared; + int prefetchLimit; long maxBufferPoolSize; Uri uri; AmqpTransportChannel amqpTransportChannel; @@ -45,6 +46,7 @@ namespace Apache.Qpid.Channel this.bindingElement = bindingElement; this.channelProperties = bindingElement.ChannelProperties.Clone(); this.shared = bindingElement.Shared; + this.prefetchLimit = bindingElement.PrefetchLimit; this.maxBufferPoolSize = bindingElement.MaxBufferPoolSize; @@ -132,7 +134,7 @@ namespace Apache.Qpid.Channel { amqpTransportChannel = new AmqpTransportChannel(this, this.channelProperties, new EndpointAddress(uri), messageEncoderFactory.Encoder, - maxBufferPoolSize, this.shared); + maxBufferPoolSize, this.shared, this.prefetchLimit); return (IInputChannel)(object) amqpTransportChannel; } diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportBindingElement.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportBindingElement.cs index f23b8072e9..08c565af18 100644 --- a/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportBindingElement.cs +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportBindingElement.cs @@ -29,6 +29,7 @@ namespace Apache.Qpid.Channel { AmqpChannelProperties channelProperties; bool shared; + int prefetchLimit; public AmqpTransportBindingElement() { @@ -41,6 +42,7 @@ namespace Apache.Qpid.Channel { this.channelProperties = other.channelProperties.Clone(); this.shared = other.shared; + this.prefetchLimit = other.prefetchLimit; } public override IChannelFactory BuildChannelFactory(BindingContext context) @@ -98,6 +100,12 @@ namespace Apache.Qpid.Channel set { this.channelProperties.BrokerPort = value; } } + public int PrefetchLimit + { + get { return this.prefetchLimit; } + set { this.prefetchLimit = value; } + } + public bool Shared { get { return this.shared; } diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportChannel.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportChannel.cs index ca9c10be69..5924142046 100644 --- a/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportChannel.cs +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportChannel.cs @@ -50,6 +50,7 @@ namespace Apache.Qpid.Channel private MessageEncoder encoder; private AmqpChannelProperties factoryChannelProperties; private bool shared; + private int prefetchLimit; private string encoderContentType; // input = 0-10 queue, output = 0-10 exchange @@ -68,7 +69,7 @@ namespace Apache.Qpid.Channel private AsyncTimeSpanCaller asyncOpenCaller; private AsyncTimeSpanCaller asyncCloseCaller; - internal AmqpTransportChannel(ChannelManagerBase factory, AmqpChannelProperties channelProperties, EndpointAddress remoteAddress, MessageEncoder msgEncoder, long maxBufferPoolSize, bool sharedConnection) + internal AmqpTransportChannel(ChannelManagerBase factory, AmqpChannelProperties channelProperties, EndpointAddress remoteAddress, MessageEncoder msgEncoder, long maxBufferPoolSize, bool sharedConnection, int prefetchLimit) : base(factory) { this.isInputChannel = (factory is ChannelListenerBase) || (factory is AmqpChannelFactory); @@ -80,6 +81,7 @@ namespace Apache.Qpid.Channel this.factoryChannelProperties = channelProperties; this.shared = sharedConnection; + this.prefetchLimit = prefetchLimit; this.remoteAddress = remoteAddress; // pull out host, port, queue, and connection arguments @@ -128,6 +130,7 @@ namespace Apache.Qpid.Channel if (this.isInputChannel) { this.inputLink = ConnectionManager.GetInputLink(this.factoryChannelProperties, shared, false, this.queueName); + this.inputLink.PrefetchLimit = this.prefetchLimit; } else { @@ -287,7 +290,7 @@ namespace Apache.Qpid.Channel return false; } - + public IAsyncResult BeginTryReceive(TimeSpan timeout, AsyncCallback callback, object state) { return this.inputLink.BeginTryReceive(timeout, callback, state); @@ -464,7 +467,7 @@ namespace Apache.Qpid.Channel } return amqpMessage; } - + private Message QpidToWcf(AmqpMessage amqpMessage) { @@ -531,7 +534,7 @@ namespace Apache.Qpid.Channel { this.bufferManager.ReturnBuffer(managedBuffer); } - } + } return wcfMessage; } diff --git a/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.cpp b/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.cpp index bab73da74e..425a592509 100644 --- a/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.cpp +++ b/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.cpp @@ -63,11 +63,12 @@ AmqpSession::AmqpSession(AmqpConnection^ conn, qpid::client::Connection* qpidCon sessionp = new qpid::client::AsyncSession; *sessionp = qpidConnectionp->newSession(); subs_mgrp = new SubscriptionManager (*sessionp); - success = true; waiters = gcnew Collections::Generic::List(); + success = true; } finally { if (!success) { Cleanup(); + // TODO: include inner exception information throw gcnew QpidException ("session creation failure"); } } @@ -76,12 +77,6 @@ AmqpSession::AmqpSession(AmqpConnection^ conn, qpid::client::Connection* qpidCon void AmqpSession::Cleanup() { - if (subscriptionp != NULL) { - subscriptionp->cancel(); - delete subscriptionp; - subscriptionp=NULL; - } - if (subs_mgrp != NULL) { subs_mgrp->stop(); delete subs_mgrp; @@ -112,6 +107,7 @@ void AmqpSession::Cleanup() void AmqpSession::ConnectionClosed() { + lock l(waiters); Cleanup(); } @@ -283,5 +279,27 @@ void AmqpSession::asyncHelper(Object ^unused) } } +bool AmqpSession::MessageStop(Completion &comp, std::string &name) +{ + lock l(waiters); + + if (sessionp == NULL) + return false; + + comp = sessionp->messageStop(name, true); + return true; +} + +void AmqpSession::AcceptAndComplete(SequenceSet& transfers) +{ + lock l(waiters); + + if (sessionp == NULL) + throw gcnew ObjectDisposedException("Accept"); + + sessionp->markCompleted(transfers, false); + sessionp->messageAccept(transfers, false); +} + }}} // namespace Apache::Qpid::Cli diff --git a/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.h b/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.h index b959a4123a..8306cdf720 100644 --- a/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.h +++ b/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.h @@ -44,7 +44,6 @@ private: AsyncSession* sessionp; SessionImpl* sessionImplp; SubscriptionManager* subs_mgrp; - Subscription* subscriptionp; LocalQueue* localQueuep; Collections::Generic::List^ waiters; bool helperRunning; @@ -69,6 +68,8 @@ internal: void ConnectionClosed(); void internalWaitForCompletion(IntPtr Future); void removeWaiter(CompletionWaiter^ waiter); + bool MessageStop(Completion &comp, std::string &name); + void AcceptAndComplete(SequenceSet& transfers); property AmqpConnection^ Connection { AmqpConnection^ get () { return connection; } diff --git a/qpid/wcf/src/Apache/Qpid/Interop/CompletionWaiter.h b/qpid/wcf/src/Apache/Qpid/Interop/CompletionWaiter.h index 197ac632b0..88880c3721 100644 --- a/qpid/wcf/src/Apache/Qpid/Interop/CompletionWaiter.h +++ b/qpid/wcf/src/Apache/Qpid/Interop/CompletionWaiter.h @@ -32,7 +32,6 @@ private: bool timedOut; // has an owner thread bool assigned; - // can Run (i.e. earlier CompletionWaiters in the queue have completed) System::Exception^ runException; AsyncCallback^ asyncCallback; Threading::Timer ^timer; diff --git a/qpid/wcf/src/Apache/Qpid/Interop/InputLink.cpp b/qpid/wcf/src/Apache/Qpid/Interop/InputLink.cpp index cee394b05d..e12151d943 100644 --- a/qpid/wcf/src/Apache/Qpid/Interop/InputLink.cpp +++ b/qpid/wcf/src/Apache/Qpid/Interop/InputLink.cpp @@ -59,6 +59,12 @@ using namespace Apache::Qpid::AmqpTypes; // with proposed changes to the native library to reduce the number of servicing // threads for large numbers of subscriptions. +// synchronization is accomplished with locks, but also by ensuring that only one +// MessageWaiter (the one at the front of the line) is ever active. +// async threads to watch for: Close/finalizer, Timers, SyncCredit and the native Dispatch +// thread (who deposits FrameSets into the local queue and is oblivious to the +// managed space locks). + // The folowing def must match the "Frames" private typedef. // TODO, make Qpid-cpp "Frames" definition visible. @@ -94,6 +100,8 @@ InputLink::InputLink(AmqpSession^ session, System::String^ sourceQueue, localQueuep = new LocalQueue; SubscriptionSettings settings; settings.flowControl = FlowControl::messageCredit(0); + settings.completionMode = CompletionMode::MANUAL_COMPLETION; + Subscription sub = qpidSubsMgrp->subscribe(*localQueuep, qname, settings); subscriptionp = new Subscription (sub); // copy smart pointer for later IDisposable cleanup @@ -197,6 +205,7 @@ bool InputLink::haveMessage() IntPtr InputLink::nextLocalMessage() { lock l(waiters); + if (disposed) return (IntPtr) NULL; @@ -279,8 +288,7 @@ bool InputLink::internalWaitForMessage() if (haveMessage()) return true; - // TODO: prefetch window of messages, compatible with both 0-10 and 1.0. - subscriptionp->grantMessageCredit(1); + AdjustCredit(); // get a scoped smart ptr ref to guard against async close or hangup demuxQueuePtr = *queuePtrp; @@ -350,7 +358,12 @@ void InputLink::removeWaiter(MessageWaiter^ waiter) { } return; } + waiters->RemoveAt(idx); + if (waiter->TimedOut) { + // may have to give back message if it arrives momentarily + AdjustCredit(); + } // let the next waiter know it's his turn. if (waiters->Count > 0) { @@ -411,6 +424,129 @@ void InputLink::sync() } +void InputLink::PrefetchLimit::set(int value) +{ + lock l(waiters); + prefetchLimit = value; + + int delta = 0; + + // rough rule of thumb to keep the flow, but reduce chatter. + // for small messages, the credit request is almost as expensive as the transfer itself. + // experience may suggest a better heuristic or require a property for the low water mark + if (prefetchLimit >= 3) { + delta = prefetchLimit / 3; + } + minWorkingCredit = prefetchLimit - delta; + AdjustCredit(); +} + + +// call with lock held +void InputLink::AdjustCredit() +{ + if (creditSyncPending || disposed) + return; + + // low watermark check + if ((prefetchLimit != 0) && + (workingCredit >= minWorkingCredit) && + (workingCredit >= waiters->Count)) + return; + + // should have enough for all waiters or to satisfy the prefetch window + int targetCredit = waiters->Count; + if (targetCredit < prefetchLimit) + targetCredit = prefetchLimit; + + if (targetCredit > workingCredit) { + subscriptionp->grantMessageCredit(targetCredit - workingCredit); + workingCredit = targetCredit; + return; + } + if (targetCredit < workingCredit) { + if ((targetCredit == 0) && (prefetchLimit == 0)) { + creditSyncPending = true; + ThreadPool::QueueUserWorkItem(gcnew WaitCallback(this, &InputLink::SyncCredit)); + } + // TODO: also shrink credit when prefetchLimit != 0 + } +} + +void InputLink::SyncCredit(Object ^unused) +{ + lock l(waiters); + + try { + if (disposed) + return; + + Completion comp; + if (!amqpSession->MessageStop(comp, subscriptionp->getName())) { + // connection closed + return; + } + + // get a private scoped copy to use outside the lock + Subscription s(*subscriptionp); + + l.release(); + // use setFlowControl to re-enable credit flow on the broker. + // previously used comp.wait() here, but setFlowControl is a sync operation + s.setFlowControl(s.getSettings().flowControl); + l.acquire(); + + if (disposed) + return; + + // let existing waiters use up any + // local queue size can only decrease until more credit is issued + while (true) { + if ((waiters->Count > 0) && ((*queuePtrp)->size() > 0)) { + l.release(); + // a rare use case and not used in performance oriented code. + // optimization can wait until the qpid/messaging api is used + Thread::Sleep(10); + l.acquire(); + if (disposed) + return; + } + else { + break; + } + } + + // At this point, the lock is held and we are fully synced with the broker + // so we have a valid snapshot + + if ((prefetchLimit == 0) && ((*queuePtrp)->size() > 0)) { + // can't be sure application will request a message again any time soon + QpidFrameSetPtr frameSetp; + while (!(*queuePtrp)->empty()) { + (*queuePtrp)->pop(frameSetp); + SequenceSet frameSetID(frameSetp->getId()); + subscriptionp->release(frameSetID); + } + + // don't touch dequeuedFrameSetpp. It is spoken for: explicitely from a + // MessageWaiter about to to get the nextLocalMessage(), or implicitely + // from a WaitForMessage(). + } + // TODO: if prefetchLimit != 0, release messages from back of the queue that exceed targetCredit + + workingCredit = (*queuePtrp)->size(); + if (dequeuedFrameSetpp != NULL) { + workingCredit++; + } + } + finally { + creditSyncPending = false; + } + + AdjustCredit(); +} + + AmqpMessage^ InputLink::createAmqpMessage(IntPtr msgp) { QpidFrameSetPtr* fspp = (QpidFrameSetPtr*) msgp.ToPointer(); @@ -539,7 +675,15 @@ AmqpMessage^ InputLink::createAmqpMessage(IntPtr msgp) // We have a message we can return to the caller. // Tell the broker we got it. - subscriptionp->accept(frameSetID); + + // subscriptionp->accept(frameSetID) is a slow sync operation in the native API + // so do it within the AsyncSession directly + amqpSession->AcceptAndComplete(frameSetID); + + workingCredit--; + // check if more messages need to be requested from broker + AdjustCredit(); + return amqpMessage; } finally { diff --git a/qpid/wcf/src/Apache/Qpid/Interop/InputLink.h b/qpid/wcf/src/Apache/Qpid/Interop/InputLink.h index 366780c137..f59a03a8c3 100644 --- a/qpid/wcf/src/Apache/Qpid/Interop/InputLink.h +++ b/qpid/wcf/src/Apache/Qpid/Interop/InputLink.h @@ -47,6 +47,14 @@ private: bool finalizing; QpidFrameSetPtr* dequeuedFrameSetpp; ManualResetEvent^ asyncHelperWaitHandle; + // number of messages to buffer locally for future consumption + int prefetchLimit; + // the number of messages requested and not yet processed + int workingCredit; + // stopping and restarting the message flow + bool creditSyncPending; + // working credit low water mark + int minWorkingCredit; void Cleanup(); void ReleaseNative(); @@ -54,6 +62,8 @@ private: void addWaiter(MessageWaiter^ waiter); void asyncHelper(); AmqpMessage^ createAmqpMessage(IntPtr msgp); + void AdjustCredit(); + void SyncCredit(Object ^); internal: InputLink(AmqpSession^ session, System::String^ sourceQueue, qpid::client::AsyncSession *qpidSessionp, @@ -80,6 +90,11 @@ public: IAsyncResult^ BeginWaitForMessage(TimeSpan timeout, AsyncCallback^ callback, Object^ state); bool EndWaitForMessage(IAsyncResult^ result); + property int PrefetchLimit { + int get () { return prefetchLimit; } + void set (int value); + } + }; }}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/Interop.vcproj b/qpid/wcf/src/Apache/Qpid/Interop/Interop.vcproj index 32f78c8344..484f6898fb 100644 --- a/qpid/wcf/src/Apache/Qpid/Interop/Interop.vcproj +++ b/qpid/wcf/src/Apache/Qpid/Interop/Interop.vcproj @@ -65,7 +65,7 @@ Name="VCCLCompilerTool" AdditionalOptions="/FU Debug\Apache.Qpid.AmqpTypes.netmodule" Optimization="0" - AdditionalIncludeDirectories="..\..\..\..\..\cpp\build\include;..\..\..\..\..\cpp\build\src;..\..\..\..\..\cpp\include;..\..\..\..\..\cpp\src;"$(BOOST_ROOT)"" + AdditionalIncludeDirectories=""$(QPID_BUILD_ROOT)\include";"$(QPID_BUILD_ROOT)\src";..\..\..\..\..\cpp\include;..\..\..\..\..\cpp\src;"$(BOOST_ROOT)"" PreprocessorDefinitions="WIN32;_DEBUG;_CRT_NONSTDC_NO_WARNINGS;WIN32_LEAN_AND_MEAN;NOMINMAX;_SCL_SECURE_NO_WARNINGS;BOOST_ALL_DYN_LINK" RuntimeLibrary="3" UsePrecompiledHeader="0" @@ -83,7 +83,7 @@ /> Date: Wed, 14 Oct 2009 21:26:25 +0000 Subject: Merge from trunk git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-network-refactor@825292 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/cpp/CMakeLists.txt | 21 +++ qpid/cpp/src/CMakeLists.txt | 8 +- qpid/cpp/src/posix/QpiddBroker.cpp | 12 +- qpid/cpp/src/qpid/sys/Socket.h | 12 +- qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp | 24 +-- qpid/cpp/src/qpid/sys/posix/Socket.cpp | 42 +++-- qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp | 2 +- qpid/cpp/src/qpid/sys/windows/Socket.cpp | 62 ++++---- qpid/cpp/src/qpid/sys/windows/SocketAddress.cpp | 70 +++++++++ qpid/java/common.xml | 3 + qpid/java/module.xml | 6 +- .../MessageDisappearWithIOExceptionTest.java | 23 ++- .../qpid/server/security/acl/SimpleACLTest.java | 171 +++++++++++++++++++++ .../ack/AcknowledgeAfterFailoverOnMessageTest.java | 6 +- .../unit/ack/AcknowledgeAfterFailoverTest.java | 2 +- .../test/unit/ack/AcknowledgeOnMessageTest.java | 33 ++-- qpid/java/test-profiles/010Excludes | 3 - qpid/java/test-profiles/08StandaloneExcludes | 1 - qpid/java/test-profiles/Excludes | 3 - qpid/python/examples/api/ping | 76 --------- qpid/python/examples/api/server | 87 +++++++++++ qpid/python/examples/api/spout | 103 +++++++++++++ qpid/python/qpid/driver.py | 18 ++- qpid/python/qpid/tests/messaging.py | 8 + .../wcf/src/Apache/Qpid/AmqpTypes/AmqpTypes.csproj | 5 + qpid/wcf/src/Apache/Qpid/Channel/Channel.csproj | 8 +- qpid/wcf/src/Apache/Qpid/Interop/Interop.vcproj | 1 + qpid/wcf/src/wcfnet.snk | Bin 0 -> 596 bytes 28 files changed, 633 insertions(+), 177 deletions(-) create mode 100644 qpid/cpp/src/qpid/sys/windows/SocketAddress.cpp delete mode 100755 qpid/python/examples/api/ping create mode 100755 qpid/python/examples/api/server create mode 100755 qpid/python/examples/api/spout create mode 100644 qpid/wcf/src/wcfnet.snk (limited to 'qpid') diff --git a/qpid/cpp/CMakeLists.txt b/qpid/cpp/CMakeLists.txt index 18a7616d99..b15a60ca37 100644 --- a/qpid/cpp/CMakeLists.txt +++ b/qpid/cpp/CMakeLists.txt @@ -101,6 +101,27 @@ if (WIN32) "Directory to load client plug-in modules from") set (QPIDD_MODULE_DIR plugins/broker CACHE STRING "Directory to load broker plug-in modules from") + + # The WCF/C++ client is built separately (it doesn't have a CMakeLists.txt) + # but installed with the C++ components on Windows. + # Don't freak out if it's not there (but it may be good to freak out if + # building the real one...) + install (PROGRAMS + ../wcf/src/Apache/Qpid/Channel/bin/Debug/Apache.Qpid.Channel.dll + ../wcf/src/Apache/Qpid/Channel/bin/Debug/Apache.Qpid.Interop.dll + DESTINATION ${QPID_INSTALL_LIBDIR} + COMPONENT ${QPID_COMPONENT_CLIENT} + OPTIONAL) +# Not sure about this syntax yet... or how to only do it if Client is installed. +# set (CPACK_NSIS_EXTRA_INSTALL_COMMANDS " +# gacutil -I '$INSTDIR\\${QPID_INSTALL_LIBDIR}\\Apache.Qpid.Channel.dll' +# gacutil -I '$INSTDIR\\${QPID_INSTALL_LIBDIR}\\Apache.Qpid.Interop.dll' +# ") +# set (CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS " +# gacutil /u 'Apache.Qpid.Channel' +# gacutil /u 'Apache.Qpid.Interop' +# ") + endif (WIN32) if (CMAKE_SYSTEM_NAME STREQUAL Linux) # Set up install locations. Since the Linux install puts some files in diff --git a/qpid/cpp/src/CMakeLists.txt b/qpid/cpp/src/CMakeLists.txt index 6deacbbece..0c8606c4de 100644 --- a/qpid/cpp/src/CMakeLists.txt +++ b/qpid/cpp/src/CMakeLists.txt @@ -333,6 +333,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL Windows) qpid/sys/windows/PollableCondition.cpp qpid/sys/windows/Shlib.cpp qpid/sys/windows/Socket.cpp + qpid/sys/windows/SocketAddress.cpp qpid/sys/windows/StrError.cpp qpid/sys/windows/SystemInfo.cpp qpid/sys/windows/Thread.cpp @@ -387,6 +388,7 @@ else (CMAKE_SYSTEM_NAME STREQUAL Windows) qpid/sys/posix/Shlib.cpp qpid/log/posix/SinkOptions.cpp qpid/sys/posix/Socket.cpp + qpid/sys/posix/SocketAddress.cpp qpid/sys/posix/StrError.cpp qpid/sys/posix/SystemInfo.cpp qpid/sys/posix/Thread.cpp @@ -738,7 +740,7 @@ set_target_properties (qpidbroker PROPERTIES VERSION ${qpidc_version}) if (MSVC) set_target_properties (qpidbroker PROPERTIES COMPILE_FLAGS /wd4290) endif (MSVC) -install (TARGETS qpidbroker LIBRARY +install (TARGETS qpidbroker DESTINATION ${QPID_INSTALL_LIBDIR} COMPONENT ${QPID_COMPONENT_BROKER}) @@ -772,7 +774,7 @@ add_library (qmf SHARED ${qmf_SOURCES}) target_link_libraries (qmf qmfengine) set_target_properties (qmf PROPERTIES VERSION ${qmf_version}) -install (TARGETS qmf LIBRARY +install (TARGETS qmf OPTIONAL DESTINATION ${QPID_INSTALL_LIBDIR} COMPONENT ${QPID_COMPONENT_QMF}) @@ -806,7 +808,7 @@ add_library (qmfengine SHARED ${qmfengine_SOURCES}) target_link_libraries (qmfengine qpidclient) set_target_properties (qmfengine PROPERTIES VERSION ${qmfengine_version}) -install (TARGETS qmfengine +install (TARGETS qmfengine OPTIONAL DESTINATION ${QPID_INSTALL_LIBDIR} COMPONENT ${QPID_COMPONENT_QMF}) diff --git a/qpid/cpp/src/posix/QpiddBroker.cpp b/qpid/cpp/src/posix/QpiddBroker.cpp index aa934571be..3a20087062 100644 --- a/qpid/cpp/src/posix/QpiddBroker.cpp +++ b/qpid/cpp/src/posix/QpiddBroker.cpp @@ -144,8 +144,16 @@ int QpiddBroker::execute (QpiddOptions *options) { return 1; if (myOptions->daemon.check) cout << pid << endl; - if (myOptions->daemon.quit && kill(pid, SIGINT) < 0) - throw Exception("Failed to stop daemon: " + qpid::sys::strError(errno)); + if (myOptions->daemon.quit) { + if (kill(pid, SIGINT) < 0) + throw Exception("Failed to stop daemon: " + qpid::sys::strError(errno)); + // Wait for the process to die before returning + int retry=10000; // Try up to 10 seconds + while (kill(pid,0) == 0 && --retry) + sys::usleep(1000); + if (retry == 0) + throw Exception("Gave up waiting for daemon process to exit"); + } return 0; } diff --git a/qpid/cpp/src/qpid/sys/Socket.h b/qpid/cpp/src/qpid/sys/Socket.h index d108402682..76b993fd63 100644 --- a/qpid/cpp/src/qpid/sys/Socket.h +++ b/qpid/cpp/src/qpid/sys/Socket.h @@ -39,12 +39,9 @@ public: /** Create a socket wrapper for descriptor. */ QPID_COMMON_EXTERN Socket(); - /** Create an initialized TCP socket */ - void createTcp() const; - /** Set timeout for read and write */ void setTimeout(const Duration& interval) const; - + /** Set socket non blocking */ void setNonblocking() const; @@ -59,7 +56,8 @@ public: *@return The bound port. */ QPID_COMMON_EXTERN int listen(uint16_t port = 0, int backlog = 10) const; - + QPID_COMMON_EXTERN int listen(const SocketAddress&, int backlog = 10) const; + /** Returns the "socket name" ie the address bound to * the near end of the socket */ @@ -102,8 +100,12 @@ public: QPID_COMMON_EXTERN void setTcpNoDelay(bool nodelay) const; private: + /** Create socket */ + void createSocket(const SocketAddress&) const; + Socket(IOHandlePrivate*); mutable std::string connectname; + mutable bool nonblocking; }; }} diff --git a/qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp b/qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp index 9fd0602ce9..fd9a4b3468 100644 --- a/qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp +++ b/qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp @@ -60,19 +60,23 @@ class PollerHandlePrivate { DELETED }; - int fd; ::__uint32_t events; + const IOHandlePrivate* ioHandle; PollerHandle* pollerHandle; FDStat stat; Mutex lock; - PollerHandlePrivate(int f, PollerHandle* p) : - fd(f), + PollerHandlePrivate(const IOHandlePrivate* h, PollerHandle* p) : events(0), + ioHandle(h), pollerHandle(p), stat(ABSENT) { } + int fd() const { + return toFd(ioHandle); + } + bool isActive() const { return stat == MONITORED || stat == MONITORED_HUNGUP; } @@ -131,7 +135,7 @@ class PollerHandlePrivate { }; PollerHandle::PollerHandle(const IOHandle& h) : - impl(new PollerHandlePrivate(toFd(h.impl), this)) + impl(new PollerHandlePrivate(h.impl, this)) {} PollerHandle::~PollerHandle() { @@ -303,7 +307,7 @@ void Poller::registerHandle(PollerHandle& handle) { epe.data.u64 = 0; // Keep valgrind happy epe.data.ptr = &eh; - QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_ADD, eh.fd, &epe)); + QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_ADD, eh.fd(), &epe)); eh.setActive(); } @@ -313,7 +317,7 @@ void Poller::unregisterHandle(PollerHandle& handle) { ScopedLock l(eh.lock); assert(!eh.isIdle()); - int rc = ::epoll_ctl(impl->epollFd, EPOLL_CTL_DEL, eh.fd, 0); + int rc = ::epoll_ctl(impl->epollFd, EPOLL_CTL_DEL, eh.fd(), 0); // Ignore EBADF since deleting a nonexistent fd has the overall required result! // And allows the case where a sloppy program closes the fd and then does the delFd() if (rc == -1 && errno != EBADF) { @@ -344,7 +348,7 @@ void PollerPrivate::resetMode(PollerHandlePrivate& eh) { epe.data.u64 = 0; // Keep valgrind happy epe.data.ptr = &eh; - QPID_POSIX_CHECK(::epoll_ctl(epollFd, EPOLL_CTL_MOD, eh.fd, &epe)); + QPID_POSIX_CHECK(::epoll_ctl(epollFd, EPOLL_CTL_MOD, eh.fd(), &epe)); eh.setActive(); return; @@ -382,7 +386,7 @@ void Poller::monitorHandle(PollerHandle& handle, Direction dir) { epe.data.u64 = 0; // Keep valgrind happy epe.data.ptr = &eh; - QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_MOD, eh.fd, &epe)); + QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_MOD, eh.fd(), &epe)); } void Poller::unmonitorHandle(PollerHandle& handle, Direction dir) { @@ -408,7 +412,7 @@ void Poller::unmonitorHandle(PollerHandle& handle, Direction dir) { epe.data.u64 = 0; // Keep valgrind happy epe.data.ptr = &eh; - QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_MOD, eh.fd, &epe)); + QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_MOD, eh.fd(), &epe)); } void Poller::shutdown() { @@ -443,7 +447,7 @@ bool Poller::interrupt(PollerHandle& handle) { epe.events = 0; epe.data.u64 = 0; // Keep valgrind happy epe.data.ptr = &eh; - QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_MOD, eh.fd, &epe)); + QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_MOD, eh.fd(), &epe)); if (eh.isInactive()) { eh.setInterrupted(); diff --git a/qpid/cpp/src/qpid/sys/posix/Socket.cpp b/qpid/cpp/src/qpid/sys/posix/Socket.cpp index 02004b1999..481aa6c88e 100644 --- a/qpid/cpp/src/qpid/sys/posix/Socket.cpp +++ b/qpid/cpp/src/qpid/sys/posix/Socket.cpp @@ -97,22 +97,30 @@ std::string getService(int fd, bool local) } Socket::Socket() : - IOHandle(new IOHandlePrivate) -{ - createTcp(); -} + IOHandle(new IOHandlePrivate), + nonblocking(false) +{} Socket::Socket(IOHandlePrivate* h) : - IOHandle(h) + IOHandle(h), + nonblocking(false) {} -void Socket::createTcp() const +void Socket::createSocket(const SocketAddress& sa) const { int& socket = impl->fd; if (socket != -1) Socket::close(); - int s = ::socket (AF_INET, SOCK_STREAM, 0); + int s = ::socket(getAddrInfo(sa).ai_family, getAddrInfo(sa).ai_socktype, 0); if (s < 0) throw QPID_POSIX_ERROR(errno); socket = s; + + try { + if (nonblocking) setNonblocking(); + } catch (std::exception&) { + ::close(s); + socket = -1; + throw; + } } void Socket::setTimeout(const Duration& interval) const @@ -125,7 +133,9 @@ void Socket::setTimeout(const Duration& interval) const } void Socket::setNonblocking() const { - QPID_POSIX_CHECK(::fcntl(impl->fd, F_SETFL, O_NONBLOCK)); + int& socket = impl->fd; + if (socket != -1) QPID_POSIX_CHECK(::fcntl(socket, F_SETFL, O_NONBLOCK)); + nonblocking = true; } void Socket::connect(const std::string& host, uint16_t port) const @@ -138,8 +148,9 @@ void Socket::connect(const SocketAddress& addr) const { connectname = addr.asString(); - const int& socket = impl->fd; + createSocket(addr); + const int& socket = impl->fd; // TODO the correct thing to do here is loop on failure until you've used all the returned addresses if ((::connect(socket, getAddrInfo(addr).ai_addr, getAddrInfo(addr).ai_addrlen) < 0) && (errno != EINPROGRESS)) { @@ -157,16 +168,23 @@ Socket::close() const } int Socket::listen(uint16_t port, int backlog) const +{ + SocketAddress sa("", boost::lexical_cast(port)); + + createSocket(sa); + return listen(sa, backlog); +} + +int Socket::listen(const SocketAddress& sa, int backlog) const { const int& socket = impl->fd; int yes=1; QPID_POSIX_CHECK(setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes))); - SocketAddress sa("", boost::lexical_cast(port)); if (::bind(socket, getAddrInfo(sa).ai_addr, getAddrInfo(sa).ai_addrlen) < 0) - throw Exception(QPID_MSG("Can't bind to port " << port << ": " << strError(errno))); + throw Exception(QPID_MSG("Can't bind to port " << sa.asString() << ": " << strError(errno))); if (::listen(socket, backlog) < 0) - throw Exception(QPID_MSG("Can't listen on port " << port << ": " << strError(errno))); + throw Exception(QPID_MSG("Can't listen on port " << sa.asString() << ": " << strError(errno))); struct sockaddr_in name; socklen_t namelen = sizeof(name); diff --git a/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp b/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp index 475b18600d..971f0bb665 100644 --- a/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp +++ b/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp @@ -216,7 +216,7 @@ AsynchConnector::AsynchConnector(const Socket& sock, connCallback(socket); } catch(std::exception& e) { if (failCallback) - failCallback(-1, std::string(e.what())); + failCallback(socket, -1, std::string(e.what())); socket.close(); delete &socket; } diff --git a/qpid/cpp/src/qpid/sys/windows/Socket.cpp b/qpid/cpp/src/qpid/sys/windows/Socket.cpp index 18fa7c3b1c..8e6233bbf8 100755 --- a/qpid/cpp/src/qpid/sys/windows/Socket.cpp +++ b/qpid/cpp/src/qpid/sys/windows/Socket.cpp @@ -20,19 +20,18 @@ */ #include "qpid/sys/Socket.h" +#include "qpid/sys/SocketAddress.h" #include "qpid/sys/windows/IoHandlePrivate.h" #include "qpid/sys/windows/check.h" #include "qpid/sys/Time.h" #include #include -#include -#include #include -#include #include +#include // Need to initialize WinSock. Ideally, this would be a singleton or embedded // in some one-time initialization function. I tried boost singleton and could @@ -138,20 +137,36 @@ std::string getService(SOCKET fd, bool local) Socket::Socket() : IOHandle(new IOHandlePrivate) { - createTcp(); + SOCKET& socket = impl->fd; + if (socket != INVALID_SOCKET) Socket::close(); + SOCKET s = ::socket (PF_INET, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) throw QPID_WINDOWS_ERROR(WSAGetLastError()); + socket = s; } Socket::Socket(IOHandlePrivate* h) : IOHandle(h) {} -void Socket::createTcp() const +void +Socket::createSocket(const SocketAddress& sa) const { SOCKET& socket = impl->fd; if (socket != INVALID_SOCKET) Socket::close(); - SOCKET s = ::socket (PF_INET, SOCK_STREAM, 0); + + SOCKET s = ::socket (getAddrInfo(sa).ai_family, + getAddrInfo(sa).ai_socktype, + 0); if (s == INVALID_SOCKET) throw QPID_WINDOWS_ERROR(WSAGetLastError()); socket = s; + + try { + if (nonblocking) setNonblocking(); + } catch (std::exception&) { + closesocket(s); + socket = INVALID_SOCKET; + throw; + } } void Socket::setTimeout(const Duration& interval) const @@ -175,41 +190,26 @@ void Socket::setNonblocking() const { void Socket::connect(const std::string& host, uint16_t port) const { - std::stringstream portstream; - portstream << port << std::ends; - std::string portstr = portstream.str(); - std::stringstream namestream; - namestream << host << ":" << port; - connectname = namestream.str(); + SocketAddress sa(host, boost::lexical_cast(port)); + connect(sa); +} +void +Socket::connect(const SocketAddress& addr) const +{ const SOCKET& socket = impl->fd; - // TODO: Be good to make this work for IPv6 as well as IPv4. Would require - // other changes, such as waiting to create the socket until after we - // have the address family. Maybe unbundle the translation of names here; - // use TcpAddress to resolve things and make this class take a TcpAddress - // and grab its address family to create the socket. - struct addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; // We always creating AF_INET-only sockets. - hints.ai_socktype = SOCK_STREAM; // We always do TCP - addrinfo *addrs; - int status = getaddrinfo(host.c_str(), portstr.c_str(), &hints, &addrs); - if (status != 0) - throw Exception(QPID_MSG("Cannot resolve " << host << ": " << - gai_strerror(status))); - addrinfo *addr = addrs; + const addrinfo *addrs = &(getAddrInfo(addr)); int error = 0; WSASetLastError(0); - while (addr != 0) { - if ((::connect(socket, addr->ai_addr, addr->ai_addrlen) == 0) || + while (addrs != 0) { + if ((::connect(socket, addrs->ai_addr, addrs->ai_addrlen) == 0) || (WSAGetLastError() == WSAEWOULDBLOCK)) break; // Error... save this error code and see if there are other address // to try before throwing the exception. error = WSAGetLastError(); - addr = addr->ai_next; + addrs = addrs->ai_next; } - freeaddrinfo(addrs); if (error) throw qpid::Exception(QPID_MSG(strError(error) << ": " << connectname)); } diff --git a/qpid/cpp/src/qpid/sys/windows/SocketAddress.cpp b/qpid/cpp/src/qpid/sys/windows/SocketAddress.cpp new file mode 100644 index 0000000000..a3e03c9be8 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/windows/SocketAddress.cpp @@ -0,0 +1,70 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/SocketAddress.h" + +#include "qpid/sys/windows/check.h" + +#include +#include + +namespace qpid { +namespace sys { + +SocketAddress::SocketAddress(const std::string& host0, const std::string& port0) : + host(host0), + port(port0), + addrInfo(0) +{ + ::addrinfo hints; + ::memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; // In order to allow AF_INET6 we'd have to change createTcp() as well + hints.ai_socktype = SOCK_STREAM; + + const char* node = 0; + if (host.empty()) { + hints.ai_flags |= AI_PASSIVE; + } else { + node = host.c_str(); + } + const char* service = port.empty() ? "0" : port.c_str(); + + int n = ::getaddrinfo(node, service, &hints, &addrInfo); + if (n != 0) + throw Exception(QPID_MSG("Cannot resolve " << host << ": " << ::gai_strerror(n))); +} + +SocketAddress::~SocketAddress() +{ + ::freeaddrinfo(addrInfo); +} + +std::string SocketAddress::asString() const +{ + return host + ":" + port; +} + +const ::addrinfo& getAddrInfo(const SocketAddress& sa) +{ + return *sa.addrInfo; +} + +}} diff --git a/qpid/java/common.xml b/qpid/java/common.xml index 3393be7070..6b9c961b35 100644 --- a/qpid/java/common.xml +++ b/qpid/java/common.xml @@ -51,6 +51,9 @@ + + + diff --git a/qpid/java/module.xml b/qpid/java/module.xml index 5796af928a..9fcc8ded4d 100644 --- a/qpid/java/module.xml +++ b/qpid/java/module.xml @@ -287,9 +287,9 @@ - - - + + + diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/failover/MessageDisappearWithIOExceptionTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/failover/MessageDisappearWithIOExceptionTest.java index f1a1c1a9a8..e507ebc534 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/failover/MessageDisappearWithIOExceptionTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/failover/MessageDisappearWithIOExceptionTest.java @@ -179,9 +179,16 @@ public class MessageDisappearWithIOExceptionTest extends FailoverBaseCase implem messages.remove(0).getIntProperty("count"), received.getIntProperty("count")); - // Allow ack to be sent to broker, by performing a synchronous command - // along the session. -// _session.createConsumer(_session.createTemporaryQueue()).close(); + // When the Exception is received by the underlying IO layer it will + // initiate failover. The first step of which is to ensure that the + // existing conection is closed. So in this situation the connection + // will be flushed casuing the above ACK to be sent to the broker. + // + // That said: + // when the socket close is detected on the server it will rise up the + // Mina filter chain and interrupt processing. + // this has been raised as QPID-2138 + _session.createConsumer(_session.createTemporaryQueue()).close(); //Retain IO Layer AMQProtocolSession protocolSession = _connection.getProtocolHandler().getProtocolSession(); @@ -260,8 +267,14 @@ public class MessageDisappearWithIOExceptionTest extends FailoverBaseCase implem private void initialiseConnection() throws Exception { - //Create Connection - _connection = (AMQConnection) getConnection(); + //Create Connection using the default connection URL. i.e. not the Failover URL that would be used by default + _connection = (AMQConnection) getConnection(getConnectionFactory("default").getConnectionURL()); + // The default connection does not have any retries configured so + // Allow this connection to retry so that we can block on the failover. + // The alternative would be to use the getConnection() default. However, + // this would add additional complexity in the logging as a second + // broker is defined in that url. We do not need it for this test. + _connection.getFailoverPolicy().getCurrentMethod().setRetries(1); _connection.setConnectionListener(this); _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java index 3e5470d5cb..7a7d7f646e 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java @@ -147,10 +147,23 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E setUpACLTest(); + //QPID-2081: use a latch to sync on exception causing connection close, to work + //around the connection close race during tearDown() causing sporadic failures + final CountDownLatch exceptionReceived = new CountDownLatch(1); + try { Connection conn = getConnection("guest", "guest"); + conn.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException e) + { + exceptionReceived.countDown(); + } + }); + + Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); @@ -166,6 +179,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E assertNotNull("There was no liked exception", cause); assertEquals("Wrong linked exception type", AMQAuthenticationException.class, cause.getClass()); assertEquals("Incorrect error code received", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode()); + + //use the latch to ensure the control thread waits long enough for the exception thread + //to have done enough to mark the connection closed before teardown commences + assertTrue("Timed out waiting for conneciton to report close", + exceptionReceived.await(2, TimeUnit.SECONDS)); } } @@ -195,6 +213,10 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E { setUpACLTest(); + //QPID-2081: use a latch to sync on exception causing connection close, to work + //around the connection close race during tearDown() causing sporadic failures + final CountDownLatch exceptionReceived = new CountDownLatch(1); + try { Connection conn = getConnection("client", "guest"); @@ -202,6 +224,14 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E //Prevent Failover ((AMQConnection) conn).setConnectionListener(this); + conn.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException e) + { + exceptionReceived.countDown(); + } + }); + Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); @@ -217,6 +247,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E assertNotNull("There was no liked exception", cause); assertEquals("Wrong linked exception type", AMQAuthenticationException.class, cause.getClass()); assertEquals("Incorrect error code received", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode()); + + //use the latch to ensure the control thread waits long enough for the exception thread + //to have done enough to mark the connection closed before teardown commences + assertTrue("Timed out waiting for conneciton to report close", + exceptionReceived.await(2, TimeUnit.SECONDS)); } } @@ -248,6 +283,10 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E { setUpACLTest(); + //QPID-2081: use a latch to sync on exception causing connection close, to work + //around the connection close race during tearDown() causing sporadic failures + final CountDownLatch exceptionReceived = new CountDownLatch(1); + try { Connection conn = getConnection("client", "guest"); @@ -256,6 +295,14 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E conn.start(); + conn.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException e) + { + exceptionReceived.countDown(); + } + }); + //Create a Named Queue ((AMQSession) sesh).createQueue(new AMQShortString("IllegalQueue"), false, false, false); @@ -266,6 +313,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E { amqe.printStackTrace(); assertEquals("Incorrect error code thrown", 403, ((AMQAuthenticationException) amqe).getErrorCode().getCode()); + + //use the latch to ensure the control thread waits long enough for the exception thread + //to have done enough to mark the connection closed before teardown commences + assertTrue("Timed out waiting for conneciton to report close", + exceptionReceived.await(2, TimeUnit.SECONDS)); } } @@ -334,11 +386,23 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E { setUpACLTest(); + //QPID-2081: use a latch to sync on exception causing connection close, to work + //around the connection close race during tearDown() causing sporadic failures + final CountDownLatch exceptionReceived = new CountDownLatch(1); + try { Connection conn = getConnection("client", "guest"); ((AMQConnection) conn).setConnectionListener(this); + + conn.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException e) + { + exceptionReceived.countDown(); + } + }); Session session = conn.createSession(true, Session.AUTO_ACKNOWLEDGE); @@ -370,6 +434,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E foundCorrectException = true; } } + + //use the latch to ensure the control thread waits long enough for the exception thread + //to have done enough to mark the connection closed before teardown commences + assertTrue("Timed out waiting for conneciton to report close", + exceptionReceived.await(2, TimeUnit.SECONDS)); } assertTrue("Did not get AMQAuthenticationException thrown", foundCorrectException); } @@ -400,10 +469,22 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E { setUpACLTest(); + //QPID-2081: use a latch to sync on exception causing connection close, to work + //around the connection close race during tearDown() causing sporadic failures + final CountDownLatch exceptionReceived = new CountDownLatch(1); + try { Connection conn = getConnection("client", "guest"); + conn.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException e) + { + exceptionReceived.countDown(); + } + }); + Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); @@ -420,6 +501,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E assertNotNull("There was no liked exception", cause); assertEquals("Wrong linked exception type", AMQAuthenticationException.class, cause.getClass()); assertEquals("Incorrect error code received", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode()); + + //use the latch to ensure the control thread waits long enough for the exception thread + //to have done enough to mark the connection closed before teardown commences + assertTrue("Timed out waiting for conneciton to report close", + exceptionReceived.await(2, TimeUnit.SECONDS)); } } @@ -427,10 +513,22 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E { setUpACLTest(); + //QPID-2081: use a latch to sync on exception causing connection close, to work + //around the connection close race during tearDown() causing sporadic failures + final CountDownLatch exceptionReceived = new CountDownLatch(1); + try { Connection conn = getConnection("server", "guest"); + conn.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException e) + { + exceptionReceived.countDown(); + } + }); + Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); @@ -446,6 +544,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E assertNotNull("There was no liked exception", cause); assertEquals("Wrong linked exception type", AMQAuthenticationException.class, cause.getClass()); assertEquals("Incorrect error code received", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode()); + + //use the latch to ensure the control thread waits long enough for the exception thread + //to have done enough to mark the connection closed before teardown commences + assertTrue("Timed out waiting for conneciton to report close", + exceptionReceived.await(2, TimeUnit.SECONDS)); } } @@ -487,10 +590,22 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E { setUpACLTest(); + //QPID-2081: use a latch to sync on exception causing connection close, to work + //around the connection close race during tearDown() causing sporadic failures + final CountDownLatch exceptionReceived = new CountDownLatch(1); + try { Connection conn = getConnection("server", "guest"); + conn.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException e) + { + exceptionReceived.countDown(); + } + }); + Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); @@ -504,6 +619,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E catch (AMQAuthenticationException amqe) { assertEquals("Incorrect error code thrown", 403, amqe.getErrorCode().getCode()); + + //use the latch to ensure the control thread waits long enough for the exception thread + //to have done enough to mark the connection closed before teardown commences + assertTrue("Timed out waiting for conneciton to report close", + exceptionReceived.await(2, TimeUnit.SECONDS)); } } @@ -511,10 +631,22 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E { setUpACLTest(); + //QPID-2081: use a latch to sync on exception causing connection close, to work + //around the connection close race during tearDown() causing sporadic failures + final CountDownLatch exceptionReceived = new CountDownLatch(1); + try { Connection conn = getConnection("server", "guest"); + conn.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException e) + { + exceptionReceived.countDown(); + } + }); + Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); @@ -531,6 +663,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E assertNotNull("There was no liked exception", cause); assertEquals("Wrong linked exception type", AMQAuthenticationException.class, cause.getClass()); assertEquals("Incorrect error code received", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode()); + + //use the latch to ensure the control thread waits long enough for the exception thread + //to have done enough to mark the connection closed before teardown commences + assertTrue("Timed out waiting for conneciton to report close", + exceptionReceived.await(2, TimeUnit.SECONDS)); } } @@ -538,11 +675,23 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E { setUpACLTest(); + //QPID-2081: use a latch to sync on exception causing connection close, to work + //around the connection close race during tearDown() causing sporadic failures + final CountDownLatch exceptionReceived = new CountDownLatch(1); + Connection connection = null; try { connection = getConnection("server", "guest"); + connection.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException e) + { + exceptionReceived.countDown(); + } + }); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); connection.start(); @@ -556,6 +705,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E catch (AMQAuthenticationException amqe) { assertEquals("Incorrect error code thrown", 403, amqe.getErrorCode().getCode()); + + //use the latch to ensure the control thread waits long enough for the exception thread + //to have done enough to mark the connection closed before teardown commences + assertTrue("Timed out waiting for conneciton to report close", + exceptionReceived.await(2, TimeUnit.SECONDS)); } } @@ -653,9 +807,21 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E { setUpACLTest(); + //QPID-2081: use a latch to sync on exception causing connection close, to work + //around the connection close race during tearDown() causing sporadic failures + final CountDownLatch exceptionReceived = new CountDownLatch(1); + try { Connection conn = getConnection("server", "guest"); + + conn.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException e) + { + exceptionReceived.countDown(); + } + }); ((AMQConnection) conn).setConnectionListener(this); @@ -699,6 +865,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener, E assertEquals("Incorrect exception", AMQAuthenticationException.class, cause.getClass()); assertEquals("Incorrect error code thrown", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode()); } + + //use the latch to ensure the control thread waits long enough for the exception thread + //to have done enough to mark the connection closed before teardown commences + assertTrue("Timed out waiting for conneciton to report close", + exceptionReceived.await(2, TimeUnit.SECONDS)); } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverOnMessageTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverOnMessageTest.java index f22a405fc3..7c5db290c4 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverOnMessageTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverOnMessageTest.java @@ -155,7 +155,7 @@ public class AcknowledgeAfterFailoverOnMessageTest extends AcknowledgeOnMessageT public void onMessage(Message message) { // Stop processing if we have an error and had to stop running. - if (_receviedAll.getCount() == 0) + if (_receivedAll.getCount() == 0) { _logger.debug("Dumping msgs due to error(" + _causeOfFailure.get().getMessage() + "):" + message); return; @@ -191,7 +191,7 @@ public class AcknowledgeAfterFailoverOnMessageTest extends AcknowledgeOnMessageT // Acknowledge the first message if we are now on the cleaned pass if (cleaned) { - _receviedAll.countDown(); + _receivedAll.countDown(); } return; @@ -234,7 +234,7 @@ public class AcknowledgeAfterFailoverOnMessageTest extends AcknowledgeOnMessageT // this will then trigger test teardown. if (cleaned) { - _receviedAll.countDown(); + _receivedAll.countDown(); } //Reset message count so we can try again. diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverTest.java index eb36522fac..ae7e30c231 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverTest.java @@ -299,7 +299,7 @@ public class AcknowledgeAfterFailoverTest extends AcknowledgeTest implements Con } catch (InterruptedException e) { - fail("Failover was interuppted"); + fail("Failover was interrupted"); } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeOnMessageTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeOnMessageTest.java index 4254727d36..a2703be298 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeOnMessageTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeOnMessageTest.java @@ -34,7 +34,7 @@ import java.util.concurrent.atomic.AtomicReference; public class AcknowledgeOnMessageTest extends AcknowledgeTest implements MessageListener { - protected CountDownLatch _receviedAll; + protected CountDownLatch _receivedAll; protected AtomicReference _causeOfFailure = new AtomicReference(null); @Override @@ -46,7 +46,7 @@ public class AcknowledgeOnMessageTest extends AcknowledgeTest implements Message @Override public void init(boolean transacted, int mode) throws Exception { - _receviedAll = new CountDownLatch(NUM_MESSAGES); + _receivedAll = new CountDownLatch(NUM_MESSAGES); super.init(transacted, mode); _consumer.setMessageListener(this); @@ -64,26 +64,36 @@ public class AcknowledgeOnMessageTest extends AcknowledgeTest implements Message _connection.start(); - int lastCount = (int) _receviedAll.getCount(); + // Set the lastCount to NUM_MESSAGES, this ensures that the compare + // against the receviedAll count is accurate. + int lastCount = NUM_MESSAGES; - boolean complete = _receviedAll.await(5000L, TimeUnit.MILLISECONDS); + // Wait for messages to arrive + boolean complete = _receivedAll.await(5000L, TimeUnit.MILLISECONDS); + // If the messasges haven't arrived while (!complete) { - int currentCount = (int) _receviedAll.getCount(); + // Check how many we have received + int currentCount = (int) _receivedAll.getCount(); // make sure we have received a message in the last cycle. if (lastCount == currentCount) { + // If we didn't receive any messages then stop. + // Something must have gone wrong. + System.err.println("Giving up waiting as we didn't receive anything."); break; } // Remember the currentCount as the lastCount for the next cycle. // so we can exit if things get locked up. lastCount = currentCount; - complete = _receviedAll.await(5000L, TimeUnit.MILLISECONDS); + // Wait again for messages to arrive. + complete = _receivedAll.await(5000L, TimeUnit.MILLISECONDS); } + // If we failed to receive all the messages then fail the test. if (!complete) { // Check to see if we ended due to an exception in the onMessage handler @@ -95,10 +105,11 @@ public class AcknowledgeOnMessageTest extends AcknowledgeTest implements Message } else { - fail("All messages not received missing:" + _receviedAll.getCount() + "/" + NUM_MESSAGES); + fail("All messages not received missing:" + _receivedAll.getCount() + "/" + NUM_MESSAGES); } } + // Even if we received all the messages. // Check to see if we ended due to an exception in the onMessage handler Exception cause = _causeOfFailure.get(); if (cause != null) @@ -131,7 +142,7 @@ public class AcknowledgeOnMessageTest extends AcknowledgeTest implements Message { try { - int count = NUM_MESSAGES - (int) _receviedAll.getCount(); + int count = NUM_MESSAGES - (int) _receivedAll.getCount(); assertEquals("Incorrect message received", count, message.getIntProperty(INDEX)); @@ -144,7 +155,7 @@ public class AcknowledgeOnMessageTest extends AcknowledgeTest implements Message doAcknowlegement(message); - _receviedAll.countDown(); + _receivedAll.countDown(); } catch (Exception e) { @@ -162,9 +173,9 @@ public class AcknowledgeOnMessageTest extends AcknowledgeTest implements Message { _causeOfFailure.set(e); // End the test. - while (_receviedAll.getCount() != 0) + while (_receivedAll.getCount() != 0) { - _receviedAll.countDown(); + _receivedAll.countDown(); } } } diff --git a/qpid/java/test-profiles/010Excludes b/qpid/java/test-profiles/010Excludes index 757a1e425c..454aede07e 100755 --- a/qpid/java/test-profiles/010Excludes +++ b/qpid/java/test-profiles/010Excludes @@ -75,9 +75,6 @@ org.apache.qpid.server.AlertingTest#* // The C++ server has a totally different persistence mechanism org.apache.qpid.server.store.PersistentStoreTest#* -// QPID-1225 : Temporary remove this test until the problem has been addressed -org.apache.qpid.server.security.acl.SimpleACLTest#testClientPublishInvalidQueueSuccess - // CPP Broker does not follow the same Logging convention as the Java broker org.apache.qpid.server.logging.* diff --git a/qpid/java/test-profiles/08StandaloneExcludes b/qpid/java/test-profiles/08StandaloneExcludes index ee781fb80f..ed12973498 100644 --- a/qpid/java/test-profiles/08StandaloneExcludes +++ b/qpid/java/test-profiles/08StandaloneExcludes @@ -23,7 +23,6 @@ org.apache.qpid.test.client.failover.FailoverTest#* // InVM Broker tests awaiting resolution of QPID-1103 org.apache.qpid.test.client.timeouts.SyncWaitDelayTest#* org.apache.qpid.test.client.timeouts.SyncWaitTimeoutDelayTest#* -org.apache.qpid.server.security.acl.SimpleACLTest#* // Those tests are written against the 0.10 path org.apache.qpid.test.unit.message.UTF8Test#* diff --git a/qpid/java/test-profiles/Excludes b/qpid/java/test-profiles/Excludes index aa60554c04..c9c9e91836 100644 --- a/qpid/java/test-profiles/Excludes +++ b/qpid/java/test-profiles/Excludes @@ -17,9 +17,6 @@ org.apache.qpid.server.logging.MemoryMessageStoreLoggingTest#testMessageStoreClo // QPID-XXX : Test fails to start external broker due to Derby Exception. org.apache.qpid.server.logging.DerbyMessageStoreLoggingTest#* -// QPID-2081 :The configuration changes are now highlighting the close race condition -org.apache.qpid.server.security.acl.SimpleACLTest#* - // QPID-1816 : Client Ack has not been addressed org.apache.qpid.test.unit.ack.AcknowledgeAfterFailoverOnMessageTest#testDirtyClientAck org.apache.qpid.test.unit.ack.AcknowledgeAfterFailoverOnMessageTest#testClientAck diff --git a/qpid/python/examples/api/ping b/qpid/python/examples/api/ping deleted file mode 100755 index 59b367cca6..0000000000 --- a/qpid/python/examples/api/ping +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT 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 optparse, time -from qpid.messaging import * -from qpid.util import URL - -parser = optparse.OptionParser(usage="usage: %prog [options] ADDRESS [ CONTENT ... ]", - description="Drain messages from the supplied address.") -parser.add_option("-b", "--broker", default="localhost", - help="connect to specified BROKER (default %default)") -parser.add_option("-c", "--count", type=int, default=1, - help="stop after count messages have been sent, zero disables (default %default)") -parser.add_option("-t", "--timeout", type=float, default=None, - help="exit after the specified time") -parser.add_option("-m", "--map", action="store_true", - help="interpret content as map") -parser.add_option("-i", "--id", help="use the supplied id instead of generating one") - -opts, args = parser.parse_args() - -url = URL(opts.broker) -if opts.id is None: - ping_id = str(uuid4()) -else: - ping_id = opts.id -if args: - addr = args.pop(0) -else: - parser.error("address is required") -if args: - content = " ".join(args) - if opts.map: - content = eval(content) -else: - content = None - -# XXX: should make URL default the port for us -conn = Connection.open(url.host, url.port or AMQP_PORT, - username=url.user, password=url.password) -ssn = conn.session() -snd = ssn.sender(addr) - -count = 0 -start = time.time() -while (opts.count == 0 or count < opts.count) and \ - (opts.timeout is None or time.time() - start < opts.timeout): - msg = Message(content) - msg.properties["ping-id"] = "%s:%s" % (ping_id, count) - - try: - snd.send(msg) - count += 1 - print msg - except SendError, e: - print e - break - -conn.close() diff --git a/qpid/python/examples/api/server b/qpid/python/examples/api/server new file mode 100755 index 0000000000..adb2dcf792 --- /dev/null +++ b/qpid/python/examples/api/server @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT 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 optparse, sys, traceback +from qpid.messaging import * +from qpid.util import URL +from subprocess import Popen, STDOUT, PIPE +from qpid.log import enable, DEBUG, WARN + +parser = optparse.OptionParser(usage="usage: %prog [options] ADDRESS ...", + description="handle requests from the supplied address.") +parser.add_option("-b", "--broker", default="localhost", + help="connect to specified BROKER (default %default)") +parser.add_option("-v", dest="verbose", action="store_true", help="enable logging") + +opts, args = parser.parse_args() + +if opts.verbose: + enable("qpid", DEBUG) +else: + enable("qpid", WARN) + +url = URL(opts.broker) +if args: + addr = args.pop(0) +else: + parser.error("address is required") + +# XXX: should make URL default the port for us +conn = Connection.open(url.host, url.port or AMQP_PORT, + username=url.user, password=url.password) +conn.reconnect = True +ssn = conn.session() +rcv = ssn.receiver(addr) + +def dispatch(msg): + msg_type = msg.properties.get("type") + if msg_type == "shell": + proc = Popen(msg.content, shell=True, stderr=STDOUT, stdin=PIPE, stdout=PIPE) + output, _ = proc.communicate() + result = Message(output) + result.properties["exit"] = proc.returncode + elif msg_type == "eval": + try: + content = eval(msg.content) + except: + content = traceback.format_exc() + result = Message(content) + else: + result = Message("unrecognized message type: %s" % msg_type) + return result + +while True: + try: + msg = rcv.fetch() + response = dispatch(msg) + snd = ssn.sender(msg.reply_to) + try: + snd.send(response) + except SendError, e: + print e + snd.close() + ssn.acknowledge() + except Empty: + break + except ReceiveError, e: + print e + break + +conn.close() diff --git a/qpid/python/examples/api/spout b/qpid/python/examples/api/spout new file mode 100755 index 0000000000..6a9b2b6e3d --- /dev/null +++ b/qpid/python/examples/api/spout @@ -0,0 +1,103 @@ +#!/usr/bin/env python +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT 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 optparse, time +from qpid.messaging import * +from qpid.util import URL + +def nameval(st): + idx = st.find("=") + if idx >= 0: + name = st[0:idx] + value = st[idx+1:] + else: + name = st + value = None + return name, value + +parser = optparse.OptionParser(usage="usage: %prog [options] ADDRESS [ CONTENT ... ]", + description="Send messages to the supplied address.") +parser.add_option("-b", "--broker", default="localhost", + help="connect to specified BROKER (default %default)") +parser.add_option("-c", "--count", type=int, default=1, + help="stop after count messages have been sent, zero disables (default %default)") +parser.add_option("-t", "--timeout", type=float, default=None, + help="exit after the specified time") +parser.add_option("-i", "--id", help="use the supplied id instead of generating one") +parser.add_option("-r", "--reply-to", help="specify reply-to address") +parser.add_option("-P", "--property", dest="properties", action="append", default=[], + help="specify message property") +parser.add_option("-M", "--map", dest="entries", action="append", default=[], + help="specify map entry for message body") + +opts, args = parser.parse_args() + +url = URL(opts.broker) +if opts.id is None: + spout_id = str(uuid4()) +else: + spout_id = opts.id +if args: + addr = args.pop(0) +else: + parser.error("address is required") + +content = None + +if args: + text = " ".join(args) +else: + text = None + +if opts.entries: + content = {} + if text: + content["text"] = text + for e in opts.entries: + name, val = nameval(e) + content[name] = val +else: + content = text + +# XXX: should make URL default the port for us +conn = Connection.open(url.host, url.port or AMQP_PORT, + username=url.user, password=url.password) +ssn = conn.session() +snd = ssn.sender(addr) + +count = 0 +start = time.time() +while (opts.count == 0 or count < opts.count) and \ + (opts.timeout is None or time.time() - start < opts.timeout): + msg = Message(content, reply_to=opts.reply_to) + msg.properties["spout-id"] = "%s:%s" % (spout_id, count) + for p in opts.properties: + name, val = nameval(p) + msg.properties[name] = val + + try: + snd.send(msg) + count += 1 + print msg + except SendError, e: + print e + break + +conn.close() diff --git a/qpid/python/qpid/driver.py b/qpid/python/qpid/driver.py index 7c293fe146..588b46064c 100644 --- a/qpid/python/qpid/driver.py +++ b/qpid/python/qpid/driver.py @@ -439,14 +439,19 @@ class Driver: if _snd is None and not snd.closing and not snd.closed: _snd = Attachment(snd) + if snd.target is None: + snd.error = ("target is None",) + snd.closed = True + return + try: _snd.name, _snd.subject, _snd.options = address.parse(snd.target) except address.LexError, e: - snd.error = e + snd.error = (e,) snd.closed = True return except address.ParseError, e: - snd.error = e + snd.error = (e,) snd.closed = True return @@ -502,14 +507,19 @@ class Driver: _rcv.canceled = False _rcv.draining = False + if rcv.source is None: + rcv.error = ("source is None",) + rcv.closed = True + return + try: _rcv.name, _rcv.subject, _rcv.options = address.parse(rcv.source) except address.LexError, e: - rcv.error = e + rcv.error = (e,) rcv.closed = True return except address.ParseError, e: - rcv.error = e + rcv.error = (e,) rcv.closed = True return diff --git a/qpid/python/qpid/tests/messaging.py b/qpid/python/qpid/tests/messaging.py index 2e4c0ca1ab..860c3660d1 100644 --- a/qpid/python/qpid/tests/messaging.py +++ b/qpid/python/qpid/tests/messaging.py @@ -597,6 +597,14 @@ class AddressErrorTests(Base): assert check(e), "unexpected error: %s" % e rcv.close() + def testNoneTarget(self): + # XXX: should have specific exception for this + self.sendErrorTest(None, SendError) + + def testNoneSource(self): + # XXX: should have specific exception for this + self.fetchErrorTest(None, ReceiveError) + def testNoTarget(self): # XXX: should have specific exception for this self.sendErrorTest(NOSUCH_Q, SendError, lambda e: NOSUCH_Q in str(e)) diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpTypes.csproj b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpTypes.csproj index 9c13d47296..cca131b98a 100644 --- a/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpTypes.csproj +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpTypes.csproj @@ -31,6 +31,8 @@ under the License. v3.5 512 OnBuildSuccess + true + ..\..\..\wcfnet.snk true @@ -73,6 +75,9 @@ under the License. + + + ${basedir}${file.separator}${broker.home.dir} ${qpid.home}${file.separator}qbtc-output - ${project.basedir}${file.separator}..${file.separator}test-profiles${file.separator}log4j-test.xml + ${basedir}${file.separator}target${file.separator}surefire-reports + ${basedir}/.. ${basedir}/.. ${basedir} @@ -83,7 +84,14 @@ integration-test phase below instead --> true - ${test.log4j.configuration.file} + ${test.output.dir} + ${basedir}${file.separator}target${file.separator}classes/log4j.xml + + false + true + + ${basedir}${file.separator}target${file.separator}classes/spawned-broker-log4j.xml diff --git a/qpid/java/qpid-test-utils/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java b/qpid/java/qpid-test-utils/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java index 5c9dc12d77..9238224f50 100644 --- a/qpid/java/qpid-test-utils/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java +++ b/qpid/java/qpid-test-utils/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java @@ -46,7 +46,7 @@ public class QpidTestCase extends TestCase public static final String TEST_RESOURCES_DIR = QPID_HOME + "/../test-profiles/test_resources/"; public static final String TEST_PROFILES_DIR = QPID_HOME + "/../test-profiles/"; public static final String TMP_FOLDER = System.getProperty("java.io.tmpdir"); - public static final String LOG4J_CONFIG_FILE_PATH = System.getProperty("log4j.configuration.file"); + public static final String SPAWNED_BROKER_LOG4J_CONFIG_FILE_PATH = System.getProperty("spawnedbroker.log4j.configuration.file"); private static final Logger _logger = Logger.getLogger(QpidTestCase.class); diff --git a/qpid/java/systests/pom.xml b/qpid/java/systests/pom.xml index d6c095dd4c..26e1d5b974 100644 --- a/qpid/java/systests/pom.xml +++ b/qpid/java/systests/pom.xml @@ -189,4 +189,15 @@ + + + + src/main/resources + + + ${test.resource.directory}/test-profiles/test_resources + + + + diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/BrokerHolder.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/BrokerHolder.java index 3af57327d2..4537d1a678 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/BrokerHolder.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/BrokerHolder.java @@ -20,10 +20,19 @@ */ package org.apache.qpid.test.utils; +import org.apache.qpid.server.BrokerOptions; + public interface BrokerHolder { - String getWorkingDirectory(); + public void start(BrokerOptions options) throws Exception; void shutdown(); void kill(); String dumpThreads(); + + enum BrokerType + { + EXTERNAL /** Test case relies on a Broker started independently of the test-suite */, + INTERNAL /** Test case starts an embedded broker within this JVM */, + SPAWNED /** Test case spawns a new broker as a separate process */ + } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/InternalBrokerHolder.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/InternalBrokerHolder.java index f980453d49..a76f79f8b0 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/InternalBrokerHolder.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/InternalBrokerHolder.java @@ -21,12 +21,13 @@ package org.apache.qpid.test.utils; import java.security.PrivilegedAction; +import java.util.Map; import java.util.Set; import org.apache.log4j.Logger; import org.apache.qpid.server.Broker; -import org.apache.qpid.server.security.auth.TaskPrincipal; +import org.apache.qpid.server.BrokerOptions; import org.apache.qpid.server.security.SecurityManager; import javax.security.auth.Subject; @@ -35,27 +36,22 @@ public class InternalBrokerHolder implements BrokerHolder { private static final Logger LOGGER = Logger.getLogger(InternalBrokerHolder.class); - private final Broker _broker; - private final String _workingDirectory; + private Broker _broker; private Set _portsUsedByBroker; - public InternalBrokerHolder(final Broker broker, String workingDirectory, Set portsUsedByBroker) + public InternalBrokerHolder(Set portsUsedByBroker) { - if(broker == null) - { - throw new IllegalArgumentException("Broker must not be null"); - } - - _broker = broker; - _workingDirectory = workingDirectory; _portsUsedByBroker = portsUsedByBroker; } @Override - public String getWorkingDirectory() + public void start(BrokerOptions options) throws Exception { - return _workingDirectory; + LOGGER.info("Starting internal broker (same JVM)"); + + _broker = new Broker(); + _broker.startup(options); } public void shutdown() diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/Piper.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/Piper.java index 9413e38606..12be233c35 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/Piper.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/Piper.java @@ -35,27 +35,21 @@ public final class Piper extends Thread private static final Logger LOGGER = Logger.getLogger(Piper.class); private final BufferedReader _in; - private final PrintStream _out; + private final Logger _out; private final String _ready; private final CountDownLatch _latch; private final String _stopped; - private final String _prefix; private volatile boolean _seenReady; private volatile String _stopLine; - public Piper(InputStream in, PrintStream out, String ready, String stopped) - { - this(in, out, ready, stopped, null); - } - - public Piper(InputStream in, PrintStream out, String ready, String stopped, String prefix) + public Piper(InputStream in, String ready, String stopped, String threadName, String loggerName) { + super(threadName); _in = new BufferedReader(new InputStreamReader(in)); - _out = out; + _out = Logger.getLogger(loggerName); _ready = ready; _stopped = stopped; _seenReady = false; - _prefix = prefix; if (this._ready != null && !this._ready.equals("")) { @@ -87,11 +81,7 @@ public final class Piper extends Thread String line; while ((line = _in.readLine()) != null) { - if (_prefix != null) - { - line = _prefix + line; - } - _out.println(line); + _out.info(line); if (_latch != null && line.contains(_ready)) { diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java index 369a76a6c6..9a3308603b 100755 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java @@ -18,9 +18,7 @@ package org.apache.qpid.test.utils; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.PrintStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -28,7 +26,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.TimeUnit; import javax.jms.BytesMessage; import javax.jms.Connection; @@ -48,8 +45,10 @@ import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; -import org.apache.commons.lang.StringUtils; +import org.apache.log4j.FileAppender; +import org.apache.log4j.LogManager; import org.apache.log4j.Logger; +import org.apache.log4j.xml.DOMConfigurator; import org.apache.qpid.AMQException; import org.apache.qpid.client.AMQConnectionFactory; @@ -60,7 +59,6 @@ import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.jms.ConnectionURL; -import org.apache.qpid.server.Broker; import org.apache.qpid.server.BrokerOptions; import org.apache.qpid.server.configuration.BrokerProperties; import org.apache.qpid.server.configuration.updater.TaskExecutor; @@ -83,19 +81,11 @@ public class QpidBrokerTestCase extends QpidTestCase { private TaskExecutor _taskExecutor; - public enum BrokerType - { - EXTERNAL /** Test case relies on a Broker started independently of the test-suite */, - INTERNAL /** Test case starts an embedded broker within this JVM */, - SPAWNED /** Test case spawns a new broker as a separate process */ - } - public static final String GUEST_USERNAME = "guest"; public static final String GUEST_PASSWORD = "guest"; - protected final static String QpidHome = System.getProperty("QPID_HOME"); private final File _configFile = new File(System.getProperty("broker.config")); - private File _logConfigFile; + private File _spawnedBrokerLogConfigFile; protected final String _brokerStoreType = System.getProperty("broker.config-store-type", "JSON"); protected static final Logger _logger = Logger.getLogger(QpidBrokerTestCase.class); protected static final int LOGMONITOR_TIMEOUT = 5000; @@ -128,11 +118,7 @@ public class QpidBrokerTestCase extends QpidTestCase private static final String BROKER_COMMAND_PLATFORM = "broker.command." + SystemUtils.getOSConfigSuffix(); private static final String BROKER_CLEAN_BETWEEN_TESTS = "broker.clean.between.tests"; private static final String BROKER_VERSION = "broker.version"; - protected static final String BROKER_READY = "broker.ready"; - private static final String BROKER_STOPPED = "broker.stopped"; private static final String TEST_OUTPUT = "test.output"; - private static final String BROKER_LOG_INTERLEAVE = "broker.log.interleave"; - private static final String BROKER_LOG_PREFIX = "broker.log.prefix"; private static final String BROKER_PERSITENT = "broker.persistent"; public static final String PROFILE_USE_SSL = "profile.use_ssl"; @@ -141,41 +127,30 @@ public class QpidBrokerTestCase extends QpidTestCase public static final int DEFAULT_JMXPORT_REGISTRYSERVER = 8999; public static final int JMXPORT_CONNECTORSERVER_OFFSET = 100; public static final int DEFAULT_HTTP_MANAGEMENT_PORT_VALUE = 8080; - public static final int DEFAULT_HTTPS_MANAGEMENT_PORT_VALUE = 8443; public static final String TEST_AMQP_PORT_PROTOCOLS_PROPERTY="test.amqp_port_protocols"; // values protected static final String JAVA = "java"; - protected static final String CPP = "cpp"; - - protected static final String QPID_HOME = "QPID_HOME"; public static final int DEFAULT_PORT = Integer.getInteger("test.port", DEFAULT_PORT_VALUE); public static final int FAILING_PORT = Integer.parseInt(System.getProperty("test.port.alt")); public static final int DEFAULT_MANAGEMENT_PORT = Integer.getInteger("test.mport", DEFAULT_JMXPORT_REGISTRYSERVER); public static final int DEFAULT_HTTP_MANAGEMENT_PORT = Integer.getInteger("test.hport", DEFAULT_HTTP_MANAGEMENT_PORT_VALUE); - public static final int DEFAULT_HTTPS_MANAGEMENT_PORT = Integer.getInteger("test.hsport", DEFAULT_HTTPS_MANAGEMENT_PORT_VALUE); public static final int DEFAULT_SSL_PORT = Integer.getInteger("test.port.ssl", DEFAULT_SSL_PORT_VALUE); protected String _brokerLanguage = System.getProperty(BROKER_LANGUAGE, JAVA); - protected BrokerType _brokerType = BrokerType.valueOf(System.getProperty(BROKER_TYPE, "").toUpperCase()); + protected BrokerHolder.BrokerType _brokerType = BrokerHolder.BrokerType.valueOf(System.getProperty(BROKER_TYPE, "").toUpperCase()); private static final String BROKER_COMMAND_TEMPLATE = System.getProperty(BROKER_COMMAND_PLATFORM, System.getProperty(BROKER_COMMAND)); - protected BrokerCommandHelper _brokerCommandHelper = new BrokerCommandHelper(BROKER_COMMAND_TEMPLATE); private Boolean _brokerCleanBetweenTests = Boolean.getBoolean(BROKER_CLEAN_BETWEEN_TESTS); private final Protocol _brokerProtocol = Protocol.valueOf("AMQP_" + System.getProperty(BROKER_VERSION, " ").substring(1)); protected String _output = System.getProperty(TEST_OUTPUT, System.getProperty("java.io.tmpdir")); protected Boolean _brokerPersistent = Boolean.getBoolean(BROKER_PERSITENT); - protected static String _brokerLogPrefix = System.getProperty(BROKER_LOG_PREFIX,"BROKER: "); - protected static boolean _interleaveBrokerLog = Boolean.valueOf(System.getProperty(BROKER_LOG_INTERLEAVE,"true")); - protected File _outputFile; - protected PrintStream _testcaseOutputStream; - protected Map _brokers = new HashMap(); protected InitialContext _initialContext; @@ -195,6 +170,8 @@ public class QpidBrokerTestCase extends QpidTestCase /** Size to create our message*/ private int _messageSize = DEFAULT_MESSAGE_SIZE; + private String _brokerCommandTemplate; + /** Type of message*/ protected enum MessageType { @@ -210,7 +187,8 @@ public class QpidBrokerTestCase extends QpidTestCase { super(); _brokerConfigurations = new HashMap(); - initialiseLogConfigFile(); + initialiseSpawnedBrokerLogConfigFile(); + _brokerCommandTemplate = BROKER_COMMAND_TEMPLATE; } public TestBrokerConfiguration getBrokerConfiguration(int port) @@ -259,12 +237,12 @@ public class QpidBrokerTestCase extends QpidTestCase return configuration; } - private void initialiseLogConfigFile() + private void initialiseSpawnedBrokerLogConfigFile() { - _logConfigFile = new File(LOG4J_CONFIG_FILE_PATH); - if(!_logConfigFile.exists()) + _spawnedBrokerLogConfigFile = new File(SPAWNED_BROKER_LOG4J_CONFIG_FILE_PATH); + if(!_spawnedBrokerLogConfigFile.exists()) { - throw new RuntimeException("Log config file " + _logConfigFile.getAbsolutePath() + " does not exist"); + throw new RuntimeException("Log config file " + _spawnedBrokerLogConfigFile.getAbsolutePath() + " does not exist"); } } @@ -276,36 +254,18 @@ public class QpidBrokerTestCase extends QpidTestCase @Override public void runBare() throws Throwable { - String qname = getClass().getName() + "." + getName(); - // Initialize this for each test run _env = new HashMap(); - PrintStream oldOut = System.out; - PrintStream oldErr = System.err; - PrintStream out = null; - PrintStream err = null; + // Log4j properties expects this to be set + System.setProperty("qpid.testMethod", "-" + getName()); + System.setProperty("qpid.testClass", getClass().getName()); - boolean redirected = _output != null && _output.length() > 0; - if (redirected) - { - _outputFile = new File(String.format("%s/TEST-%s.out", _output, qname)); - out = new PrintStream(new FileOutputStream(_outputFile), true); - err = new PrintStream(String.format("%s/TEST-%s.err", _output, qname)); + String log4jConfigFile = System.getProperty("log4j.configuration.file"); + DOMConfigurator.configure(log4jConfigFile); - System.setOut(out); - System.setErr(err); - - if (_interleaveBrokerLog) - { - _testcaseOutputStream = out; - } - else - { - _testcaseOutputStream = new PrintStream(new FileOutputStream(String - .format("%s/TEST-%s.broker.out", _output, qname)), true); - } - } + // get log file from file appender + _outputFile = new File(((FileAppender)LogManager.getRootLogger().getAllAppenders().nextElement()).getFile()); try { @@ -324,7 +284,7 @@ public class QpidBrokerTestCase extends QpidTestCase revertSystemProperties(); revertLoggingLevels(); - if(_brokerCleanBetweenTests) + if (_brokerCleanBetweenTests) { final String qpidWork = System.getProperty("QPID_WORK"); cleanBrokerWork(qpidWork); @@ -333,17 +293,7 @@ public class QpidBrokerTestCase extends QpidTestCase _logger.info("========== stop " + getTestName() + " =========="); - if (redirected) - { - System.setErr(oldErr); - System.setOut(oldOut); - err.close(); - out.close(); - if (!_interleaveBrokerLog) - { - _testcaseOutputStream.close(); - } - } + LogManager.resetConfiguration(); } } @@ -403,7 +353,7 @@ public class QpidBrokerTestCase extends QpidTestCase protected int getPort(int port) { - if (!_brokerType.equals(BrokerType.EXTERNAL)) + if (!_brokerType.equals(BrokerHolder.BrokerType.EXTERNAL)) { return port == 0 ? DEFAULT_PORT : port; } @@ -424,29 +374,34 @@ public class QpidBrokerTestCase extends QpidTestCase } public void startBroker(int port, boolean managementMode) throws Exception + { + startBroker(port, managementMode, null); + } + + public void startBroker(int port, boolean managementMode, String log4jFile) throws Exception { int actualPort = getPort(port); TestBrokerConfiguration configuration = getBrokerConfiguration(actualPort); - startBroker(actualPort, configuration, managementMode); + startBroker(actualPort, configuration, managementMode, log4jFile); } protected File getBrokerCommandLog4JFile() { - return _logConfigFile; + return _spawnedBrokerLogConfigFile; } protected void setBrokerCommandLog4JFile(File file) { - _logConfigFile = file; + _spawnedBrokerLogConfigFile = file; _logger.info("Modified log config file to: " + file); } - public void startBroker(int port, TestBrokerConfiguration testConfiguration) throws Exception + public void startBroker(int port, TestBrokerConfiguration testConfiguration, String log4jFile) throws Exception { - startBroker(port, testConfiguration, false); + startBroker(port, testConfiguration, false, log4jFile); } - public void startBroker(int port, TestBrokerConfiguration testConfiguration, boolean managementMode) throws Exception + protected void startBroker(int port, TestBrokerConfiguration testConfiguration, boolean managementMode, String log4jFile) throws Exception { port = getPort(port); @@ -455,181 +410,12 @@ public class QpidBrokerTestCase extends QpidTestCase throw new IllegalStateException("There is already an existing broker running on port " + port); } - Set portsUsedByBroker = guessAllPortsUsedByBroker(port); String testConfig = saveTestConfiguration(port, testConfiguration); - - if (_brokerType.equals(BrokerType.INTERNAL) && !existingInternalBroker()) - { - setSystemProperty(BrokerProperties.PROPERTY_USE_CUSTOM_RMI_SOCKET_FACTORY, "false"); - BrokerOptions options = new BrokerOptions(); - - options.setConfigurationStoreType(_brokerStoreType); - options.setConfigurationStoreLocation(testConfig); - options.setManagementMode(managementMode); - if (managementMode) - { - options.setManagementModePassword(MANAGEMENT_MODE_PASSWORD); - } - - //Set the log config file, relying on the log4j.configuration system property - //set on the JVM by the JUnit runner task in module.xml. - options.setLogConfigFileLocation(_logConfigFile.getAbsolutePath()); - - Broker broker = new Broker(); - _logger.info("Starting internal broker (same JVM)"); - broker.startup(options); - - _brokers.put(port, new InternalBrokerHolder(broker, System.getProperty("QPID_WORK"), portsUsedByBroker)); - } - else if (!_brokerType.equals(BrokerType.EXTERNAL)) - { - // Add the port to QPID_WORK to ensure unique working dirs for multi broker tests - final String qpidWork = getQpidWork(_brokerType, port); - - String[] cmd = _brokerCommandHelper.getBrokerCommand(port, testConfig, _brokerStoreType, _logConfigFile); - if (managementMode) - { - String[] newCmd = new String[cmd.length + 3]; - System.arraycopy(cmd, 0, newCmd, 0, cmd.length); - newCmd[cmd.length] = "-mm"; - newCmd[cmd.length + 1] = "-mmpass"; - newCmd[cmd.length + 2] = MANAGEMENT_MODE_PASSWORD; - cmd = newCmd; - } - _logger.info("Starting spawn broker using command: " + StringUtils.join(cmd, ' ')); - ProcessBuilder pb = new ProcessBuilder(cmd); - pb.redirectErrorStream(true); - Map processEnv = pb.environment(); - String qpidHome = System.getProperty(QPID_HOME); - processEnv.put(QPID_HOME, qpidHome); - //Augment Path with bin directory in QPID_HOME. - boolean foundPath = false; - final String pathEntry = qpidHome + File.separator + "bin"; - for(Map.Entry entry : processEnv.entrySet()) - { - if(entry.getKey().equalsIgnoreCase("path")) - { - entry.setValue(entry.getValue().concat(File.pathSeparator + pathEntry)); - foundPath = true; - } - } - if(!foundPath) - { - processEnv.put("PATH", pathEntry); - - } - //Add the test name to the broker run. - // DON'T change PNAME, qpid.stop needs this value. - processEnv.put("QPID_PNAME", "-DPNAME=QPBRKR -DTNAME=\"" + getTestName() + "\""); - processEnv.put("QPID_WORK", qpidWork); - - // Use the environment variable to set amqj.logging.level for the broker - // The value used is a 'server' value in the test configuration to - // allow a differentiation between the client and broker logging levels. - if (System.getProperty("amqj.server.logging.level") != null) - { - setBrokerEnvironment("AMQJ_LOGGING_LEVEL", System.getProperty("amqj.server.logging.level")); - } - - // Add all the environment settings the test requested - if (!_env.isEmpty()) - { - for (Map.Entry entry : _env.entrySet()) - { - processEnv.put(entry.getKey(), entry.getValue()); - } - } - - String qpidOpts = ""; - - // a synchronized hack to avoid adding into QPID_OPTS the values - // of JVM properties "test.virtualhosts" and "test.config" set by a concurrent startup process - synchronized (_propertiesSetForBroker) - { - // Add default test logging levels that are used by the log4j-test - // Use the convenience methods to push the current logging setting - // in to the external broker's QPID_OPTS string. - setSystemProperty("amqj.protocol.logging.level"); - setSystemProperty("root.logging.level"); - - setSystemProperty("test.port"); - setSystemProperty("test.mport"); - setSystemProperty("test.cport"); - setSystemProperty("test.hport"); - setSystemProperty("test.hsport"); - setSystemProperty("test.port.ssl"); - setSystemProperty("test.port.alt"); - setSystemProperty("test.port.alt.ssl"); - setSystemProperty("test.amqp_port_protocols"); - - setSystemProperty("virtualhostnode.type"); - setSystemProperty("virtualhostnode.context.blueprint"); - - // Add all the specified system properties to QPID_OPTS - if (!_propertiesSetForBroker.isEmpty()) - { - for (String key : _propertiesSetForBroker.keySet()) - { - qpidOpts += " -D" + key + "=" + _propertiesSetForBroker.get(key); - } - } - } - if (processEnv.containsKey("QPID_OPTS")) - { - qpidOpts = processEnv.get("QPID_OPTS") + qpidOpts; - } - processEnv.put("QPID_OPTS", qpidOpts); - - // cpp broker requires that the work directory is created - createBrokerWork(qpidWork); - - Process process = pb.start(); - - Piper p = new Piper(process.getInputStream(), - _testcaseOutputStream, - System.getProperty(BROKER_READY), - System.getProperty(BROKER_STOPPED), - _interleaveBrokerLog ? _brokerLogPrefix : null); - - p.start(); - StringBuilder cmdLine = new StringBuilder(cmd[0]); - for(int i = 1; i< cmd.length; i++) - { - cmdLine.append(' '); - cmdLine.append(cmd[i]); - } - - SpawnedBrokerHolder holder = new SpawnedBrokerHolder(process, qpidWork, portsUsedByBroker, cmdLine.toString()); - if (!p.await(30, TimeUnit.SECONDS)) - { - _logger.info("broker failed to become ready (" + p.getReady() + "):" + p.getStopLine()); - String threadDump = holder.dumpThreads(); - if (!threadDump.isEmpty()) - { - _logger.info("the result of a try to capture thread dump:" + threadDump); - } - //Ensure broker has stopped - process.destroy(); - cleanBrokerWork(qpidWork); - throw new RuntimeException("broker failed to become ready:" - + p.getStopLine()); - } - - try - { - //test that the broker is still running and hasn't exited unexpectedly - int exit = process.exitValue(); - _logger.info("broker aborted: " + exit); - cleanBrokerWork(qpidWork); - throw new RuntimeException("broker aborted: " + exit); - } - catch (IllegalThreadStateException e) - { - // this is expect if the broker started successfully - } - - _brokers.put(port, holder); - } + String log4jConfig = log4jFile == null ? getBrokerCommandLog4JFile().getAbsolutePath() : log4jFile; + BrokerOptions options = getBrokerOptions(managementMode, testConfig, log4jConfig, log4jFile == null); + BrokerHolder holder = new BrokerHolderFactory().create(_brokerType, port, this); + _brokers.put(port, holder); + holder.start(options); } private boolean existingInternalBroker() @@ -645,21 +431,6 @@ public class QpidBrokerTestCase extends QpidTestCase return false; } - private String getQpidWork(BrokerType broker, int port) - { - if (!broker.equals(BrokerType.EXTERNAL)) - { - return System.getProperty("QPID_WORK")+ "/" + port; - } - - return System.getProperty("QPID_WORK"); - } - - public String getTestConfigFile() - { - return getTestConfigFile(getPort()); - } - public String getTestConfigFile(int port) { return _output + File.separator + getTestQueueName() + "-" + port + "-config"; @@ -1035,7 +806,7 @@ public class QpidBrokerTestCase extends QpidTestCase protected boolean isInternalBroker() { - return _brokerType.equals(BrokerType.INTERNAL); + return _brokerType.equals(BrokerHolder.BrokerType.INTERNAL); } protected boolean isBrokerStorePersistent() @@ -1397,16 +1168,6 @@ public class QpidBrokerTestCase extends QpidTestCase return message; } - protected int getMessageSize() - { - return _messageSize; - } - - protected void setMessageSize(int byteSize) - { - _messageSize = byteSize; - } - public BrokerDetails getBroker() { try @@ -1453,4 +1214,91 @@ public class QpidBrokerTestCase extends QpidTestCase session.commit(); session.close(); } + + protected BrokerOptions getBrokerOptions( boolean managementMode, String testConfig, String log4jConfig, boolean skipLoggingConfiguration) + { + BrokerOptions options = new BrokerOptions(); + + options.setConfigurationStoreType(_brokerStoreType); + options.setConfigurationStoreLocation(testConfig); + options.setManagementMode(managementMode); + if (managementMode) + { + options.setManagementModePassword(MANAGEMENT_MODE_PASSWORD); + } + options.setSkipLoggingConfiguration(skipLoggingConfiguration); + options.setLogConfigFileLocation(log4jConfig); + options.setStartupLoggedToSystemOut(false); + return options; + } + + private Map getJvmProperties() + { + Map jvmOptions = new HashMap(); + synchronized (_propertiesSetForBroker) + { + jvmOptions.putAll(_propertiesSetForBroker); + + copySystemProperty("amqj.protocol.logging.level", jvmOptions); + copySystemProperty("root.logging.level", jvmOptions); + + copySystemProperty("test.port", jvmOptions); + copySystemProperty("test.mport", jvmOptions); + copySystemProperty("test.cport", jvmOptions); + copySystemProperty("test.hport", jvmOptions); + copySystemProperty("test.hsport", jvmOptions); + copySystemProperty("test.port.ssl", jvmOptions); + copySystemProperty("test.port.alt", jvmOptions); + copySystemProperty("test.port.alt.ssl", jvmOptions); + copySystemProperty("test.amqp_port_protocols", jvmOptions); + + copySystemProperty("virtualhostnode.type", jvmOptions); + copySystemProperty("virtualhostnode.context.blueprint", jvmOptions); + } + return jvmOptions; + } + + private void copySystemProperty(String name, Map jvmOptions) + { + String value = System.getProperty(name); + if (value != null) + { + jvmOptions.put(name, value); + } + } + + private Map getEnvironmentProperties() + { + return new HashMap<>(_env); + } + + private String getBrokerCommandTemplate() + { + return _brokerCommandTemplate; + } + + public static class BrokerHolderFactory + { + + public BrokerHolder create(BrokerHolder.BrokerType brokerType, int port, QpidBrokerTestCase testCase) + { + Set portsUsedByBroker = testCase.guessAllPortsUsedByBroker(port); + BrokerHolder holder = null; + if (brokerType.equals(BrokerHolder.BrokerType.INTERNAL) && !testCase.existingInternalBroker()) + { + testCase.setSystemProperty(BrokerProperties.PROPERTY_USE_CUSTOM_RMI_SOCKET_FACTORY, "false"); + holder = new InternalBrokerHolder(portsUsedByBroker); + } + else if (!brokerType.equals(BrokerHolder.BrokerType.EXTERNAL)) + { + + Map jvmOptions = testCase.getJvmProperties(); + Map environmentProperties = testCase.getEnvironmentProperties(); + + holder = new SpawnedBrokerHolder(testCase.getBrokerCommandTemplate(), port, testCase.getTestName(), jvmOptions, environmentProperties, brokerType, portsUsedByBroker); + } + return holder; + } + } + } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/SpawnedBrokerHolder.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/SpawnedBrokerHolder.java index 4b747e869c..2581093f97 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/SpawnedBrokerHolder.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/SpawnedBrokerHolder.java @@ -22,6 +22,7 @@ package org.apache.qpid.test.utils; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -30,39 +31,215 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; +import org.apache.qpid.server.BrokerOptions; +import org.apache.qpid.server.configuration.BrokerProperties; +import org.apache.qpid.server.logging.messages.BrokerMessages; +import org.apache.qpid.util.FileUtils; import org.apache.qpid.util.SystemUtils; public class SpawnedBrokerHolder implements BrokerHolder { private static final Logger LOGGER = Logger.getLogger(SpawnedBrokerHolder.class); + protected static final String BROKER_READY = System.getProperty("broker.ready", BrokerMessages.READY().toString()); + private static final String BROKER_STOPPED = System.getProperty("broker.stopped", BrokerMessages.STOPPED().toString()); - private final Process _process; - private final Integer _pid; - private final String _workingDirectory; + private final BrokerType _type; + private final int _port; + private final String _name; + private final Map _jvmOptions; + private final Map _environmentSettings; + protected BrokerCommandHelper _brokerCommandHelper; + + private Process _process; + private Integer _pid; private Set _portsUsedByBroker; - private final String _brokerCommand; + private String _brokerCommand; + + public SpawnedBrokerHolder(String brokerCommandTemplate, int port, String name, Map jvmOptions, Map environmentSettings, BrokerType type, Set portsUsedByBroker) + { + _type = type; + _portsUsedByBroker = portsUsedByBroker; + _port = port; + _name = name; + _jvmOptions = jvmOptions; + _environmentSettings = environmentSettings; + _brokerCommandHelper = new BrokerCommandHelper(brokerCommandTemplate); + } - public SpawnedBrokerHolder(final Process process, final String workingDirectory, Set portsUsedByBroker, - String brokerCmd) + + @Override + public void start(BrokerOptions brokerOptions) throws Exception { - if(process == null) + // Add the port to QPID_WORK to ensure unique working dirs for multi broker tests + final String qpidWork = getQpidWork(_type, _port); + + String[] cmd = _brokerCommandHelper.getBrokerCommand(_port, brokerOptions.getConfigurationStoreLocation(), brokerOptions.getConfigurationStoreType(), + new File(brokerOptions.getLogConfigFileLocation())); + if (brokerOptions.isManagementMode()) + { + String[] newCmd = new String[cmd.length + 3]; + System.arraycopy(cmd, 0, newCmd, 0, cmd.length); + newCmd[cmd.length] = "-mm"; + newCmd[cmd.length + 1] = "-mmpass"; + newCmd[cmd.length + 2] = brokerOptions.getManagementModePassword(); + cmd = newCmd; + } + ProcessBuilder pb = new ProcessBuilder(cmd); + pb.redirectErrorStream(true); + Map processEnv = pb.environment(); + String qpidHome = System.getProperty(BrokerProperties.PROPERTY_QPID_HOME); + processEnv.put(BrokerProperties.PROPERTY_QPID_HOME, qpidHome); + + //Augment Path with bin directory in QPID_HOME. + boolean foundPath = false; + final String pathEntry = qpidHome + File.separator + "bin"; + for(Map.Entry entry : processEnv.entrySet()) + { + if(entry.getKey().equalsIgnoreCase("path")) + { + entry.setValue(entry.getValue().concat(File.pathSeparator + pathEntry)); + foundPath = true; + } + } + if(!foundPath) + { + processEnv.put("PATH", pathEntry); + } + //Add the test name to the broker run. + // DON'T change PNAME, qpid.stop needs this value. + processEnv.put("QPID_PNAME", "-DPNAME=QPBRKR -DTNAME=\"" + _name + "\""); + processEnv.put("QPID_WORK", qpidWork); + + // Use the environment variable to set amqj.logging.level for the broker + // The value used is a 'server' value in the test configuration to + // allow a differentiation between the client and broker logging levels. + if (System.getProperty("amqj.server.logging.level") != null) + { + processEnv.put("AMQJ_LOGGING_LEVEL", System.getProperty("amqj.server.logging.level")); + } + + // Add all the environment settings the test requested + if (!_environmentSettings.isEmpty()) + { + for (Map.Entry entry : _environmentSettings.entrySet()) + { + processEnv.put(entry.getKey(), entry.getValue()); + } + } + + String qpidOpts = ""; + + // Add all the specified system properties to QPID_OPTS + if (!_jvmOptions.isEmpty()) { - throw new IllegalArgumentException("Process must not be null"); + for (String key : _jvmOptions.keySet()) + { + qpidOpts += " -D" + key + "=" + _jvmOptions.get(key); + } } - _process = process; + if (processEnv.containsKey("QPID_OPTS")) + { + qpidOpts = processEnv.get("QPID_OPTS") + qpidOpts; + } + processEnv.put("QPID_OPTS", qpidOpts); + + // cpp broker requires that the work directory is created + createBrokerWork(qpidWork); + + _process = pb.start(); + + Piper standardOutputPiper = new Piper(_process.getInputStream(), + BROKER_READY, + BROKER_STOPPED, + "STD", "BROKER-" + _port); + + standardOutputPiper.start(); + + new Piper(_process.getErrorStream(), null, null, "ERROR", "BROKER-" + _port).start(); + + StringBuilder cmdLine = new StringBuilder(cmd[0]); + for(int i = 1; i< cmd.length; i++) + { + cmdLine.append(' '); + cmdLine.append(cmd[i]); + } + + _brokerCommand = cmdLine.toString(); _pid = retrieveUnixPidIfPossible(); - _workingDirectory = workingDirectory; - _portsUsedByBroker = portsUsedByBroker; - _brokerCommand = brokerCmd; + + if (!standardOutputPiper.await(30, TimeUnit.SECONDS)) + { + LOGGER.info("broker failed to become ready (" + standardOutputPiper.getReady() + "):" + standardOutputPiper.getStopLine()); + String threadDump = dumpThreads(); + if (!threadDump.isEmpty()) + { + LOGGER.info("the result of a try to capture thread dump:" + threadDump); + } + //Ensure broker has stopped + _process.destroy(); + cleanBrokerWork(qpidWork); + throw new RuntimeException("broker failed to become ready:" + + standardOutputPiper.getStopLine()); + } + + try + { + //test that the broker is still running and hasn't exited unexpectedly + int exit = _process.exitValue(); + LOGGER.info("broker aborted: " + exit); + cleanBrokerWork(qpidWork); + throw new RuntimeException("broker aborted: " + exit); + } + catch (IllegalThreadStateException e) + { + // this is expect if the broker started successfully + } + } - @Override - public String getWorkingDirectory() + protected void createBrokerWork(final String qpidWork) { - return _workingDirectory; + if (qpidWork != null) + { + final File dir = new File(qpidWork); + dir.mkdirs(); + if (!dir.isDirectory()) + { + throw new RuntimeException("Failed to created Qpid work directory : " + qpidWork); + } + } + } + + private String getQpidWork(BrokerType broker, int port) + { + if (!broker.equals(BrokerType.EXTERNAL)) + { + return System.getProperty(BrokerProperties.PROPERTY_QPID_WORK) + File.separator + port; + } + + return System.getProperty(BrokerProperties.PROPERTY_QPID_WORK); + } + + private void cleanBrokerWork(final String qpidWork) + { + if (qpidWork != null) + { + LOGGER.info("Cleaning broker work dir: " + qpidWork); + + File file = new File(qpidWork); + if (file.exists()) + { + final boolean success = FileUtils.delete(file, true); + if(!success) + { + throw new RuntimeException("Failed to recursively delete beneath : " + file); + } + } + } } public void shutdown() diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/server/BrokerStartupTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/server/BrokerStartupTest.java index e267bf439b..c475432113 100644 --- a/qpid/java/systests/src/test/java/org/apache/qpid/server/BrokerStartupTest.java +++ b/qpid/java/systests/src/test/java/org/apache/qpid/server/BrokerStartupTest.java @@ -77,7 +77,7 @@ public class BrokerStartupTest extends AbstractTestLogging brokerConfigFile.delete(); } - startBroker(port, null); + startBroker(port, null, null); AMQConnectionURL url = new AMQConnectionURL(String.format("amqp://" + GUEST_USERNAME @@ -209,7 +209,7 @@ public class BrokerStartupTest extends AbstractTestLogging brokerConfigFile.delete(); } - startBroker(port, null); + startBroker(port, null, null); AMQConnectionURL url = new AMQConnectionURL(String.format("amqp://" + GUEST_USERNAME diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/server/logging/BrokerLoggingTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/server/logging/BrokerLoggingTest.java index 4952c4e10e..f7953e56d1 100644 --- a/qpid/java/systests/src/test/java/org/apache/qpid/server/logging/BrokerLoggingTest.java +++ b/qpid/java/systests/src/test/java/org/apache/qpid/server/logging/BrokerLoggingTest.java @@ -22,13 +22,15 @@ package org.apache.qpid.server.logging; import junit.framework.AssertionFailedError; -import org.apache.qpid.server.BrokerOptions; +import org.apache.log4j.FileAppender; +import org.apache.log4j.LogManager; import org.apache.qpid.server.model.Port; import org.apache.qpid.server.model.Transport; import org.apache.qpid.test.utils.TestBrokerConfiguration; import org.apache.qpid.transport.ConnectionException; import org.apache.qpid.util.LogMonitor; +import java.io.File; import java.io.IOException; import java.net.Socket; import java.util.Collections; @@ -59,6 +61,7 @@ public class BrokerLoggingTest extends AbstractTestLogging private static final Pattern BROKER_MESSAGE_LOG_PATTERN = Pattern.compile(BROKER_MESSAGE_LOG_REG_EXP); private static final String BRK_LOG_PREFIX = "BRK-"; + @Override public void setUp() throws Exception { setLogMessagePrefix(); @@ -144,94 +147,6 @@ public class BrokerLoggingTest extends AbstractTestLogging return getPathRelativeToWorkingDirectory(getTestConfigFile(DEFAULT_PORT)); } - /** - * Description: - * On startup the broker must report correctly report the log4j file in use. This is important as it can help diagnose why logging messages are not being reported. - * Input: - * No custom -l value should be provided on the command line so that the default value is correctly reported. - * Output: - * - * MESSAGE BRK-1007 : Using logging configuration : <$QPID_HOME>/etc/log4j.xml - * - * Validation Steps: - * - * 1. The BRK ID is correct - * 2. This occurs before the BRK-1001 startup message. - * 3. The log4j file is the full path to the file specified on the commandline. - * - * @throws Exception caused by broker startup - */ - public void testBrokerStartupDefaultLog4j() throws Exception - { - if (isJavaBroker() && isExternalBroker() && !isInternalBroker()) - { - String TESTID = "BRK-1007"; - - _brokerCommandHelper.removeBrokerCommandLog4JFile(); - - startBroker(); - - // Now we can create the monitor as _outputFile will now be defined - _monitor = new LogMonitor(_outputFile); - - // Ensure broker has fully started up. - getConnection(); - - // Ensure we wait for TESTID to be logged - waitAndFindMatches(TESTID); - - List results = waitAndFindMatches(BRK_LOG_PREFIX); - try - { - // Validation - - assertTrue("BRKer message not logged", results.size() > 0); - - boolean validation = false; - for (String rawLog : results) - { - // We don't care about messages after we have our log config - if (validation) - { - break; - } - - String log = getLog(rawLog); - - // Ensure we do not have a BRK-1001 message before - if (!getMessageID(log).equals(TESTID)) - { - assertFalse(getMessageID(log).equals("BRK-1001")); - continue; - } - - //1 - validateMessageID(TESTID, log); - - //2 - //There will be 1 copy of this startup message (via SystemOut) - assertEquals("Unexpected log4j configuration message count.", - 1, findMatches(TESTID).size()); - - //3 - String defaultLog4j = System.getProperty(QPID_HOME) + "/" + BrokerOptions.DEFAULT_LOG_CONFIG_FILE; - assertTrue("Log4j file(" + defaultLog4j + ") details not correctly logged:" + getMessageString(log), - getMessageString(log).endsWith(defaultLog4j)); - - validation = true; - } - - assertTrue("Validation not performed: " + TESTID + " not logged", validation); - } - catch (AssertionFailedError afe) - { - dumpLogs(results, _monitor); - - throw afe; - } - } - } - /** * Description: * On startup the broker must report correctly report the log4j file in use. This is important as it can help diagnose why logging messages are not being reported. The broker must also be capable of correctly recognising the command line property to specify the custom logging configuration. @@ -254,11 +169,17 @@ public class BrokerLoggingTest extends AbstractTestLogging // This logging startup code only occurs when you run a Java broker if (isJavaBroker()) { - String customLog4j = getBrokerCommandLog4JFile().getAbsolutePath(); + // Log4j properties expects this to be set + System.setProperty("qpid.testMethod", "-" + getName() + ".customLog4j"); + System.setProperty("qpid.testClass", getClass().getName() ); + + String customLog4j = System.getProperty("log4j.configuration.file"); String TESTID = "BRK-1007"; - startBroker(); + startBroker(0, false, customLog4j); + _outputFile = new File(((FileAppender) LogManager.getRootLogger().getAllAppenders().nextElement()).getFile()); + // Now we can create the monitor as _outputFile will now be defined _monitor = new LogMonitor(_outputFile); @@ -385,7 +306,7 @@ public class BrokerLoggingTest extends AbstractTestLogging //2 //There will be 2 copies of the startup message (one via SystemOut, and one via Log4J) assertEquals("Unexpected startup message count", - 2, findMatches(TESTID).size()); + 1, findMatches(TESTID).size()); validation = true; } @@ -471,9 +392,8 @@ public class BrokerLoggingTest extends AbstractTestLogging assertEquals("Incorrect message", TESTID, id); //2 - //There will be 2 copies of the startup message (one via SystemOut, and one via Log4J) assertEquals("Unexpected listen message count", - 2, findMatches(TESTID).size()); + 1, findMatches(TESTID).size()); //3 String message = getMessageString(log); @@ -587,7 +507,7 @@ public class BrokerLoggingTest extends AbstractTestLogging //There will be 4 copies of the startup message (two via SystemOut, and two via Log4J) List listenMessages = findMatches(TESTID); assertEquals("Four listen messages should be found.", - 4, listenMessages .size()); + 2, listenMessages .size()); int tcpStarted = 0; int sslStarted = 0; @@ -604,8 +524,8 @@ public class BrokerLoggingTest extends AbstractTestLogging } } - assertEquals("Unexpected number of logs 'Listening on TCP port'", 2, tcpStarted); - assertEquals("Unexpected number of logs 'Listening on SSL port'", 2, sslStarted); + assertEquals("Unexpected number of logs 'Listening on TCP port'", 1, tcpStarted); + assertEquals("Unexpected number of logs 'Listening on SSL port'", 1, sslStarted); //4 Test ports open testSocketOpen(getPort()); @@ -690,10 +610,9 @@ public class BrokerLoggingTest extends AbstractTestLogging //2 assertEquals("Ready message not present", "Qpid Broker Ready", getMessageString(log)); - //There will be 2 copies of the startup message (one via SystemOut, and one via Log4J) assertEquals("Unexpected ready message count", - 2, findMatches(TESTID).size()); - assertEquals("The ready messages should have been the last 2 messages", results.size() - 2, i); + 1, findMatches(TESTID).size()); + assertEquals("The ready messages should have been the last 2 messages", results.size() - 1, i); validationComplete = true; break; diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java b/qpid/java/systests/src/test/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java index 789ad420d8..f7722a8384 100644 --- a/qpid/java/systests/src/test/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java +++ b/qpid/java/systests/src/test/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java @@ -59,7 +59,7 @@ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements @Override public void setUp() throws Exception { - getBrokerConfiguration().addGroupFileConfiguration(System.getProperty(QPID_HOME) + "/etc/groups-systests"); + getBrokerConfiguration().addGroupFileConfiguration(QPID_HOME + "/etc/groups-systests"); // run test specific setup String testSetup = StringUtils.replace(getName(), "test", "setUp"); diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/LoggingManagementTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/LoggingManagementTest.java index 3717c1594d..a06ed7be52 100644 --- a/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/LoggingManagementTest.java +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/LoggingManagementTest.java @@ -25,6 +25,7 @@ import javax.management.openmbean.CompositeData; import javax.management.openmbean.TabularData; import org.apache.qpid.management.common.mbeans.LoggingManagement; +import org.apache.qpid.server.configuration.BrokerProperties; import org.apache.qpid.server.logging.log4j.LoggingManagementFacadeTest; import org.apache.qpid.test.utils.JMXTestUtils; import org.apache.qpid.test.utils.QpidBrokerTestCase; @@ -35,7 +36,6 @@ import org.apache.qpid.util.LogMonitor; * System test for Logging Management. These tests rely on value set within * test-profiles/log4j-test.xml. * - * @see LoggingManagementMBeanTest * @see LoggingManagementFacadeTest * */ @@ -44,7 +44,9 @@ public class LoggingManagementTest extends QpidBrokerTestCase private JMXTestUtils _jmxUtils; private LoggingManagement _loggingManagement; private LogMonitor _monitor; + private File _logConfig; + @Override public void setUp() throws Exception { getBrokerConfiguration().addJmxManagementConfiguration(); @@ -56,8 +58,9 @@ public class LoggingManagementTest extends QpidBrokerTestCase File tmpLogFile = File.createTempFile("log4j" + "." + getName(), ".xml"); tmpLogFile.deleteOnExit(); - FileUtils.copy(getBrokerCommandLog4JFile(), tmpLogFile); + FileUtils.copy(new File(System.getProperty("log4j.configuration.file")), tmpLogFile); setBrokerCommandLog4JFile(tmpLogFile); + _logConfig = tmpLogFile; super.setUp(); _jmxUtils.open(); @@ -66,6 +69,12 @@ public class LoggingManagementTest extends QpidBrokerTestCase _monitor = new LogMonitor(_outputFile); } + public void startBroker() throws Exception + { + super.startBroker(0, false, _logConfig.getAbsolutePath()); + } + + @Override public void tearDown() throws Exception { try @@ -74,6 +83,11 @@ public class LoggingManagementTest extends QpidBrokerTestCase { _jmxUtils.close(); } + + if (_logConfig != null) + { + _logConfig.delete(); + } } finally { @@ -122,7 +136,7 @@ public class LoggingManagementTest extends QpidBrokerTestCase _loggingManagement.setConfigFileLoggerLevel(operationalLoggingLogger, "OFF"); List matches = _monitor.waitAndFindMatches("Setting level to OFF for logger 'qpid.message'", 5000); - assertEquals(1, matches.size()); + assertTrue(matches.size()>=1); assertEffectiveLoggingLevel(operationalLoggingLogger, "INFO"); diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java index cb6eae013e..4df81845d8 100644 --- a/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java @@ -104,7 +104,7 @@ public class ManagementLoggingTest extends AbstractTestLogging //There will be 2 copies of the startup message (one via SystemOut, and one via Log4J) results = findMatches("MNG-1001"); assertEquals("Unexpected startup message count.", - 2, results.size()); + 1, results.size()); //3 assertEquals("Startup log message is not 'Startup'.", "JMX Management Startup", @@ -185,7 +185,7 @@ public class ManagementLoggingTest extends AbstractTestLogging // Validation //There will be 4 startup messages (two via SystemOut, and two via Log4J) - assertEquals("Unexpected MNG-1002 message count", 4, results.size()); + assertEquals("Unexpected MNG-1002 message count", 2, results.size()); String log = getLogMessage(results, 0); @@ -197,7 +197,7 @@ public class ManagementLoggingTest extends AbstractTestLogging assertTrue("RMI Registry port not as expected(" + mPort + ").:" + getMessageString(log), getMessageString(log).endsWith(String.valueOf(mPort))); - log = getLogMessage(results, 2); + log = getLogMessage(results, 1); //1 validateMessageID("MNG-1002", log); @@ -243,7 +243,7 @@ public class ManagementLoggingTest extends AbstractTestLogging // Validate we only have two MNG-1002 (one via stdout, one via log4j) results = findMatches("MNG-1006"); assertEquals("Upexpected SSL Keystore message count", - 2, results.size()); + 1, results.size()); // Validate the keystore path is as expected assertTrue("SSL Keystore entry expected.:" + getMessageString(log), diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/KeyStoreRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/KeyStoreRestTest.java index 4b881d1e9f..169ece986e 100644 --- a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/KeyStoreRestTest.java +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/KeyStoreRestTest.java @@ -56,7 +56,7 @@ public class KeyStoreRestTest extends QpidRestTestCase Map keystore = keyStores.get(0); assertKeyStoreAttributes(keystore, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE, - System.getProperty(QPID_HOME) + "/../" + TestSSLConstants.BROKER_KEYSTORE, null); + QPID_HOME + "/../" + TestSSLConstants.BROKER_KEYSTORE, null); } public void testCreate() throws Exception @@ -98,7 +98,7 @@ public class KeyStoreRestTest extends QpidRestTestCase List> keyStores = assertNumberOfKeyStores(1); Map keystore = keyStores.get(0); assertKeyStoreAttributes(keystore, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE, - System.getProperty(QPID_HOME) + "/../" + TestSSLConstants.BROKER_KEYSTORE, null); + QPID_HOME + "/../" + TestSSLConstants.BROKER_KEYSTORE, null); } public void testDeleteFailsWhenKeyStoreInUse() throws Exception diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/LogViewerTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/LogViewerTest.java index f2fb2581f7..a7bf743e72 100644 --- a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/LogViewerTest.java +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/LogViewerTest.java @@ -28,25 +28,40 @@ import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import org.apache.qpid.server.BrokerOptions; +import org.apache.log4j.FileAppender; +import org.apache.log4j.Logger; +import org.apache.log4j.SimpleLayout; +import org.apache.qpid.server.configuration.BrokerProperties; public class LogViewerTest extends QpidRestTestCase { - public static final String DEFAULT_FILE_APPENDER_NAME = "FileAppender"; private String _expectedLogFileName; + private FileAppender _fileAppender; + private String _appenderName; + @Override public void setUp() throws Exception { - setSystemProperty("logsuffix", "-" + getTestQueueName()); - _expectedLogFileName = System.getProperty("logprefix", "") + "qpid" + System.getProperty("logsuffix", "") + ".log"; - - // use real broker log file - File brokerLogFile = new File(System.getProperty(QPID_HOME), BrokerOptions.DEFAULT_LOG_CONFIG_FILE); - setBrokerCommandLog4JFile(brokerLogFile); + _appenderName = getTestQueueName(); + _expectedLogFileName = "qpid-" + _appenderName + ".log"; + _fileAppender = new FileAppender(new SimpleLayout(), + System.getProperty(BrokerProperties.PROPERTY_QPID_WORK) + File.separator + _expectedLogFileName, false); + _fileAppender.setName(_appenderName); + Logger.getRootLogger().addAppender(_fileAppender); super.setUp(); } + @Override + public void tearDown() throws Exception + { + if (_fileAppender != null) + { + Logger.getRootLogger().removeAppender(_fileAppender); + } + super.tearDown(); + } + public void testGetLogFiles() throws Exception { List> logFiles = getRestTestHelper().getJsonAsList("/service/logfilenames"); @@ -54,24 +69,32 @@ public class LogViewerTest extends QpidRestTestCase // 1 file appender is configured in QPID default log4j xml: assertTrue("Unexpected number of log files", logFiles.size() > 0); + Map logFileDetails = null; + for (Map appenderDetails: logFiles) + { + if (_appenderName.equals(appenderDetails.get("appenderName"))) + { + logFileDetails = appenderDetails; + break; + } + } - Map logFileDetails = logFiles.get(0); assertEquals("Unexpected log file name", _expectedLogFileName, logFileDetails.get("name")); assertEquals("Unexpected log file mime type", "text/plain", logFileDetails.get("mimeType")); - assertEquals("Unexpected log file appender",DEFAULT_FILE_APPENDER_NAME, logFileDetails.get("appenderName")); + assertEquals("Unexpected log file appender",_appenderName, logFileDetails.get("appenderName")); assertTrue("Unexpected log file size", ((Number)logFileDetails.get("size")).longValue()>0); assertTrue("Unexpected log file modification time", ((Number)logFileDetails.get("lastModified")).longValue()>0); } public void testDownloadExistingLogFiles() throws Exception { - byte[] bytes = getRestTestHelper().getBytes("/service/logfile?l=" + DEFAULT_FILE_APPENDER_NAME + "%2F" + _expectedLogFileName); + byte[] bytes = getRestTestHelper().getBytes("/service/logfile?l=" + _appenderName + "%2F" + _expectedLogFileName); ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(bytes)); try { ZipEntry entry = zis.getNextEntry(); - assertEquals("Unexpected broker log file name", DEFAULT_FILE_APPENDER_NAME + "/" + _expectedLogFileName, entry.getName()); + assertEquals("Unexpected broker log file name", _appenderName + "/" + _expectedLogFileName, entry.getName()); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; @@ -91,7 +114,7 @@ public class LogViewerTest extends QpidRestTestCase public void testDownloadNonExistingLogFiles() throws Exception { - int responseCode = getRestTestHelper().submitRequest("/service/logfile?l=" + DEFAULT_FILE_APPENDER_NAME + "%2F" + int responseCode = getRestTestHelper().submitRequest("/service/logfile?l=" + _appenderName + "%2F" + _expectedLogFileName + "_" + System.currentTimeMillis(), "GET"); assertEquals("Unexpected response code", 404, responseCode); diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/TrustStoreRestTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/TrustStoreRestTest.java index 5d2e9de3fa..1aac22d0aa 100644 --- a/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/TrustStoreRestTest.java +++ b/qpid/java/systests/src/test/java/org/apache/qpid/systest/rest/TrustStoreRestTest.java @@ -56,7 +56,7 @@ public class TrustStoreRestTest extends QpidRestTestCase Map truststore = trustStores.get(0); assertTrustStoreAttributes(truststore, TestBrokerConfiguration.ENTRY_NAME_SSL_TRUSTSTORE, - System.getProperty(QPID_HOME) + "/../" + TestSSLConstants.BROKER_TRUSTSTORE, false); + QPID_HOME + "/../" + TestSSLConstants.BROKER_TRUSTSTORE, false); } public void testCreate() throws Exception @@ -96,7 +96,7 @@ public class TrustStoreRestTest extends QpidRestTestCase List> trustStores = assertNumberOfTrustStores(1); Map truststore = trustStores.get(0); assertTrustStoreAttributes(truststore, TestBrokerConfiguration.ENTRY_NAME_SSL_TRUSTSTORE, - System.getProperty(QPID_HOME) + "/../" + TestSSLConstants.BROKER_TRUSTSTORE, false); + QPID_HOME + "/../" + TestSSLConstants.BROKER_TRUSTSTORE, false); } public void testDeleteFailsWhenTrustStoreInUse() throws Exception diff --git a/qpid/java/test-profiles/log4j-test.xml b/qpid/java/test-profiles/log4j-test.xml deleted file mode 100644 index a393fa0952..0000000000 --- a/qpid/java/test-profiles/log4j-test.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/qpid/java/test-profiles/test_resources/log4j.xml b/qpid/java/test-profiles/test_resources/log4j.xml new file mode 100644 index 0000000000..8c59ffdd98 --- /dev/null +++ b/qpid/java/test-profiles/test_resources/log4j.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qpid/java/test-profiles/test_resources/spawned-broker-log4j.xml b/qpid/java/test-profiles/test_resources/spawned-broker-log4j.xml new file mode 100644 index 0000000000..6bb91f88b1 --- /dev/null +++ b/qpid/java/test-profiles/test_resources/spawned-broker-log4j.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.1 From 372b2b83eee4501412199326de903d3be65fd6f8 Mon Sep 17 00:00:00 2001 From: Alex Rudyy Date: Thu, 11 Dec 2014 12:00:26 +0000 Subject: QPID-6265: Unify log4j layout for system tests and spawn broker, rename test folder qbtc-ouput to qbtc-test-config git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1644611 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/pom.xml | 2 +- qpid/java/qpid-systests-parent/pom.xml | 2 +- qpid/java/test-profiles/test_resources/spawned-broker-log4j.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'qpid') diff --git a/qpid/java/pom.xml b/qpid/java/pom.xml index 43e50980d1..0c9b1cb98e 100644 --- a/qpid/java/pom.xml +++ b/qpid/java/pom.xml @@ -54,7 +54,7 @@ ${basedir} - ${qpid.home}${file.separator}target${file.separator}qbtc-output + ${qpid.home}${file.separator}target${file.separator}qbtc-test-config ${project.build.directory}${file.separator}QPID_WORK java-mms.0-10 diff --git a/qpid/java/qpid-systests-parent/pom.xml b/qpid/java/qpid-systests-parent/pom.xml index ce25221ffc..fc76cce231 100644 --- a/qpid/java/qpid-systests-parent/pom.xml +++ b/qpid/java/qpid-systests-parent/pom.xml @@ -34,7 +34,7 @@ target${file.separator}qpid-broker${file.separator}${project.version} ${basedir}${file.separator}${broker.home.dir} - ${qpid.home}${file.separator}qbtc-output + ${qpid.home}${file.separator}qbtc-test-config ${basedir}${file.separator}target${file.separator}surefire-reports ${basedir}/.. diff --git a/qpid/java/test-profiles/test_resources/spawned-broker-log4j.xml b/qpid/java/test-profiles/test_resources/spawned-broker-log4j.xml index 6bb91f88b1..eb75e740b9 100644 --- a/qpid/java/test-profiles/test_resources/spawned-broker-log4j.xml +++ b/qpid/java/test-profiles/test_resources/spawned-broker-log4j.xml @@ -32,7 +32,7 @@ - + -- cgit v1.2.1 From c1b1f97c589be6b5bde0b57da5e3e932728d91e2 Mon Sep 17 00:00:00 2001 From: Robert Godfrey Date: Fri, 12 Dec 2014 14:48:34 +0000 Subject: QPID-6268 : [Java Broker] increment/decrement the unacknowledge count on the queue precisely when the queue entry changes state git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1644906 13f79535-47bb-0310-9956-ffa450edef68 --- .../src/main/java/org/apache/qpid/server/queue/AMQQueue.java | 2 ++ .../src/main/java/org/apache/qpid/server/queue/AbstractQueue.java | 5 +++-- .../src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) (limited to 'qpid') diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/AMQQueue.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/AMQQueue.java index 9cfa7dbcf3..aa64d6947d 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/AMQQueue.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/AMQQueue.java @@ -71,6 +71,8 @@ public interface AMQQueue> void decrementUnackedMsgCount(QueueEntry queueEntry); + void incrementUnackedMsgCount(QueueEntry entry); + boolean resend(final QueueEntry entry, final QueueConsumer consumer); List getMessagesOnTheQueue(); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/AbstractQueue.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/AbstractQueue.java index 8328baca3f..f905558f13 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/AbstractQueue.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/AbstractQueue.java @@ -1230,7 +1230,6 @@ public abstract class AbstractQueue> setLastSeenEntry(sub, entry); _deliveredMessages.incrementAndGet(); - incrementUnackedMsgCount(entry); sub.send(entry, batch); } @@ -2461,13 +2460,15 @@ public abstract class AbstractQueue> return _unackedMsgBytes.get(); } + @Override public void decrementUnackedMsgCount(QueueEntry queueEntry) { _unackedMsgCount.decrementAndGet(); _unackedMsgBytes.addAndGet(-queueEntry.getSize()); } - private void incrementUnackedMsgCount(QueueEntry entry) + @Override + public void incrementUnackedMsgCount(QueueEntry entry) { _unackedMsgCount.incrementAndGet(); _unackedMsgBytes.addAndGet(entry.getSize()); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java index a0f2dc798d..452c5ff14f 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java @@ -214,6 +214,7 @@ public abstract class QueueEntryImpl implements QueueEntry if(acquired) { _deliveryCountUpdater.compareAndSet(this,-1,0); + getQueue().incrementUnackedMsgCount(this); } return acquired; } -- cgit v1.2.1 From 5b46665ddfa9875ab7b11c6a916b943d4fbcce75 Mon Sep 17 00:00:00 2001 From: Keith Wall Date: Mon, 15 Dec 2014 13:03:19 +0000 Subject: QPID-6125: [Java Broker] Consistently quote queue/exchange names in error messages sent back over the wire git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1645645 13f79535-47bb-0310-9956-ffa450edef68 --- .../qpid/server/protocol/v0_8/AMQChannel.java | 89 +++++++++++----------- 1 file changed, 43 insertions(+), 46 deletions(-) (limited to 'qpid') diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java index 7604662980..2550221949 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java @@ -2113,32 +2113,32 @@ public class AMQChannel catch (AMQQueue.ExistingExclusiveConsumer e) { _connection.closeConnection(AMQConstant.ACCESS_REFUSED, - "Cannot subscribe to queue " + "Cannot subscribe to queue '" + queue1.getName() - + " as it already has an existing exclusive consumer", _channelId); + + "' as it already has an existing exclusive consumer", _channelId); } catch (AMQQueue.ExistingConsumerPreventsExclusive e) { _connection.closeConnection(AMQConstant.ACCESS_REFUSED, - "Cannot subscribe to queue " + "Cannot subscribe to queue '" + queue1.getName() - + " exclusively as it already has a consumer", _channelId); + + "' exclusively as it already has a consumer", _channelId); } catch (AccessControlException e) { - _connection.closeConnection(AMQConstant.ACCESS_REFUSED, "Cannot subscribe to queue " + _connection.closeConnection(AMQConstant.ACCESS_REFUSED, "Cannot subscribe to queue '" + queue1.getName() - + " permission denied", _channelId); + + "' permission denied", _channelId); } catch (MessageSource.ConsumerAccessRefused consumerAccessRefused) { _connection.closeConnection(AMQConstant.ACCESS_REFUSED, - "Cannot subscribe to queue " + "Cannot subscribe to queue '" + queue1.getName() - + " as it already has an incompatible exclusivity policy", _channelId); + + "' as it already has an incompatible exclusivity policy", _channelId); } @@ -2247,7 +2247,7 @@ public class AMQChannel // if the exchange does not exist we raise a channel exception if (destination == null) { - closeChannel(AMQConstant.NOT_FOUND, "Unknown exchange name: " + exchangeName); + closeChannel(AMQConstant.NOT_FOUND, "Unknown exchange name: '" + exchangeName + "'"); } else { @@ -2805,15 +2805,14 @@ public class AMQChannel exchange = virtualHost.getExchange(exchangeName.toString()); if (exchange == null) { - closeChannel(AMQConstant.NOT_FOUND, "Unknown exchange: " + exchangeName); + closeChannel(AMQConstant.NOT_FOUND, "Unknown exchange: '" + exchangeName + "'"); } else if (!(type == null || type.length() == 0) && !exchange.getType().equals(type.asString())) { - _connection.closeConnection(AMQConstant.NOT_ALLOWED, "Attempt to redeclare exchange: " - + - exchangeName - + " of type " + _connection.closeConnection(AMQConstant.NOT_ALLOWED, "Attempt to redeclare exchange: '" + + exchangeName + + "' of type " + exchange.getType() + " to " + type @@ -2840,7 +2839,6 @@ public class AMQChannel { attributes.putAll(FieldTable.convertToMap(arguments)); } - attributes.put(Exchange.ID, null); attributes.put(Exchange.NAME, name); attributes.put(Exchange.TYPE, typeString); attributes.put(Exchange.DURABLE, durable); @@ -2865,8 +2863,8 @@ public class AMQChannel catch (ReservedExchangeNameException e) { _connection.closeConnection(AMQConstant.NOT_ALLOWED, - "Attempt to declare exchange: " + exchangeName + - " which begins with reserved prefix.", getChannelId()); + "Attempt to declare exchange: '" + exchangeName + + "' which begins with reserved prefix.", getChannelId()); } @@ -2875,8 +2873,8 @@ public class AMQChannel exchange = e.getExistingExchange(); if (!new AMQShortString(exchange.getType()).equals(type)) { - _connection.closeConnection(AMQConstant.NOT_ALLOWED, "Attempt to redeclare exchange: " - + exchangeName + " of type " + _connection.closeConnection(AMQConstant.NOT_ALLOWED, "Attempt to redeclare exchange: '" + + exchangeName + "' of type " + exchange.getType() + " to " + type + ".", getChannelId()); @@ -2912,7 +2910,7 @@ public class AMQChannel // note - since 0-8/9/9-1 can't set the alt. exchange this exception should never occur final String message = "Unknown alternate exchange " + (e.getName() != null - ? "name: \"" + e.getName() + "\"" + ? "name: '" + e.getName() + "'" : "id: " + e.getId()); _connection.closeConnection(AMQConstant.NOT_FOUND, message, getChannelId()); @@ -2958,7 +2956,7 @@ public class AMQChannel final ExchangeImpl exchange = virtualHost.getExchange(exchangeName); if (exchange == null) { - closeChannel(AMQConstant.NOT_FOUND, "No such exchange: " + exchangeStr); + closeChannel(AMQConstant.NOT_FOUND, "No such exchange: '" + exchangeStr + "'"); } else { @@ -3035,7 +3033,7 @@ public class AMQChannel else if (isDefaultExchange(exchange)) { _connection.closeConnection(AMQConstant.NOT_ALLOWED, - "Cannot bind the queue " + queueName + " to the default exchange", getChannelId()); + "Cannot bind the queue '" + queueName + "' to the default exchange", getChannelId()); } else @@ -3047,7 +3045,7 @@ public class AMQChannel if (exch == null) { closeChannel(AMQConstant.NOT_FOUND, - "Exchange " + exchangeName + " does not exist."); + "Exchange '" + exchangeName + "' does not exist."); } else { @@ -3138,19 +3136,19 @@ public class AMQChannel if (queue == null) { closeChannel(AMQConstant.NOT_FOUND, - "Queue: " + "Queue: '" + queueName - + " not found on VirtualHost(" - + virtualHost - + ")."); + + "' not found on VirtualHost '" + + virtualHost.getName() + + "'."); } else { if (!queue.verifySessionAccess(this)) { - _connection.closeConnection(AMQConstant.NOT_ALLOWED, "Queue " + _connection.closeConnection(AMQConstant.NOT_ALLOWED, "Queue '" + queue.getName() - + " is exclusive, but not created on this Connection.", getChannelId()); + + "' is exclusive, but not created on this Connection.", getChannelId()); } else { @@ -3180,7 +3178,6 @@ public class AMQChannel QueueArgumentsConverter.convertWireArgsToModel(FieldTable.convertToMap(arguments)); final String queueNameString = AMQShortString.toString(queueName); attributes.put(Queue.NAME, queueNameString); - attributes.put(Queue.ID, UUID.randomUUID()); attributes.put(Queue.DURABLE, durable); LifetimePolicy lifetimePolicy; @@ -3227,9 +3224,9 @@ public class AMQChannel if (!queue.verifySessionAccess(this)) { - _connection.closeConnection(AMQConstant.NOT_ALLOWED, "Queue " + _connection.closeConnection(AMQConstant.NOT_ALLOWED, "Queue '" + queue.getName() - + " is exclusive, but not created on this Connection.", getChannelId()); + + "' is exclusive, but not created on this Connection.", getChannelId()); } else if (queue.isExclusive() != exclusive) @@ -3323,27 +3320,27 @@ public class AMQChannel if (queue == null) { - closeChannel(AMQConstant.NOT_FOUND, "Queue " + queueName + " does not exist."); + closeChannel(AMQConstant.NOT_FOUND, "Queue '" + queueName + "' does not exist."); } else { if (ifEmpty && !queue.isEmpty()) { - closeChannel(AMQConstant.IN_USE, "Queue: " + queueName + " is not empty."); + closeChannel(AMQConstant.IN_USE, "Queue: '" + queueName + "' is not empty."); } else if (ifUnused && !queue.isUnused()) { // TODO - Error code - closeChannel(AMQConstant.IN_USE, "Queue: " + queueName + " is still used."); + closeChannel(AMQConstant.IN_USE, "Queue: '" + queueName + "' is still used."); } else { if (!queue.verifySessionAccess(this)) { - _connection.closeConnection(AMQConstant.NOT_ALLOWED, "Queue " + _connection.closeConnection(AMQConstant.NOT_ALLOWED, "Queue '" + queue.getName() - + " is exclusive, but not created on this Connection.", getChannelId()); + + "' is exclusive, but not created on this Connection.", getChannelId()); } else @@ -3383,7 +3380,7 @@ public class AMQChannel } else if ((queueName != null) && (queue = virtualHost.getQueue(queueName.toString())) == null) { - closeChannel(AMQConstant.NOT_FOUND, "Queue " + queueName + " does not exist."); + closeChannel(AMQConstant.NOT_FOUND, "Queue '" + queueName + "' does not exist."); } else if (!queue.verifySessionAccess(this)) { @@ -3416,14 +3413,14 @@ public class AMQChannel @Override public void receiveQueueUnbind(final AMQShortString queueName, final AMQShortString exchange, - final AMQShortString routingKey, + final AMQShortString bindingKey, final FieldTable arguments) { if(_logger.isDebugEnabled()) { _logger.debug("RECV[" + _channelId + "] QueueUnbind[" +" queue: " + queueName + " exchange: " + exchange + - " bindingKey: " + routingKey + + " bindingKey: " + bindingKey + " arguments: " + arguments + " ]"); } @@ -3440,14 +3437,14 @@ public class AMQChannel { String message = useDefaultQueue ? "No default queue defined on channel and queue was null" - : "Queue " + queueName + " does not exist."; + : "Queue '" + queueName + "' does not exist."; closeChannel(AMQConstant.NOT_FOUND, message); } else if (isDefaultExchange(exchange)) { - _connection.closeConnection(AMQConstant.NOT_ALLOWED, "Cannot unbind the queue " + _connection.closeConnection(AMQConstant.NOT_ALLOWED, "Cannot unbind the queue '" + queue.getName() - + " from the default exchange", getChannelId()); + + "' from the default exchange", getChannelId()); } else @@ -3457,9 +3454,9 @@ public class AMQChannel if (exch == null) { - closeChannel(AMQConstant.NOT_FOUND, "Exchange " + exchange + " does not exist."); + closeChannel(AMQConstant.NOT_FOUND, "Exchange '" + exchange + "' does not exist."); } - else if (!exch.hasBinding(String.valueOf(routingKey), queue)) + else if (!exch.hasBinding(String.valueOf(bindingKey), queue)) { closeChannel(AMQConstant.NOT_FOUND, "No such binding"); } @@ -3467,7 +3464,7 @@ public class AMQChannel { try { - exch.deleteBinding(String.valueOf(routingKey), queue); + exch.deleteBinding(String.valueOf(bindingKey), queue); final AMQMethodBody responseBody = _connection.getMethodRegistry().createQueueUnbindOkBody(); sync(); -- cgit v1.2.1 From 96f9c3e225682940e18bd5fa6a9a270a80de034c Mon Sep 17 00:00:00 2001 From: Keith Wall Date: Mon, 15 Dec 2014 14:28:17 +0000 Subject: QPID-6125: [Java Broker] Fix test broken by my previous commit git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1645667 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/qpid/test/unit/close/JavaServerCloseRaceConditionTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid') diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/JavaServerCloseRaceConditionTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/JavaServerCloseRaceConditionTest.java index 3fe45143d5..dfec1a8517 100644 --- a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/JavaServerCloseRaceConditionTest.java +++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/JavaServerCloseRaceConditionTest.java @@ -106,7 +106,7 @@ public class JavaServerCloseRaceConditionTest extends QpidBrokerTestCase } catch (Exception e) { - assertTrue("Exception should say the exchange is not known.", e.getMessage().contains("Unknown exchange: " + EXCHANGE_NAME)); + assertTrue("Exception should say the exchange is not known.", e.getMessage().contains("Unknown exchange: '" + EXCHANGE_NAME + "'")); } try @@ -119,7 +119,7 @@ public class JavaServerCloseRaceConditionTest extends QpidBrokerTestCase } catch (Exception e) { - assertTrue("Exception should say the exchange is not known.", e.getMessage().contains("Unknown exchange: " + EXCHANGE_NAME)); + assertTrue("Exception should say the exchange is not known.", e.getMessage().contains("Unknown exchange: '" + EXCHANGE_NAME + "'")); } } -- cgit v1.2.1 From 77dbff8daed044b9c95ef0c210b5fe69ff9bbc2c Mon Sep 17 00:00:00 2001 From: Keith Wall Date: Tue, 16 Dec 2014 08:55:22 +0000 Subject: QPID-6272: [Java Broker] Null reference to AMQChannel#defaultQueue once the queue is deleted git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1645880 13f79535-47bb-0310-9956-ffa450edef68 --- .../qpid/server/protocol/v0_8/AMQChannel.java | 50 ++++++++++++++++------ 1 file changed, 36 insertions(+), 14 deletions(-) (limited to 'qpid') diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java index 2550221949..5461b47eb4 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java @@ -124,6 +124,8 @@ public class AMQChannel public static final int DEFAULT_PREFETCH = 4096; private static final Logger _logger = Logger.getLogger(AMQChannel.class); + private final DefaultQueueAssociationClearingTask + _defaultQueueAssociationClearingTask = new DefaultQueueAssociationClearingTask(); //TODO use Broker property to configure message authorization requirements private boolean _messageAuthorizationRequired = Boolean.getBoolean(BrokerProperties.PROPERTY_MSG_AUTH); @@ -140,7 +142,7 @@ public class AMQChannel private long _deliveryTag = 0; /** A channel has a default queue (the last declared) that is used when no queue name is explicitly set */ - private AMQQueue _defaultQueue; + private volatile AMQQueue _defaultQueue; /** This tag is unique per subscription to a queue. The server returns this in response to a basic.consume request. */ private int _consumerTag; @@ -181,11 +183,9 @@ public class AMQChannel private LogSubject _logSubject; private volatile boolean _rollingBack; - private static final Runnable NULL_TASK = new Runnable() { public void run() {} }; private List _resendList = new ArrayList(); private static final AMQShortString IMMEDIATE_DELIVERY_REPLY_TEXT = new AMQShortString("Immediate delivery is not possible."); - private long _createTime = System.currentTimeMillis(); private final ClientDeliveryMethod _clientDeliveryMethod; @@ -1289,17 +1289,6 @@ public class AMQChannel return "("+ _suspended.get() + ", " + _closing.get() + ", " + _connection.isClosing() + ") "+"["+ _connection.toString()+":"+_channelId+"]"; } - public void setDefaultQueue(AMQQueue queue) - { - _defaultQueue = queue; - } - - public AMQQueue getDefaultQueue() - { - return _defaultQueue; - } - - public boolean isClosing() { return _closing.get(); @@ -3585,4 +3574,37 @@ public class AMQChannel return exchangeName == null || AMQShortString.EMPTY_STRING.equals(exchangeName); } + private void setDefaultQueue(AMQQueue queue) + { + AMQQueue currentDefaultQueue = _defaultQueue; + if (queue != currentDefaultQueue) + { + if (currentDefaultQueue != null) + { + currentDefaultQueue.removeDeleteTask(_defaultQueueAssociationClearingTask); + } + if (queue != null) + { + queue.addDeleteTask(_defaultQueueAssociationClearingTask); + } + } + _defaultQueue = queue; + } + + private AMQQueue getDefaultQueue() + { + return _defaultQueue; + } + + private class DefaultQueueAssociationClearingTask implements Action + { + @Override + public void performAction(final AMQQueue queue) + { + if ( queue == _defaultQueue) + { + _defaultQueue = null; + } + } + } } -- cgit v1.2.1 From f62711351f225b7ac77cebe5066fa0a69cdbdf00 Mon Sep 17 00:00:00 2001 From: Robert Godfrey Date: Tue, 16 Dec 2014 16:56:59 +0000 Subject: QPID-5099 : [Java Client] release pre-acquired messages after explicit consumer close git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1646009 13f79535-47bb-0310-9956-ffa450edef68 --- .../src/main/java/org/apache/qpid/client/BasicMessageConsumer.java | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'qpid') diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java index 3d0e972ca2..b1e606b8e9 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java @@ -645,6 +645,12 @@ public abstract class BasicMessageConsumer extends Closeable implements Messa _receivingThread.interrupt(); } + + + if(!(isBrowseOnly() || getSession().isClosing())) + { + rollback(); + } } } -- cgit v1.2.1 From 87b282857f59a43fa77ab65a06ea55cd14de0e05 Mon Sep 17 00:00:00 2001 From: Robert Godfrey Date: Wed, 17 Dec 2014 00:22:01 +0000 Subject: QPID-6272 : [Java Broker] clear queue delete task on default queue before closing session git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1646118 13f79535-47bb-0310-9956-ffa450edef68 --- .../java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'qpid') diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java index 5461b47eb4..d52fb735a2 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java @@ -142,7 +142,7 @@ public class AMQChannel private long _deliveryTag = 0; /** A channel has a default queue (the last declared) that is used when no queue name is explicitly set */ - private volatile AMQQueue _defaultQueue; + private volatile AMQQueue _defaultQueue; /** This tag is unique per subscription to a queue. The server returns this in response to a basic.consume request. */ private int _consumerTag; @@ -827,7 +827,7 @@ public class AMQChannel getVirtualHost().getEventLogger().message(_logSubject, operationalLogMessage); unsubscribeAllConsumers(); - + setDefaultQueue(null); for (Action task : _taskList) { task.performAction(this); @@ -3114,7 +3114,7 @@ public class AMQChannel queueName = queueStr.intern(); } - AMQQueue queue; + AMQQueue queue; //TODO: do we need to check that the queue already exists with exactly the same "configuration"? @@ -3574,9 +3574,9 @@ public class AMQChannel return exchangeName == null || AMQShortString.EMPTY_STRING.equals(exchangeName); } - private void setDefaultQueue(AMQQueue queue) + private void setDefaultQueue(AMQQueue queue) { - AMQQueue currentDefaultQueue = _defaultQueue; + AMQQueue currentDefaultQueue = _defaultQueue; if (queue != currentDefaultQueue) { if (currentDefaultQueue != null) -- cgit v1.2.1 From b5dba684ab72f5f8ea3e28672ba7e65668332dbc Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 17 Dec 2014 14:29:13 +0000 Subject: QPID-5003: Use correct property for delayed auto-delete. Explicit 0 timeout should override default. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1646255 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'qpid') diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp index bbb310b0f4..920dd4172b 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp @@ -538,15 +538,16 @@ Subscription::Subscription(const Address& address, const std::string& type) exclusiveSubscription((Opt(address)/LINK/X_SUBSCRIBE/EXCLUSIVE).asBool(exclusiveQueue)), alternateExchange((Opt(address)/LINK/X_DECLARE/ALTERNATE_EXCHANGE).str()) { - const Variant* timeout = (Opt(address)/LINK/TIMEOUT).value; - if (timeout) { + + if ((Opt(address)/LINK).hasKey(TIMEOUT)) { + const Variant* timeout = (Opt(address)/LINK/TIMEOUT).value; if (timeout->asUint32()) queueOptions.setInt("qpid.auto_delete_timeout", timeout->asUint32()); } else if (durable && !(Opt(address)/LINK/RELIABILITY).value) { //if durable but not explicitly reliable, then set a non-zero //default for the autodelete timeout (previously this would //have defaulted to autodelete immediately anyway, so the risk //of the change causing problems is mitigated) - queueOptions.setInt("qpid.auto_delete_delay", 15*60); + queueOptions.setInt("qpid.auto_delete_timeout", 15*60); } (Opt(address)/LINK/X_DECLARE/ARGUMENTS).collect(queueOptions); (Opt(address)/LINK/X_SUBSCRIBE/ARGUMENTS).collect(subscriptionOptions); -- cgit v1.2.1 From 1412aadeb7b519f1f0bc19ac0274db32cb640ae7 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 17 Dec 2014 14:29:19 +0000 Subject: QPID-6267: honour policy timeout even if it is 0 git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1646256 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/cpp/src/qpid/broker/amqp/Session.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid') diff --git a/qpid/cpp/src/qpid/broker/amqp/Session.cpp b/qpid/cpp/src/qpid/broker/amqp/Session.cpp index 85da8c0c21..32a923cac5 100644 --- a/qpid/cpp/src/qpid/broker/amqp/Session.cpp +++ b/qpid/cpp/src/qpid/broker/amqp/Session.cpp @@ -505,7 +505,7 @@ void Session::setupOutgoing(pn_link_t* link, pn_terminus_t* source, const std::s if (!settings.autodelete) settings.autodelete = autodelete; altExchange = node.topic->getAlternateExchange(); } - if (!settings.autoDeleteDelay) { + if (settings.original.find("qpid.auto_delete_timeout") == settings.original.end()) { //only use delay from link if policy didn't specify one settings.autoDeleteDelay = pn_terminus_get_timeout(source); settings.original["qpid.auto_delete_timeout"] = settings.autoDeleteDelay; -- cgit v1.2.1 From 72f126ed32ec0bcdccd7caa6a4dab5f6d12e2495 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 17 Dec 2014 14:29:24 +0000 Subject: QPID-6269: trigger autodelete timer on queue recovery git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1646257 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/cpp/src/qpid/broker/Queue.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'qpid') diff --git a/qpid/cpp/src/qpid/broker/Queue.cpp b/qpid/cpp/src/qpid/broker/Queue.cpp index d897029c85..658fc26919 100644 --- a/qpid/cpp/src/qpid/broker/Queue.cpp +++ b/qpid/cpp/src/qpid/broker/Queue.cpp @@ -1258,6 +1258,10 @@ Queue::shared_ptr Queue::restore( QueueRegistry& queues, Buffer& buffer ) if (has_userId) result.first->setOwningUser(_userId); + if (result.first->getSettings().autoDeleteDelay) { + result.first->scheduleAutoDelete(); + } + return result.first; } -- cgit v1.2.1 From 39ccbda213efd951ff7355fa1cf2fdc5da58a96b Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 17 Dec 2014 14:29:31 +0000 Subject: QPID-6167: ensure broker responds with a header for an enabled protocol git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1646258 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/cpp/src/qpid/broker/Protocol.cpp | 23 ++++++++++++++++++++--- qpid/cpp/src/qpid/broker/Protocol.h | 4 +++- qpid/cpp/src/qpid/broker/amqp/Domain.cpp | 4 ++++ qpid/cpp/src/qpid/broker/amqp/ProtocolPlugin.cpp | 6 ++++++ qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp | 2 +- qpid/cpp/src/qpid/sys/ConnectionCodec.h | 2 ++ 6 files changed, 36 insertions(+), 5 deletions(-) (limited to 'qpid') diff --git a/qpid/cpp/src/qpid/broker/Protocol.cpp b/qpid/cpp/src/qpid/broker/Protocol.cpp index 2ef8c66445..e9e7892499 100644 --- a/qpid/cpp/src/qpid/broker/Protocol.cpp +++ b/qpid/cpp/src/qpid/broker/Protocol.cpp @@ -35,8 +35,10 @@ ProtocolRegistry::ProtocolRegistry(const std::set& e, Broker* b) : qpid::sys::ConnectionCodec* ProtocolRegistry::create(const qpid::framing::ProtocolVersion& v, qpid::sys::OutputControl& o, const std::string& id, const qpid::sys::SecuritySettings& s) { - if (v == qpid::framing::ProtocolVersion(0, 10) && isEnabled(AMQP_0_10)) { - return create_0_10(o, id, s, false); + if (v == qpid::framing::ProtocolVersion(0, 10)) { + if (isEnabled(AMQP_0_10)) { + return create_0_10(o, id, s, false); + } } qpid::sys::ConnectionCodec* codec = 0; for (Protocols::const_iterator i = protocols.begin(); !codec && i != protocols.end(); ++i) { @@ -51,7 +53,22 @@ qpid::sys::ConnectionCodec* ProtocolRegistry::create(qpid::sys::OutputControl& o return create_0_10(o, id, s, true); } -bool ProtocolRegistry::isEnabled(const std::string& name) +qpid::framing::ProtocolVersion ProtocolRegistry::supportedVersion() const +{ + if (isEnabled(AMQP_0_10)) { + return qpid::framing::ProtocolVersion(0,10); + } else { + for (Protocols::const_iterator i = protocols.begin(); i != protocols.end(); ++i) { + if (isEnabled(i->first)) { + return i->second->supportedVersion(); + } + } + } + QPID_LOG(error, "No enabled protocols!"); + return qpid::framing::ProtocolVersion(0,0); +} + +bool ProtocolRegistry::isEnabled(const std::string& name) const { return enabled.empty()/*if nothing is explicitly enabled, assume everything is*/ || enabled.find(name) != enabled.end(); } diff --git a/qpid/cpp/src/qpid/broker/Protocol.h b/qpid/cpp/src/qpid/broker/Protocol.h index 59a631848e..a7dbc98fff 100644 --- a/qpid/cpp/src/qpid/broker/Protocol.h +++ b/qpid/cpp/src/qpid/broker/Protocol.h @@ -61,6 +61,7 @@ class Protocol virtual qpid::sys::ConnectionCodec* create(const qpid::framing::ProtocolVersion&, qpid::sys::OutputControl&, const std::string&, const qpid::sys::SecuritySettings&) = 0; virtual boost::intrusive_ptr translate(const Message&) = 0; virtual boost::shared_ptr recover(qpid::framing::Buffer&) = 0; + virtual qpid::framing::ProtocolVersion supportedVersion() const = 0; private: }; @@ -70,6 +71,7 @@ class ProtocolRegistry : public Protocol, public qpid::sys::ConnectionCodec::Fac public: QPID_BROKER_EXTERN qpid::sys::ConnectionCodec* create(const qpid::framing::ProtocolVersion&, qpid::sys::OutputControl&, const std::string&, const qpid::sys::SecuritySettings&); QPID_BROKER_EXTERN qpid::sys::ConnectionCodec* create(qpid::sys::OutputControl&, const std::string&, const qpid::sys::SecuritySettings&); + QPID_BROKER_EXTERN qpid::framing::ProtocolVersion supportedVersion() const; QPID_BROKER_EXTERN boost::intrusive_ptr translate(const Message&); QPID_BROKER_EXTERN boost::shared_ptr recover(qpid::framing::Buffer&); QPID_BROKER_EXTERN Message decode(qpid::framing::Buffer&); @@ -86,7 +88,7 @@ class ProtocolRegistry : public Protocol, public qpid::sys::ConnectionCodec::Fac Broker* broker; qpid::sys::ConnectionCodec* create_0_10(qpid::sys::OutputControl&, const std::string&, const qpid::sys::SecuritySettings&, bool); - bool isEnabled(const std::string&); + bool isEnabled(const std::string&) const; }; }} // namespace qpid::broker diff --git a/qpid/cpp/src/qpid/broker/amqp/Domain.cpp b/qpid/cpp/src/qpid/broker/amqp/Domain.cpp index cc714c0730..c2d4782fc4 100644 --- a/qpid/cpp/src/qpid/broker/amqp/Domain.cpp +++ b/qpid/cpp/src/qpid/broker/amqp/Domain.cpp @@ -140,6 +140,10 @@ class InterconnectFactory : public BrokerContext, public qpid::sys::ConnectionCo boost::shared_ptr, BrokerContext&, boost::shared_ptr); qpid::sys::ConnectionCodec* create(const framing::ProtocolVersion&, qpid::sys::OutputControl&, const std::string&, const qpid::sys::SecuritySettings&); qpid::sys::ConnectionCodec* create(qpid::sys::OutputControl&, const std::string&, const qpid::sys::SecuritySettings&); + qpid::framing::ProtocolVersion supportedVersion() const + { + return qpid::framing::ProtocolVersion(1, 0); + } bool connect(); void failed(int, std::string); private: diff --git a/qpid/cpp/src/qpid/broker/amqp/ProtocolPlugin.cpp b/qpid/cpp/src/qpid/broker/amqp/ProtocolPlugin.cpp index 2ea381e2bc..621f25f04b 100644 --- a/qpid/cpp/src/qpid/broker/amqp/ProtocolPlugin.cpp +++ b/qpid/cpp/src/qpid/broker/amqp/ProtocolPlugin.cpp @@ -73,6 +73,7 @@ class ProtocolImpl : public BrokerContext, public Protocol qpid::sys::ConnectionCodec* create(const qpid::framing::ProtocolVersion&, qpid::sys::OutputControl&, const std::string&, const qpid::sys::SecuritySettings&); boost::intrusive_ptr translate(const qpid::broker::Message&); boost::shared_ptr recover(qpid::framing::Buffer&); + qpid::framing::ProtocolVersion supportedVersion() const; private: }; @@ -158,5 +159,10 @@ boost::shared_ptr ProtocolImpl::recover(qpid::framing::Buffe } } +qpid::framing::ProtocolVersion ProtocolImpl::supportedVersion() const +{ + return qpid::framing::ProtocolVersion(1,0); +} + }}} // namespace qpid::broker::amqp diff --git a/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp b/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp index bb59530883..7be625a1a3 100644 --- a/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp +++ b/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp @@ -150,7 +150,7 @@ void AsynchIOHandler::readbuff(AsynchIO& , AsynchIO::BufferBase* buff) { if (!codec) { //TODO: may still want to revise this... //send valid version header & close connection. - write(framing::ProtocolInitiation(framing::highestProtocolVersion)); + write(framing::ProtocolInitiation(factory->supportedVersion())); readError = true; aio->queueWriteClose(); } else { diff --git a/qpid/cpp/src/qpid/sys/ConnectionCodec.h b/qpid/cpp/src/qpid/sys/ConnectionCodec.h index 969a3877e3..8b5b69cdb4 100644 --- a/qpid/cpp/src/qpid/sys/ConnectionCodec.h +++ b/qpid/cpp/src/qpid/sys/ConnectionCodec.h @@ -60,6 +60,8 @@ class ConnectionCodec : public Codec { virtual ConnectionCodec* create( OutputControl&, const std::string& id, const SecuritySettings& ) = 0; + + virtual framing::ProtocolVersion supportedVersion() const = 0; }; }; -- cgit v1.2.1 From 9f634ba1b0f2ee6ea2f2e0c3a7a3d892d3eeda7c Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 17 Dec 2014 14:29:37 +0000 Subject: QPID-6273: ensure requests and responses are sent as textual strings rather than binary data git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1646259 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/cpp/examples/messaging/client.cpp | 6 +++--- qpid/cpp/examples/messaging/server.cpp | 13 +++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'qpid') diff --git a/qpid/cpp/examples/messaging/client.cpp b/qpid/cpp/examples/messaging/client.cpp index 983f0a8878..d695dd9ada 100644 --- a/qpid/cpp/examples/messaging/client.cpp +++ b/qpid/cpp/examples/messaging/client.cpp @@ -39,7 +39,7 @@ using std::string; int main(int argc, char** argv) { const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; std::string connectionOptions = argc > 2 ? argv[2] : ""; - + Connection connection(url, connectionOptions); try { connection.open(); @@ -62,10 +62,10 @@ int main(int argc, char** argv) { Message request; request.setReplyTo(responseQueue); for (int i=0; i<4; i++) { - request.setContent(s[i]); + request.setContentObject(s[i]); sender.send(request); Message response = receiver.fetch(); - std::cout << request.getContent() << " -> " << response.getContent() << std::endl; + std::cout << request.getContentObject() << " -> " << response.getContentObject() << std::endl; } connection.close(); return 0; diff --git a/qpid/cpp/examples/messaging/server.cpp b/qpid/cpp/examples/messaging/server.cpp index aa271d91f9..07f4901441 100644 --- a/qpid/cpp/examples/messaging/server.cpp +++ b/qpid/cpp/examples/messaging/server.cpp @@ -52,14 +52,15 @@ int main(int argc, char** argv) { const Address& address = request.getReplyTo(); if (address) { Sender sender = session.createSender(address); - std::string s = request.getContent(); + std::string s = request.getContentObject(); std::transform(s.begin(), s.end(), s.begin(), toupper); - Message response(s); + Message response; + response.setContentObject(s); sender.send(response); - std::cout << "Processed request: " - << request.getContent() - << " -> " - << response.getContent() << std::endl; + std::cout << "Processed request: " + << request.getContentObject() + << " -> " + << response.getContentObject() << std::endl; session.acknowledge(); } else { std::cerr << "Error: no reply address specified for request: " << request.getContent() << std::endl; -- cgit v1.2.1 From faec291315dc851eefa59b63f3c7107888b75f98 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 17 Dec 2014 14:29:44 +0000 Subject: QPID-6274: Delete subscription queue immediately on link close git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1646260 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/cpp/src/qpid/broker/Queue.cpp | 8 ++++---- qpid/cpp/src/qpid/broker/Queue.h | 4 ++-- qpid/cpp/src/qpid/broker/amqp/Incoming.cpp | 2 +- qpid/cpp/src/qpid/broker/amqp/Incoming.h | 2 +- qpid/cpp/src/qpid/broker/amqp/Outgoing.cpp | 10 ++++++---- qpid/cpp/src/qpid/broker/amqp/Outgoing.h | 4 ++-- qpid/cpp/src/qpid/broker/amqp/Relay.cpp | 4 ++-- qpid/cpp/src/qpid/broker/amqp/Relay.h | 4 ++-- qpid/cpp/src/qpid/broker/amqp/Session.cpp | 12 ++++++------ 9 files changed, 26 insertions(+), 24 deletions(-) (limited to 'qpid') diff --git a/qpid/cpp/src/qpid/broker/Queue.cpp b/qpid/cpp/src/qpid/broker/Queue.cpp index 658fc26919..b1f7d0524b 100644 --- a/qpid/cpp/src/qpid/broker/Queue.cpp +++ b/qpid/cpp/src/qpid/broker/Queue.cpp @@ -1300,10 +1300,10 @@ struct AutoDeleteTask : qpid::sys::TimerTask } }; -void Queue::scheduleAutoDelete() +void Queue::scheduleAutoDelete(bool immediate) { if (canAutoDelete()) { - if (settings.autoDeleteDelay) { + if (!immediate && settings.autoDeleteDelay) { AbsTime time(now(), Duration(settings.autoDeleteDelay * TIME_SEC)); autoDeleteTask = boost::intrusive_ptr(new AutoDeleteTask(shared_from_this(), time)); broker->getTimer().add(autoDeleteTask); @@ -1343,7 +1343,7 @@ bool Queue::isExclusiveOwner(const OwnershipToken* const o) const return o == owner; } -void Queue::releaseExclusiveOwnership() +void Queue::releaseExclusiveOwnership(bool immediateExpiry) { bool unused; { @@ -1355,7 +1355,7 @@ void Queue::releaseExclusiveOwnership() unused = !users.isUsed(); } if (unused && settings.autodelete) { - scheduleAutoDelete(); + scheduleAutoDelete(immediateExpiry); } } diff --git a/qpid/cpp/src/qpid/broker/Queue.h b/qpid/cpp/src/qpid/broker/Queue.h index 65a91b8729..efca9b9d40 100644 --- a/qpid/cpp/src/qpid/broker/Queue.h +++ b/qpid/cpp/src/qpid/broker/Queue.h @@ -379,7 +379,7 @@ class Queue : public boost::enable_shared_from_this, QPID_BROKER_EXTERN uint32_t getConsumerCount() const; inline const std::string& getName() const { return name; } QPID_BROKER_EXTERN bool isExclusiveOwner(const OwnershipToken* const o) const; - QPID_BROKER_EXTERN void releaseExclusiveOwnership(); + QPID_BROKER_EXTERN void releaseExclusiveOwnership(bool immediateExpiry=false); QPID_BROKER_EXTERN bool setExclusiveOwner(const OwnershipToken* const o); QPID_BROKER_EXTERN bool hasExclusiveConsumer() const; QPID_BROKER_EXTERN bool hasExclusiveOwner() const; @@ -389,7 +389,7 @@ class Queue : public boost::enable_shared_from_this, inline bool isAutoDelete() const { return settings.autodelete; } inline bool isBrowseOnly() const { return settings.isBrowseOnly; } QPID_BROKER_EXTERN bool canAutoDelete() const; - QPID_BROKER_EXTERN void scheduleAutoDelete(); + QPID_BROKER_EXTERN void scheduleAutoDelete(bool immediate=false); QPID_BROKER_EXTERN bool isDeleted() const; const QueueBindings& getBindings() const { return bindings; } diff --git a/qpid/cpp/src/qpid/broker/amqp/Incoming.cpp b/qpid/cpp/src/qpid/broker/amqp/Incoming.cpp index ce4c73dead..d4f73fc511 100644 --- a/qpid/cpp/src/qpid/broker/amqp/Incoming.cpp +++ b/qpid/cpp/src/qpid/broker/amqp/Incoming.cpp @@ -57,7 +57,7 @@ uint32_t Incoming::getCredit() return credit;//TODO: proper flow control } -void Incoming::detached() +void Incoming::detached(bool /*closed*/) { } diff --git a/qpid/cpp/src/qpid/broker/amqp/Incoming.h b/qpid/cpp/src/qpid/broker/amqp/Incoming.h index 1a7064337d..ccf999a256 100644 --- a/qpid/cpp/src/qpid/broker/amqp/Incoming.h +++ b/qpid/cpp/src/qpid/broker/amqp/Incoming.h @@ -42,7 +42,7 @@ class Incoming : public ManagedIncomingLink virtual ~Incoming(); virtual bool doWork();//do anything that requires output virtual bool haveWork();//called when handling input to see whether any output work is needed - virtual void detached(); + virtual void detached(bool closed); virtual void readable(pn_delivery_t* delivery) = 0; void verify(const std::string& userid, const std::string& defaultRealm); void wakeup(); diff --git a/qpid/cpp/src/qpid/broker/amqp/Outgoing.cpp b/qpid/cpp/src/qpid/broker/amqp/Outgoing.cpp index 54993d071e..d0b41c6c90 100644 --- a/qpid/cpp/src/qpid/broker/amqp/Outgoing.cpp +++ b/qpid/cpp/src/qpid/broker/amqp/Outgoing.cpp @@ -156,7 +156,7 @@ bool OutgoingFromQueue::canDeliver() return deliveries[current].delivery == 0 && pn_link_credit(link); } -void OutgoingFromQueue::detached() +void OutgoingFromQueue::detached(bool closed) { QPID_LOG(debug, "Detaching outgoing link " << getName() << " from " << queue->getName()); queue->cancel(shared_from_this()); @@ -164,12 +164,14 @@ void OutgoingFromQueue::detached() for (size_t i = 0 ; i < deliveries.capacity(); ++i) { if (deliveries[i].msg) queue->release(deliveries[i].cursor, true); } - if (exclusive) queue->releaseExclusiveOwnership(); - else if (isControllingUser) queue->releaseFromUse(true); + if (exclusive) { + queue->releaseExclusiveOwnership(closed); + } else if (isControllingUser) { + queue->releaseFromUse(true); + } cancelled = true; } - OutgoingFromQueue::~OutgoingFromQueue() { if (!cancelled && isControllingUser) queue->releaseFromUse(true); diff --git a/qpid/cpp/src/qpid/broker/amqp/Outgoing.h b/qpid/cpp/src/qpid/broker/amqp/Outgoing.h index 27d8205fc8..d3825d0894 100644 --- a/qpid/cpp/src/qpid/broker/amqp/Outgoing.h +++ b/qpid/cpp/src/qpid/broker/amqp/Outgoing.h @@ -70,7 +70,7 @@ class Outgoing : public ManagedOutgoingLink /** * Signals that this link has been detached */ - virtual void detached() = 0; + virtual void detached(bool closed) = 0; /** * Called when a delivery is writable */ @@ -98,7 +98,7 @@ class OutgoingFromQueue : public Outgoing, public qpid::broker::Consumer, public void write(const char* data, size_t size); void handle(pn_delivery_t* delivery); bool canDeliver(); - void detached(); + void detached(bool closed); //Consumer interface: bool deliver(const QueueCursor& cursor, const qpid::broker::Message& msg); diff --git a/qpid/cpp/src/qpid/broker/amqp/Relay.cpp b/qpid/cpp/src/qpid/broker/amqp/Relay.cpp index 83b3e64ee6..495fe800cb 100644 --- a/qpid/cpp/src/qpid/broker/amqp/Relay.cpp +++ b/qpid/cpp/src/qpid/broker/amqp/Relay.cpp @@ -163,7 +163,7 @@ void OutgoingFromRelay::handle(pn_delivery_t* delivery) /** * Signals that this link has been detached */ -void OutgoingFromRelay::detached() +void OutgoingFromRelay::detached(bool /*closed*/) { relay->detached(this); } @@ -221,7 +221,7 @@ uint32_t IncomingToRelay::getCredit() return relay->getCredit(); } -void IncomingToRelay::detached() +void IncomingToRelay::detached(bool /*closed*/) { relay->detached(this); } diff --git a/qpid/cpp/src/qpid/broker/amqp/Relay.h b/qpid/cpp/src/qpid/broker/amqp/Relay.h index ef700690fd..32f317bfe1 100644 --- a/qpid/cpp/src/qpid/broker/amqp/Relay.h +++ b/qpid/cpp/src/qpid/broker/amqp/Relay.h @@ -100,7 +100,7 @@ class OutgoingFromRelay : public Outgoing const std::string& target, const std::string& name, boost::shared_ptr); bool doWork(); void handle(pn_delivery_t* delivery); - void detached(); + void detached(bool closed); void init(); void setSubjectFilter(const std::string&); void setSelectorFilter(const std::string&); @@ -118,7 +118,7 @@ class IncomingToRelay : public Incoming bool settle(); bool doWork(); bool haveWork(); - void detached(); + void detached(bool closed); void readable(pn_delivery_t* delivery); uint32_t getCredit(); private: diff --git a/qpid/cpp/src/qpid/broker/amqp/Session.cpp b/qpid/cpp/src/qpid/broker/amqp/Session.cpp index 32a923cac5..2e7d30118a 100644 --- a/qpid/cpp/src/qpid/broker/amqp/Session.cpp +++ b/qpid/cpp/src/qpid/broker/amqp/Session.cpp @@ -577,7 +577,7 @@ void Session::detach(pn_link_t* link) if (pn_link_is_sender(link)) { OutgoingLinks::iterator i = outgoing.find(link); if (i != outgoing.end()) { - i->second->detached(); + i->second->detached(true/*TODO: checked whether actually closed; see PROTON-773*/); boost::shared_ptr q = OutgoingFromQueue::getExclusiveSubscriptionQueue(i->second.get()); if (q && !q->isAutoDelete() && !q->isDeleted()) { connection.getBroker().deleteQueue(q->getName(), connection.getUserId(), connection.getMgmtId()); @@ -588,7 +588,7 @@ void Session::detach(pn_link_t* link) } else { IncomingLinks::iterator i = incoming.find(link); if (i != incoming.end()) { - i->second->detached(); + i->second->detached(true/*TODO: checked whether actually closed; see PROTON-773*/); incoming.erase(i); QPID_LOG(debug, "Incoming link detached"); } @@ -653,7 +653,7 @@ bool Session::dispatch() pn_condition_set_name(error, e.symbol()); pn_condition_set_description(error, e.what()); pn_link_close(s->first); - s->second->detached(); + s->second->detached(true); outgoing.erase(s++); output = true; } @@ -678,7 +678,7 @@ bool Session::dispatch() pn_condition_set_name(error, e.symbol()); pn_condition_set_description(error, e.what()); pn_link_close(i->first); - i->second->detached(); + i->second->detached(true); incoming.erase(i++); output = true; } @@ -690,10 +690,10 @@ bool Session::dispatch() void Session::close() { for (OutgoingLinks::iterator i = outgoing.begin(); i != outgoing.end(); ++i) { - i->second->detached(); + i->second->detached(false); } for (IncomingLinks::iterator i = incoming.begin(); i != incoming.end(); ++i) { - i->second->detached(); + i->second->detached(false); } outgoing.clear(); incoming.clear(); -- cgit v1.2.1 From b2e043b69e7049c31fb0a75bb3e41f2550223a24 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 17 Dec 2014 14:29:50 +0000 Subject: QPID-6275: reduce default autodelete timeout for durable subscriptions git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1646261 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp | 2 +- qpid/cpp/src/qpid/messaging/amqp/AddressHelper.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid') diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp index 920dd4172b..6128f4c0fc 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp @@ -547,7 +547,7 @@ Subscription::Subscription(const Address& address, const std::string& type) //default for the autodelete timeout (previously this would //have defaulted to autodelete immediately anyway, so the risk //of the change causing problems is mitigated) - queueOptions.setInt("qpid.auto_delete_timeout", 15*60); + queueOptions.setInt("qpid.auto_delete_timeout", 2*60); } (Opt(address)/LINK/X_DECLARE/ARGUMENTS).collect(queueOptions); (Opt(address)/LINK/X_SUBSCRIBE/ARGUMENTS).collect(subscriptionOptions); diff --git a/qpid/cpp/src/qpid/messaging/amqp/AddressHelper.cpp b/qpid/cpp/src/qpid/messaging/amqp/AddressHelper.cpp index c6ad8cdb6c..e631501977 100644 --- a/qpid/cpp/src/qpid/messaging/amqp/AddressHelper.cpp +++ b/qpid/cpp/src/qpid/messaging/amqp/AddressHelper.cpp @@ -242,7 +242,7 @@ bool replace(Variant::Map& map, const std::string& original, const std::string& } } -const uint32_t DEFAULT_DURABLE_TIMEOUT(15*60);//15 minutes +const uint32_t DEFAULT_DURABLE_TIMEOUT(2*60);//2 minutes const uint32_t DEFAULT_TIMEOUT(0); } -- cgit v1.2.1 From aa51ac52f3bd77d92acf585699bc7429666ad785 Mon Sep 17 00:00:00 2001 From: Ken Giusti Date: Wed, 17 Dec 2014 22:37:37 +0000 Subject: QPID-6255: Use Proton event model in qpidd when available. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1646354 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/cpp/src/amqp.cmake | 3 +- qpid/cpp/src/config.h.cmake | 1 + qpid/cpp/src/qpid/broker/amqp/Connection.cpp | 289 ++++++++++++++++++--------- qpid/cpp/src/qpid/broker/amqp/Connection.h | 15 ++ qpid/cpp/src/qpid/broker/amqp/Relay.cpp | 8 +- 5 files changed, 225 insertions(+), 91 deletions(-) (limited to 'qpid') diff --git a/qpid/cpp/src/amqp.cmake b/qpid/cpp/src/amqp.cmake index 3be9f520e0..77ade87c8e 100644 --- a/qpid/cpp/src/amqp.cmake +++ b/qpid/cpp/src/amqp.cmake @@ -22,7 +22,7 @@ find_package(Proton 0.5) set (amqp_default ${amqp_force}) -set (maximum_version 0.7) +set (maximum_version 0.8) if (Proton_FOUND) if (Proton_VERSION GREATER ${maximum_version}) message(WARNING "Qpid proton ${Proton_VERSION} is not a tested version and might not be compatible, ${maximum_version} is highest tested; build may not work") @@ -35,6 +35,7 @@ if (Proton_FOUND) endif (NOT Proton_VERSION EQUAL 0.5) if (Proton_VERSION GREATER 0.7) set (USE_PROTON_TRANSPORT_CONDITION 1) + set (HAVE_PROTON_EVENTS 1) endif (Proton_VERSION GREATER 0.7) else () message(STATUS "Qpid proton not found, amqp 1.0 support not enabled") diff --git a/qpid/cpp/src/config.h.cmake b/qpid/cpp/src/config.h.cmake index f8139262d5..dbfc4ced8a 100644 --- a/qpid/cpp/src/config.h.cmake +++ b/qpid/cpp/src/config.h.cmake @@ -58,5 +58,6 @@ #cmakedefine HAVE_LOG_FTP #cmakedefine HAVE_PROTON_TRACER #cmakedefine USE_PROTON_TRANSPORT_CONDITION +#cmakedefine HAVE_PROTON_EVENTS #endif /* QPID_CONFIG_H */ diff --git a/qpid/cpp/src/qpid/broker/amqp/Connection.cpp b/qpid/cpp/src/qpid/broker/amqp/Connection.cpp index 04bbe8b944..f04cd8eb6e 100644 --- a/qpid/cpp/src/qpid/broker/amqp/Connection.cpp +++ b/qpid/cpp/src/qpid/broker/amqp/Connection.cpp @@ -37,6 +37,9 @@ extern "C" { #include #include +#ifdef HAVE_PROTON_EVENTS +#include +#endif } namespace qpid { @@ -117,8 +120,14 @@ Connection::Connection(qpid::sys::OutputControl& o, const std::string& i, Broker : BrokerContext(b), ManagedConnection(getBroker(), i, brokerInitiated), connection(pn_connection()), transport(pn_transport()), + collector(0), out(o), id(i), haveOutput(true), closeInitiated(false), closeRequested(false) { +#ifdef HAVE_PROTON_EVENTS + collector = pn_collector(); + pn_connection_collect(connection, collector); +#endif + if (pn_transport_bind(transport, connection)) { //error QPID_LOG(error, "Failed to bind transport to connection: " << getError()); @@ -157,6 +166,9 @@ Connection::~Connection() getBroker().getConnectionObservers().closed(*this); pn_transport_free(transport); pn_connection_free(connection); +#ifdef HAVE_PROTON_EVENTS + pn_collector_free(collector); +#endif } pn_transport_t* Connection::getTransport() @@ -222,10 +234,15 @@ size_t Connection::encode(char* buffer, size_t size) void Connection::doOutput(size_t capacity) { - for (ssize_t n = pn_transport_pending(transport); n > 0 && n < (ssize_t) capacity; n = pn_transport_pending(transport)) { - if (dispatch()) processDeliveries(); - else break; - } + ssize_t n = 0; + do { + if (dispatch()) { + processDeliveries(); + ssize_t next = pn_transport_pending(transport); + if (n == next) break; + n = next; + } else break; + } while (n > 0 && n < (ssize_t) capacity); } bool Connection::dispatch() @@ -327,85 +344,70 @@ framing::ProtocolVersion Connection::getVersion() const { return qpid::framing::ProtocolVersion(1,0); } -namespace { -pn_state_t REQUIRES_OPEN = PN_LOCAL_UNINIT | PN_REMOTE_ACTIVE; -pn_state_t REQUIRES_CLOSE = PN_LOCAL_ACTIVE | PN_REMOTE_CLOSED; -} void Connection::process() { QPID_LOG(trace, id << " process()"); +#ifdef HAVE_PROTON_EVENTS + pn_event_t *event = pn_collector_peek(collector); + while (event) { + switch (pn_event_type(event)) { + case PN_CONNECTION_REMOTE_OPEN: + doConnectionRemoteOpen(); + break; + case PN_CONNECTION_REMOTE_CLOSE: + doConnectionRemoteClose(); + break; + case PN_SESSION_REMOTE_OPEN: + doSessionRemoteOpen(pn_event_session(event)); + break; + case PN_SESSION_REMOTE_CLOSE: + doSessionRemoteClose(pn_event_session(event)); + break; + case PN_LINK_REMOTE_OPEN: + doLinkRemoteOpen(pn_event_link(event)); + break; + case PN_LINK_REMOTE_CLOSE: + doLinkRemoteClose(pn_event_link(event)); + break; + case PN_DELIVERY: + doDeliveryUpdated(pn_event_delivery(event)); + break; + default: + break; + } + pn_collector_pop(collector); + event = pn_collector_peek(collector); + } + +#else // !HAVE_PROTON_EVENTS + + const pn_state_t REQUIRES_OPEN = PN_LOCAL_UNINIT | PN_REMOTE_ACTIVE; + const pn_state_t REQUIRES_CLOSE = PN_LOCAL_ACTIVE | PN_REMOTE_CLOSED; + if ((pn_connection_state(connection) & REQUIRES_OPEN) == REQUIRES_OPEN) { - QPID_LOG_CAT(debug, model, id << " connection opened"); - open(); - setContainerId(pn_connection_remote_container(connection)); + doConnectionRemoteOpen(); } for (pn_session_t* s = pn_session_head(connection, REQUIRES_OPEN); s; s = pn_session_next(s, REQUIRES_OPEN)) { - QPID_LOG_CAT(debug, model, id << " session begun"); - pn_session_open(s); - boost::shared_ptr ssn(new Session(s, *this, out)); - sessions[s] = ssn; + doSessionRemoteOpen(s); } for (pn_link_t* l = pn_link_head(connection, REQUIRES_OPEN); l; l = pn_link_next(l, REQUIRES_OPEN)) { - pn_link_open(l); - - Sessions::iterator session = sessions.find(pn_link_session(l)); - if (session == sessions.end()) { - QPID_LOG(error, id << " Link attached on unknown session!"); - } else { - try { - session->second->attach(l); - QPID_LOG_CAT(debug, protocol, id << " link " << l << " attached on " << pn_link_session(l)); - } catch (const Exception& e) { - QPID_LOG_CAT(error, protocol, "Error on attach: " << e.what()); - pn_condition_t* error = pn_link_condition(l); - pn_condition_set_name(error, e.symbol()); - pn_condition_set_description(error, e.what()); - pn_link_close(l); - } catch (const qpid::framing::UnauthorizedAccessException& e) { - QPID_LOG_CAT(error, protocol, "Error on attach: " << e.what()); - pn_condition_t* error = pn_link_condition(l); - pn_condition_set_name(error, qpid::amqp::error_conditions::UNAUTHORIZED_ACCESS.c_str()); - pn_condition_set_description(error, e.what()); - pn_link_close(l); - } catch (const std::exception& e) { - QPID_LOG_CAT(error, protocol, "Error on attach: " << e.what()); - pn_condition_t* error = pn_link_condition(l); - pn_condition_set_name(error, qpid::amqp::error_conditions::INTERNAL_ERROR.c_str()); - pn_condition_set_description(error, e.what()); - pn_link_close(l); - } - } + doLinkRemoteOpen(l); } processDeliveries(); for (pn_link_t* l = pn_link_head(connection, REQUIRES_CLOSE); l; l = pn_link_next(l, REQUIRES_CLOSE)) { - pn_link_close(l); - Sessions::iterator session = sessions.find(pn_link_session(l)); - if (session == sessions.end()) { - QPID_LOG(error, id << " peer attempted to detach link on unknown session!"); - } else { - session->second->detach(l); - QPID_LOG_CAT(debug, model, id << " link detached"); - } + doLinkRemoteClose(l); } for (pn_session_t* s = pn_session_head(connection, REQUIRES_CLOSE); s; s = pn_session_next(s, REQUIRES_CLOSE)) { - pn_session_close(s); - Sessions::iterator i = sessions.find(s); - if (i != sessions.end()) { - i->second->close(); - sessions.erase(i); - QPID_LOG_CAT(debug, model, id << " session ended"); - } else { - QPID_LOG(error, id << " peer attempted to close unrecognised session"); - } + doSessionRemoteClose(s); } if ((pn_connection_state(connection) & REQUIRES_CLOSE) == REQUIRES_CLOSE) { - QPID_LOG_CAT(debug, model, id << " connection closed"); - pn_connection_close(connection); + doConnectionRemoteClose(); } +#endif // !HAVE_PROTON_EVENTS } namespace { std::string convert(pn_delivery_tag_t in) @@ -415,34 +417,15 @@ std::string convert(pn_delivery_tag_t in) } void Connection::processDeliveries() { - //handle deliveries +#ifdef HAVE_PROTON_EVENTS + // with the event API, there's no way to selectively process only + // the delivery-related events. We have to process all events: + process(); +#else for (pn_delivery_t* delivery = pn_work_head(connection); delivery; delivery = pn_work_next(delivery)) { - pn_link_t* link = pn_delivery_link(delivery); - try { - if (pn_link_is_receiver(link)) { - Sessions::iterator i = sessions.find(pn_link_session(link)); - if (i != sessions.end()) { - i->second->readable(link, delivery); - } else { - pn_delivery_update(delivery, PN_REJECTED); - } - } else { //i.e. SENDER - Sessions::iterator i = sessions.find(pn_link_session(link)); - if (i != sessions.end()) { - QPID_LOG(trace, id << " handling outgoing delivery for " << link << " on session " << pn_link_session(link)); - i->second->writable(link, delivery); - } else { - QPID_LOG(error, id << " Got delivery for non-existent session: " << pn_link_session(link) << ", link: " << link); - } - } - } catch (const Exception& e) { - QPID_LOG_CAT(error, protocol, "Error processing deliveries: " << e.what()); - pn_condition_t* error = pn_link_condition(link); - pn_condition_set_name(error, e.symbol()); - pn_condition_set_description(error, e.what()); - pn_link_close(link); - } + doDeliveryUpdated(delivery); } +#endif } std::string Connection::getError() @@ -470,4 +453,132 @@ void Connection::closedByManagement() closeRequested = true; out.activateOutput(); } + +// the peer has issued an Open performative +void Connection::doConnectionRemoteOpen() +{ + // respond in kind if we haven't yet + if ((pn_connection_state(connection) & PN_LOCAL_UNINIT) == PN_LOCAL_UNINIT) { + QPID_LOG_CAT(debug, model, id << " connection opened"); + open(); + setContainerId(pn_connection_remote_container(connection)); + } +} + +// the peer has issued a Close performative +void Connection::doConnectionRemoteClose() +{ + if ((pn_connection_state(connection) & PN_LOCAL_CLOSED) == 0) { + QPID_LOG_CAT(debug, model, id << " connection closed"); + pn_connection_close(connection); + } +} + +// the peer has issued a Begin performative +void Connection::doSessionRemoteOpen(pn_session_t *session) +{ + if ((pn_session_state(session) & PN_LOCAL_UNINIT) == PN_LOCAL_UNINIT) { + QPID_LOG_CAT(debug, model, id << " session begun"); + pn_session_open(session); + boost::shared_ptr ssn(new Session(session, *this, out)); + sessions[session] = ssn; + } +} + +// the peer has issued an End performative +void Connection::doSessionRemoteClose(pn_session_t *session) +{ + if ((pn_session_state(session) & PN_LOCAL_CLOSED) == 0) { + pn_session_close(session); + Sessions::iterator i = sessions.find(session); + if (i != sessions.end()) { + i->second->close(); + sessions.erase(i); + QPID_LOG_CAT(debug, model, id << " session ended"); + } else { + QPID_LOG(error, id << " peer attempted to close unrecognised session"); + } + } +} + +// the peer has issued an Attach performative +void Connection::doLinkRemoteOpen(pn_link_t *link) +{ + if ((pn_link_state(link) & PN_LOCAL_UNINIT) == PN_LOCAL_UNINIT) { + pn_link_open(link); + Sessions::iterator session = sessions.find(pn_link_session(link)); + if (session == sessions.end()) { + QPID_LOG(error, id << " Link attached on unknown session!"); + } else { + try { + session->second->attach(link); + QPID_LOG_CAT(debug, protocol, id << " link " << link << " attached on " << pn_link_session(link)); + } catch (const Exception& e) { + QPID_LOG_CAT(error, protocol, "Error on attach: " << e.what()); + pn_condition_t* error = pn_link_condition(link); + pn_condition_set_name(error, e.symbol()); + pn_condition_set_description(error, e.what()); + pn_link_close(link); + } catch (const qpid::framing::UnauthorizedAccessException& e) { + QPID_LOG_CAT(error, protocol, "Error on attach: " << e.what()); + pn_condition_t* error = pn_link_condition(link); + pn_condition_set_name(error, qpid::amqp::error_conditions::UNAUTHORIZED_ACCESS.c_str()); + pn_condition_set_description(error, e.what()); + pn_link_close(link); + } catch (const std::exception& e) { + QPID_LOG_CAT(error, protocol, "Error on attach: " << e.what()); + pn_condition_t* error = pn_link_condition(link); + pn_condition_set_name(error, qpid::amqp::error_conditions::INTERNAL_ERROR.c_str()); + pn_condition_set_description(error, e.what()); + pn_link_close(link); + } + } + } +} + +// the peer has issued a Detach performative +void Connection::doLinkRemoteClose(pn_link_t *link) +{ + if ((pn_link_state(link) & PN_LOCAL_CLOSED) == 0) { + pn_link_close(link); + Sessions::iterator session = sessions.find(pn_link_session(link)); + if (session == sessions.end()) { + QPID_LOG(error, id << " peer attempted to detach link on unknown session!"); + } else { + session->second->detach(link); + QPID_LOG_CAT(debug, model, id << " link detached"); + } + } +} + +// the status of the delivery has changed +void Connection::doDeliveryUpdated(pn_delivery_t *delivery) +{ + pn_link_t* link = pn_delivery_link(delivery); + try { + if (pn_link_is_receiver(link)) { + Sessions::iterator i = sessions.find(pn_link_session(link)); + if (i != sessions.end()) { + i->second->readable(link, delivery); + } else { + pn_delivery_update(delivery, PN_REJECTED); + } + } else { //i.e. SENDER + Sessions::iterator i = sessions.find(pn_link_session(link)); + if (i != sessions.end()) { + QPID_LOG(trace, id << " handling outgoing delivery for " << link << " on session " << pn_link_session(link)); + i->second->writable(link, delivery); + } else { + QPID_LOG(error, id << " Got delivery for non-existent session: " << pn_link_session(link) << ", link: " << link); + } + } + } catch (const Exception& e) { + QPID_LOG_CAT(error, protocol, "Error processing deliveries: " << e.what()); + pn_condition_t* error = pn_link_condition(link); + pn_condition_set_name(error, e.symbol()); + pn_condition_set_description(error, e.what()); + pn_link_close(link); + } +} + }}} // namespace qpid::broker::amqp diff --git a/qpid/cpp/src/qpid/broker/amqp/Connection.h b/qpid/cpp/src/qpid/broker/amqp/Connection.h index 17c5b0ecf0..ea4ce06163 100644 --- a/qpid/cpp/src/qpid/broker/amqp/Connection.h +++ b/qpid/cpp/src/qpid/broker/amqp/Connection.h @@ -31,6 +31,9 @@ struct pn_connection_t; struct pn_session_t; struct pn_transport_t; +struct pn_collector_t; +struct pn_link_t; +struct pn_delivery_t; namespace qpid { namespace sys { @@ -69,6 +72,7 @@ class Connection : public BrokerContext, public sys::ConnectionCodec, public Man typedef std::map > Sessions; pn_connection_t* connection; pn_transport_t* transport; + pn_collector_t* collector; qpid::sys::OutputControl& out; const std::string id; bool haveOutput; @@ -86,6 +90,17 @@ class Connection : public BrokerContext, public sys::ConnectionCodec, public Man void open(); void readPeerProperties(); void closedByManagement(); + + private: + // handle Proton engine events + void doConnectionRemoteOpen(); + void doConnectionRemoteClose(); + void doSessionRemoteOpen(pn_session_t *session); + void doSessionRemoteClose(pn_session_t *session); + void doLinkRemoteOpen(pn_link_t *link); + void doLinkRemoteClose(pn_link_t *link); + void doDeliveryUpdated(pn_delivery_t *delivery); + }; }}} // namespace qpid::broker::amqp diff --git a/qpid/cpp/src/qpid/broker/amqp/Relay.cpp b/qpid/cpp/src/qpid/broker/amqp/Relay.cpp index 495fe800cb..5e7a3af889 100644 --- a/qpid/cpp/src/qpid/broker/amqp/Relay.cpp +++ b/qpid/cpp/src/qpid/broker/amqp/Relay.cpp @@ -126,7 +126,13 @@ bool OutgoingFromRelay::doWork() { relay->check(); relay->setCredit(pn_link_credit(link)); - return relay->send(link); + bool worked = relay->send(link); + pn_delivery_t *d = pn_link_current(link); + if (d && pn_delivery_writable(d)) { + handle(d); + return true; + } + return worked; } /** * Called when a delivery is writable -- cgit v1.2.1 From 40e74eaa3f8a345e7bc888e36de79717b7c761d0 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Fri, 19 Dec 2014 03:18:57 +0000 Subject: QPID-6278: HA broker abort in TXN soak test The crash appears to be a race condition in async completion exposed by the HA TX code code as follows: 1. Message received and placed on tx-replication queue, completion delayed till backups ack. Completion count goes up for each backup then down as each backup acks. 2. Prepare received, message placed on primary's local persistent queue. Completion count goes up one then down one for local store completion (null store in this case). The race is something like this: - last backup ack arrives (on backup IO thread) and drops completion count to 0. - prepare arrives (on client thread) null store bumps count to 1 and immediately drops to 0. - both threads try to invoke the completion callback, one deletes it while the other is still invoking. The old completion logic assumed that only one thread can see the atomic counter go to 0. It does not handle the count going to 0 in one thread and concurrently being increased and decreased back to 0 in another. This case is introduced by HA transactions because the same message is put onto a tx-replication queue and then put again onto another persistent local queue, so there are two cycles of completion. The new logic fixes this only one call to completion callback is possible in all cases. Also fixed missing lock in ha/Primary.cpp. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1646618 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/cpp/src/qpid/broker/AsyncCompletion.h | 5 +++-- qpid/cpp/src/qpid/ha/Primary.cpp | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'qpid') diff --git a/qpid/cpp/src/qpid/broker/AsyncCompletion.h b/qpid/cpp/src/qpid/broker/AsyncCompletion.h index 1ab69f32d3..cb5d58977b 100644 --- a/qpid/cpp/src/qpid/broker/AsyncCompletion.h +++ b/qpid/cpp/src/qpid/broker/AsyncCompletion.h @@ -111,13 +111,14 @@ class AsyncCompletion : public virtual RefCounted qpid::sys::Mutex::ScopedLock l(callbackLock); if (active) { if (callback.get()) { + boost::intrusive_ptr save = callback; + callback = boost::intrusive_ptr(); // Nobody else can run callback. inCallback = true; { qpid::sys::Mutex::ScopedUnlock ul(callbackLock); - callback->completed(sync); + save->completed(sync); } inCallback = false; - callback = boost::intrusive_ptr(); callbackLock.notifyAll(); } active = false; diff --git a/qpid/cpp/src/qpid/ha/Primary.cpp b/qpid/cpp/src/qpid/ha/Primary.cpp index 0e87346ac1..870e4723b2 100644 --- a/qpid/cpp/src/qpid/ha/Primary.cpp +++ b/qpid/cpp/src/qpid/ha/Primary.cpp @@ -482,6 +482,7 @@ shared_ptr Primary::makeTxObserver( { shared_ptr observer = PrimaryTxObserver::create(*this, haBroker, txBuffer); + sys::Mutex::ScopedLock l(lock); txMap[observer->getTxQueue()->getName()] = observer; return observer; } -- cgit v1.2.1 From aa516ab4d908927a95b20275fc7daaa7bb9420a9 Mon Sep 17 00:00:00 2001 From: Keith Wall Date: Fri, 19 Dec 2014 19:13:50 +0000 Subject: QPID-6276: [Java Broker] Enhance the virtualhost UI to support upload/download of virtualhost config expressed as JSON. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1646829 13f79535-47bb-0310-9956-ffa450edef68 --- .../bdb_ha/add/existinggroup/add.js | 3 + .../virtualhostnode/bdb_ha/add/newgroup/add.js | 3 + .../plugin/servlet/rest/RestServlet.java | 44 +++++- .../addVirtualHostNodeAndVirtualHost.html | 28 ++++ .../src/main/java/resources/css/common.css | 16 +++ .../src/main/java/resources/index.html | 1 + .../resources/js/qpid/management/VirtualHost.js | 14 ++ .../management/addVirtualHostNodeAndVirtualHost.js | 156 +++++++++++++++------ .../src/main/java/resources/showVirtualHost.html | 1 + 9 files changed, 219 insertions(+), 47 deletions(-) (limited to 'qpid') diff --git a/qpid/java/bdbstore/src/main/java/resources/js/qpid/management/virtualhostnode/bdb_ha/add/existinggroup/add.js b/qpid/java/bdbstore/src/main/java/resources/js/qpid/management/virtualhostnode/bdb_ha/add/existinggroup/add.js index ee467e69e4..532c37f65b 100644 --- a/qpid/java/bdbstore/src/main/java/resources/js/qpid/management/virtualhostnode/bdb_ha/add/existinggroup/add.js +++ b/qpid/java/bdbstore/src/main/java/resources/js/qpid/management/virtualhostnode/bdb_ha/add/existinggroup/add.js @@ -39,6 +39,9 @@ define(["dojo/_base/xhr", registry.byId("addVirtualHostNode.helperNodeName").set("regExpGen", util.nameOrContextVarRegexp); registry.byId("addVirtualHostNode.helperAddress").set("regExpGen", util.nodeAddressOrContextVarRegexp); registry.byId("addVirtualHostNode.address").set("regExpGen", util.nodeAddressOrContextVarRegexp); + + dom.byId("addVirtualHostNode.uploadFields").style.display = "none"; + } }; } diff --git a/qpid/java/bdbstore/src/main/java/resources/js/qpid/management/virtualhostnode/bdb_ha/add/newgroup/add.js b/qpid/java/bdbstore/src/main/java/resources/js/qpid/management/virtualhostnode/bdb_ha/add/newgroup/add.js index 464a01b5b0..c1aa9ffe4f 100644 --- a/qpid/java/bdbstore/src/main/java/resources/js/qpid/management/virtualhostnode/bdb_ha/add/newgroup/add.js +++ b/qpid/java/bdbstore/src/main/java/resources/js/qpid/management/virtualhostnode/bdb_ha/add/newgroup/add.js @@ -70,6 +70,9 @@ define(["dojo/_base/xhr", this.addVirtualHostNodePermittedNodes = registry.byId("addVirtualHostNode.permittedNodes"); registry.byId("addVirtualHostNode.groupName").set("regExpGen", util.nameOrContextVarRegexp); + + dom.byId("addVirtualHostNode.uploadFields").style.display = "block"; + }, _updatePermittedNodesJson: function () { diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java index 0bc0a4514f..52c9b10e59 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java @@ -62,13 +62,20 @@ public class RestServlet extends AbstractServlet public static final String INHERITED_ACTUALS_PARAM = "inheritedActuals"; public static final String EXTRACT_INITIAL_CONFIG_PARAM = "extractInitialConfig"; + /** + * Signifies that the agent wishes the servlet to set the Content-Disposition on the + * response with the value attachment. This filename will be derived from the parameter value. + */ + public static final String CONTENT_DISPOSITION_ATTACHMENT_FILENAME_PARAM = "contentDispositionAttachmentFilename"; + public static final Set RESERVED_PARAMS = new HashSet<>(Arrays.asList(DEPTH_PARAM, SORT_PARAM, ACTUALS_PARAM, INCLUDE_SYS_CONTEXT_PARAM, EXTRACT_INITIAL_CONFIG_PARAM, - INHERITED_ACTUALS_PARAM)); + INHERITED_ACTUALS_PARAM, + CONTENT_DISPOSITION_ATTACHMENT_FILENAME_PARAM)); private Class[] _hierarchy; @@ -316,19 +323,23 @@ public class RestServlet extends AbstractServlet @Override protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + // TODO - sort special params, everything else should act as a filter + String attachmentFilename = request.getParameter(CONTENT_DISPOSITION_ATTACHMENT_FILENAME_PARAM); + boolean extractInitialConfig = getBooleanParameterFromRequest(request, EXTRACT_INITIAL_CONFIG_PARAM); + response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_OK); setCachingHeadersOnResponse(response); + setContentDispositionHeaderIfNecessary(response, attachmentFilename); Collection> allObjects = getObjects(request); - // TODO - sort special params, everything else should act as a filter - boolean extractInitialConfig = getBooleanParameterFromRequest(request, EXTRACT_INITIAL_CONFIG_PARAM); int depth; boolean actuals; boolean includeSystemContext; boolean inheritedActuals; + if(extractInitialConfig) { depth = Integer.MAX_VALUE; @@ -344,20 +355,35 @@ public class RestServlet extends AbstractServlet inheritedActuals = getBooleanParameterFromRequest(request, INHERITED_ACTUALS_PARAM); } - List> output = new ArrayList>(); + List> output = new ArrayList<>(); for(ConfiguredObject configuredObject : allObjects) { output.add(_objectConverter.convertObjectToMap(configuredObject, getConfiguredClass(), depth, actuals, inheritedActuals, includeSystemContext, extractInitialConfig)); } + Writer writer = getOutputWriter(request, response); ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true); mapper.writeValue(writer, extractInitialConfig && output.size() == 1 ? output.get(0) : output); + } - response.setContentType("application/json"); - response.setStatus(HttpServletResponse.SC_OK); + private void setContentDispositionHeaderIfNecessary(final HttpServletResponse response, + final String attachmentFilename) + { + if (attachmentFilename != null) + { + String filenameRfc2183 = ensureFilenameIsRfc2183(attachmentFilename); + if (filenameRfc2183.length() > 0) + { + response.setHeader("Content-disposition", String.format("attachment; filename=\"%s\"", filenameRfc2183)); + } + else + { + response.setHeader("Content-disposition", String.format("attachment")); // Agent will allow user to choose a name + } + } } private Class getConfiguredClass() @@ -671,4 +697,10 @@ public class RestServlet extends AbstractServlet return Boolean.parseBoolean(request.getParameter(paramName)); } + private String ensureFilenameIsRfc2183(final String requestedFilename) + { + String fileNameRfc2183 = requestedFilename.replaceAll("[\\P{InBasic_Latin}\\\\:/]", ""); + return fileNameRfc2183; + } + } diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/addVirtualHostNodeAndVirtualHost.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/addVirtualHostNodeAndVirtualHost.html index 3e3e931829..383c782d60 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/addVirtualHostNodeAndVirtualHost.html +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/addVirtualHostNodeAndVirtualHost.html @@ -59,6 +59,34 @@
+
+
+ +
+
+ +
+
+
+ +
+
+ + + +
+
+
+ +
+
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/css/common.css b/qpid/java/broker-plugins/management-http/src/main/java/resources/css/common.css index d04117b266..e6a0ce467f 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/css/common.css +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/css/common.css @@ -215,6 +215,22 @@ div .messages { height: 16px; } +.loadingIcon +{ + background: url("../dojo/dojox/image/resources/images/loading.gif") no-repeat; + width: 25px; + height: 25px; + display: inline-block; +} + +.loadedIcon +{ + background: url("../dojo/dojox/mobile/themes/common/domButtons/compat/mblDomButtonDarkBlueCheck.png") no-repeat; + width: 25px; + height: 25px; + display: inline-block; +} + .infoMessage { padding: 5px; diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/index.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/index.html index eb742bbfa0..379a25bbcd 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/index.html +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/index.html @@ -25,6 +25,7 @@ +